PageSourceSearch

https://www.dekanat-hochsauerland-west.de/wp-content/themes/flib/t…?ver=51a6dd162072a3ba59dd7d30fba9add9

js dekanat-hochsauerland-west.de collected 2026-10-06 20:04:43 UTC 7,766 bytes, 217 lines download raw bytes

1/**
2 * Highlight Element JavaScript
3 * 
4 * Handles responsive layout adjustments for highlight sections:
5 * - Synchronizes text width with content limits on desktop
6 * - Matches image height to text content height
7 * - Repositions author credits for contain-fit images
8 */
9
10(function(){
11  /** Maximum viewport width considered "mobile" in pixels */
12  const MOBILE_MAX = 768;
13  
14  /**
15   * Checks if the current viewport is mobile size
16   * @returns {boolean} True if window width is <= 768px
17   */
18  const inMobile = () => window.innerWidth <= MOBILE_MAX;
19
20  /**
21   * Gets all highlight wrapper elements from the DOM
22   * Note: Called each time to handle dynamic WordPress content changes
23   * @returns {NodeList} All elements with class 'highlight-wrapper'
24   */
25  function getWrappers(){ return document.querySelectorAll('.highlight-wrapper'); }
26
27  /**
28   * Creates a debounced function that uses requestAnimationFrame for smooth performance
29   * Combines setTimeout debouncing with RAF to prevent layout thrashing during rapid events
30   * 
31   * @param {Function} fn - The function to debounce
32   * @param {number} delay - Debounce delay in milliseconds
33   * @returns {Function} Debounced function that runs on next animation frame
34   */
35  function rafDebounce(fn, delay){
36    let raf = null, t = null, lastArgs = null;
37    const run = () => { raf = null; fn.apply(null, lastArgs || []); };
38    return function(...args){
39      lastArgs = args;
40      if(t){ clearTimeout(t); }
41      t = setTimeout(()=>{ t=null; if(!raf){ raf = requestAnimationFrame(run); } }, delay);
42    };
43  }
44
45  /**
46   * Synchronizes text width with content limit and matches image height to text
47   * 
48   * Desktop behavior:
49   * - Calculates how wide the text should be based on the content limit boundary
50   * - Accounts for whether image is on left or right side
51   * - Sets image height to match the calculated text height
52   * 
53   * Mobile behavior:
54   * - Resets all custom widths/heights to allow natural stacking
55   * 
56   * Uses read-then-write batching to prevent layout thrashing:
57   * - First phase: reads all DOM measurements without writes
58   * - Second phase: applies all DOM changes at once
59   */
60  function syncTextAndImage(){
61    const wMobile = inMobile();
62    const work = []; // batch writes
63
64    getWrappers().forEach(function(wrapper){
65      const textWrap = wrapper.querySelector('.textcontent-wrapper');
66      if (textWrap?.classList.contains('no-images')) return; // skip wrappers without images
67
68      const text = textWrap?.querySelector('.textcontent');
69      const limit = wrapper.querySelector('.highlightWidth .contentlimit');
70      const image = wrapper.querySelector('.image');
71      const content = wrapper.querySelector('.highlight-content');
72      const imageRight = content?.classList.contains('image-right');
73
74      // Missing required elements - reset to defaults
75      if (!text || !limit){
76        work.push(()=>{ 
77          if(text) text.style.width = '';
78          if(image) image.style.height = 'auto';
79        });
80        return;
81      }
82      // Mobile viewport - use natural layout
83      if (wMobile){
84        work.push(()=>{ 
85          text.style.width = '';
86          image.style.height = 'auto';
87        });
88        return;
89      }
90
91      // READS (no writes here)
92      const limitRect = limit.getBoundingClientRect();
93      const limitLeft = limitRect.left;
94      const limitW = limitRect.width;
95      const textWrapRect = textWrap.getBoundingClientRect();
96      const textLeft = textWrapRect.left;
97      const textWrapOuterW = textWrapRect.width;
98
99      let newW;
100      if (imageRight){
101        // Image on right: calculate text width from right edge
102        const rightLimit = limitLeft + limitW;
103        const rightText = textLeft + textWrapOuterW;
104        newW = limitW - Math.abs(rightLimit - rightText);
105      } else {
106        // Image on left: calculate text width from left edge
107        newW = limitW - (limitLeft - textLeft) * -1;
108      }
109
110      // WRITES (batched)
111      work.push(()=>{
112        text.style.width = newW + 'px';
113        if(image) image.style.height = text.offsetHeight + 'px';
114      });
115    });
116
117    work.forEach(fn => fn());
118  }
119
120  /**
121   * Repositions author credit overlay for images with object-fit: contain
122   * 
123   * Problem: When an image uses object-fit: contain, it may have empty space (letterboxing/pillarboxing)
124   * around it. The author credit should be positioned relative to the actual visible image,
125   * not the container edges.
126   * 
127   * Solution: Calculates the scale and gaps around the contained image, then offsets the author
128   * credit accordingly so it appears in the corner of the actual image.
129   * 
130   * Desktop only - on mobile, author credit uses default positioning.
131   */
132  function positionContainAuthors(){
133    const desktop = !inMobile();
134    const work = [];
135
136    getWrappers().forEach(function(wrapper){
137      const auth = wrapper.querySelector('.image-author');
138      if(!auth) return;
139
140      const contain = wrapper.querySelector('.previewimage .contain-image');
141      const img = contain?.querySelector('img');
142      // Reset if missing elements or on mobile
143      if(!contain || !img || !desktop){
144        work.push(()=>{ 
145          auth.style.right = '';
146          auth.style.bottom = '';
147        });
148        return;
149      }
150
151      /**
152       * Calculates and applies the author credit position
153       * Waits for image load to get accurate natural dimensions
154       */
155      const apply = () => {
156        const rect = contain.getBoundingClientRect();
157        const cw = rect.width, ch = rect.height;           // container width/height
158        const nw = img.naturalWidth || cw;                 // natural image width
159        const nh = img.naturalHeight || ch;                // natural image height
160        const scale = Math.min(cw/nw, ch/nh);              // how much the image is scaled
161        const gapX = (cw - nw*scale)/2;                    // horizontal gap (pillarboxing)
162        const gapY = (ch - nh*scale)/2;                    // vertical gap (letterboxing)
163        const inset = 6;                                   // small offset from image edge
164        work.push(()=>{ 
165          auth.style.right = (gapX+inset) + 'px';
166          auth.style.bottom = (gapY+inset) + 'px';
167        });
168        work.forEach(fn => fn());
169      };
170
171      // Apply immediately if loaded, otherwise wait for load event
172      if(img.complete) apply(); 
173      else img.addEventListener('load', apply, { once: true });
174    });
175  }
176
177  /**
178   * Executes all layout adjustment functions
179   * Called on initial page load and on window resize
180   */
181  function runAll(){
182    syncTextAndImage();
183    positionContainAuthors();
184  }
185
186  /**
187   * Initialize on DOM ready
188   * Sets up initial layout and registers resize listener
189   */
190  if (document.readyState === 'loading') {
191    document.addEventListener('DOMContentLoaded', function(){
192      // Lightbox gallery presence is handled globally; just log once if present
193      getWrappers().forEach(function(wrapper){
194        const gallery = wrapper.querySelector('.lightbox-gallery');
195        if(gallery) { /* console.log('Highlight lightbox gallery initialized'); */ }
196      });
197      runAll();
198    });
199  } else {
200    // Document already loaded - run immediately
201    runAll();
202  }
203
204  /**
205   * Handle window resize with optimized debouncing
206   * Uses 80ms delay with RAF to balance responsiveness and performance
207   */
208  const onResize = rafDebounce(runAll, 80);
209  window.addEventListener('resize', onResize);
210
211  /**
212   * Expose layout functions globally for external use (e.g., contact-flag font sizing)
213   */
214  window.syncHighlightTextAndImage = syncTextAndImage;
215  window.positionHighlightContainAuthors = positionContainAuthors;
216
217})();

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.