Webbserverprogrammering 1

Show sourcecode

The following files exists in this folder. Click to view.

Webbsrvprg/js/

terminal.js

terminal.js

154 lines UTF-8 Unix (LF)
// ===== 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");
    }
}