mirror of
https://github.com/KubaPro010/fm-dx-webserver.git
synced 2026-07-30 00:39:16 +02:00
offline optimization, new signal chart, ui optimization
This commit is contained in:
+55
-30
@@ -84,16 +84,20 @@ function handleConnect(clientIp, currentUsers, ws, callback) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}).on("error", (err) => {
|
}).on("error", (err) => {
|
||||||
console.error("Error fetching location data:", err);
|
consoleCmd.logError("Error fetching location data:", err.code);
|
||||||
callback("User allowed");
|
callback("User allowed");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function processConnection(clientIp, locationInfo, currentUsers, ws, callback) {
|
let bannedASCache = { data: null, timestamp: 0 };
|
||||||
const options = { year: "numeric", month: "numeric", day: "numeric", hour: "2-digit", minute: "2-digit" };
|
|
||||||
const connectionTime = new Date().toLocaleString([], options);
|
|
||||||
|
|
||||||
https.get("https://fmdx.org/banned_as.json", (banResponse) => {
|
function fetchBannedAS(callback) {
|
||||||
|
const now = Date.now();
|
||||||
|
if (bannedASCache.data && now - bannedASCache.timestamp < 10 * 60 * 1000) {
|
||||||
|
return callback(null, bannedASCache.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
const req = https.get("https://fmdx.org/banned_as.json", { family: 4 }, (banResponse) => {
|
||||||
let banData = "";
|
let banData = "";
|
||||||
|
|
||||||
banResponse.on("data", (chunk) => {
|
banResponse.on("data", (chunk) => {
|
||||||
@@ -103,39 +107,60 @@ function processConnection(clientIp, locationInfo, currentUsers, ws, callback) {
|
|||||||
banResponse.on("end", () => {
|
banResponse.on("end", () => {
|
||||||
try {
|
try {
|
||||||
const bannedAS = JSON.parse(banData).banned_as || [];
|
const bannedAS = JSON.parse(banData).banned_as || [];
|
||||||
|
bannedASCache = { data: bannedAS, timestamp: now };
|
||||||
if (bannedAS.some((as) => locationInfo.as?.includes(as))) {
|
callback(null, bannedAS);
|
||||||
return callback("User banned");
|
|
||||||
}
|
|
||||||
|
|
||||||
const userLocation =
|
|
||||||
locationInfo.country === undefined
|
|
||||||
? "Unknown"
|
|
||||||
: `${locationInfo.city}, ${locationInfo.regionName}, ${locationInfo.countryCode}`;
|
|
||||||
|
|
||||||
storage.connectedUsers.push({
|
|
||||||
ip: clientIp,
|
|
||||||
location: userLocation,
|
|
||||||
time: connectionTime,
|
|
||||||
instance: ws,
|
|
||||||
});
|
|
||||||
|
|
||||||
consoleCmd.logInfo(
|
|
||||||
`Web client \x1b[32mconnected\x1b[0m (${clientIp}) \x1b[90m[${currentUsers}]\x1b[0m Location: ${userLocation}`
|
|
||||||
);
|
|
||||||
|
|
||||||
callback("User allowed");
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error parsing banned AS list:", error);
|
console.error("Error parsing banned AS list:", error);
|
||||||
callback("User allowed");
|
callback(null, []); // Default to allowing user
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}).on("error", (err) => {
|
});
|
||||||
|
|
||||||
|
// Set timeout for the request (5 seconds)
|
||||||
|
req.setTimeout(5000, () => {
|
||||||
|
console.error("Error: Request timed out while fetching banned AS list.");
|
||||||
|
req.abort();
|
||||||
|
callback(null, []); // Default to allowing user
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on("error", (err) => {
|
||||||
console.error("Error fetching banned AS list:", err);
|
console.error("Error fetching banned AS list:", err);
|
||||||
callback("User allowed");
|
callback(null, []); // Default to allowing user
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function processConnection(clientIp, locationInfo, currentUsers, ws, callback) {
|
||||||
|
const options = { year: "numeric", month: "numeric", day: "numeric", hour: "2-digit", minute: "2-digit" };
|
||||||
|
const connectionTime = new Date().toLocaleString([], options);
|
||||||
|
|
||||||
|
fetchBannedAS((error, bannedAS) => {
|
||||||
|
if (error) {
|
||||||
|
console.error("Error fetching banned AS list:", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bannedAS.some((as) => locationInfo.as?.includes(as))) {
|
||||||
|
return callback("User banned");
|
||||||
|
}
|
||||||
|
|
||||||
|
const userLocation =
|
||||||
|
locationInfo.country === undefined
|
||||||
|
? "Unknown"
|
||||||
|
: `${locationInfo.city}, ${locationInfo.regionName}, ${locationInfo.countryCode}`;
|
||||||
|
|
||||||
|
storage.connectedUsers.push({
|
||||||
|
ip: clientIp,
|
||||||
|
location: userLocation,
|
||||||
|
time: connectionTime,
|
||||||
|
instance: ws,
|
||||||
|
});
|
||||||
|
|
||||||
|
consoleCmd.logInfo(
|
||||||
|
`Web client \x1b[32mconnected\x1b[0m (${clientIp}) \x1b[90m[${currentUsers}]\x1b[0m Location: ${userLocation}`
|
||||||
|
);
|
||||||
|
|
||||||
|
callback("User allowed");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function formatUptime(uptimeInSeconds) {
|
function formatUptime(uptimeInSeconds) {
|
||||||
const secondsInMinute = 60;
|
const secondsInMinute = 60;
|
||||||
|
|||||||
+3
-5
@@ -469,11 +469,9 @@ wss.on('connection', (ws, request) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ws.on('close', (code, reason) => {
|
ws.on('close', (code, reason) => {
|
||||||
if (clientIp !== '::ffff:127.0.0.1' ||
|
if (clientIp !== '::ffff:127.0.0.1' || (request.connection && request.connection.remoteAddress && request.connection.remoteAddress !== '::ffff:127.0.0.1') || (request.headers && request.headers['origin'] && request.headers['origin'].trim() !== '')) {
|
||||||
(request.connection && request.connection.remoteAddress && request.connection.remoteAddress !== '::ffff:127.0.1') ||
|
currentUsers--;
|
||||||
(request.headers && request.headers['origin'] && request.headers['origin'].trim() !== '')) {
|
}
|
||||||
currentUsers--;
|
|
||||||
}
|
|
||||||
dataHandler.showOnlineUsers(currentUsers);
|
dataHandler.showOnlineUsers(currentUsers);
|
||||||
|
|
||||||
const index = storage.connectedUsers.findIndex(user => user.ip === clientIp);
|
const index = storage.connectedUsers.findIndex(user => user.ip === clientIp);
|
||||||
|
|||||||
+39
-35
@@ -13,8 +13,14 @@ let usStatesGeoJson = null; // To cache the GeoJSON data for US states
|
|||||||
// Load the US states GeoJSON data
|
// Load the US states GeoJSON data
|
||||||
async function loadUsStatesGeoJson() {
|
async function loadUsStatesGeoJson() {
|
||||||
if (!usStatesGeoJson) {
|
if (!usStatesGeoJson) {
|
||||||
const response = await fetch(usStatesGeoJsonUrl);
|
try {
|
||||||
usStatesGeoJson = await response.json();
|
const response = await fetch(usStatesGeoJsonUrl);
|
||||||
|
if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
|
||||||
|
usStatesGeoJson = await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to load US States GeoJSON:", error);
|
||||||
|
usStatesGeoJson = null; // Ensure it's null so it can retry later
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,18 +85,17 @@ async function fetchTx(freq, piCode, rdsPs) {
|
|||||||
|
|
||||||
const url = "https://maps.fmdx.org/api/?freq=" + freq;
|
const url = "https://maps.fmdx.org/api/?freq=" + freq;
|
||||||
|
|
||||||
return fetch(url, {
|
try {
|
||||||
redirect: 'manual'
|
const response = await fetch(url, { redirect: 'manual' });
|
||||||
})
|
if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
|
||||||
.then(response => response.json())
|
const data = await response.json();
|
||||||
.then(async (data) => {
|
cachedData[freq] = data;
|
||||||
cachedData[freq] = data;
|
await loadUsStatesGeoJson();
|
||||||
await loadUsStatesGeoJson();
|
return processData(data, piCode, rdsPs);
|
||||||
return processData(data, piCode, rdsPs);
|
} catch (error) {
|
||||||
})
|
console.error("Error fetching data:", error);
|
||||||
.catch(error => {
|
return null; // Return null to indicate failure
|
||||||
console.error("Error fetching data:", error);
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processData(data, piCode, rdsPs) {
|
async function processData(data, piCode, rdsPs) {
|
||||||
@@ -182,29 +187,28 @@ function checkEs() {
|
|||||||
let esSwitch = false;
|
let esSwitch = false;
|
||||||
|
|
||||||
if (now - esSwitchCache.lastCheck < esFetchInterval) {
|
if (now - esSwitchCache.lastCheck < esFetchInterval) {
|
||||||
esSwitch = esSwitchCache.esSwitch;
|
return esSwitchCache.esSwitch;
|
||||||
} else if (serverConfig.identification.lat > 20) {
|
|
||||||
esSwitchCache.lastCheck = now;
|
|
||||||
fetch(url)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
if (serverConfig.identification.lon < -32) {
|
|
||||||
if (data.north_america.max_frequency != "No data") {
|
|
||||||
esSwitch = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (data.europe.max_frequency != "No data") {
|
|
||||||
esSwitch = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
esSwitchCache.esSwitch = esSwitch;
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error("Error fetching data:", error);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return esSwitch;
|
if (serverConfig.identification.lat > 20) {
|
||||||
|
esSwitchCache.lastCheck = now;
|
||||||
|
fetch(url)
|
||||||
|
.then(response => {
|
||||||
|
if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
if ((serverConfig.identification.lon < -32 && data.north_america.max_frequency !== "No data") ||
|
||||||
|
(serverConfig.identification.lon >= -32 && data.europe.max_frequency !== "No data")) {
|
||||||
|
esSwitchCache.esSwitch = true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error("Error fetching Es data:", error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return esSwitchCache.esSwitch;
|
||||||
}
|
}
|
||||||
|
|
||||||
function haversine(lat1, lon1, lat2, lon2) {
|
function haversine(lat1, lon1, lat2, lon2) {
|
||||||
|
|||||||
+15
-2
@@ -22,6 +22,15 @@ h1#tuner-name {
|
|||||||
transition: 0.3s ease color;
|
transition: 0.3s ease color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#tuner-name i {
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tuner-name i.rotated {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
h1#tuner-name:hover {
|
h1#tuner-name:hover {
|
||||||
color: var(--color-main-bright);
|
color: var(--color-main-bright);
|
||||||
}
|
}
|
||||||
@@ -75,9 +84,13 @@ label {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.canvas-container {
|
.canvas-container {
|
||||||
width: 100%;
|
width: calc(100% - 20px);
|
||||||
height: 175px;
|
height: 140px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
border-radius: 15px;
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
#data-ant {
|
#data-ant {
|
||||||
|
|||||||
Vendored
+9
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+17
-29
@@ -4,8 +4,12 @@
|
|||||||
<title><%= tunerName %> - FM-DX Webserver</title>
|
<title><%= tunerName %> - FM-DX Webserver</title>
|
||||||
<link href="css/entry.css" type="text/css" rel="stylesheet">
|
<link href="css/entry.css" type="text/css" rel="stylesheet">
|
||||||
<link href="css/flags.min.css" type="text/css" rel="stylesheet">
|
<link href="css/flags.min.css" type="text/css" rel="stylesheet">
|
||||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css" type="text/css" rel="stylesheet">
|
<link href="css/libs/fontawesome.css" type="text/css" rel="stylesheet">
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js" integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
<script src="js/libs/jquery.min.js"></script>
|
||||||
|
<script src="js/libs/chart.umd.min.js"></script>
|
||||||
|
<script src="js/libs/luxon.min.js"></script>
|
||||||
|
<script src="js/libs/chartjs-adapter-luxon.umd.min.js"></script>
|
||||||
|
<script src="js/libs/chartjs-plugin-streaming.min.js"></script>
|
||||||
|
|
||||||
<link rel="icon" type="image/png" href="favicon.png" />
|
<link rel="icon" type="image/png" href="favicon.png" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
@@ -16,7 +20,7 @@
|
|||||||
<meta property="og:description" content="Server description: <%= tunerDescMeta %>.">
|
<meta property="og:description" content="Server description: <%= tunerDescMeta %>.">
|
||||||
|
|
||||||
<script src="js/init.js"></script>
|
<script src="js/init.js"></script>
|
||||||
|
|
||||||
<!-- 3LAS Scripts for Audio streaming -->
|
<!-- 3LAS Scripts for Audio streaming -->
|
||||||
<script src="js/3las/util/3las.helpers.js"></script>
|
<script src="js/3las/util/3las.helpers.js"></script>
|
||||||
<script src="js/3las/util/3las.logging.js"></script>
|
<script src="js/3las/util/3las.logging.js"></script>
|
||||||
@@ -39,7 +43,7 @@
|
|||||||
<div class="wrapper-outer dashboard-panel" style="padding-top: 20px; z-index: 10; position: relative;">
|
<div class="wrapper-outer dashboard-panel" style="padding-top: 20px; z-index: 10; position: relative;">
|
||||||
<div class="panel-100-real m-0 flex-container bg-phone flex-phone-column" style="min-height: 64px; max-width: 1160px; margin-top: 10px;align-items: center; justify-content: space-between; padding-left: 20px;padding-right: 10px;">
|
<div class="panel-100-real m-0 flex-container bg-phone flex-phone-column" style="min-height: 64px; max-width: 1160px; margin-top: 10px;align-items: center; justify-content: space-between; padding-left: 20px;padding-right: 10px;">
|
||||||
<h1 id="tuner-name" class="text-left flex-container flex-phone flex-center" style="padding-bottom: 3px; padding-right: 5px;height: 64px;">
|
<h1 id="tuner-name" class="text-left flex-container flex-phone flex-center" style="padding-bottom: 3px; padding-right: 5px;height: 64px;">
|
||||||
<span class="text-200-px" style="max-width: 450px;"><%= tunerName %></span> <i class="fa-solid fa-chevron-down p-left-10" style="font-size: 15px;"></i>
|
<span class="text-200-px" style="max-width: 450px;padding-right: 10px;"><%= tunerName %></span> <i class="fa-solid fa-chevron-down" style="font-size: 15px;"></i>
|
||||||
</h1>
|
</h1>
|
||||||
<% if(!publicTuner || tunerLock) { %>
|
<% if(!publicTuner || tunerLock) { %>
|
||||||
<div class="tuner-status p-10 color-3">
|
<div class="tuner-status p-10 color-3">
|
||||||
@@ -109,27 +113,16 @@
|
|||||||
<div style="width: 1px;background: var(--color-2);" class="m-10 hide-phone"></div>
|
<div style="width: 1px;background: var(--color-2);" class="m-10 hide-phone"></div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
<% const presets = [1, 2, 3, 4]; %>
|
||||||
|
|
||||||
<div style="height: 64px;" class="flex-center flex-phone">
|
<div style="height: 64px;" class="flex-center flex-phone">
|
||||||
<button class="no-bg color-4 hover-brighten" id="preset1" style="padding: 6px; width: 64px; min-width: 64px;">
|
<% presets.forEach(preset => { %>
|
||||||
<i class="fa-solid fa-wave-square fa-lg top-10"></i><br>
|
<button class="no-bg color-4 hover-brighten" id="preset<%= preset %>" style="padding: 6px; width: 64px; min-width: 64px;">
|
||||||
<span style="font-size: 10px; color: var(--color-text);" id="preset1-text"></span>
|
<i class="fa-solid fa-wave-square fa-lg top-10"></i><br>
|
||||||
</button>
|
<span style="font-size: 10px; color: var(--color-text);" id="preset<%= preset %>-text"></span>
|
||||||
|
</button>
|
||||||
<button class="no-bg color-4 hover-brighten" id="preset2" style="padding: 6px; width: 64px; min-width: 64px;">
|
<% }); %>
|
||||||
<i class="fa-solid fa-wave-square fa-lg top-10"></i><br>
|
</div>
|
||||||
<span style="font-size: 10px; color: var(--color-text);" id="preset2-text"></span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="no-bg color-4 hover-brighten" id="preset3" style="padding: 6px; width: 64px; min-width: 64px;">
|
|
||||||
<i class="fa-solid fa-wave-square fa-lg top-10"></i><br>
|
|
||||||
<span style="font-size: 10px; color: var(--color-text);" id="preset3-text"></span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="no-bg color-4 hover-brighten" id="preset4" style="padding: 6px; width: 64px; min-width: 64px;">
|
|
||||||
<i class="fa-solid fa-wave-square fa-lg top-10"></i><br>
|
|
||||||
<span style="font-size: 10px; color: var(--color-text);" id="preset4-text"></span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex-container flex-phone" style="align-items: center; height: 64px;">
|
<div class="flex-container flex-phone" style="align-items: center; height: 64px;">
|
||||||
<div class="color-3 m-10 text-medium">
|
<div class="color-3 m-10 text-medium">
|
||||||
@@ -440,11 +433,6 @@
|
|||||||
<%- include('_components', {component: 'checkbox', cssClass: '', label: 'RDS PS Underscores', id: 'ps-underscores'}) %>
|
<%- include('_components', {component: 'checkbox', cssClass: '', label: 'RDS PS Underscores', id: 'ps-underscores'}) %>
|
||||||
<%- include('_components', {component: 'checkbox', cssClass: '', label: 'Imperial units', id: 'imperial-units'}) %>
|
<%- include('_components', {component: 'checkbox', cssClass: '', label: 'Imperial units', id: 'imperial-units'}) %>
|
||||||
|
|
||||||
<div class="form-group bottom-20 hide-desktop" style="float: none;">
|
|
||||||
<label for="users-online"><i class="fa-solid fa-user"></i> Users online</label>
|
|
||||||
<span class="users-online" name="users-online">0</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<% if (isAdminAuthenticated) { %>
|
<% if (isAdminAuthenticated) { %>
|
||||||
<p class="color-3">You are logged in as an adminstrator.</p>
|
<p class="color-3">You are logged in as an adminstrator.</p>
|
||||||
<div class="admin-quick-dashboard">
|
<div class="admin-quick-dashboard">
|
||||||
|
|||||||
@@ -46,3 +46,11 @@ function tuneTo(freq) {
|
|||||||
function resetRDS() {
|
function resetRDS() {
|
||||||
socket.send("T0");
|
socket.send("T0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getCurrentFreq() {
|
||||||
|
currentFreq = $('#data-frequency').text();
|
||||||
|
currentFreq = parseFloat(currentFreq).toFixed(3);
|
||||||
|
currentFreq = parseFloat(currentFreq);
|
||||||
|
|
||||||
|
return currentFreq;
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -59,7 +59,7 @@ $(document).ready(function() {
|
|||||||
chatNicknameSave.click(function() {
|
chatNicknameSave.click(function() {
|
||||||
const currentNickname = chatNicknameInput.val().trim() || `Anonymous User ${generateRandomString(5)}`;
|
const currentNickname = chatNicknameInput.val().trim() || `Anonymous User ${generateRandomString(5)}`;
|
||||||
localStorage.setItem('nickname', currentNickname);
|
localStorage.setItem('nickname', currentNickname);
|
||||||
savedNickname = currentNickname; // Update the savedNickname variable
|
savedNickname = currentNickname;
|
||||||
chatIdentityNickname.text(savedNickname);
|
chatIdentityNickname.text(savedNickname);
|
||||||
chatNicknameInput.blur();
|
chatNicknameInput.blur();
|
||||||
});
|
});
|
||||||
|
|||||||
+2
-2
@@ -1,9 +1,9 @@
|
|||||||
var currentDate = new Date('Feb 16, 2025 15:00:00');
|
var currentDate = new Date('Feb 23, 2025 15:30:00');
|
||||||
var day = currentDate.getDate();
|
var day = currentDate.getDate();
|
||||||
var month = currentDate.getMonth() + 1; // Months are zero-indexed, so add 1
|
var month = currentDate.getMonth() + 1; // Months are zero-indexed, so add 1
|
||||||
var year = currentDate.getFullYear();
|
var year = currentDate.getFullYear();
|
||||||
var formattedDate = day + '/' + month + '/' + year;
|
var formattedDate = day + '/' + month + '/' + year;
|
||||||
var currentVersion = 'v1.3.5 [' + formattedDate + ']';
|
var currentVersion = 'v1.3.6 [' + formattedDate + ']';
|
||||||
|
|
||||||
getInitialSettings();
|
getInitialSettings();
|
||||||
removeUrlParameters();
|
removeUrlParameters();
|
||||||
|
|||||||
Vendored
+20
File diff suppressed because one or more lines are too long
@@ -0,0 +1,7 @@
|
|||||||
|
/*!
|
||||||
|
* chartjs-adapter-luxon v1.3.1
|
||||||
|
* https://www.chartjs.org
|
||||||
|
* (c) 2023 chartjs-adapter-luxon Contributors
|
||||||
|
* Released under the MIT license
|
||||||
|
*/
|
||||||
|
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(require("chart.js"),require("luxon")):"function"==typeof define&&define.amd?define(["chart.js","luxon"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Chart,e.luxon)}(this,(function(e,t){"use strict";const n={datetime:t.DateTime.DATETIME_MED_WITH_SECONDS,millisecond:"h:mm:ss.SSS a",second:t.DateTime.TIME_WITH_SECONDS,minute:t.DateTime.TIME_SIMPLE,hour:{hour:"numeric"},day:{day:"numeric",month:"short"},week:"DD",month:{month:"short",year:"numeric"},quarter:"'Q'q - yyyy",year:{year:"numeric"}};e._adapters._date.override({_id:"luxon",_create:function(e){return t.DateTime.fromMillis(e,this.options)},init(e){this.options.locale||(this.options.locale=e.locale)},formats:function(){return n},parse:function(e,n){const i=this.options,r=typeof e;return null===e||"undefined"===r?null:("number"===r?e=this._create(e):"string"===r?e="string"==typeof n?t.DateTime.fromFormat(e,n,i):t.DateTime.fromISO(e,i):e instanceof Date?e=t.DateTime.fromJSDate(e,i):"object"!==r||e instanceof t.DateTime||(e=t.DateTime.fromObject(e,i)),e.isValid?e.valueOf():null)},format:function(e,t){const n=this._create(e);return"string"==typeof t?n.toFormat(t):n.toLocaleString(t)},add:function(e,t,n){const i={};return i[n]=t,this._create(e).plus(i).valueOf()},diff:function(e,t,n){return this._create(e).diff(this._create(t)).as(n).valueOf()},startOf:function(e,t,n){if("isoWeek"===t){n=Math.trunc(Math.min(Math.max(0,n),6));const t=this._create(e);return t.minus({days:(t.weekday-n+7)%7}).startOf("day").valueOf()}return t?this._create(e).startOf(t).valueOf():e},endOf:function(e,t){return this._create(e).endOf(t).valueOf()}})}));
|
||||||
+7
File diff suppressed because one or more lines are too long
Vendored
+2
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
+165
-175
@@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
|
|
||||||
var parsedData, signalChart, previousFreq;
|
var parsedData, signalChart, previousFreq;
|
||||||
var signalData = [];
|
|
||||||
var data = [];
|
var data = [];
|
||||||
|
var signalData = [];
|
||||||
let updateCounter = 0;
|
let updateCounter = 0;
|
||||||
let messageCounter = 0; // Count for WebSocket data length returning 0
|
let messageCounter = 0; // Count for WebSocket data length returning 0
|
||||||
let messageData = 800; // Initial value anything above 0
|
let messageData = 800; // Initial value anything above 0
|
||||||
@@ -32,7 +32,7 @@ const usa_programmes = [
|
|||||||
const rdsMode = localStorage.getItem('rdsMode');
|
const rdsMode = localStorage.getItem('rdsMode');
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
var canvas = $('#signal-canvas')[0];
|
const signalToggle = $("#signal-units-toggle");
|
||||||
|
|
||||||
var $panel = $('.admin-quick-dashboard');
|
var $panel = $('.admin-quick-dashboard');
|
||||||
var panelWidth = $panel.outerWidth();
|
var panelWidth = $panel.outerWidth();
|
||||||
@@ -47,12 +47,7 @@ $(document).ready(function () {
|
|||||||
$panel.css('left', -panelWidth);
|
$panel.css('left', -panelWidth);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
canvas.width = canvas.parentElement.clientWidth;
|
|
||||||
canvas.height = canvas.parentElement.clientHeight;
|
|
||||||
|
|
||||||
// Start updating the canvas
|
|
||||||
initCanvas();
|
|
||||||
fillPresets();
|
fillPresets();
|
||||||
|
|
||||||
signalToggle.on("change", function () {
|
signalToggle.on("change", function () {
|
||||||
@@ -205,7 +200,6 @@ $(document).ready(function () {
|
|||||||
$(freqContainer).on("click", function () {
|
$(freqContainer).on("click", function () {
|
||||||
textInput.focus();
|
textInput.focus();
|
||||||
});
|
});
|
||||||
initTooltips();
|
|
||||||
|
|
||||||
//FMLIST logging
|
//FMLIST logging
|
||||||
$('.popup-content').on('click', function(event) {
|
$('.popup-content').on('click', function(event) {
|
||||||
@@ -273,7 +267,9 @@ $(document).ready(function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
initCanvas();
|
||||||
|
initTooltips();
|
||||||
});
|
});
|
||||||
|
|
||||||
function getServerTime() {
|
function getServerTime() {
|
||||||
@@ -294,29 +290,21 @@ function getServerTime() {
|
|||||||
|
|
||||||
const serverOptions = {
|
const serverOptions = {
|
||||||
...options,
|
...options,
|
||||||
timeZone: 'Etc/UTC' // Add timeZone only for server time
|
timeZone: 'Etc/UTC'
|
||||||
};
|
};
|
||||||
|
|
||||||
const formattedServerTime = new Date(serverTimeUtc).toLocaleString(navigator.language ? navigator.language : 'en-US', serverOptions);
|
const formattedServerTime = new Date(serverTimeUtc).toLocaleString(navigator.language ? navigator.language : 'en-US', serverOptions);
|
||||||
|
|
||||||
$("#server-time").text(formattedServerTime);
|
$("#server-time").text(formattedServerTime);
|
||||||
|
|
||||||
// Get and format user's local time directly without specifying timeZone:
|
|
||||||
const localTime = new Date();
|
|
||||||
const formattedLocalTime = new Date(localTime).toLocaleString(navigator.language ? navigator.language : 'en-US', options);
|
|
||||||
|
|
||||||
// Display client time:
|
|
||||||
$("#client-time").text(formattedLocalTime);
|
|
||||||
},
|
},
|
||||||
error: function(jqXHR, textStatus, errorThrown) {
|
error: function(jqXHR, textStatus, errorThrown) {
|
||||||
console.error("Error fetching server time:", errorThrown);
|
console.error("Error fetching server time:", errorThrown);
|
||||||
// Handle error gracefully (e.g., display a fallback message)
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendPingRequest() {
|
function sendPingRequest() {
|
||||||
const timeoutDuration = 15000; // Ping response can become buggy if it exceeds 20 seconds
|
const timeoutDuration = 5000;
|
||||||
const startTime = new Date().getTime();
|
const startTime = new Date().getTime();
|
||||||
|
|
||||||
const fetchWithTimeout = (url, options, timeout = timeoutDuration) => {
|
const fetchWithTimeout = (url, options, timeout = timeoutDuration) => {
|
||||||
@@ -407,7 +395,6 @@ function sendPingRequest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Automatic UI resume on WebSocket reconnect
|
|
||||||
function handleWebSocketMessage(event) {
|
function handleWebSocketMessage(event) {
|
||||||
if (event.data == 'KICK') {
|
if (event.data == 'KICK') {
|
||||||
console.log('Kick initiated.')
|
console.log('Kick initiated.')
|
||||||
@@ -429,151 +416,161 @@ function handleWebSocketMessage(event) {
|
|||||||
// Attach the message handler
|
// Attach the message handler
|
||||||
socket.onmessage = handleWebSocketMessage;
|
socket.onmessage = handleWebSocketMessage;
|
||||||
|
|
||||||
function initCanvas(parsedData) {
|
const signalBuffer = [];
|
||||||
signalToggle = $("#signal-units-toggle");
|
|
||||||
|
|
||||||
// Check if signalChart is already initialized
|
|
||||||
if (!signalChart) {
|
|
||||||
const canvas = $('#signal-canvas')[0];
|
|
||||||
const context = canvas.getContext('2d');
|
|
||||||
const maxDataPoints = 300;
|
|
||||||
const pointWidth = (canvas.width - 80) / maxDataPoints;
|
|
||||||
|
|
||||||
|
|
||||||
signalChart = {
|
|
||||||
canvas,
|
|
||||||
context,
|
|
||||||
parsedData,
|
|
||||||
maxDataPoints,
|
|
||||||
pointWidth,
|
|
||||||
color2: null,
|
|
||||||
color3: null,
|
|
||||||
color4: null,
|
|
||||||
signalUnit: localStorage.getItem('signalUnit'),
|
|
||||||
offset: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
switch(signalChart.signalUnit) {
|
|
||||||
case 'dbuv': signalChart.offset = 11.25; break;
|
|
||||||
case 'dbm': signalChart.offset = 120; break;
|
|
||||||
default: signalChart.offset = 0;
|
|
||||||
}
|
|
||||||
// Initialize colors and signal unit
|
|
||||||
updateChartSettings(signalChart);
|
|
||||||
|
|
||||||
// Periodically check for color and signal unit updates
|
|
||||||
setInterval(() => {
|
|
||||||
updateChartSettings(signalChart);
|
|
||||||
}, 1000); // Check every 1 second
|
|
||||||
}
|
|
||||||
|
|
||||||
updateCanvas(parsedData, signalChart);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateChartSettings(signalChart) {
|
function initCanvas() {
|
||||||
// Update colors
|
const ctx = document.getElementById("signal-canvas").getContext("2d");
|
||||||
const newColor2 = getComputedStyle(document.documentElement).getPropertyValue('--color-2').trim();
|
|
||||||
const newColor3 = getComputedStyle(document.documentElement).getPropertyValue('--color-3').trim();
|
|
||||||
const newColor4 = getComputedStyle(document.documentElement).getPropertyValue('--color-4').trim();
|
|
||||||
if (newColor2 !== signalChart.color2 || newColor4 !== signalChart.color4) {
|
|
||||||
signalChart.color2 = newColor2;
|
|
||||||
signalChart.color3 = newColor3;
|
|
||||||
signalChart.color4 = newColor4;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update signal unit
|
|
||||||
const newSignalUnit = localStorage.getItem('signalUnit');
|
|
||||||
if (newSignalUnit !== signalChart.signalUnit) {
|
|
||||||
signalChart.signalUnit = newSignalUnit;
|
|
||||||
// Adjust the offset based on the new signal unit
|
|
||||||
switch(newSignalUnit) {
|
|
||||||
case 'dbuv': signalChart.offset = 11.25; break;
|
|
||||||
case 'dbm': signalChart.offset = 120; break;
|
|
||||||
default: signalChart.offset = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateCanvas(parsedData, signalChart) {
|
window.signalChart = new Chart(ctx, {
|
||||||
const { context, canvas, maxDataPoints, pointWidth, color2, color3, color4, offset } = signalChart;
|
type: "line",
|
||||||
|
data: {
|
||||||
if (data.length > maxDataPoints) {
|
datasets: [{
|
||||||
data = data.slice(data.length - maxDataPoints);
|
label: "Signal Strength",
|
||||||
}
|
borderColor: () => getComputedStyle(document.documentElement).getPropertyValue("--color-4").trim(),
|
||||||
|
borderWidth: 2,
|
||||||
const actualLowestValue = Math.min(...data);
|
fill: {
|
||||||
const actualHighestValue = Math.max(...data);
|
target: 'start'
|
||||||
const zoomMinValue = actualLowestValue - ((actualHighestValue - actualLowestValue) / 2);
|
},
|
||||||
const zoomMaxValue = actualHighestValue + ((actualHighestValue - actualLowestValue) / 2);
|
backgroundColor: () => getComputedStyle(document.documentElement).getPropertyValue("--color-1-transparent").trim(),
|
||||||
const zoomAvgValue = (zoomMaxValue - zoomMinValue) / 2 + zoomMinValue;
|
tension: 0.6,
|
||||||
|
data: []
|
||||||
// Clear the canvas
|
}]
|
||||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
},
|
||||||
context.beginPath();
|
options: {
|
||||||
|
layout: {
|
||||||
const startingIndex = Math.max(0, data.length - maxDataPoints);
|
padding: {
|
||||||
|
left: -10,
|
||||||
for (let i = startingIndex; i < data.length; i++) {
|
right: -10,
|
||||||
const x = canvas.width - (data.length - i) * pointWidth - 40;
|
bottom: -10
|
||||||
const y = canvas.height - (data[i] - zoomMinValue) * (canvas.height / (zoomMaxValue - zoomMinValue));
|
},
|
||||||
|
},
|
||||||
if (i === startingIndex) {
|
responsive: true,
|
||||||
context.moveTo(x, y);
|
maintainAspectRatio: false,
|
||||||
} else {
|
elements: {
|
||||||
const prevX = canvas.width - (data.length - i + 1) * pointWidth - 40;
|
point: { radius: 0 },
|
||||||
const prevY = canvas.height - (data[i - 1] - zoomMinValue) * (canvas.height / (zoomMaxValue - zoomMinValue));
|
},
|
||||||
const interpolatedX = (x + prevX) / 2;
|
scales: {
|
||||||
const interpolatedY = (y + prevY) / 2;
|
x: {
|
||||||
|
type: "realtime",
|
||||||
context.quadraticCurveTo(prevX, prevY, interpolatedX, interpolatedY);
|
ticks: { display: false },
|
||||||
}
|
border: { display: false },
|
||||||
}
|
grid: { display: false, borderWidth: 0, borderColor: "transparent" },
|
||||||
|
realtime: {
|
||||||
context.strokeStyle = color4;
|
duration: 30000,
|
||||||
context.lineWidth = 2;
|
refresh: 75,
|
||||||
context.stroke();
|
delay: 150,
|
||||||
|
onRefresh: (chart) => {
|
||||||
// Draw horizontal lines for lowest, highest, and average values
|
if (!chart?.data?.datasets || parsedData?.sig === undefined) return;
|
||||||
context.strokeStyle = color3;
|
|
||||||
context.lineWidth = 1;
|
signalBuffer.push(parsedData.sig);
|
||||||
|
if (signalBuffer.length > 8) {
|
||||||
// Draw the lowest value line
|
signalBuffer.shift();
|
||||||
const lowestY = canvas.height - (zoomMinValue - zoomMinValue) * (canvas.height / (zoomMaxValue - zoomMinValue));
|
}
|
||||||
context.beginPath();
|
const avgSignal = signalBuffer.reduce((sum, val) => sum + val, 0) / signalBuffer.length;
|
||||||
context.moveTo(40, lowestY - 18);
|
|
||||||
context.lineTo(canvas.width - 40, lowestY - 18);
|
chart.data.datasets[0].data.push({
|
||||||
context.stroke();
|
x: Date.now(),
|
||||||
|
y: avgSignal
|
||||||
// Draw the highest value line
|
});
|
||||||
const highestY = canvas.height - (zoomMaxValue - zoomMinValue) * (canvas.height / (zoomMaxValue - zoomMinValue));
|
}
|
||||||
context.beginPath();
|
}
|
||||||
context.moveTo(40, highestY + 10);
|
},
|
||||||
context.lineTo(canvas.width - 40, highestY + 10);
|
y: {
|
||||||
context.stroke();
|
beginAtZero: false,
|
||||||
|
grace: 0.25,
|
||||||
const avgY = canvas.height / 2;
|
border: { display: false },
|
||||||
context.beginPath();
|
ticks: {
|
||||||
context.moveTo(40, avgY - 7);
|
maxTicksLimit: 3,
|
||||||
context.lineTo(canvas.width - 40, avgY - 7);
|
display: false // Hide default labels
|
||||||
context.stroke();
|
},
|
||||||
|
grid: {
|
||||||
// Label the lines with their values
|
display: false, // Hide default grid lines
|
||||||
context.fillStyle = color4;
|
},
|
||||||
context.font = '12px Titillium Web';
|
},
|
||||||
|
y2: {
|
||||||
context.textAlign = 'right';
|
position: 'right', // Position on the right side
|
||||||
context.fillText(`${(zoomMinValue - offset).toFixed(1)}`, 35, lowestY - 14);
|
beginAtZero: false,
|
||||||
context.fillText(`${(zoomMaxValue - offset).toFixed(1)}`, 35, highestY + 14);
|
grace: 0.25,
|
||||||
context.fillText(`${(zoomAvgValue - offset).toFixed(1)}`, 35, avgY - 3);
|
border: { display: false },
|
||||||
|
ticks: {
|
||||||
context.textAlign = 'left';
|
maxTicksLimit: 3,
|
||||||
context.fillText(`${(zoomMinValue - offset).toFixed(1)}`, canvas.width - 35, lowestY - 14);
|
display: false // Hide default labels for the right axis
|
||||||
context.fillText(`${(zoomMaxValue - offset).toFixed(1)}`, canvas.width - 35, highestY + 14);
|
},
|
||||||
context.fillText(`${(zoomAvgValue - offset).toFixed(1)}`, canvas.width - 35, avgY - 3);
|
grid: {
|
||||||
|
display: false, // No grid for right axis
|
||||||
setTimeout(() => {
|
}
|
||||||
requestAnimationFrame(() => updateCanvas(parsedData, signalChart));
|
}
|
||||||
}, 1000 / 15);
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: { display: false },
|
||||||
|
tooltip: { enabled: false }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: [{
|
||||||
|
id: 'customYAxisLabels',
|
||||||
|
afterDraw: (chart) => {
|
||||||
|
const { ctx, scales, chartArea } = chart;
|
||||||
|
const yAxis = scales.y;
|
||||||
|
const y2Axis = scales.y2;
|
||||||
|
|
||||||
|
const gridLineColor = getComputedStyle(document.documentElement).getPropertyValue("--color-2-transparent").trim(); // Grid color using CSS variable
|
||||||
|
const textColor = getComputedStyle(document.documentElement).getPropertyValue("--color-3-transparent").trim(); // Use the same color for text labels
|
||||||
|
|
||||||
|
ctx.save();
|
||||||
|
ctx.font = "12px Titillium Web";
|
||||||
|
ctx.fillStyle = textColor;
|
||||||
|
ctx.textAlign = "center";
|
||||||
|
|
||||||
|
const leftX = yAxis.left + 20;
|
||||||
|
const rightX = y2Axis.right - 20;
|
||||||
|
|
||||||
|
const offset = 10;
|
||||||
|
|
||||||
|
yAxis.ticks.forEach((tick, index) => {
|
||||||
|
const y = yAxis.getPixelForValue(tick.value);
|
||||||
|
var adjustedY = Math.max(yAxis.top + 13, Math.min(y, yAxis.bottom - 6));
|
||||||
|
const isMiddleTick = index === Math.floor(yAxis.ticks.length / 2);
|
||||||
|
|
||||||
|
let adjustedTickValue;
|
||||||
|
switch(localStorage.getItem("signalUnit")) {
|
||||||
|
case "dbuv": adjustedTickValue = tick.value - 11.25; break;
|
||||||
|
case "dbm": adjustedTickValue = tick.value - -120; break;
|
||||||
|
default: adjustedTickValue = tick.value; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isMiddleTick) { adjustedY += 3; }
|
||||||
|
ctx.textAlign = 'right';
|
||||||
|
ctx.fillText(adjustedTickValue.toFixed(1), leftX + 25, adjustedY);
|
||||||
|
|
||||||
|
ctx.textAlign = 'left';
|
||||||
|
ctx.fillText(adjustedTickValue.toFixed(1), rightX - 25, adjustedY); // Right side
|
||||||
|
});
|
||||||
|
|
||||||
|
const gridLineWidth = 0.5; // Make the lines thinner to avoid overlapping text
|
||||||
|
const adjustedGridTop = chartArea.top + offset;
|
||||||
|
const adjustedGridBottom = chartArea.bottom - offset;
|
||||||
|
const middleY = chartArea.top + chartArea.height / 2;
|
||||||
|
const padding = 45; // 30px inward on both sides
|
||||||
|
|
||||||
|
// Helper function to draw a horizontal line
|
||||||
|
function drawGridLine(y) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(chartArea.left + padding, y);
|
||||||
|
ctx.lineTo(chartArea.right - padding, y);
|
||||||
|
ctx.strokeStyle = gridLineColor;
|
||||||
|
ctx.lineWidth = gridLineWidth;
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the three horizontal grid lines
|
||||||
|
drawGridLine(adjustedGridTop);
|
||||||
|
drawGridLine(adjustedGridBottom);
|
||||||
|
drawGridLine(middleY);
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let reconnectTimer = null;
|
let reconnectTimer = null;
|
||||||
@@ -641,14 +638,6 @@ function processString(string, errors) {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCurrentFreq() {
|
|
||||||
currentFreq = $('#data-frequency').text();
|
|
||||||
currentFreq = parseFloat(currentFreq).toFixed(3);
|
|
||||||
currentFreq = parseFloat(currentFreq);
|
|
||||||
|
|
||||||
return currentFreq;
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkKey(e) {
|
function checkKey(e) {
|
||||||
e = e || window.event;
|
e = e || window.event;
|
||||||
|
|
||||||
@@ -899,14 +888,12 @@ function throttle(fn, wait) {
|
|||||||
return wrapper;
|
return wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility function to update element's text if changed
|
|
||||||
function updateTextIfChanged($element, newText) {
|
function updateTextIfChanged($element, newText) {
|
||||||
if ($element.text() !== newText) {
|
if ($element.text() !== newText) {
|
||||||
$element.text(newText);
|
$element.text(newText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility function to update element's HTML content if changed
|
|
||||||
function updateHtmlIfChanged($element, newHtml) {
|
function updateHtmlIfChanged($element, newHtml) {
|
||||||
if ($element.html() !== newHtml) {
|
if ($element.html() !== newHtml) {
|
||||||
$element.html(newHtml);
|
$element.html(newHtml);
|
||||||
@@ -1005,7 +992,7 @@ const updateDataElements = throttle(function(parsedData) {
|
|||||||
$dataRt1.attr('aria-label', parsedData.rt1);
|
$dataRt1.attr('aria-label', parsedData.rt1);
|
||||||
$('#users-online-container').attr("aria-label", "Online users: " + parsedData.users);
|
$('#users-online-container').attr("aria-label", "Online users: " + parsedData.users);
|
||||||
}
|
}
|
||||||
}, 100); // Update at most once every 100 milliseconds
|
}, 75); // Update at most once every 100 milliseconds
|
||||||
|
|
||||||
let isEventListenerAdded = false;
|
let isEventListenerAdded = false;
|
||||||
|
|
||||||
@@ -1114,9 +1101,12 @@ function showTunerDescription() {
|
|||||||
parentDiv.css("border-radius", "");
|
parentDiv.css("border-radius", "");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#tuner-name i").toggleClass("rotated");
|
||||||
|
|
||||||
if ($(window).width() < 768) {
|
if ($(window).width() < 768) {
|
||||||
$('.dashboard-panel-plugin-list').slideToggle(300);
|
$('.dashboard-panel-plugin-list').slideToggle(300);
|
||||||
|
$('#users-online-container').slideToggle(300);
|
||||||
$('.chatbutton').slideToggle(300);
|
$('.chatbutton').slideToggle(300);
|
||||||
$('#settings').slideToggle(300);
|
$('#settings').slideToggle(300);
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-8
@@ -1,8 +1,22 @@
|
|||||||
$.getScript('./js/api.js');
|
function loadScript(src) {
|
||||||
$.getScript('./js/main.js');
|
return new Promise((resolve, reject) => {
|
||||||
$.getScript('./js/dropdown.js');
|
const script = document.createElement('script');
|
||||||
$.getScript('./js/modal.js');
|
script.src = src;
|
||||||
$.getScript('./js/settings.js');
|
script.onload = resolve;
|
||||||
$.getScript('./js/chat.js');
|
script.onerror = reject;
|
||||||
$.getScript('./js/toast.js');
|
document.head.appendChild(script);
|
||||||
$.getScript('./js/plugins.js');
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadScriptsInOrder() {
|
||||||
|
await loadScript('./js/api.js');
|
||||||
|
await loadScript('./js/main.js');
|
||||||
|
await loadScript('./js/dropdown.js');
|
||||||
|
await loadScript('./js/modal.js');
|
||||||
|
await loadScript('./js/settings.js');
|
||||||
|
await loadScript('./js/chat.js');
|
||||||
|
await loadScript('./js/toast.js');
|
||||||
|
await loadScript('./js/plugins.js');
|
||||||
|
}
|
||||||
|
|
||||||
|
loadScriptsInOrder();
|
||||||
|
|||||||
Reference in New Issue
Block a user