More actions
d nepoužívať ?. Značka: vrátené |
Bez shrnutí editace Značka: vrátené |
||
Riadok 316: | Riadok 316: | ||
// ---------------------------------------------------------------- | // ---------------------------------------------------------------- | ||
globalThis.window.addEventListener("load", () => { | //globalThis.window.addEventListener("load", () => { | ||
const poznamkyElement = document.querySelector("#mw-content-text .mw-parser-output"); | const poznamkyElement = document.querySelector("#mw-content-text .mw-parser-output"); | ||
const elementMapy = document.getElementById("mapa"); | const elementMapy = document.getElementById("mapa"); | ||
Riadok 326: | Riadok 326: | ||
console.log("Na stránke sa nenachádza pojmová mapa."); | console.log("Na stránke sa nenachádza pojmová mapa."); | ||
} | } | ||
}); | //}); | ||
})(); | })(); | ||
Verzia z 18:13, 9. august 2024
// 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() {
let tb = jsonMapy.vrcholy.find((vrchol) => vrchol.id === bunkaId)
const titulokBunky = tb.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 ce = document.querySelector(hash);
var cielovyElement = ce.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);
})();