PageSourceSearch

https://intax.de/files/layout/js/ki-lightbox.js?v=27902920

js intax.de collected 2026-10-04 14:40:56 UTC 17,685 bytes, 717 lines download raw bytes

1/**
2 * KI-Kennzeichnung für intax.de
3 *
4 * Unterstützt:
5 *
6 * 1. manuell gesetzte CSS-Klassen
7 *    ki-generiert
8 *    ki-bearbeitet
9 *    ki-inhalt
10 *
11 * 2. automatische Erkennung über Dateinamen
12 *    --ki-generiert
13 *    --ki-bearbeitet
14 *    --ki-inhalt
15 *
16 * 3. automatische Erkennung über Ordnernamen
17 *    /_ki-generiert/
18 *    /_ki-bearbeitet/
19 *    /_ki-inhalt/
20 *
21 * 4. Kennzeichnung in der Contao-Colorbox
22 *
23 * Bei automatisch erkannten Bildern wird ausschließlich das
24 * eigentliche Bild umschlossen. Bildunterschriften und Namen
25 * bleiben außerhalb der Positionierungsfläche.
26 */
27
28(function (window, document) {
29    "use strict";
30
31
32    /* =====================================================
33       KONFIGURATION
34       ===================================================== */
35
36    const markerClasses = [
37        "ki-generiert",
38        "ki-bearbeitet",
39        "ki-inhalt"
40    ];
41
42    const markerSelector =
43        ".ki-generiert, .ki-bearbeitet, .ki-inhalt";
44
45    const markerRules = [
46        {
47            className: "ki-generiert",
48            patterns: [
49                "--ki-generiert",
50                "/_ki-generiert/",
51                "/ki-generiert/"
52            ]
53        },
54        {
55            className: "ki-bearbeitet",
56            patterns: [
57                "--ki-bearbeitet",
58                "/_ki-bearbeitet/",
59                "/ki-bearbeitet/"
60            ]
61        },
62        {
63            className: "ki-inhalt",
64            patterns: [
65                "--ki-inhalt",
66                "/_ki-inhalt/",
67                "/ki-inhalt/"
68            ]
69        }
70    ];
71
72    const badgeFiles = {
73        "ki-generiert":
74            "https://intax.de/files/img/_ki/" +
75            "LABEL_AI%20GENERATED_white%20transparent.svg",
76
77        "ki-bearbeitet":
78            "https://intax.de/files/img/_ki/" +
79            "LABEL_AI%20MODIFIED_white%20transparent.svg",
80
81        "ki-inhalt":
82            "https://intax.de/files/img/_ki/" +
83            "LABEL_AI_white%20transparent.svg"
84    };
85
86    let lightboxBadge = null;
87
88
89    /* =====================================================
90       ALLGEMEINE HILFSFUNKTIONEN
91       ===================================================== */
92
93    function normalizeValue(value) {
94        if (!value) {
95            return "";
96        }
97
98        let normalizedValue = String(value);
99
100        try {
101            normalizedValue = decodeURIComponent(
102                normalizedValue
103            );
104        } catch (error) {
105            /*
106             * Ungültig codierte URLs werden unverändert
107             * weiter geprüft.
108             */
109        }
110
111        return normalizedValue
112            .replace(/\\/g, "/")
113            .toLowerCase();
114    }
115
116
117    function detectMarkerFromText(value) {
118        const normalizedValue = normalizeValue(value);
119
120        if (!normalizedValue) {
121            return null;
122        }
123
124        const matchingRule = markerRules.find(
125            function (rule) {
126                return rule.patterns.some(
127                    function (pattern) {
128                        return normalizedValue.includes(
129                            pattern
130                        );
131                    }
132                );
133            }
134        );
135
136        return matchingRule
137            ? matchingRule.className
138            : null;
139    }
140
141
142    function detectMarkerFromClasses(element) {
143        if (!element || !element.closest) {
144            return null;
145        }
146
147        const markedElement = element.closest(
148            markerSelector
149        );
150
151        if (!markedElement) {
152            return null;
153        }
154
155        return markerClasses.find(
156            function (className) {
157                return markedElement.classList.contains(
158                    className
159                );
160            }
161        ) || null;
162    }
163
164
165    function getImageValues(image) {
166        const values = [];
167
168        if (!image) {
169            return values;
170        }
171
172        values.push(image.currentSrc);
173        values.push(image.src);
174        values.push(image.getAttribute("src"));
175        values.push(image.getAttribute("srcset"));
176
177        const picture = image.closest("picture");
178
179        if (picture) {
180            picture
181                .querySelectorAll("source")
182                .forEach(function (source) {
183                    values.push(
184                        source.getAttribute("src")
185                    );
186
187                    values.push(
188                        source.getAttribute("srcset")
189                    );
190                });
191        }
192
193        const link = image.closest("a");
194
195        if (link) {
196            values.push(link.href);
197            values.push(link.getAttribute("href"));
198        }
199
200        return values.filter(Boolean);
201    }
202
203
204    function detectMarkerFromImageUrl(image) {
205        const values = getImageValues(image);
206
207        for (const value of values) {
208            const markerClass =
209                detectMarkerFromText(value);
210
211            if (markerClass) {
212                return markerClass;
213            }
214        }
215
216        return null;
217    }
218
219
220    function removeMarkerClasses(element) {
221        if (!element) {
222            return;
223        }
224
225        markerClasses.forEach(function (className) {
226            element.classList.remove(className);
227        });
228    }
229
230
231    /* =====================================================
232       BILDER AUSSCHLIESSEN, DIE NICHT VERÄNDERT WERDEN DÜRFEN
233       ===================================================== */
234
235    function shouldIgnoreImage(image) {
236        if (!(image instanceof HTMLImageElement)) {
237            return true;
238        }
239
240        /*
241         * Das Bild innerhalb der Colorbox darf nicht mit einer
242         * zusätzlichen Hülle versehen werden.
243         */
244
245        if (
246            image.closest(
247                "#colorbox, #cboxLoadedContent, " +
248                ".cboxPhoto, #ki-lightbox-badge"
249            )
250        ) {
251            return true;
252        }
253
254        /*
255         * Auch die eigentlichen Kennzeichnungsdateien werden
256         * nicht als normale Inhaltsbilder verarbeitet.
257         */
258
259        const source = normalizeValue(
260            image.currentSrc ||
261            image.src ||
262            image.getAttribute("src")
263        );
264
265        if (source.includes("/files/img/_ki/")) {
266            return true;
267        }
268
269        return false;
270    }
271
272
273    /* =====================================================
274       EIGENE HÜLLE NUR UM DAS BILD ERZEUGEN
275       ===================================================== */
276
277    function getExistingImageStage(image) {
278        const stage = image.closest(".ki-image-stage");
279
280        return stage || null;
281    }
282
283
284    function createImageStage(image) {
285        const existingStage =
286            getExistingImageStage(image);
287
288        if (existingStage) {
289            return existingStage;
290        }
291
292        /*
293         * Wenn das Bild innerhalb eines picture-Elements
294         * steht, wird das gesamte picture-Element umschlossen.
295         * Die source-Elemente bleiben dadurch unverändert.
296         */
297
298        const picture = image.closest("picture");
299        const mediaElement = picture || image;
300        const parent = mediaElement.parentNode;
301
302        if (!parent) {
303            return null;
304        }
305
306        const stage = document.createElement("span");
307
308        stage.className = "ki-image-stage";
309        stage.dataset.kiCreatedWrapper = "true";
310        stage.setAttribute("aria-hidden", "true");
311
312        parent.insertBefore(stage, mediaElement);
313        stage.appendChild(mediaElement);
314
315        return stage;
316    }
317
318
319    function removeAutomaticMarkerFromStage(stage) {
320        if (!stage) {
321            return;
322        }
323
324        removeMarkerClasses(stage);
325
326        delete stage.dataset.kiAutomaticMarker;
327    }
328
329
330    /* =====================================================
331       AUTOMATISCHE KENNZEICHNUNG NORMALER BILDER
332       ===================================================== */
333
334    function applyAutomaticMarkerToImage(image) {
335        if (shouldIgnoreImage(image)) {
336            return;
337        }
338
339        const existingStage =
340            getExistingImageStage(image);
341
342        /*
343         * Wenn bereits eine manuelle Klasse an einem äußeren
344         * Contao-Element sitzt, wird keine zweite automatische
345         * Kennzeichnung erzeugt.
346         */
347
348        const nearestMarkedElement =
349            image.closest(markerSelector);
350
351        if (
352            nearestMarkedElement &&
353            nearestMarkedElement !== existingStage
354        ) {
355            removeAutomaticMarkerFromStage(
356                existingStage
357            );
358
359            return;
360        }
361
362        const markerClass =
363            detectMarkerFromImageUrl(image);
364
365        if (!markerClass) {
366            removeAutomaticMarkerFromStage(
367                existingStage
368            );
369
370            return;
371        }
372
373        const stage = existingStage ||
374            createImageStage(image);
375
376        if (!stage) {
377            return;
378        }
379
380        removeMarkerClasses(stage);
381
382        stage.classList.add(markerClass);
383        stage.dataset.kiAutomaticMarker =
384            markerClass;
385    }
386
387
388    function scanImages(root) {
389        if (!root) {
390            return;
391        }
392
393        if (root instanceof HTMLImageElement) {
394            applyAutomaticMarkerToImage(root);
395        }
396
397        if (
398            root.querySelectorAll &&
399            typeof root.querySelectorAll === "function"
400        ) {
401            root
402                .querySelectorAll("img")
403                .forEach(
404                    applyAutomaticMarkerToImage
405                );
406        }
407    }
408
409
410    function startImageObserver() {
411        scanImages(document);
412
413        const observer = new MutationObserver(
414            function (mutations) {
415                mutations.forEach(
416                    function (mutation) {
417
418                        if (
419                            mutation.type === "childList"
420                        ) {
421                            mutation.addedNodes.forEach(
422                                function (node) {
423                                    if (
424                                        node.nodeType ===
425                                        Node.ELEMENT_NODE
426                                    ) {
427                                        scanImages(node);
428                                    }
429                                }
430                            );
431                        }
432
433                        if (
434                            mutation.type === "attributes" &&
435                            mutation.target instanceof
436                            HTMLImageElement
437                        ) {
438                            applyAutomaticMarkerToImage(
439                                mutation.target
440                            );
441                        }
442                    }
443                );
444            }
445        );
446
447        observer.observe(
448            document.documentElement,
449            {
450                childList: true,
451                subtree: true,
452                attributes: true,
453                attributeFilter: [
454                    "src",
455                    "srcset"
456                ]
457            }
458        );
459    }
460
461
462    /* =====================================================
463       COLORBOX-GROSSANSICHT
464       ===================================================== */
465
466    function getLightboxBadge() {
467        if (lightboxBadge) {
468            return lightboxBadge;
469        }
470
471        lightboxBadge =
472            document.createElement("div");
473
474        lightboxBadge.id =
475            "ki-lightbox-badge";
476
477        lightboxBadge.setAttribute(
478            "aria-hidden",
479            "true"
480        );
481
482        document.body.appendChild(
483            lightboxBadge
484        );
485
486        return lightboxBadge;
487    }
488
489
490    function hideLightboxBadge() {
491        if (!lightboxBadge) {
492            return;
493        }
494
495        lightboxBadge.style.display = "none";
496        lightboxBadge.style.visibility = "hidden";
497        lightboxBadge.style.left = "-9999px";
498        lightboxBadge.style.top = "-9999px";
499        lightboxBadge.style.backgroundImage = "none";
500    }
501
502
503    function detectMarkerFromColorboxLink(
504        linkElement
505    ) {
506        if (!linkElement) {
507            return null;
508        }
509
510        /*
511         * Zuerst wird nach einer manuell oder automatisch
512         * gesetzten Klasse gesucht.
513         */
514
515        const markerFromClasses =
516            detectMarkerFromClasses(linkElement);
517
518        if (markerFromClasses) {
519            return markerFromClasses;
520        }
521
522        /*
523         * Danach werden alle Bilder innerhalb des Links
524         * anhand ihrer Dateinamen geprüft.
525         */
526
527        const images =
528            linkElement.querySelectorAll("img");
529
530        for (const image of images) {
531            const markerFromImage =
532                detectMarkerFromImageUrl(image);
533
534            if (markerFromImage) {
535                return markerFromImage;
536            }
537        }
538
539        /*
540         * Zuletzt wird die href-Adresse des Links geprüft.
541         */
542
543        return detectMarkerFromText(
544            linkElement.href ||
545            linkElement.getAttribute("href")
546        );
547    }
548
549
550    function positionLightboxBadge() {
551        const image = document.querySelector(
552            "#colorbox .cboxPhoto"
553        );
554
555        if (!image) {
556            hideLightboxBadge();
557            return;
558        }
559
560        const imageRect =
561            image.getBoundingClientRect();
562
563        if (
564            imageRect.width < 100 ||
565            imageRect.height < 100
566        ) {
567            hideLightboxBadge();
568            return;
569        }
570
571        const badge = getLightboxBadge();
572
573        const gap =
574            window.innerWidth <= 600
575                ? 8
576                : 12;
577
578        badge.style.display = "block";
579        badge.style.visibility = "hidden";
580
581        const badgeWidth =
582            badge.offsetWidth;
583
584        const badgeHeight =
585            badge.offsetHeight;
586
587        let left =
588            imageRect.left + gap;
589
590        let top =
591            imageRect.bottom -
592            badgeHeight -
593            gap;
594
595        left = Math.max(
596            gap,
597            Math.min(
598                left,
599                window.innerWidth -
600                badgeWidth -
601                gap
602            )
603        );
604
605        top = Math.max(
606            gap,
607            Math.min(
608                top,
609                window.innerHeight -
610                badgeHeight -
611                gap
612            )
613        );
614
615        badge.style.left =
616            Math.round(left) + "px";
617
618        badge.style.top =
619            Math.round(top) + "px";
620
621        badge.style.visibility = "visible";
622    }
623
624
625    function startColorboxSupport() {
626        const $ = window.jQuery;
627
628        if (!$ || !$.colorbox) {
629            return;
630        }
631
632        $(document).on(
633            "cbox_complete",
634            function () {
635                const currentLink =
636                    $.colorbox.element();
637
638                if (
639                    !currentLink ||
640                    !currentLink.length
641                ) {
642                    hideLightboxBadge();
643                    return;
644                }
645
646                const markerClass =
647                    detectMarkerFromColorboxLink(
648                        currentLink[0]
649                    );
650
651                if (
652                    !markerClass ||
653                    !badgeFiles[markerClass]
654                ) {
655                    hideLightboxBadge();
656                    return;
657                }
658
659                const badge =
660                    getLightboxBadge();
661
662                badge.style.backgroundImage =
663                    'url("' +
664                    badgeFiles[markerClass] +
665                    '")';
666
667                window.requestAnimationFrame(
668                    function () {
669                        window.requestAnimationFrame(
670                            positionLightboxBadge
671                        );
672                    }
673                );
674            }
675        );
676
677        $(document).on(
678            "cbox_cleanup cbox_closed",
679            hideLightboxBadge
680        );
681
682        $(window).on(
683            "resize scroll",
684            positionLightboxBadge
685        );
686
687        document.addEventListener(
688            "scroll",
689            positionLightboxBadge,
690            true
691        );
692    }
693
694
695    /* =====================================================
696       INITIALISIERUNG
697       ===================================================== */
698
699    function initialize() {
700        startImageObserver();
701        startColorboxSupport();
702    }
703
704
705    if (document.readyState === "loading") {
706        document.addEventListener(
707            "DOMContentLoaded",
708            initialize,
709            {
710                once: true
711            }
712        );
713    } else {
714        initialize();
715    }
716
717})(window, document);

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.