/** Bietet ein Hauptinterface für alle Navigationselemente unter `navbar`.
*
* Hat Implementierungen/Funktionen um alle Nodes anzusprechen. Siehe `NavbarNode` für genauere Beschreibung.
*
* @param {Array(NavbarNode/Object)} nodes --- Alle Elemente die verwaltet werden sollen.
*/
class NavbarCoreIndex{
nodes = []; // Nodes bezieht sich auf die Navigationselemente welche sich unterordnen
nodesNeverHighlight = false; // Wenn true werden highlightText aufrufe niemals ausgeführt
constructor(...nodes){
// Schaut ob jede Node schon ein NavbarNode ist, konvertiert ggf. Js-Objekte welche es noch nicht sind
for(const rawNode of nodes){
let node = rawNode;
if(typeof node !== "NavbarNode"){
node = new NavbarNode( // Erstellt neues NavbarNode aus Daten (die Werte haben für den Fall Ausweichwerte)
rawNode["path"],
rawNode["name"],
rawNode["dbname"],
rawNode["url"],
rawNode["nodes"]
)
}
this.nodes.push(node);
}
//this.sort(); // (Sortierung passiert jetzt Serverseitig)
}
get hasNodes(){
return this.nodes.length != 0;
}
get allNodes(){
let all = [];
for(const node of this.nodes) all = [...all, node, ...node.allNodes]; // Nodes von allen nodes hinzufügen
return all;
}
get allNodesVisible(){
let all = [];
for(const node of this.nodes){
if(node.selfHighlight || node.nodesHighlight){
all = [...all, node, ...node.allNodesVisible]; // Nodes von allen nodes hinzufügen
}
}
return all;
}
/** Sortiert alphabetisch. __Diese Funktion hat derzeit keine Verwendung__ */
sort(){
this.nodes.sort((a, b) => { // Überschreiben die normale Sortierfunktion, damit nach .name geschaut wird
const nameA = a.name;
const nameB = b.name;
if(nameA < nameB) return -1;
if(nameA > nameB) return 1;
return 0;
}
)
}
/** `firstnode` (objekt ref) wird an den Anfang der Nodes gestellt. __Diese Funktion hat derzeit keine Verwendung__ */
putNodeFirst(firstnode){
this.nodes = [firstnode, ...this.nodes.filter(node => node != firstnode)];
}
/** `lastnode` (objekt ref) wird ans Ende gestellt. __Diese Funktion hat derzeit keine Verwendung__ */
putNodeLast(lastnode){
this.nodes = [...this.nodes.filter(node => node != lastnode), lastnode];
}
/** Hängt das html von allen `.htmlMakeElement` in dieses Element */
sendNodeHtml(target){
for(const node of this.nodes) target.appendChild(node.htmlMakeElement());
}
/** Implementierung der Suche */
highlightText(regex, inAttributes, hideIfNot=true){
if(this.nodesNeverHighlight) return false;
let nodesHighlight = false; // Vorhanden in den namen der Nodes
const inst = this.fullpath;
for(const node of this.nodes) nodesHighlight = node.highlightText(regex, inAttributes, hideIfNot) || nodesHighlight; // Alle nodes highlighten, und merken ob welche ge-highlighted
return nodesHighlight;
}
highlightReset(){
for(const node of this.nodes) node.highlightReset();
}
toString(){ // Besseres debuging
return `NavbarCoreIndex`;
}
}
/** Bietet ein Interface für die Navigationselemente unter `navbar`.
* Node bezieht sich auf die Navigationselemente welche sich jeweils unterordnen, sie werden überall mal stellenweise Ordner oder Einträge (Entrys) gennant.
*
* Wärend der instanzierung wird anhand der Url erkennt ob dieses Element geöffnet sein soll (`.opened`), und ob dieses Element gerade von der aktiven Seite ist (`.current`).
*
* Die Html-Elemente werden von der Klasse erzeugt über die Funktion `.htmlMakeElement()` als Rückgabewert.
* Diese Elemente sind so aufgebaut und als eigene Attribute registriert:
*
* ```html
*
*
*
*
*
*
*
* ```
*
* Die Suchfunktion realisiert sich über `highlightText(...)`, welches das erstellte Element ausblendet/öffnet/highlightet nach den Parametern die in der Funktion übergeben wurden.
* `highlightText()` kann dann auch denn selben Vorgang für alle eigenen Nodes ausführen, sodas durch denn kaskadierenden Effekt eine Suchfunktion realisiert wird.
*
* @param {string} fullpath --- Der Zope-Pfad der zu diesem Ordner führt (zb `Intranet/Kundenauftragsinfo`), wird in der Url-erkennung benutzt.
* @param {string} name --- Der Name, welcher auch in der Leiste angezeigt wird.
* @param {string} dbname --- (Wenn vorhanden) der Name der Tabelle in der Datenbank, welche hier dargestellt wird.
* @param {string} url --- Welche Seite aufgerufen werden soll wenn man auf dass Element klickt.
* @param {Array(NavbarNode/Object)} nodes --- Alle Unterordner (wenn sie ein js-Obj sind werden sie als NavbarNode instanziert gespeichert).
*/
class NavbarNode extends NavbarCoreIndex{
opened = true; // Wenn der Unterordner hat, ob sie gerade zu sehen sein sollen
current = false; // Ob gerade die Seite von diesem Eintrag aufgerufen wird.
selfHighlight = false; // Ob gerade in der Suchansicht relevant
nodesHighlight = false; // Ob eine der Nodes in der Suchansicht relevant
constructor(fullpath, name="", dbname="", url="", nodes=[]){
super(...nodes); // Weiterleitung zur Auswertung der Nodes
this.path = fullpath.split("/");
this.name = name;
this.dbname = dbname;
this.url = url;
this.htmlElement = undefined; // Das eigentliche Element selbst
this.htmlContainer = undefined; // Hier sollen die Elemente der Untereren rein kommen
this.htmlTitle = undefined; // Hier ist der Text drinne
this.htmlArrow = undefined; // Das ist der Blaue Pfeil, bzw Punkt
this.current = this.isSiteUrl;
this.opened = this.currentInNodes || (this.current && this.hasNodes);
}
toString(){ // Besseres debuging
return "[NavbarNode for \"" + this.systemname + "\"]";
}
get isSiteUrl(){ // Ob die aktuelle Seite auf diesem Element ist (vorher dadrauf geklickt wurde)
// Der relevante Teil der url von der Seite und des Elements werden auf übereinstimmung getestet
// Der relevante Teil ist alles ohne Domain und Unterseite ("index_html").
// "/Ven/AV/index_html" --> [ "", "Ven", "AV", "index_html" ] --> [ "Ven", "AV" ] --> "Ven/AV"
const pathname = window.location.pathname.split("/").slice(1, -1).join("/");
// "https://www.domain.test/Ven/AV" --> ["https", "", "www.domain.test", "Ven", "AV", "index_html"] --> ["Ven", "AV"] --> "Ven/AV"
const mypathname = this.url.split("/").slice(3, -1).join("/");
return pathname == mypathname;
}
get systemname(){
return this.path[this.path.length -1];
}
get fullpath(){
return this.path.join("/");
}
get navtype(){ // Ob Liste zum erweitern oder Menupunkt
if(this.hasNodes) return "navlist";
else return "naventry";
}
/** Ob eine Node darunter ist, welche current ist */
get currentInNodes(){
for(const node of this.nodes) if(node.currentInNodes || node.current) return true;
return false;
}
get htmlContainerFilled(){
return this.htmlContainer.children.length != 0;
}
/** Erstellt und registriert ein eigenes html-Element, und gibt dieses zurück */
htmlMakeElement(){
// Element
const element = document.createElement("div");
element.classList.add("bullet", this.navtype, this.systemname.replaceAll(" ", "_")); // damit Klasse keine Leerzeichen hat
if(this.opened) element.classList.add("opened"); // So wird erstaml der Status gesetzt
if(this.current) element.classList.add("current");
if(this.currentInNodes) element.classList.add("hascurrent"); // .hascurrent Damit man naher dass andere aktive wiederfindet wenn man dieses hier schließst
element.setAttribute("title", this.name);
// Titel
const head = document.createElement("li");
head.classList.add("bullet_title");
head.addEventListener("click", this.mouseEventhandler.bind(this)); // Wenn man draufklickt
head.addEventListener("contextmenu", this.mouseEventhandler.bind(this));
element.appendChild(head);
// Pfeil (oder punkt)
const arrow = document.createElement("span");
arrow.classList.add("bullet_arrow_placeholder");
if(this.hasNodes) arrow.addEventListener("click", this.mouseEventhandlerArrow.bind(this)); // Damit man die Listen aufmachen kann
head.appendChild(arrow);
// Titel Text
const title = document.createElement("span");
title.innerHTML = this.name;
head.appendChild(title);
// Container
element.appendChild(this.htmlMakeContainer(this.opened)); // Unterordner werden nur ondemand erstellt
// Anmelden
this.htmlArrow = arrow;
this.htmlTitle = title;
this.htmlElement = element;
this.updateArrowState(); // Damit der Pfeil in die richtige Richtung zeigt
return element;
}
/** Erstellt und registriert einen eigenen Container für das html-Element, und gibt diesen zurück */
htmlMakeContainer(fill=true){
const container = document.createElement("div");
container.classList.add("bullet_container");
this.htmlContainer = container;
if(fill) this.htmlFillContainer();
return container;
}
/** Befüllt den registrierten Container mit dem html der Nodes */
htmlFillContainer(){
if(this.htmlContainer) this.sendNodeHtml(this.htmlContainer);
}
/** Soll die Suchfunktion ersetzten
* Zurücksetzen möglich mit `.highlightReset()`
* @param {RegExp/string} regex --- Das soll im titel ge-highlighted werden
* @param {Array(string)} inAttributes --- Testet das Regex an diesem Attribut von sich selbst (Dafür muss das eigene Attribut auch immer string sein zb "name", "dbname", "fullpath" oder "systemname")
* @param {bool} hideIfNot --- Wenn nichts ge-highlighted wird unsichbar machen
* @param {bool} includeNodes --- Ob diese funktion bei allen Nodes angewand werden soll
* @returns {bool} --- Ob was ge-highlighted wird (auch wenn nur in den Nodes)
*/
highlightText(regex, inAttributes=["name"], hideIfNot=true, includeNodes=true){
if(typeof regex == "string") regex = new RegExp(regex); // Falls regex string ist (noch kein regex obj)
if(!this.htmlContainerFilled) this.htmlFillContainer(); // Container mit nodes füllen, für highlight brauchen alle ein html element
let matchingTestval = "";
let matchingAttribute = "";
this.selfHighlight = false; // Vorhanden im eigenen Attribut
this.nodesHighlight = false; // Vorhanden in den Attribut der Nodes
for(const attribute of inAttributes){
const testval = this[attribute];
if(regex.test(testval) && !this.selfHighlight){
this.selfHighlight = true;
matchingTestval = testval;
matchingAttribute = attribute;
}
}
if(this.selfHighlight){
// Wenn das Attribut nicht der Name ist (also normal nicht im Titel sein sollte) wird der Titel mit dem namen und den Attribut in Klammern modifiziert: Name (Attribut_hier)
if(matchingAttribute=="name"){
this.htmlTitle.innerHTML = this.name.replace(regex, "$&");
}
else{
this.htmlTitle.innerHTML = `${this.name} (${matchingTestval.replace(regex, "$&")})`;
this.htmlTitle.setAttribute("title", `${this.name} (${matchingTestval})`); // So steht es sicherhaltshalber nochmal wenn man mit der Maus drübergeht
}
this.htmlElement.classList.add("bullet_highlight");
}
// Test für alle Nodes (Aus NavbarCoreIndex)
if(includeNodes) this.nodesHighlight = super.highlightText(regex, inAttributes, hideIfNot);
// Sichtbarkeit wenn nichts ge-highlight (hideIfNot)
if(!(this.selfHighlight || this.nodesHighlight) && hideIfNot) this.htmlElement.classList.add("bullethidden");
else this.htmlElement.classList.remove("bullethidden");
// Container schließen wenn keine Node dabei ist (und umgekehrt)
if(this.nodesHighlight) this.htmlElement.classList.add("opened");
else this.htmlElement.classList.remove("opened");
return this.selfHighlight || this.nodesHighlight;
}
highlightReset(includeNodes=true){
if(!this.htmlContainerFilled) this.htmlFillContainer(); // Container mit nodes füllen, für highlight brauchen alle ein html element
this.htmlTitle.innerHTML = this.name;
this.htmlElement.classList.remove("bullethidden");
this.htmlElement.classList.remove("bullet_highlight");
this.htmlElement.setAttribute("title", this.name);
if(this.opened) this.htmlElement.classList.add("opened"); // Offenheit wie es vorher war
else this.htmlElement.classList.remove("opened");
if(includeNodes){
super.highlightReset(); // Reset für alle Nodes (Aus NavbarCoreIndex)
this.nodesHighlight = false;
}
this.selfHighlight = false;
}
/** Box auf oder zu */
htmlSetContainer(opened=null){
this.opened = ((opened==null) ? !this.opened : opened); // Wenn nicht gesetzt, dann gegenteil von .opened (toogle)
if(this.opened) this.htmlElement.classList.add("opened");
else this.htmlElement.classList.remove("opened");
if(this.opened && !this.htmlContainerFilled) this.htmlFillContainer(); // Wenn offen: Container mit nodes füllen, falls nicht schon passiert
this.updateArrowState();
}
/** Ersatz der Attribut eventhandler (`onclick` und `oncontextmenu`) */
mouseEventhandler(event){
this.openMyUrl(event.type=="contextmenu"); // Wenn Rechtsklick, dann ist newtab true
event.stopPropagation();
event.preventDefault();
}
/** Damit man auf denn Pfeil drücken kann, und sich der Inhalt offenbart */
mouseEventhandlerArrow(event){
// Wenn in der Suche geöffnet, wird ein Klick an die normale Klickfunktion weitergeleitet,
// Sonst wird be einem Klick der Inhalt ganz normal geöffnet
if(!this.nodesHighlight && !this.selfHighlight) this.htmlSetContainer();
else this.mouseEventhandler(event);
event.preventDefault();
event.stopPropagation();
}
openMyUrl(newtab=false){
if(newtab) window.open(this.url, "_blank");
else window.location.href=this.url;
}
/** Damit passt man den Pfeil an */
updateArrowState(opened=this.opened){
if(!this.hasNodes) this.htmlArrow.innerHTML = ``; // Hat sowieso keine nodes, ist also immer ein Punkt
else if(opened) this.htmlArrow.innerHTML = ``; // Offen, Pfeil nach unten
else this.htmlArrow.innerHTML = ``; // Geschlossen, Pfeil nach rechts
}
}
/** Nimmt versehentlichen Regex syntax raus */
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
/* ####### IMPLEMENTIERUNG AB HIER #######
Alle globalen Funktions- und Variabelennamen benutzen ab hier die Präfixe `navbar` oder `navbar_` um Kollisionen mit bereits vorhandenen Namen zu vermeiden.
NavbarIndex ist eine Instanz von NavbarCoreIndex welches die navbar Elemente mit Instanzen der eigenen Unterklasse NavbarNode verwaltet.
NavbarCoreIndex wird mit dem umgewandelten json index von `layout_venjakob_folders` gefüttert und sollte alles weitere automatisch initalisieren.
Die erstellung und registrierung der html-Elemente wird über `navbarIndex.sendNodeHtml(navbarDefaultContentElement)` gemacht wobei `navbarDefaultContentElement` das
exsistierende html element ist, dass mit den Nav-Elementen gefüllt wird.
Um die Ladezeiten zu verkürzen wird der von `layout_venjakob_folders` erstellte Index in den LocalStorage geladen.
Der Index soll alle 24 Stunden, nach einem Login/Logout oder nach einem Ladefehler neu geholt werden.
*/
// (in Millisekunden) wie lange ein index halten soll (1000*60*60*24 = 24 Stunden, 0 = Würde Index effektiv deaktivieren)
const navbarIndex_validtimeframe = 1000*60*60*24;
// Falls sich was im Ausgabeformat von `layout_venjakob_folders` geändert hat hier die aktuelle Zeit eintragen.
// Wird einen neuen Index holen wenn dieser Älter als dieser Zeitstempel ist, egal ob er noch in _validtimeframe
const navbarIndex_expireAllwaysAt = Number(new Date("2024-08-23 10:13:10"));
//window.addEventListener("load", function() {})
async function navbar_init(){
globalThis.navbarIndex = null; // Wird als json string im local storage unter unter "navbar_index" gespeichert
globalThis.navbarSearchBlock = true; // Wenn gerade Geladet wird
globalThis.navbarSearchInputElement = document.getElementById("navbar_search_input");
globalThis.navbarDefaultContentElement = document.getElementById("navbar_default_content");
globalThis.navbarSearchCancelElement = document.getElementById("navbar_search_cancel");
globalThis.navbarSearchHintsElement = document.getElementById("navbar_search_hint");
globalThis.navbarRefreshElement = document.getElementById("navbar_refresh");
// Erkennung ob unangemeldet
// Schaut ob im dom ein Element mit dem Benutzernamen von Zope erzeugt wurde.
// Erkennung des "__user_name" Cookies währe eine bessere Methode der Erkennung, ist aber leider "httponly".
globalThis.isNotLoggedIn = document.querySelectorAll("#logon .user").length == 0;
// Voreinstellungen für unangemeldete Nutzer
if(globalThis.isNotLoggedIn){
navbar_killIndex(); // Damit der Index aus dem angemeldeten Zustand nicht auftaucht
document.querySelectorAll(".navbar_search_box")[0].classList.add("noheight", "transparent"); // Suchleiste verstecken
}
await navbar_loadIndex();
// Löscht den durch navbar_loadIndex() gespeicherten Index wieder (im unangemeldeten Zustand), damit nach dem einloggen ein neuer Index bezogen wird.
// Da der Index für unangemeldete nur 5ms dauert ist es auch akzeptabel ihn wiederholt anzufragen.
// Die bessere Alternative währe es sich im localStorage auch zu vermerken dass dieser Index für logout bestimmt ist, und ihn nach der anmeldung neu anzufragen.
// Aber es währe besser zu warten ob sich insgesamt eine bessere Möglichkeit ergibt das zu Verwalten.
if(globalThis.isNotLoggedIn) navbar_killIndex();
// Nach dem Laden freigeben
globalThis.navbarSearchInputElement.removeAttribute("disabled");
}
/** Ladet den index (wenn nicht schon geladen) */
async function navbar_loadIndex(){
// Schauen ob der gespeicherte index vorhanden und valide ist
const navbarIndex = localStorage.getItem("navbar_index");
const navbarIndex_recived = Number(localStorage.getItem("navbar_index_recived")); // Hinweiß: Wegen Number() wird wenn nicht vorhanden wird 0 sein und nicht `null`
const currentTime = Number(new Date());
const timepassed = currentTime - navbarIndex_recived;
if(
navbarIndex != null && // Ist überhaupt ein Index vorhanden?
timepassed < navbarIndex_validtimeframe && // Ist dieser Index im Zeitfenster?
Number(navbarIndex_recived) > navbarIndex_expireAllwaysAt // Ist ein Zeitpunkt erreicht wo `layout_venjakob_folders` was anderes herausgibt?
){
try{
navbar_load(JSON.parse(navbarIndex)); // Aus der Cache ziehen
return;
}catch{
console.log("Erzwinge Index Update");
navbar_killIndex(); // Wenn veraltet, kann nicht laden
}
}
// Neuen index ziehen
try{
const data = await fetch("layout_venjakob_folders", {cache: "no-cache"}); // Index daten laden
const json = await data.json(); // Index daten umwandeln (json umwandlung ist auch eine auch asynchrone operation)
navbar_load(json);
localStorage.setItem("navbar_index_recived", Number(new Date()) ); // Wann es Zeit wird für eine neuen index
localStorage.setItem("navbar_index", JSON.stringify(json)); // Speichern für diese Sitzung
}catch(exeption){
console.log(exeption);
console.log("Fehler beim beziehen des Searchtool-pools");
}
}
/** Setzt denn eintrag in der cache zurück */
function navbar_killIndex(){
localStorage.removeItem("navbar_index_recived");
localStorage.removeItem("navbar_index");
}
function navbar_load(data){
// Index laden
navbarIndex = new NavbarCoreIndex(...data);
// In die Navbar einsetzen
navbarDefaultContentElement.innerHTML = "";
navbarIndex.sendNodeHtml(navbarDefaultContentElement);
navbarSearchBlock = false; // Jetzt darf gesucht werden
// Damit das Lesezeichen wie gewohnt aussieht
const lesezeichen = navbarIndex.nodes.find((node) => node.systemname=="Lesezeichen");
if(lesezeichen){
lesezeichen.nodesNeverHighlight = true; // So wird Suche für alle Unterelemente von Lesezeichen abgeschaltet, der Eintrag Lesezeichen an sich kann aber immer noch gesucht werden
lesezeichen.htmlSetContainer(true); // Damit die erste ebene vom Lesezeichen immer offen ist.
}
}
/** Startet die Suche mit query */
function navbar_search(query){
navbarIndex.highlightReset();
if(query == ""){ // "" um die Suche abzuschalten
navbarSearchInputElement.value = "";
navbarSearchCancelElement.classList.add("transparent");
navbarDefaultContentElement.classList.remove("searchmode");
return;
}
query = query.trim(); // Versehentliche Leerschritte und regex syntax wegmachen
query = escapeRegExp(query);
navbarSearchHintsElement.innerHTML = "Suche...";
navbarSearchCancelElement.classList.remove("transparent");
navbarDefaultContentElement.classList.add("searchmode");
navbarSearchBlock = true;
if(navbarIndex != null){
if(query.length < 3){ // Zu umfangreiche suche (aber die searchbank ist schon geladen, was die nächste Eingabe schneller macht)
navbarSearchHintsElement.innerHTML = "Mehr als 2 Zeichen benutzen";
}else{
let results = navbarIndex.highlightText(new RegExp(query, "i"), ["name","dbname"]); // new RegExp(query, "i") "i" um groß/klein zu ignorieren
//if(!results) results = navbarIndex.highlightText(new RegExp("^"+query+"$", "i"), "dbname"); // Wenn nichts gefunden, wird geschaut ob der Datenbank name exakt irgentwo vorhanden ist
if(!results){
navbarSearchHintsElement.innerHTML = "Nichts gefunden";
navbarIndex.highlightReset();
}
else{
navbarSearchHintsElement.innerHTML = "";
}
}
}else{
navbarSearchHintsElement.innerHTML = "Fehler beim Suchen";
}
// Erkennen ob nichts gefunden
navbarSearchBlock = false;
}
/** Damit der X Button funktioniert */
function navbar_cancelClick(){
if(navbarSearchCancelElement.classList.contains("transparent")){
return;
}else{
navbar_search(""); // Suche ausschalten
navbar_removeArrowIndex();
}
}
/** Den Gespeicherten Suchindex aufrischen (für ↻ Button) */
function navbar_refresh(){
try{ // Im try da bei einem kaputten Index die Suchfunktion nicht richtig resettet werden kann
navbar_search(""); // Suche resetten
}catch(exeption){
console.log("Alter Suchindex beschädigt")
}
navbarSearchBlock = true; // Verhindert dass beim laden noch gesucht wird, wird von navbar_loadIndex() wieder freigeschaltet
navbarRefreshElement.setAttribute("disabled", "true");
navbarDefaultContentElement.innerHTML = `
...
`; // Platzhalter bis laden fertig ist
navbar_killIndex();
navbar_loadIndex();
}
let navbar_arrowNavigationIndex = 0;
/** Duch die Eingabe in der Suchleiste, steuert dann den Suchaufruf. */
function navbar_searchKeyPress(event){
event.stopPropagation();
//navbarSearchHintsElement.innerHTML = "Suche mit Enter starten"; // Falls da noch eine Fehlermeldung steht wird die wieder hiermit überschrieben
let workingList = navbarIndex.allNodesVisible;
if(event.code == "Enter") { // Suche starten via Enter
if(navbar_arrowNavigationIndex == 0){ // Wenn nicht mit den Pfeiltasten aus der Suchleiste raus
if(navbarSearchBlock){ // Verhindern das parallel aufgerufen wird
event.preventDefault();
return;
}
navbar_search(navbarSearchInputElement.value);
}else{
workingList[navbar_arrowNavigationIndex - 1].openMyUrl(event.ctrlKey);
return;
}
}
else if(event.code == "Escape"){ // Suche und Pfeilauswahl beenden via Escape
navbarSearchInputElement.blur();
navbar_search("");
navbar_endArrowNavigation();
}
else if(event.code == "ArrowDown" || event.code == "ArrowUp" ){ // Navigation mit Pfeiltasten
if(event.code == "ArrowDown"){
navbar_arrowNavigationIndex += 1;
}else if(event.code == "ArrowUp"){
navbar_arrowNavigationIndex -= 1;
}
navbar_removeArrowIndex();
if(navbar_arrowNavigationIndex < 0){
navbar_arrowNavigationIndex = workingList.length;
}else if(navbar_arrowNavigationIndex > workingList.length){
navbar_arrowNavigationIndex = 0;
}
if(navbar_arrowNavigationIndex != 0){
navbarSearchInputElement.parentNode.classList.add("fakeBlur");
const nodeAtIndex = workingList[navbar_arrowNavigationIndex - 1]
nodeAtIndex.htmlElement.classList.add("fakeFocus");
// Logik ob gescrollt werden soll
const distanceToTop = nodeAtIndex.htmlTitle.getBoundingClientRect().top;
const saveAreaHeight = 60; // Soviel soll zusätzlich gescrollt werden
//console.log(distanceToTop, window.innerHeight, workingList[navbar_arrowNavigationIndex - 1].name);
if(distanceToTop + saveAreaHeight >= window.innerHeight){
window.scrollTo(0, window.scrollY + (distanceToTop + saveAreaHeight) - window.innerHeight);
}else if(distanceToTop < saveAreaHeight){
window.scrollTo(0, window.scrollY + (distanceToTop - saveAreaHeight));
}
}else{
navbarSearchInputElement.parentNode.classList.remove("fakeBlur");
window.scrollTo(0,0);
}
event.preventDefault();
}else if( event.ctrlKey || event.altKey || event.shiftKey){ // Beim halten dieser Tasten sollte nicht weiter passieren, ist sonst etwas irritierend
return;
}else{ // Jeder andere Kopf beendet dann die Auswahl über die Pfeiltasten
navbar_endArrowNavigation();
}
// ******* Hier könnte man eine Navigation mit den Pfeiltasten bauen *******
}
function navbar_endArrowNavigation(){
navbar_arrowNavigationIndex = 0;
navbar_removeArrowIndex();
window.scrollTo(0,0);
}
/** Makierung für dass durch die Pfeiltasten auserwählte Element entfernen. */
function navbar_removeArrowIndex(){
const oldArrow = document.querySelector(".fakeFocus");
if(oldArrow){
oldArrow.classList.remove("fakeFocus");
}
navbarSearchInputElement.parentNode.classList.remove("fakeBlur");
}
// Für die Anzeigen
function navbar_showHint(){
navbarSearchHintsElement.innerHTML = "";
navbarSearchHintsElement.classList.remove("transparent");
}
function navbar_hideHint(){
navbarSearchHintsElement.classList.add("transparent");
navbarSearchHintsElement.innerHTML = "";
}