1/** 2 * Live Filter for Recent Pages View 3 * Filters articles in real-time based on search input 4 */ 5 6(function (Drupal, once) { 7 'use strict'; 8 9 Drupal.behaviors.liveFilter = { 10 attach: function (context, settings) { 11 // Only run on the "Alle Seiten" page 12 const pageDisplay = once('live-filter', '.view-recent-pages.view-display-id-page_1', context); 13 14 if (pageDisplay.length === 0) { 15 return; 16 } 17 18 pageDisplay.forEach(function(viewElement) { 19 const searchInput = viewElement.querySelector('input[name="title"]'); 20 const articles = viewElement.querySelectorAll('.views-row'); 21 const emptyMessage = document.createElement('div'); 22 23 // Setup empty message 24 emptyMessage.className = 'view-empty text-center py-5'; 25 emptyMessage.innerHTML = '<p class="lead">Keine Artikel gefunden.</p>'; 26 emptyMessage.style.display = 'none'; 27 28 const contentWrapper = viewElement.querySelector('.view-content'); 29 if (contentWrapper && contentWrapper.parentNode) { 30 contentWrapper.parentNode.insertBefore(emptyMessage, contentWrapper.nextSibling); 31 } 32 33 if (!searchInput || articles.length === 0) { 34 return; 35 } 36 37 // Disable form submission - we filter on the fly 38 const form = searchInput.closest('form'); 39 if (form) { 40 form.addEventListener('submit', function(e) { 41 e.preventDefault(); 42 return false; 43 }); 44 } 45 46 // Filter function 47 function filterArticles() { 48 const searchTerm = searchInput.value.toLowerCase().trim(); 49 let visibleCount = 0; 50 51 articles.forEach(function(article) { 52 const titleElement = article.querySelector('.node__title a, h2 a, h3 a'); 53 const teaserElement = article.querySelector('.field--name-field-teaser-text, .node__content'); 54 55 if (!titleElement) { 56 return; 57 } 58 59 const title = titleElement.textContent.toLowerCase(); 60 const teaser = teaserElement ? teaserElement.textContent.toLowerCase() : ''; 61 62 // Check if search term is in title or teaser 63 if (searchTerm === '' || title.includes(searchTerm) || teaser.includes(searchTerm)) { 64 article.style.display = ''; 65 visibleCount++; 66 } else { 67 article.style.display = 'none'; 68 } 69 }); 70 71 // Show/hide empty message 72 if (visibleCount === 0 && searchTerm !== '') { 73 emptyMessage.style.display = 'block'; 74 if (contentWrapper) { 75 contentWrapper.style.display = 'none'; 76 } 77 } else { 78 emptyMessage.style.display = 'none'; 79 if (contentWrapper) { 80 contentWrapper.style.display = ''; 81 } 82 } 83 84 // Hide pager if filtering 85 const pager = viewElement.querySelector('.pager'); 86 if (pager) { 87 if (searchTerm !== '') { 88 pager.style.display = 'none'; 89 } else { 90 pager.style.display = ''; 91 } 92 } 93 } 94 95 // Add event listeners 96 searchInput.addEventListener('input', filterArticles); 97 searchInput.addEventListener('keyup', filterArticles); 98 99 // Clear button functionality 100 const resetButton = viewElement.querySelector('input[type="reset"], .form-actions input[value*="ücksetzen"]'); 101 if (resetButton) { 102 resetButton.addEventListener('click', function() { 103 setTimeout(function() { 104 searchInput.value = ''; 105 filterArticles(); 106 }, 10); 107 }); 108 } 109 110 // Initial filter (in case there's a value from GET params) 111 if (searchInput.value) { 112 filterArticles(); 113 } 114 }); 115 } 116 }; 117 118})(Drupal, once);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.