(function () { "use strict"; // Alle unterstützten Suchformulare finden var forms = document.querySelectorAll( "#header-suche, #header-suche-eng, #header-suche-desktop" ); if (!forms.length) { return; } // Elemente, deren Inhalte nicht durchsucht werden sollen var excludedTags = [ "SCRIPT", "STYLE", "NOSCRIPT", "TEXTAREA", "INPUT", "SELECT", "BUTTON", "OPTION" ]; // Bereits vorhandene Hervorhebungen entfernen function removeHighlights() { var marks = document.querySelectorAll( "mark.header-suche-highlight" ); marks.forEach(function (mark) { var parent = mark.parentNode; if (parent) { parent.replaceChild( document.createTextNode(mark.textContent), mark ); parent.normalize(); } }); } // Inhaltsbereich zwischen CONTENT START und CONTENT ENDE ermitteln function getContentArea() { var walker = document.createTreeWalker( document.body, NodeFilter.SHOW_COMMENT, null ); var startComment = null; var endComment = null; var comment; while ((comment = walker.nextNode())) { var value = comment.nodeValue.trim(); if (value === "CONTENT START") { startComment = comment; } if (value === "CONTENT ENDE" && startComment) { endComment = comment; break; } } if (!startComment || !endComment) { return null; } var range = document.createRange(); range.setStartAfter(startComment); range.setEndBefore(endComment); return range; } // Prüfen, ob ein Textknoten durchsucht werden darf function isAllowedNode(node) { var parent = node.parentElement; while (parent) { // Bestimmte HTML-Elemente grundsätzlich ausschließen if (excludedTags.indexOf(parent.tagName) !== -1) { return false; } // Bereits hervorgehobene Texte ausschließen if ( parent.classList && parent.classList.contains("header-suche-highlight") ) { return false; } // Unsichtbare Elemente ausschließen if (parent.hidden) { return false; } var style = window.getComputedStyle(parent); if ( style.display === "none" || style.visibility === "hidden" || style.visibility === "collapse" || style.opacity === "0" || style.contentVisibility === "hidden" ) { return false; } parent = parent.parentElement; } // Prüfen, ob der Text tatsächlich eine sichtbare Fläche besitzt var range = document.createRange(); range.selectNodeContents(node); var rects = range.getClientRects(); for (var i = 0; i < rects.length; i++) { if (rects[i].width > 0 && rects[i].height > 0) { return true; } } return false; } // Prüfen, ob ein Textknoten vollständig im CONTENT-Bereich liegt function isInsideContentArea(node, contentRange) { try { var startPosition = contentRange.comparePoint(node, 0); var endPosition = contentRange.comparePoint( node, node.nodeValue.length ); return startPosition >= 0 && endPosition <= 0; } catch (e) { return false; } } // Alle Vorkommen des Suchbegriffs im CONTENT-Bereich finden // Weiche Trennzeichen (­) werden dabei ignoriert function findMatches(term) { var matches = []; if (!term) { return matches; } // Weiche Trennzeichen auch im Suchbegriff entfernen var searchTerm = term.replace(/\u00AD/g, "").toLowerCase(); if (!searchTerm) { return matches; } var contentRange = getContentArea(); if (!contentRange) { return matches; } var walker = document.createTreeWalker( document.body, NodeFilter.SHOW_TEXT, { acceptNode: function (node) { if ( !node.nodeValue || !isAllowedNode(node) || !isInsideContentArea(node, contentRange) ) { return NodeFilter.FILTER_REJECT; } // Weiche Trennzeichen für die Suche entfernen var normalizedText = node.nodeValue.replace( /\u00AD/g, "" ).toLowerCase(); if (normalizedText.indexOf(searchTerm) !== -1) { return NodeFilter.FILTER_ACCEPT; } return NodeFilter.FILTER_REJECT; } } ); var node; while ((node = walker.nextNode())) { var text = node.nodeValue; // Weiche Trennzeichen entfernen und ursprüngliche // Zeichenpositionen für die Hervorhebung merken var normalizedText = ""; var originalPositions = []; for (var i = 0; i < text.length; i++) { if (text.charAt(i) === "\u00AD") { continue; } normalizedText += text.charAt(i); originalPositions.push(i); } var lowerText = normalizedText.toLowerCase(); var position = 0; while ( (position = lowerText.indexOf( searchTerm, position )) !== -1 ) { matches.push({ node: node, start: originalPositions[position], end: originalPositions[ position + searchTerm.length - 1 ] + 1 }); position += searchTerm.length; } } return matches; } // Ein einzelnes Vorkommen hervorheben function highlightMatch(matches, index) { if (index < 0 || index >= matches.length) { return false; } var match = matches[index]; var node = match.node; if (!node.parentNode) { return false; } var text = node.nodeValue; var before = text.substring(0, match.start); var found = text.substring(match.start, match.end); var after = text.substring(match.end); var fragment = document.createDocumentFragment(); if (before) { fragment.appendChild( document.createTextNode(before) ); } var mark = document.createElement("mark"); mark.className = "header-suche-highlight"; mark.textContent = found; mark.style.backgroundColor = "#ffff00"; mark.style.color = "#000000"; mark.style.padding = "0"; // Innerhalb der Klasse .header den Textschatten entfernen if (node.parentElement.closest(".header")) { mark.style.setProperty("text-shadow", "none", "important"); } fragment.appendChild(mark); if (after) { fragment.appendChild( document.createTextNode(after) ); } node.parentNode.replaceChild(fragment, node); // Zum gefundenen Text springen mark.scrollIntoView({ behavior: "smooth", block: "center" }); return true; } // Dialogfenster bei keinen weiteren Ergebnissen function showNoMoreResults(term, state) { // Bereits geöffnetes Dialogfenster vermeiden if (document.getElementById("header-suche-dialog")) { return; } var isEnglish = true; // Hintergrund var overlay = document.createElement("div"); overlay.id = "header-suche-dialog"; overlay.style.position = "fixed"; overlay.style.top = "0"; overlay.style.left = "0"; overlay.style.width = "100%"; overlay.style.height = "100%"; overlay.style.backgroundColor = "rgba(52, 52, 52, 0.70)"; overlay.style.zIndex = "999999"; overlay.style.display = "flex"; overlay.style.alignItems = "center"; overlay.style.justifyContent = "center"; overlay.style.padding = "15px"; overlay.style.boxSizing = "border-box"; // Dialogfenster var dialog = document.createElement("div"); dialog.style.backgroundColor = "#FFFFFF"; dialog.style.color = "#000000"; dialog.style.width = "100%"; dialog.style.maxWidth = "420px"; dialog.style.padding = "25px"; dialog.style.boxSizing = "border-box"; dialog.style.borderRadius = "8px"; dialog.style.boxShadow = "0 5px 25px rgba(0,0,0,0.3)"; dialog.style.textAlign = "center"; dialog.style.fontFamily = "inherit"; // Überschrift var heading = document.createElement("div"); heading.textContent = isEnglish ? "All contents scanned" : "Alle Inhalte gescannt"; heading.style.fontSize = "18px"; heading.style.fontWeight = "bold"; heading.style.marginBottom = "15px"; // Beschreibung var message = document.createElement("div"); message.textContent = isEnglish ? "There are no more matches on this page. Do you want to search all other pages or images instead?" : "Es gibt keine weiteren Vorkommnisse auf dieser Seite. Möchtest Du stattdessen auf allen Seiten oder nach Bildern suchen?"; message.style.fontSize = "15px"; message.style.lineHeight = "1.5"; message.style.marginBottom = "25px"; // Zusätzliches DIV mit KITT oberhalb der Schaltflächen var diary = document.createElement("div"); diary.className = "message-kitt"; diary.style.width = "180px"; diary.style.height = "150px"; diary.style.margin = "-20px auto 20px auto"; diary.style.flexShrink = "0"; // Schaltflächen-Container var buttons = document.createElement("div"); buttons.style.display = "flex"; buttons.style.flexWrap = "wrap"; buttons.style.justifyContent = "center"; buttons.style.gap = "10px"; // Schaltfläche erstellen function createButton(text, backgroundColor) { var button = document.createElement("button"); button.type = "button"; button.textContent = text; button.style.backgroundColor = backgroundColor; button.style.color = "#ffffff"; button.style.border = "none"; button.style.borderRadius = "4px"; button.style.padding = "11px 16px"; button.style.fontSize = "14px"; button.style.cursor = "pointer"; button.style.height = "44px"; button.style.minHeight = "44px"; button.style.boxSizing = "border-box"; button.style.display = "flex"; button.style.alignItems = "center"; button.style.justifyContent = "center"; button.style.flex = "1 1 auto"; return button; } // Dialog schließen function closeDialog() { document.removeEventListener( "keydown", handleEscape ); if (overlay.parentNode) { overlay.parentNode.removeChild(overlay); } } // ESC-Taste function handleEscape(event) { if (event.key === "Escape") { cancelSearch(); } } // Alle Unterseiten durchsuchen var searchButton = createButton( isEnglish ? "All pages" : "Alle Seiten", "#000040" ); searchButton.addEventListener("click", function () { closeDialog(); window.location.href = "suche.php?lang=eng&term=" + encodeURIComponent('"' + term + '"'); }); // Bilder durchsuchen var imageButton = createButton( isEnglish ? "Images" : "Bilder", "#000040" ); imageButton.addEventListener("click", function () { closeDialog(); window.location.href = "suche.php?lang=eng&mode=images&term=" + encodeURIComponent('"' + term + '"'); }); // Abbrechen var cancelButton = createButton( isEnglish ? "Cancel" : "Abbrechen", "#777777" ); // Beide Suchbuttons gleich breit searchButton.style.flex = "1 1 calc(50% - 5px)"; imageButton.style.flex = "1 1 calc(50% - 5px)"; // Abbrechen in eine eigene Zeile cancelButton.style.flex = "1 1 100%"; function cancelSearch() { closeDialog(); // Wieder zum ersten Ergebnis springen state.currentIndex = 0; removeHighlights(); state.matches = findMatches(term); if (state.matches.length > 0) { highlightMatch(state.matches, 0); } } cancelButton.addEventListener("click", cancelSearch); // Elemente zusammensetzen buttons.appendChild(searchButton); buttons.appendChild(imageButton); buttons.appendChild(cancelButton); dialog.appendChild(heading); dialog.appendChild(message); dialog.appendChild(diary); dialog.appendChild(buttons); overlay.appendChild(dialog); // Klick auf den Hintergrund schließt den Dialog overlay.addEventListener("click", function (event) { if (event.target === overlay) { cancelSearch(); } }); document.addEventListener("keydown", handleEscape); document.body.appendChild(overlay); // Fokus auf die erste Schaltfläche setzen searchButton.focus(); } // Jedes Formular separat initialisieren forms.forEach(function (form) { var input = form.querySelector('input[name="term"]'); var mode = form.querySelector('select[name="mode"]'); if (!input || !mode) { return; } // Eigener Suchstatus für jedes Formular var state = { currentTerm: "", currentIndex: -1, matches: [] }; // Highlight bei Klick oder Tippen außerhalb der Suche entfernen document.addEventListener("click", function (event) { // Klicks innerhalb eines Suchformulars oder des Dialogfensters ignorieren if ( event.target.closest("#header-suche, #header-suche-eng, #header-suche-desktop, #header-suche-dialog") ) { return; } removeHighlights(); // Suchstatus zurücksetzen state.currentTerm = ""; state.currentIndex = -1; state.matches = []; }); // Suche neu starten function startSearch(term) { removeHighlights(); state.currentTerm = term; state.currentIndex = -1; state.matches = findMatches(term); if (state.matches.length > 0) { state.currentIndex = 0; highlightMatch( state.matches, 0 ); } } // Suche beim Tippen starten input.addEventListener("input", function () { if (mode.value !== "here") { return; } var term = input.value.trim(); if (term !== state.currentTerm) { startSearch(term); } }); // Formular absenden form.addEventListener("submit", function (event) { // Normale Suche für Inhalte und Bilder if (mode.value !== "here") { return; } // Suche auf dieser Seite event.preventDefault(); var term = input.value.trim(); if (!term) { return; } // Falls der Begriff noch nicht gesucht wurde if (term !== state.currentTerm) { startSearch(term); return; } // Falls keine Ergebnisse vorhanden sind if (state.matches.length === 0) { showNoMoreResults(term, state); return; } // Zum nächsten Vorkommen springen state.currentIndex++; // Hervorhebungen entfernen und Fundstellen neu ermitteln removeHighlights(); state.matches = findMatches(term); if (state.currentIndex < state.matches.length) { highlightMatch( state.matches, state.currentIndex ); } else { showNoMoreResults(term, state); } }); }); })();