Show sourcecode
The following files exists in this folder. Click to view.
terminal.js
154 lines UTF-8 Unix (LF)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
// ===== Knappraden, temabytare och skrivanimation för terminalsidorna =====
// Laddas i <head> utan defer, så att temat sätts innan sidan ritas och inte blinkar till.
// Samma teman och localStorage-nyckel som Framtidens teknik, så valet följer med mellan sidorna
const TEMAN = ["matrix", "amber", "paper", "dracula", "nord", "gruvbox", "ocean", "sunset", "cobalt"];
// localStorage kan kasta fel (t.ex. i privat läge), då används standardvärdena
function las(nyckel) {
try {
return localStorage.getItem(nyckel);
} catch {
return null;
}
}
function spara(nyckel, varde) {
try {
localStorage.setItem(nyckel, varde);
} catch {}
}
const sparatTema = las("tema");
let tema = TEMAN.includes(sparatTema) ? sparatTema : TEMAN[0];
document.documentElement.setAttribute("data-theme", tema);
document.addEventListener("DOMContentLoaded", () => {
skapaKnapprad();
skrivUt();
});
// Knapparna upptill: [cd ..] och [cat source] till vänster, [tema] till höger.
// [cd ..] finns redan i HTML:en så att den fungerar utan JS, den flyttas in i raden
function skapaKnapprad() {
const rad = document.createElement("div");
rad.className = "knapprad";
const vanster = document.createElement("div");
const hoger = document.createElement("div");
const tillbaka = document.querySelector(".tillbaka");
if (tillbaka) {
vanster.append(tillbaka);
}
// Bara övningarna har källkod att visa (exercises/incl/kallkod.php)
const kallkod = document.getElementById("kallkod");
if (kallkod) {
vanster.append(skapaKallkodsknapp(kallkod));
}
hoger.append(skapaTemabytare());
rad.append(vanster, hoger);
document.body.prepend(rad);
}
// [cat source] visar sidans PHP-kod bredvid resultatet
function skapaKallkodsknapp(kallkod) {
const knapp = document.createElement("button");
knapp.type = "button";
knapp.className = "knapp";
knapp.textContent = "[cat source]";
knapp.setAttribute("aria-controls", kallkod.id);
knapp.setAttribute("aria-expanded", false);
knapp.addEventListener("click", () => {
kallkod.hidden = !kallkod.hidden;
knapp.setAttribute("aria-expanded", !kallkod.hidden);
});
return knapp;
}
// [tema], som fäller ut en lista med alla teman.
// Byggs med JS eftersom den ändå bara fungerar med JS
function skapaTemabytare() {
const bytare = document.createElement("details");
bytare.className = "tema-bytare";
const knapp = document.createElement("summary");
const lista = document.createElement("ul");
lista.className = "tema-lista";
const temaKnappar = TEMAN.map((namn) => {
const rad = document.createElement("li");
const temaKnapp = document.createElement("button");
temaKnapp.type = "button";
temaKnapp.textContent = namn;
// Som i Framtidens teknik: knappen får temats färger, så man ser hur det ser ut innan man väljer
temaKnapp.setAttribute("data-theme", namn);
temaKnapp.addEventListener("click", () => {
valjTema(namn);
bytare.open = false;
knapp.focus();
});
rad.append(temaKnapp);
lista.append(rad);
return temaKnapp;
});
function uppdatera() {
knapp.textContent = `[tema: ${tema}]`;
for (const temaKnapp of temaKnappar) {
temaKnapp.setAttribute("aria-pressed", temaKnapp.textContent === tema);
}
}
function valjTema(namn) {
tema = namn;
document.documentElement.setAttribute("data-theme", tema);
spara("tema", tema);
uppdatera();
}
// Stäng listan vid klick utanför den, eller med Escape
document.addEventListener("click", (e) => {
if (!bytare.contains(e.target)) {
bytare.open = false;
}
});
bytare.addEventListener("keydown", (e) => {
if (e.key === "Escape" && bytare.open) {
bytare.open = false;
knapp.focus();
}
});
uppdatera();
bytare.append(knapp, lista);
return bytare;
}
// Element med klassen "skriv" skrivs ut ett tecken i taget, i ordning
async function skrivUt() {
// Respektera om besökaren har bett om mindre rörelse
if (matchMedia("(prefers-reduced-motion: reduce)").matches) {
return;
}
const vanta = (ms) => new Promise((klar) => setTimeout(klar, ms));
for (const rad of document.querySelectorAll(".skriv")) {
const text = rad.textContent.trim();
rad.textContent = "";
rad.classList.add("skriver");
for (const tecken of text) {
rad.textContent += tecken;
await vanta(20);
}
rad.classList.remove("skriver");
}
}