diff --git a/server/datahandler.js b/server/datahandler.js index 05d332b..c1b55bc 100644 --- a/server/datahandler.js +++ b/server/datahandler.js @@ -193,8 +193,7 @@ function handleData(wss, receivedData, rdsWss) { data += (((errors & 0x03) == 0) ? modifiedData.slice(12, 16) : '----'); const newDataString = "G:\r\n" + data + "\r\n\r\n"; - const finalBuffer = Buffer.from(newDataString, 'utf-8'); - client.send(finalBuffer); + client.send(newDataString); }); rdsdec.decodeGroup(parseInt(modifiedData.slice(0, 4), 16), parseInt(modifiedData.slice(4, 8), 16), parseInt(modifiedData.slice(8, 12), 16), parseInt(modifiedData.slice(12, 16), 16)); diff --git a/web/403.ejs b/web/403.ejs index c965475..e07acad 100644 --- a/web/403.ejs +++ b/web/403.ejs @@ -13,14 +13,14 @@

[403]

- +

There's a possibility you were kicked by the system.
Please try again later.

-
+
diff --git a/web/index.ejs b/web/index.ejs index 4a00c2e..00b45f4 100644 --- a/web/index.ejs +++ b/web/index.ejs @@ -261,7 +261,7 @@ <% if (bwSwitch) { %> <%- include('_bwSwitch', { device: device, tunerProfiles: tunerProfiles, 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)) { %> <% } %> @@ -445,14 +445,14 @@
- + <% if (chatEnabled) { %> <% } %>
- +
- +
@@ -469,7 +469,7 @@

Settings

- <%- include('_components', { component: 'dropdown', id: 'theme-selector', inputId: 'theme-selector-input', label: 'Theme', cssClass: '', placeholder: 'Default', + <%- include('_components', { component: 'dropdown', id: 'theme-selector', inputId: 'theme-selector-input', label: 'Theme', cssClass: '', placeholder: 'Default', options: [ { value: 'theme1', label: 'Mint' }, { value: 'theme2', label: 'Cappuccino' }, @@ -486,7 +486,7 @@ <% if (device !== 'sdr') { %>
- <%- include('_components', { component: 'dropdown', id: 'signal-selector', inputId: 'signal-selector-input', label: 'Signal units', cssClass: '', placeholder: 'dBf', + <%- include('_components', { component: 'dropdown', id: 'signal-selector', inputId: 'signal-selector-input', label: 'Signal units', cssClass: '', placeholder: 'dBf', options: [ { value: 'dbf', label: 'dBf' }, { value: 'dbuv', label: 'dBuV' }, diff --git a/web/js/3las/main.js b/web/js/3las/main.js index 41f56d6..a1d1ce4 100644 --- a/web/js/3las/main.js +++ b/web/js/3las/main.js @@ -27,11 +27,8 @@ function destroyStream() { function OnConnectivityCallback(isConnected) { console.log("Connectivity changed:", isConnected); - if (Stream) { - Stream.Volume = $('#volumeSlider').val(); - } else { - console.warn("Stream is not initialized."); - } + if (Stream) Stream.Volume = $('#volumeSlider').val(); + else console.warn("Stream is not initialized."); } @@ -44,18 +41,14 @@ function OnPlayButtonClick(_ev) { shouldReconnect = false; destroyStream(); $playbutton.find('.fa-solid').toggleClass('fa-stop fa-play'); - if (isAppleiOS && 'audioSession' in navigator) { - navigator.audioSession.type = "none"; - } + if (isAppleiOS && 'audioSession' in navigator) navigator.audioSession.type = "none"; } else { console.log("Starting stream..."); shouldReconnect = true; createStream(); Stream.Start(); $playbutton.find('.fa-solid').toggleClass('fa-play fa-stop'); - if (isAppleiOS && 'audioSession' in navigator) { - navigator.audioSession.type = "playback"; - } + if (isAppleiOS && 'audioSession' in navigator) navigator.audioSession.type = "playback"; } $playbutton.addClass('bg-gray').prop('disabled', true); @@ -70,9 +63,7 @@ function updateVolume() { newVolumeGlobal = newVolume; console.log("Volume updated to:", newVolume); Stream.Volume = newVolume; - } else { - console.warn("Stream is not initialized."); - } + } else console.warn("Stream is not initialized."); } $(document).ready(Init); diff --git a/web/js/api.js b/web/js/api.js index 5e64c1a..35a1b27 100644 --- a/web/js/api.js +++ b/web/js/api.js @@ -3,18 +3,13 @@ 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) { + 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); - } + } 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)); } } @@ -23,18 +18,13 @@ function tuneDown() { if (socket.readyState === WebSocket.OPEN) { getCurrentFreq(); let subVal = 0; - if (currentFreq < 0.52) { + 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 (Americans use 10, because of dumbfuckinstan) 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); - } + } 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)); } } @@ -52,6 +42,6 @@ function getCurrentFreq() { currentFreq = $('#data-frequency').text(); currentFreq = parseFloat(currentFreq).toFixed(3); currentFreq = parseFloat(currentFreq); - + return currentFreq; } diff --git a/web/js/chat.js b/web/js/chat.js index 4bd34e0..2d32123 100644 --- a/web/js/chat.js +++ b/web/js/chat.js @@ -9,42 +9,40 @@ $(document).ready(function() { const chatIdentityNickname = $('#chat-identity-nickname'); const chatNicknameInput = $('#chat-nickname'); const chatNicknameSave = $('#chat-nickname-save'); - + $(".chatbutton").on("click", function () { togglePopup("#popup-panel-chat"); chatMessages.scrollTop(chatMessages[0].scrollHeight); }); - + // Function to generate a random string function generateRandomString(length) { const characters = 'ABCDEFGHJKMNOPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz0123456789'; let result = ''; - for (let i = 0; i < length; i++) { - result += characters.charAt(Math.floor(Math.random() * characters.length)); - } + for (let i = 0; i < length; i++) result += characters.charAt(Math.floor(Math.random() * characters.length)); 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 ? '[ADMIN]' : ''; - + if (messageData.type === 'clientIp') { chatIdentityNickname.html(isAdmin).append(document.createTextNode(" " + savedNickname)); chatIdentityNickname.attr('title', messageData.ip); } else { const chatMessage = ` [${messageData.time}] - ${isAdmin} ${messageData.nickname}: + ${isAdmin} ${messageData.nickname}: ${$('
').text(messageData.message).html()}
`; chatMessages.append(chatMessage); - + if (chatMessages.is(':visible')) { setTimeout(function() { chatMessages.scrollTop(chatMessages[0].scrollHeight); @@ -59,7 +57,7 @@ $(document).ready(function() { } } }; - + $('.chat-send-message-btn').click(sendMessage); chatNicknameSave.click(function() { const currentNickname = chatNicknameInput.val().trim() || `Anonymous User ${generateRandomString(5)}`; @@ -68,34 +66,32 @@ $(document).ready(function() { chatIdentityNickname.text(savedNickname); chatNicknameInput.blur(); }); - + chatButton.click(function() { chatMessageCount = 0; 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(); - } + 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)); diff --git a/web/js/confighandler.js b/web/js/confighandler.js index 533b524..1622abc 100644 --- a/web/js/confighandler.js +++ b/web/js/confighandler.js @@ -28,7 +28,7 @@ function submitConfig() { function fetchConfig() { $.getJSON("./getData") .done(data => { - configData = data; + configData = data; populateFields(configData); initVolumeSlider(); initConnectionToggle(); @@ -38,9 +38,7 @@ function fetchConfig() { function populateFields(data, prefix = "") { $.each(data, (key, value) => { - if (value === null) { - value = ""; // Convert null to an empty string - } + if (value === null) value = ""; // Convert null to an empty string let id = `${prefix}${prefix ? "-" : ""}${key}`; const $element = $(`#${id}`); @@ -50,16 +48,13 @@ function populateFields(data, prefix = "") { $element.find('option').each(function() { const $option = $(this); const dataName = $option.data('name'); - if (value.includes(dataName)) { - $option.prop('selected', true); - } else { - $option.prop('selected', false); - } + if (value.includes(dataName)) $option.prop('selected', true); + else $option.prop('selected', false); }); $element.trigger('change'); } - return; + return; } if (typeof value === "object" && value !== null) { @@ -68,11 +63,8 @@ function populateFields(data, prefix = "") { const arrayId = `${id}-${index + 1}`; const $arrayElement = $(`#${arrayId}`); - if ($arrayElement.length) { - $arrayElement.val(item); - } else { - console.log(`Element with id ${arrayId} not found`); - } + if ($arrayElement.length) $arrayElement.val(item); + else console.log(`Element with id ${arrayId} not found`); }); return; } else { @@ -92,9 +84,7 @@ function populateFields(data, prefix = "") { 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); - } + } else $element.val(value); }); } @@ -111,9 +101,7 @@ function updateConfigData(data, prefix = "") { if ($presetElement.length) { data[key].push($presetElement.val() || null); // Allow null if necessary index++; - } else { - break; - } + } else break; } return; } @@ -123,16 +111,12 @@ function updateConfigData(data, prefix = "") { const $selectedOptions = $element.find('option:selected'); $selectedOptions.each(function() { const dataName = $(this).attr('data-name'); - if (dataName) { - data[key].push(dataName); - } + if (dataName) data[key].push(dataName); }); return; } - if (typeof value === "object" && value !== null && !Array.isArray(value)) { - return updateConfigData(value, id); - } + if (typeof value === "object" && value !== null && !Array.isArray(value)) return updateConfigData(value, id); if ($element.length) { const newValue = $element.attr("data-value") ?? $element.val() ?? null; diff --git a/web/js/dropdown.js b/web/js/dropdown.js index 7841ffc..36cbe7f 100644 --- a/web/js/dropdown.js +++ b/web/js/dropdown.js @@ -23,38 +23,38 @@ $(document).ready(function() { switch($currentDropdown.attr('id')) { case 'data-ant': - socket.send("Z" + $(event.currentTarget).attr('data-value')); - resetRDS(getCurrentFreq()); // Reset RDS when change antenna input - break; + 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; + 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; + 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; + 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-agc': - socket.send("A" + $(event.currentTarget).attr('data-value')); - $currentDropdown.find('input').val($(event.currentTarget).text()); - break; + socket.send("A" + $(event.currentTarget).attr('data-value')); + $currentDropdown.find('input').val($(event.currentTarget).text()); + break; case 'data-agc-phone': - socket.send("A" + $(event.currentTarget).attr('data-value')); - $currentDropdown.find('input').val($(event.currentTarget).text()); - break; + socket.send("A" + $(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; + $currentDropdown.find('input') + .val($(event.currentTarget).text()) + .attr('data-value', $(event.currentTarget).data('value')); + break; } // Use setTimeout to delay class removal @@ -80,24 +80,24 @@ $(document).ready(function() { const $options = currentDropdown.find('.option'); switch (event.key) { case 'ArrowDown': - event.preventDefault(); - currentIndex = (currentIndex + 1) % $options.length; - $options.eq(currentIndex).focus(); - break; + 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; + event.preventDefault(); + currentIndex = (currentIndex - 1 + $options.length) % $options.length; + $options.eq(currentIndex).focus(); + break; case 'Enter': - event.preventDefault(); - $options.eq(currentIndex).click(); - break; + event.preventDefault(); + $options.eq(currentIndex).click(); + break; case 'Escape': - currentDropdown.removeClass('opened'); - currentDropdown = null; - currentIndex = -1; - break; + currentDropdown.removeClass('opened'); + currentDropdown = null; + currentIndex = -1; + break; } }; @@ -106,9 +106,7 @@ $(document).ready(function() { $listOfOptions.on('click', selectOption); $dropdowns.on('click', 'input', toggleDropdown); $dropdowns.on('keydown', 'input', function(event) { - if (event.key === 'Enter') { - toggleDropdown(event); - } + if (event.key === 'Enter') toggleDropdown(event); }); $dropdowns.on('keydown', '.option', navigateOptions); diff --git a/web/js/main.js b/web/js/main.js index 0789cba..ac654b1 100644 --- a/web/js/main.js +++ b/web/js/main.js @@ -25,23 +25,23 @@ const europe_programmes = [ const usa_programmes = [ "No PTY", "News", "Information", "Sports", "Talk", "Rock", "Classic Rock", "Adults Hits", "Soft Rock", "Top 40", "Country", "Oldies", "Soft Music", - "Nostalgia", "Jazz", "Classical", "Rhythm and Blues", "Soft Rhythm and Blues", + "Nostalgia", "Jazz", "Classical", "Rhythm and Blues", "Soft Rhythm and Blues", "Language", "Religious Music", "Religious Talk", "Personality", "Public", "College", - "Spanish Talk", "Spanish Music", "Hip Hop", "", "", "Weather", "Emergency Test", "Emergency" + "Spanish Talk", "Spanish Music", "Hip Hop", "", "", "Weather", "Emergency Test", "Emergency" ]; const rdsMode = localStorage.getItem('rdsMode'); $(document).ready(function () { const signalToggle = $("#signal-units-toggle"); - + var $panel = $('.admin-quick-dashboard'); var panelWidth = $panel.outerWidth(); - + $(document).mousemove(function(e) { var mouseX = e.pageX; var panelLeft = parseInt($panel.css('left')); - + if (mouseX <= 10 || (panelLeft === 4 && mouseX <= 100)) { $panel.css('left', '4px'); } else { @@ -50,10 +50,10 @@ $(document).ready(function () { }); fillPresets(); - + signalToggle.on("change", function () { const signalText = localStorage.getItem('signalUnit'); - + if (signalText == 'dbuv') { signalText.text('dBµV'); } else if (signalText == 'dbf') { @@ -62,7 +62,7 @@ $(document).ready(function () { signalText.text('dBm'); } }); - + // Check if device is an iPhone to prevent zoom on button press if (/iPhone|iPod|iPad/.test(navigator.userAgent) && !window.MSStream) { // Handle touchstart for buttons to prevent zoom @@ -89,9 +89,9 @@ $(document).ready(function () { $viewportMeta.attr('content', content); } } - + const textInput = $('#commandinput'); - + textInput.on('change blur', function (event) { const inputValue = Number(textInput.val()); // Check if the user agent contains 'iPhone' @@ -101,18 +101,18 @@ $(document).ready(function () { textInput.val(''); } }); - + textInput.on('keyup', function (event) { - + if (event.key !== 'Backspace' && localStorage.getItem('extendedFreqRange') != "true") { let inputValue = textInput.val(); inputValue = inputValue.replace(/[^0-9.]/g, ''); - + if (inputValue.includes("..")) { inputValue = inputValue.slice(0, inputValue.lastIndexOf('.')) + inputValue.slice(inputValue.lastIndexOf('.') + 1); textInput.val(inputValue); } - + if (!inputValue.includes(".")) { if (inputValue.startsWith('10') && inputValue.length > 2) { inputValue = inputValue.slice(0, 3) + '.' + inputValue.slice(3); @@ -130,31 +130,31 @@ $(document).ready(function () { textInput.val(''); } }); - + 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; - + if (e.shiftKey) { adjustment = e.altKey ? 1 : 0.01; } else if (e.ctrlKey) { @@ -167,21 +167,21 @@ $(document).ready(function () { } return false; } - + var newFreq = currentFreq + (delta > 0 ? -adjustment : adjustment); socket.send("T" + (Math.round(newFreq * 1000))); return false; }); - + setInterval(getServerTime, 10000); getServerTime(); setInterval(sendPingRequest, 5000); sendPingRequest(); - + $("#tuner-name").click(function() { showTunerDescription(); }); - + var freqUpButton = $('#freq-up')[0]; var freqDownButton = $('#freq-down')[0]; var psContainer = $('#ps-container')[0]; @@ -189,19 +189,19 @@ $(document).ready(function () { var piCodeContainer = $('#pi-code-container')[0]; var freqContainer = $('#freq-container')[0]; var txContainer = $('#data-station-container')[0]; - + $(".data-eq").click(function () { toggleButtonState("eq"); }); - + $(".data-ims").click(function () { toggleButtonState("ims"); }); - + $("#volumeSlider").on('mouseup', function() { $('#volumeSlider').blur(); }) - + $(freqUpButton).on("click", tuneUp); $(freqDownButton).on("click", tuneDown); $(psContainer).on("click", copyPs); @@ -212,37 +212,37 @@ $(document).ready(function () { $(freqContainer).on("click", function () { textInput.focus(); }); - + //FMLIST logging $('.popup-content').on('click', function(event) { event.stopPropagation(); $('.popup-content').removeClass('show'); }); - + $('.log-fmlist').on('click', function() { - const logKey = 'fmlistLogChoice'; - const logTimestampKey = 'fmlistLogTimestamp'; - const expirationTime = 10 * 60 * 1000; + const logKey = 'fmlistLogChoice'; + const logTimestampKey = 'fmlistLogTimestamp'; + const expirationTime = 10 * 60 * 1000; const now = Date.now(); - + const storedChoice = localStorage.getItem(logKey); const storedTimestamp = localStorage.getItem(logTimestampKey); - + if (storedChoice && storedTimestamp && (now - storedTimestamp < expirationTime)) { - sendLog(storedChoice); + sendLog(storedChoice); return; } - + if (parsedData.txInfo.dist > 700) { $('.log-fmlist .mini-popup-content').addClass('show'); // Show popup if no valid choice - + $('.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 .mini-popup-content').removeClass('show'); }); - + $('.log-fmlist-tropo').off('click').on('click', function () { localStorage.setItem(logKey, './log_fmlist?type=tropo'); localStorage.setItem(logTimestampKey, now); @@ -250,9 +250,9 @@ $(document).ready(function () { $('.log-fmlist .mini-popup-content').removeClass('show'); }); } else { - sendLog('./log_fmlist'); + sendLog('./log_fmlist'); } - + function sendLog(endpoint) { $.ajax({ url: endpoint, @@ -262,7 +262,7 @@ $(document).ready(function () { }, error: function(xhr) { let errorMessage; - + switch (xhr.status) { case 429: errorMessage = xhr.responseText; @@ -273,7 +273,7 @@ $(document).ready(function () { default: errorMessage = xhr.statusText || 'An error occurred'; } - + sendToast('error', 'Log failed', errorMessage, false, true); } }); @@ -290,7 +290,7 @@ function getServerTime() { dataType: "json", success: function(data) { const serverTimeUtc = data.serverTime; - + const options = { year: 'numeric', month: 'short', @@ -299,32 +299,32 @@ function getServerTime() { minute: '2-digit', hour12: false }; - + const serverOptions = { ...options, timeZone: 'Etc/UTC' }; - + const formattedServerTime = new Date(serverTimeUtc).toLocaleString(navigator.language ? navigator.language : 'en-US', serverOptions); - - $("#server-time").text(formattedServerTime); + + $("#server-time").text(formattedServerTime); }, error: function(jqXHR, textStatus, errorThrown) { console.error("Error fetching server time:", errorThrown); } }); -} +} function sendPingRequest() { const timeoutDuration = 5000; const startTime = new Date().getTime(); - + const fetchWithTimeout = (url, options, timeout = timeoutDuration) => { return new Promise((resolve, reject) => { const timerTimeout = setTimeout(() => { reject(new Error('Request timed out')); }, timeout); - + fetch(url, options) .then(response => { clearTimeout(timerTimeout); @@ -336,7 +336,7 @@ function sendPingRequest() { }); }); }; - + fetchWithTimeout('./ping', { cache: 'no-store' }, timeoutDuration) .then(response => { const endTime = new Date().getTime(); @@ -354,7 +354,7 @@ function sendPingRequest() { pingTimeLimit = true; } }); - + function handleMessage(message) { messageData = JSON.parse(message.data.length); socket.removeEventListener('message', handleMessage); @@ -362,7 +362,7 @@ function sendPingRequest() { socket.addEventListener('message', handleMessage); messageLength = messageData; messageData = 0; - + // Force reconnection if no WebSocket data after several queries if (messageLength === 0) { messageCounter++; @@ -375,7 +375,7 @@ function sendPingRequest() { } else { messageCounter = 0; } - + // Automatic reconnection on WebSocket close with cooldown const now = Date.now(); if ( @@ -421,12 +421,12 @@ function handleWebSocketMessage(event) { }, 500); return; } - + parsedData = JSON.parse(event.data); - + resetDataTimeout(); updatePanels(parsedData); - + const sum = signalData.reduce((acc, strNum) => acc + parseFloat(strNum), 0); const averageSignal = sum / signalData.length; data.push(averageSignal); @@ -500,11 +500,11 @@ function initCanvas() { beginAtZero: false, grace: 0.25, border: { display: false }, - ticks: { - maxTicksLimit: 3, + ticks: { + maxTicksLimit: 3, display: false // Hide default labels }, - grid: { + grid: { display: false, // Hide default grid lines }, }, @@ -513,11 +513,11 @@ function initCanvas() { beginAtZero: false, grace: 0.25, border: { display: false }, - ticks: { - maxTicksLimit: 3, + ticks: { + maxTicksLimit: 3, display: false // Hide default labels for the right axis }, - grid: { + grid: { display: false, // No grid for right axis } } @@ -558,21 +558,21 @@ function initCanvas() { case "dbm": adjustedTickValue = tick.value - 120; break; default: adjustedTickValue = tick.value; break; } - + if (isMiddleTick) { adjustedY += 3; } ctx.textAlign = 'right'; - ctx.fillText(adjustedTickValue.toFixed(1), leftX + 25, adjustedY); + ctx.fillText(adjustedTickValue.toFixed(1), leftX + 25, adjustedY); ctx.textAlign = 'left'; ctx.fillText(adjustedTickValue.toFixed(1), rightX - 25, adjustedY); // Right side }); - + const gridLineWidth = 0.5; // Make the lines thinner to avoid overlapping text const adjustedGridTop = chartArea.top + offset; const adjustedGridBottom = chartArea.bottom - offset; const middleY = chartArea.top + chartArea.height / 2; const padding = 45; // 30px inward on both sides - + // Helper function to draw a horizontal line function drawGridLine(y) { ctx.beginPath(); @@ -582,12 +582,12 @@ function initCanvas() { ctx.lineWidth = gridLineWidth; ctx.stroke(); } - + // Draw the three horizontal grid lines drawGridLine(adjustedGridTop); drawGridLine(adjustedGridBottom); drawGridLine(middleY); - + ctx.restore(); } }] @@ -634,12 +634,12 @@ socket.onmessage = (event) => { }, 500); return; } - + parsedData = JSON.parse(event.data); - + resetDataTimeout(); updatePanels(parsedData); - + const sum = signalData.reduce((acc, strNum) => acc + parseFloat(strNum), 0); const averageSignal = sum / signalData.length; data.push(averageSignal); @@ -661,7 +661,7 @@ function processString(string, errors) { const alpha_range = 50; const max_error = 10; errors = errors?.split(','); - + for (let i = 0; i < string.length; i++) { alpha = parseInt(errors[i]) * (alpha_range / (max_error + 1)); if (alpha) { @@ -670,27 +670,27 @@ function processString(string, errors) { output += escapeHTML(string[i]); } } - + return output; } function checkKey(e) { e = e || window.event; - + if (e.ctrlKey || e.altKey || e.shiftKey || e.metaKey) { return; } - + if ($('#password:focus').length > 0 || $('#chat-send-message:focus').length > 0 || $('#volumeSlider:focus').length > 0 || $('#chat-nickname:focus').length > 0 || $('.option:focus').length > 0) { - return; + return; } - + getCurrentFreq(); - + if (socket.readyState === WebSocket.OPEN) { switch (e.keyCode) { case 66: // Back to previous frequency @@ -717,9 +717,9 @@ function checkKey(e) { let $dropdown = $(".data-ant"); let $input = $dropdown.find("input"); let $options = $dropdown.find("ul.options .option"); - + if ($options.length === 0) return; // No antennas available - + // Find the currently selected antenna let currentText = $input.val().trim(); let currentIndex = $options.index($options.filter(function () { @@ -727,15 +727,15 @@ function checkKey(e) { })); console.log(currentIndex, currentText); - + // Cycle to the next option let nextIndex = (currentIndex + 1) % $options.length; let $nextOption = $options.eq(nextIndex); - + // Update UI $input.attr("placeholder", $nextOption.text()); $input.data("value", $nextOption.data("value")); - + let socketMessage = "Z" + $nextOption.data("value"); socket.send(socketMessage); break; @@ -769,7 +769,7 @@ async function copyPs() { var signal = $('#data-signal').text(); var signalDecimal = $('#data-signal-decimal').text(); var signalUnit = $('.signal-units').eq(0).text(); - + try { await copyToClipboard(frequency + " - " + pi + " | " + ps + " [" + signal + signalDecimal + " " + signalUnit + "]"); } catch (error) { @@ -785,7 +785,7 @@ async function copyTx() { const stationItu = $('#data-station-itu').text(); const stationDistance = $('#data-station-distance').text(); const stationErp = $('#data-station-erp').text(); - + try { await copyToClipboard(frequency + " - " + pi + " | " + stationName + " [" + stationCity + ", " + stationItu + "] - " + stationDistance + " | " + stationErp + " kW"); } catch (error) { @@ -796,7 +796,7 @@ async function copyTx() { async function copyRt() { var rt0 = $('#data-rt0 span').text(); var rt1 = $('#data-rt1 span').text(); - + try { await copyToClipboard("[0] RT: " + rt0 + "\n[1] RT: " + rt1); } catch (error) { @@ -818,10 +818,10 @@ function copyToClipboard(textToCopy) { 'position': 'absolute', 'left': '-999999px' }); - + $('body').prepend(textArea); textArea.select(); - + try { document.execCommand('copy'); } catch (error) { @@ -837,9 +837,9 @@ function findOnMaps() { var pi = $('#data-pi').text(); var latitude = localStorage.getItem('qthLongitude'); var longitude = localStorage.getItem('qthLatitude'); - + frequency > 74 ? frequency = frequency.toFixed(1) : null; - + var url = `https://maps.fmdx.org/#qth=${longitude},${latitude}&freq=${frequency}&findPi=${pi}`; window.open(url, "_blank"); } @@ -849,33 +849,33 @@ function updateSignalUnits(parsedData, averageSignal) { const signalUnit = localStorage.getItem('signalUnit'); let currentSignal; let highestSignal = parsedData.sigTop; - + currentSignal = averageSignal let signalText = $('.signal-units'); let signalValue; - + switch (signalUnit) { case 'dbuv': signalValue = currentSignal - 11.25; highestSignal = highestSignal - 11.25; signalText.text('dBµV'); break; - + case 'dbm': signalValue = currentSignal - 120; highestSignal = highestSignal - 120; signalText.text('dBm'); break; - + default: signalValue = currentSignal; signalText.text('dBf'); break; } - + const formatted = (Math.round(signalValue * 10) / 10).toFixed(1); const [integerPart, decimalPart] = formatted.split('.'); - + $('#data-signal-highest').text(Number(highestSignal).toFixed(1)); $('#data-signal').text(integerPart); $('#data-signal-decimal').text('.' + decimalPart); @@ -902,17 +902,17 @@ const $dataPty = $('.data-pty'); // Throttling function to limit the frequency of updates function throttle(fn, wait) { let isThrottled = false, savedArgs, savedThis; - + function wrapper() { if (isThrottled) { savedArgs = arguments; savedThis = this; return; } - + fn.apply(this, arguments); isThrottled = true; - + setTimeout(function() { isThrottled = false; if (savedArgs) { @@ -921,7 +921,7 @@ function throttle(fn, wait) { } }, wait); } - + return wrapper; } @@ -968,18 +968,18 @@ const updateDataElements = throttle(function(parsedData) { updateTextIfChanged($dataFrequency, parsedData.freq); $commandInput.attr("aria-label", "Current frequency: " + parsedData.freq); updateHtmlIfChanged($dataPi, parsedData.pi === '?' ? "?" : parsedData.pi); - + if ($('#ps-underscores').is(':checked')) { parsedData.ps = parsedData.ps.replace(/\s/g, '_'); } updateHtmlIfChanged($dataPs, parsedData.ps === '?' ? "?" : processString(parsedData.ps, parsedData.ps_errors)); - + if(parsedData.st) { $dataSt.parent().removeClass('opacity-half'); } else { $dataSt.parent().addClass('opacity-half'); } - + if(parsedData.stForced) { if (!parsedData.st) { stereoColor = 'gray'; @@ -992,28 +992,28 @@ const updateDataElements = throttle(function(parsedData) { $('.data-st.circle1').css('left', '0px'); $('.data-st.circle2').css('display', 'block'); } - + updateHtmlIfChanged($dataRt0, processString(parsedData.rt0, parsedData.rt0_errors)); updateHtmlIfChanged($dataRt1, processString(parsedData.rt1, parsedData.rt1_errors)); - + updateTextIfChanged($dataPty, rdsMode == 'true' ? usa_programmes[parsedData.pty] : europe_programmes[parsedData.pty]); - + if (parsedData.rds === true) { $flagDesktopCointainer.css('background-color', 'var(--color-2-transparent)'); } else { $flagDesktopCointainer.css('background-color', 'var(--color-1-transparent)'); } - + $('.data-flag').html(``); $('.data-flag-big').html(``); - + $dataAntInput.val($('.data-ant li[data-value="' + parsedData.ant + '"]').first().text()); - + if (typeof parsedData.agc !== 'undefined') $dataAgcInput.val($('.data-agc li[data-value="' + parsedData.agc + '"]').first().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 + '"]').first().text()); - + if (parsedData.txInfo.tx.length > 1) { updateTextIfChanged($('#data-station-name'), parsedData.txInfo.tx.replace(/%/g, '%25')); updateTextIfChanged($('#data-station-erp'), parsedData.txInfo.erp); @@ -1028,17 +1028,12 @@ const updateDataElements = throttle(function(parsedData) { updateHtmlIfChanged($('#alternative-txes'), altTxInfo); updateTextIfChanged($('#data-station-distance'), txDistance); $dataStationContainer.css('display', 'block'); - } else { - $dataStationContainer.removeAttr('style'); - } - - if(parsedData.txInfo.tx.length > 1 && parsedData.txInfo.dist > 150 && parsedData.txInfo.dist < 4000) { - $('.log-fmlist').removeAttr('disabled').removeClass('btn-disabled cursor-disabled'); - } else { - $('.log-fmlist').attr('disabled', 'true').addClass('btn-disabled cursor-disabled'); - } + } else $dataStationContainer.removeAttr('style'); + + if(parsedData.txInfo.tx.length > 1 && parsedData.txInfo.dist > 150 && parsedData.txInfo.dist < 4000) $('.log-fmlist').removeAttr('disabled').removeClass('btn-disabled cursor-disabled'); + else $('.log-fmlist').attr('disabled', 'true').addClass('btn-disabled cursor-disabled'); updateHtmlIfChanged($('#data-regular-pi'), parsedData.txInfo.reg === true ? parsedData.txInfo.pi : ' '); - + if (updateCounter % 8 === 0) { $dataTp.html(parsedData.tp === 0 ? "TP" : "TP"); $dataTa.html(parsedData.ta === 0 ? "TA" : "TA"); @@ -1050,7 +1045,7 @@ const updateDataElements = throttle(function(parsedData) { ) ); } - + if (updateCounter % 30 === 0) { $dataPs.attr('aria-label', parsedData.ps); $dataRt0.attr('aria-label', parsedData.rt0); @@ -1063,36 +1058,35 @@ let isEventListenerAdded = false; function updatePanels(parsedData) { updateCounter = (updateCounter % 10000) + 1; // Count to 10000 then reset back to 1 - + signalData.push(parsedData.sig); if (signalData.length > 8) { signalData.shift(); // Remove the oldest element } const sum = signalData.reduce((acc, strNum) => acc + parseFloat(strNum), 0); const averageSignal = sum / signalData.length; - + const sortedAf = parsedData.af.sort(compareNumbers); const scaledArray = sortedAf.map(element => element / 1000); - + const listContainer = $('#af-list'); const scrollTop = listContainer.scrollTop(); let ul = listContainer.find('ul'); - + if (!ul.length) { ul = $('
    '); listContainer.append(ul); } - + if (updateCounter % 3 === 0) { - updateButtonState("data-eq", parsedData.eq); updateButtonState("data-ims", parsedData.ims); - + // Only update #af-list on every 3rd call ul.html(''); const listItems = scaledArray.map(createListItem); ul.append(listItems); - + // Add the event listener only once if (!isEventListenerAdded) { ul.on('click', 'a', function () { @@ -1101,10 +1095,10 @@ function updatePanels(parsedData) { }); isEventListenerAdded = true; } - + listContainer.scrollTop(scrollTop); } - + updateDataElements(parsedData); updateSignalUnits(parsedData, averageSignal); $('.users-online').text(parsedData.users); @@ -1116,11 +1110,11 @@ function createListItem(element) { function updateButtonState(buttonId, value) { var button = $("#" + buttonId); - + if (button.length === 0) { button = $("." + buttonId); } - + if (button.length > 0) { if (value == 0) { button.hasClass("btn-disabled") ? null : button.addClass("btn-disabled"); @@ -1152,7 +1146,7 @@ function toggleForcedStereo() { function toggleLock(buttonSelector, activeMessage, inactiveMessage, activeLabel, inactiveLabel) { let $lockButton = $(buttonSelector); - + if ($lockButton.hasClass('active')) { socket.send(inactiveMessage); $lockButton.attr('aria-label', inactiveLabel); @@ -1166,17 +1160,17 @@ function toggleLock(buttonSelector, activeMessage, inactiveMessage, activeLabel, function showTunerDescription() { let parentDiv = $("#tuner-name").parent(); - + if (!$("#dashboard-panel-description").is(":visible")) { parentDiv.css("border-radius", "15px 15px 0 0"); } - + $("#dashboard-panel-description").slideToggle(300, function() { if (!$(this).is(":visible")) { parentDiv.css("border-radius", ""); } }); - + $("#tuner-name i").toggleClass("rotated"); if ($(window).width() < 768) { @@ -1187,29 +1181,29 @@ function showTunerDescription() { function initTooltips(target = null) { // Define scope: all tooltips or specific one if target is provided const tooltips = target ? $(target) : $('.tooltip'); - + // Unbind existing event handlers before rebinding to avoid duplication tooltips.off('mouseenter mouseleave'); - + tooltips.hover(function () { if ($(this).closest('.popup-content').length) { return; } - + var tooltipText = $(this).data('tooltip'); var placement = $(this).data('tooltip-placement') || 'top'; // Default to 'top' - + // Clear existing timeouts $(this).data('timeout', setTimeout(() => { $('.tooltip-wrapper').remove(); - + var tooltip = $(`
    ${tooltipText}
    `); $('body').append(tooltip); - + var tooltipEl = $('.tooltiptext'); var tooltipWidth = tooltipEl.outerWidth(); var tooltipHeight = tooltipEl.outerHeight(); @@ -1217,7 +1211,7 @@ function initTooltips(target = null) { var targetOffset = targetEl.offset(); var targetWidth = targetEl.outerWidth(); var targetHeight = targetEl.outerHeight(); - + // Compute position var posX, posY; switch (placement) { @@ -1239,7 +1233,7 @@ function initTooltips(target = null) { posY = targetOffset.top - tooltipHeight - 10; break; } - + // Apply positioning tooltipEl.css({ top: posY, left: posX, opacity: 1 }); @@ -1247,32 +1241,32 @@ function initTooltips(target = null) { if ((/Mobi|Android|iPhone|iPad|iPod|Opera Mini/i.test(navigator.userAgent)) && ('ontouchstart' in window || navigator.maxTouchPoints)) { setTimeout(() => { $('.tooltiptext').remove(); }, 5000); } - + }, 300)); }, function () { clearTimeout($(this).data('timeout')); - + setTimeout(() => { $('.tooltip-wrapper').fadeOut(300, function () { - $(this).remove(); + $(this).remove(); }); - }, 100); + }, 100); }); - + $('.popup-content').off('mouseenter').on('mouseenter', function () { clearTimeout($('.tooltip').data('timeout')); $('.tooltip-wrapper').fadeOut(300, function () { - $(this).remove(); + $(this).remove(); }); }); } - + function fillPresets() { let hasAnyPreset = false; - + for (let i = 1; i <= 4; i++) { let presetText = localStorage.getItem(`preset${i}`); - + if (presetText != "null") { hasAnyPreset = true; $(`#preset${i}-text`).text(presetText); @@ -1283,9 +1277,8 @@ function initTooltips(target = null) { $(`#preset${i}`).hide(); } } - + if (!hasAnyPreset) { $('#preset1').parent().hide(); } } - \ No newline at end of file diff --git a/web/js/modal.js b/web/js/modal.js index 6646d27..cb6876b 100644 --- a/web/js/modal.js +++ b/web/js/modal.js @@ -3,15 +3,15 @@ $(document).ready(function() { var modalPanel = $(".modal-panel"); var openBtn = $(".settings"); var closeBtn = $(".closeModal, .closeModalButton"); - + initPopups(); - + openBtn.on("click", function() { openModal(modalPanel); }); - + closeBtn.on("click", closeModal); - + function openModal(panel) { modal.css("display", "block"); panel.css("display", "block"); @@ -20,7 +20,7 @@ $(document).ready(function() { modal.css("opacity", 1); }, 10); } - + function closeModal() { modal.css("opacity", 0); setTimeout(function() { @@ -28,24 +28,20 @@ $(document).ready(function() { $("body").removeClass("modal-open"); // Enable body scrolling }, 300); } - + $(document).on("click", function(event) { // Close the modal when clicking outside of it - if ($(event.target).is(modal)) { - closeModal(); - } + if ($(event.target).is(modal)) closeModal(); }); - + $(document).on("keydown", function(event) { // Close the modal when pressing ESC key - if (event.key === "Escape") { - closeModal(); - } + if (event.key === "Escape") closeModal(); }); $(".tuner-mobile-settings").on("click", function () { togglePopup("#popup-panel-mobile-settings"); }); - + $("#data-station-others").on("click", function () { togglePopup("#popup-panel-transmitters"); }); @@ -54,13 +50,13 @@ $(document).ready(function() { function initPopups() { $(".popup-window").draggable({ handle: ".popup-header", - containment: "body" + containment: "body" }).resizable({ minHeight: 330, minWidth: 350, containment: "body" }); - + $(".popup-close").on("click", function () { $(".popup-window").fadeOut(200); }); @@ -69,9 +65,8 @@ function initPopups() { function togglePopup(targetSelector) { const $target = $(targetSelector); - if ($target.is(":visible")) { - $target.fadeOut(200); - } else { + if ($target.is(":visible")) $target.fadeOut(200); + else { $(".popup-window").fadeOut(200); $target.fadeIn(200); } diff --git a/web/js/settings.js b/web/js/settings.js index 382e522..ea7e64f 100644 --- a/web/js/settings.js +++ b/web/js/settings.js @@ -189,9 +189,7 @@ function loadInitialSettings() { if(signalParameter && !localStorage.getItem('signalUnit')) { signalSelector.find('input').val(signalSelector.find('.option[data-value="' + signalParameter + '"]').text()); localStorage.setItem('signalUnit', signalParameter); - } else { - signalSelector.find('input').val(signalSelector.find('.option[data-value="' + savedUnit + '"]').text()); - } + } else signalSelector.find('input').val(signalSelector.find('.option[data-value="' + savedUnit + '"]').text()); signalSelector.on('click', '.option', (event) => { const selectedSignalUnit = $(event.target).data('value'); diff --git a/web/js/setup.js b/web/js/setup.js index 18f2b6e..74601f3 100644 --- a/web/js/setup.js +++ b/web/js/setup.js @@ -27,9 +27,7 @@ function mapCreate() { zoom: 3, worldCopyJump: true }); - } else { - map.setZoom(3).panTo([40, 0]); - } + } else map.setZoom(3).panTo([40, 0]); L.tileLayer(tilesURL, { attribution: mapAttrib, @@ -272,9 +270,7 @@ function checkTunnelServers() { // If this li is the currently selected one, update input text too // Note: input.val() holds the label, so match by label is safer - if ($li.text() === selectedValue || server.value === selectedValue) { - $input.val(server.label); - } + if ($li.text() === selectedValue || server.value === selectedValue) $input.val(server.label); } }); }, diff --git a/web/js/wizard.js b/web/js/wizard.js index 65fb492..cba4580 100644 --- a/web/js/wizard.js +++ b/web/js/wizard.js @@ -27,8 +27,6 @@ function navigateStep(isNext) { currentStep.hide(); targetStep.show(); updateProgressBar(targetStep); - } else if (isNext) { - submitConfig(); - } + } else if (isNext) submitConfig(); updateWizardContent(); } \ No newline at end of file diff --git a/web/setup.ejs b/web/setup.ejs index 8b86fe2..1a8cebd 100644 --- a/web/setup.ejs +++ b/web/setup.ejs @@ -109,7 +109,7 @@ <%= user.ip.replace('::ffff:', '') %> - + <%= user.location %> <%= user.time %> Kick @@ -121,10 +121,10 @@ <% } %> - +
    - +

    Quick settings

    @@ -132,7 +132,7 @@ <%- include('_components', {component: 'checkbox', cssClass: '', label: 'Unlocked Tuner', id: 'publicTuner'}) %> <%- include('_components', {component: 'checkbox', cssClass: '', label: 'Admin lock', id: 'lockToAdmin'}) %>
    - + <%- include('_components', {component: 'text', cssClass: 'w-150 br-15', placeholder: '', label: 'Tune password', id: 'password-tunePass', password: true}) %> <%- include('_components', {component: 'text', cssClass: 'w-150 br-15', placeholder: '', label: 'Admin password', id: 'password-adminPass', password: true}) %>
    @@ -156,7 +156,7 @@

    Audio settings

    - +

    Device

    @@ -181,14 +181,14 @@ label: `${device.name}` })) ] - }) %> + }) %>

    Channels

    Audio channel count.
    Choose between Mono / Stereo.

    - <%- include('_components', { component: 'dropdown', id: 'audio-channels-dropdown', inputId: 'audio-audioChannels', label: 'Audio channels', cssClass: '', placeholder: 'Stereo', + <%- include('_components', { component: 'dropdown', id: 'audio-channels-dropdown', inputId: 'audio-audioChannels', label: 'Audio channels', cssClass: '', placeholder: 'Stereo', options: [ { value: '2', label: 'Stereo' }, { value: '1', label: 'Mono' } @@ -200,7 +200,7 @@

    The bitrate of the mp3 audio.
    Minimum: 64 Kbps • Maximum: 320 Kbps

    - <%- include('_components', { component: 'dropdown', id: 'audio-quality-dropdown', inputId: 'audio-audioBitrate', label: 'Audio quality', cssClass: '', placeholder: '128kbps (standard)', + <%- include('_components', { component: 'dropdown', id: 'audio-quality-dropdown', inputId: 'audio-audioBitrate', label: 'Audio quality', cssClass: '', placeholder: '128kbps (standard)', options: [ { value: '64k', label: '64kbps (lowest quality)' }, { value: '96k', label: '96kbps (low quality)' }, @@ -251,11 +251,11 @@

    Design

    -

    Background image

    - <%- include('_components', {component: 'text', cssClass: 'br-15', placeholder: 'Direct image link', label: 'Image link', id: 'webserver-bgImage'}) %>
    +

    Background image

    + <%- include('_components', {component: 'text', cssClass: 'br-15', 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', + <%- include('_components', { component: 'dropdown', id: 'server-theme-selector', inputId: 'webserver-defaultTheme', label: 'Default server theme', cssClass: '', placeholder: 'Default', options: [ { value: 'theme1', label: 'Mint' }, { value: 'theme2', label: 'Cappuccino' }, @@ -280,17 +280,17 @@ <%- include('_components', {component: 'checkbox', cssClass: '', label: 'Antenna 1', id: 'antennas-ant1-enabled'}) %> <%- include('_components', {component: 'text', cssClass: 'w-100 br-15', placeholder: 'Ant A', label: 'Antenna 1 name', id: 'antennas-ant1-name'}) %>
    - +
    <%- include('_components', {component: 'checkbox', cssClass: 'top-25', label: 'Antenna 2', id: 'antennas-ant2-enabled'}) %> <%- include('_components', {component: 'text', cssClass: 'w-100 br-15', placeholder: 'Ant B', label: 'Antenna 2 name', id: 'antennas-ant2-name'}) %>
    - +
    <%- include('_components', {component: 'checkbox', cssClass: '', label: 'Antenna 3', id: 'antennas-ant3-enabled'}) %> <%- include('_components', {component: 'text', cssClass: 'w-100 br-15', 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 br-15', placeholder: 'Ant D', label: 'Antenna 4 name', id: 'antennas-ant4-name'}) %>
    @@ -337,7 +337,7 @@

    Transmitter Search Algorithm

    Different modes may help with more accurate transmitter identification depending on your region.

    - <%- include('_components', { component: 'dropdown', id: 'server-tx-id-algo', inputId: 'webserver-txIdAlgorithm', label: 'Transmitter ID Algorithm', cssClass: '', placeholder: 'Algorithm 1', + <%- include('_components', { component: 'dropdown', id: 'server-tx-id-algo', inputId: 'webserver-txIdAlgorithm', label: 'Transmitter ID Algorithm', cssClass: '', placeholder: 'Algorithm 1', options: [ { value: '0', label: 'Algorithm 1' }, { value: '1', label: 'Algorithm 2' }, @@ -354,7 +354,7 @@

    Device type

    - <%- include('_components', { component: 'dropdown', id: 'device-selector', inputId: 'device', label: 'Device', cssClass: '', placeholder: 'TEF668x / TEA685x', + <%- include('_components', { component: 'dropdown', id: 'device-selector', inputId: 'device', label: 'Device', cssClass: '', placeholder: 'TEF668x / TEA685x', options: tunerProfiles.map(profile => ({ value: profile.id, label: profile.label @@ -374,7 +374,7 @@ Direct TCP/IP - +
    @@ -390,9 +390,9 @@ value: serialPort.path, label: `${serialPort.path} - ${serialPort.friendlyName}` })) - }) %> + }) %>
    - +

    If you are connecting your tuner wirelessly, enter the tuner IP.
    If you use xdrd, use 127.0.0.1 as your IP.

    <%- include('_components', {component: 'text', cssClass: 'w-150 br-15', label: 'xdrd IP address', id: 'xdrd-xdrdIp'}) %> @@ -409,7 +409,7 @@

    - +

    Default frequency

    <%- include('_components', {component: 'checkbox', cssClass: '', label: 'Default frequency for first client', id: 'enableDefaultFreq'}) %>
    @@ -468,7 +468,7 @@

    Basic info

    - +

    Set your tuner name and description here.
    This info will be visible to anyone who tunes in.

    @@ -486,7 +486,7 @@ <%- include('_components', {component: 'checkbox', cssClass: '', label: 'Broadcast to map', id: 'identification-broadcastTuner'}) %>
    <%- include('_components', {component: 'text', cssClass: 'br-15', placeholder: 'Your e-mail or Discord...', label: 'Owner contact', id: 'identification-contact'}) %> <%- include('_components', {component: 'text', cssClass: 'br-15', label: 'Proxy address', id: 'identification-proxyIp'}) %> - +

    Check your tuner at servers.fmdx.org.

    By activating the Broadcast to map option,
    you agree to the Terms of Service.

    @@ -498,7 +498,7 @@ <%- include('_components', {component: 'text', cssClass: 'w-150 br-15', placeholder: '', label: 'Latitude', id: 'identification-lat'}) %> <%- include('_components', {component: 'text', cssClass: 'w-150 br-15', placeholder: '', label: 'Longitude', id: 'identification-lon'}) %> - +

    @@ -508,7 +508,7 @@

    User management

    Chat options

    - <%- include('_components', {component: 'checkbox', cssClass: '', label: 'Chat', id: 'webserver-chatEnabled'}) %> + <%- include('_components', {component: 'checkbox', cssClass: '', label: 'Chat', id: 'webserver-chatEnabled'}) %>
    @@ -573,33 +573,33 @@

    These settings will be applied after a server launch or restart.

    <% if (device === 'tef') { %> - <%- include('_components', { component: 'dropdown', id: 'ceqStartup-dropdown', inputId: 'ceqStartup', label: 'cEQ', cssClass: '', placeholder: 'Disabled', + <%- include('_components', { component: 'dropdown', id: 'ceqStartup-dropdown', inputId: 'ceqStartup', label: 'cEQ', cssClass: '', placeholder: 'Disabled', options: [ { value: '0', label: 'Disabled' }, { value: '1', label: 'Enabled' }, ] }) %>
    - <%- include('_components', { component: 'dropdown', id: 'imsStartup-dropdown', inputId: 'imsStartup', label: 'iMS', cssClass: '', placeholder: 'Disabled', + <%- include('_components', { component: 'dropdown', id: 'imsStartup-dropdown', inputId: 'imsStartup', label: 'iMS', cssClass: '', placeholder: 'Disabled', options: [ { value: '0', label: 'Disabled' }, { value: '1', label: 'Enabled' }, ] }) %>
    <% } else if (device === 'xdr') { %> - <%- include('_components', { component: 'dropdown', id: 'rfStartup-dropdown', inputId: 'ceqStartup', label: 'RF+', cssClass: '', placeholder: 'Disabled', + <%- include('_components', { component: 'dropdown', id: 'rfStartup-dropdown', inputId: 'ceqStartup', label: 'RF+', cssClass: '', placeholder: 'Disabled', options: [ { value: '0', label: 'Disabled' }, { value: '1', label: 'Enabled' }, ] }) %>
    - <%- include('_components', { component: 'dropdown', id: 'ifStartup-dropdown', inputId: 'imsStartup', label: 'IF+', cssClass: '', placeholder: 'Disabled', + <%- include('_components', { component: 'dropdown', id: 'ifStartup-dropdown', inputId: 'imsStartup', label: 'IF+', cssClass: '', placeholder: 'Disabled', options: [ { value: '0', label: 'Disabled' }, { value: '1', label: 'Enabled' }, ] }) %>
    <% } %> - <%- include('_components', { component: 'dropdown', id: 'stereoStartup-dropdown', inputId: 'stereoStartup', label: 'Stereo Mode', cssClass: '', placeholder: 'Stereo (Default)', + <%- include('_components', { component: 'dropdown', id: 'stereoStartup-dropdown', inputId: 'stereoStartup', label: 'Stereo Mode', cssClass: '', placeholder: 'Stereo (Default)', options: [ { value: '0', label: 'Stereo (Default)' }, { value: '1', label: 'Mono' }, @@ -607,7 +607,7 @@ }) %>
    - <%- include('_components', { component: 'dropdown', id: 'antennaStartup-dropdown', inputId: 'antennaStartup', label: 'Antenna', cssClass: '', placeholder: 'Antenna 0 (Default)', + <%- include('_components', { component: 'dropdown', id: 'antennaStartup-dropdown', inputId: 'antennaStartup', label: 'Antenna', cssClass: '', placeholder: 'Antenna 0 (Default)', options: [ { value: '0', label: 'Antenna 0 (Default)' }, { value: '1', label: 'Antenna 1' }, @@ -623,21 +623,21 @@

    Empty server defaults

    These settings will apply once the last user disconnects from the server, so the server can be ready for a new user with default settings.

    - <%- include('_components', { component: 'dropdown', id: 'bwAutoNoUsers-dropdown', inputId: 'bwAutoNoUsers', label: 'Auto BW', cssClass: '', placeholder: 'Unchanged', + <%- include('_components', { component: 'dropdown', id: 'bwAutoNoUsers-dropdown', inputId: 'bwAutoNoUsers', label: 'Auto BW', cssClass: '', placeholder: 'Unchanged', options: [ { value: '0', label: 'Unchanged' }, { value: '1', label: 'Enabled' }, ] }) %>
    <% if (device === 'tef') { %> - <%- include('_components', { component: 'dropdown', id: 'ceqNoUsers-dropdown', inputId: 'ceqNoUsers', label: 'cEQ', cssClass: '', placeholder: 'Unchanged', + <%- include('_components', { component: 'dropdown', id: 'ceqNoUsers-dropdown', inputId: 'ceqNoUsers', label: 'cEQ', cssClass: '', placeholder: 'Unchanged', options: [ { value: '0', label: 'Unchanged' }, { value: '1', label: 'Disabled' }, { value: '2', label: 'Enabled' }, ] }) %>
    - <%- include('_components', { component: 'dropdown', id: 'imsNoUsers-dropdown', inputId: 'imsNoUsers', label: 'iMS', cssClass: '', placeholder: 'Unchanged', + <%- include('_components', { component: 'dropdown', id: 'imsNoUsers-dropdown', inputId: 'imsNoUsers', label: 'iMS', cssClass: '', placeholder: 'Unchanged', options: [ { value: '0', label: 'Unchanged' }, { value: '1', label: 'Disabled' }, @@ -645,14 +645,14 @@ ] }) %>
    <% } else if (device === 'xdr') { %> - <%- include('_components', { component: 'dropdown', id: 'rfNoUsers-dropdown', inputId: 'ceqNoUsers', label: 'RF+', cssClass: '', placeholder: 'Unchanged', + <%- include('_components', { component: 'dropdown', id: 'rfNoUsers-dropdown', inputId: 'ceqNoUsers', label: 'RF+', cssClass: '', placeholder: 'Unchanged', options: [ { value: '0', label: 'Unchanged' }, { value: '1', label: 'Disabled' }, { value: '2', label: 'Enabled' }, ] }) %>
    - <%- include('_components', { component: 'dropdown', id: 'ifNoUsers-dropdown', inputId: 'imsNoUsers', label: 'IF+', cssClass: '', placeholder: 'Unchanged', + <%- include('_components', { component: 'dropdown', id: 'ifNoUsers-dropdown', inputId: 'imsNoUsers', label: 'IF+', cssClass: '', placeholder: 'Unchanged', options: [ { value: '0', label: 'Unchanged' }, { value: '1', label: 'Disabled' }, @@ -660,7 +660,7 @@ ] }) %>
    <% } %> - <%- include('_components', { component: 'dropdown', id: 'stereoNoUsers-dropdown', inputId: 'stereoNoUsers', label: 'Stereo Mode', cssClass: '', placeholder: 'Unchanged', + <%- include('_components', { component: 'dropdown', id: 'stereoNoUsers-dropdown', inputId: 'stereoNoUsers', label: 'Stereo Mode', cssClass: '', placeholder: 'Unchanged', options: [ { value: '0', label: 'Unchanged' }, { value: '1', label: 'Stereo' }, @@ -670,7 +670,7 @@
    <%- include('_components', {component: 'checkbox', cssClass: '', label: 'Delayed Antenna Change', id: 'antennaNoUsersDelay'}) %>
    - <%- include('_components', { component: 'dropdown', id: 'antennaNoUsers-dropdown', inputId: 'antennaNoUsers', label: 'Antenna', cssClass: '', placeholder: 'Unchanged', + <%- include('_components', { component: 'dropdown', id: 'antennaNoUsers-dropdown', inputId: 'antennaNoUsers', label: 'Antenna', cssClass: '', placeholder: 'Unchanged', options: [ { value: '0', label: 'Unchanged' }, { value: '1', label: 'Antenna 0' }, @@ -705,7 +705,7 @@

    You can also get an tunnel from kuba201 discord, one of the contributors of this version of the application.

    Main tunnel settings

    <%- include('_components', {component: 'checkbox', cssClass: 'm-right-10', label: 'Enable tunnel', id: 'tunnel-enabled'}) %>
    - <%- include('_components', { component: 'dropdown', id: 'tunnel-regionSelect', inputId: 'tunnel-region', label: 'Official server region', cssClass: '', placeholder: 'Europe', + <%- include('_components', { component: 'dropdown', id: 'tunnel-regionSelect', inputId: 'tunnel-region', label: 'Official server region', cssClass: '', placeholder: 'Europe', options: [ { value: 'eu', label: 'Europe' }, { value: 'us', label: 'Americas' }, diff --git a/web/wizard.ejs b/web/wizard.ejs index 74dcb64..89a6953 100644 --- a/web/wizard.ejs +++ b/web/wizard.ejs @@ -48,7 +48,7 @@

    Tuner type

    Settings a proper device type ensures that the correct interface and settings will load.

    - <%- include('_components', { component: 'dropdown', id: 'device-selector', inputId: 'device', label: 'Device', cssClass: '', placeholder: 'TEF668x / TEA685x', + <%- include('_components', { component: 'dropdown', id: 'device-selector', inputId: 'device', label: 'Device', cssClass: '', placeholder: 'TEF668x / TEA685x', options: tunerProfiles.map(profile => ({ value: profile.id, label: profile.label @@ -64,12 +64,12 @@ Direct TCP/IP - +

    It's time to choose your serial port.

    - +
    <%- include('_components', { component: 'dropdown', @@ -82,7 +82,7 @@ value: serialPort.path, label: `${serialPort.path} - ${serialPort.friendlyName}` })) - }) %> + }) %>
    @@ -103,7 +103,7 @@

    In this section, we will set up the audio.
    Choose the audio port your tuner is connected to and desired audio settings here.

    Recommended defaults have already been set for the audio quality, you can keep them as-is.

    - +
    <%- include('_components', { component: 'dropdown', @@ -122,16 +122,16 @@ label: `${device.name}` })) ] - }) %> - - <%- include('_components', { component: 'dropdown', id: 'audio-channels-dropdown', inputId: 'audio-audioChannels', label: 'Audio channels', cssClass: '', placeholder: 'Stereo', + }) %> + + <%- include('_components', { component: 'dropdown', id: 'audio-channels-dropdown', inputId: 'audio-audioChannels', label: 'Audio channels', cssClass: '', placeholder: 'Stereo', options: [ { value: '2', label: 'Stereo' }, { value: '1', label: 'Mono' } ] }) %> - - <%- include('_components', { component: 'dropdown', id: 'audio-quality-dropdown', inputId: 'audio-audioBitrate', label: 'Audio quality', cssClass: '', placeholder: '128kbps (standard)', + + <%- include('_components', { component: 'dropdown', id: 'audio-quality-dropdown', inputId: 'audio-audioBitrate', label: 'Audio quality', cssClass: '', placeholder: '128kbps (standard)', options: [ { value: '64k', label: '64kbps (lowest quality)' }, { value: '96k', label: '96kbps (low quality)' }, @@ -168,7 +168,7 @@
    - +

    Location

    Location info is useful for automatic identification of stations using RDS.