Deploy: Fri May 15 14:02:26 UTC 2026
This commit is contained in:
commit
884ab01739
900 changed files with 23978 additions and 0 deletions
13
docs/static/scripts/88x31.js
vendored
Normal file
13
docs/static/scripts/88x31.js
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
const images = document.querySelectorAll(".i88x31");
|
||||
|
||||
images.forEach(image => {
|
||||
image.addEventListener("click", () => {
|
||||
const selected = document.querySelectorAll(".i88x31.selected");
|
||||
selected.forEach(s => {
|
||||
if (s != image) {
|
||||
s.classList.toggle("selected");
|
||||
}
|
||||
})
|
||||
image.classList.toggle("selected");
|
||||
})
|
||||
})
|
||||
14
docs/static/scripts/ccd.js
vendored
Normal file
14
docs/static/scripts/ccd.js
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
const konamiCode = ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowLeft', 'ArrowRight', 'KeyB', 'KeyA'];
|
||||
let keyIndex = 0;
|
||||
|
||||
document.addEventListener('keydown', function(event) {
|
||||
if (event.code === konamiCode[keyIndex]) {
|
||||
keyIndex++;
|
||||
if (keyIndex === konamiCode.length) {
|
||||
window.location.href = `${rootPrefix}static/toyourdreams.txt`
|
||||
keyIndex = 0;
|
||||
}
|
||||
} else {
|
||||
keyIndex = 0;
|
||||
}
|
||||
});
|
||||
51
docs/static/scripts/home.js
vendored
Normal file
51
docs/static/scripts/home.js
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
const _homeSquares = document.querySelector("#homeSquares");
|
||||
const main = document.querySelector("main");
|
||||
|
||||
let info = [
|
||||
["Default", "defaultSquare", "arrow-cursor.svg", "Computer cursor arrow line drawing"],
|
||||
["Music", "headerSquareMusic", "8thNote.svg", "Music note line drawing"],
|
||||
["Video", "headerSquareVideo", "video.svg", "Video roll line drawing"],
|
||||
["Code", "headerSquareCode", "file-code.svg", "Computer code file line drawing"]
|
||||
]
|
||||
|
||||
info.forEach(square => {
|
||||
let rawHTML = `<div class='headerSquare' id='${square[1]}'><img src='${rootPrefix}static/images/${square[2]}' alt='${square[3]}'></div>`;
|
||||
_homeSquares.innerHTML += rawHTML;
|
||||
});
|
||||
|
||||
const homeSquares = document.querySelectorAll(".headerSquare");
|
||||
let selectedSquare = "defaultSquare";
|
||||
let selectedSquareDiv;
|
||||
updateSquare();
|
||||
|
||||
homeSquares.forEach(square =>
|
||||
square.addEventListener('click', () => {
|
||||
toggleSquare(square);
|
||||
})
|
||||
)
|
||||
|
||||
function toggleSquare(square) {
|
||||
if (selectedSquare && (selectedSquare == square.id)) {
|
||||
return
|
||||
}
|
||||
else if (selectedSquare) {
|
||||
oldSquare = document.getElementById(selectedSquare);
|
||||
oldSquare.classList.toggle("selected");
|
||||
document.querySelector(`#${selectedSquareDiv}`).classList.toggle("selected");
|
||||
}
|
||||
selectedSquare = square.id;
|
||||
updateSquare()
|
||||
}
|
||||
|
||||
function getSquareDivByID(id) {
|
||||
divID = `hs${info.find(item => item[1] === id)[0]}`;
|
||||
return document.querySelector(`#${divID}`);
|
||||
}
|
||||
|
||||
function updateSquare() {
|
||||
square = document.getElementById(selectedSquare);
|
||||
square.classList.toggle("selected");
|
||||
div = getSquareDivByID(square.id);
|
||||
selectedSquareDiv = div.id;
|
||||
div.classList.toggle("selected");
|
||||
}
|
||||
37
docs/static/scripts/language.js
vendored
Normal file
37
docs/static/scripts/language.js
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
const el = document.getElementById("languageTitle");
|
||||
const texts = [["Pick a language", "English"], ["Escolha um idioma", "Português Brasileiro"]];
|
||||
let i = 0;
|
||||
let fadeTime = 1000;
|
||||
let holdTime = 2000;
|
||||
const links = document.querySelectorAll("#languageList li a");
|
||||
let currentLang = 0;
|
||||
|
||||
el.style.transition = `opacity ${fadeTime}ms`;
|
||||
el.style.opacity = 1;
|
||||
|
||||
function cycle() {
|
||||
el.style.opacity = 0;
|
||||
removeOldHighlightedLang();
|
||||
setTimeout(() => {
|
||||
i = (i + 1) % texts.length;
|
||||
currentLang = i;
|
||||
el.textContent = texts[i][0];
|
||||
el.style.opacity = 1;
|
||||
setNewHighlightedLang()
|
||||
}, fadeTime);
|
||||
}
|
||||
|
||||
function removeOldHighlightedLang() {
|
||||
document.querySelector(".languagesHighlightedLink").classList.remove("languagesHighlightedLink");
|
||||
}
|
||||
|
||||
function setNewHighlightedLang() {
|
||||
links.forEach(link => {
|
||||
if (link.innerText == texts[currentLang][1]) {
|
||||
link.classList.add('languagesHighlightedLink');
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
setNewHighlightedLang()
|
||||
setInterval(cycle, fadeTime * 2 + holdTime);
|
||||
227
docs/static/scripts/music.js
vendored
Normal file
227
docs/static/scripts/music.js
vendored
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
// This script handles the playback of music in the header's miniplayer ;)
|
||||
import { showNotification } from './notification.js';
|
||||
const body = document.querySelector("body");
|
||||
|
||||
const musicdiv = document.getElementById("music");
|
||||
musicdiv.innerHTML = `
|
||||
<img src="${rootPrefix}static/images/gears.svg" class="optionsToggle invertedc">
|
||||
<img src="${rootPrefix}static/images/sound-on.png" id="sound">
|
||||
<select name="song" id="songSelection"></select>
|
||||
`
|
||||
const linksHelper = document.getElementById("linksHelper");
|
||||
linksHelper.insertBefore(document.createElement("hs"), document.getElementById("headerLinks"));
|
||||
|
||||
const songs = [
|
||||
{ file: "Velkommen.mp3", name: 'Velkommen', artwork: "velkommen.jpg" },
|
||||
{ file: "PG2.mp3", name: 'Frugal APE', artwork: "pg.jpg" },
|
||||
{ file: "dreamscape.mp3", name: 'Dreamscape', artwork: "winds.png" },
|
||||
{ file: "skychat.mp3", name: 'Skychat', artwork: "winds.png" }
|
||||
];
|
||||
|
||||
// Options page
|
||||
const optionsAside = document.createElement("aside");
|
||||
optionsAside.classList.add("closed");
|
||||
optionsAside.classList.add("metromenu");
|
||||
{
|
||||
const back = document.createElement("p");
|
||||
back.textContent = headeri18n.back;
|
||||
back.classList.add("optionsToggle");
|
||||
|
||||
const title = document.createElement("h2");
|
||||
title.textContent = headeri18n.options;
|
||||
optionsAside.appendChild(title);
|
||||
optionsAside.appendChild(back);
|
||||
|
||||
const content = document.createElement("div");
|
||||
content.innerHTML = `
|
||||
<div id="content">
|
||||
<div id="songDrawer"></div>
|
||||
<div>
|
||||
<p class="playingSong"></p>
|
||||
<p>${headeri18n.by} tenkuma</p>
|
||||
</div>
|
||||
<div id="playlist"></div>
|
||||
<div>
|
||||
<p>Volume</p>
|
||||
<input id="volume" type="range" min="0" max="100"></input>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<p>${headeri18n.hideBackground}</p>
|
||||
<input id="background" type="checkbox"></input>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
optionsAside.appendChild(content);
|
||||
}
|
||||
body.appendChild(optionsAside);
|
||||
|
||||
document.getElementById("volume").addEventListener("input", (e) => {
|
||||
setVolume(e.target.value / 100);
|
||||
}); // dirty workaround to replace inline function calling in the volume input
|
||||
|
||||
const toggleIMG = document.querySelector('#sound');
|
||||
toggleIMG.addEventListener('click', () => {
|
||||
toggleAudio();
|
||||
})
|
||||
|
||||
const hideBG = document.querySelector("input#background");
|
||||
if (localStorage.getItem("bgHidden") === "true") hideBG.checked = true, toggleBG();
|
||||
hideBG.addEventListener("click", () => {
|
||||
toggleBG();
|
||||
})
|
||||
|
||||
function toggleBG() {
|
||||
const bg = document.querySelector(".bg");
|
||||
bg.classList.toggle("invisible");
|
||||
localStorage.setItem("bgHidden", bg.classList.contains("invisible"))
|
||||
}
|
||||
|
||||
const songsDrawer = document.querySelector("#songDrawer");
|
||||
const drawerSongs = [];
|
||||
const playlist = document.querySelector("#playlist");
|
||||
const expandButton = document.createElement('p');
|
||||
expandButton.textContent = "Playlist";
|
||||
expandButton.classList.add("playlistTitle");
|
||||
playlist.appendChild(expandButton);
|
||||
|
||||
songs.forEach(song => {
|
||||
const songElement = document.createElement("div");
|
||||
songElement.classList.add("drawerSong");
|
||||
songElement.dataset.song = song.file;
|
||||
const songImage = document.createElement("img");
|
||||
songImage.src = `${rootPrefix}static/images/songs/${song.artwork}`;
|
||||
songElement.appendChild(songImage);
|
||||
songElement.addEventListener('click', () => {
|
||||
changeSong(song.file);
|
||||
});
|
||||
drawerSongs.push(songElement);
|
||||
songsDrawer.appendChild(songElement);
|
||||
|
||||
// Playlist
|
||||
const playlistEntry = document.createElement("p");
|
||||
playlistEntry.textContent = song.name;
|
||||
playlistEntry.addEventListener('click', () => {
|
||||
changeSong(song.file);
|
||||
})
|
||||
playlist.appendChild(playlistEntry);
|
||||
})
|
||||
|
||||
const audioSelect = document.getElementById("songSelection");
|
||||
songs.forEach(song => {
|
||||
const songOption = document.createElement("option");
|
||||
songOption.value = song.file;
|
||||
songOption.textContent = song.name;
|
||||
audioSelect.appendChild(songOption);
|
||||
});
|
||||
|
||||
const playingSongLabel = document.querySelector(".playingSong");
|
||||
|
||||
function updatePlayingLabel(label) {
|
||||
drawerSongs.forEach(sng => {
|
||||
sng.classList.remove("selected");
|
||||
if (sng.dataset.song == label) {
|
||||
sng.classList.add("selected");
|
||||
}
|
||||
});
|
||||
|
||||
const songString = songs.find(item => item.file === label).name;
|
||||
playingSongLabel.textContent = songString;
|
||||
}
|
||||
|
||||
const savedSong = localStorage.getItem("song");
|
||||
|
||||
if (savedSong) {
|
||||
audioSelect.value = savedSong;
|
||||
updatePlayingLabel(savedSong);
|
||||
} else {
|
||||
audioSelect.value = songs[0].file;
|
||||
updatePlayingLabel(songs[0].file);
|
||||
}
|
||||
|
||||
const optionsButton = document.querySelectorAll(".optionsToggle");
|
||||
optionsButton.forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
optionsAside.classList.toggle('closed');
|
||||
});
|
||||
});
|
||||
|
||||
// Create the audio object using the current select value
|
||||
let audio = new Audio(`${rootPrefix}static/music/${audioSelect.value}`);
|
||||
|
||||
const savedTime = localStorage.getItem("audioTime");
|
||||
const savedVolume = localStorage.getItem("volume");
|
||||
|
||||
if (savedVolume !== null) {
|
||||
audio.volume = parseFloat(savedVolume);
|
||||
} else {
|
||||
audio.volume = 0.8;
|
||||
}
|
||||
|
||||
const wasPlaying = localStorage.getItem("audioPlaying") === 'true';
|
||||
|
||||
function play() {
|
||||
audio.volume = localStorage.getItem("volume") ?? 0.8;
|
||||
audio.play().catch(() => {
|
||||
stop();
|
||||
showNotification(headeri18n.permissionIssue, headeri18n.permissionIssueNotificationContent, 5000);
|
||||
});;
|
||||
localStorage.setItem("audioPlaying", "true")
|
||||
toggleIMG.src = `${rootPrefix}static/images/sound-on.png`
|
||||
console.log(`[Music Player] playing ${audioSelect.value}`)
|
||||
}
|
||||
|
||||
function stop() {
|
||||
audio.pause();
|
||||
localStorage.setItem("audioPlaying", "false")
|
||||
toggleIMG.src = `${rootPrefix}static/images/sound-off.png`
|
||||
}
|
||||
|
||||
function setVolume(volume) {
|
||||
audio.volume = volume;
|
||||
localStorage.setItem("volume", volume);
|
||||
}
|
||||
|
||||
function toggleAudio() {
|
||||
if (!audio.paused) {
|
||||
stop();
|
||||
} else {
|
||||
play();
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("beforeunload", () => {
|
||||
localStorage.setItem("audioTime", audio.currentTime);
|
||||
localStorage.setItem("audioPlaying", !audio.paused);
|
||||
});
|
||||
|
||||
function changeSong(song) {
|
||||
const wasPlaying = !audio.paused;
|
||||
stop();
|
||||
localStorage.removeItem("audioTime");
|
||||
audio = new Audio(`${rootPrefix}static/music/${song}`);
|
||||
if (savedVolume) setVolume(savedVolume);
|
||||
console.log(`[Music Player] changing song to ${song}`)
|
||||
localStorage.setItem("song", song);
|
||||
updatePlayingLabel(song);
|
||||
if (wasPlaying) play();
|
||||
}
|
||||
|
||||
// hooking into the options menu 'change' event to update the song
|
||||
audioSelect.addEventListener('change', () => {
|
||||
changeSong(audioSelect.value);
|
||||
})
|
||||
|
||||
// Set initial playback state and volume based on saved preferences
|
||||
if (savedTime) audio.currentTime = parseFloat(savedTime);
|
||||
|
||||
if (savedVolume) {
|
||||
document.getElementById("volume").value = savedVolume * 100;
|
||||
} else {
|
||||
document.getElementById("volume").value = 100;
|
||||
}
|
||||
|
||||
if (wasPlaying) {
|
||||
play();
|
||||
} else {
|
||||
stop();
|
||||
}
|
||||
45
docs/static/scripts/notification.js
vendored
Normal file
45
docs/static/scripts/notification.js
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import { registerElementHint } from "./tips.js";
|
||||
|
||||
const notificationBox = document.createElement('div');
|
||||
notificationBox.classList.add('notificationBox');
|
||||
|
||||
export async function showNotification(title, subtitle, time, hint) {
|
||||
if (!hint) {
|
||||
hint = headeri18n.notificationDefaultHint;
|
||||
}
|
||||
const notificationBox = document.createElement('div');
|
||||
notificationBox.classList.add('notificationBox');
|
||||
notificationBox.dataset.tip = hint;
|
||||
|
||||
const notificationTitle = document.createElement('h1');
|
||||
notificationTitle.innerHTML = title;
|
||||
|
||||
const notificationSubtitle = document.createElement('p');
|
||||
notificationSubtitle.innerHTML = subtitle;
|
||||
|
||||
notificationBox.appendChild(notificationTitle);
|
||||
notificationBox.appendChild(notificationSubtitle);
|
||||
document.querySelector('body').appendChild(notificationBox);
|
||||
|
||||
registerElementHint(notificationBox);
|
||||
|
||||
let clicked = false;
|
||||
|
||||
notificationBox.addEventListener('click', () => {
|
||||
hideNotification(notificationBox);
|
||||
})
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
notificationBox.classList.add('shown');
|
||||
});
|
||||
await new Promise(r => setTimeout(r, time));
|
||||
if (!clicked) {
|
||||
hideNotification(notificationBox);
|
||||
}
|
||||
}
|
||||
|
||||
async function hideNotification(notificationBox) {
|
||||
notificationBox.classList.remove('shown');
|
||||
await new Promise(r => setTimeout(r, 1000));
|
||||
notificationBox.remove();
|
||||
}
|
||||
66
docs/static/scripts/telnetSimulator.js
vendored
Normal file
66
docs/static/scripts/telnetSimulator.js
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
const screen = document.getElementById('telnetSimulationClientScreen');
|
||||
const input = document.getElementById('telnetSimulationInputBox');
|
||||
const btn = document.getElementById('telnetSimulationClientSend')
|
||||
const sbtn = document.getElementById('telnetSimulationServerClean');
|
||||
const sscreen = document.getElementById('telnetSimulationServerScreen');
|
||||
const loading = document.getElementById("telnetSimulationLoadingHolder")
|
||||
const loadingText = document.getElementById("telnetSimulationLoadingText")
|
||||
let loadingms = 2500;
|
||||
|
||||
btn.addEventListener('click', () => {
|
||||
send_command();
|
||||
})
|
||||
|
||||
sbtn.addEventListener('click', () => {
|
||||
screen.innerHTML = '';
|
||||
sscreen.innerHTML = '';
|
||||
})
|
||||
|
||||
async function send_command() {
|
||||
if (!input.value) return;
|
||||
const command = input.value;
|
||||
input.value = '';
|
||||
screen.value += `telnet> ${command}\n`;
|
||||
await wait(loadingms, "<b>Camada OSI #7 - Aplicação:</b> Usuário digitou o texto na aplicação.");
|
||||
await wait(loadingms, "<b>Camada OSI #6 - Apresentação:</b> Tradução do comando para um pacote legível para o servidor.");
|
||||
await wait(loadingms, "<b>Camada OSI #5 - Sessão:</b> Sistema do cliente abre uma conexão com o servidor.");
|
||||
await wait(loadingms, "<b>Camada OSI #4 - Transporte:</b> Sistema do cliente troca informações com o servidor.");
|
||||
await wait(loadingms, "<b>Camada OSI #3 - Rede:</b> O sistema do cliente resolve o endereço do servidor.");
|
||||
await wait(loadingms, "<b>Camada OSI #2 - Enlace de dados:</b> Os frames são entregues ao dispositivo com o endereço MAC correto.");
|
||||
await wait(loadingms, "<b>Camada OSI #1 - Física:</b> Os dados são transmitidos por cabo, ou via <i>wireless</i>, para o dispositivo de destino.");
|
||||
process_command(command);
|
||||
}
|
||||
|
||||
async function process_command(command) {
|
||||
sscreen.value += `Comando recebido: ${command}\n`;
|
||||
args = command.split(" ");
|
||||
command = args[0]
|
||||
args.shift();
|
||||
await wait(loadingms, "<b>O servidor empacota uma resposta, que também será passada por todas camadas até chegar no cliente.");
|
||||
switch (command) {
|
||||
case 'help':
|
||||
screen.value += `Comandos disponíveis:\nhelp - mostra essa mensagem de ajuda\nping - responde 'pong'\necho [texto] - retorna o texto especificado no comando\ntimeout [milissegundos] - muda o tempo que as mensagens de carregamento da simulação duram`
|
||||
break;
|
||||
case 'echo':
|
||||
screen.value += `Resposta do servidor: ${args.join(' ')}\n`
|
||||
break;
|
||||
case 'ping':
|
||||
screen.value += `Pong!\n`
|
||||
break;
|
||||
case 'timeout':
|
||||
loadingms = args[0]
|
||||
screen.value += `O tempo das mensagens de carregamento foi mudado para ${args[0]}ms!`
|
||||
break;
|
||||
default:
|
||||
screen.value += `Comando desconhecido! Envie help para ver a lista de comandos.\n`
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async function wait(ms, txt = '') {
|
||||
const delay = ms => new Promise(res => setTimeout(res, ms));
|
||||
loadingText.innerHTML = txt;
|
||||
loading.style.display = "flex"
|
||||
await delay(ms)
|
||||
loading.style.display = "none"
|
||||
}
|
||||
57
docs/static/scripts/tips.js
vendored
Normal file
57
docs/static/scripts/tips.js
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
||||
const body = document.querySelector('body');
|
||||
const elements = document.querySelectorAll('[data-tip]');
|
||||
const hint = document.querySelector("#headerSubtitle");
|
||||
const hintPanelDefaultText = hint.innerHTML;
|
||||
let fixedHint;
|
||||
let currentObserver;
|
||||
|
||||
elements.forEach(el => {
|
||||
registerElementHint(el);
|
||||
})
|
||||
|
||||
function cleanup() {
|
||||
hint.innerHTML = hintPanelDefaultText;
|
||||
hint.classList.remove('fixed');
|
||||
if (fixedHint) {
|
||||
fixedHint.remove();
|
||||
}
|
||||
if (currentObserver) {
|
||||
currentObserver.disconnect();
|
||||
currentObserver = null;
|
||||
}
|
||||
}
|
||||
|
||||
export function registerElementHint(el) {
|
||||
if (isMobile) {
|
||||
return;
|
||||
}
|
||||
el.addEventListener('mouseenter', function() {
|
||||
cleanup();
|
||||
|
||||
if (currentObserver) {
|
||||
currentObserver.disconnect();
|
||||
}
|
||||
|
||||
currentObserver = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (!entry.isIntersecting) {
|
||||
fixedHint = document.createElement('p');
|
||||
fixedHint.id = "fixedHint";
|
||||
fixedHint.innerHTML = el.dataset.tip;
|
||||
fixedHint.setAttribute('aria-hidden', 'true');
|
||||
body.appendChild(fixedHint);
|
||||
} else {
|
||||
hint.innerHTML = el.dataset.tip;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
hint.innerHTML = el.dataset.tip;
|
||||
currentObserver.observe(hint);
|
||||
});
|
||||
|
||||
el.addEventListener('mouseleave', function() {
|
||||
cleanup();
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue