PageSourceSearch

https://www.dieerlebniswerft.de/wp-content/plugins/nextgen-gallery…xtgen_pro_horizontal_filmstrip.js?ver=4.5.0

js dieerlebniswerft.de collected 2026-10-06 16:28:15 UTC 29,158 bytes, 614 lines download raw bytes

1(function($) {
2    Galleria.addTheme({
3        name: 'nextgen_pro_horizontal_filmstrip',
4        author: 'Imagely',
5        version: 2.0,
6        defaults: {
7            carousel:   true,
8            thumbnails: true,
9            autoplay:   true,
10            showInfo:   false,
11            fullscreenDoubleTap: false,
12            trueFullscreen:      false
13        },
14        init: function(options) {
15            Galleria.requires(1.41, 'This version of Classic theme requires Galleria 1.4.1 or later');
16
17            var self = this;
18
19            // TikTok video playback handling
20            var currentVideo = null;
21            var wasPlayingBeforeVideo = false;
22            var videoPlaying = false;
23            var currentVideoIndex = -1;
24
25            // Utility function to detect and convert video platform URLs to embed URLs
26            var getVideoEmbedInfo = function(url) {
27                if (!url || typeof url !== 'string') {
28                    return { isEmbed: false, embedUrl: null, useIframe: false };
29                }
30
31                var embedUrl = null;
32                var useIframe = false;
33                var isEmbed = false;
34
35                // YouTube URLs
36                var youtubeRegex = /(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]{11})/;
37                var youtubeMatch = url.match(youtubeRegex);
38                if (youtubeMatch) {
39                    embedUrl = 'https://www.youtube.com/embed/' + youtubeMatch[1] + '?autoplay=1&rel=0';
40                    useIframe = true;
41                    isEmbed = true;
42                }
43
44                // Vimeo URLs
45                var vimeoRegex = /(?:vimeo\.com\/)(?:.*\/)?(\d+)/;
46                var vimeoMatch = url.match(vimeoRegex);
47                if (vimeoMatch && !isEmbed) {
48                    embedUrl = 'https://player.vimeo.com/video/' + vimeoMatch[1] + '?autoplay=1';
49                    useIframe = true;
50                    isEmbed = true;
51                }
52
53                // Dailymotion URLs
54                var dailymotionRegex = /(?:dailymotion\.com\/(?:embed\/)?video\/|dai\.ly\/)([a-zA-Z0-9]+)/;
55                var dailymotionMatch = url.match(dailymotionRegex);
56                if (dailymotionMatch && !isEmbed) {
57                    embedUrl = 'https://www.dailymotion.com/embed/video/' + dailymotionMatch[1] + '?autoplay=1';
58                    useIframe = true;
59                    isEmbed = true;
60                }
61
62                // Twitch URLs
63                var twitchRegex = /(?:twitch\.tv\/videos\/|twitch\.tv\/)(\d+|[a-zA-Z0-9_]+)/;
64                var twitchMatch = url.match(twitchRegex);
65                if (twitchMatch && !isEmbed) {
66                    var twitchId = twitchMatch[1];
67                    // Check if it's a video ID (numeric) or channel name
68                    if (/^\d+$/.test(twitchId)) {
69                        embedUrl = 'https://player.twitch.tv/?video=' + twitchId + '&autoplay=true&parent=' + window.location.hostname;
70                    } else {
71                        embedUrl = 'https://player.twitch.tv/?channel=' + twitchI
71d + '&autoplay=true&parent=' + window.location.hostname;
72                    }
73                    useIframe = true;
74                    isEmbed = true;
75                }
76
77                // VideoPress URLs
78                var videopressRegex = /(?:videopress\.com\/v\/|video\.wordpress\.com\/v\/)([a-zA-Z0-9]+)/;
79                var videopressMatch = url.match(videopressRegex);
80                if (videopressMatch && !isEmbed) {
81                    embedUrl = 'https://videopress.com/embed/' + videopressMatch[1] + '?autoplay=1';
82                    useIframe = true;
83                    isEmbed = true;
84                }
85
86                // Wistia URLs
87                var wistiaRegex = /(?:wistia\.com\/medias\/|wistia\.net\/embed\/)([a-zA-Z0-9]+)/;
88                var wistiaMatch = url.match(wistiaRegex);
89                if (wistiaMatch && !isEmbed) {
90                    embedUrl = 'https://fast.wistia.net/embed/iframe/' + wistiaMatch[1] + '?autoplay=1';
91                    useIframe = true;
92                    isEmbed = true;
93                }
94
95                return {
96                    isEmbed: isEmbed,
97                    embedUrl: embedUrl,
98                    useIframe: useIframe,
99                    originalUrl: url
100                };
101            };
102
103            // Adjust some settings specific to this gallery
104            options.showInfo               = (options.showInfo && options._nggCaptionClass.length > 0) ? true : false;
105            options._nggCaptionPadding     = parseInt(window.ngg_galleria.get_setting(options._nggGalleryID, 'caption_padding',  '5'));
106            options._nggOverrideThumbnails = window.ngg_galleria.get_setting(options._nggGalleryID, 'override_thumbnail_settings',  '0') != '0';
107
108            // Galleria doesn't correctly position images with borders
109            var adjust_border_positioning = function($img) {
110                if (options._nggBorderSize > 0 && options.imageCrop !== true) {
111                    var top  = (parseInt($img.css('top')) - options._nggBorderSize);
112                    var left = (parseInt($img.css('left')) - options._nggBorderSize);
113                    if (top < 0)  top  = 0;
114                    if (left < 0) left = 0;
115                    $img.css('top',  top + 'px');
116                    $img.css('left', left + 'px');
117                }
118            };
119
120            var cleanupVideo = function() {
121                if (currentVideo) {
122                    // Stop video playback
123                    var $video = $(currentVideo).find('video, iframe');
124                    if ($video.length) {
125                        if ($video.is('video')) {
126                            $video[0].pause();
127                            $video[0].src = ''; // Clear source to stop loading
128                            $video.off('ended error playing');
129                        } else if ($video.is('iframe')) {
130                            // For iframes, we can't directly stop playback, but we can remove the src
131                            $video.attr('src', 'about:blank');
132                        }
133                    }
134                    $(currentVideo).remove();
135                    currentVideo = null;
136                    videoPlaying = false;
137                    currentVideoIndex = -1;
138                }
139            };
140
141            // Helper function to show error banner (removes duplicate code)
142            var showVideoErrorBanner = function($container) {
143                // Get image dimensions to match error banner size
144                var $img = self.$('stage').find('.galleria-image img');
145                var imgWidth = $img.width() || $img.attr('width') || 0;
146                var imgHeight = $img.height() || $img.attr('height') || 0;
147                
148                var $errorMsg;
149                if (typeof NextGEN_TikTok !== 'undefined' && NextGEN_TikTok.create_error) {
150                    var errorEl = NextGEN_TikTok.create_error('Video failed to load', 'ngg-tiktok-error');
151                    $errorMsg = $(errorEl);
152                } else {
153                    // Fallback error element
154                    $errorMsg = $('<div class="ngg-tiktok-error"><div class="ngg-tiktok-error-content"><span class="ngg-tiktok-error-icon">&#9888;</span><span class="ngg-tiktok-error-text">Video failed to load</span></div></div>');
155                }
156                
157                // Size error banner to match image dimensions
158                if (imgWidth > 0 && imgHeight > 0) {
159                    $errorMsg.css({
160                        width: imgWidth + 'px',
161                        height: imgHeight + 'px',
162                        position: 'relative',
163                        top: 'auto',
164                        left: 'auto',
165                        right: 'auto',
166                        bottom: 'auto'
167                    });
168                }
169                
170                $container.append($errorMsg);
171            };
172
173            var handleVideoPlayback = function(index) {
174                var data = self.getData(index);
175                // Check for video URL - prioritize video_link, then tiktok_play_url
176                var videoUrl = data.video_link || data.tiktok_play_url;
177
178                // Check if this is a video (TikTok or generic video_link)
179                // For TikTok: check is_tiktok flag
180                // For generic video: check has_video_link flag or if video_link exists
181                var isVideo = (data.is_tiktok == 1 || data.is_tiktok == '1' || data.is_tiktok === true)
182                           || (data.is_video_link == 1 || data.is_video_link == '1' || data.is_video_link === true)
183                           || (data.video_link && data.video_link.length > 0);
184
185                if (!videoUrl || !isVideo) {
186                    // Not a video - cleanup any existing video and show playback button
187                    cleanupVideo();
188                    $('.galleria-playback-button').show();
189                    // Resume slideshow if it was playing before
190                    if (wasPlayingBeforeVideo && !self._playing) {
191                        self.play();
192                    }
193                    return;
194                }
195
196                // This is a video - pause slideshow immediately to prevent auto-advance
197                wasPlayingBeforeVideo = self._playing;
198                if (wasPlayingBeforeVideo) {
199                    self.pause();
200                }
201
202                // Cleanup any previous video
203                cleanupVideo();
204
205                // Hide playback button when showing video
206                $('.galleria-playback-button').hide();
207
208                // Create video container
209                var $videoContainer = $('<div class="galleria-video-container"></div>');
210
211                // Get the video URL - prioritize video_link, then TikTok URLs
212                var videoUrl = data.video_link || data.tiktok_play_url || data.tiktok_share_url;
213                
214                // Check if this is an embed URL (YouTube, Vimeo, etc.)
215                var videoInfo = getVideoEmbedInfo(videoUrl);
216                var $videoElement;
217
218                if (videoInfo.useIframe && videoInfo.embedUrl) {
219                    // Create iframe for embed platforms (YouTube, Vimeo, etc.)
220                    $videoElement = $('<iframe>', {
221                        src: videoInfo.embedUrl,
222                        frameborder: '0',
223                        allow: 'autoplay; fullscreen; picture-in-picture',
224                        allowfullscreen: true,
225                        webkitallowfullscreen: true,
226                        mozallowfullscreen: true
227                    });
228
229                    // Apply border styling if configured
230                    if (options._nggBorderSize > 0) {
231                        $videoElement.css({
232                            'border': 'solid ' + parseInt(options._nggBorderSize) + 'px ' + options._nggBorderColor
233                        });
234                    }
235
236                    // For iframes, we can't detect 'ended' event, so we'll rely on the platform's controls
237                    videoPlaying = true; // Assume playing for embed videos
238                } else {
239                    // Create HTML5 video element for direct video URLs (local files, TikTok, etc.)
240                    $videoElement = $('<video>', {
241                        controls: true,
242                        autoplay: true,
243                        playsinline: true,
244                        preload: 'auto',
245                        muted: false
246                    });
247
248                    // Apply border styling if configured
249                    if (options._nggBorderSize > 0) {
250                        $videoElement.css({
251                            'border': 'solid ' + parseInt(options._nggBorderSize) + 'px ' + options._nggBorderColor
252                        });
253                    }
254
255                    // Set video source
256                    $videoElement.attr('src', videoUrl);
257
258                    // Track when video actually starts playing
259                    $videoElement.on('playing', function() {
260                        videoPlaying = true;
261                    });
262                }
263
264                // Handle video end - resume slideshow (only for HTML5 video, not iframes)
265                if ($videoElement.is('video')) {
266                    $videoElement.on('ended', function() {
267                        cleanupVideo();
268                        $('.galleria-playback-button').show();
269                        if (wasPlayingBeforeVideo) {
270                            // Small delay to ensure cleanup is complete
271                            setTimeout(function() {
272                                self.play();
273                                self.next();
274                            }, 100);
275                        } else {
276                            // Even if slideshow wasn't playing, advance to next slide
277                            self.next();
278                        }
279                    });
280
281                    // Handle video error - try fallback URL (only for HTML5 video)
282                    $videoElement.on('error', function() {
283                        var currentSrc = $videoElement.attr('src');
284                        var isTikTok = (data.is_tiktok == 1 || data.is_tiktok == '1' || data.is_tiktok === true);
285                        
286                        // Keep TikTok and video_link error handling completely separate
287                        if (isTikTok) {
288                            // TikTok video: Original error handling logic (preserved exactly as before)
289                            // Try tiktok_share_url as fallback if tiktok_play_url failed
290                            if (data.tiktok_share_url && currentSrc !== data.tiktok_share_url) {
291                                // Try fallback URL
292                                $videoElement.attr('src', data.tiktok_share_url);
293                            } else {
294                                // Video failed to load, show error banner
295                                $videoElement.remove();
296                                showVideoErrorBanner($videoContainer);
297                            }
298                        } else {
299                            // Generic video_link: Separate error handling (doesn't interfere with TikTok)
300                            // For generic videos, we don't have fallback URLs, so show error immediately
301                            // Video failed to load, show error banner
302                            $videoElement.remove();
303                            showVideoErrorBanner($videoContainer);
304                        }
305                    });
306                }
307
308                // Add to container and append to stage
309                $videoContainer.append($videoElement);
310                self.$('stage').append($videoContainer);
311                currentVideo = $videoContainer[0];
312                currentVideoIndex = index;
313
314                // Force play attempt for HTML5 video (some browsers need user interaction, but autoplay should work)
315                if ($videoElement.is('video')) {
316                    setTimeout(function() {
317                        if ($videoElement[0] && $videoElement[0].paused) {
318                            $videoElement[0].play().catch(function(err) {
319                                console.warn('Video autoplay failed:', err);
320                            });
321                        }
322                    }, 100);
323                }
324            };
325
326            // Set our thumbnail sizes to exactly the dimensions requested by the user
327            if (options._nggOverrideThumbnails) {
328                options._nggThumbnailWidth  = parseInt(window.ngg_galleria.get_setting(options._nggGalleryID, 'thumbnail_width', '120'));
329                options._nggThumbnailHeight = parseInt(window.ngg_galleria.get_setting(options._nggGalleryID, 'thumbnail_height', '90'));
330
331                this.$('thumbnails').find('.galleria-image').each(function(ndx, image) {
332                    $(image).css({
333                        width:  options._nggThumbnailWidth  + 'px',
334                        height: options._nggThumbnailHeight + 'px'
335                    }).find('img').css({
336                        'max-width': options._nggThumbnailWidth,
337                        width:  options._nggThumbnailWidth  + 'px',
338                        height: options._nggThumbnailHeight + 'px'
339                    });
340                });
341            } else {
342                // Will be the height of the tallest thumbnail to display
343                options._nggThumbnailHeight = 1;
344
345                // Find the tallest thumbnail
346                for (var i = 0; i <= (this.getDataLength() - 1); i++) {
347                    var img = this.getData(i);
348                    if (img.thumb_dimensions.height > options._nggThumbnailHeight) {
349                        options._nggThumbnailHeight = img.thumb_dimensions.height;
350                    }
351                }
352
353                // Position shorter thumbnails to be vertically centered
354                this.$('thumbnails').find('.galleria-image').each(function(ndx, image) {
355                    var cur_h = self._data[ndx].thumb_dimensions.height;
356                    $(image).css({
357                        'width': self._data[ndx].thumb_dimensions.width + 'px',
358                        'height': self._data[ndx].thumb_dimensions.height + 'px'
359                    }).find('img').css({
360                        'width': self._data[ndx].thumb_dimensions.width + 'px',
361                        'height': self._data[ndx].thumb_dimensions.height + 'px'
362                    });
363                    if (cur_h < options._nggThumbnailHeight) {
364                        $(image).css('top', (options._nggThumbnailHeight - cur_h) / 2);
365                    }
366                });
367            }
368
369            // It's much faster to add this by CSS than hooking onto 'loadfinish'
370            if (options._nggBorderSize > 0 && options.imageCrop !== true) {
371                $("<style type='text/css'>#displayed_gallery_" + options._nggGalleryID + " .galleria-stage .galleria-image img {"
372                    + "border: solid " + parseInt(options._nggBorderSize) + 'px ' + options._nggBorderColor
373                    + " } </style>").appendTo("head");
374            }
375
376            // Adjust some dimensions before Galleria starts the display
377            this.$('info').css('max-height', options._nggCaptionHeight + 'px');
378            this.$('thumbnails-container').css('height', options._nggThumbnailHeight + 'px');
379
380            // Calculate how far from the bottom the carousel should appear. The
381            // extra 2 pixels is for the border-top/border-bottom of the thumbnails.
382            // The last 4 pixels is to match what Photocrati Slideshows look like
383            this.$('stage').css(
384                'bottom',
385                options._nggThumbnailHeight + (parseInt(options._nggBorderSize) > 0 ? parseInt(options._nggBorderSize) : 0)
386                + 2 + 4 + 'px'
387            );
388
389            this.$('thumbnails-container').height(options._nggThumbnailHeight);
390            this.$('stage').css({
391                bottom: (($(options._nggGalleryParent).offset()
392                        + $(options._nggGalleryParent).outerHeight())
393                        - this.$('thumbnails-container').offset().top)
394                        + 6
395            });
396
397            // Let Galleria reflow itself after we've altered so much
398            this.rescale();
399
400            if (!Galleria.TOUCH) {
401                this.addIdleState(this.get('image-nav-left'),  { left:  -50 });
402                this.addIdleState(this.get('image-nav-right'), { right: -50 });
403                this.addIdleState(this.get('counter'),         { opacity: 0 });
404            }
405
406            // Add the caption class to the Galleria container
407            if (options.showInfo && options._nggCaptionClass.length > 0) {
408                this.$('info').show();
409                this.$('container').addClass(options._nggCaptionClass);
410
411                // if the info overflows its container this effect will let viewers read the remaining description
412                this.$('info').on('mouseenter', function() {
413                    var $info = $(this);
414                    var text = self.$('info-text');
415                    var diff = $info.outerHeight() - text.outerHeight();
416                    if (diff < 0) {
417                        $info.stop().animate({ scrollTop: -diff }, ((-diff) / 17) * 450);
418                    }
419                });
420                this.$('info').on('mouseleave', function() {
421                    var $info = $(this);
422                    var text = self.$('info-text');
423                    var diff = $info.outerHeight() - text.outerHeight();
424                    if (diff < 0) {
425                        $info.stop().animate({ scrollTop: 0 }, 'fast');
426                    }
427                });
428
429                // Adjust the dimensions of the stage to fit captions
430                switch (options._nggCaptionClass) {
431                    case 'caption_above_stage': {
432                        this.$('stage').css({ top: parseInt(options._nggCaptionHeight) + options.imageMargin });
433                        break;
434                    }
435                    case 'caption_below_stage': {
436                        this.$('thumbnails-container').css({
437                            bottom: (parseInt(options._nggCaptionHeight) + parseInt(options._nggCaptionPadding)) + 'px'
438                        });
439                        this.$('stage').css({
440                            bottom: parseInt(options._nggCaptionHeight)
441                                    + (options._nggCaptionPadding * 2)
442                                    + this.$('thumbnails-container').height()
443                                    + options.imageMargin
444                                    + 6
445                        });
446                        break;
447                    }
448                    case 'caption_overlay_bottom': {
449                        this.$('info').css({ bottom: parseInt(this.$('stage').css('bottom')) });
450                        break;
451                    }
452                }
453
454                this.rescale();
455            }
456
457            // Thumbnail navigation shown on hover
458            $([this.$('thumb-nav-left'), this.$('thumb-nav-right')])
459                .css({ display: 'none' })
460                .on('mouseenter', function() {
461                    if (!$(this).hasClass('disabled')) {
462                        $(this).stop().animate({
463                            opacity: 1
464                        }, 'fast');
465                    }
466                })
467                .on('mouseleave', function() {
468                    if (!$(this).hasClass('disabled')) {
469                        $(this).stop().animate({
470                            opacity: 0.8
471                        }, 'fast');
472                    }
473                });
474
475            this.$('thumbnails-container').on('mouseenter',
476                this.proxy(function() {
477                    var navList = [this.get('thumb-nav-left'), this.get('thumb-nav-right')];
478                    $(navList).css({ display : 'block' });
479                    $(navList).stop().animate({
480                        opacity: 0.8
481                    });
482                })
483            );
484
485            this.$('thumbnails-container').on('mouseleave',
486                this.proxy(function() {
487                    var navList = [this.get('thumb-nav-left'), this.get('thumb-nav-right')];
488                    $(navList).stop().animate({
489                        opacity: 0
490                    }, function() {
491                        $(this).css({ display: 'none' });
492                    });
493                })
494            );
495
496            // Handle video playback when slide loads
497            this.bind('loadstart', this.proxy(function(e) {
498                handleVideoPlayback(e.index);
499            }));
500
501            // Ensure video cleanup when navigating between slides
502            this.bind('image', this.proxy(function(e) {
503                // Only cleanup if we're navigating to a different slide
504                // This prevents cleanup when the same slide reloads
505                if (currentVideo && currentVideoIndex !== e.index) {
506                    cleanupVideo();
507                    $('.galleria-playback-button').show();
508                    // Resume slideshow if it was playing before video
509                    if (wasPlayingBeforeVideo && !self._playing) {
510                        self.play();
511                    }
512                }
513            }));
514
515            // set slideshow speed
516            if (options._nggSlideshowSpeed) {
517                this.setPlaytime(options._nggSlideshowSpeed);
518            }
519
520            // add playback controls if we're to do so
521            if (options._nggShowPlaybackControls) {
522                // Add playback controls
523                var playback_button = $('<div/>').addClass('galleria-playback-button');
524                if (this._playing) {
525                    playback_button.removeClass('play').addClass('pause');
526                } else {
527                    playback_button.removeClass('pause').addClass('play');
528                }
529                $(this._dom.stage).append(playback_button);
530
531                var button = $('<a/>')
532                    .on('mouseenter', function() { $(this).parent().css('opacity', 0.9); })
533                    .on('mouseleave', function() { $(this).parent().css('opacity', 0.7); })
534                    .on('click', this.proxy(function(e) {
535                        var controls = $(e.target).parent();
536                        if (this._playing) {
537                            this.pause();
538                            controls.removeClass('pause').addClass('play');
539                        } else {
540                            this.play().next();
541                            controls.removeClass('play').addClass('pause');
542                        }
543                    }));
544                playback_button.append(button);
545
546                playback_button
547                    .on('mouseenter', function() { $(this).css('opacity', 0.7); })
548                    .on('mouseleave', function() { $(this).animate({ opacity: 0.0}); });
549            }
550
551            this.bind('rescale', this.proxy(function(e) {
552                var $img = $(self.getActiveImage());
553                setTimeout(function() {
554                    adjust_border_positioning($img);
555                }, 30);
556            }));
557
558            this.bind('loadfinish', this.proxy(function(e) {
559                var $img = $(e.imageTarget);
560                adjust_border_positioning($img);
561
562                // Adjust the Pro Lightbox triggers if they exist
563                $(options._nggGalleryParent).siblings('div.ngg-trigger-buttons').each(function () {
564                    $(this).width(self.$('stage').width());
565                    $(this).css('margin', '0 auto');
566                    $(this).find('i').each(function () {
567                        $(this).data('nplmodal-image-id', self.getData(self.getIndex()).image_id);
568                    });
569                });
570
571                // The current thumbnail should have full opacity
572                $(e.thumbTarget).css('opacity', 1)
573                                .parent()
574                                .siblings()
575                                .children()
576                                .css('opacity', 0.6);
577            }));
578
579            this.bind('thumbnail', this.proxy(function(e) {
580                if (!Galleria.TOUCH ) {
581                    // fade thumbnails
582                    $(e.thumbTarget).css('opacity', 0.6).parent()
583                        .on('mouseenter', function() {
584                            $(this).not('.active').children().stop().fadeTo(100, 1);
585                        })
586                        .on('mouseleave', function() {
587                            $(this).not('.active').children().stop().fadeTo(400, 0.6);
588                        });
589                    if (e.index === this.getIndex()) {
590                        $(e.thumbTarget).css('opacity', 1);
591                    }
592                } else {
593                    $(e.thumbTarget).css('opacity', this.getIndex() ? 1 : 0.6);
594                }
595            }));
596
597            // Disable image right-click and drag when requested
598            if (window.ngg_galleria.get_setting(options._nggGalleryID, 'protect_images', false)) {
599                this.addElement('image-protection');
600                document.oncontextmenu = function(event) {
601                    event = event || window.event;
602                    event.preventDefault();
603                };
604                this.prependChild('images', 'image-protection');
605                this.$('thumbnails').find('.galleria-image').bind('dragstart', function(event) {
606                    event.preventDefault();
607                });
608            }
609        }
610    });
611
612    $(window).trigger('ngg.galleria.themeadded', ['nextgen_pro_horizontal_filmstrip']);
613
614}(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.