PageSourceSearch

https://hellerup.moviehouse.dk/Scripts/js/jquery.flipster.js

js moviehouse.dk collected 2026-10-08 17:33:05 UTC 27,810 bytes, 721 lines download raw bytes

1/* jshint browser: true, jquery: true, devel: true */
2/* global window, jQuery */
3
4(function($, window, undefined) {
5    'use strict';
6
7    function throttle(func, delay) {
8        var timer = null;
9
10        return function() {
11            var context = this,
12                args = arguments;
13
14            if ( timer === null ) {
15                timer = setTimeout(function() {
16                    func.apply(context, args);
17                    timer = null;
18                }, delay);
19            }
20        };
21    }
22
23    // Check for browser CSS support and cache the result for subsequent calls.
24    var checkStyleSupport = (function() {
25        var support = {};
26        return function(prop) {
27            if ( support[prop] !== undefined ) { return support[prop]; }
28
29            var div = document.createElement('div'),
30                style = div.style,
31                ucProp = prop.charAt(0).toUpperCase() + prop.slice(1),
32                prefixes = ["webkit", "moz", "ms", "o"],
33                props = (prop + ' ' + (prefixes).join(ucProp + ' ') + ucProp).split(' ');
34
35            for (var i in props) {
36                if ( props[i] in style ) { return support[prop] = props[i]; }
37            }
38
39            return support[prop] = false;
40        };
41    }());
42
43    var svgNS = 'http://www.w3.org/2000/svg',
44        svgSupport = (function() {
45            var support;
46            return function() {
47                if ( support !== undefined ) { return support; }
48                var div = document.createElement('div');
49                div.innerHTML = '<svg/>';
50                support = ( div.firstChild && div.firstChild.namespaceURI === svgNS );
51                return support;
52            };
53        }());
54
55    var $window = $(window),
56
57        transformSupport = checkStyleSupport('transform'),
58
59        defaults = {
60            itemContainer: 'ul',
61            // [string|object]
62            // Selector for the container of the flippin' items.
63
64            itemSelector: 'li',
65            // [string|object]
66            // Selector for children of `itemContainer` to flip
67
68            start: 'start',
69            // ['center'|number]
70            // Zero based index of the starting item, or use 'center' to start in the middle
71
72            fadeIn: 0,
73            // [milliseconds]
74            // Speed of the fade in animation after items have been setup
75
76            loop: false,
77            // [true|false]
78            // Loop around when the start or end is reached
79
80            autoplay: false,
81            // [false|milliseconds]
82            // If a positive number, Flipster will automatically advance to next item after that number of milliseconds
83
84            pauseOnHover: true,
85            // [true|false]
86            // If true, autoplay advancement will pause when Flipster is hovered
87
88            style: 'coverflow',
89            // [coverflow|carousel|flat|...]
90            // Adds a class (e.g. flipster--coverflow) to the flipster element to switch between display styles
91            // Create your own theme in CSS and use this setting to have Flipster add the custom class
92
93            spacing: -0.6,
94            // [number]
95            // Space between items relative to each item's width. 0 for no spacing, negative values to overlap
96
97            click: true,
98            // [true|false]
99            // Clicking an item switches to that item
100
101            keyboard: true,
102            // [true|false]
103            // Enable left/right arrow navigation
104
105            scrollwheel: true,
106            // [true|false]
107            // Enable mousewheel/trackpad navigation; up/left = previous, down/right = next
108
109            touch: true,
110            // [true|false]
111            // Enable swipe navigation for touch devices
112
113            nav: false,
114            // [true|false|'before'|'after']
115            // If not false, Flipster will build an unordered list of the items
116            // Values true or 'before' will insert the navigation before the items, 'after' will append the navigation after the items
117
118            buttons: false,
119            // [true|false|'custom']
120            // If true, Flipster will insert Previous / Next buttons with SVG arrows
121            // If 'custom', Flipster will not insert the arrows and will instead use the values of `buttonPrev` and `buttonNext`
122
123            buttonPrev: 'Previous',
124            // [text|html]
125            // Changes the text for the Previous button
126
127            buttonNext: 'Next',
128            // [text|html]
129            // Changes the text for the Next button
130
131            onItemSwitch: false
132
133            // [function]
134            // Callback function when items are switched
135            // Arguments received: [currentItem, previousItem]
136        },
137
138        classes = {
139            main: 'flipster',
140            active: 'flipster--active',
141            container: 'flipster__container',
142
143            nav: 'flipster__nav',
144            navChild: 'flipster__nav__child',
145            navItem: 'flipster__nav__item',
146            navLink: 'flipster__nav__link',
147            navCurrent: 'flipster__nav__item--current',
148            navCategory: 'flipster__nav__item--category',
149            navCategoryLink: 'flipster__nav__link--category',
150
151            button: 'flipster__button',
152            buttonPrev: 'flipster__button--prev',
153            buttonNext: 'flipster__button--next',
154
155            item: 'flipster__item',
156            itemCurrent: 'flipster__item--current',
157            itemPast: 'flipster__item--past',
158            itemFuture: 'flipster__item--future',
159            itemContent: 'flipster__item__content'
160        },
161
162        classRemover = new RegExp('\\b(' + classes.itemCurrent + '|' + classes.itemPast + '|' + classes.itemFuture + ')(.*?)(\\s|$)', 'g'),
163        whiteSpaceRemover = new RegExp('\\s\\s+', 'g');
164
165    $.fn.flipster = function(options) {
166        var isMethodCall = (typeof options === 'string' ? true : false);
167
168        if ( isMethodCall ) {
169            var args = Array.prototype.slice.call(arguments, 1);
170            return this.each(function() {
171                var methods = $(this).data('methods');
172                if ( methods[options] ) {
173                    return methods[options].apply(this, args);
174                } else {
175                    return this;
176                }
177            });
178        }
179
180        var settings = $.extend({}, defaults, options);
181
182        return this.each(function() {
183
184            var self = $(this),
185                methods,
186
187                _container,
188                _containerWidth,
189                _resizeInterval,
190
191                _items,
192                _itemOffsets = [],
193                _currentItem,
194                _currentIndex = 0,
195
196                _nav,
197                _navItems,
198                _navLinks,
199
200                _playing = false,
201                _startDrag = false;
202
203            function buildButtonContent(dir) {
204                var text = ( dir === 'next' ? settings.buttonNext : settings.buttonPrev );
205
206                if ( settings.buttons === 'custom' || !svgSupport ) { return text; }
207
208                return '<svg viewBox="0 0 13 20" xmlns="' + svgNS + '" aria-labelledby="title"><title>' + text + '</title><polyline points="10,3 3,10 10,17"' + (dir === 'next' ? ' transform="rotate(180 6.5,10)"' : '') + '/></svg>';
209            }
210
211            function buildButton(dir) {
212                dir = dir || 'next';
213
214                return $('<button class="' + classes.button + ' ' + ( dir === 'next' ? classes.buttonNext : classes.buttonPrev ) + '" role="button" />')
215                    .html(buildButtonContent(dir))
216                    .on('click', function(e) {
217                        jump(dir);
218                        e.preventDefault();
219                    });
220
221            }
222
223            function buildButtons() {
224                if ( settings.buttons && _items.length > 1 ) {
225                    self.find('.' + classes.button).remove();
226                    self.append(buildButton('prev'), buildButton('next'));
227                }
228            }
229
230            function buildNav() {
231                var navCategories = {};
232
233                if ( !settings.nav || _items.length <= 1 ) { return; }
234
235                if ( _nav ) { _nav.remove(); }
236
237                _nav = $('<ul class="' + classes.nav + '" role="navigation" />');
238                _navLinks = $('');
239
240                _items.each(function(i) {
241                    var item = $(this),
242                        category = item.data('flip-category'),
243                        itemTitle = item.data('flip-title') || item.attr('title') || i,
244                        navLink = $('<a href="#" class="' + classes.navLink + '">' + itemTitle + '</a>')
245                        .data('index', i);
246
247                    _navLinks = _navLinks.add(navLink);
248
249                    if ( category ) {
250
251                        if ( !navCategories[category] ) {
252
253                            var categoryItem = $('<li class="' + classes.navItem + ' ' + classes.navCategory + '">');
254                            var categoryLink = $('<a href="#" class="' + classes.navLink + ' ' + classes.navCategoryLink + '" data-flip-category="' + category + '">' + category + '</a>')
255                                    .data('category', category)
256                                    .data('index', i);
257
258                            navCategories[category] = $('<ul class="' + classes.navChild + '" />');
259
260                            _navLinks = _navLinks.add(categoryLink);
261
262                            categoryItem
263                                .append(categoryLink, navCategories[category])
264                                .appendTo(_nav);
265                        }
266
267                        navCategories[category].append(navLink);
268                    } else {
269                        _nav.append(navLink);
270                    }
271
272                    navLink.wrap('<li class="' + classes.navItem + '">');
273
274                });
275
276                _nav.on('click', 'a', function(e) {
277                    var index = $(this).data('index');
278                    if ( index >= 0 ) {
279                        jump(index);
280                        e.preventDefault();
281                    }
282                });
283
284                if ( settings.nav === 'after' ) { self.append(_nav); }
285                else { self.prepend(_nav); }
286
287                _navItems = _nav.find('.' + classes.navItem);
288            }
289
290            function updateNav() {
291                if ( settings.nav ) {
292
293                    var category = _currentItem.data('flip-category');
294
295                    _navItems.removeClass(classes.navCurrent);
296
297                    _navLinks
298                        .filter(function() {
299                            return ($(this).data('index') === _currentIndex || (category && $(this).data('category') === category));
300                        })
301                        .parent()
302                        .addClass(classes.navCurrent);
303
304                }
305            }
306
307            function noTransition() {
308                self.css('transition', 'none');
309                _container.css('transition', 'none');
310                _items.css('transition', 'none');
311            }
312
313            function resetTransition() {
314                self.css('transition', '');
315                _container.css('transition', '');
316                _items.css('transition', '');
317            }
318
319            function calculateBiggestItemHeight() {
320                var biggestHeight = 0,
321                    itemHeight;
322
323                _items.each(function() {
324                    itemHeight = $(this).height();
325                    if ( itemHeight > biggestHeight ) { biggestHeight = itemHeight; }
326                });
327                return biggestHeight;
328            }
329
330            function resize(skipTransition) {
331                if ( skipTransition ) { noTransition(); }
332
333                _containerWidth = _container.width();
334                _container.height(calculateBiggestItemHeight());
335
336                // Prevent maximum callstack error. #79 #74
337                if ( !_containerWidth ) {
338                    _resizeInterval = _resizeInterval || setInterval(function(){ resize(skipTransition); },500);
339                    return;
340                } else if ( _resizeInterval ) {
341                    clearInterval(_resizeInterval);
342                    _resizeInterval = false;
343                }
344
345                _items.each(function(i) {
346                    var item = $(this),
347                        width,
348                        left;
349
350                    item.attr('class', function(i, c) {
351                        return c && c.replace(classRemover, '').replace(whiteSpaceRemover, ' ');
352                    });
353
354                    width = item.outerWidth();
355
356                    if (settings.spacing !== 0 && _items.length > 1 ) {
357                        item.css('margin-right', ( width * settings.spacing ) + 'px');
358                    }
359
360                    left = item.position().left;
361                    _itemOffsets[i] = -1 * ((left + (width / 2)) - (_containerWidth / 2));
362
363                    if ( i === _items.length - 1 ) {
364                        center();
365                        if ( skipTransition ) { setTimeout(resetTransition, 1); }
366                    }
367                });
368            }
369
370            function center() {
371                var total = _items.length;
372                var item;
373                var newClass;
374                var zIndex;
375
376                _items.each(function (i) {
377                    item = $(this);
378                    newClass = ' ';
379
380                    if (i === _currentIndex)
381                    {
382                        newClass += classes.itemCurrent;
383                        zIndex = (total + 1);
384                    }
385                    else if (i < _currentIndex)
386                    {
387
388                        newClass += classes.itemPast + ' ' +
389                            classes.itemPast + '-' + (_currentIndex - i);
390                        zIndex = total - (_currentIndex - i);
391                    }
392                    else
393                    {
394                        newClass += classes.itemFuture + ' ' +
395                            classes.itemFuture + '-' + (i - _currentIndex);
396
397                        zIndex = total -  (i - _currentIndex);
398                    }
399
400                    item.css('z-index', zIndex )
401                      .attr('class',function(i, c){
402                        return c && c.replace(classRemover, '').replace(whiteSpaceRemover,' ') + newClass;
403                      });
404                });
405
406                if ( _currentIndex >= 0 ) {
407                    if ( !_containerWidth || _itemOffsets[_currentIndex] === undefined ) { resize(true); }
408
409                    if ( transformSupport ) {
410                        _container.css('transform', 'translateX(' + _itemOffsets[_currentIndex] + 'px)');
411                    } else {
412                        _container.css({ 'left': _itemOffsets[_currentIndex] + 'px' });
413                    }
414                }
415
416                updateNav();
417            }
418
419            function jump(to) {
420                var _previous = _currentIndex;
421
422                if ( _items.length <= 0 ) { return; }
423
424                if (to === 'prev') {
425                    if ( _currentIndex > 0 ) { _currentIndex--; }
426                    else if ( settings.loop ) { _currentIndex = _items.length - 1; }
427                } else if (to === 'next') {
428                    if ( _currentIndex < _items.length - 1 ) { _currentIndex++; }
429                    else if ( settings.loop ) { _currentIndex = 0; }
430                } else if (typeof to === 'number') {
431                    _currentIndex = to;
432                } else if ( to !== undefined ) {
433                    // if object is sent, get its index
434                    _currentIndex = _items.index(to);
435                    if (settings.loop && _previous != _currentIndex) {
436                        if (_previous == (_items.length - 1) && _currentIndex != (_items.length - 2)) _currentIndex = 0;
437                        if (_previous == 0 && _currentIndex != 1) _currentIndex = (_items.length - 1);
438                    }
439                }
440
441                _currentItem = _items.eq(_currentIndex);
442
443                if ( _currentIndex !== _previous && settings.onItemSwitch ) {
444                    settings.onItemSwitch.call(self, _items[_currentIndex], _items[_previous]);
445                }
446
447                center();
448
449                return self;
450            }
451
452            function play(interval) {
453                settings.autoplay = interval || settings.autoplay;
454
455                clearInterval(_playing);
456
457                _playing = setInterval(function() {
458                    var prev = _currentIndex;
459                    jump('next');
460                    if ( prev === _currentIndex && !settings.loop ) { clearInterval(_playing); }
461                }, settings.autoplay);
462
463                return self;
464            }
465
466            function stop(){
467                clearInterval(_playing);
468                _playing = 0;
469                return self;
470            }
471
472            function pause(forced) {
473                stop();
474                if ( settings.autoplay && forced ) { _playing = -1; }
475                return self;
476            }
477
478            function show() {
479                resize(true);
480                self.hide()
481                    .css('visibility', '')
482                    .addClass(classes.active)
483                    .fadeIn(settings.fadeIn);
484            }
485
486            function index() {
487
488                _container = self.find(settings.itemContainer).addClass(classes.container);
489
490                _items = _container.find(settings.itemSelector);
491
492                if ( _items.length <= 0 ) { return; }
493
494                _items
495                    .addClass(classes.item)
496                    // Wrap inner content
497                    .each(function() {
498                        var item = $(this);
499                        if ( !item.children('.' + classes.itemContent ).length) {
500                            item.wrapInner('<div class="' + classes.itemContent + '" />');
501                        }
502                    });
503
504                // Navigate directly to an item by clicking
505                if ( settings.click ) {
506                    _items.on('click.flipster touchend.flipster', function (e) {
507                        if ( !_startDrag ) {
508                            if ( !$(this).hasClass(classe
508s.itemCurrent) ) { e.preventDefault(); }
509                            jump(this);
510                        }
511                    });
512                }
513
514                // Insert navigation if enabled.
515                buildButtons();
516                buildNav();
517
518                if ( _currentIndex >= 0 ) { jump(_currentIndex); }
519
520                return self;
521            }
522
523            function keyboardEvents(elem) {
524                if ( settings.keyboard ) {
525                    elem[0].tabIndex = 0;
526                    elem.on('keydown.flipster', throttle(function(e) {
527                        var code = e.which;
528                        if ( code === 37 || code === 39 ) {
529                            jump( code === 37 ? 'prev' : 'next' );
530                            e.preventDefault();
531                        }
532                    }, 250, true));
533                }
534            }
535
536            function wheelEvents(elem) {
537                if ( settings.scrollwheel ) {
538                    var _wheelInside = false,
539                        _actionThrottle = 0,
540                        _throttleTimeout = 0,
541                        _delta = 0,
542                        _dir, _lastDir,
543                        _isMozilla = /mozilla/.test(navigator.userAgent.toLowerCase()) && !/webkit/.test(navigator.userAgent.toLowerCase());
544
545                    elem
546                        .on('mousewheel.flipster wheel.flipster', function() { _wheelInside = true; })
547                        .on('mousewheel.flipster wheel.flipster', throttle(function(e) {
548
549                            // Reset after a period without scrolling.
550                            clearTimeout(_throttleTimeout);
551                            _throttleTimeout = setTimeout(function() {
552                                _actionThrottle = 0;
553                                _delta = 0;
554                            }, 300);
555
556                            e = e.originalEvent;
557
558                            // Add to delta (+=) so that continuous small events can still get past the speed limit, and quick direction reversals get cancelled out
559                            _delta += (e.wheelDelta || (e.deltaY + e.deltaX) * -1); // Invert numbers for Firefox
560
561                            // Don't trigger unless the scroll is decent speed.
562                            // There's need to check if _isMozilla because of different working delta in Firefox (in FF scroll delta is less than 25 or more than -25)
563                            if ( Math.abs(_delta) < 25 && ! _isMozilla) { return; }
564
565                            _actionThrottle++;
566
567                            _dir = (_delta > 0 ? 'prev' : 'next');
568
569                            // Reset throttle if direction changed.
570                            if ( _lastDir !== _dir ) { _actionThrottle = 0; }
571                            _lastDir = _dir;
572
573                            // Regular scroll wheels trigger less events, so they don't need to be throttled. Trackpads trigger many events (inertia), so only trigger jump every three times to slow things down.
574                            if ( _actionThrottle < 6 || _actionThrottle % 3 === 0 ) { jump(_dir); }
575
576                            _delta = 0;
577
578                        }, 50));
579
580                    // Disable mousewheel on window if event began in elem.
581                    $window.on('mousewheel.flipster wheel.flipster', function(e) {
582                        if ( _wheelInside ) {
583                            e.preventDefault();
584                            _wheelInside = false;
585                        }
586                    });
587                }
588            }
589
590            function touchEvents(elem) {
591                if (settings.touch) {
592                    var _startDragX, _startDragY, x, y, offsetY, offsetX;
593
594                    elem.on({
595                        'touchstart.flipster': function (e) {
596                            // e.preventDefault();
597                            e = e.originalEvent;
598                            _startDragX = (e.touches ? e.touches[0].clientX : e.clientX);
599                            _startDragY = (e.touches ? e.touches[0].clientY : e.clientY);
600                        },
601
602                        'touchmove.flipster': function (e) {
603                            // e.preventDefault();
604                            e = e.originalEvent;
605                            x = (e.touches ? e.touches[0].clientX : e.clientX);
606                            y = (e.touches ? e.touches[0].clientY : e.clientY);
607                            offsetX = x - _startDragX;
608                            offsetY = y - _startDragY;
609                            if (Math.abs(offsetX) > 30 && Math.abs(offsetY) < 100) {
610                                e.preventDefault();
611                            }
612                        },
613
614                        'touchend.flipster touchcancel.flipster ': function () {
615                            offsetX = x - _startDragX;
616                            offsetY = y - _startDragY;
617                            if (Math.abs(offsetX) > 30 && Math.abs(offsetY) < 100) {
618                                if (offsetX > 0) {
619                                    jump('prev');
620                                }
621                                else {
622                                    jump('next');
623                                }
624                            }
625                        }
626                    });
627                }
628            }
629
630            function init() {
631
632                var style;
633
634                self.css('visibility', 'hidden');
635
636                index();
637
638                if ( _items.length <= 0 ) {
639                    self.css('visibility', '');
640                    return;
641                }
642
643                style = (settings.style ? 'flipster--' + settings.style.split(' ').join(' flipster--') : false);
644
645                self.addClass([
646                    classes.main,
647                    (transformSupport ? 'flipster--transform' : ' flipster--no-transform'),
648                    style, // 'flipster--'+settings.style : '' ),
649                    (settings.click ? 'flipster--click' : '')
650                ].join(' '));
651
652                // Set the starting item
653                if ( settings.start ) {
654                    // Find the middle item if start = center
655                    _currentIndex = ( settings.start === 'center' ? Math.floor(_items.length / 2) : settings.start );
656                }
657
658                jump(_currentIndex);
659
660                var images = self.find('img');
661
662                if ( images.length ) {
663                    var imagesLoaded = 0;
664
665                    // Resize after all images have loaded.
666                    images.on('load', function() {
667                        imagesLoaded++;
668                        if ( imagesLoaded >= images.length ) { show(); }
669                    });
670
671                    // Fallback to show Flipster while images load in case it takes a while.
672                    setTimeout(show, 750);
673                } else {
674                    show();
675                }
676
677                // Attach event bindings.
678                $window.on('resize.flipster', throttle(resize, 400));
679                // Attach scroll event 1StepAhead.
680                $window.on('scroll.flipster', doTheScroll);
681                if ( settings.autoplay ) { play(); }
682
683                if ( settings.pauseOnHover ) {
684                    _container
685                        .on('mouseenter.flipster', function(){
686                            if (_playing) { pause(true); }
687                            else { stop() }
688                        })
689                        .on('mouseleave.flipster', function() {
690                            if ( _playing === -1 ) { play(); }
691                        });
692                }
693
694                // Stop flipster
695                function doTheScroll() {
696                    stop();
697                    return false;
698                }
699
700                keyboardEvents(self);
701                wheelEvents(_container);
702                touchEvents(_container);
703            }
704
705            // public methods
706            methods = {
707                jump: jump,
708                next: function() { return jump('next'); },
709                prev: function() { return jump('prev'); },
710                play: play,
711                stop: stop,
712                pause: pause,
713                index: index
714            };
715            self.data('methods', methods);
716
717            // Initialize if flipster is not already active.
718            if ( !self.hasClass(classes.active) ) { init(); }
719        });
720    };
721})(jQuery, window);

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.