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
Add files via upload
This commit is contained in:
104
web/index.html
Normal file
104
web/index.html
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>FM-DX Webserver [Noobish's Server]</title>
|
||||||
|
<link href="/styles.css" type="text/css" rel="stylesheet">
|
||||||
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css" type="text/css" rel="stylesheet">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="wrapper">
|
||||||
|
<canvas id="signal-canvas" width="1024" height="200" style="margin-top: 120px;"></canvas>
|
||||||
|
|
||||||
|
<div class="flex-container">
|
||||||
|
<div class="panel-75" style="height: 110px;">
|
||||||
|
<span class="text-big" id="data-ps"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel-33">
|
||||||
|
<h2 style="padding-top: 18px;">
|
||||||
|
<span id="data-pty" style="color: #eee;"></span>
|
||||||
|
<span style="margin-left: 30px;" id="data-tp">TP</span>
|
||||||
|
<span style="margin-left: 15px; color: #ff5776;" id="data-st"></span>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="flex-container">
|
||||||
|
<div class="panel-33">
|
||||||
|
<h2>PI CODE</h2>
|
||||||
|
<span id="data-pi" class="text-big"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel-33">
|
||||||
|
<h2>FREQUENCY</h2>
|
||||||
|
<span id="data-frequency" class="text-big"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel-33">
|
||||||
|
<h2>SIGNAL</h2>
|
||||||
|
<span class="text-big">
|
||||||
|
<span id="data-signal"></span>
|
||||||
|
<span id="signal-units" class="text-medium-big">dBf</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex-container">
|
||||||
|
<div class="panel-33" style="height: 48px;">
|
||||||
|
<audio id="myAudio" preload="none" autoplay></audio>
|
||||||
|
<input type="range" id="volumeSlider" min="0" max="1" step="0.01" value="1">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel-33" style="opacity: 0;">
|
||||||
|
<button id="playButton">play</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel-33" style="height: 48px;">
|
||||||
|
<label class="toggleSwitch nolabel" onclick="">
|
||||||
|
<input id="signal-units-toggle" type="checkbox"/>
|
||||||
|
<a></a>
|
||||||
|
<span>
|
||||||
|
<span class="left-span">dBf</span>
|
||||||
|
<span class="right-span">dBµV</span>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel-100">
|
||||||
|
<h2 style="margin: 0;">RADIOTEXT</h2>
|
||||||
|
<div id="data-rt0"></div>
|
||||||
|
<div id="data-rt1"></div>
|
||||||
|
<div id="data-container" style="display: none;"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="data-af" style="text-align: center;"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button id="settings"><i class="fa-solid fa-gear"></i></button>
|
||||||
|
|
||||||
|
<div id="myModal" class="modal">
|
||||||
|
<div class="modal-content">
|
||||||
|
<span class="modal-title">Settings</span>
|
||||||
|
<span class="close" id="closeModal"><i class="fa-solid fa-xmark"></i></span>
|
||||||
|
|
||||||
|
<label for="themes" style="margin-top: 50px;"><i class="fa-solid fa-palette"></i> Theme:</label>
|
||||||
|
<select name="themes" style="margin-bottom: 50px;" id="theme-selector">
|
||||||
|
<option value="theme1">Blurple</option>
|
||||||
|
<option value="theme2">Red</option>
|
||||||
|
<option value="theme3">Green</option>
|
||||||
|
<option value="theme4">Cyan</option>
|
||||||
|
<option value="theme5">Orange</option>
|
||||||
|
</select>
|
||||||
|
<p class="text-small" style="margin-bottom: 50px;">FM-DX WebServer uses librds by <a href="https://fmdx.pl" target="_blank">Konrad Kosmatka</a>.</p>
|
||||||
|
<button class="button-close" id="closeModalButton">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="main.js"></script>
|
||||||
|
<script src="themes.js"></script>
|
||||||
|
<script src="modal.js"></script>
|
||||||
|
<script src="stream.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
168
web/main.js
Normal file
168
web/main.js
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
const hostParts = window.location.host.split(':');
|
||||||
|
const hostname = hostParts[0]; // Extract the hostname
|
||||||
|
const port = hostParts[1] || '8080'; // Extract the port or use a default (e.g., 8080)
|
||||||
|
const socketAddress = `ws://${hostname}:${port}/text`; // Use 'wss' for secure WebSocket connections (recommended for external access)
|
||||||
|
const socket = new WebSocket(socketAddress);
|
||||||
|
|
||||||
|
const dataContainer = document.querySelector('#data-container');
|
||||||
|
const canvas = document.querySelector('#signal-canvas');
|
||||||
|
const context = canvas.getContext('2d');
|
||||||
|
|
||||||
|
var signalToggle = document.getElementById("signal-units-toggle");
|
||||||
|
|
||||||
|
canvas.width = canvas.parentElement.clientWidth;
|
||||||
|
|
||||||
|
const data = [];
|
||||||
|
const maxDataPoints = 250;
|
||||||
|
const pointWidth = (canvas.width - 80) / maxDataPoints;
|
||||||
|
|
||||||
|
var europe_programmes = [
|
||||||
|
"No PTY", "News", "Current Affairs", "Info",
|
||||||
|
"Sport", "Education", "Drama", "Culture", "Science", "Varied",
|
||||||
|
"Pop M", "Rock M", "Easy Listening", "Light Classical",
|
||||||
|
"Serious Classical", "Other Music", "Weather", "Finance",
|
||||||
|
"Children's Programmes", "Social Affairs", "Religion", "Phone-in",
|
||||||
|
"Travel", "Leisure", "Jazz Music", "Country Music", "National Music",
|
||||||
|
"Oldies Music", "Folk Music", "Documentary", "Alarm Test"
|
||||||
|
];
|
||||||
|
|
||||||
|
// Function to handle zoom in
|
||||||
|
function zoomIn() {
|
||||||
|
zoomMinValue *= 0.9;
|
||||||
|
zoomMaxValue *= 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to handle zoom out
|
||||||
|
function zoomOut() {
|
||||||
|
zoomMinValue *= 1.1;
|
||||||
|
zoomMaxValue *= 1.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateCanvas() {
|
||||||
|
// Remove old data when it exceeds the maximum data points
|
||||||
|
|
||||||
|
const color2 = getComputedStyle(document.documentElement).getPropertyValue('--color-2').trim();
|
||||||
|
const color4 = getComputedStyle(document.documentElement).getPropertyValue('--color-4').trim();
|
||||||
|
|
||||||
|
while (data.length >= maxDataPoints) {
|
||||||
|
data.shift();
|
||||||
|
}
|
||||||
|
// Modify the WebSocket onmessage callback
|
||||||
|
socket.onmessage = (event) => {
|
||||||
|
const parsedData = JSON.parse(event.data);
|
||||||
|
|
||||||
|
updatePanels(parsedData);
|
||||||
|
// Push the new signal data to the array
|
||||||
|
data.push(parsedData.signal);
|
||||||
|
const actualLowestValue = Math.min(...data);
|
||||||
|
const actualHighestValue = Math.max(...data);
|
||||||
|
zoomMinValue = actualLowestValue - ((actualHighestValue - actualLowestValue) / 2);
|
||||||
|
zoomMaxValue = actualHighestValue + ((actualHighestValue - actualLowestValue) / 2);
|
||||||
|
zoomAvgValue = (zoomMaxValue - zoomMinValue) / 2 + zoomMinValue;
|
||||||
|
|
||||||
|
// Clear the canvas
|
||||||
|
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
// Draw the signal graph with zoom
|
||||||
|
context.beginPath();
|
||||||
|
context.moveTo(50, canvas.height - (data[0] - zoomMinValue) * (canvas.height / (zoomMaxValue - zoomMinValue)));
|
||||||
|
|
||||||
|
for (let i = 1; i < data.length; i++) {
|
||||||
|
const x = i * pointWidth;
|
||||||
|
const y = canvas.height - (data[i] - zoomMinValue) * (canvas.height / (zoomMaxValue - zoomMinValue));
|
||||||
|
context.lineTo(x + 40, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.strokeStyle = color4;
|
||||||
|
context.lineWidth = 1;
|
||||||
|
context.stroke();
|
||||||
|
|
||||||
|
// Draw horizontal lines for lowest, highest, and average values
|
||||||
|
context.strokeStyle = color2; // Set line color
|
||||||
|
context.lineWidth = 1;
|
||||||
|
|
||||||
|
// Draw the lowest value line
|
||||||
|
const lowestY = canvas.height - (zoomMinValue - zoomMinValue) * (canvas.height / (zoomMaxValue - zoomMinValue));
|
||||||
|
context.beginPath();
|
||||||
|
context.moveTo(40, lowestY - 18);
|
||||||
|
context.lineTo(canvas.width - 40, lowestY - 18);
|
||||||
|
context.stroke();
|
||||||
|
|
||||||
|
// Draw the highest value line
|
||||||
|
const highestY = canvas.height - (zoomMaxValue - zoomMinValue) * (canvas.height / (zoomMaxValue - zoomMinValue));
|
||||||
|
context.beginPath();
|
||||||
|
context.moveTo(40, highestY + 10);
|
||||||
|
context.lineTo(canvas.width - 40, highestY + 10);
|
||||||
|
context.stroke();
|
||||||
|
|
||||||
|
const avgY = canvas.height / 2;
|
||||||
|
context.beginPath();
|
||||||
|
context.moveTo(40, avgY - 7);
|
||||||
|
context.lineTo(canvas.width - 40, avgY - 7);
|
||||||
|
context.stroke();
|
||||||
|
|
||||||
|
// Label the lines with their values
|
||||||
|
context.fillStyle = color4;
|
||||||
|
context.font = '12px Titillium Web';
|
||||||
|
|
||||||
|
const offset = signalToggle.checked ? 11.75 : 0;
|
||||||
|
context.textAlign = 'right';
|
||||||
|
context.fillText(`${(zoomMinValue - offset).toFixed(1)}`, 35, lowestY - 14);
|
||||||
|
context.fillText(`${(zoomMaxValue - offset).toFixed(1)}`, 35, highestY + 14);
|
||||||
|
context.fillText(`${(zoomAvgValue - offset).toFixed(1)}`, 35, avgY - 3);
|
||||||
|
|
||||||
|
context.textAlign = 'left';
|
||||||
|
context.fillText(`${(zoomMinValue - offset).toFixed(1)}`, canvas.width - 35, lowestY - 14);
|
||||||
|
context.fillText(`${(zoomMaxValue - offset).toFixed(1)}`, canvas.width - 35, highestY + 14);
|
||||||
|
context.fillText(`${(zoomAvgValue - offset).toFixed(1)}`, canvas.width - 35, avgY - 3);
|
||||||
|
|
||||||
|
// Update the data container with the latest data
|
||||||
|
dataContainer.innerHTML = event.data + '<br>';
|
||||||
|
};
|
||||||
|
|
||||||
|
requestAnimationFrame(updateCanvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start updating the canvas
|
||||||
|
updateCanvas();
|
||||||
|
|
||||||
|
function compareNumbers(a, b) {
|
||||||
|
return a - b;
|
||||||
|
}
|
||||||
|
|
||||||
|
function divideByHundred(a) {
|
||||||
|
a = a / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePanels(parsedData) {
|
||||||
|
sortedAf = parsedData.af.sort(compareNumbers);
|
||||||
|
|
||||||
|
sortedAf.forEach((element, index, array) => {
|
||||||
|
array[index] = element / 1000;
|
||||||
|
|
||||||
|
// Check if it's the last element in the array
|
||||||
|
if (index === array.length - 1) {
|
||||||
|
document.querySelector('#data-af').innerHTML = array;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelector('#data-frequency').textContent = parsedData.freq;
|
||||||
|
document.querySelector('#data-pi').innerHTML = parsedData.pi === '?' ? "<span class='text-gray'>?</span>" : parsedData.pi;
|
||||||
|
document.querySelector('#data-ps').innerHTML = parsedData.ps === '?' ? "<span class='text-gray'>?</span>" : parsedData.ps;
|
||||||
|
document.querySelector('#data-tp').innerHTML = parsedData.tp === false ? "<span class='text-gray'>TP</span>" : "TP";
|
||||||
|
document.querySelector('#data-pty').innerHTML = europe_programmes[parsedData.pty];
|
||||||
|
document.querySelector('#data-st').innerHTML = parsedData.st === false ? "<span class='text-gray'>ST</span>" : "ST";
|
||||||
|
document.querySelector('#data-rt0').innerHTML = parsedData.rt0;
|
||||||
|
document.querySelector('#data-rt1').innerHTML = parsedData.rt1;
|
||||||
|
document.querySelector('#data-signal').textContent = signalToggle.checked ? (parsedData.signal - 11.75).toFixed(1) : parsedData.signal;
|
||||||
|
}
|
||||||
|
|
||||||
|
signalToggle.addEventListener("change", function() {
|
||||||
|
signalText = document.querySelector('#signal-units');
|
||||||
|
if (signalToggle.checked) {
|
||||||
|
signalText.textContent = 'dBµV';
|
||||||
|
} else {
|
||||||
|
// Checkbox is unchecked
|
||||||
|
signalText.textContent = 'dBf';
|
||||||
|
}
|
||||||
|
});
|
||||||
33
web/modal.js
Normal file
33
web/modal.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// Get the modal element and the buttons to open and close it
|
||||||
|
var modal = document.getElementById("myModal");
|
||||||
|
var openBtn = document.getElementById("settings");
|
||||||
|
var closeBtn = document.getElementById("closeModal");
|
||||||
|
var closeBtnFull = document.getElementById("closeModalButton");
|
||||||
|
|
||||||
|
// Function to open the modal
|
||||||
|
function openModal() {
|
||||||
|
modal.style.display = "block";
|
||||||
|
setTimeout(function() {
|
||||||
|
modal.style.opacity = 1;
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to close the modal
|
||||||
|
function closeModal() {
|
||||||
|
modal.style.opacity = 0;
|
||||||
|
setTimeout(function() {
|
||||||
|
modal.style.display = "none";
|
||||||
|
}, 300); // This delay should match the transition duration (0.3s).
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event listeners for the open and close buttons
|
||||||
|
openBtn.addEventListener("click", openModal);
|
||||||
|
closeBtn.addEventListener("click", closeModal);
|
||||||
|
closeBtnFull.addEventListener("click", closeModal);
|
||||||
|
|
||||||
|
// Close the modal when clicking outside of it
|
||||||
|
window.addEventListener("click", function(event) {
|
||||||
|
if (event.target == modal) {
|
||||||
|
closeModal();
|
||||||
|
}
|
||||||
|
});
|
||||||
20
web/stream.js
Normal file
20
web/stream.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
const audioElement = document.getElementById("myAudio");
|
||||||
|
const volumeSlider = document.getElementById("volumeSlider");
|
||||||
|
const audioStream = "/audio-proxy";
|
||||||
|
const uniqueTimestamp = Date.now(); // Create a unique timestamp
|
||||||
|
|
||||||
|
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||||||
|
const audioSource = audioContext.createMediaElementSource(audioElement);
|
||||||
|
|
||||||
|
audioSource.connect(audioContext.destination);
|
||||||
|
|
||||||
|
// Set the audio element's source to your external audio stream
|
||||||
|
audioElement.src = `${audioStream}?${uniqueTimestamp}`;
|
||||||
|
|
||||||
|
|
||||||
|
audioElement.play();
|
||||||
|
|
||||||
|
volumeSlider.addEventListener("input", (event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
audioElement.volume = volumeSlider.value;
|
||||||
|
});
|
||||||
443
web/styles.css
Normal file
443
web/styles.css
Normal file
@@ -0,0 +1,443 @@
|
|||||||
|
@import url('https://fonts.googleapis.com/css2?family=Titillium+Web:ital,wght@0,200;0,300;0,400;0,600;0,700;0,900;1,200;1,300;1,400;1,600;1,700&display=swap');
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--color-main: #1d1838;
|
||||||
|
--color-main-bright: #8069fa;
|
||||||
|
|
||||||
|
--color-1: color-mix(in srgb, var(--color-main) 95%, var(--color-main-bright));
|
||||||
|
--color-2: color-mix(in srgb, var(--color-main) 75%, var(--color-main-bright));
|
||||||
|
--color-3: color-mix(in srgb, var(--color-main) 50%, var(--color-main-bright));
|
||||||
|
--color-4: color-mix(in srgb, var(--color-main) 20%, var(--color-main-bright));
|
||||||
|
--color-5: color-mix(in srgb, var(--color-main) 0%, var(--color-main-bright));
|
||||||
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
background: var(--color-main-bright);
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Titillium Web', sans-serif;
|
||||||
|
color: white;
|
||||||
|
background: var(--color-main);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#data-pi {
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wrapper {
|
||||||
|
margin: auto;
|
||||||
|
width: auto;
|
||||||
|
max-width: 1024px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#color-settings, #settings {
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
color: white;
|
||||||
|
position: absolute;
|
||||||
|
top: 15px;
|
||||||
|
right: 15px;
|
||||||
|
font-size: 16px;
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
line-height: 64px;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: 500ms ease-in-out background;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#color-settings {
|
||||||
|
top: 96px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#settings:hover, #color-settings:hover {
|
||||||
|
background: var(--color-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
color: var(--color-4);
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-container {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-33 {
|
||||||
|
width: 33%;
|
||||||
|
background: var(--color-1);
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
border-radius: 30px;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-75 {
|
||||||
|
width: 68%;
|
||||||
|
background: var(--color-1);
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
border-radius: 30px;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.panel-100 {
|
||||||
|
width: 98%;
|
||||||
|
background: var(--color-1);
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
min-height: 100px;
|
||||||
|
border-radius: 30px;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auto {
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-big {
|
||||||
|
font-size: 72px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-medium-big {
|
||||||
|
font-size: 24px;
|
||||||
|
color: #aaa;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-small {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-gray {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-dark {
|
||||||
|
background: #100d1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@media only screen and (max-width: 768px) {
|
||||||
|
.flex-container {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.modal-content {
|
||||||
|
max-width: 90%;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
.panel-33 {
|
||||||
|
width: 90%;
|
||||||
|
margin: auto;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.panel-75 {
|
||||||
|
margin: 80px auto 0 auto !important;
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
.panel-33 h2 {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
.text-big {
|
||||||
|
font-size: 54px;
|
||||||
|
display: block;
|
||||||
|
margin-top: -50px;
|
||||||
|
}
|
||||||
|
.panel-100 {
|
||||||
|
width: 90%;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="range"] {
|
||||||
|
margin: 0;
|
||||||
|
/* removing default appearance */
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
/* creating a custom design */
|
||||||
|
width: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
/* slider progress trick */
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 30px;
|
||||||
|
height: 100%;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Track: Mozilla Firefox */
|
||||||
|
input[type="range"]::-moz-range-track {
|
||||||
|
height: 48px;
|
||||||
|
background: var(--color-1);
|
||||||
|
border-radius: 30px;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Thumb: webkit */
|
||||||
|
input[type="range"]::-webkit-slider-thumb {
|
||||||
|
/* removing default appearance */
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
/* creating a custom design */
|
||||||
|
height: 48px;
|
||||||
|
width: 48px;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 2px solid var(--color-4);
|
||||||
|
/* slider progress trick */
|
||||||
|
box-shadow: -407px 0 0 400px var(--color-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Thumb: Firefox */
|
||||||
|
input[type="range"]::-moz-range-thumb {
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 48px;
|
||||||
|
width: 48px;
|
||||||
|
background-color: var(--color-4);
|
||||||
|
border-radius: 0px 30px 30px 0px;
|
||||||
|
border: 0;
|
||||||
|
outline: none;
|
||||||
|
/* slider progress trick */
|
||||||
|
box-shadow: -420px 0 0 400px var(--color-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Toggle Switch */
|
||||||
|
|
||||||
|
.toggleSwitch span span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggleSwitch {
|
||||||
|
user-select: none;
|
||||||
|
display: inline-block;
|
||||||
|
height: 48px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 25px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.toggleSwitch * {
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggleSwitch input:focus ~ a,
|
||||||
|
.toggleSwitch input:focus + label {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.toggleSwitch label {
|
||||||
|
position: relative;
|
||||||
|
z-index: 3;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.toggleSwitch input {
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
.toggleSwitch > span {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
width: calc(100% - 6px);
|
||||||
|
margin: 0;
|
||||||
|
text-align: left;
|
||||||
|
white-space: nowrap;
|
||||||
|
margin:0;
|
||||||
|
}
|
||||||
|
.toggleSwitch > span span {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 5;
|
||||||
|
display: block;
|
||||||
|
width: 50%;
|
||||||
|
margin-left: 50px;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 0.9em;
|
||||||
|
width: auto;
|
||||||
|
opacity: 1;
|
||||||
|
width: 40%;
|
||||||
|
text-align: center;
|
||||||
|
line-height:48px;
|
||||||
|
}
|
||||||
|
.toggleSwitch a {
|
||||||
|
position: absolute;
|
||||||
|
right: 50%;
|
||||||
|
z-index: 4;
|
||||||
|
display: block;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
padding: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 50%;
|
||||||
|
background-color: var(--color-4);
|
||||||
|
border-radius: 25px;
|
||||||
|
-webkit-transition: all 0.2s ease-out;
|
||||||
|
-moz-transition: all 0.2s ease-out;
|
||||||
|
transition: all 0.2s ease-out;
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
.toggleSwitch > span span:first-of-type {
|
||||||
|
color: var(--color-1);
|
||||||
|
opacity: 1;
|
||||||
|
left: 0;
|
||||||
|
margin: 0;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
.toggleSwitch > span span:last-of-type {
|
||||||
|
left:auto;
|
||||||
|
right:0;
|
||||||
|
color: var(--color-4);
|
||||||
|
margin: 0;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
.toggleSwitch > span:before {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: -2px;
|
||||||
|
border-radius: 30px;
|
||||||
|
-webkit-transition: all 0.2s ease-out;
|
||||||
|
-moz-transition: all 0.2s ease-out;
|
||||||
|
transition: all 0.2s ease-out;
|
||||||
|
}
|
||||||
|
.toggleSwitch input:checked ~ a {
|
||||||
|
left: calc(50% - 3px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggleSwitch input:checked ~ span span:first-of-type {
|
||||||
|
left:0;
|
||||||
|
color: var(--color-4);
|
||||||
|
}
|
||||||
|
.toggleSwitch input:checked ~ span span:last-of-type {
|
||||||
|
color: var(--color-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* End Toggle Switch */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Style for the modal container */
|
||||||
|
.modal {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.6); /* Semi-transparent background */
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease-in-out; /* Fade-in/out transition */
|
||||||
|
z-index: 20; /* Ensure the modal is above other content */
|
||||||
|
color: var(--color-4);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style for the modal content */
|
||||||
|
.modal-content {
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
background-color: var(--color-main);
|
||||||
|
padding: 30px;
|
||||||
|
border-radius: 30px;
|
||||||
|
opacity: 1;
|
||||||
|
transition: opacity 0.3s ease-in-out; /* Fade-in/out transition */
|
||||||
|
z-index: 21; /* Ensure the modal content is above the modal background */
|
||||||
|
min-width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
font-size: 20px;
|
||||||
|
position: absolute;
|
||||||
|
font-weight: 300;
|
||||||
|
top: 14px;
|
||||||
|
left: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style for the close button */
|
||||||
|
.close {
|
||||||
|
position: absolute;
|
||||||
|
top: 17px;
|
||||||
|
right: 30px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: 0.3s ease-in-out color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close:hover {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content .button-close {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 25px;
|
||||||
|
right: 35px;
|
||||||
|
width: 100px;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 30px;
|
||||||
|
background: var(--color-4);
|
||||||
|
font-weight: bold;
|
||||||
|
border: 0;
|
||||||
|
transition: 0.35s ease-in-out background;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content .button-close:hover {
|
||||||
|
background: var(--color-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal label {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.themes-list select {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#theme-selector {
|
||||||
|
height: 42px;
|
||||||
|
width: 150px;
|
||||||
|
padding: 10px;
|
||||||
|
background: var(--color-4);
|
||||||
|
color: var(--color-1);
|
||||||
|
border: 0;
|
||||||
|
border-bottom: 4px solid var(--color-2);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: 0.35s ease-in-out background;
|
||||||
|
}
|
||||||
|
|
||||||
|
#theme-selector:hover {
|
||||||
|
background: var(--color-5);
|
||||||
|
}
|
||||||
49
web/themes.js
Normal file
49
web/themes.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
const themes = {
|
||||||
|
theme1: {
|
||||||
|
'--color-main': '#1d1838',
|
||||||
|
'--color-main-bright': '#8069fa',
|
||||||
|
},
|
||||||
|
theme2: {
|
||||||
|
'--color-main': '#381818',
|
||||||
|
'--color-main-bright': '#ff7070',
|
||||||
|
},
|
||||||
|
theme3: {
|
||||||
|
'--color-main': '#121c0c',
|
||||||
|
'--color-main-bright': '#a9ff70',
|
||||||
|
},
|
||||||
|
theme4: {
|
||||||
|
'--color-main': '#0c1c1b',
|
||||||
|
'--color-main-bright': '#68f7ee',
|
||||||
|
},
|
||||||
|
theme5: {
|
||||||
|
'--color-main': '#171106',
|
||||||
|
'--color-main-bright': '#f5b642',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function setTheme(themeName) {
|
||||||
|
const theme = themes[themeName];
|
||||||
|
if (theme) {
|
||||||
|
for (const [variable, value] of Object.entries(theme)) {
|
||||||
|
document.documentElement.style.setProperty(variable, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the dropdown element
|
||||||
|
const themeSelector = document.getElementById('theme-selector');
|
||||||
|
|
||||||
|
const savedTheme = localStorage.getItem("theme");
|
||||||
|
if(savedTheme) {
|
||||||
|
setTheme(savedTheme);
|
||||||
|
themeSelector.value = savedTheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for changes in the dropdown
|
||||||
|
themeSelector.addEventListener('change', (event) => {
|
||||||
|
const selectedTheme = event.target.value;
|
||||||
|
setTheme(selectedTheme);
|
||||||
|
localStorage.setItem("theme", selectedTheme);
|
||||||
|
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user