New Features
* Implements Terminal
This commit is contained in:
210
hg.css
210
hg.css
@@ -325,6 +325,10 @@ canvas {
|
||||
color: #ffff;
|
||||
right: 0;
|
||||
margin-right: 5px;
|
||||
padding: 5px;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
}
|
||||
|
||||
.ERRORS {
|
||||
@@ -344,3 +348,209 @@ canvas {
|
||||
border: 2px solid red;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* Terminal Overlay Styles - ORED Initial Console Emulator Style */
|
||||
.terminal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(192, 192, 192, 0.3);
|
||||
backdrop-filter: none;
|
||||
z-index: 10001;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.terminal-window {
|
||||
width: 80%;
|
||||
max-width: 600px;
|
||||
height: 65%;
|
||||
max-height: 450px;
|
||||
min-height: 300px;
|
||||
background: #c0c0c0;
|
||||
border: 4px outset #c0c0c0;
|
||||
border-radius: 0;
|
||||
font-family: 'VCR', 'Monaco', 'Courier New', monospace;
|
||||
box-shadow: 3px 3px 8px rgba(0, 0, 0, 0.6);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.terminal-header {
|
||||
background: #c0c0c0;
|
||||
color: #000000;
|
||||
padding: 1px 0;
|
||||
border-bottom: 2px inset #c0c0c0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
font-family: 'VCR', 'Geneva', 'Helvetica', sans-serif;
|
||||
min-height: 16px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.terminal-title {
|
||||
color: #000000;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.terminal-close {
|
||||
cursor: pointer;
|
||||
color: #000000;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
width: 16px;
|
||||
height: 14px;
|
||||
border: 1px outset #c0c0c0;
|
||||
background: #c0c0c0;
|
||||
text-align: center;
|
||||
line-height: 12px;
|
||||
border-radius: 0;
|
||||
position: absolute;
|
||||
right: 2px;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
.terminal-close:hover {
|
||||
background: #b0b0b0;
|
||||
}
|
||||
|
||||
.terminal-close:active {
|
||||
border: 1px inset #c0c0c0;
|
||||
background: #a0a0a0;
|
||||
}
|
||||
|
||||
.terminal-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 2px;
|
||||
overflow: hidden;
|
||||
background: #c0c0c0;
|
||||
}
|
||||
|
||||
#terminal-output {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
margin-bottom: 4px;
|
||||
color: #000000;
|
||||
font-size: 9px;
|
||||
line-height: 1.1;
|
||||
font-family: 'VCR', 'Monaco', 'Courier New', monospace;
|
||||
background: #c0c0c0;
|
||||
border: 2px inset #c0c0c0;
|
||||
padding: 2px 4px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.terminal-line {
|
||||
margin-bottom: 0;
|
||||
word-wrap: break-word;
|
||||
color: #000000;
|
||||
font-size: 9px;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.terminal-input-line {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #000000;
|
||||
font-size: 9px;
|
||||
background: #c0c0c0;
|
||||
border: 2px inset #c0c0c0;
|
||||
padding: 1px 4px;
|
||||
min-height: 14px;
|
||||
}
|
||||
|
||||
.terminal-prompt {
|
||||
color: #000000;
|
||||
margin-right: 2px;
|
||||
font-weight: normal;
|
||||
font-family: 'VCR', 'Monaco', 'Courier New', monospace;
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
.terminal-input {
|
||||
flex: 1;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: #000000;
|
||||
font-family: 'VCR', 'Monaco', 'Courier New', monospace;
|
||||
font-size: 9px;
|
||||
outline: none;
|
||||
caret-color: #000000;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.terminal-input::selection {
|
||||
background: #000080;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.terminal-btn {
|
||||
float: right;
|
||||
margin-right: 10px;
|
||||
font-size: 16px;
|
||||
color: #565656;
|
||||
}
|
||||
|
||||
.terminal-btn:hover {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.terminal-overlay.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Custom scrollbar for terminal output - Classic style */
|
||||
#terminal-output::-webkit-scrollbar {
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
#terminal-output::-webkit-scrollbar-track {
|
||||
background: #c0c0c0;
|
||||
border: 1px inset #c0c0c0;
|
||||
}
|
||||
|
||||
#terminal-output::-webkit-scrollbar-thumb {
|
||||
background: linear-gradient(to bottom, #e0e0e0 0%, #c0c0c0 50%, #a0a0a0 100%);
|
||||
border: 1px outset #c0c0c0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
#terminal-output::-webkit-scrollbar-thumb:hover {
|
||||
background: linear-gradient(to bottom, #d0d0d0 0%, #b0b0b0 50%, #909090 100%);
|
||||
}
|
||||
|
||||
#terminal-output::-webkit-scrollbar-thumb:active {
|
||||
border: 1px inset #c0c0c0;
|
||||
background: linear-gradient(to bottom, #a0a0a0 0%, #b0b0b0 50%, #d0d0d0 100%);
|
||||
}
|
||||
|
||||
#terminal-output::-webkit-scrollbar-corner {
|
||||
background: #c0c0c0;
|
||||
}
|
||||
|
||||
#terminal-output::-webkit-scrollbar-button {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: linear-gradient(to bottom, #e0e0e0 0%, #c0c0c0 50%, #a0a0a0 100%);
|
||||
border: 1px outset #c0c0c0;
|
||||
}
|
||||
|
||||
#terminal-output::-webkit-scrollbar-button:hover {
|
||||
background: linear-gradient(to bottom, #d0d0d0 0%, #b0b0b0 50%, #909090 100%);
|
||||
}
|
||||
|
||||
#terminal-output::-webkit-scrollbar-button:active {
|
||||
border: 1px inset #c0c0c0;
|
||||
background: linear-gradient(to bottom, #a0a0a0 0%, #b0b0b0 50%, #d0d0d0 100%);
|
||||
}
|
||||
|
||||
70
index.html
70
index.html
@@ -16,29 +16,32 @@
|
||||
<div class="modal" id="intro-modal">
|
||||
<div class="modal-bg modal-exit"></div>
|
||||
<div class="modal-container">
|
||||
<h3 class="text-center">Welcome to Retrowave Player</h3>
|
||||
<p class="mt-5">Here are the controls:</p>
|
||||
<h3 class="text-center">WELCOME TO RETROWAVE PLAYER</h3>
|
||||
<p class="mt-5">HERE ARE THE CONTROLS:</p>
|
||||
<h4 class="mt-5">
|
||||
Play/Pause <span class="controls">SPACE or Click or Touch</span>
|
||||
PLAY/PAUSE <span class="controls">SPACE OR CLICK OR TOUCH</span>
|
||||
</h4>
|
||||
<h4 class="mt-5">
|
||||
Volume Up <span class="controls">w or Swipe Up</span>
|
||||
VOLUME UP <span class="controls">W OR SWIPE UP</span>
|
||||
</h4>
|
||||
<h4 class="mt-5">
|
||||
Volume Down <span class="controls">s or Swipe Down</span>
|
||||
VOLUME DOWN <span class="controls">S OR SWIPE DOWN</span>
|
||||
</h4>
|
||||
<h4 class="mt-5">
|
||||
Next Track<span class="controls"> n or Press the Refresh Button</span>
|
||||
NEXT TRACK<span class="controls"> N OR PRESS THE REFRESH BUTTON</span>
|
||||
</h4>
|
||||
<h4 class="mt-5">
|
||||
Toggle Controls<span class="controls"> h</span>
|
||||
TOGGLE CONTROLS<span class="controls"> H</span>
|
||||
</h4>
|
||||
<h4 class="mt-5">
|
||||
OPEN TERMINAL<span class="controls"> T</span>
|
||||
</h4>
|
||||
<p class="mt-5">
|
||||
(You can download the music history of your current browser by
|
||||
clicking the history link on the player)
|
||||
(YOU CAN DOWNLOAD THE MUSIC HISTORY OF YOUR CURRENT BROWSER BY
|
||||
CLICKING THE HISTORY LINK ON THE PLAYER)
|
||||
</p>
|
||||
<p class="mt-5">All the music is fetched from retrowave.ru</p>
|
||||
<div id="warning">⚠️ WARNING: This application contains strobing/flashing lights</div>
|
||||
<p class="mt-5">ALL THE MUSIC IS FETCHED FROM RETROWAVE.RU</p>
|
||||
<div id="warning">⚠️ WARNING: THIS APPLICATION CONTAINS STROBING/FLASHING LIGHTS</div>
|
||||
<button class="modal-close modal-exit" id="initButton">X</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -78,22 +81,23 @@
|
||||
</svg>
|
||||
</div>
|
||||
<div class="no-pause buttons refresh toggleable" title="NEXT TRACK">🔃</div>
|
||||
<div class="no-pause buttons terminal-btn toggleable" title="OPEN TERMINAL">⌨️</div>
|
||||
<div class="title-content">
|
||||
<div class="intro">Now Playing</div>
|
||||
<div class="intro">NOW PLAYING</div>
|
||||
<hr />
|
||||
<h3 id="track-name">Retrowave Player</h3>
|
||||
<h3 id="track-name">RETROWAVE PLAYER</h3>
|
||||
</div>
|
||||
<div class="gradient-overlay"></div>
|
||||
<div class="color-overlay"></div>
|
||||
<div class="footer toggleable">
|
||||
<div title="DOWNLOAD YOUR PLAYLIST HISTORY" id="history" class="no-pause footer-items">history</div>
|
||||
<div title="DOWNLOAD YOUR PLAYLIST HISTORY" id="history" class="no-pause footer-items">HISTORY</div>
|
||||
<div id="source">
|
||||
<a
|
||||
title="SOURCE CODE"
|
||||
class="no-pause footer-items"
|
||||
href="https://git.indrajith.dev/retrowave-player/"
|
||||
target="_blank"
|
||||
>source code</a
|
||||
>SOURCE CODE</a
|
||||
>
|
||||
</div>
|
||||
<div id="retrowaveru" class="no-pause">
|
||||
@@ -102,7 +106,7 @@
|
||||
class="no-pause footer-items"
|
||||
href="http://retrowave.ru/"
|
||||
target="_blank"
|
||||
>retrowave.ru</a
|
||||
>RETROWAVE.RU</a
|
||||
>
|
||||
</div>
|
||||
<div id="guestbook" class="no-pause">
|
||||
@@ -111,13 +115,45 @@
|
||||
class="no-pause footer-items"
|
||||
href="https://indrajith.atabook.org/"
|
||||
target="_blank"
|
||||
>guestbook</a
|
||||
>GUESTBOOK</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div id="upload-info" class="no-pause" title="Upload a playlist (downloaded from history)">Upload</div> -->
|
||||
</div>
|
||||
</div>
|
||||
<div id="terminal-overlay" class="terminal-overlay hidden">
|
||||
<div class="terminal-window">
|
||||
<div class="terminal-header">
|
||||
<span class="terminal-title">Retrowave Terminal</span>
|
||||
<span class="terminal-close">□</span>
|
||||
</div>
|
||||
<div class="terminal-content">
|
||||
<div id="terminal-output">
|
||||
<div class="terminal-line">Welcome to Retrowave Terminal</div>
|
||||
<div class="terminal-line">Type 'help' for available commands</div>
|
||||
<div class="terminal-line"></div>
|
||||
<div class="terminal-line">Available commands:</div>
|
||||
<div class="terminal-line"> help - Show this help message</div>
|
||||
<div class="terminal-line"> play - Start/resume playback</div>
|
||||
<div class="terminal-line"> pause - Pause playback</div>
|
||||
<div class="terminal-line"> next - Skip to next track</div>
|
||||
<div class="terminal-line"> volume [0-10] - Set volume (0-10)</div>
|
||||
<div class="terminal-line"> status - Show current track info</div>
|
||||
<div class="terminal-line"> history - Download playlist history</div>
|
||||
<div class="terminal-line"> effect - Change visual effect</div>
|
||||
<div class="terminal-line"> fullscreen - Toggle fullscreen mode</div>
|
||||
<div class="terminal-line"> clear - Clear terminal</div>
|
||||
<div class="terminal-line"> exit - Close terminal</div>
|
||||
<div class="terminal-line"></div>
|
||||
</div>
|
||||
<div class="terminal-input-line">
|
||||
<span class="terminal-prompt">indrajith@retrowave:$ </span>
|
||||
<input type="text" id="terminal-input" class="terminal-input" autocomplete="off" spellcheck="false">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="codef-canvas"></div>
|
||||
<div class="OVR hidden"></div>
|
||||
<div class="ERRORS hidden"></div>
|
||||
|
||||
217
player.js
217
player.js
@@ -9,6 +9,11 @@
|
||||
let refreshBtn = document.querySelector(".refresh");
|
||||
let ovr = document.querySelector(".OVR");
|
||||
let fullScreenBtn = document.querySelector(".fullscreen");
|
||||
let terminalBtn = document.querySelector(".terminal-btn");
|
||||
let terminalOverlay = document.getElementById("terminal-overlay");
|
||||
let terminalInput = document.getElementById("terminal-input");
|
||||
let terminalOutput = document.getElementById("terminal-output");
|
||||
let terminalClose = document.querySelector(".terminal-close");
|
||||
let errorEl = document.querySelector(".ERRORS");
|
||||
let currentEffectIndex = 0;
|
||||
const modalEl = document.getElementById("intro-modal");
|
||||
@@ -113,15 +118,15 @@
|
||||
});
|
||||
|
||||
document.addEventListener("keyup", (event) => {
|
||||
if (!terminalOverlay.classList.contains("hidden")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { key } = event;
|
||||
switch (key) {
|
||||
case " ":
|
||||
togglePlay();
|
||||
break;
|
||||
case "a":
|
||||
break;
|
||||
case "d":
|
||||
break;
|
||||
case "w":
|
||||
volumeUp();
|
||||
break;
|
||||
@@ -143,6 +148,9 @@
|
||||
case "e":
|
||||
rotateHydraEffect();
|
||||
break;
|
||||
case "t":
|
||||
toggleTerminal();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -255,10 +263,11 @@
|
||||
}
|
||||
|
||||
function updateInfo(trackDetails) {
|
||||
titleEl.innerText = trackDetails.title;
|
||||
document.title = `Now Playing... ${trackDetails.title}`;
|
||||
const trackTitle = trackDetails.title.toString().toUpperCase() || "Unknown Track";
|
||||
titleEl.innerText = trackTitle;
|
||||
document.title = `Now Playing... ${trackTitle}`;
|
||||
coverArtEl.style.backgroundImage = `url("${retroWaveRu}${trackDetails.artworkUrl}")`;
|
||||
ovr.innerHTML = `${trackDetails.title}`;
|
||||
ovr.innerHTML = `> ${trackTitle}`;
|
||||
}
|
||||
|
||||
function getHistory() {
|
||||
@@ -324,6 +333,27 @@ https://retrowave.ru/${musicData.streamUrl}
|
||||
fullScreenBtn.addEventListener("click", () => {
|
||||
toggleFullScreen();
|
||||
});
|
||||
|
||||
terminalBtn.addEventListener("click", () => {
|
||||
toggleTerminal();
|
||||
});
|
||||
|
||||
terminalClose.addEventListener("click", () => {
|
||||
closeTerminal();
|
||||
});
|
||||
|
||||
terminalInput.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Enter") {
|
||||
executeCommand(terminalInput.value.trim());
|
||||
terminalInput.value = "";
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Escape" && !terminalOverlay.classList.contains("hidden")) {
|
||||
closeTerminal();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function toggleFullScreen() {
|
||||
@@ -469,4 +499,177 @@ https://retrowave.ru/${musicData.streamUrl}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleTerminal() {
|
||||
terminalOverlay.classList.toggle("hidden");
|
||||
if (!terminalOverlay.classList.contains("hidden")) {
|
||||
terminalInput.focus();
|
||||
}
|
||||
}
|
||||
|
||||
function closeTerminal() {
|
||||
terminalOverlay.classList.add("hidden");
|
||||
}
|
||||
|
||||
function addTerminalLine(text, isCommand = false) {
|
||||
const line = document.createElement("div");
|
||||
line.className = "terminal-line";
|
||||
if (isCommand) {
|
||||
line.innerHTML = `<span style="color: #000000; font-weight: normal;">indrajith@retrowave:$ ${text}</span>`;
|
||||
} else {
|
||||
line.textContent = text;
|
||||
}
|
||||
terminalOutput.appendChild(line);
|
||||
terminalOutput.scrollTop = terminalOutput.scrollHeight;
|
||||
}
|
||||
|
||||
function executeCommand(command) {
|
||||
if (!command) return;
|
||||
|
||||
addTerminalLine(command, true);
|
||||
|
||||
const args = command.toLowerCase().split(' ');
|
||||
const cmd = args[0];
|
||||
|
||||
switch (cmd) {
|
||||
case 'help':
|
||||
addTerminalLine('Available commands:');
|
||||
addTerminalLine(' help - Show this help message');
|
||||
addTerminalLine(' play - Start/resume playback');
|
||||
addTerminalLine(' pause - Pause playback');
|
||||
addTerminalLine(' next - Skip to next track');
|
||||
addTerminalLine(' volume [0-10] - Set volume (0-10)');
|
||||
addTerminalLine(' status - Show current track info');
|
||||
addTerminalLine(' history - Download playlist history');
|
||||
addTerminalLine(' effect [list|name] - Change/list visual effects');
|
||||
addTerminalLine(' fullscreen - Toggle fullscreen mode');
|
||||
addTerminalLine(' clear - Clear terminal');
|
||||
addTerminalLine(' exit - Close terminal');
|
||||
break;
|
||||
|
||||
case 'play':
|
||||
if (howlerInstance && !isPlaying) {
|
||||
togglePlay();
|
||||
addTerminalLine('Playback resumed.');
|
||||
} else if (isPlaying) {
|
||||
addTerminalLine('Already playing.');
|
||||
} else {
|
||||
addTerminalLine('No track loaded.');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'pause':
|
||||
if (howlerInstance && isPlaying) {
|
||||
togglePlay();
|
||||
addTerminalLine('Playback paused.');
|
||||
} else {
|
||||
addTerminalLine('Not currently playing.');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'next':
|
||||
if (howlerInstance) {
|
||||
playNextTrack();
|
||||
addTerminalLine('Skipped to next track.');
|
||||
} else {
|
||||
addTerminalLine('No track loaded.');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'volume':
|
||||
if (args[1]) {
|
||||
const vol = parseInt(args[1]);
|
||||
if (vol >= 0 && vol <= 10) {
|
||||
volume = vol / 10;
|
||||
if (howlerInstance) setVolume();
|
||||
addTerminalLine(`Volume set to ${vol}/10`);
|
||||
} else {
|
||||
addTerminalLine('Volume must be between 0 and 10.');
|
||||
}
|
||||
} else {
|
||||
addTerminalLine(`Current volume: ${Math.round(volume * 10)}/10`);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'status':
|
||||
if (currentTracks.length > 0) {
|
||||
const track = currentTracks[0];
|
||||
addTerminalLine(`Now playing: ${track.title}`);
|
||||
addTerminalLine(`Status: ${isPlaying ? 'Playing' : 'Paused'}`);
|
||||
addTerminalLine(`Volume: ${Math.round(volume * 10)}/10`);
|
||||
} else {
|
||||
addTerminalLine('No track loaded.');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'history':
|
||||
downloadHistory();
|
||||
addTerminalLine('Playlist history downloaded.');
|
||||
break;
|
||||
|
||||
case 'effect':
|
||||
if (args[1]) {
|
||||
if (args[1] === 'list') {
|
||||
addTerminalLine('Available effects:');
|
||||
addTerminalLine(' vernoi - Voronoi pattern effect');
|
||||
addTerminalLine(' waveyzz - Wave synthesis effect');
|
||||
addTerminalLine(' oscrotate - Oscillating rotation effect');
|
||||
addTerminalLine('Usage: effect <effect_name> or effect list');
|
||||
} else {
|
||||
const effectName = args[1].toLowerCase();
|
||||
let effectIndex = -1;
|
||||
|
||||
switch (effectName) {
|
||||
case 'vernoi':
|
||||
effectIndex = 0;
|
||||
break;
|
||||
case 'waveyzz':
|
||||
effectIndex = 1;
|
||||
break;
|
||||
case 'oscrotate':
|
||||
effectIndex = 2;
|
||||
break;
|
||||
default:
|
||||
addTerminalLine(`Unknown effect: ${effectName}`);
|
||||
addTerminalLine('Type "effect list" to see available effects.');
|
||||
break;
|
||||
}
|
||||
|
||||
if (effectIndex !== -1) {
|
||||
try {
|
||||
hydraEffects[effectIndex]();
|
||||
currentEffectIndex = effectIndex;
|
||||
addTerminalLine(`Effect changed to: ${effectName}`);
|
||||
} catch (error) {
|
||||
addTerminalLine('Failed to apply effect.');
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rotateHydraEffect();
|
||||
addTerminalLine('Visualization changed to next effect.');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'fullscreen':
|
||||
toggleFullScreen();
|
||||
addTerminalLine('Toggled fullscreen mode.');
|
||||
break;
|
||||
|
||||
case 'clear':
|
||||
terminalOutput.innerHTML = '';
|
||||
break;
|
||||
|
||||
case 'exit':
|
||||
closeTerminal();
|
||||
break;
|
||||
|
||||
default:
|
||||
addTerminalLine(`Command not found: ${cmd}`);
|
||||
addTerminalLine('Type "help" for available commands.');
|
||||
break;
|
||||
}
|
||||
|
||||
addTerminalLine('');
|
||||
}
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user