|
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);
| |
| })();
| |