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
ui changes + map preparation + bugfixes
This commit is contained in:
5
index.js
5
index.js
@@ -48,7 +48,9 @@ let serverConfig = {
|
||||
tunerName: "",
|
||||
tunerDesc: "",
|
||||
lat: "0",
|
||||
lon: "0"
|
||||
lon: "0",
|
||||
broadcastTuner: false,
|
||||
proxyIp: "",
|
||||
},
|
||||
password: {
|
||||
tunePass: "",
|
||||
@@ -404,6 +406,7 @@ wss.on('connection', (ws, request) => {
|
||||
|
||||
ws.on('close', (code, reason) => {
|
||||
currentUsers--;
|
||||
dataHandler.showOnlineUsers(currentUsers);
|
||||
logInfo(`Web client \x1b[31mdisconnected\x1b[0m (${clientIp}) \x1b[90m[${currentUsers}]`);
|
||||
});
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ const fetchInterval = 3000;
|
||||
// Fetch data from maps
|
||||
function fetchTx(freq, piCode, rdsPs) {
|
||||
const now = Date.now();
|
||||
|
||||
freq = parseFloat(freq);
|
||||
// Check if it's been at least 3 seconds since the last fetch and if the QTH is correct
|
||||
if (now - lastFetchTime < fetchInterval || serverConfig.identification.lat.length < 2) {
|
||||
if (now - lastFetchTime < fetchInterval || serverConfig.identification.lat.length < 2 || freq < 87) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
|
||||
@@ -137,6 +137,10 @@
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.top-25 {
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
.bottom-20 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@@ -68,6 +68,60 @@
|
||||
background: var(--color-5);
|
||||
}
|
||||
|
||||
.modal-panel {
|
||||
width: 450px;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
text-align: center;
|
||||
background-color: var(--color-main);
|
||||
}
|
||||
|
||||
.modal-panel .flex-container {
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.modal-panel h1 {
|
||||
font-size: 42px;
|
||||
}
|
||||
|
||||
.modal-panel-sidebar {
|
||||
width: 64px;
|
||||
background-color: var(--color-1);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.modal-panel-content {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.modal-panel-content .version-info {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.modal-panel-footer {
|
||||
width: 450px;
|
||||
height: 100px;
|
||||
background-color: var(--color-main);
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.modal-panel .form-group {
|
||||
float: none !important;
|
||||
}
|
||||
|
||||
.modal-panel .dropdown {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.modal-panel label {
|
||||
width: 200px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.modal-content {
|
||||
min-width: 90% !important;
|
||||
@@ -83,4 +137,17 @@
|
||||
#closeModalButton {
|
||||
position: static;
|
||||
}
|
||||
.modal-panel {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-height: 768px) {
|
||||
.modal-panel .version-info {
|
||||
position: static;
|
||||
bottom: auto;
|
||||
}
|
||||
.modal-panel-content {
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
@@ -195,13 +195,15 @@
|
||||
<button id="settings" aria-label="Settings"><i class="fa-solid fa-gear"></i></button>
|
||||
<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>
|
||||
|
||||
<div id="myModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<span class="modal-title">Settings</span>
|
||||
<span class="close" id="closeModal"><i class="fa-solid fa-xmark"></i></span>
|
||||
<div id="myModal" class="modal">
|
||||
<div class="modal-panel">
|
||||
<div class="flex-container flex-phone" style="height: calc(100% - 100px)">
|
||||
<div class="modal-panel-sidebar hover-brighten flex-center text-medium-big" id="closeModal"><i class="fa-solid fa-chevron-right"></i></div>
|
||||
<div class="modal-panel-content">
|
||||
<h1 class="top-25">Settings</h1>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="themes" style="margin-top: 50px;"><i class="fa-solid fa-palette"></i> Theme:</label>
|
||||
<div class="form-group top-25">
|
||||
<label for="themes"><i class="fa-solid fa-palette"></i> Theme:</label>
|
||||
<div class="dropdown" id="theme-selector">
|
||||
<input type="text" placeholder="Theme" readonly />
|
||||
<ul class="options">
|
||||
@@ -216,9 +218,9 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="signal" style="margin-top: 50px;"><i class="fa-solid fa-signal"></i> Signal units:</label>
|
||||
|
||||
<div class="form-group top-25">
|
||||
<label for="signal"><i class="fa-solid fa-signal"></i> Signal units:</label>
|
||||
<div class="dropdown" id="signal-selector">
|
||||
<input type="text" placeholder="Signal Units" readonly />
|
||||
<ul class="options">
|
||||
@@ -228,39 +230,60 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group bottom-20 hide-desktop" style="float: none;">
|
||||
<label for="themes"><i class="fa-solid fa-user"></i> Users online:</label>
|
||||
<span class="users-online" name="users-online">0</span>
|
||||
</div>
|
||||
|
||||
|
||||
<% if (isAdminAuthenticated) { %>
|
||||
<p>You are logged in as an adminstrator. <a href="./setup">Setup</a> | <a class="logout-link" href="#">Logout</a></p>
|
||||
<% } else if (isTuneAuthenticated) { %>
|
||||
<p>You are logged in and can control the receiver. <a class="logout-link" href="#">Logout</a></p>
|
||||
<% } else { %>
|
||||
<form action="./login" method="post" id="login-form">
|
||||
<label for="password">Password:</label>
|
||||
<input type="password" id="password" name="password" required>
|
||||
<button type="submit" class="br-0 w-100" style="height: 44px">Login</button>
|
||||
</form>
|
||||
<% } %>
|
||||
<div id="login-message"></div>
|
||||
|
||||
<div class="flex-container flex-left text-left hover-brighten p-10 br-5" onclick="window.open('https://discord.com/invite/ZAVNdS74mC')">
|
||||
<i class="fa-brands fa-discord"></i> <span>Join our <strong>OpenRadio Discord</strong> community!</span>
|
||||
</div>
|
||||
<div class="flex-container flex-left text-left bottom-20 hover-brighten p-10 br-5" onclick="window.open('https://buymeacoffee.com/noobish')">
|
||||
<i class="fa-solid fa-hand-holding-medical"></i> <span><strong>Support</strong> the developer!</span>
|
||||
</div>
|
||||
<p class="text-small">FM-DX WebServer <span style="color: var(--color-3);">v1.0.3 [7/2/2024]</span> by <a href="https://noobish.eu" target="_blank">Noobish</a> & the OpenRadio community.</p>
|
||||
<p class="text-small bottom-50">This app works thanks to these amazing projects: <br>
|
||||
<span class="text-smaller">- librdsparser & maps.fmdx.pl by <a href="https://fmdx.pl" target="_blank">Konrad Kosmatka</a></span><br>
|
||||
<span class="text-smaller">- 3LAS by <a href="https://github.com/JoJoBond/3LAS" target="_blank">JoJoBond</a></span><br>
|
||||
<span class="text-smaller">- flat-flags by <a href="https://github.com/luishdez/flat-flags/tree/master" target="_blank">luishdez</a></span><br></p>
|
||||
<button class="button-close" id="closeModalButton">Close</button>
|
||||
<p>You are logged in as an adminstrator.<br>
|
||||
<a href="./setup">Setup</a> • <a class="logout-link" href="#">Logout</a>
|
||||
</p>
|
||||
<% } else if (isTuneAuthenticated) { %>
|
||||
<p>You are logged in and can control the receiver. <a class="logout-link" href="#">Logout</a></p>
|
||||
<% } else { %>
|
||||
<form action="./login" method="post" id="login-form" class="top-25">
|
||||
<label for="password">Password:</label>
|
||||
<input type="password" id="password" name="password" style="width: 200px;" required><br>
|
||||
<button type="submit" class="br-0 w-100 top-10" style="height: 44px">Login</button>
|
||||
</form>
|
||||
<% } %>
|
||||
<div id="login-message"></div>
|
||||
|
||||
<div class="version-info">
|
||||
<p class="text-small">FM-DX WebServer <br>by <a href="https://noobish.eu" target="_blank">Noobish</a> & the OpenRadio community.</p>
|
||||
<span style="color: var(--color-3);">v1.0.4 [8/2/2024]</span>
|
||||
<p class="text-small bottom-50">
|
||||
<span class="text-smaller">librds & maps.fmdx.pl by <a href="https://fmdx.pl" target="_blank">Konrad Kosmatka</a></span><br>
|
||||
<span class="text-smaller">3LAS by <a href="https://github.com/JoJoBond/3LAS" target="_blank">JoJoBond</a></span><br>
|
||||
<span class="text-smaller">flat-flags by <a href="https://github.com/luishdez/flat-flags/tree/master" target="_blank">luishdez</a></span><br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="js/webserver.js"></script>
|
||||
<div class="modal-panel-footer flex-container flex-phone">
|
||||
<div class="modal-panel-sidebar" style="font-size: 22px;">
|
||||
<div class="flex-center" style="height: 50px">
|
||||
<i class="fa-solid fa-hand-holding-medical"></i>
|
||||
</div>
|
||||
<div class="flex-center" style="height: 50px">
|
||||
<i class="fa-brands fa-discord"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-panel-content">
|
||||
<div class="hover-brighten br-0" style="height: 50px;padding:12px;" onclick="window.open('https://buymeacoffee.com/noobish')">
|
||||
<strong>Support</strong> the developer!
|
||||
</div>
|
||||
<div class="hover-brighten br-0" style="height: 50px;padding:12px;"onclick="window.open('https://discord.com/invite/ZAVNdS74mC')">
|
||||
Join our <strong>OpenRadio Discord</strong> community!
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="js/webserver.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -39,6 +39,23 @@ $(document).ready(function() {
|
||||
signalText.text('dBm');
|
||||
}
|
||||
});
|
||||
|
||||
var input = $("#tuner-desc").text();
|
||||
var parsed = input;
|
||||
|
||||
var grayTextRegex = /--(.*?)--/g;
|
||||
parsed = parsed.replace(grayTextRegex, '<span class="text-gray">$1</span>');
|
||||
|
||||
var boldRegex = /\*\*(.*?)\*\*/g;
|
||||
parsed = parsed.replace(boldRegex, '<strong>$1</strong>');
|
||||
|
||||
var italicRegex = /\*(.*?)\*/g;
|
||||
parsed = parsed.replace(italicRegex, '<em>$1</em>');
|
||||
|
||||
var breakLineRegex = /\\n/g;
|
||||
parsed = parsed.replace(breakLineRegex, '<br>');
|
||||
|
||||
$("#tuner-desc").html(parsed);
|
||||
|
||||
const textInput = $('#commandinput');
|
||||
|
||||
@@ -298,24 +315,34 @@ function getCurrentFreq() {
|
||||
|
||||
function checkKey(e) {
|
||||
e = e || window.event;
|
||||
|
||||
|
||||
// Check if any input element is focused using jQuery
|
||||
if ($('input:focus').length > 0) {
|
||||
return; // Do nothing if an input is focused
|
||||
}
|
||||
|
||||
getCurrentFreq();
|
||||
|
||||
|
||||
if (socket.readyState === WebSocket.OPEN) {
|
||||
if (e.keyCode == '82') { // RDS Reset (R key)
|
||||
socket.send("T" + (currentFreq.toFixed(1) * 1000));
|
||||
}
|
||||
if (e.keyCode == '38') {
|
||||
socket.send("T" + ((currentFreq + 0.01).toFixed(2) * 1000));
|
||||
}
|
||||
else if (e.keyCode == '40') {
|
||||
socket.send("T" + ((currentFreq - 0.01).toFixed(2) * 1000));
|
||||
}
|
||||
else if (e.keyCode == '37') {
|
||||
tuneDown();
|
||||
}
|
||||
else if (e.keyCode == '39') {
|
||||
tuneUp();
|
||||
switch (e.keyCode) {
|
||||
case 82: // RDS Reset (R key)
|
||||
socket.send("T" + (currentFreq.toFixed(1) * 1000));
|
||||
break;
|
||||
case 38:
|
||||
socket.send("T" + ((currentFreq + 0.01).toFixed(2) * 1000));
|
||||
break;
|
||||
case 40:
|
||||
socket.send("T" + ((currentFreq - 0.01).toFixed(2) * 1000));
|
||||
break;
|
||||
case 37:
|
||||
tuneDown();
|
||||
break;
|
||||
case 39:
|
||||
tuneUp();
|
||||
break;
|
||||
default:
|
||||
// Handle default case if needed
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,14 +6,14 @@ var mapAttrib='© <a href="http://www.openstreetmap.org/copyright">OpenStree
|
||||
// add map container
|
||||
|
||||
$(document).ready(function() {
|
||||
MapCreate();
|
||||
fetchData();
|
||||
|
||||
|
||||
map.on('click', function(ev) {
|
||||
MapCreate();
|
||||
fetchData();
|
||||
|
||||
|
||||
map.on('click', function(ev) {
|
||||
$('#lat').val((ev.latlng.lat).toFixed(6));
|
||||
$('#lng').val((ev.latlng.lng).toFixed(6));
|
||||
|
||||
|
||||
if (typeof pin == "object") {
|
||||
pin.setLatLng(ev.latlng);
|
||||
} else {
|
||||
@@ -81,7 +81,31 @@ $(document).ready(function() {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function stripAnsi(str) {
|
||||
return str.replace(/\u001b\[\d+m/g, '');
|
||||
}
|
||||
|
||||
$("pre").html(function(_, html) {
|
||||
html = stripAnsi(html);
|
||||
return html.replace(/\[(\d{2}:\d{2})\]|\[(INFO|DEBUG|WARN|ERROR)\]/g, function(match, time, level) {
|
||||
if (time) {
|
||||
return "<span style='color: gray;'>" + match + "</span>";
|
||||
} else if (level === "INFO") {
|
||||
return "<span style='color: lime;'>" + match + "</span>";
|
||||
} else if (level === "DEBUG") {
|
||||
return "<span style='color: cyan;'>" + match + "</span>";
|
||||
} else if (level === "WARN") {
|
||||
return "<span style='color: yellow;'>" + match + "</span>";
|
||||
} else if (level === "ERROR") {
|
||||
return "<span style='color: red;'>" + match + "</span>";
|
||||
} else {
|
||||
return "<span style='color: white;'>" + match + "</span>";
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#console-output").scrollTop($("#console-output")[0].scrollHeight);
|
||||
});
|
||||
|
||||
function MapCreate() {
|
||||
@@ -112,9 +136,6 @@ function fetchData() {
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
// Save the received JSON data to a local variable (you may want to handle this differently on the client)
|
||||
console.log('Received data:', data);
|
||||
|
||||
$('#webserver-ip').val(data.webserver.webserverIp);
|
||||
$('#webserver-port').val(data.webserver.webserverPort);
|
||||
$('#audio-port').val(data.webserver.audioPort);
|
||||
@@ -131,6 +152,8 @@ function fetchData() {
|
||||
$('#webserver-desc').val(data.identification.tunerDesc);
|
||||
$('#lat').val(data.identification.lat);
|
||||
$('#lng').val(data.identification.lon);
|
||||
$("#broadcast-tuner").prop("checked", data.identification.broadcastTuner);
|
||||
$("#broadcast-address").val(data.identification.proxyIp);
|
||||
|
||||
$('#tune-pass').val(data.password.tunePass);
|
||||
$('#admin-pass').val(data.password.adminPass);
|
||||
@@ -183,6 +206,8 @@ function submitData() {
|
||||
const tunerDesc = $('#webserver-desc').val() || 'Default FM tuner description';
|
||||
const lat = $('#lat').val();
|
||||
const lon = $('#lng').val();
|
||||
const broadcastTuner = $("#broadcast-tuner").is(":checked");
|
||||
const proxyIp = $("#broadcast-address").val();
|
||||
|
||||
const tunePass = $('#tune-pass').val();
|
||||
const adminPass = $('#admin-pass').val();
|
||||
@@ -211,6 +236,8 @@ function submitData() {
|
||||
tunerDesc,
|
||||
lat,
|
||||
lon,
|
||||
broadcastTuner,
|
||||
proxyIp
|
||||
},
|
||||
password: {
|
||||
tunePass,
|
||||
@@ -239,3 +266,6 @@ function submitData() {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -106,7 +106,18 @@
|
||||
<input style="width: 100%; max-width: 768px;" class="input-text" type="text" name="webserver-name" id="webserver-name" placeholder="Fill your server name here.">
|
||||
<br>
|
||||
<label for="webserver-desc" style="width: 100%;max-width: 768px; margin: auto;">Webserver description:</label>
|
||||
<textarea id="webserver-desc" name="webserver-desc" placeholder="Fill the server description here. You can put useful info here such as your antenna setup."></textarea>
|
||||
<textarea id="webserver-desc" name="webserver-desc" placeholder="Fill the server description here. You can put useful info here such as your antenna setup. You can use simple markdown."></textarea>
|
||||
</div>
|
||||
|
||||
<h3>Map broadcast:</h3>
|
||||
<p>If your tuner is set to public and other information is filled, you can add your tuner to a public map.</p>
|
||||
<div class="form-group checkbox">
|
||||
<input type="checkbox" id="broadcast-tuner">
|
||||
<label for="broadcast-tuner">Broadcast to map</label>
|
||||
</div><br>
|
||||
<div class="form-group">
|
||||
<label for="broadcast-address">Broadcast address (if using a proxy):</label>
|
||||
<input class="input-text" type="text" name="broadcast-address" id="broadcast-address">
|
||||
</div>
|
||||
|
||||
<h3>Tuner location:</h3>
|
||||
@@ -181,34 +192,6 @@
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
function stripAnsi(str) {
|
||||
return str.replace(/\u001b\[\d+m/g, '');
|
||||
}
|
||||
|
||||
$("pre").html(function(_, html) {
|
||||
html = stripAnsi(html);
|
||||
return html.replace(/\[(\d{2}:\d{2})\]|\[(INFO|DEBUG|WARN|ERROR)\]/g, function(match, time, level) {
|
||||
if (time) {
|
||||
return "<span style='color: gray;'>" + match + "</span>";
|
||||
} else if (level === "INFO") {
|
||||
return "<span style='color: lime;'>" + match + "</span>";
|
||||
} else if (level === "DEBUG") {
|
||||
return "<span style='color: cyan;'>" + match + "</span>";
|
||||
} else if (level === "WARN") {
|
||||
return "<span style='color: yellow;'>" + match + "</span>";
|
||||
} else if (level === "ERROR") {
|
||||
return "<span style='color: red;'>" + match + "</span>";
|
||||
} else {
|
||||
return "<span style='color: white;'>" + match + "</span>";
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#console-output").scrollTop($("#console-output")[0].scrollHeight);
|
||||
});
|
||||
</script>
|
||||
<script src="js/settings.js"></script>
|
||||
<script src="js/dropdown.js"></script>
|
||||
<script src="js/setup.js"></script>
|
||||
|
||||
Reference in New Issue
Block a user