1/* 2 * FrontendâSkript für das Recent Posts TilesâElement. 3 * 4 * Dieses Skript steuert die Kachelansicht: es lädt Beiträge per RESTâAPI 5 * nach, filtert anhand von Kategorien und zeigt einen âMehr ladenââButton 6 * an, wenn zusätzliche Seiten verfügbar sind. Die Kategorienliste wird 7 * serverseitig lokalisiert (rptpData.categories). Für jede Anfrage 8 * wird ein Nonce (rptpData.nonce) gesetzt, um die WordPressâRESTâAPI 9 * vor CSRFâAngriffen zu schützen. Die Ausgabe wird clientseitig 10 * generiert, damit der DOM minimal bleibt und CSS/Animationsklassen 11 * reibungslos funktionieren. 12 */ 13 14(function ($) { 15 'use strict'; 16 17 // Beende, wenn die notwendigen Daten nicht vorhanden sind. 18 if ( typeof rptpData === 'undefined' ) { 19 return; 20 } 21 22 /** 23 * Erstellt das Markup für eine einzelne Kachel. 24 * 25 * @param {Object} item Ein Beitrag mit Titel, Permalink, Bild, Datum und Kategorien. 26 * @param {number} index Position des Eintrags in der aktuellen Reihe (für Delay). 27 * @returns {jQuery} jQueryâObjekt des <article> Elements. 28 */ 29 function buildTile(item, index) { 30 const delay = ( index % 3 ) * 0.6; // 0s, 0.6s, 1.2s 31 const $article = $( '<article>', { 32 class: 'post fusion-column column col col-lg-4 col-md-4 col-sm-4 fade-slide up show', 33 style: 'transition-delay:' + delay + 's;' 34 } ); 35 const partnerHtml = typeof item.partner_html === 'string' ? item.partner_html.trim() : ''; 36 const $meta = $( '<p>', { class: 'meta' } ); 37 38 // Kategorien in der Kachel ausgeben â wie bei der serverseitigen Ausgabe 39 item.categories.forEach( function ( cat, idx ) { 40 let name = ''; 41 let href = ''; 42 43 if ( typeof cat === 'string' ) { 44 name = cat; 45 } else if ( cat && typeof cat === 'object' ) { 46 name = cat.name || ''; 47 href = cat.link || ''; 48 } 49 50 if ( ! name ) { 51 return; 52 } 53 54 if ( idx > 0 ) { 55 $meta.append( 56 $( '<span>', { class: 'fusion-inline-sep', text: '|' } ) 57 ); 58 } 59 60 const $catLink = href 61 ? $( '<a>', { 62 href: href, 63 rel: 'category tag', 64 text: name 65 } ) 66 : $( '<span>', { text: name } ); 67 68 $meta.append( $catLink ); 69 } ); 70 71 // Bild 72 if ( item.image_url ) { 73 const $img = $( '<img>', { 74 src: item.image_url, 75 class: 'attachment-recent-posts size-recent-posts lazyautosizes', 76 alt: '' 77 } ); 78 const $anchor = $( '<a>', { 79 href: item.permalink, 80 'aria-label': item.title, 81 class: 'hover-type-none' 82 } ).append( $img ); 83 const $li = $( '<li>' ).append( $anchor ); 84 const $ul = $( '<ul>', { class: 'slides' } ).append( $li ); 85 const $slider = $( '<div>', { 86 class: 'fusion-flexslider fusion-flexslider-loading flexslider flexslider-hover-type-none' 87 } ).append( $ul ); 88 89 if ( partnerHtml ) { 90 const $overlay = $( '<div>', { class: 'recent-post-tile__overlay' } ); 91 $overlay.append( $( partnerHtml ) ); 92 $li.append( $overlay ); 93 } 94 95 $article.append( $slider ); 96 } 97 // Inhalt 98 const $content = $( '<div>', { class: 'recent-posts-content' } ); 99 const $updated = $( '<span>', { class: 'updated', style: 'display:none;' } ).text( item.date ); 100 const $title = $( '<h4>', { class: 'entry-title' } ).append( $( '<a>', { href: item.permalink, text: item.title } ) ); 101 102 $content.append( $updated, $title ); 103 104 if ( $meta.children().length ) { 105 $content.append( $meta ); 106 } 107 108 if ( ! item.image_url && partnerHtml ) { 109 $content.append( $( partnerHtml ) ); 110 } 111 112 $article.append( $content ); 113 return $article; 114 } 115 116 function initInstance($root) { 117 const $posts = $root.find( '.fusion-recent-posts' ).first(); 118 const $container = $root.find( '.rptp-post-container' ).first(); 119 let postLimit = parseInt( $posts.data( 'post-limit' ), 10 ); 120 let activeCatId = 0; 121 const categoryMode = String( $posts.data( 'category-mode' ) || 'all' ); 122 const categoryIds = String( $posts.attr( 'data-category-ids' ) || '' ); 123 124 if ( isNaN( postLimit ) || postLimit < 1 ) { 125 postLimit = 0; 126 } 127 128 /** 129 * Beiträge vom Server abrufen und das Grid aktualisieren. 130 * 131 * @param {boolean} reset Gibt an, ob vorhandene Kacheln gelöscht werden sollen. 132 */ 133 function fetchPosts(reset) { 134 const params = new URLSearchParams(); 135 if ( postLimit > 0 ) { 136 params.append( 'limit', postLimit ); 137 } 138 if ( activeCatId && activeCatId > 0 ) { 139 params.append( 'category', activeCatId ); 140 } 141 if ( categoryMode !== 'all' ) { 142 params.append( 'category_mode', categoryMode ); 143 params.append( 'category_ids', categoryIds ); 144 } 145 const url = rptpData.restUrl + '?' + params.toString(); 146 fetch( url, { 147 headers: { 148 'X-WP-Nonce': rptpData.nonce 149 } 150 } ) 151 .then( function ( resp ) { 152 return resp.json(); 153 } ) 154 .then( function ( res ) { 155 if ( reset ) { 156 $container.empty(); 157 } 158 if ( Array.isArray( res.data ) && res.data.length ) { 159 res.data.forEach( function ( item, idx ) { 160 const tile = buildTile( item, idx ); 161 $container.append( tile ); 162 } ); 163 } 164 } ) 165 .catch( function ( err ) { 166 // Fehler im Backend protokollieren, aber die Nutzeroberfläche nicht 167 // abbrechen. In einer realen Implementierung könnte hier eine 168 // Benachrichtigung eingeblendet werden. 169 console.error( 'Recent Posts Tiles:', err ); 170 } ); 171 } 172 173 /** 174 * KategorieâFilter initialisieren. Fügt KlickâHandler hinzu und
175 * markiert den aktiven Filter. Beim Wechsel des Filters wird die 176 * Seitennummer zurückgesetzt und die Beiträge neu geladen. 177 */ 178 function initFilters() { 179 const $links = $root.find( '.rptp-filter-link' ); 180 $links.on( 'click', function ( e ) { 181 e.preventDefault(); 182 const $this = $( this ); 183 const catId = parseInt( $this.data( 'cat-id' ), 10 ); 184 if ( catId === activeCatId ) { 185 return; 186 } 187 activeCatId = catId; 188 $links.removeClass( 'active' ); 189 $this.addClass( 'active' ); 190 fetchPosts( true ); 191 } ); 192 } 193 194 initFilters(); 195 } 196 197 /** 198 * Initialisierung. Filter und âMehr ladenââButton konfigurieren und die 199 * ersten Daten laden. 200 */ 201 $( document ).ready( function () { 202 $( '.rptp-container' ).each( function () { 203 initInstance( $( this ) ); 204 } ); 205 } ); 206})( jQuery );
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.