diff --git a/web/js/api.js b/web/js/api.js
new file mode 100644
index 0000000..ae856a1
--- /dev/null
+++ b/web/js/api.js
@@ -0,0 +1,48 @@
+
+function tuneUp() {
+ if (socket.readyState === WebSocket.OPEN) {
+ getCurrentFreq();
+ let addVal = 0;
+ if (currentFreq < 0.52) {
+ addVal = 9 - (Math.round(currentFreq*1000) % 9);
+ } else if (currentFreq < 1.71) {
+ // TODO: Rework to replace 9 with 9 or 10 based on regionalisation setting
+ addVal = 9 - (Math.round(currentFreq*1000) % 9);
+ } else if (currentFreq < 29.6) {
+ addVal = 5 - (Math.round(currentFreq*1000) % 5);
+ } else if (currentFreq >= 65.9 && currentFreq < 74) {
+ addVal = 30 - ((Math.round(currentFreq*1000) - 65900) % 30);
+ } else {
+ addVal = 100 - (Math.round(currentFreq*1000) % 100);
+ }
+ socket.send("T" + (Math.round(currentFreq*1000) + addVal));
+ }
+}
+
+function tuneDown() {
+ if (socket.readyState === WebSocket.OPEN) {
+ getCurrentFreq();
+ let subVal = 0;
+ if (currentFreq < 0.52) {
+ subVal = (Math.round(currentFreq*1000) % 9 == 0) ? 9 : (Math.round(currentFreq*1000) % 9);
+ } else if (currentFreq < 1.71) {
+ // TODO: Rework to replace 9 with 9 or 10 based on regionalisation setting
+ subVal = (Math.round(currentFreq*1000) % 9 == 0) ? 9 : (Math.round(currentFreq*1000) % 9);
+ } else if (currentFreq < 29.6) {
+ subVal = (Math.round(currentFreq*1000) % 5 == 0) ? 5 : (Math.round(currentFreq*1000) % 5);
+ } else if (currentFreq > 65.9 && currentFreq <= 74) {
+ subVal = ((Math.round(currentFreq*1000) - 65900) % 30 == 0) ? 30 : ((Math.round(currentFreq*1000) - 65900) % 30);
+ } else {
+ subVal = (Math.round(currentFreq*1000) % 100 == 0) ? 100 : (Math.round(currentFreq*1000) % 100);
+ }
+ socket.send("T" + (Math.round(currentFreq*1000) - subVal));
+ }
+}
+
+function tuneTo(freq) {
+ socket.send("T" + ((freq).toFixed(1) * 1000));
+}
+
+function resetRDS() {
+ socket.send("T0");
+}
diff --git a/web/js/confighandler.js b/web/js/confighandler.js
index a8c6436..7f27330 100644
--- a/web/js/confighandler.js
+++ b/web/js/confighandler.js
@@ -1,358 +1,123 @@
-function submitData() {
- const webserverIp = $('#webserver-ip').val() || '0.0.0.0';
- const webserverPort = $('#webserver-port').val() || '8080';
- const tuningLimit = $('#tuning-limit').is(":checked") || false;
- const tuningLowerLimit = $('#tuning-lower-limit').val() || '0';
- const tuningUpperLimit = $('#tuning-upper-limit').val() || '108';
- const chatEnabled = $("#chat-switch").length > 0 ? $("#chat-switch").is(":checked") : true;
+let configData = {}; // Store the original data structure globally
- var themeSelectedValue = $("#selected-theme").val();
- var themeDataValue = $(".option:contains('" + themeSelectedValue + "')").attr('data-value') || 'theme1';
- const defaultTheme = themeDataValue;
+$(document).ready(function() {
+ fetchConfig();
+});
- const bgImage = $("#bg-image").val() || '';
- const rdsMode = $('#rds-mode').is(":checked") || false;
-
- const ant1enabled = $('#ant1-enabled').is(":checked") || false;
- const ant2enabled = $('#ant2-enabled').is(":checked") || false;
- const ant3enabled = $('#ant3-enabled').is(":checked") || false;
- const ant4enabled = $('#ant4-enabled').is(":checked") || false;
-
- const ant1name = $("#ant1-name").val() || 'Ant A';
- const ant2name = $("#ant2-name").val() || 'Ant B';
- const ant3name = $("#ant3-name").val() || 'Ant C';
- const ant4name = $("#ant4-name").val() || 'Ant D';
-
- let presets = [];
- presets.push($('#preset1').val() || '87.5');
- presets.push($('#preset2').val() || '87.5');
- presets.push($('#preset3').val() || '87.5');
- presets.push($('#preset4').val() || '87.5');
-
- const enableDefaultFreq = $('#default-freq-enable').is(":checked") || false;
- const defaultFreq = $('#default-freq').val() || '87.5';
-
- let banlist = [];
- if($('#ip-addresses').length > 0) {
- validateAndAdd(banlist);
- }
-
-
- var comDevicesValue = $("#com-devices").val();
- var comDevicesDataValue = $(".option:contains('" + comDevicesValue + "')").attr('data-value') || '';
- const comPort = comDevicesDataValue;
- const wirelessConnection = $('#connection-type-toggle').is(":checked") || false;
- const xdrdIp = $('#xdrd-ip').val() || '127.0.0.1';
- const xdrdPort = $('#xdrd-port').val() || '7373';
- const xdrdPassword = $('#xdrd-password').val() || 'password';
-
- const audioDevice = $('#audio-devices').val() || 'Microphone (High Definition Audio Device)';
- const audioChannels = ($('.options .option').filter(function() {
- return $(this).text() === $('#audio-channels').val();
- }).data('value') || 2);
- const audioBitrate = ($('.options .option').filter(function() {
- return $(this).text() === $('#audio-quality').val();
- }).data('value') || "192k");
-
- const device = ($('.options .option').filter(function() {
- return $(this).text() === $('#device-type').val();
- }).data('value') || "tef");
-
- const softwareMode = $('#audio-software-mode').is(":checked") || false;
- const startupVolume = $('#startup-volume').val() || '1';
-
- const tunerName = $('#webserver-name').val() || 'FM Tuner';
- const tunerDesc = $('#webserver-desc').val() || 'Default FM tuner description';
- const broadcastTuner = $("#broadcast-tuner").is(":checked");
- const contact = $("#owner-contact").val() || '';
- const lat = $('#lat').val();
- const lon = $('#lng').val();
- const proxyIp = $("#broadcast-address").val();
-
- const tunePass = $('#tune-pass').val();
- const adminPass = $('#admin-pass').val();
-
- let plugins = [];
- $('#plugin-list option:selected').each(function() {
- plugins.push($(this).data('name'));
- });
-
- const fmlistIntegration = $("#fmlist-integration").is(":checked") || false;
- const fmlistOmid = $('#fmlist-omid').val();
-
- const tunnelUsername = $('#tunnel-username').val();
- const tunnelSubdomain = $('#tunnel-subdomain').val();
- const tunnelToken = $('#tunnel-token').val();
- const tunnelEnabled = $("#tunnel-enable").is(":checked");
- const tunnelLowLatency = $("#tunnel-lowlatency").is(":checked");
-
- const publicTuner = $("#tuner-public").is(":checked");
- const lockToAdmin = $("#tuner-lock").is(":checked");
- const autoShutdown = $("#shutdown-tuner").is(":checked") || false;
- const antennasEnabled = $("#antenna-switch").is(":checked") || false;
- const bwSwitch = $("#toggle-bw").is(":checked") || false;
-
- const data = {
- webserver: {
- webserverIp,
- webserverPort,
- tuningLimit,
- tuningLowerLimit,
- tuningUpperLimit,
- chatEnabled,
- defaultTheme,
- presets,
- banlist,
- bgImage,
- rdsMode,
- },
- antennas: {
- enabled: antennasEnabled,
- ant1: {
- enabled: ant1enabled,
- name: ant1name
- },
- ant2: {
- enabled: ant2enabled,
- name: ant2name
- },
- ant3: {
- enabled: ant3enabled,
- name: ant3name
- },
- ant4: {
- enabled: ant4enabled,
- name: ant4name
- },
- },
- xdrd: {
- comPort,
- wirelessConnection,
- xdrdIp,
- xdrdPort,
- xdrdPassword
- },
- audio: {
- audioDevice,
- audioChannels,
- audioBitrate,
- softwareMode,
- startupVolume,
- },
- identification: {
- tunerName,
- tunerDesc,
- broadcastTuner,
- contact,
- lat,
- lon,
- proxyIp
- },
- password: {
- tunePass,
- adminPass,
- },
- extras: {
- fmlistIntegration,
- fmlistOmid,
- },
- tunnel: {
- enabled: tunnelEnabled,
- username: tunnelUsername,
- token: tunnelToken,
- lowLatencyMode: tunnelLowLatency,
- subdomain: tunnelSubdomain,
- },
- plugins,
- device,
- publicTuner,
- lockToAdmin,
- autoShutdown,
- enableDefaultFreq,
- defaultFreq,
- bwSwitch,
- };
-
- if(adminPass.length < 1) {
- alert('You need to fill in the admin password before continuing further.');
- return;
- }
- // Send data to the server using jQuery
- $.ajax({
- url: './saveData',
- type: 'POST',
- contentType: 'application/json',
- data: JSON.stringify(data),
- success: function (message) {
- sendToast('success', 'Data saved!', message, true, true);
- },
- error: function (error) {
- console.error(error);
- }
- });
+function submitConfig() {
+ updateConfigData(configData);
+ if (!configData.password || !configData.password.adminPass) {
+ alert('You need to fill in the admin password before continuing further.');
+ return;
}
- function fetchData() {
- fetch("./getData")
- .then(response => {
- if (!response.ok) {
- throw new Error(`HTTP error! Status: ${response.status}`);
- }
- return response.json();
- })
- .then(data => {
- $('#webserver-ip').val(data.webserver.webserverIp);
- $('#webserver-port').val(data.webserver.webserverPort);
- $('#tuning-limit').prop("checked", data.webserver.tuningLimit);
- $('#tuning-lower-limit').val(data.webserver.tuningLowerLimit || "");
- $('#tuning-upper-limit').val(data.webserver.tuningUpperLimit || "");
- $("#chat-switch").prop("checked", data.webserver.chatEnabled || false);
-
- $('#selected-theme').val(data.webserver.defaultTheme || 'Default');
- $('#rds-mode').prop("checked", data.webserver.rdsMode || false);
-
- var selectedTheme = $(".option[data-value='" + data.webserver.defaultTheme + "']");
-
- // If the option exists, set its text as the value of the input
- if (selectedTheme.length > 0) {
- $("#selected-theme").val(selectedTheme.text());
- }
-
- if (data.webserver.bgImage && data.webserver.bgImage.length > 0) {
- $("#bg-image").val(data.webserver.bgImage);
- }
-
- if(Array.isArray(data.webserver.presets)) {
- $('#preset1').val(data.webserver.presets[0] || "");
- $('#preset2').val(data.webserver.presets[1] || "");
- $('#preset3').val(data.webserver.presets[2] || "");
- $('#preset4').val(data.webserver.presets[3] || "");
- }
-
- $("#default-freq-enable").prop("checked", data.enableDefaultFreq || false);
- $('#default-freq').val(data.defaultFreq || "87.5");
-
- $('#ip-addresses').val(data.webserver.banlist?.join('\n') || "");
-
- $('#connection-type-toggle').prop("checked", data.xdrd.wirelessConnection || false);
-
- if($('#connection-type-toggle').is(":checked")) {
- $('#tuner-usb').hide();
- $('#tuner-wireless').show();
- } else {
- $('#tuner-wireless').hide();
- $('#tuner-usb').show();
- }
-
- $('#xdrd-ip').val(data.xdrd.xdrdIp);
- $('#xdrd-port').val(data.xdrd.xdrdPort);
- $('#xdrd-password').val(data.xdrd.xdrdPassword);
- $('#com-devices').val(data.xdrd.comPort);
- var selectedDevice = $(".option[data-value='" + data.xdrd.comPort + "']");
- if (selectedDevice.length > 0) {
- $("#com-devices").val(selectedDevice.text());
- }
-
- $('#device-type').val(data.device);
- var selectedDevice = $(".option[data-value='" + data.device + "']");
- if (selectedDevice.length > 0) {
- $("#device-type").val(selectedDevice.text());
- }
-
- $('#toggle-bw').prop("checked", data.bwSwitch ? data.bwSwitch : false);
-
- $('#audio-devices').val(data.audio.audioDevice);
- $('#audio-channels').val(data.audio.audioChannels);
- var selectedChannels = $(".option[data-value='" + data.audio.audioChannels + "']");
- if (selectedChannels.length > 0) {
- $("#audio-channels").val(selectedChannels.text());
- }
- $('#audio-quality').val(data.audio.audioBitrate);
- var selectedQuality = $(".option[data-value='" + data.audio.audioBitrate + "']");
- if (selectedQuality.length > 0) {
- $("#audio-quality").val(selectedQuality.text());
- }
-
- var antennaNames = ['ant1', 'ant2', 'ant3', 'ant4'];
-
- // Iterate over each antenna name
- antennaNames.forEach(function(antenna) {
- // Set values based on the antenna name
- $('#' + antenna + '-name').val(data.antennas?.[antenna]?.name || '');
- $('#' + antenna + '-enabled').prop("checked", data.antennas?.[antenna]?.enabled || false);
- });
-
- $('#audio-software-mode').prop("checked", data.audio.softwareMode || false);
- $('#startup-volume').val(data.audio.startupVolume || 1);
- $('#volume-percentage-value').text(data.audio.startupVolume !== undefined ? (data.audio.startupVolume * 100).toFixed(0) + '%' : '100%');
-
- $('#webserver-name').val(data.identification.tunerName);
- $('#webserver-desc').val(data.identification.tunerDesc);
- $("#broadcast-tuner").prop("checked", data.identification.broadcastTuner);
- $("#broadcast-address").val(data.identification.proxyIp);
- $("#owner-contact").val(data.identification.contact);
- $('#lat').val(data.identification.lat);
- $('#lng').val(data.identification.lon);
-
- $('#tune-pass').val(data.password.tunePass);
- $('#admin-pass').val(data.password.adminPass);
-
- $("#tuner-public").prop("checked", data.publicTuner);
- $("#tuner-lock").prop("checked", data.lockToAdmin);
- $("#shutdown-tuner").prop("checked", data.autoShutdown);
- $("#antenna-switch").prop("checked", data.antennas?.enabled);
-
- data.plugins.forEach(function(name) {
- // Find the option with the corresponding data-name attribute and mark it as selected
- $('#plugin-list option[data-name="' + name + '"]').prop('selected', true);
- });
-
- // Update the multi-select element to reflect the changes
- $('#plugin-list').trigger('change');
-
- // Check if latitude and longitude are present in the data
- if (data.identification.lat && data.identification.lon) {
- // Set the map's center to the received coordinates
- map.setView([data.identification.lat, data.identification.lon], 13);
-
- // Add a pin to the map
- if (typeof pin == "object") {
- pin.setLatLng([data.identification.lat, data.identification.lon]);
- } else {
- pin = L.marker([data.identification.lat, data.identification.lon], { riseOnHover:true, draggable:true });
- pin.addTo(map);
- pin.on('drag',function(ev) {
- $('#lat').val((ev.latlng.lat).toFixed(6));
- $('#lng').val((ev.latlng.lng).toFixed(6));
- });
- }
- }
-
- $("#fmlist-integration").prop("checked", !!(data.extras && data.extras?.fmlistIntegration));
- $('#fmlist-omid').val(data.extras?.fmlistOmid);
-
- $("#tunnel-enable").prop("checked", !!(data.tunnel && data.tunnel?.enabled));
- $("#tunnel-lowlatency").prop("checked", !!(data.tunnel && data.tunnel?.lowLatencyMode));
- $('#tunnel-token').val(data.tunnel?.token);
- $('#tunnel-subdomain').val(data.tunnel?.subdomain);
- $('#tunnel-username').val(data.tunnel?.username);
- })
- .catch(error => {
- console.error('Error fetching data:', error.message);
- });
+ $.ajax({
+ url: './saveData',
+ type: 'POST',
+ contentType: 'application/json',
+ data: JSON.stringify(configData),
+ success: function (message) {
+ sendToast('success', 'Data saved!', message, true, true);
+ },
+ error: function (error) {
+ console.error(error);
+ }
+ });
}
+function fetchConfig() {
+ $.getJSON("./getData")
+ .done(data => {
+ configData = data;
+ populateFields(configData);
+ initVolumeSlider();
+ initConnectionToggle();
+ })
+ .fail(error => console.error("Error fetching data:", error.message));
+}
-function validateAndAdd(banlist) {
- var textarea = $('#ip-addresses');
- var ipAddresses = textarea.val().split('\n');
+function populateFields(data, prefix = "") {
+ $.each(data, (key, value) => {
- // Regular expression to validate IP address
- var ipRegex = /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/;
+ if (key === "presets" && Array.isArray(value)) {
+ value.forEach((item, index) => {
+ const presetId = `${prefix}${prefix ? "-" : ""}${key}-${index + 1}`;
+ const $element = $(`#${presetId}`);
- ipAddresses.forEach(function(ip) {
- if (ipRegex.test(ip)) {
- banlist.push(ip);
+ if ($element.length) {
+ $element.val(item);
+ }
+ });
+ return;
+ }
+
+ if (key === "banlist" && Array.isArray(value)) {
+ const $textarea = $(`#${prefix}${prefix ? "-" : ""}${key}`);
+ if ($textarea.length && $textarea.is("textarea")) {
+ $textarea.val(value.join("\n"));
}
+ return;
+ }
+
+ const id = `${prefix}${prefix ? "-" : ""}${key}`;
+ const $element = $(`#${id}`);
+
+ if (typeof value === "object" && !Array.isArray(value)) {
+ populateFields(value, id);
+ return;
+ }
+
+ if (!$element.length) {
+ console.log(`Element with id ${id} not found`);
+ return;
+ }
+
+ if (typeof value === "boolean") {
+ $element.prop("checked", value);
+ } else if ($element.is('input[type="text"]') && $element.closest('.dropdown').length) {
+ const $dropdownOption = $element.siblings('ul.options').find(`li[data-value="${value}"]`);
+ $element.val($dropdownOption.length ? $dropdownOption.text() : value);
+ $element.attr('data-value', value);
+ } else {
+ $element.val(value);
+ }
+ });
+}
+
+function updateConfigData(data, prefix = "") {
+ $.each(data, (key, value) => {
+ const id = `${prefix}${prefix ? "-" : ""}${key}`;
+ const $element = $(`#${id}`);
+
+ if (key === "presets") {
+ data[key] = [];
+ let index = 1;
+ while (true) {
+ const $presetElement = $(`#${prefix}${prefix ? "-" : ""}${key}-${index}`);
+ if ($presetElement.length) {
+ data[key].push($presetElement.val());
+ index++;
+ } else {
+ break;
+ }
+ }
+ return;
+ }
+
+ if (key === "banlist") {
+ const $textarea = $(`#${prefix}${prefix ? "-" : ""}${key}`);
+ if ($textarea.length && $textarea.is("textarea")) {
+ data[key] = $textarea.val().split("\n").filter(line => line.trim() !== ""); // Split lines into an array and filter out empty lines
+ }
+ return;
+ }
+
+ if (typeof value === "object" && !Array.isArray(value)) {
+ return updateConfigData(value, id);
+ }
+
+ if ($element.length) {
+ data[key] = typeof value === "boolean" ? $element.is(":checked") : $element.attr("data-value") ?? $element.val();
+ }
});
}
\ No newline at end of file
diff --git a/web/js/dropdown.js b/web/js/dropdown.js
index a35e99d..77958d6 100644
--- a/web/js/dropdown.js
+++ b/web/js/dropdown.js
@@ -33,7 +33,9 @@ $(document).ready(function() {
$currentDropdown.find('input').val($(event.currentTarget).text());
break;
default:
- $currentDropdown.find('input').val($(event.currentTarget).text());
+ $currentDropdown.find('input')
+ .val($(event.currentTarget).text())
+ .attr('data-value', $(event.currentTarget).data('value'));
break;
}
diff --git a/web/js/init.js b/web/js/init.js
index e65497b..3c66a94 100644
--- a/web/js/init.js
+++ b/web/js/init.js
@@ -1,27 +1,28 @@
-var currentDate = new Date('Sep 26, 2024 12:00:00');
+var currentDate = new Date('Nov 5, 2024 21: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.1 [' + formattedDate + ']';
+var currentVersion = 'v1.3.2 [' + formattedDate + ']';
getInitialSettings();
-removeUrlParameters(); // Call this function to remove URL parameters
+removeUrlParameters();
function getInitialSettings() {
$.ajax({
url: './static_data',
dataType: 'json',
success: function (data) {
- localStorage.setItem('qthLatitude', data.qthLatitude);
- localStorage.setItem('qthLongitude', data.qthLongitude);
- localStorage.setItem('defaultTheme', data.defaultTheme);
- localStorage.setItem('preset1', data.presets[0]);
- localStorage.setItem('preset2', data.presets[1]);
- localStorage.setItem('preset3', data.presets[2]);
- localStorage.setItem('preset4', data.presets[3]);
- localStorage.setItem('bgImage', data.bgImage);
- localStorage.setItem('rdsMode', data.rdsMode);
+
+ ['qthLatitude', 'qthLongitude', 'defaultTheme', 'bgImage', 'rdsMode'].forEach(key => {
+ if (data[key] !== undefined) {
+ localStorage.setItem(key, data[key]);
+ }
+ });
+
+ data.presets.forEach((preset, index) => {
+ localStorage.setItem(`preset${index + 1}`, preset);
+ });
},
error: function (error) {
console.error('Error:', error);
diff --git a/web/js/main.js b/web/js/main.js
index 3cc0eeb..8c0cb08 100644
--- a/web/js/main.js
+++ b/web/js/main.js
@@ -124,9 +124,26 @@ $(document).ready(function () {
}
});
- document.onkeydown = checkKey;
+ document.onkeydown = function(event) {
+ if (!event.repeat) {
+ checkKey(event);
+ }
+ };
+
+ let lastExecutionTime = 0;
+ const throttleDelay = 100; // Time in ms
$('#freq-container').on('wheel keypress', function (e) {
+ e.preventDefault();
+ const now = Date.now();
+
+ if (now - lastExecutionTime < throttleDelay) {
+ // Ignore this event as it's within the throttle delay
+ return;
+ }
+
+ lastExecutionTime = now; // Update the last execution time
+
getCurrentFreq();
var delta = e.originalEvent.deltaY;
var adjustment = 0;
@@ -654,54 +671,6 @@ function checkKey(e) {
}
}
-function tuneUp() {
- if (socket.readyState === WebSocket.OPEN) {
- getCurrentFreq();
- let addVal = 0;
- if (currentFreq < 0.52) {
- addVal = 9 - (Math.round(currentFreq*1000) % 9);
- } else if (currentFreq < 1.71) {
- // TODO: Rework to replace 9 with 9 or 10 based on regionalisation setting
- addVal = 9 - (Math.round(currentFreq*1000) % 9);
- } else if (currentFreq < 29.6) {
- addVal = 5 - (Math.round(currentFreq*1000) % 5);
- } else if (currentFreq >= 65.9 && currentFreq < 74) {
- addVal = 30 - ((Math.round(currentFreq*1000) - 65900) % 30);
- } else {
- addVal = 100 - (Math.round(currentFreq*1000) % 100);
- }
- socket.send("T" + (Math.round(currentFreq*1000) + addVal));
- }
-}
-
-function tuneDown() {
- if (socket.readyState === WebSocket.OPEN) {
- getCurrentFreq();
- let subVal = 0;
- if (currentFreq < 0.52) {
- subVal = (Math.round(currentFreq*1000) % 9 == 0) ? 9 : (Math.round(currentFreq*1000) % 9);
- } else if (currentFreq < 1.71) {
- // TODO: Rework to replace 9 with 9 or 10 based on regionalisation setting
- subVal = (Math.round(currentFreq*1000) % 9 == 0) ? 9 : (Math.round(currentFreq*1000) % 9);
- } else if (currentFreq < 29.6) {
- subVal = (Math.round(currentFreq*1000) % 5 == 0) ? 5 : (Math.round(currentFreq*1000) % 5);
- } else if (currentFreq > 65.9 && currentFreq <= 74) {
- subVal = ((Math.round(currentFreq*1000) - 65900) % 30 == 0) ? 30 : ((Math.round(currentFreq*1000) - 65900) % 30);
- } else {
- subVal = (Math.round(currentFreq*1000) % 100 == 0) ? 100 : (Math.round(currentFreq*1000) % 100);
- }
- socket.send("T" + (Math.round(currentFreq*1000) - subVal));
- }
-}
-
-function tuneTo(freq) {
- socket.send("T" + ((freq).toFixed(1) * 1000));
-}
-
-function resetRDS(freq) {
- socket.send("T" + ((freq).toFixed(3) * 1000));
-}
-
async function copyPs() {
var frequency = $('#data-frequency').text();
var pi = $('#data-pi').text();
@@ -1055,33 +1024,20 @@ function toggleForcedStereo() {
socket.send(message);
}
-function toggleAdminLock() {
- let $adminLockButton = $('#dashboard-lock-admin');
+function toggleLock(buttonSelector, activeMessage, inactiveMessage, activeLabel, inactiveLabel) {
+ let $lockButton = $(buttonSelector);
- if($adminLockButton.hasClass('active')) {
- socket.send('wL0');
- $adminLockButton.attr('aria-label', 'Lock Tuner (Admin)')
- $adminLockButton.removeClass('active');
+ if ($lockButton.hasClass('active')) {
+ socket.send(inactiveMessage);
+ $lockButton.attr('aria-label', inactiveLabel);
+ $lockButton.removeClass('active');
} else {
- socket.send('wL1');
- $adminLockButton.attr('aria-label', 'Unlock Tuner (Admin)')
- $adminLockButton.addClass('active');
+ socket.send(activeMessage);
+ $lockButton.attr('aria-label', activeLabel);
+ $lockButton.addClass('active');
}
}
-function togglePasswordLock() {
- let $passwordLockButton = $('#dashboard-lock-tune');
-
- if($passwordLockButton.hasClass('active')) {
- socket.send('wT0');
- $passwordLockButton.removeClass('active');
- $passwordLockButton.attr('aria-label', 'Lock Tuner (Password tune)')
- } else {
- socket.send('wT1');
- $passwordLockButton.addClass('active');
- $passwordLockButton.attr('aria-label', 'Unlock Tuner (Password tune)')
- }
-}
function initTooltips() {
$('.tooltip').hover(function(e){
diff --git a/web/js/setup.js b/web/js/setup.js
index 2621da9..892a899 100644
--- a/web/js/setup.js
+++ b/web/js/setup.js
@@ -3,161 +3,20 @@ var pin;
var tilesURL=' https://tile.openstreetmap.org/{z}/{x}/{y}.png';
var mapAttrib='©
OpenStreetMap ';
-// add map container
-
$(document).ready(function() {
- MapCreate();
- fetchData();
-
- $('#startup-volume').on('change', function() {
- var value = $(this).val(); // Get the value of the range input
- var percentage = value * 100; // Convert to percentage
- $('#volume-percentage-value').text(percentage.toFixed(0) + '%'); // Display the percentage value
-});
-
- 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 {
- pin = L.marker(ev.latlng,{ riseOnHover:true,draggable:true });
- pin.addTo(map);
- pin.on('drag',function(ev) {
- $('#lat').val((ev.latlng.lat).toFixed(6));
- $('#lng').val((ev.latlng.lng).toFixed(6));
- });
- }
- });
-
- $('#dashboard').show();
+ mapCreate();
+ loadConsoleLogs();
+
showPanelFromHash();
- $('.nav li').click(function() {
- // Remove background color from all li elements
- $('.nav li').removeClass('active');
-
- // Add background color to the clicked li element
- $(this).addClass('active');
-
- // Get the data-panel attribute value
- var panelId = $(this).data('panel');
- window.location.hash = panelId;
- // Hide all panels
- $('.tab-content').hide();
-
- // Show the corresponding panel
- $('#' + panelId).show();
-
- if(panelId == 'identification') {
- setTimeout(function () {
- map.invalidateSize();
- }, 200);
- }
- });
-
- $('#connection-type-toggle').change(function(){
- if($(this).is(":checked")) {
- $('#tuner-usb').hide();
- $('#tuner-wireless').show();
- } else {
- $('#tuner-wireless').hide();
- $('#tuner-usb').show();
- }
-});
-
- $('.logout-link').click(function (event) {
- event.preventDefault();
-
- // Perform an AJAX request to the /logout endpoint
- $.ajax({
- type: 'GET', // Assuming the logout is a GET request, adjust accordingly
- url: './logout',
- success: function (data) {
- // Update the content on the page with the message from the response
- $('#login-message').text(data.message);
- setTimeout(function () {
- location.reload(true);
- }, 1750);
- },
- error: function (xhr, status, error) {
- // Handle error response
- if (xhr.status === 403) {
- // Update the content on the page with the message from the error response
- $('#login-message').text(xhr.responseJSON.message);
- } else {
- // Handle other types of errors if needed
- console.error('Error:', status, error);
- }
- }
- });
- });
-
- 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 "
" + match + " ";
- } else if (level === "INFO") {
- return "
" + match + " ";
- } else if (level === "DEBUG") {
- return "
" + match + " ";
- } else if (level === "WARN") {
- return "
" + match + " ";
- } else if (level === "ERROR") {
- return "
" + match + " ";
- } else {
- return "
" + match + " ";
- }
- });
- });
-
- if($("#console-output").length > 0) {
- $("#console-output").scrollTop($("#console-output")[0].scrollHeight);
- }
-
- const $tabs = $('.nav li[role="presentation"]');
- let currentTabIndex = 0;
-
- function updateTabFocus(index) {
- $tabs.each(function(i) {
- const $link = $(this).find('a');
- if (i === index) {
- $(this).attr('aria-selected', 'true');
- $link.attr('tabindex', '0').focus();
- } else {
- $(this).attr('aria-selected', 'false');
- $link.attr('tabindex', '-1');
- }
- });
- }
-
- function handleKeyDown(event) {
- if (event.key === 'ArrowRight') {
- event.preventDefault();
- currentTabIndex = (currentTabIndex + 1) % $tabs.length;
- updateTabFocus(currentTabIndex);
- } else if (event.key === 'ArrowLeft') {
- event.preventDefault();
- currentTabIndex = (currentTabIndex - 1 + $tabs.length) % $tabs.length;
- updateTabFocus(currentTabIndex);
- } else if (event.key === 'Enter') {
- event.preventDefault();
- $tabs.eq(currentTabIndex).find('a')[0].click();
- }
- }
-
- updateTabFocus(currentTabIndex);
- $tabs.on('keydown', handleKeyDown);
- //toggleNav();
+ initNav();
});
-function MapCreate() {
- // create map instance
+/**
+ * Function to create & handle maps.
+ * Also contains map handling such as reloading / pin click registering.
+ */
+
+function mapCreate() {
if (!(typeof map == "object")) {
map = L.map('map', {
center: [40,0],
@@ -167,39 +26,70 @@ function MapCreate() {
else {
map.setZoom(3).panTo([40,0]);
}
- // create the tile layer with correct attribution
+
L.tileLayer(tilesURL, {
attribution: mapAttrib,
maxZoom: 19
}).addTo(map);
+
+ map.on('click', function(ev) {
+ $('#identification-lat').val((ev.latlng.lat).toFixed(6));
+ $('#identification-lon').val((ev.latlng.lng).toFixed(6));
+
+ if (typeof pin == "object") {
+ pin.setLatLng(ev.latlng);
+ } else {
+ pin = L.marker(ev.latlng,{ riseOnHover:true,draggable:true });
+ pin.addTo(map);
+ pin.on('dragend',function(ev) {
+ $('#identification-lat').val((ev.latlng.lat).toFixed(6));
+ $('#identification.lon').val((ev.latlng.lng).toFixed(6));
+ });
+ }
+ });
+ mapReload();
}
- function showPanelFromHash() {
- var panelId = window.location.hash.substring(1);
- if (panelId) {
- // Hide all panels
- $('.tab-content').hide();
-
- // Show the panel corresponding to the hash fragment
- $('#' + panelId).show();
-
- // Remove active class from all li elements
- $('.nav li').removeClass('active');
-
- // Add active class to the corresponding li element
- $('.nav li[data-panel="' + panelId + '"]').addClass('active');
- }
- if(window.location.hash.length == 0) {
- $('.nav li[data-panel="dashboard"]').addClass('active');
+function mapReload() {
+ setTimeout(function () {
+ map.invalidateSize();
+ }, 200);
+}
+
+function showPanelFromHash() {
+ var panelId = window.location.hash.substring(1) || 'dashboard';
+
+ $('.tab-content').hide();
+ $('#' + panelId).show();
+
+ $('.nav li').removeClass('active');
+ $('.nav li[data-panel="' + panelId + '"]').addClass('active');
+}
+
+function initNav() {
+ $('.nav li').click(function() {
+ $('.nav li').removeClass('active');
+ $(this).addClass('active');
+ var panelId = $(this).data('panel');
+ window.location.hash = panelId;
+ $('.tab-content').hide();
+ $('#' + panelId).show();
+
+ panelId == 'identification' ? mapReload() : null;
+ });
+
+ $('[role="tab"]').on('keydown', function(event) {
+ if (event.key === 'Enter') {
+ $(this).find('a').click();
}
+ });
}
function toggleNav() {
const navOpen = $("#navigation").css('margin-left') === '0px';
- const isMobile = window.innerWidth <= 768; // Define mobile screen width threshold (you can adjust this as needed)
-
+ const isMobile = window.innerWidth <= 768;
+
if (navOpen) {
- // Close the navigation
if (isMobile) {
// Do nothing to .admin-wrapper on mobile (since we're overlaying)
$(".admin-wrapper").css({
@@ -217,10 +107,8 @@ function toggleNav() {
}
$(".sidenav-close").html('
');
} else {
- // Open the navigation
$("#navigation").css('margin-left', '0');
if (isMobile) {
- // On mobile, overlay the navigation
$(".admin-wrapper").css({
'margin-left': '0', // Keep content in place when sidenav is open
'width': '100%' // Keep content at full width
@@ -235,3 +123,69 @@ function toggleNav() {
$(".sidenav-close").html('
');
}
}
+
+function initVolumeSlider() {
+ const $volumeInput = $('#audio-startupVolume');
+ const $percentageValue = $('#volume-percentage-value');
+
+ const updateDisplay = () => {
+ $percentageValue.text(($volumeInput.val() * 100).toFixed(0) + '%');
+ };
+
+ updateDisplay();
+ $volumeInput.on('change', updateDisplay);
+}
+
+function initConnectionToggle() {
+ const connectionToggle = $('#xdrd-wirelessConnection');
+ const tunerUSB = $('#tuner-usb');
+ const tunerWireless = $('#tuner-wireless');
+
+ function toggleType() {
+ if (connectionToggle.is(":checked")) {
+ tunerUSB.hide();
+ tunerWireless.show();
+ } else {
+ tunerWireless.hide();
+ tunerUSB.show();
+ }
+ }
+
+ toggleType();
+ connectionToggle.change(toggleType);
+}
+
+function stripAnsi(str) {
+ return str.replace(/\u001b\[\d+m/g, '');
+}
+
+async function loadConsoleLogs() {
+ await new Promise((resolve) => {
+ $("pre").html(function (_, html) {
+ html = stripAnsi(html);
+
+ const logColors = {
+ INFO: "lime",
+ DEBUG: "cyan",
+ WARN: "yellow",
+ ERROR: "red"
+ };
+
+ let firstBracketProcessed = false;
+
+ const processedHtml = html.replace(/\[([^\]]+)\]/g, function (match, content) {
+ if (!firstBracketProcessed) {
+ firstBracketProcessed = true;
+ return `
${match} `;
+ }
+
+ const color = logColors[content] || "white";
+ return `
${match} `;
+ });
+
+ return processedHtml;
+ });
+ resolve();
+ });
+ $("#console-output").scrollTop($("#console-output")[0].scrollHeight);
+}
diff --git a/web/js/toast.js b/web/js/toast.js
index e0d4613..9179727 100644
--- a/web/js/toast.js
+++ b/web/js/toast.js
@@ -1,22 +1,18 @@
function sendToast(type, title, message, persistent, important) {
- var toastId = 'toast-' + new Date().getTime(); // Unique ID for each toast
+ var toastId = 'toast-' + new Date().getTime();
- // If title isn't provided, use the type as the title
var toastTitle = title ? title : capitalizeFirstLetter(type);
- // Icon mapping based on the toast type
var toastIcons = {
success: 'fa-check-circle',
error: 'fa-times-circle',
warning: 'fa-exclamation-triangle',
info: 'fa-info-circle',
- default: 'fa-bell' // Default icon if the type is not found
+ default: 'fa-bell'
};
- // Get the icon class based on the toast type, fallback to 'default' if type doesn't exist
- var iconClass = toastIcons[type] || toastIcons['default'];
+ var iconClass = toastIcons[type] || toastIcons['default']; // Get the icon class based on the toast type, fallback to 'default' if type doesn't exist
- // Create the toast element
var $toast = $(`
@@ -28,33 +24,28 @@ function sendToast(type, title, message, persistent, important) {
`);
- // Append the toast to the container
$('#toast-container').append($toast);
- // Add the 'show' class after appending for fade-in effect
setTimeout(function () {
$toast.addClass('show');
- }, 10); // Timeout to ensure the element is appended before the animation triggers
+ }, 10);
- // Close button functionality
$toast.find('.close-btn').click(function () {
closeToast($toast);
});
- // If not persistent, remove it after 5 seconds
if (!persistent) {
setTimeout(function () {
closeToast($toast);
- }, 5000); // 5000 ms = 5 seconds
+ }, 5000);
}
}
-// Function to close and remove the toast
function closeToast($toast) {
- $toast.removeClass('show'); // Trigger fade-out
+ $toast.removeClass('show');
setTimeout(function () {
- $toast.remove(); // Remove the element from DOM after the animation
- }, 300); // Timeout matches the CSS transition duration
+ $toast.remove();
+ }, 300);
}
function capitalizeFirstLetter(string) {
diff --git a/web/js/webserver.js b/web/js/webserver.js
index c2ac43e..5170e21 100644
--- a/web/js/webserver.js
+++ b/web/js/webserver.js
@@ -1,3 +1,4 @@
+$.getScript('./js/api.js');
$.getScript('./js/main.js');
$.getScript('./js/dropdown.js');
$.getScript('./js/modal.js');
diff --git a/web/js/wizard.js b/web/js/wizard.js
index 1b7c986..b9e0f48 100644
--- a/web/js/wizard.js
+++ b/web/js/wizard.js
@@ -1,40 +1,9 @@
$(document).ready(function() {
- if($('.step:visible').index() == 0) {
- $('.btn-prev').hide();
- }
-
- $('.btn-next').click(function() {
- var currentStep = $('.step:visible');
- var nextStep = currentStep.next('.step');
-
- if (nextStep.length !== 0) {
- currentStep.hide();
- nextStep.show();
- updateProgressBar(nextStep);
- } else {
- submitData();
- }
-
- updateWizardContent();
- });
-
- $('.btn-prev').click(function() {
- var currentStep = $('.step:visible');
- var nextStep = currentStep.prev('.step');
-
- if (nextStep.length !== 0) {
- currentStep.hide();
- nextStep.show();
- updateProgressBar(nextStep);
- } else {
- alert('You have reached the beginning of the wizard.');
- }
-
- updateWizardContent();
- });
+ $('.btn-prev').toggle($('.step:visible').index() !== 0);
+ $('.btn-next').click(() => navigateStep(true));
+ $('.btn-prev').click(() => navigateStep(false));
});
-// Function to update the progress bar buttons
function updateProgressBar(currentStep) {
var stepIndex = $('.step').index(currentStep) + 1;
$('.btn-rounded-cube').removeClass('activated');
@@ -42,21 +11,24 @@ function updateProgressBar(currentStep) {
}
function updateWizardContent() {
- if($('.step:visible').index() == 0) {
- $('.btn-prev').hide();
- } else {
- $('.btn-prev').show();
- }
+ var visibleStepIndex = $('.step:visible').index();
- if($('.step:visible').index() == 3) {
- setTimeout(function () {
- map.invalidateSize();
- }, 200);
- }
+ $('.btn-prev').toggle(visibleStepIndex !== 0);
+ $('.btn-next').text(visibleStepIndex === 4 ? 'Save' : 'Next');
+
+ visibleStepIndex === 3 && mapReload();
+}
+
+function navigateStep(isNext) {
+ var currentStep = $('.step:visible');
+ var targetStep = isNext ? currentStep.next('.step') : currentStep.prev('.step');
- if($('.step:visible').index() == 4) {
- $('.btn-next').text('Save');
- } else {
- $('.btn-next').text('Next')
+ if (targetStep.length !== 0) {
+ currentStep.hide();
+ targetStep.show();
+ updateProgressBar(targetStep);
+ } else if (isNext) {
+ submitData();
}
+ updateWizardContent();
}
\ No newline at end of file
diff --git a/web/setup.ejs b/web/setup.ejs
index 1391a9a..0be1e29 100644
--- a/web/setup.ejs
+++ b/web/setup.ejs
@@ -18,25 +18,25 @@
Settings
-
- Dashboard
+
+ Dashboard
-
+
Tuner
-
+
Audio
-
+
Webserver
-
+
Plugins
-
+
Identification & Map
-
+
Extras
@@ -44,7 +44,7 @@
-
@@ -232,88 +215,44 @@
Connection
Leave the IP at 0.0.0.0 unless you explicitly know you have to change it. Don't enter your public IP here.
-
- Webserver IP:
-
-
-
- Webserver port:
-
-
-
+ <%- include('_components', {component: 'text', cssClass: 'w-150', placeholder: '0.0.0.0', label: 'Webserver IP', id: 'webserver-webserverIp'}) %>
+ <%- include('_components', {component: 'text', cssClass: 'w-100', placeholder: '8080', label: 'Webserver port', id: 'webserver-webserverPort'}) %>
Design
-
Background image
-
- Image link:
-
-
+
Background image
+ <%- include('_components', {component: 'text', cssClass: '', placeholder: 'Direct image link', label: 'Image link', id: 'webserver-bgImage'}) %>
+
Themes
-
+ <%- include('_components', { component: 'dropdown', id: 'server-theme-selector', inputId: 'webserver-defaultTheme', label: 'Default server theme', cssClass: '', placeholder: 'Default',
+ options: [
+ { value: 'theme1', label: 'Default' },
+ { value: 'theme2', label: 'Cappuccino' },
+ { value: 'theme3', label: 'Nature' },
+ { value: 'theme4', label: 'Ocean' },
+ { value: 'theme5', label: 'Terminal' },
+ { value: 'theme6', label: 'Nightlife' },
+ { value: 'theme7', label: 'Blurple' },
+ { value: 'theme8', label: 'Construction' },
+ { value: 'theme9', label: 'Amoled' },
+ ]
+ }) %>
Antennas
-
-
-
-
-
- Antenna switch
-
-
-
-
-
-
-
- Antenna 1 name:
-
-
+ <%- include('_components', {component: 'checkbox', cssClass: 'bottom-20', label: 'Antenna switch', id: 'antennas-enabled'}) %>
-
-
-
-
-
- Antenna 2 name:
-
-
+ <%- include('_components', {component: 'checkbox', cssClass: '', label: 'Antenna 1', id: 'antennas-ant1-enabled'}) %>
+ <%- include('_components', {component: 'text', cssClass: 'w-100', placeholder: 'Ant A', label: 'Antenna 1 name', id: 'antennas-ant1-name'}) %>
-
-
-
-
-
- Antenna 3 name:
-
-
+ <%- include('_components', {component: 'checkbox', cssClass: '', label: 'Antenna 2', id: 'antennas-ant2-enabled'}) %>
+ <%- include('_components', {component: 'text', cssClass: 'w-100', placeholder: 'Ant B', label: 'Antenna 2 name', id: 'antennas-ant2-name'}) %>
-
-
-
-
-
- Antenna 4 name:
-
-
+ <%- include('_components', {component: 'checkbox', cssClass: '', label: 'Antenna 3', id: 'antennas-ant3-enabled'}) %>
+ <%- include('_components', {component: 'text', cssClass: 'w-100', placeholder: 'Ant C', label: 'Antenna 3 name', id: 'antennas-ant3-name'}) %>
+ <%- include('_components', {component: 'checkbox', cssClass: '', label: 'Antenna 4', id: 'antennas-ant4-enabled'}) %>
+ <%- include('_components', {component: 'text', cssClass: 'w-100', placeholder: 'Ant D', label: 'Antenna 4 name', id: 'antennas-ant4-name'}) %>
@@ -323,40 +262,21 @@
If you want to limit which frequencies the users can tune to, you can set the lower and upper limit here.
Enter frequencies in MHz.
-
-
- Limit tuning
-
-
- Lower limit:
-
-
-
- Upper Limit:
-
-
+
+ <%- include('_components', {component: 'checkbox', cssClass: '', label: 'Limit tuning', id: 'webserver-tuningLimit'}) %>
+ <%- include('_components', {component: 'text', cssClass: 'w-100', placeholder: '0', label: 'Lower limit', id: 'webserver-tuningLowerLimit'}) %>
+ <%- include('_components', {component: 'text', cssClass: 'w-100', placeholder: '108', label: 'Upper limit', id: 'webserver-tuningUpperLimit'}) %>