1
0
mirror of https://github.com/KubaPro010/fm-dx-webserver.git synced 2026-02-26 22:13:53 +01:00

mobile panel, UI bugfixes, security improvements

This commit is contained in:
Marek Farkaš
2025-05-02 16:06:48 +02:00
parent 27e9ee93fb
commit fb8af10ce5
19 changed files with 909 additions and 888 deletions

972
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "fm-dx-webserver",
"version": "1.3.7",
"version": "1.3.8",
"description": "FM DX Webserver",
"main": "index.js",
"scripts": {

View File

@@ -62,7 +62,7 @@ router.get('/', (req, res) => {
tunerLock: serverConfig.lockToAdmin,
publicTuner: serverConfig.publicTuner,
ownerContact: serverConfig.identification.contact,
antennas: serverConfig.antennas ? serverConfig.antennas : {},
antennas: serverConfig.antennas,
tuningLimit: serverConfig.webserver.tuningLimit,
tuningLowerLimit: serverConfig.webserver.tuningLowerLimit,
tuningUpperLimit: serverConfig.webserver.tuningUpperLimit,
@@ -70,8 +70,9 @@ router.get('/', (req, res) => {
device: serverConfig.device,
noPlugins,
plugins: serverConfig.plugins,
fmlist_integration: typeof(serverConfig.extras?.fmlistIntegration) !== undefined ? serverConfig.extras?.fmlistIntegration : true,
bwSwitch: serverConfig.bwSwitch ? serverConfig.bwSwitch : false
fmlist_integration: serverConfig.extras.fmlistIntegration,
fmlist_adminOnly: serverConfig.extras.fmlistAdminOnly,
bwSwitch: serverConfig.bwSwitch
});
}
});
@@ -380,15 +381,14 @@ router.get('/log_fmlist', (req, res) => {
return;
}
if (serverConfig.extras?.fmlistIntegration === false) {
res.status(500).send('FMLIST Integration is not enabled on this server.');
if (serverConfig.extras.fmlistIntegration === false || serverConfig.extras.fmlistAdminOnly && !isTuneAuthenticated) {
res.status(500).send('FMLIST Integration is not available.');
return;
}
const clientIp = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
const txId = dataHandler.dataToSend.txInfo.id; // Extract the ID
// Check if the ID can be logged (i.e., not logged within the last 60 minutes)
if (!canLog(txId)) {
res.status(429).send(`ID ${txId} was already logged recently. Please wait before logging again.`);
return;

View File

@@ -187,7 +187,7 @@ if (serverConfig.xdrd.wirelessConnection === false) {
serialport.write('A0\n');
serialport.write('F-1\n');
serialport.write('W0\n');
serialport.write('D0\n');
serverConfig.webserver.rdsMode ? serialport.write('D1\n') : serialport.write('D0\n');
serialport.write('G00\n');
serverConfig.audio.startupVolume
? serialport.write('Y' + (serverConfig.audio.startupVolume * 100).toFixed(0) + '\n')
@@ -271,6 +271,7 @@ function connectToXdrd() {
dataHandler.dataToSend.freq = serverConfig.defaultFreq && serverConfig.enableDefaultFreq === true ? Number(serverConfig.defaultFreq).toFixed(3) : (87.5).toFixed(3);
client.write('A0\n');
client.write(serverConfig.audio.startupVolume ? 'Y' + (serverConfig.audio.startupVolume * 100).toFixed(0) + '\n' : 'Y100\n');
serverConfig.webserver.rdsMode ? client.write('D1\n') : client.write('D0\n');
client.off('data', authDataHandler);
return;
}
@@ -484,6 +485,8 @@ wss.on('connection', (ws, request) => {
if (currentUsers === 0) {
storage.connectedUsers = [];
output.write('W0\n');
output.write('B0\n');
}
if (tunerLockTracker.has(ws)) {

View File

@@ -85,6 +85,7 @@ let serverConfig = {
},
extras: {
fmlistIntegration: true,
fmlistAdminOnly: false,
fmlistOmid: "",
},
tunnel: {

View File

@@ -208,7 +208,9 @@ async function processData(data, piCode, rdsPs) {
if (matchingStation) {
return {
station: detectedByPireg ? matchingStation.station.replace("R.", "Radio ") + ' ' + matchingStation.regname : matchingStation.station.replace("R.", "Radio "),
station: detectedByPireg
? `${matchingStation.station.replace("R.", "Radio ")}${matchingStation.regname ? ' ' + matchingStation.regname : ''}`
: matchingStation.station.replace("R.", "Radio "),
pol: matchingStation.pol.toUpperCase(),
erp: matchingStation.erp && matchingStation.erp > 0 ? matchingStation.erp : '?',
city: matchingCity.name,

70
web/_bwSwitch.ejs Normal file
View File

@@ -0,0 +1,70 @@
<%
let options = [];
if (device === 'tef') {
options = [
{ value: 0, label: 'Auto' },
{ value: 56000, label: '56 kHz' },
{ value: 64000, label: '64 kHz' },
{ value: 72000, label: '72 kHz' },
{ value: 84000, label: '84 kHz' },
{ value: 97000, label: '97 kHz' },
{ value: 114000, label: '114 kHz' },
{ value: 133000, label: '133 kHz' },
{ value: 151000, label: '151 kHz' },
{ value: 184000, label: '184 kHz' },
{ value: 200000, label: '200 kHz' },
{ value: 217000, label: '217 kHz' },
{ value: 236000, label: '236 kHz' },
{ value: 254000, label: '254 kHz' },
{ value: 287000, label: '287 kHz' },
{ value: 311000, label: '311 kHz' }
];
} else if (device === 'xdr') {
options = [
{ value: 0, value2: -1, label: 'Auto' },
{ value: 55000, value2: 0, label: '55 kHz' },
{ value: 73000, value2: 1, label: '73 kHz' },
{ value: 90000, value2: 2, label: '90 kHz' },
{ value: 108000, value2: 3, label: '108 kHz' },
{ value: 125000, value2: 4, label: '125 kHz' },
{ value: 142000, value2: 5, label: '142 kHz' },
{ value: 159000, value2: 6, label: '159 kHz' },
{ value: 177000, value2: 7, label: '177 kHz' },
{ value: 194000, value2: 8, label: '194 kHz' },
{ value: 211000, value2: 9, label: '211 kHz' },
{ value: 229000, value2: 10, label: '229 kHz' },
{ value: 246000, value2: 11, label: '246 kHz' },
{ value: 263000, value2: 12, label: '263 kHz' },
{ value: 281000, value2: 13, label: '281 kHz' },
{ value: 298000, value2: 14, label: '298 kHz' },
{ value: 309000, value2: 15, label: '309 kHz' }
];
} else if (device === 'sdr') {
options = [
{ value: 0, label: 'Auto' },
{ value: 4000, label: '4 kHz' },
{ value: 8000, label: '8 kHz' },
{ value: 10000, label: '10 kHz' },
{ value: 20000, label: '20 kHz' },
{ value: 30000, label: '30 kHz' },
{ value: 50000, label: '50 kHz' },
{ value: 75000, label: '75 kHz' },
{ value: 100000, label: '100 kHz' },
{ value: 125000, label: '125 kHz' },
{ value: 150000, label: '150 kHz' },
{ value: 175000, label: '175 kHz' },
{ value: 200000, label: '200 kHz' },
{ value: 225000, label: '225 kHz' }
];
}
%>
<div class="no-bg dropdown data-bw <%= cssClass %>" id="<%= id %>">
<input type="text" placeholder="Auto" readonly tabindex="0">
<ul class="options <%= cssClassOptions %>" tabindex="-1">
<% options.forEach(function(opt) { %>
<li class="option" tabindex="0" data-value="<%= opt.value %>" <%= opt.value2 !== undefined ? 'data-value2="' + opt.value2 + '"' : '' %>><%= opt.label %></li>
<% }); %>
</ul>
</div>

View File

@@ -93,10 +93,6 @@ label {
position: relative;
}
#data-ant {
margin-right: 15px !important;
}
#data-ps, #data-rt0, #data-rt1 {
font-family: "Roboto Mono", monospace;
}
@@ -153,33 +149,7 @@ table .form-group {
border-radius: 0 0 15px 15px;
}
#settings, #users-online-container, .chatbutton {
background: transparent;
border: 0;
color: var(--color-4);
font-size: 16px;
width: 48px;
height: 48px;
text-align: center;
border-radius: 15px;
transition: 300ms ease background;
cursor: pointer;
margin: 2px;
}
#users-online-container {
margin-left: 10px;
}
.chatbutton, #settings {
background-color: var(--color-1);
}
#settings:hover, #users-online-container:hover, .chatbutton:hover {
background: var(--color-2);
}
#users-online-container {
.users-online-container {
top: 80px;
}
@@ -208,30 +178,6 @@ table .form-group {
padding: 5px;
}
.checkbox input[type="checkbox"] {
padding: 0;
height: initial;
width: initial;
margin-bottom: 0;
display: none;
cursor: pointer;
}
.checkbox label {
cursor: pointer;
display: block;
user-select: none;
padding: 7px 20px;
border-radius: 15px;
text-align: center;
border: 2px solid var(--color-4);
box-sizing: border-box;
transition: 0.35s ease background-color, 0.35s ease color;
}
.checkbox label:hover {
background-color: var(--color-2);
}
.form-group input:checked + label {
background-color: var(--color-4);
color: var(--color-main);
@@ -351,6 +297,12 @@ pre {
max-width: 200px;
}
@media (min-width: 768px) {
.users-online-container {
margin-left: 10px;
}
}
@media (max-width: 768px) {
canvas, #flags-container {
display: none;
@@ -426,14 +378,6 @@ pre {
.filter-controls {
order: 2;
}
.button-play-mobile {
order: 2;
margin: 0 !important;
border-radius: 0;
}
.button-play-mobile .playbutton {
border-radius: 0;
}
.button-eq {
order: 1;
}
@@ -452,6 +396,16 @@ pre {
.modal-panel-footer {
width: auto !important;
}
#mobileTray {
width: 100%;
height: 68px;
position: fixed;
bottom: 0;
background-color: var(--color-1-transparent);
backdrop-filter: blur(5px);
display: flex;
align-items: center;
}
}
@media only screen and (min-width: 769px) and (max-height: 720px) {

View File

@@ -130,6 +130,28 @@ input[type="text"], textarea, input[type="password"] {
border-radius: 0 15px 15px 0;
}
.settings, .users-online-container, .chatbutton, .button-dark {
background: transparent;
border: 0;
color: var(--color-4);
font-size: 16px;
width: 48px;
height: 48px;
text-align: center;
border-radius: 15px;
transition: 300ms ease background;
cursor: pointer;
margin: 2px;
}
.chatbutton, .settings, .button-dark {
background-color: var(--color-1);
}
.settings:hover, .users-online-container:hover, .chatbutton:hover {
background: var(--color-2);
}
input[type="range"] {
margin: 0;
/* removing default appearance */
@@ -314,7 +336,7 @@ input[type="range"]::-moz-range-thumb {
.switch input[type=checkbox] {
height: 0;
width: 0;
visibility: hidden;
margin: 0;
}
.switch input[type=checkbox]:checked + label {
background: var(--color-4);
@@ -374,7 +396,7 @@ select:hover {
background: var(--color-5);
}
.popup {
.mini-popup {
position: relative;
cursor: pointer;
-webkit-user-select: none;
@@ -383,7 +405,7 @@ select:hover {
user-select: none;
}
.popup .popup-content {
.mini-popup .mini-popup-content {
visibility: hidden;
width: 230px;
background-color: var(--color-2-transparent);
@@ -400,15 +422,15 @@ select:hover {
border: 3px solid var(--color-3);
}
.popup .show {
.mini-popup .show {
visibility: visible;
}
button[disabled] .popup-content {
button[disabled] .mini-popup-content {
visibility: hidden;
}
.popup .popup-content::after {
.mini-popup .mini-popup-content::after {
content: "";
position: absolute;
bottom: calc(100% + 3px);
@@ -423,5 +445,9 @@ select:hover {
@media only screen and (max-width: 768px) {
#tune-buttons input[type="text"] {
background-color: var(--color-1-transparent);
height: 64px;
}
#tune-buttons button {
height: 64px;
}
}

View File

@@ -76,10 +76,22 @@
margin: 10px;
}
.m-left-5 {
margin-left: 5px;
}
.m-left-15 {
margin-left: 15px !important;
}
.m-left-20 {
margin-left: 20px;
}
.m-right-5 {
margin-right: 5px;
}
.m-right-10 {
margin-right: 10px;
}
@@ -132,6 +144,10 @@
background-color: var(--color-2-transparent);
}
.text-center {
text-align: center;
}
.text-left {
text-align: left;
}
@@ -207,6 +223,10 @@
margin-bottom: 50px;
}
.p-0 {
padding: 0;
}
.p-10 {
padding: 10px;
}

View File

@@ -157,4 +157,8 @@ hr {
.setup-wrapper h2 {
display: initial;
}
.wrapper-outer {
justify-content: flex-start;
}
}

View File

@@ -194,6 +194,7 @@ body.modal-open {
.popup-window {
width: 90%;
height: 60%;
}
}

View File

@@ -70,11 +70,11 @@
</div>
<div style="margin-left: auto;" class="dashboard-panel-plugin-content"></div>
<div>
<button id="users-online-container" class="hide-phone" aria-label="Online users"><i class="fa-solid fa-user"></i> <span class="users-online"></span></button>
<button class="users-online-container hide-phone" aria-label="Online users"><i class="fa-solid fa-user"></i> <span class="users-online"></span></button>
<% if (chatEnabled) { %>
<button class="chatbutton hide-phone" aria-label="Chatbox"><i class="fa-solid fa-comments"></i></button>
<button class="chatbutton hide-phone" aria-label="Chatbox"><i class="fa-solid fa-message"></i></button>
<% } %>
<button id="settings" aria-label="Settings" class="hide-phone"><i class="fa-solid fa-gear"></i></button>
<button aria-label="Settings" class="hide-phone settings"><i class="fa-solid fa-bars"></i></button>
</div>
</div>
</div>
@@ -142,7 +142,7 @@
</div>
</div>
<div class="wrapper-outer" style="z-index: 100;">
<div class="wrapper-outer main-content" style="z-index: 100;">
<div id="wrapper">
<div class="canvas-container hide-phone">
<canvas id="signal-canvas"></canvas>
@@ -204,23 +204,19 @@
<% if (device == 'sdr') { %> <span>dB SNR</span> <% } else { %> <span class="signal-units"></span> <% } %>
</div>
<div class="text-big">
<span id="data-signal"></span>
<!--
--><span id="data-signal-decimal" class="text-medium-big" style="opacity:0.7;"></span>
<span id="data-signal"></span><!--
--><span id="data-signal-decimal" class="text-medium-big" style="opacity:0.7;"></span>
<% if (device == 'sdr') { %> <span class="text-medium">dB SNR</span> <% } else { %> <span class="signal-units text-medium">dBf</span> <% } %>
</div>
</div>
</div>
<div class="flex-container flex-phone flex-phone-column">
<div class="panel-33 no-bg filter-controls" style="height: 48px;">
<div class="flex-container">
<div class="panel-33 no-bg filter-controls hide-phone" style="height: 48px;">
<div class="flex-container no-filter flex-phone h-100">
<div class="panel-75 no-bg h-100 m-0 hide-desktop m-right-20 button-play-mobile" style="margin-right: 20px;">
<button class="playbutton" aria-label="Play/Stop"><i class="fa-solid fa-play"></i></button>
</div>
<% if (antennas.enabled == true) { %>
<div class="panel-50 no-bg h-100 br-0 m-0 dropdown dropdown-up" id="data-ant" style="margin-right: 25px;">
<div class="panel-50 no-bg h-100 br-0 m-0 dropdown dropdown-up data-ant" id="data-ant" style="margin-right: 15px !important;">
<input type="text" placeholder="Ant A" readonly tabindex="0">
<ul class="options open-top" tabindex="-1">
<% if(antennas.ant1.enabled == true) { %><li data-value="0" class="option" tabindex="0"><%= antennas.ant1.name %></li><% } %>
@@ -232,12 +228,12 @@
<% } %>
<div class="panel-50 no-bg br-0 h-100 m-0 button-eq">
<% if (device == 'tef') { %><button id="data-eq" style="border-radius: 15px 0px 0px 15px;" class="tooltip" aria-label="EQ Filter" data-tooltip="<strong>The cEQ filter can reduce bandwidth below 56 kHz.</strong><br><br>Useful for weak stations next to strong ones,<br>although it may pick up more interference."><span class="text-bold">cEQ</span></button><% } %>
<% if (device == 'xdr') { %><button id="data-eq" style="border-radius: 15px 0px 0px 15px;" class="tooltip" aria-label="RF+ Filter" data-tooltip="<strong>The RF+ filter increases gain by 5dB</strong>"><span class="text-bold">RF+</span></button><% } %>
<% if (device == 'tef') { %><button style="border-radius: 15px 0px 0px 15px;" class="data-eq hide-phone tooltip" aria-label="EQ Filter" data-tooltip="<strong>The cEQ filter can reduce bandwidth below 56 kHz.</strong><br><br>Useful for weak stations next to strong ones,<br>although it may pick up more interference."><span class="text-bold">cEQ</span></button><% } %>
<% if (device == 'xdr') { %><button style="border-radius: 15px 0px 0px 15px;" class="data-eq hide-phone tooltip" aria-label="RF+ Filter" data-tooltip="<strong>The RF+ filter increases gain by 5dB</strong>"><span class="text-bold">RF+</span></button><% } %>
</div>
<div class="panel-50 no-bg br-0 h-100 m-0 button-ims">
<% if (device == 'tef') { %><button id="data-ims" style="border-radius: 0px 15px 15px 0px;" class="tooltip" aria-label="iMS + Filter" data-tooltip="<strong>The iMS filter reduces multipath audio artifacts.</strong><br><br>It's recommended to leave it on most of the time."><span class="text-bold">iMS</span></button><% } %>
<% if (device == 'xdr') { %><button id="data-ims" style="border-radius: 0px 15px 15px 0px;" class="tooltip" aria-label="IF+ Filter" data-tooltip="<strong>The IF+ filter increases gain by 6dB</strong>"><span class="text-bold">IF+</span></button><% } %>
<% if (device == 'tef') { %><button style="border-radius: 0px 15px 15px 0px;" class="data-ims hide-phone tooltip" aria-label="iMS + Filter" data-tooltip="<strong>The iMS filter reduces multipath audio artifacts.</strong><br><br>It's recommended to leave it on most of the time."><span class="text-bold">iMS</span></button><% } %>
<% if (device == 'xdr') { %><button style="border-radius: 0px 15px 15px 0px;" class="data-ims hide-phone tooltip" aria-label="IF+ Filter" data-tooltip="<strong>The IF+ filter increases gain by 6dB</strong>"><span class="text-bold">IF+</span></button><% } %>
</div>
</div>
</div>
@@ -253,84 +249,16 @@
<span class="panel-100-real m-0" style="height: 48px;">
<input type="range" id="volumeSlider" min="0" max="1" step="0.01" value="1" aria-label="Volume slider">
</span>
<% if (device == 'tef' && bwSwitch == true) { %>
<div class="panel-50 w-150 no-bg h-100 m-0 dropdown dropdown-up" id="data-bw" style="margin-left: 15px !important;">
<input type="text" placeholder="Auto BW" readonly tabindex="0">
<ul class="options open-top" tabindex="-1">
<li data-value="0" class="option" tabindex="0">Auto</li>
<li data-value="56000" class="option" tabindex="0">56 kHz</li>
<li data-value="64000" class="option" tabindex="0">64 kHz</li>
<li data-value="72000" class="option" tabindex="0">72 kHz</li>
<li data-value="84000" class="option" tabindex="0">84 kHz</li>
<li data-value="97000" class="option" tabindex="0">97 kHz</li>
<li data-value="114000" class="option" tabindex="0">114 kHz</li>
<li data-value="133000" class="option" tabindex="0">133 kHz</li>
<li data-value="151000" class="option" tabindex="0">151 kHz</li>
<li data-value="184000" class="option" tabindex="0">184 kHz</li>
<li data-value="200000" class="option" tabindex="0">200 kHz</li>
<li data-value="217000" class="option" tabindex="0">217 kHz</li>
<li data-value="236000" class="option" tabindex="0">236 kHz</li>
<li data-value="254000" class="option" tabindex="0">254 kHz</li>
<li data-value="287000" class="option" tabindex="0">287 kHz</li>
<li data-value="311000" class="option" tabindex="0">311 kHz</li>
</ul>
</div>
<% } %>
<% if (device == 'xdr' && bwSwitch == true) { %>
<div class="panel-50 w-150 no-bg h-100 m-0 dropdown dropdown-up" id="data-bw" style="margin-left: 15px !important;">
<input type="text" placeholder="Auto BW" readonly tabindex="0">
<ul class="options open-top" tabindex="-1">
<li data-value="0" data-value2="-1" class="option" tabindex="0">Auto</li>
<li data-value="55000" data-value2="0" class="option" tabindex="0">55 kHz</li>
<li data-value="73000" data-value2="1" class="option" tabindex="0">73 kHz</li>
<li data-value="90000" data-value2="2" class="option" tabindex="0">90 kHz</li>
<li data-value="108000" data-value2="3" class="option" tabindex="0">108 kHz</li>
<li data-value="125000" data-value2="4" class="option" tabindex="0">125 kHz</li>
<li data-value="142000" data-value2="5" class="option" tabindex="0">142 kHz</li>
<li data-value="159000" data-value2="6" class="option" tabindex="0">159 kHz</li>
<li data-value="177000" data-value2="7" class="option" tabindex="0">177 kHz</li>
<li data-value="194000" data-value2="8" class="option" tabindex="0">194 kHz</li>
<li data-value="211000" data-value2="9" class="option" tabindex="0">211 kHz</li>
<li data-value="229000" data-value2="10" class="option" tabindex="0">229 kHz</li>
<li data-value="246000" data-value2="11" class="option" tabindex="0">246 kHz</li>
<li data-value="263000" data-value2="12" class="option" tabindex="0">263 kHz</li>
<li data-value="281000" data-value2="13" class="option" tabindex="0">281 kHz</li>
<li data-value="298000" data-value2="14" class="option" tabindex="0">298 kHz</li>
<li data-value="309000" data-value2="15" class="option" tabindex="0">309 kHz</li>
</ul>
</div>
<% } %>
<% if (device == 'sdr' && bwSwitch == true) { %>
<div class="panel-50 w-150 no-bg h-100 m-0 dropdown dropdown-up" id="data-bw" style="margin-left: 15px !important;">
<input type="text" placeholder="Auto BW" readonly tabindex="0">
<ul class="options open-top" tabindex="-1">
<li data-value="0" class="option" tabindex="0">Auto</li>
<li data-value="4000" class="option" tabindex="0">4 kHz</li>
<li data-value="8000" class="option" tabindex="0">8 kHz</li>
<li data-value="10000" class="option" tabindex="0">10 kHz</li>
<li data-value="20000" class="option" tabindex="0">20 kHz</li>
<li data-value="30000" class="option" tabindex="0">30 kHz</li>
<li data-value="50000" class="option" tabindex="0">50 kHz</li>
<li data-value="75000" class="option" tabindex="0">75 kHz</li>
<li data-value="100000" class="option" tabindex="0">100 kHz</li>
<li data-value="125000" class="option" tabindex="0">125 kHz</li>
<li data-value="150000" class="option" tabindex="0">150 kHz</li>
<li data-value="175000" class="option" tabindex="0">175 kHz</li>
<li data-value="200000" class="option" tabindex="0">200 kHz</li>
<li data-value="225000" class="option" tabindex="0">225 kHz</li>
</ul>
</div>
<% } %>
<% if (fmlist_integration == true) { %>
<button class="tooltip bg-color-4 popup" id="log-fmlist" data-tooltip="<strong>LOG TO FMLIST</strong><br>Clicking this button logs the current station to FMLIST's visual logbook." aria-label="Log to FMLIST" style="width: 80px; height: 48px;margin-left: 15px !important;">
<% if (bwSwitch) { %>
<%- include('_bwSwitch', { device: device, id: 'data-bw', cssClass: 'panel-50 dropdown-up m-0 w-150 m-left-15', cssClassOptions: 'open-top' }) %>
<% } %>
<% if (fmlist_integration == true && (fmlist_adminOnly == false || isTuneAuthenticated)) { %>
<button class="tooltip bg-color-4 mini-popup log-fmlist" data-tooltip="<strong>LOG TO FMLIST</strong><br>Clicking this button logs the current station to FMLIST's visual logbook." aria-label="Log to FMLIST" style="width: 80px; height: 48px;margin-left: 15px !important;">
<i class="fa-solid fa-flag fa-lg"></i>
<span class="popup-content">
<span class="mini-popup-content">
Choose the DX propagation type:<br>
<a class="top-10 bg-color-3 text-bold" style="padding: 10px; border-radius: 15px 0 0 15px; display: inline-block;" id="log-fmlist-tropo">Tropo</a>
<!--
--><a class="top-10 bg-color-3 text-bold" style="padding: 10px; border-radius: 0 15px 15px 0; display: inline-block;" id="log-fmlist-sporadice">Sporadic-E</a>
<a class="top-10 bg-color-3 text-bold log-fmlist-tropo" style="padding: 10px; border-radius: 15px 0 0 15px; display: inline-block;">Tropo</a><!--
--><a class="top-10 bg-color-3 text-bold log-fmlist-sporadice" style="padding: 10px; border-radius: 0 15px 15px 0; display: inline-block;">Sporadic-E</a>
</span>
</button>
<% } %>
@@ -377,17 +305,17 @@
</div>
</div>
<div id="flags-container-phone" class="panel-33 no-bg-phone">
<div id="flags-container-phone" class="panel-33 no-bg-phone" style="margin-bottom: 110px !important;">
<h2 class="show-phone">
<div class="data-pty text-color-default"></div>
<div class="data-pty color-4"></div>
</h2>
<h3 style="margin-top:0;margin-bottom:0;" class="colornode-4 flex-center">
<h3 style="margin-top:0;margin-bottom:0;" class="text-color-default flex-center">
<span class="data-tp">TP</span>
<span style="margin-left: 15px;" class="data-ta">TA</span>
<div style="display:inline-block">
<span style="margin-left: 20px;display: block;margin-top: 2px;" class="data-flag"></span>
</div>
<span class="pointer stereo-container" style="position: noderelative; margin-left: 20px;" role="button" aria-label="Stereo / Mono toggle" tabindex="0">
<span class="pointer stereo-container" style="position: relative; margin-left: 20px;" role="button" aria-label="Stereo / Mono toggle" tabindex="0">
<div class="circle-container">
<div class="circle data-st circle1"></div>
<div class="circle data-st circle2"></div>
@@ -399,21 +327,76 @@
</div>
</div>
<div class="popup-window" id="popup-panel-mobile-settings" style="overflow:visible;">
<div class="flex-container flex-column flex-phone flex-phone-column" style="height: calc(100%);">
<div class="popup-header hover-brighten flex-center">
<p class="color-4" style="margin: 0; padding-left: 10px;">Quick tools</p>
<button class="popup-close">✖</button>
</div>
<div class="popup-content text-left flex-container flex-phone flex-column p-10" style="flex: 1;">
<% if (bwSwitch || antennas.enabled) { %>
<p class="flex-phone flex-center">Bandwidth & Antennas</p>
<% } %>
<div class="flex-phone">
<% if (bwSwitch) { %>
<div style="max-height: 48px;width: 50%;margin-right: 5px;">
<%- include('_bwSwitch', { device: device, id: 'data-bw-phone', cssClass: 'panel-100-real', cssClassOptions: 'text-center open-bottom' }) %>
</div>
<% } %>
<% if (antennas.enabled == true) { %>
<div class="panel-100-real no-bg h-100 br-0 dropdown data-ant" id="data-ant-phone" style="max-height: 48px;width: 50%;margin-left: 5px;">
<input type="text" placeholder="Ant A" readonly tabindex="0">
<ul class="options" tabindex="-1">
<% if(antennas.ant1.enabled == true) { %><li data-value="0" class="option" tabindex="0"><%= antennas.ant1.name %></li><% } %>
<% if(antennas.ant2.enabled == true) { %><li data-value="1" class="option" tabindex="0"><%= antennas.ant2.name %></li><% } %>
<% if(antennas.ant3.enabled == true) { %><li data-value="2" class="option" tabindex="0"><%= antennas.ant3.name %></li><% } %>
<% if(antennas.ant4.enabled == true) { %><li data-value="3" class="option" tabindex="0"><%= antennas.ant4.name %></li><% } %>
</ul>
</div>
<% } %>
</div>
<p class="flex-phone flex-center">Filters</p>
<div class="flex-container flex-phone flex-center">
<% if (device == 'tef') { %><button class="data-eq tooltip p-10 m-right-5" style="height: 48px" aria-label="EQ Filter" data-tooltip="<strong>The cEQ filter can reduce bandwidth below 56 kHz.</strong><br><br>Useful for weak stations next to strong ones,<br>although it may pick up more interference."><span class="text-bold">cEQ</span></button><% } %>
<% if (device == 'xdr') { %><button class="data-eq tooltip p-10 m-right-5" aria-label="RF+ Filter" data-tooltip="<strong>The RF+ filter increases gain by 5dB</strong>"><span class="text-bold">RF+</span></button><% } %>
<% if (device == 'tef') { %><button class="data-ims tooltip p-10 m-left-5" style="height: 48px;" aria-label="iMS + Filter" data-tooltip="<strong>The iMS filter reduces multipath audio artifacts.</strong><br><br>It's recommended to leave it on most of the time."><span class="text-bold">iMS</span></button><% } %>
<% if (device == 'xdr') { %><button class="data-ims tooltip p-10 m-left-5" aria-label="IF+ Filter" data-tooltip="<strong>The IF+ filter increases gain by 6dB</strong>"><span class="text-bold">IF+</span></button><% } %>
</div>
<% if (fmlist_integration == true && (fmlist_adminOnly == false || isTuneAuthenticated)) { %>
<p class="flex-phone flex-center">Logging</p>
<button class="tooltip bg-color-4 mini-popup log-fmlist" data-tooltip="<strong>LOG TO FMLIST</strong><br>Clicking this button logs the current station to FMLIST's visual logbook." aria-label="Log to FMLIST" style="width: calc(100%); height: 48px;">
<i class="fa-solid fa-flag fa-lg"></i>
<span class="mini-popup-content">
Choose the DX propagation type:<br>
<a class="top-10 bg-color-3 text-bold log-fmlist-tropo" style="padding: 10px; border-radius: 15px 0 0 15px; display: inline-block;">Tropo</a><!--
--><a class="top-10 bg-color-3 text-bold log-fmlist-sporadice" style="padding: 10px; border-radius: 0 15px 15px 0; display: inline-block;">Sporadic-E</a>
</span>
</button>
<% } %>
</div>
</div>
</div>
<div class="popup-window" id="popup-panel-chat">
<div class="flex-container flex-column flex-phone flex-phone-column" style="height: calc(100%);">
<div class="popup-header hover-brighten flex-center text-medium-big"><button class="popup-close">✖</button></div>
<div class="popup-header hover-brighten flex-center">
<p class="color-4" style="margin: 0; padding-left: 10px;">Chat • <span style="color: #bada55;" id="chat-admin"></span> <strong id="chat-identity-nickname"></strong></p>
<button class="popup-close">✖</button>
</div>
<div class="popup-content text-left flex-container flex-phone flex-column" style="flex: 1;">
<div style="text-align: center;white-space-collapse: collapse;">
<div class="flex-phone flex-container flex-center top-10">
<input type="text" id="chat-nickname" name="chat-nickname" placeholder="Nickname" style="border-radius: 15px 0 0 15px;padding-top:0;padding-bottom:0;border: 2px solid var(--color-4)">
<div class="flex-phone flex-container flex-center top-10 bottom-10">
<input type="text" id="chat-nickname" name="chat-nickname" placeholder="Nickname" style="width: 150px;border-radius: 15px 0 0 15px;padding-top:0;padding-bottom:0;border: 2px solid var(--color-4)">
<button class="br-0 w-100" style="height: 48px; border-radius: 0 15px 15px 0;margin-left:-3px;" id="chat-nickname-save">Save</button>
</div>
<p style="margin: 5px;" class="text-small">
Current identity: <span style="color: #bada55;" id="chat-admin"></span> <strong id="chat-identity-nickname"></strong>
</p>
</div>
<div id="chat-chatbox" class="bg-color-1" style="padding: 10px; overflow-y: auto; flex-grow: 1; min-height: 0; flex-basis: 0; min-height: 120px;"></div>
<div id="chat-chatbox" class="bg-color-1" style="padding: 10px; overflow-y: auto; flex-grow: 1; min-height: 0; flex-basis: 0; min-height: 180px;"></div>
<div class="flex-container flex-phone">
<input class="bg-color-2" type="text" id="chat-send-message" name="chat-send-message" placeholder="Send message..." style="background-color: var(--color-2);width: 100%;">
@@ -423,6 +406,24 @@
</div>
</div>
<div id="mobileTray" class="hide-desktop">
<button class="playbutton" aria-label="Play / Stop" style="width: 64px; height: 64px; position:absolute;display: block;top: -50%;left: 50%; transform: translateX(-50%);"><i class="fa-solid fa-play fa-lg"></i></button>
<div style="width: calc(50% - 32px);text-align: center;">
<button class="users-online-container" aria-label="Online users" style="display: inline-block;"><i class="fa-solid fa-user"></i> <span class="users-online"></span></button>
<button class="chatbutton m-10" aria-label="Chatbox" style="display: inline-block;"><i class="fa-solid fa-message"></i></button>
</div>
<div style="width: 64px;text-align: center;">
</div>
<div style="width: calc(50% - 32px);text-align: center;">
<button class="m-10 button-dark tuner-mobile-settings" aria-label="Advanced tuner settings" style="display: inline-block;"><i class="fa-solid fa-toolbox"></i></button>
<button class="settings m-10" aria-label="Settings" style="display: inline-block;"><i class="fa-solid fa-bars"></i></button>
</div>
</div>
<div id="myModal" class="modal">
<div class="modal-panel">
<div class="flex-container flex-phone" style="height: calc(100% - 100px)">

View File

@@ -9,27 +9,13 @@ $(document).ready(function() {
const chatIdentityNickname = $('#chat-identity-nickname');
const chatNicknameInput = $('#chat-nickname');
const chatNicknameSave = $('#chat-nickname-save');
$(function () {
$("#popup-panel-chat").draggable({
handle: ".popup-header"
}).resizable({
minHeight: 300,
minWidth: 250
});
$(".chatbutton").on("click", function () {
$("#popup-panel-chat").fadeIn(200, function () {
$(".chatbutton").on("click", function () {
$("#popup-panel-chat").fadeIn(200, function () {
chatMessages.scrollTop(chatMessages[0].scrollHeight);
});
});
$("#popup-panel-chat .popup-close").on("click", function () {
$("#popup-panel-chat").fadeOut(200);
});
});
});
// Function to generate a random string
function generateRandomString(length) {
const characters = 'ABCDEFGHJKMNOPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz0123456789';
@@ -39,12 +25,12 @@ $(document).ready(function() {
}
return result;
}
// Load nickname from localStorage on page load
let savedNickname = localStorage.getItem('nickname') || `User ${generateRandomString(5)}`;
chatNicknameInput.val(savedNickname);
chatIdentityNickname.text(savedNickname);
chatSocket.onmessage = function(event) {
const messageData = JSON.parse(event.data);
const isAdmin = messageData.admin ? '<span style="color: #bada55">[ADMIN]</span>' : '';
@@ -59,7 +45,7 @@ $(document).ready(function() {
<span style="color: var(--color-text-2);">${$('<div/>').text(messageData.message).html()}</span><br>
`;
chatMessages.append(chatMessage);
if (chatMessages.is(':visible')) {
setTimeout(function() {
chatMessages.scrollTop(chatMessages[0].scrollHeight);
@@ -74,7 +60,7 @@ $(document).ready(function() {
}
}
};
$('.chat-send-message-btn').click(sendMessage);
chatNicknameSave.click(function() {
const currentNickname = chatNicknameInput.val().trim() || `Anonymous User ${generateRandomString(5)}`;
@@ -89,28 +75,28 @@ $(document).ready(function() {
chatMessagesCount.text(chatMessageCount);
chatButton.removeClass('blink').addClass('bg-color-1');
chatSendInput.focus();
setTimeout(function() {
chatMessages.scrollTop(chatMessages[0].scrollHeight);
}, 100);
});
chatNicknameInput.keypress(function(event) {
if (event.which === 13) {
chatNicknameSave.trigger('click');
}
});
chatSendInput.keypress(function(event) {
if (event.which === 13) {
sendMessage();
}
});
function sendMessage() {
const nickname = savedNickname || `Anonymous User ${generateRandomString(5)}`;
const message = chatSendInput.val().trim();
if (message) {
const messageData = { nickname, message };
chatSocket.send(JSON.stringify(messageData));

View File

@@ -1,110 +1,120 @@
$(document).ready(function() {
// Variables
const $dropdowns = $('.dropdown');
const $listOfOptions = $('.option');
let currentDropdown = null; // Track the currently clicked dropdown
let currentIndex = -1; // Track the currently focused option
// Functions
const toggleDropdown = (event) => {
event.stopPropagation();
const $currentDropdown = $(event.currentTarget).closest('.dropdown');
// Close the previously opened dropdown if any
$dropdowns.not($currentDropdown).removeClass('opened');
$currentDropdown.toggleClass('opened');
currentDropdown = $currentDropdown.hasClass('opened') ? $currentDropdown : null;
currentIndex = -1; // Reset the current index when toggling the dropdown
};
const selectOption = (event) => {
const $currentDropdown = currentDropdown;
switch($currentDropdown.attr('id')) {
case 'data-ant':
socket.send("Z" + $(event.currentTarget).attr('data-value'));
resetRDS(getCurrentFreq()); // Reset RDS when change antenna input
break;
case 'data-bw':
legacyBwValue = $(event.currentTarget).attr('data-value2') || "";
socket.send("F" + legacyBwValue);
socket.send("W" + $(event.currentTarget).attr('data-value'));
$currentDropdown.find('input').val($(event.currentTarget).text());
break;
default:
// Variables
const $dropdowns = $('.dropdown');
const $listOfOptions = $('.option');
let currentDropdown = null; // Track the currently clicked dropdown
let currentIndex = -1; // Track the currently focused option
// Functions
const toggleDropdown = (event) => {
event.stopPropagation();
const $currentDropdown = $(event.currentTarget).closest('.dropdown');
// Close the previously opened dropdown if any
$dropdowns.not($currentDropdown).removeClass('opened');
$currentDropdown.toggleClass('opened');
currentDropdown = $currentDropdown.hasClass('opened') ? $currentDropdown : null;
currentIndex = -1; // Reset the current index when toggling the dropdown
};
const selectOption = (event) => {
const $currentDropdown = currentDropdown;
switch($currentDropdown.attr('id')) {
case 'data-ant':
socket.send("Z" + $(event.currentTarget).attr('data-value'));
resetRDS(getCurrentFreq()); // Reset RDS when change antenna input
break;
case 'data-ant-phone':
socket.send("Z" + $(event.currentTarget).attr('data-value'));
resetRDS(getCurrentFreq()); // Reset RDS when change antenna input
break;
case 'data-bw':
legacyBwValue = $(event.currentTarget).attr('data-value2') || "";
socket.send("F" + legacyBwValue);
socket.send("W" + $(event.currentTarget).attr('data-value'));
$currentDropdown.find('input').val($(event.currentTarget).text());
break;
case 'data-bw-phone':
legacyBwValue = $(event.currentTarget).attr('data-value2') || "";
socket.send("F" + legacyBwValue);
socket.send("W" + $(event.currentTarget).attr('data-value'));
$currentDropdown.find('input').val($(event.currentTarget).text());
break;
default:
$currentDropdown.find('input')
.val($(event.currentTarget).text())
.attr('data-value', $(event.currentTarget).data('value'));
break;
}
// Use setTimeout to delay class removal
setTimeout(() => {
$currentDropdown.removeClass('opened');
currentDropdown = null;
}, 10); // Adjust the delay as needed
};
const closeDropdownFromOutside = (event) => {
const $currentDropdown = currentDropdown && $(currentDropdown);
const isClickedInsideDropdown = $currentDropdown && $currentDropdown.has(event.target).length > 0;
if (!isClickedInsideDropdown && $currentDropdown && $currentDropdown.hasClass('opened')) {
$currentDropdown.removeClass('opened');
currentDropdown = null;
}
};
const navigateOptions = (event) => {
if (!currentDropdown) return;
const $options = currentDropdown.find('.option');
switch (event.key) {
case 'ArrowDown':
event.preventDefault();
currentIndex = (currentIndex + 1) % $options.length;
$options.eq(currentIndex).focus();
break;
case 'ArrowUp':
event.preventDefault();
currentIndex = (currentIndex - 1 + $options.length) % $options.length;
$options.eq(currentIndex).focus();
break;
case 'Enter':
event.preventDefault();
$options.eq(currentIndex).click();
break;
case 'Escape':
currentDropdown.removeClass('opened');
currentDropdown = null;
currentIndex = -1;
break;
}
};
// Event Listeners
$(document).on('click', closeDropdownFromOutside);
$listOfOptions.on('click', selectOption);
$dropdowns.on('click', 'input', toggleDropdown);
$dropdowns.on('keydown', 'input', function(event) {
if (event.key === 'Enter') {
toggleDropdown(event);
}
});
$dropdowns.on('keydown', '.option', navigateOptions);
// MULTISELECT
$('.multiselect option').mousedown(function(e) {
e.preventDefault();
var originalScrollTop = $(this).parent().scrollTop();
$(this).prop('selected', $(this).prop('selected') ? false : true);
var self = this;
$(this).parent().focus();
setTimeout(function() {
$(self).parent().scrollTop(originalScrollTop);
}, 0);
return false;
});
break;
}
// Use setTimeout to delay class removal
setTimeout(() => {
$currentDropdown.removeClass('opened');
currentDropdown = null;
}, 10); // Adjust the delay as needed
};
const closeDropdownFromOutside = (event) => {
const $currentDropdown = currentDropdown && $(currentDropdown);
const isClickedInsideDropdown = $currentDropdown && $currentDropdown.has(event.target).length > 0;
if (!isClickedInsideDropdown && $currentDropdown && $currentDropdown.hasClass('opened')) {
$currentDropdown.removeClass('opened');
currentDropdown = null;
}
};
const navigateOptions = (event) => {
if (!currentDropdown) return;
const $options = currentDropdown.find('.option');
switch (event.key) {
case 'ArrowDown':
event.preventDefault();
currentIndex = (currentIndex + 1) % $options.length;
$options.eq(currentIndex).focus();
break;
case 'ArrowUp':
event.preventDefault();
currentIndex = (currentIndex - 1 + $options.length) % $options.length;
$options.eq(currentIndex).focus();
break;
case 'Enter':
event.preventDefault();
$options.eq(currentIndex).click();
break;
case 'Escape':
currentDropdown.removeClass('opened');
currentDropdown = null;
currentIndex = -1;
break;
}
};
// Event Listeners
$(document).on('click', closeDropdownFromOutside);
$listOfOptions.on('click', selectOption);
$dropdowns.on('click', 'input', toggleDropdown);
$dropdowns.on('keydown', 'input', function(event) {
if (event.key === 'Enter') {
toggleDropdown(event);
}
});
$dropdowns.on('keydown', '.option', navigateOptions);
// MULTISELECT
$('.multiselect option').mousedown(function(e) {
e.preventDefault();
var originalScrollTop = $(this).parent().scrollTop();
$(this).prop('selected', $(this).prop('selected') ? false : true);
var self = this;
$(this).parent().focus();
setTimeout(function() {
$(self).parent().scrollTop(originalScrollTop);
}, 0);
return false;
});
});

View File

@@ -1,9 +1,9 @@
var currentDate = new Date('Apr 22, 2025 21:30:00');
var currentDate = new Date('May 2, 2025 16:00:00');
var day = currentDate.getDate();
var month = currentDate.getMonth() + 1; // Months are zero-indexed, so add 1
var year = currentDate.getFullYear();
var formattedDate = day + '/' + month + '/' + year;
var currentVersion = 'v1.3.7 [' + formattedDate + ']';
var currentVersion = 'v1.3.8 [' + formattedDate + ']';
getInitialSettings();
removeUrlParameters();

View File

@@ -178,11 +178,11 @@ $(document).ready(function () {
var freqContainer = $('#freq-container')[0];
var txContainer = $('#data-station-container')[0];
$("#data-eq").click(function () {
$(".data-eq").click(function () {
toggleButtonState("eq");
});
$("#data-ims").click(function () {
$(".data-ims").click(function () {
toggleButtonState("ims");
});
@@ -207,7 +207,7 @@ $(document).ready(function () {
$('.popup-content').removeClass('show');
});
$('#log-fmlist').on('click', function() {
$('.log-fmlist').on('click', function() {
const logKey = 'fmlistLogChoice';
const logTimestampKey = 'fmlistLogTimestamp';
const expirationTime = 10 * 60 * 1000;
@@ -222,20 +222,20 @@ $(document).ready(function () {
}
if (parsedData.txInfo.dist > 700) {
$('#log-fmlist .popup-content').addClass('show'); // Show popup if no valid choice
$('.log-fmlist .mini-popup-content').addClass('show'); // Show popup if no valid choice
$('#log-fmlist-sporadice').off('click').on('click', function () {
$('.log-fmlist-sporadice').off('click').on('click', function () {
localStorage.setItem(logKey, './log_fmlist?type=sporadice');
localStorage.setItem(logTimestampKey, now);
if(parsedData.txInfo.dist > 700) sendLog('./log_fmlist?type=sporadice');
$('#log-fmlist .popup-content').removeClass('show');
$('.log-fmlist .mini-popup-content').removeClass('show');
});
$('#log-fmlist-tropo').off('click').on('click', function () {
$('.log-fmlist-tropo').off('click').on('click', function () {
localStorage.setItem(logKey, './log_fmlist?type=tropo');
localStorage.setItem(logTimestampKey, now);
if(parsedData.txInfo.dist > 700) sendLog('./log_fmlist?type=tropo');
$('#log-fmlist .popup-content').removeClass('show');
$('.log-fmlist .mini-popup-content').removeClass('show');
});
} else {
sendLog('./log_fmlist');
@@ -696,7 +696,7 @@ function checkKey(e) {
tuneUp();
break;
case 46:
let $dropdown = $("#data-ant");
let $dropdown = $(".data-ant");
let $input = $dropdown.find("input");
let $options = $dropdown.find("ul.options .option");
@@ -870,8 +870,8 @@ const $dataPs = $('#data-ps');
const $dataSt = $('.data-st');
const $dataRt0 = $('#data-rt0 span');
const $dataRt1 = $('#data-rt1 span');
const $dataAntInput = $('#data-ant input');
const $dataBwInput = $('#data-bw input');
const $dataAntInput = $('.data-ant input');
const $dataBwInput = $('.data-bw input');
const $dataStationContainer = $('#data-station-container');
const $dataTp = $('.data-tp');
const $dataTa = $('.data-ta');
@@ -961,12 +961,12 @@ const updateDataElements = throttle(function(parsedData) {
$('.data-flag').html(`<i title="${parsedData.country_name}" class="flag-sm flag-sm-${parsedData.country_iso}"></i>`);
$('.data-flag-big').html(`<i title="${parsedData.country_name}" class="flag-md flag-md-${parsedData.country_iso}"></i>`);
$dataAntInput.val($('#data-ant li[data-value="' + parsedData.ant + '"]').text());
$dataAntInput.val($('.data-ant li[data-value="' + parsedData.ant + '"]').first().text());
if(parsedData.bw < 500) {
$dataBwInput.val($('#data-bw li[data-value2="' + parsedData.bw + '"]').text());
if (parsedData.bw < 500) {
$dataBwInput.val($('.data-bw li[data-value2="' + parsedData.bw + '"]').first().text());
} else {
$dataBwInput.val($('#data-bw li[data-value="' + parsedData.bw + '"]').text());
$dataBwInput.val($('.data-bw li[data-value="' + parsedData.bw + '"]').first().text());
}
if (parsedData.txInfo.tx.length > 1) {
@@ -984,9 +984,9 @@ const updateDataElements = throttle(function(parsedData) {
}
if(parsedData.txInfo.tx.length > 1 && parsedData.txInfo.dist > 150 && parsedData.txInfo.dist < 4000) {
$('#log-fmlist').removeAttr('disabled').removeClass('btn-disabled cursor-disabled');
$('.log-fmlist').removeAttr('disabled').removeClass('btn-disabled cursor-disabled');
} else {
$('#log-fmlist').attr('disabled', 'true').addClass('btn-disabled cursor-disabled');
$('.log-fmlist').attr('disabled', 'true').addClass('btn-disabled cursor-disabled');
}
updateHtmlIfChanged($('#data-regular-pi'), parsedData.txInfo.reg === true ? parsedData.txInfo.pi : '&nbsp;');
@@ -1006,7 +1006,7 @@ const updateDataElements = throttle(function(parsedData) {
$dataPs.attr('aria-label', parsedData.ps);
$dataRt0.attr('aria-label', parsedData.rt0);
$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);
}
}, 75); // Update at most once every 100 milliseconds
@@ -1067,15 +1067,25 @@ function createListItem(element) {
function updateButtonState(buttonId, value) {
var button = $("#" + buttonId);
if (value == 0) {
button.hasClass("btn-disabled") ? null : button.addClass("btn-disabled");
button.attr('aria-description', 'Off');
if (button.length === 0) {
button = $("." + buttonId);
}
if (button.length > 0) {
if (value == 0) {
button.hasClass("btn-disabled") ? null : button.addClass("btn-disabled");
button.attr('aria-description', 'Off');
} else {
button.hasClass("btn-disabled") ? button.removeClass("btn-disabled") : null;
button.attr('aria-description', 'On');
}
} else {
button.hasClass("btn-disabled") ? button.removeClass("btn-disabled") : null;
button.attr('aria-description', 'On');
console.log("Button not found!");
}
}
function toggleButtonState(buttonId) {
parsedData[buttonId] = 1 - parsedData[buttonId]; // Toggle between 0 and 1
updateButtonState(buttonId, parsedData[buttonId]);
@@ -1122,9 +1132,6 @@ function showTunerDescription() {
if ($(window).width() < 768) {
$('.dashboard-panel-plugin-list').slideToggle(300);
$('#users-online-container').slideToggle(300);
$('.chatbutton').slideToggle(300);
$('#settings').slideToggle(300);
}
}

View File

@@ -1,11 +1,17 @@
$(document).ready(function() {
// Cache jQuery objects for reuse
var modal = $("#myModal");
var modalPanel = $(".modal-panel");
var openBtn = $("#settings");
var openBtn = $(".settings");
var closeBtn = $(".closeModal, .closeModalButton");
// Function to open the modal
initPopups();
openBtn.on("click", function() {
openModal(modalPanel);
});
closeBtn.on("click", closeModal);
function openModal(panel) {
modal.css("display", "block");
panel.css("display", "block");
@@ -22,25 +28,37 @@ $(document).ready(function() {
$("body").removeClass("modal-open"); // Enable body scrolling
}, 300);
}
// Event listeners for the open and close buttons
openBtn.on("click", function() {
openModal(modalPanel);
});
closeBtn.on("click", closeModal);
// Close the modal when clicking outside of it
$(document).on("click", function(event) {
$(document).on("click", function(event) { // Close the modal when clicking outside of it
if ($(event.target).is(modal)) {
closeModal();
}
});
// Close the modal when pressing ESC key
$(document).on("keydown", function(event) {
$(document).on("keydown", function(event) { // Close the modal when pressing ESC key
if (event.key === "Escape") {
closeModal();
}
});
});
$(".tuner-mobile-settings").on("click", function () {
$(".popup-window").fadeOut(200);
$("#popup-panel-mobile-settings").fadeIn(200);
});
});
function initPopups() {
$(".popup-window").draggable({
handle: ".popup-header",
containment: "body"
}).resizable({
minHeight: 330,
minWidth: 350,
containment: "body"
});
$(".popup-close").on("click", function () {
$(".popup-window").fadeOut(200);
});
}

View File

@@ -560,6 +560,8 @@
<p>FMLIST integration allows you to get potential DXes logged on the <a href="http://fmlist.org/fm_logmap.php?hours=900" target="_blank" class="text-bold color-4">FMLIST Visual Logbook</a>.<br>
Your server also needs to have a valid UUID, which is obtained by registering on maps in the <strong>Identification & Map</strong> tab.</p>
<%- include('_components', {component: 'checkbox', cssClass: 'm-right-10', label: 'FMLIST integration', id: 'extras-fmlistIntegration'}) %><br>
<p>If you don't feel comfortable with the general public logging on your server, you can make this feature available only for people with a password</p>
<%- include('_components', {component: 'checkbox', cssClass: 'm-right-10', label: 'Admin-only logging', id: 'extras-fmlistAdminOnly'}) %><br>
<p>You can also fill in your OMID from FMLIST.org, if you want the logs to be saved to your account.</p>
<%- include('_components', {component: 'text', cssClass: 'w-100 br-15', placeholder: '', label: 'OMID', id: 'extras-fmlistOmid'}) %>