Toggle menu
Toggle preferences menu
Toggle personal menu
Neprihlásený/á
Your IP address will be publicly visible if you make any edits.
pojmová mapa + nadpisy
Značka: vrátené
presunúť na ResourceLoader
Značky: Vybielenie manuálne vrátenie
 
(2 medziľahlé úpravy od rovnakého používateľa nie sú zobrazené.)
Riadok 1: Riadok 1:
// Pojmová mapa:
(() => {
const tippyInstancia = tippy(document.createElement("div"));


/**
* Vytvorí JSON pojmovej mapy z obsahu stránky.
*
* @param {HTMLElement} poznamkyElement Element, ktorý obsahuje poznámky.
* @returns {object} JSON reprezentácia pojmovej mapy.
**/
function vytvoritDataMapy(poznamkyElement) {
    let mapa = { vrcholy: [], hrany: [] };
    const nadpisy = poznamkyElement.querySelectorAll("h1, h2, h3, h4, h5, h6");
    let posledneNadpisy = [];
    let korenovyNadpis = document.getElementById("firstHeading").innerText;
    mapa.vrcholy.push({
        id: 1,
        label: korenovyNadpis,
        color: generovatFarbu(0),
        tooltip: "",
    });
    for (const [index, nadpis] of Array.from(nadpisy).entries()) {
        const aktualnyLevel = ziskatLevelNadpisu(nadpis);
        const idVrchola = index + 2; // ID začína od 2 pre druhý nadpis
        const nazov = nadpis.querySelector(".mw-headline").innerText;
        // hľadáme posledný platný nadpis pre daný level alebo nižší, ktorý bude rodičom
        let idRodica;
        for (let lvl = aktualnyLevel - 1; lvl >= 0; lvl--) {
            if (posledneNadpisy[lvl] !== undefined) {
                idRodica = posledneNadpisy[lvl];
                break;
            }
        }
        idRodica = idRodica || 1; // ak nie je žiaden platný nadpis, použijeme koreňový nadpis
        mapa.vrcholy.push({
            id: idVrchola,
            label: nazov,
            color: generovatFarbu(idRodica),
            tooltip: obsahNadpisu(nadpis),
        });
        mapa.hrany.push({ from: idRodica, to: idVrchola });
        posledneNadpisy[aktualnyLevel] = idVrchola; // aktualizujeme posledný nadpis na aktuálnej úrovni
        // vymažeme všetky nadpisy na vyšších úrovniach ako je súčasná
        posledneNadpisy = posledneNadpisy.slice(0, aktualnyLevel + 1);
    }
    return mapa;
}
/**
* Získa obsah nadpisu a všetkých nasledujúcich elementov, pokiaľ nepríde po ďalší nadpis.
* Obsah je v HTML formáte.
*
* @param {HTMLElement} nadpisElement HTML element nadpisu.
* @returns {string} Obsah nadpisu a všetkých nasledujúcich elementov, ktoré nie sú nadpisy.
**/
function obsahNadpisu(nadpisElement) {
    // klon nadpisu - v tooltipe chceme iba text nadpisu na stred
    let novyNadpis = nadpisElement.cloneNode(true);
    novyNadpis.innerHTML = novyNadpis.querySelector(".mw-headline").outerHTML;
    novyNadpis.style.marginTop = 0;
    novyNadpis.style.textAlign = "center";
    // obsah - toto sa zobrazí v tooltipe
    let obsahElement = document.createElement("div");
    obsahElement.appendChild(novyNadpis); // nadpis
    /**
    * @param {HTMLElement | null} element Aktuálny element.
    **/
    function __pridavajObsah(element) {
        // ak nemáme element alebo je to nadpis, končíme
        if (element === null || ["H1", "H2", "H3", "H4", "H5", "H6"].includes(element.tagName)) {
            return;
        }
        if (element.querySelector(":scope > h1, h2, h3, h4, h5, h6")) {
            // ak obsahuje vnorené nadpisy, preskočíme dovnútra a pridávame obsah
            __pridavajObsah(element.firstElementChild);
        } else {
            // ak neobsahuje ďalšie vnorené nadpisy, pridávame postupne obsah elementov
            obsahElement.appendChild(element.cloneNode(true));
            __pridavajObsah(element.nextElementSibling);
        }
    }
    // pridáme obsah (HTML pod nadpisom)
    __pridavajObsah(nadpisElement.nextElementSibling);
    obsahElement = skratitElement(obsahElement);
    return obsahElement.innerHTML;
}
/**
* Skráti HTML elementu na maximálnu dĺžku.
* Vráti nový element (po skrátení).
*
* @param {HTMLElement} element
* @param {Number} maxDlzka
* @returns {HTMLElement} Nový element (po skrátení).
*/
function skratitElement(element, maxDlzka = 2000) {
    let novyElement = document.createElement(element.tagName);
    let pocitadloDlzok = 0;
    for (let child of [...element.childNodes]) {
        pocitadloDlzok += child.innerText.length;
        if (pocitadloDlzok > maxDlzka) {
            novyElement.appendChild(document.createTextNode("..."));
            break;
        }
        novyElement.appendChild(child);
    }
    return novyElement;
}
/**
* Vygeneruje zobrazenie pojmovej mapy.
*
* @param {HTMLElement} elementMapy Element, do ktorého sa vloží zobrazenie pojmovej mapy.
* @param {object} jsonMapy JSON reprezentácia pojmovej mapy.
* @returns {object} Objekt pojmovej mapy (vis-network).
**/
function vykreslitMapu(elementMapy, jsonMapy) {
    const sirkaZobrazenia = window.innerWidth;
    const jeSirokeZobrazenie = sirkaZobrazenia > 768;
    const dataSiete = {
        nodes: new vis.DataSet(jsonMapy.vrcholy),
        edges: new vis.DataSet(jsonMapy.hrany),
    };
    const nastavenia = {
        autoResize: true,
        clickToUse: true,
        interaction: {
            hover: true,
            keyboard: {
                enabled: true,
                bindToWindow: false,
                autoFocus: false,
            },
        },
        nodes: {
            shape: "box",
            widthConstraint: {
                maximum: 200,
            },
            margin: 10,
            labelHighlightBold: false,
        },
        edges: {
            width: 1.0,
            arrows: {
                to: {
                    enabled: true,
                },
            },
        },
        layout: {
            hierarchical: {
                enabled: true,
                direction: jeSirokeZobrazenie ? "UD" : "LR",
                sortMethod: "directed",
                nodeSpacing: jeSirokeZobrazenie ? 220 : 100,
                levelSeparation: jeSirokeZobrazenie ? 100 : 250,
                shakeTowards: "roots",
            },
        },
        physics: {
            hierarchicalRepulsion: {
                nodeDistance: 150,
            },
        },
        height: "400px",
    };
    const pojmova_mapa = new vis.Network(elementMapy, dataSiete, nastavenia);
    // tooltipy
    pojmova_mapa.on("hoverNode", (parametre) => {
        const obsah = jsonMapy.vrcholy.find((vrchol) => vrchol.id === parametre.node).tooltip;
        if (!parametre.node || !obsah) return;
        tippyInstancia.setProps({
            triggerTarget: elementMapy,
            maxWidth: "90vw",
            allowHTML: true,
            arrow: false,
            getReferenceClientRect: () => ({
                width: 0,
                height: 0,
                left: window.innerWidth / 2,
                right: window.innerWidth / 2,
                top: window.innerHeight - 10,
            }),
            interact: true,
        });
        tippyInstancia.setContent(`<div style="padding: 1rem; font-size: 12px; color: white;">${obsah}</div>`);
        tippyInstancia.show();
    });
    var poslednaKliknutaBunkaId;
    var casPoslednehoKliknutiaNaBunku;
    pojmova_mapa.on("click", (parametre) => {
        if (!parametre.nodes.length) return;
        let bunkaId = parametre.nodes[0];
        function _navigovat() {
            const titulokBunky = jsonMapy.vrcholy.find((vrchol) => vrchol.id === bunkaId)?.label;
            if (!titulokBunky) return;
            window.location.hash = null;
            window.location.hash = `#${titulokBunky.replaceAll(" ", "_")}`;
            tippyInstancia.hide();
        }
        if (!jeSirokeZobrazenie) {
            // mobilné zariadenia - dvojklik, keďže prvý klik zobrazí tooltip
            const aktualnyCas = new Date().getTime();
            if (poslednaKliknutaBunkaId === bunkaId && aktualnyCas - casPoslednehoKliknutiaNaBunku < 500) {
                setTimeout(() => {
                    _navigovat();
                }, 50);
            }
            poslednaKliknutaBunkaId = bunkaId;
            casPoslednehoKliknutiaNaBunku = aktualnyCas;
        } else {
            // PC - stačí kliknúť raz
            _navigovat();
        }
    });
    pojmova_mapa.on("blurNode", () => {
        tippyInstancia.hide();
    });
    let info = document.createElement("small");
    info.style.fontSize = "12px";
    if (jeSirokeZobrazenie) {
        info.innerHTML += "Kliknite na mapu pre interakciu s ňou. Kliknutím na vrchol sa presuniete na príslušnú sekciu.\n";
    } else {
        info.innerHTML += "Kliknite na mapu pre interakciu s ňou. Dvojitým kliknutím na vrchol sa presuniete na príslušnú sekciu.\n";
    }
    elementMapy.parentNode.insertBefore(info, elementMapy.nextSibling);
    return pojmova_mapa;
}
/**
* Získa úroveň nadpisu (h1, h2, atď.).
*
* @param {HTMLElement} nadpis HTML element nadpisu.
* @returns {number} Číselná reprezentácia úrovne nadpisu.
**/
function ziskatLevelNadpisu(nadpis) {
    return parseInt(nadpis.tagName.substring(1), 10);
}
/**
* Generuje náhodné číslo z čísla (seed). Rovnaký seed vždy vygeneruje rovnaké "náhodné" číslo v určenom rozsahu.
*
* https://stackoverflow.com/a/63599906
*
* @param {number} seed Číslo, z ktorého sa generuje náhodné číslo.
* @param {number[]} rozsah Rozsah, v ktorom sa náhodné číslo generuje.
* @returns {number} Náhodné číslo.
**/
function nahodneCislo(seed, rozsah = [0, 255]) {
    seed = String(seed)
        .split("")
        .reduce((c, n) => (n != 0 ? c * n : c * c));
    let od = rozsah[0];
    let po = rozsah[1];
    while (seed < rozsah[0] || seed > rozsah[1]) {
        if (seed > rozsah[1]) seed = Math.floor(seed / po--);
        if (seed < rozsah[0]) seed = Math.floor(seed * od++);
    }
    return seed;
}
/**
* Generuje náhodnú farbu z čísla. Rovnaké číslo vždy vygeneruje rovnakú farbu.
*
* @param {number} cislo Číslo, z ktorého sa generuje farba.
* @returns {string} Farba v RBG formáte.
**/
function generovatFarbu(seed = 1, svetla = true) {
    let od = svetla ? 125 : 0;
    let po = svetla ? 255 : 125;
    r = nahodneCislo(seed + 11, [od, po]);
    g = nahodneCislo(seed + 12, [od, po]);
    b = nahodneCislo(seed + 13, [od, po]);
    return `rgb(${r}, ${g}, ${b})`;
}
// ----------------------------------------------------------------
globalThis.window.addEventListener("load", () => {
    const poznamkyElement = document.querySelector("#mw-content-text .mw-parser-output");
    const elementMapy = document.getElementById("mapa");
    if (elementMapy !== null && poznamkyElement !== null) {
        const jsonMapy = vytvoritDataMapy(poznamkyElement);
        vykreslitMapu(elementMapy, jsonMapy);
    } else {
        console.log("Na stránke sa nenachádza pojmová mapa.");
    }
});
})();
// Zvýraznenie kotiev nadpisov:
(() => {
function pridatStyly() {
    var style = document.createElement("style");
    style.innerHTML = `
        .zvyraznenie {
            background-color: #ffff9910;
            padding-left: 5px;
            border-left: 5px solid #ffcc0026;
        }
    `;
    document.head.appendChild(style);
}
function zvyraznitSekciuPodlaHash() {
    // zmazať predošlé zvýraznenia
    document.querySelectorAll(".zvyraznenie").forEach(function (element) {
        element.classList.remove("zvyraznenie");
    });
    var hash = decodeURIComponent(window.location.hash);
    if (hash) {
        var cielovyElement = document.querySelector(hash)?.parentElement;
        if (cielovyElement) {
            // pridať class pre zvýraznenie
            cielovyElement.classList.add("zvyraznenie");
        }
    }
}
globalThis.window.document.addEventListener("DOMContentLoaded", function () {
    pridatStyly();
    zvyraznitSekciuPodlaHash();
});
globalThis.window.addEventListener("hashchange", zvyraznitSekciuPodlaHash);
})();

Aktuálna revízia z 18:15, 9. august 2024