You've already forked fm-dx-webserver
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:
972
package-lock.json
generated
972
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "fm-dx-webserver",
|
||||
"version": "1.3.7",
|
||||
"version": "1.3.8",
|
||||
"description": "FM DX Webserver",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -85,6 +85,7 @@ let serverConfig = {
|
||||
},
|
||||
extras: {
|
||||
fmlistIntegration: true,
|
||||
fmlistAdminOnly: false,
|
||||
fmlistOmid: "",
|
||||
},
|
||||
tunnel: {
|
||||
|
||||
@@ -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
70
web/_bwSwitch.ejs
Normal 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>
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -157,4 +157,8 @@ hr {
|
||||
.setup-wrapper h2 {
|
||||
display: initial;
|
||||
}
|
||||
|
||||
.wrapper-outer {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
@@ -194,6 +194,7 @@ body.modal-open {
|
||||
|
||||
.popup-window {
|
||||
width: 90%;
|
||||
height: 60%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
209
web/index.ejs
209
web/index.ejs
@@ -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)">
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 : ' ');
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
@@ -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'}) %>
|
||||
|
||||
Reference in New Issue
Block a user