You've already forked fm-dx-webserver
mirror of
https://github.com/KubaPro010/fm-dx-webserver.git
synced 2026-02-26 22:13:53 +01:00
some changes
This commit is contained in:
@@ -13,14 +13,14 @@
|
||||
<div class="panel-100 no-bg">
|
||||
<img class="top-10" src="./images/openradio_logo_neutral.png" height="64px">
|
||||
<h2 class="text-monospace text-light text-center">[403]</h2>
|
||||
|
||||
|
||||
<div class="panel-100 p-10">
|
||||
<br>
|
||||
<i class="text-big fa-solid fa-exclamation-triangle color-4"></i>
|
||||
<p>
|
||||
There's a possibility you were kicked by the system.<br>
|
||||
Please try again later.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="js/settings.js"></script>
|
||||
|
||||
@@ -261,7 +261,7 @@
|
||||
</span>
|
||||
<% 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)) { %>
|
||||
<button class="tooltip bg-color-4 mini-popup log-fmlist" data-tooltip="<strong>LOG TO FMLIST</strong><br>Clicking this button logs the current station to FMLIST's visual logbook." aria-label="Log to FMLIST" style="width: 80px; height: 48px;margin-left: 15px !important;">
|
||||
<i class="fa-solid fa-flag fa-lg"></i>
|
||||
@@ -367,10 +367,10 @@
|
||||
<div style="max-height: 48px;width: 50%;margin-right: 5px;">
|
||||
<%- include('_bwSwitch', { device: device, tunerProfiles: tunerProfiles, id: 'data-bw-phone', cssClass: 'panel-100-real', cssClassOptions: 'text-center open-bottom' }) %>
|
||||
</div>
|
||||
<% } %>
|
||||
<% } %>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<p class="flex-phone flex-center">Filters</p>
|
||||
<div class="flex-container flex-phone flex-center">
|
||||
<% if (device == 'tef') { %><button class="data-eq tooltip p-10 m-right-5" style="height: 48px" aria-label="EQ Filter" data-tooltip="<strong>The cEQ filter can reduce bandwidth below 56 kHz.</strong><br><br>Useful for weak stations next to strong ones,<br>although it may pick up more interference."><span class="text-bold">cEQ</span></button><% } %>
|
||||
@@ -445,14 +445,14 @@
|
||||
|
||||
<div style="width: calc(50% - 32px);text-align: center;">
|
||||
<button class="users-online-container" aria-label="Online users" style="display: inline-block;"><i class="fa-solid fa-user"></i> <span class="users-online"></span></button>
|
||||
|
||||
|
||||
<% if (chatEnabled) { %>
|
||||
<button class="chatbutton m-10" aria-label="Chatbox" style="display: inline-block;"><i class="fa-solid fa-message"></i></button>
|
||||
<% } %>
|
||||
</div>
|
||||
|
||||
|
||||
<div style="width: 64px;text-align: center;">
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div style="width: calc(50% - 32px);text-align: center;">
|
||||
@@ -469,7 +469,7 @@
|
||||
<h1 class="top-25">Settings</h1>
|
||||
|
||||
<div class="panel-full flex-center no-bg m-0">
|
||||
<%- 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') { %>
|
||||
<div class="panel-full flex-center no-bg m-0">
|
||||
<%- 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' },
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 ? '<span style="color: #bada55">[ADMIN]</span>' : '';
|
||||
|
||||
|
||||
if (messageData.type === 'clientIp') {
|
||||
chatIdentityNickname.html(isAdmin).append(document.createTextNode(" " + savedNickname));
|
||||
chatIdentityNickname.attr('title', messageData.ip);
|
||||
} else {
|
||||
const chatMessage = `
|
||||
<span class="color-2">[${messageData.time}]</span>
|
||||
${isAdmin} <strong class="color-5" title="${typeof messageData.ip !== "undefined" ? 'IP Address: ' + messageData.ip : ''}">${messageData.nickname}</strong>:
|
||||
${isAdmin} <strong class="color-5" title="${typeof messageData.ip !== "undefined" ? 'IP Address: ' + messageData.ip : ''}">${messageData.nickname}</strong>:
|
||||
<span style="color: var(--color-text-2);">${$('<div/>').text(messageData.message).html()}</span><br>
|
||||
`;
|
||||
chatMessages.append(chatMessage);
|
||||
|
||||
|
||||
if (chatMessages.is(':visible')) {
|
||||
setTimeout(function() {
|
||||
chatMessages.scrollTop(chatMessages[0].scrollHeight);
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
311
web/js/main.js
311
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 === '?' ? "<span class='opacity-half'>?</span>" : parsedData.pi);
|
||||
|
||||
|
||||
if ($('#ps-underscores').is(':checked')) {
|
||||
parsedData.ps = parsedData.ps.replace(/\s/g, '_');
|
||||
}
|
||||
updateHtmlIfChanged($dataPs, parsedData.ps === '?' ? "<span class='opacity-half'>?</span>" : 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(`<i title="${parsedData.country_name}" class="flag-sm flag-sm-${parsedData.country_iso}"></i>`);
|
||||
$('.data-flag-big').html(`<i title="${parsedData.country_name}" class="flag-md flag-md-${parsedData.country_iso}"></i>`);
|
||||
|
||||
|
||||
$dataAntInput.val($('.data-ant li[data-value="' + parsedData.ant + '"]').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 ? "<span class='opacity-half'>TP</span>" : "TP");
|
||||
$dataTa.html(parsedData.ta === 0 ? "<span class='opacity-half'>TA</span>" : "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 = $('<ul></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 = $(`
|
||||
<div class="tooltip-wrapper">
|
||||
<div class="tooltiptext">${tooltipText}</div>
|
||||
</div>
|
||||
`);
|
||||
$('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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@@ -27,8 +27,6 @@ function navigateStep(isNext) {
|
||||
currentStep.hide();
|
||||
targetStep.show();
|
||||
updateProgressBar(targetStep);
|
||||
} else if (isNext) {
|
||||
submitConfig();
|
||||
}
|
||||
} else if (isNext) submitConfig();
|
||||
updateWizardContent();
|
||||
}
|
||||
@@ -109,7 +109,7 @@
|
||||
<a href="https://dnschecker.org/ip-location.php?ip=<%= user.ip.replace('::ffff:', '') %>" target="_blank">
|
||||
<%= user.ip.replace('::ffff:', '') %>
|
||||
</a>
|
||||
</td>
|
||||
</td>
|
||||
<td><%= user.location %></td>
|
||||
<td><%= user.time %></td>
|
||||
<td><a href="./kick?ip=<%= user.ip %>">Kick</a></td>
|
||||
@@ -121,10 +121,10 @@
|
||||
</tr>
|
||||
<% } %>
|
||||
</tbody>
|
||||
</table>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex-container">
|
||||
<div class="panel-100-real p-bottom-20">
|
||||
<h3>Quick settings</h3>
|
||||
@@ -132,7 +132,7 @@
|
||||
<%- include('_components', {component: 'checkbox', cssClass: '', label: 'Unlocked Tuner', id: 'publicTuner'}) %>
|
||||
<%- include('_components', {component: 'checkbox', cssClass: '', label: 'Admin lock', id: 'lockToAdmin'}) %><br>
|
||||
</div>
|
||||
|
||||
|
||||
<%- 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}) %><br>
|
||||
</div>
|
||||
@@ -156,7 +156,7 @@
|
||||
|
||||
<div class="panel-full tab-content no-bg m-0" id="audio" role="tabpanel">
|
||||
<h2>Audio settings</h2>
|
||||
|
||||
|
||||
<div class="flex-container contains-dropdown">
|
||||
<div class="panel-33 p-bottom-20">
|
||||
<h3>Device</h3>
|
||||
@@ -181,14 +181,14 @@
|
||||
label: `${device.name}`
|
||||
}))
|
||||
]
|
||||
}) %>
|
||||
}) %>
|
||||
</div>
|
||||
<div class="panel-33 p-bottom-20">
|
||||
<h3>Channels</h3>
|
||||
<p>Audio channel count.<br>
|
||||
<span class="text-gray">Choose between Mono / Stereo.</span>
|
||||
</p>
|
||||
<%- 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 @@
|
||||
<p>The bitrate of the mp3 audio.<br>
|
||||
<span class="text-gray">Minimum: 64 Kbps • Maximum: 320 Kbps</span>
|
||||
</p>
|
||||
<%- 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 @@
|
||||
</div>
|
||||
<div class="panel-50 p-bottom-20">
|
||||
<h3>Design</h3>
|
||||
<h4>Background image</h4>
|
||||
<%- include('_components', {component: 'text', cssClass: 'br-15', placeholder: 'Direct image link', label: 'Image link', id: 'webserver-bgImage'}) %><br>
|
||||
<h4>Background image</h4>
|
||||
<%- include('_components', {component: 'text', cssClass: 'br-15', placeholder: 'Direct image link', label: 'Image link', id: 'webserver-bgImage'}) %><br>
|
||||
|
||||
<h4 class="top-25">Themes</h4>
|
||||
<%- 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'}) %><br>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex-container flex-phone flex-column bottom-20" style="margin-left: 15px; margin-right: 15px;">
|
||||
<%- 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'}) %><br>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex-container flex-phone flex-column bottom-20" style="margin-left: 15px; margin-right: 15px;">
|
||||
<%- 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'}) %><br>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex-container flex-phone flex-column bottom-20" style="margin-left: 15px; margin-right: 15px;">
|
||||
<%- 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'}) %><br>
|
||||
@@ -337,7 +337,7 @@
|
||||
<div class="panel-50 p-bottom-20" style="padding-left: 20px; padding-right: 20px; padding-bottom: 80px;">
|
||||
<h3>Transmitter Search Algorithm</h3>
|
||||
<p>Different modes may help with more accurate transmitter identification depending on your region.</p>
|
||||
<%- 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 @@
|
||||
<div class="panel-100 p-bottom-20 contains-dropdown" style="z-index: 991;">
|
||||
<h3>Device type</h3>
|
||||
<div class="flex-center" style="max-width: 520px; margin: 10px auto 0;">
|
||||
<%- 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 @@
|
||||
<span>
|
||||
<span class="left-span">Direct</span>
|
||||
<span class="right-span">TCP/IP</span>
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div id="tuner-usb">
|
||||
@@ -390,9 +390,9 @@
|
||||
value: serialPort.path,
|
||||
label: `${serialPort.path} - ${serialPort.friendlyName}`
|
||||
}))
|
||||
}) %>
|
||||
}) %>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="tuner-wireless">
|
||||
<p class="text-gray">If you are connecting your tuner <strong>wirelessly</strong>, enter the tuner IP. <br> If you use <strong>xdrd</strong>, use 127.0.0.1 as your IP.</p>
|
||||
<%- include('_components', {component: 'text', cssClass: 'w-150 br-15', label: 'xdrd IP address', id: 'xdrd-xdrdIp'}) %>
|
||||
@@ -409,7 +409,7 @@
|
||||
<input type="range" id="audio-startupVolume" min="0" max="1" step="0.01" value="1" aria-label="Startup Volume slider">
|
||||
</div>
|
||||
<h4 class="top-10 text-gray" id="volume-percentage-value"></h4>
|
||||
|
||||
|
||||
<hr>
|
||||
<h4 class="bottom-20">Default frequency</h4>
|
||||
<%- include('_components', {component: 'checkbox', cssClass: '', label: 'Default frequency for first client', id: 'enableDefaultFreq'}) %><br>
|
||||
@@ -468,7 +468,7 @@
|
||||
<div class="flex-container">
|
||||
<div class="panel-50 p-bottom-20">
|
||||
<h3>Basic info</h3>
|
||||
|
||||
|
||||
<p>Set your tuner name and description here.<br>This info will be visible to anyone who tunes in. </p>
|
||||
<div class="panel-full no-bg" style="padding-left: 20px; padding-right: 20px;">
|
||||
<label for="identification-tunerName" style="width: 100%;max-width: 768px; margin:auto;">Webserver name:</label>
|
||||
@@ -486,7 +486,7 @@
|
||||
<%- include('_components', {component: 'checkbox', cssClass: '', label: 'Broadcast to map', id: 'identification-broadcastTuner'}) %><br>
|
||||
<%- 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'}) %>
|
||||
|
||||
|
||||
<p>Check your tuner at <strong><a href="https://servers.fmdx.org" target="_blank" class="color-4">servers.fmdx.org</a></strong>.</p>
|
||||
<p class="text-small text-gray">By activating the <strong>Broadcast to map</strong> option,<br>you agree to the <a href="https://fmdx.org/projects/webserver.php#rules" target="_blank">Terms of Service</a>.</p>
|
||||
</div>
|
||||
@@ -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'}) %>
|
||||
|
||||
|
||||
<div id="map"></div>
|
||||
<br>
|
||||
</div>
|
||||
@@ -508,7 +508,7 @@
|
||||
<h2>User management</h2>
|
||||
<div class="panel-100">
|
||||
<h3>Chat options</h3>
|
||||
<%- include('_components', {component: 'checkbox', cssClass: '', label: 'Chat', id: 'webserver-chatEnabled'}) %>
|
||||
<%- include('_components', {component: 'checkbox', cssClass: '', label: 'Chat', id: 'webserver-chatEnabled'}) %>
|
||||
</div>
|
||||
|
||||
<div class="panel-100 p-bottom-20">
|
||||
@@ -573,33 +573,33 @@
|
||||
<p>These settings will be applied after a server launch or restart.</p>
|
||||
<div class="flex-container flex-center p-20">
|
||||
<% 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' },
|
||||
]
|
||||
}) %><br>
|
||||
<%- 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' },
|
||||
]
|
||||
}) %><br>
|
||||
<% } 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' },
|
||||
]
|
||||
}) %><br>
|
||||
<%- 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' },
|
||||
]
|
||||
}) %><br>
|
||||
<% } %>
|
||||
<%- 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 @@
|
||||
}) %><br>
|
||||
</div>
|
||||
<div class="panel-100-real p-bottom-20 no-bg">
|
||||
<%- 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 @@
|
||||
<h3>Empty server defaults</h3>
|
||||
<p>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.</p>
|
||||
<div class="flex-container flex-center p-20">
|
||||
<%- 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' },
|
||||
]
|
||||
}) %><br>
|
||||
<% 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' },
|
||||
]
|
||||
}) %><br>
|
||||
<%- 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 @@
|
||||
]
|
||||
}) %><br>
|
||||
<% } 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' },
|
||||
]
|
||||
}) %><br>
|
||||
<%- 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 @@
|
||||
]
|
||||
}) %><br>
|
||||
<% } %>
|
||||
<%- 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 @@
|
||||
</div>
|
||||
<div class="panel-100-real p-bottom-20 no-bg">
|
||||
<%- include('_components', {component: 'checkbox', cssClass: '', label: 'Delayed Antenna Change', id: 'antennaNoUsersDelay'}) %><br>
|
||||
<%- 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 @@
|
||||
<p>You can also get an tunnel from kuba201 discord, one of the contributors of this version of the application.</p>
|
||||
<h4>Main tunnel settings</h4>
|
||||
<%- include('_components', {component: 'checkbox', cssClass: 'm-right-10', label: 'Enable tunnel', id: 'tunnel-enabled'}) %><br>
|
||||
<%- 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' },
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<h3 class="settings-heading">Tuner type</h3>
|
||||
<p class="m-0">Settings a proper device type ensures that the correct interface and settings will load.</p>
|
||||
<div class="panel-100 no-bg flex-center" style="max-width: 520px; margin: 10px auto 0;">
|
||||
<%- 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 @@
|
||||
<span>
|
||||
<span class="left-span">Direct</span>
|
||||
<span class="right-span">TCP/IP</span>
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div id="tuner-usb" class="top-25">
|
||||
<p>It's time to choose your serial port.</p>
|
||||
|
||||
|
||||
<div class="panel-100 no-bg flex-center">
|
||||
<%- include('_components', {
|
||||
component: 'dropdown',
|
||||
@@ -82,7 +82,7 @@
|
||||
value: serialPort.path,
|
||||
label: `${serialPort.path} - ${serialPort.friendlyName}`
|
||||
}))
|
||||
}) %>
|
||||
}) %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
@@ -103,7 +103,7 @@
|
||||
<p class="m-0">In this section, we will set up the audio.<br>
|
||||
Choose the audio port your tuner is connected to and desired audio settings here.</p>
|
||||
<p class="text-gray">Recommended defaults have already been set for the audio quality, you can keep them as-is.</p>
|
||||
|
||||
|
||||
<div class="panel-100 no-bg p-bottom-20 flex-container flex-center">
|
||||
<%- 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 @@
|
||||
<br>
|
||||
<label for="identification-tunerDesc" style="width: 100%;max-width: 768px; margin: auto;">Webserver description:</label>
|
||||
<textarea id="identification-tunerDesc" name="webserver-desc" class="br-15" placeholder="Fill the server description here. You can put useful info here such as your antenna setup. You can use simple markdown." maxlength="255"></textarea>
|
||||
|
||||
|
||||
<h3 class="settings-heading">Location</h3>
|
||||
<p>Location info is useful for automatic identification of stations using RDS.</p>
|
||||
<div class="panel-100 no-bg flex-container flex-center">
|
||||
|
||||
Reference in New Issue
Block a user