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