mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2026-08-15 00:47:32 +00:00
Fix missing headers in SSE queries
This commit is contained in:
@@ -26,7 +26,7 @@ class ZLOTA(FileDownloadSIGRefDataProvider):
|
|||||||
longitude = ref["longitude"]
|
longitude = ref["longitude"]
|
||||||
|
|
||||||
new_ref = SIGRef(sig=self.SIG, id=ref_id, name=ref["name"],
|
new_ref = SIGRef(sig=self.SIG, id=ref_id, name=ref["name"],
|
||||||
ref_type=ref["asset_type"].title,
|
ref_type=ref["asset_type"].title(),
|
||||||
url="https://ontheair.nz/assets/" + ref_id.replace("/", "_"),
|
url="https://ontheair.nz/assets/" + ref_id.replace("/", "_"),
|
||||||
latitude=latitude,
|
latitude=latitude,
|
||||||
longitude=longitude)
|
longitude=longitude)
|
||||||
|
|||||||
+3
-2
@@ -6,9 +6,10 @@ let lastUpdateTime;
|
|||||||
// Storage for the alert data that the server gives us.
|
// Storage for the alert data that the server gives us.
|
||||||
let alerts = [];
|
let alerts = [];
|
||||||
|
|
||||||
// Load alerts and populate the table.
|
// Load alerts and populate the table. No need for QRZ/HamQTH credential headers as these won't add any useful data
|
||||||
|
// to alerts
|
||||||
function loadAlerts() {
|
function loadAlerts() {
|
||||||
$.ajax({url: '/api/v2/alerts' + buildQueryString(), dataType: 'json', headers: getCredentialHeaders(), success: function (jsonData) {
|
$.ajax({url: '/api/v2/alerts' + buildQueryString(), dataType: 'json', success: function (jsonData) {
|
||||||
// Store last updated time
|
// Store last updated time
|
||||||
lastUpdateTime = moment.utc();
|
lastUpdateTime = moment.utc();
|
||||||
updateRefreshDisplay();
|
updateRefreshDisplay();
|
||||||
|
|||||||
+24
-19
@@ -1,6 +1,5 @@
|
|||||||
// SSE connection for live spot updates
|
// SSE connection for live spot updates
|
||||||
let evtSource;
|
let sseAbortController = null;
|
||||||
let restartSSEOnErrorTimeoutId;
|
|
||||||
// Debounce timer so rapid SSE bursts only trigger one updateBands() call, as these could trigger a flash of the user
|
// Debounce timer so rapid SSE bursts only trigger one updateBands() call, as these could trigger a flash of the user
|
||||||
// visible content
|
// visible content
|
||||||
let updateBandsDebounceId;
|
let updateBandsDebounceId;
|
||||||
@@ -17,10 +16,10 @@ BAND_COLUMN_SPOT_DIV_HEIGHT_PX = BAND_COLUMN_FONT_SIZE * 1.6;
|
|||||||
// Load spots and populate the bands display.
|
// Load spots and populate the bands display.
|
||||||
function loadSpots() {
|
function loadSpots() {
|
||||||
// Close any existing SSE connection before fetching fresh data
|
// Close any existing SSE connection before fetching fresh data
|
||||||
if (evtSource != null) {
|
if (sseAbortController != null) {
|
||||||
evtSource.close();
|
sseAbortController.abort();
|
||||||
}
|
}
|
||||||
$.ajax({url: '/api/v2/spots' + buildQueryString(), dataType: 'json', headers: getCredentialHeaders(), success: function (jsonData) {
|
$.ajax({url: '/api/v2/spots' + buildQueryString(), dataType: 'json', success: function (jsonData) {
|
||||||
// Store data
|
// Store data
|
||||||
spots = jsonData;
|
spots = jsonData;
|
||||||
// Update bands display
|
// Update bands display
|
||||||
@@ -33,12 +32,21 @@ function loadSpots() {
|
|||||||
|
|
||||||
// Start an SSE connection to receive new spots as they arrive.
|
// Start an SSE connection to receive new spots as they arrive.
|
||||||
function startSSEConnection() {
|
function startSSEConnection() {
|
||||||
if (evtSource != null) {
|
if (sseAbortController != null) {
|
||||||
evtSource.close();
|
sseAbortController.abort();
|
||||||
|
}
|
||||||
|
sseAbortController = new AbortController();
|
||||||
|
|
||||||
|
// No need to include QRZ/HamQTH credentials because the information wouldn't be displayed on the bands panel anyway
|
||||||
|
fetchEventSource('/api/v2/spots/stream' + buildQueryString(), {
|
||||||
|
signal: sseAbortController.signal,
|
||||||
|
openWhenHidden: true,
|
||||||
|
|
||||||
|
onmessage(event) {
|
||||||
|
if (!event.data) {
|
||||||
|
return; // heartbeat/keep-alive, nothing to do
|
||||||
}
|
}
|
||||||
evtSource = new EventSource('/api/v2/spots/stream' + buildQueryString());
|
|
||||||
|
|
||||||
evtSource.onmessage = function (event) {
|
|
||||||
const newSpot = JSON.parse(event.data);
|
const newSpot = JSON.parse(event.data);
|
||||||
|
|
||||||
// Replace any existing spot for this callsign
|
// Replace any existing spot for this callsign
|
||||||
@@ -48,15 +56,12 @@ function startSSEConnection() {
|
|||||||
// Debounce. Wait 500ms after the last message before re-rendering to avoid too many ugly flashes
|
// Debounce. Wait 500ms after the last message before re-rendering to avoid too many ugly flashes
|
||||||
clearTimeout(updateBandsDebounceId);
|
clearTimeout(updateBandsDebounceId);
|
||||||
updateBandsDebounceId = setTimeout(updateBands, 5000);
|
updateBandsDebounceId = setTimeout(updateBands, 5000);
|
||||||
};
|
},
|
||||||
|
onerror(err) {
|
||||||
evtSource.onerror = function () {
|
console.error('SSE error:', err);
|
||||||
if (evtSource != null) {
|
return 1000;
|
||||||
evtSource.close();
|
|
||||||
}
|
}
|
||||||
clearTimeout(restartSSEOnErrorTimeoutId);
|
});
|
||||||
restartSSEOnErrorTimeoutId = setTimeout(startSSEConnection, 1000);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove spots from the display that are older than the selected max age.
|
// Remove spots from the display that are older than the selected max age.
|
||||||
@@ -319,8 +324,8 @@ function displayUpdated() {
|
|||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
// Close SSE connection cleanly when navigating away
|
// Close SSE connection cleanly when navigating away
|
||||||
window.addEventListener('beforeunload', function () {
|
window.addEventListener('beforeunload', function () {
|
||||||
if (evtSource != null) {
|
if (sseAbortController != null) {
|
||||||
evtSource.close();
|
sseAbortController.abort();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+33
-22
@@ -9,8 +9,7 @@ const ITU_ZONES_COLOR_DARK = 'rgba(120, 120, 60, 1.0)';
|
|||||||
const WAB_WAI_GRID_COLOR_DARK = 'rgba(60, 60, 120, 1.0)';
|
const WAB_WAI_GRID_COLOR_DARK = 'rgba(60, 60, 120, 1.0)';
|
||||||
|
|
||||||
// SSE connection for live spot updates
|
// SSE connection for live spot updates
|
||||||
let evtSource;
|
let sseAbortController = null;
|
||||||
let restartSSEOnErrorTimeoutId;
|
|
||||||
// Map dx_call to a pair of marker & geodesic line, so we can de-duplicate spots as they arrive and only show
|
// Map dx_call to a pair of marker & geodesic line, so we can de-duplicate spots as they arrive and only show
|
||||||
// one marker per dx_call. The key here is actually dx_call + SSID if the spot gives us an SSID; this is mostly for
|
// one marker per dx_call. The key here is actually dx_call + SSID if the spot gives us an SSID; this is mostly for
|
||||||
// if APRS spots are enabled so we can ID a home and mobile station separately rather than our marker oscillating
|
// if APRS spots are enabled so we can ID a home and mobile station separately rather than our marker oscillating
|
||||||
@@ -35,8 +34,8 @@ let firstLoad = true;
|
|||||||
// Load spots and populate the map.
|
// Load spots and populate the map.
|
||||||
function loadSpots() {
|
function loadSpots() {
|
||||||
// Close any existing SSE connection before fetching fresh data
|
// Close any existing SSE connection before fetching fresh data
|
||||||
if (evtSource != null) {
|
if (sseAbortController != null) {
|
||||||
evtSource.close();
|
sseAbortController.abort();
|
||||||
}
|
}
|
||||||
// Including QRZ/HamQTH lookups to improve positions causes the load to be really slow, so on first load
|
// Including QRZ/HamQTH lookups to improve positions causes the load to be really slow, so on first load
|
||||||
// the user would be waiting ages without data and will think it's broken. We therefore load in several
|
// the user would be waiting ages without data and will think it's broken. We therefore load in several
|
||||||
@@ -45,7 +44,8 @@ function loadSpots() {
|
|||||||
// 2) (If we have credentials) reload with them, replacing what's already there,
|
// 2) (If we have credentials) reload with them, replacing what's already there,
|
||||||
// 3) Subscribe to the SSE endpoint (with credentials if we have them) so that updates come with augmented
|
// 3) Subscribe to the SSE endpoint (with credentials if we have them) so that updates come with augmented
|
||||||
// data if they can.
|
// data if they can.
|
||||||
$.ajax({url: '/api/v2/spots' + buildQueryString(), dataType: 'json', headers: getCredentialHeaders(), success: function (jsonData) {
|
$.ajax({
|
||||||
|
url: '/api/v2/spots' + buildQueryString(), dataType: 'json', success: function (jsonData) {
|
||||||
// Store data
|
// Store data
|
||||||
spots = jsonData;
|
spots = jsonData;
|
||||||
// Update map
|
// Update map
|
||||||
@@ -54,14 +54,19 @@ function loadSpots() {
|
|||||||
terminator.setTime();
|
terminator.setTime();
|
||||||
}
|
}
|
||||||
// Check if we have any credentials to use
|
// Check if we have any credentials to use
|
||||||
if (getCredentialQueryString() !== "") {
|
if (Object.keys(getCredentialHeaders()).length > 0) {
|
||||||
// OK, we have credentials and have loaded once without them so the user has a basic map. Now reload
|
// OK, we have credentials and have loaded once without them so the user has a basic map. Now reload
|
||||||
// with the credentials and replace what's on the map, so we can improve the data.
|
// with the credentials and replace what's on the map, so we can improve the data.
|
||||||
$.getJSON('/api/v1/spots' + buildQueryString(true), function (jsonData2) {
|
$.ajax({
|
||||||
|
url: '/api/v2/spots' + buildQueryString(),
|
||||||
|
dataType: 'json',
|
||||||
|
headers: getCredentialHeaders(),
|
||||||
|
success: function(jsonData2) {
|
||||||
spots = jsonData2;
|
spots = jsonData2;
|
||||||
updateMap();
|
updateMap();
|
||||||
// Now start the ongoing SSE connection
|
// Now start the ongoing SSE connection
|
||||||
startSSEConnection();
|
startSSEConnection();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// We had no credentials with which to augment the data anyway, so just start the SSE connection
|
// We had no credentials with which to augment the data anyway, so just start the SSE connection
|
||||||
@@ -73,13 +78,22 @@ function loadSpots() {
|
|||||||
|
|
||||||
// Start the SSE connection to receive new spots as they arrive
|
// Start the SSE connection to receive new spots as they arrive
|
||||||
function startSSEConnection() {
|
function startSSEConnection() {
|
||||||
if (evtSource != null) {
|
if (sseAbortController != null) {
|
||||||
evtSource.close();
|
sseAbortController.abort();
|
||||||
|
}
|
||||||
|
sseAbortController = new AbortController();
|
||||||
|
|
||||||
|
// SSE is going to fetch only a few spots at a time, so now we include QRZ/HamQTH credentials because the delay won't be significant.
|
||||||
|
fetchEventSource('/api/v2/spots/stream' + buildQueryString(), {
|
||||||
|
headers: getCredentialHeaders(),
|
||||||
|
signal: sseAbortController.signal,
|
||||||
|
openWhenHidden: true,
|
||||||
|
|
||||||
|
onmessage(event) {
|
||||||
|
if (!event.data) {
|
||||||
|
return; // heartbeat/keep-alive, nothing to do
|
||||||
}
|
}
|
||||||
// SSE is going to fetch only a few spots at a time, so now we include QRZ/HamQTH credentials because the delay won't be significant.
|
|
||||||
evtSource = new EventSource('/api/v1/spots/stream' + buildQueryString(true));
|
|
||||||
|
|
||||||
evtSource.onmessage = function (event) {
|
|
||||||
const newSpot = JSON.parse(event.data);
|
const newSpot = JSON.parse(event.data);
|
||||||
const key = spotKey(newSpot);
|
const key = spotKey(newSpot);
|
||||||
|
|
||||||
@@ -95,15 +109,12 @@ function startSSEConnection() {
|
|||||||
// Add to data store and map
|
// Add to data store and map
|
||||||
spots.unshift(newSpot);
|
spots.unshift(newSpot);
|
||||||
addSpotToMap(newSpot);
|
addSpotToMap(newSpot);
|
||||||
};
|
},
|
||||||
|
onerror(err) {
|
||||||
evtSource.onerror = function () {
|
console.error('SSE error:', err);
|
||||||
if (evtSource != null) {
|
return 1000;
|
||||||
evtSource.close();
|
|
||||||
}
|
}
|
||||||
clearTimeout(restartSSEOnErrorTimeoutId);
|
});
|
||||||
restartSSEOnErrorTimeoutId = setTimeout(startSSEConnection, 1000);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove spots from the map that are older than the selected max age.
|
// Remove spots from the map that are older than the selected max age.
|
||||||
@@ -603,8 +614,8 @@ function displayIntroBox() {
|
|||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
// Close SSE connection cleanly when navigating away
|
// Close SSE connection cleanly when navigating away
|
||||||
window.addEventListener('beforeunload', function () {
|
window.addEventListener('beforeunload', function () {
|
||||||
if (evtSource != null) {
|
if (sseAbortController != null) {
|
||||||
evtSource.close();
|
sseAbortController.abort();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+26
-20
@@ -1,26 +1,25 @@
|
|||||||
// SSE event source
|
// SSE event source
|
||||||
let evtSource;
|
let sseAbortController = null;
|
||||||
let restartSSEOnErrorTimeoutId;
|
|
||||||
// Table row count, to alternate shading
|
// Table row count, to alternate shading
|
||||||
let rowCount = 0;
|
let rowCount = 0;
|
||||||
|
|
||||||
// Set up a listener to close the SSE connection nicely when we navigate away from the page, to prevent console errors
|
// Set up a listener to close the SSE connection nicely when we navigate away from the page, to prevent console errors
|
||||||
// and keep things nice and tidy for the server.
|
// and keep things nice and tidy for the server.
|
||||||
window.addEventListener('beforeunload', function () {
|
window.addEventListener('beforeunload', function () {
|
||||||
if (evtSource != null) {
|
if (sseAbortController != null) {
|
||||||
evtSource.close();
|
sseAbortController.abort();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Load spots and populate the table.
|
// Load spots and populate the table.
|
||||||
function loadSpots() {
|
function loadSpots() {
|
||||||
// If we have an ongoing SSE connection, stop it so it doesn't interfere with our reload
|
// If we have an ongoing SSE connection, stop it so it doesn't interfere with our reload
|
||||||
if (evtSource != null) {
|
if (sseAbortController != null) {
|
||||||
evtSource.close();
|
sseAbortController.abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make the new query
|
// Make the new query. No credential headers on the first load to keep things quick
|
||||||
$.ajax({url: '/api/v2/spots' + buildQueryString(), dataType: 'json', headers: getCredentialHeaders(), success: function (jsonData) {
|
$.ajax({url: '/api/v2/spots' + buildQueryString(), dataType: 'json', success: function (jsonData) {
|
||||||
// Store data
|
// Store data
|
||||||
spots = jsonData;
|
spots = jsonData;
|
||||||
// Update table
|
// Update table
|
||||||
@@ -36,12 +35,22 @@ function loadSpots() {
|
|||||||
// Start an SSE connection (closing an existing one if it exists). This will then be used to add to the table on the
|
// Start an SSE connection (closing an existing one if it exists). This will then be used to add to the table on the
|
||||||
// fly.
|
// fly.
|
||||||
function startSSEConnection() {
|
function startSSEConnection() {
|
||||||
if (evtSource != null) {
|
if (sseAbortController != null) {
|
||||||
evtSource.close();
|
sseAbortController.abort();
|
||||||
|
}
|
||||||
|
sseAbortController = new AbortController();
|
||||||
|
|
||||||
|
// SSE is going to fetch only a few spots at a time, so now we include QRZ/HamQTH credentials because the delay won't be significant.
|
||||||
|
fetchEventSource('/api/v2/spots/stream' + buildQueryString(), {
|
||||||
|
headers: getCredentialHeaders(),
|
||||||
|
signal: sseAbortController.signal,
|
||||||
|
openWhenHidden: true,
|
||||||
|
|
||||||
|
onmessage(event) {
|
||||||
|
if (!event.data) {
|
||||||
|
return; // heartbeat/keep-alive, nothing to do
|
||||||
}
|
}
|
||||||
evtSource = new EventSource('/api/v2/spots/stream' + buildQueryString());
|
|
||||||
|
|
||||||
evtSource.onmessage = function (event) {
|
|
||||||
// Get the new spot
|
// Get the new spot
|
||||||
const newSpot = JSON.parse(event.data);
|
const newSpot = JSON.parse(event.data);
|
||||||
// Awful fudge to ensure new incoming spots at the top of the list don't have timestamps that make them look
|
// Awful fudge to ensure new incoming spots at the top of the list don't have timestamps that make them look
|
||||||
@@ -74,15 +83,12 @@ function startSSEConnection() {
|
|||||||
if ($("#pingOnNewSpots")[0].checked) {
|
if ($("#pingOnNewSpots")[0].checked) {
|
||||||
new Audio("/audio/ping.mp3").play();
|
new Audio("/audio/ping.mp3").play();
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
onerror(err) {
|
||||||
evtSource.onerror = function () {
|
console.error('SSE error:', err);
|
||||||
if (evtSource != null) {
|
return 1000;
|
||||||
evtSource.close();
|
|
||||||
}
|
}
|
||||||
clearTimeout(restartSSEOnErrorTimeoutId)
|
});
|
||||||
restartSSEOnErrorTimeoutId = setTimeout(startSSEConnection, 1000);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build a query string for the API, based on the filters that the user has selected.
|
// Build a query string for the API, based on the filters that the user has selected.
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
var __rest = (this && this.__rest) || function (s, e) {
|
||||||
|
var t = {};
|
||||||
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
||||||
|
t[p] = s[p];
|
||||||
|
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
||||||
|
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
||||||
|
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
||||||
|
t[p[i]] = s[p[i]];
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
};
|
||||||
|
import { getBytes, getLines, getMessages } from './parse.js';
|
||||||
|
export const EventStreamContentType = 'text/event-stream';
|
||||||
|
const DefaultRetryInterval = 1000;
|
||||||
|
const LastEventId = 'last-event-id';
|
||||||
|
export function fetchEventSource(input, _a) {
|
||||||
|
var { signal: inputSignal, headers: inputHeaders, onopen: inputOnOpen, onmessage, onclose, onerror, openWhenHidden, fetch: inputFetch } = _a, rest = __rest(_a, ["signal", "headers", "onopen", "onmessage", "onclose", "onerror", "openWhenHidden", "fetch"]);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const headers = Object.assign({}, inputHeaders);
|
||||||
|
if (!headers.accept) {
|
||||||
|
headers.accept = EventStreamContentType;
|
||||||
|
}
|
||||||
|
let curRequestController;
|
||||||
|
function onVisibilityChange() {
|
||||||
|
curRequestController.abort();
|
||||||
|
if (!document.hidden) {
|
||||||
|
create();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!openWhenHidden) {
|
||||||
|
document.addEventListener('visibilitychange', onVisibilityChange);
|
||||||
|
}
|
||||||
|
let retryInterval = DefaultRetryInterval;
|
||||||
|
let retryTimer = 0;
|
||||||
|
function dispose() {
|
||||||
|
document.removeEventListener('visibilitychange', onVisibilityChange);
|
||||||
|
window.clearTimeout(retryTimer);
|
||||||
|
curRequestController.abort();
|
||||||
|
}
|
||||||
|
inputSignal === null || inputSignal === void 0 ? void 0 : inputSignal.addEventListener('abort', () => {
|
||||||
|
dispose();
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
const fetch = inputFetch !== null && inputFetch !== void 0 ? inputFetch : window.fetch;
|
||||||
|
const onopen = inputOnOpen !== null && inputOnOpen !== void 0 ? inputOnOpen : defaultOnOpen;
|
||||||
|
async function create() {
|
||||||
|
var _a;
|
||||||
|
curRequestController = new AbortController();
|
||||||
|
try {
|
||||||
|
const response = await fetch(input, Object.assign(Object.assign({}, rest), { headers, signal: curRequestController.signal }));
|
||||||
|
await onopen(response);
|
||||||
|
await getBytes(response.body, getLines(getMessages(id => {
|
||||||
|
if (id) {
|
||||||
|
headers[LastEventId] = id;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
delete headers[LastEventId];
|
||||||
|
}
|
||||||
|
}, retry => {
|
||||||
|
retryInterval = retry;
|
||||||
|
}, onmessage)));
|
||||||
|
onclose === null || onclose === void 0 ? void 0 : onclose();
|
||||||
|
dispose();
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
if (!curRequestController.signal.aborted) {
|
||||||
|
try {
|
||||||
|
const interval = (_a = onerror === null || onerror === void 0 ? void 0 : onerror(err)) !== null && _a !== void 0 ? _a : retryInterval;
|
||||||
|
window.clearTimeout(retryTimer);
|
||||||
|
retryTimer = window.setTimeout(create, interval);
|
||||||
|
}
|
||||||
|
catch (innerErr) {
|
||||||
|
dispose();
|
||||||
|
reject(innerErr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
create();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function defaultOnOpen(response) {
|
||||||
|
const contentType = response.headers.get('content-type');
|
||||||
|
if (!(contentType === null || contentType === void 0 ? void 0 : contentType.startsWith(EventStreamContentType))) {
|
||||||
|
throw new Error(`Expected content-type to be ${EventStreamContentType}, Actual: ${contentType}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=fetch.js.map
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export { fetchEventSource, EventStreamContentType } from './fetch.js';
|
||||||
|
//# sourceMappingURL=index.js.map
|
||||||
+110
@@ -0,0 +1,110 @@
|
|||||||
|
export async function getBytes(stream, onChunk) {
|
||||||
|
const reader = stream.getReader();
|
||||||
|
let result;
|
||||||
|
while (!(result = await reader.read()).done) {
|
||||||
|
onChunk(result.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function getLines(onLine) {
|
||||||
|
let buffer;
|
||||||
|
let position;
|
||||||
|
let fieldLength;
|
||||||
|
let discardTrailingNewline = false;
|
||||||
|
return function onChunk(arr) {
|
||||||
|
if (buffer === undefined) {
|
||||||
|
buffer = arr;
|
||||||
|
position = 0;
|
||||||
|
fieldLength = -1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buffer = concat(buffer, arr);
|
||||||
|
}
|
||||||
|
const bufLength = buffer.length;
|
||||||
|
let lineStart = 0;
|
||||||
|
while (position < bufLength) {
|
||||||
|
if (discardTrailingNewline) {
|
||||||
|
if (buffer[position] === 10) {
|
||||||
|
lineStart = ++position;
|
||||||
|
}
|
||||||
|
discardTrailingNewline = false;
|
||||||
|
}
|
||||||
|
let lineEnd = -1;
|
||||||
|
for (; position < bufLength && lineEnd === -1; ++position) {
|
||||||
|
switch (buffer[position]) {
|
||||||
|
case 58:
|
||||||
|
if (fieldLength === -1) {
|
||||||
|
fieldLength = position - lineStart;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
discardTrailingNewline = true;
|
||||||
|
case 10:
|
||||||
|
lineEnd = position;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lineEnd === -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
onLine(buffer.subarray(lineStart, lineEnd), fieldLength);
|
||||||
|
lineStart = position;
|
||||||
|
fieldLength = -1;
|
||||||
|
}
|
||||||
|
if (lineStart === bufLength) {
|
||||||
|
buffer = undefined;
|
||||||
|
}
|
||||||
|
else if (lineStart !== 0) {
|
||||||
|
buffer = buffer.subarray(lineStart);
|
||||||
|
position -= lineStart;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export function getMessages(onId, onRetry, onMessage) {
|
||||||
|
let message = newMessage();
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
return function onLine(line, fieldLength) {
|
||||||
|
if (line.length === 0) {
|
||||||
|
onMessage === null || onMessage === void 0 ? void 0 : onMessage(message);
|
||||||
|
message = newMessage();
|
||||||
|
}
|
||||||
|
else if (fieldLength > 0) {
|
||||||
|
const field = decoder.decode(line.subarray(0, fieldLength));
|
||||||
|
const valueOffset = fieldLength + (line[fieldLength + 1] === 32 ? 2 : 1);
|
||||||
|
const value = decoder.decode(line.subarray(valueOffset));
|
||||||
|
switch (field) {
|
||||||
|
case 'data':
|
||||||
|
message.data = message.data
|
||||||
|
? message.data + '\n' + value
|
||||||
|
: value;
|
||||||
|
break;
|
||||||
|
case 'event':
|
||||||
|
message.event = value;
|
||||||
|
break;
|
||||||
|
case 'id':
|
||||||
|
onId(message.id = value);
|
||||||
|
break;
|
||||||
|
case 'retry':
|
||||||
|
const retry = parseInt(value, 10);
|
||||||
|
if (!isNaN(retry)) {
|
||||||
|
onRetry(message.retry = retry);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function concat(a, b) {
|
||||||
|
const res = new Uint8Array(a.length + b.length);
|
||||||
|
res.set(a);
|
||||||
|
res.set(b, a.length);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
function newMessage() {
|
||||||
|
return {
|
||||||
|
data: '',
|
||||||
|
event: '',
|
||||||
|
id: '',
|
||||||
|
retry: undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=parse.js.map
|
||||||
@@ -76,7 +76,7 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/static/js/add-spot.js?v=1786726755"></script>
|
<script src="/static/js/add-spot.js?v=1786747642"></script>
|
||||||
<script>$(document).ready(function () {
|
<script>$(document).ready(function () {
|
||||||
$("#nav-link-add-spot").addClass("active");
|
$("#nav-link-add-spot").addClass("active");
|
||||||
}); <!-- highlight active page in nav --></script>
|
}); <!-- highlight active page in nav --></script>
|
||||||
|
|||||||
@@ -82,7 +82,7 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/static/js/alerts.js?v=1786726755"></script>
|
<script src="/static/js/alerts.js?v=1786747642"></script>
|
||||||
<script>$(document).ready(function () {
|
<script>$(document).ready(function () {
|
||||||
$("#nav-link-alerts").addClass("active");
|
$("#nav-link-alerts").addClass("active");
|
||||||
}); <!-- highlight active page in nav --></script>
|
}); <!-- highlight active page in nav --></script>
|
||||||
|
|||||||
@@ -79,8 +79,8 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/static/js/spotsbandsandmap.js?v=1786726755"></script>
|
<script src="/static/js/spotsbandsandmap.js?v=1786747642"></script>
|
||||||
<script src="/static/js/bands.js?v=1786726755"></script>
|
<script src="/static/js/bands.js?v=1786747642"></script>
|
||||||
<script>$(document).ready(function () {
|
<script>$(document).ready(function () {
|
||||||
$("#nav-link-bands").addClass("active");
|
$("#nav-link-bands").addClass("active");
|
||||||
}); <!-- highlight active page in nav --></script>
|
}); <!-- highlight active page in nav --></script>
|
||||||
|
|||||||
+10
-5
@@ -1,6 +1,6 @@
|
|||||||
{% extends "skeleton.html" %}
|
{% extends "skeleton.html" %}
|
||||||
{% block head_extra %}
|
{% block head_extra %}
|
||||||
<link rel="stylesheet" href="/static/css/style.css?v=1786726755" type="text/css">
|
<link rel="stylesheet" href="/static/css/style.css?v=1786747642" type="text/css">
|
||||||
<link href="/static/vendor/css/bootstrap-5.3.8.min.css" rel="stylesheet">
|
<link href="/static/vendor/css/bootstrap-5.3.8.min.css" rel="stylesheet">
|
||||||
<link href="/static/vendor/css/fontawesome-6.7.2.min.css" rel="stylesheet">
|
<link href="/static/vendor/css/fontawesome-6.7.2.min.css" rel="stylesheet">
|
||||||
<link href="/static/vendor/css/solid-6.7.2.min.css" rel="stylesheet">
|
<link href="/static/vendor/css/solid-6.7.2.min.css" rel="stylesheet">
|
||||||
@@ -10,10 +10,15 @@
|
|||||||
<script src="/static/vendor/js/bootstrap-5.3.8.bundle.min.js"></script>
|
<script src="/static/vendor/js/bootstrap-5.3.8.bundle.min.js"></script>
|
||||||
<script src="/static/vendor/js/tinycolor2-1.6.0.min.js"></script>
|
<script src="/static/vendor/js/tinycolor2-1.6.0.min.js"></script>
|
||||||
|
|
||||||
<script src="/static/js/utils.js?v=1786726755"></script>
|
<script type="module">
|
||||||
<script src="/static/js/ui-ham.js?v=1786726755"></script>
|
import { fetchEventSource } from '/static/vendor/js/fetch-event-source-2.0.1/index.js';
|
||||||
<script src="/static/js/geo.js?v=1786726755"></script>
|
window.fetchEventSource = fetchEventSource;
|
||||||
<script src="/static/js/common.js?v=1786726755"></script>
|
</script>
|
||||||
|
|
||||||
|
<script src="/static/js/utils.js?v=1786747642"></script>
|
||||||
|
<script src="/static/js/ui-ham.js?v=1786747642"></script>
|
||||||
|
<script src="/static/js/geo.js?v=1786747642"></script>
|
||||||
|
<script src="/static/js/common.js?v=1786747642"></script>
|
||||||
{% end %}
|
{% end %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|||||||
@@ -284,7 +284,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/static/vendor/js/chart-4.4.9.umd.min.js"></script>
|
<script src="/static/vendor/js/chart-4.4.9.umd.min.js"></script>
|
||||||
<script src="/static/js/conditions.js?v=1786726755"></script>
|
<script src="/static/js/conditions.js?v=1786747642"></script>
|
||||||
<script>$(document).ready(function () {
|
<script>$(document).ready(function () {
|
||||||
$("#nav-link-conditions").addClass("active");
|
$("#nav-link-conditions").addClass("active");
|
||||||
}); <!-- highlight active page in nav --></script>
|
}); <!-- highlight active page in nav --></script>
|
||||||
|
|||||||
+2
-2
@@ -112,8 +112,8 @@
|
|||||||
<script src="/static/vendor/js/leaflet-cqzones.js"></script>
|
<script src="/static/vendor/js/leaflet-cqzones.js"></script>
|
||||||
<script src="/static/vendor/js/leaflet-workedallbritainireland.js" type="module"></script>
|
<script src="/static/vendor/js/leaflet-workedallbritainireland.js" type="module"></script>
|
||||||
|
|
||||||
<script src="/static/js/spotsbandsandmap.js?v=1786726755"></script>
|
<script src="/static/js/spotsbandsandmap.js?v=1786747642"></script>
|
||||||
<script src="/static/js/map.js?v=1786726755"></script>
|
<script src="/static/js/map.js?v=1786747642"></script>
|
||||||
<script>$(document).ready(function () {
|
<script>$(document).ready(function () {
|
||||||
$("#nav-link-map").addClass("active");
|
$("#nav-link-map").addClass("active");
|
||||||
}); <!-- highlight active page in nav --></script>
|
}); <!-- highlight active page in nav --></script>
|
||||||
|
|||||||
@@ -118,8 +118,8 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/static/js/spotsbandsandmap.js?v=1786726755"></script>
|
<script src="/static/js/spotsbandsandmap.js?v=1786747642"></script>
|
||||||
<script src="/static/js/spots.js?v=1786726755"></script>
|
<script src="/static/js/spots.js?v=1786747642"></script>
|
||||||
<script>$(document).ready(function () {
|
<script>$(document).ready(function () {
|
||||||
$("#nav-link-spots").addClass("active");
|
$("#nav-link-spots").addClass("active");
|
||||||
}); <!-- highlight active page in nav --></script>
|
}); <!-- highlight active page in nav --></script>
|
||||||
|
|||||||
@@ -86,7 +86,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/static/js/status.js?v=1786726755"></script>
|
<script src="/static/js/status.js?v=1786747642"></script>
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
$("#nav-link-status").addClass("active");
|
$("#nav-link-status").addClass("active");
|
||||||
|
|||||||
Reference in New Issue
Block a user