PageSourceSearch

https://aast.edu/template/en/about/js/scripts.js

js aast.edu collected 2026-09-24 09:23:36 UTC 64,443 bytes, 1,788 lines download raw bytes

1/***********************************************************************************************
2         jquery.backstretch.js
3**********************************************************************************************/
4/*
5 * Backstretch
6 * http://srobbin.com/jquery-plugins/backstretch/
7 *
8 * Copyright (c) 2013 Scott Robbin
9 * Licensed under the MIT license.
10 */
11
12
13(function ($, window, undefined) {
14    'use strict';
15
16    /** @const */
17    var YOUTUBE_REGEXP = /^.*(youtu\.be\/|youtube\.com\/v\/|youtube\.com\/embed\/|youtube\.com\/watch\?v=|youtube\.com\/watch\?.*\&v=)([^#\&\?]*).*/i;
18
19    /* PLUGIN DEFINITION
20     * ========================= */
21
22    $.fn.backstretch = function (images, options) {
23        var args = arguments;
24
25        /*
26         * Scroll the page one pixel to get the right window height on iOS
27         * Pretty harmless for everyone else
28         */
29        if ($(window).scrollTop() === 0) {
30            window.scrollTo(0, 0);
31        }
32
33        var returnValues;
34
35        this.each(function (eachIndex) {
36            var $this = $(this),
37                obj = $this.data('backstretch');
38
39            // Do we already have an instance attached to this element?
40            if (obj) {
41
42                // Is this a method they're trying to execute?
43                if (typeof args[0] === 'string' &&
44                    typeof obj[args[0]] === 'function') {
45
46                    // Call the method
47                    var returnValue = obj[args[0]].apply(obj, Array.prototype.slice.call(args, 1));
48                    if (returnValue === obj) { // If a method is chaining
49                        returnValue = undefined;
50                    }
51                    if (returnValue !== undefined) {
52                        returnValues = returnValues || [];
53                        returnValues[eachIndex] = returnValue;
54                    }
55
56                    return; // Nothing further to do
57                }
58
59                // Merge the old options with the new
60                options = $.extend(obj.options, options);
61
62                // Remove the old instance
63                if (typeof obj === 'object' && 'destroy' in obj) {
64                    obj.destroy(true);
65                }
66            }
67
68            // We need at least one image
69            if (!images || (images && images.length === 0)) {
70                var cssBackgroundImage = $this.css('background-image');
71                if (cssBackgroundImage && cssBackgroundImage !== 'none') {
72                    images = [{
73                        url: $this.css('backgroundImage').replace(/url\(|\)|"|'/g, "")
74                    }];
75                } else {
76                    $.error('No images were supplied for Backstretch, or element must have a CSS-defined background image.');
77                }
78            }
79
80            obj = new Backstretch(this, images, options || {});
81            $this.data('backstretch', obj);
82        });
83
84        return returnValues ? returnValues.length === 1 ? returnValues[0] : returnValues : this;
85    };
86
87    // If no element is supplied, we'll attach to body
88    $.backstretch = function (images, options) {
89        // Return the instance
90        return $('body')
91            .backstretch(images, options)
92            .data('backstretch');
93    };
94
95    // Custom selector
96    $.expr[':'].backstretch = function (elem) {
97        return $(elem).data('backstretch') !== undefined;
98    };
99
100    /* DEFAULTS
101     * ========================= */
102
103    $.fn.backstretch.defaults = {
104        duration: 5000 // Amount of time in between slides (if slideshow)
105            ,
106        transition: 'fade' // Type of transition between slides
107            ,
108        transitionDuration: 0 // Duration of transition between slides
109            ,
110        animateFirst: true // Animate the transition of first image of slideshow in?
111            ,
112        alignX: 0.5 // The x-alignment for the image, can be 'left'|'center'|'right' or any number between 0.0 and 1.0
113            ,
114        alignY: 0.5 // The y-alignment for the image, can be 'top'|'center'|'bottom' or any number between 0.0 and 1.0
115            ,
116        paused: false // Whether the images should slide after given duration
117            ,
118        start: 0 // Index of the first image to show
119            ,
120        preload: 2 // How many images preload at a time?
121            ,
122        preloadSize: 1 // How many images can we preload in parallel?
123            ,
124        resolutionRefreshRate: 2500 // How long to wait before switching resolution?
125            ,
126        resolutionChangeRatioThreshold: 0.1 // How much a change should it be before switching resolution?
127    };
128
129    /* STYLES
130     *
131     * Baked-in styles that we'll apply to our elements.
132     * In an effort to keep the plugin simple, these are not exposed as options.
133     * That said, anyone can override these in their own stylesheet.
134     * ========================= */
135    var styles = {
136        wrap: {
137            left: 0,
138            top: 0,
139            overflow: 'hidden',
140            margin: 0,
141            padding: 0,
142            height: '100%',
143            width: '100%',
144            zIndex: -999999
145        },
146        itemWrapper: {
147            position: 'absolute',
148            display: 'none',
149            margin: 0,
150            padding: 0,
151            border: 'none',
152            width: '100%',
153            height: '100%',
154            zIndex: -999999
155        },
156        item: {
157            position: 'absolute',
158            margin: 0,
159            padding: 0,
160            border: 'none',
161            width: '100%'
162                // , height: '100%'
163                ,
164            maxWidth: 'none'
165        }
166    };
167
168    /* Given an array of different options for an image,
169     * choose the optimal image for the container size.
170     *
171     * Given an image template (a string with {{ width }} and/or
172     * {{height}} inside) and a container object, returns the
173     * image url with the exact values for the size of that
174     * container.
175     *
176     * Returns an array of urls optimized for the specified resolution.
177     *
178     */
179    var optimalSizeImages = (function () {
180
181        /* Sorts the array of image sizes based on width */
182        var widthInsertSort = function (arr) {
183            for (var i = 1; i < arr.length; i++) {
184                var tmp = arr[i],
185                    j = i;
186                while (arr[j - 1] && parseInt(arr[j - 1].width, 10) > parseInt(tmp.width, 10)) {
187                    arr[j] = arr[j - 1];
188                    --j;
189                }
190                arr[j] = tmp;
191            }
192
193            return arr;
194        };
195
196        /* Given an array of various sizes of the same image and a container width,
197         * return the best image.
198         */
199        var selectBest = function (containerWidth, containerHeight, imageSizes) {
200
201            var devicePixelRatio = window.devicePixelRatio || 1;
202            var deviceOrientation = getDeviceOrientation();
203            var windowOrientation = getWindowOrientation();
204            var wrapperOrientation = (containerHeight > containerWidth) ?
205                'portrait' :
206                (containerWidth > containerHeight ? 'landscape' : 'square');
207
208            var lastAllowedImage = 0;
209            var testWidth;
210
211            for (var j = 0, image; j < imageSizes.length; j++) {
212
213                image = imageSizes[j];
214
215                // In case a new image was pushed in, process it:
216                if (typeof image === 'string') {
217                    image = imageSizes[j] = {
218                        url: image
219                    };
220                }
221
222                if (image.pixelRatio && image.pixelRatio !== 'auto' && parseFloat(image.pixelRatio) !== devicePixelRatio) {
223                    // We disallowed choosing this image for current device pixel ratio,
224                    // So skip this one.
225                    continue;
226                }
227
228                if (image.deviceOrientation && image.deviceOrientation !== deviceOrientation) {
229                    // We disallowed choosing this image for current device orientation,
230                    // So skip this one.
231                    continue;
232                }
233
234                if (image.windowOrientation && image.windowOrientation !== deviceOrientation) {
235                    // We disallowed choosing this image for current window orientation,
236                    // So skip this one.
237                    continue;
238                }
239
240                if (image.orientation && image.orientation !== wrapperOrientation) {
241                    // We disallowed choosing this image for current element's orientation,
242                    // So skip this one.
243                    continue;
244                }
245
246                // Mark this one as the last one we investigated
247                // which does not violate device pixel ratio rules.
248                // We may choose this one later if there's no match.
249                lastAllowedImage = j;
250
251                // For most images, we match the specified width against element width,
252                // And enforcing a limit depending on the "pixelRatio" property if specified.
253                // But if a pixelRatio="auto", then we consider the width as the physical width of the image,
254                // And match it while considering the device's pixel ratio.
255                testWidth = containerWidth;
256                if (image.pixelRatio === 'auto') {
257                    containerWidth *= devicePixelRatio;
258                }
259
260                // Stop when the width of the image is larger or equal to the container width
261                if (image.width >= testWidth) {
262                    break;
263                }
264            }
265
266            // Use the image located at where we stopped
267            return imageSizes[Math.min(j, lastAllowedImage)];
268        };
269
270        var replaceTagsInUrl = function (url, templateReplacer) {
271
272            if (typeof url === 'string') {
273                url = url.replace(/{{(width|height)}}/g, templateReplacer);
274            } else if (url instanceof Array) {
275                for (var i = 0; i < url.length; i++) {
276                    if (url[i].src) {
277                        url[i].src = replaceTagsInUrl(url[i].src, templateReplacer);
278                    } else {
279                        url[i] = replaceTagsInUrl(url[i], templateReplacer);
280                    }
281                }
282            }
283
284            return url;
285        };
286
287        return function ($container, images) {
288            var containerWidth = $container.width(),
289                containerHeight = $container.height();
290
291            var chosenImages = [];
292
293            var templateReplacer = function (match, key) {
294                if (key === 'width') {
295                    return containerWidth;
296                }
297                if (key === 'height') {
298                    return containerHeight;
299                }
300                return match;
301            };
302
303            for (var i = 0; i < images.length; i++) {
304                if ($.isArray(images[i])) {
305                    images[i] = widthInsertSort(images[i]);
306                    var chosen = selectBest(containerWidth, containerHeight, images[i]);
307                    chosenImages.push(chosen);
308                } else {
309                    // In case a new image was pushed in, process it:
310                    if (typeof images[i] === 'string') {
311                        images[i] = {
312                            url: images[i]
313                        };
314                    }
315
316                    var item = $.extend({}, images[i]);
317                    item.url = replaceTagsInUrl(item.url, templateReplacer);
318                    chosenImages.push(item);
319                }
320            }
321            return chosenImages;
322        };
323
324    })();
325
326    var isVideoSource = function (source) {
327        return YOUTUBE_REGEXP.test(source.url) || source.isVideo;
328    };
329
330    /* Preload images */
331    var preload = (function (sources, startAt, count, batchSize, callback) {
332        // Plugin cache
333        var cache = [];
334
335        // Wrapper for cache
336        var caching = function (image) {
337            for (var i = 0; i < cache.length; i++) {
338                if (cache[i].src === image.src) {
339                    return cache[i];
340                }
341            }
342            cache.push(image);
343            return image;
344        };
345
346        // Execute callback
347        var exec = function (sources, callback, last) {
348            if (typeof callback === 'function') {
349                callback.call(sources, last);
350            }
351        };
352
353        // Closure to hide cache
354        return function preload(sources, startAt, count, batchSize, callback) {
355            // Check input data
356            if (typeof sources === 'undefined') {
357                return;
358            }
359            if (!$.isArray(sources)) {
360                sources = [sources];
361            }
362
363            if (arguments.length < 5 && typeof arguments[arguments.length - 1] === 'function') {
364                callback = arguments[arguments.length - 1];
365            }
366
367            startAt = (typeof startAt === 'function' || !startAt) ? 0 : startAt;
368            count = (typeof count === 'function' || !count || count < 0) ? sources.length : Math.min(count, sources.length);
369            batchSize = (typeof batchSize === 'function' || !batchSize) ? 1 : batchSize;
370
371            if (startAt >= sources.length) {
372                startAt = 0;
373                count = 0;
374            }
375            if (batchSize < 0) {
376                batchSize = count;
377            }
378            batchSize = Math.min(batchSize, count);
379
380            var next = sources.slice(startAt + batchSize, count - batchSize);
381            sources = sources.slice(startAt, batchSize);
382            count = sources.length;
383
384            // If sources array is empty
385            if (!count) {
386                exec(sources, callback, true);
387                return;
388            }
389
390            // Image loading callback
391            var countLoaded = 0;
392
393            var loaded = function () {
394                countLoaded++;
395                if (countLoaded !== count) {
396                    return;
397                }
398
399                exec(sources, callback, !next);
400                preload(next, 0, 0, batchSize, callback);
401            };
402
403            // Loop sources to preload
404            var image;
405
406            for (var i = 0; i < sources.length; i++) {
407
408                if (isVideoSource(sources[i])) {
409
410                    // Do not preload videos. There are issues with that.
411                    // First - we need to keep an instance of the preloaded and use that exactly, not a copy.
412                    // Second - there are memory issues.
413                    // If there will be a requirement from users - I'll try to implement this.
414
415                    continue;
416
417                } else {
418
419                    image = new Image();
420                    image.src = sources[i].url;
421
422                    image = caching(image);
423
424                    if (image.complete) {
425                        loaded();
426                    } else {
427                        $(image).on('load error', loaded);
428                    }
429
430                }
431
432            }
433        };
434    })();
435
436    /* Process images array */
437    var processImagesArray = function (images) {
438        var processed = [];
439        for (var i = 0; i < images.length; i++) {
440            if (typeof images[i] === 'string') {
441                processed.push({
442                    url: images[i]
443                });
444            } else if ($.isArray(images[i])) {
445                processed.push(processImagesArray(images[i]));
446            } else {
447                processed.push(processOptions(images[i]));
448            }
449        }
450        return processed;
451    };
452
453    /* Process options */
454    var processOptions = function (options, required) {
455
456        // Convert old options
457
458        // centeredX/centeredY are deprecated
459        if (options.centeredX || options.centeredY) {
460            if (window.console && window.console.log) {
461                window.console.log('jquery.backstretch: `centeredX`/`centeredY` is deprecated, please use `alignX`/`alignY`');
462            }
463            if (options.centeredX) {
464                options.alignX = 0.5;
465            }
466            if (options.centeredY) {
467                options.alignY = 0.5;
468            }
469        }
470
471        // Deprecated spec
472        if (options.speed !== undefined) {
473
474            if (window.console && window.console.log) {
475                window.console.log('jquery.backstretch: `speed` is deprecated, please use `transitionDuration`');
476            }
477
478            options.transitionDuration = options.speed;
479            options.transition = 'fade';
480        }
481
482        // Typo
483        if (options.resolutionChangeRatioTreshold !== undefined) {
484            window.console.log('jquery.backstretch: `treshold` is a typo!');
485            options.resolutionChangeRatioThreshold = options.resolutionChangeRatioTreshold;
486        }
487
488        // Current spec that needs processing
489
490        if (options.fadeFirst !== undefined) {
491            options.animateFirst = options.fadeFirst;
492        }
493
494        if (options.fade !== undefined) {
495            options.transitionDuration = options.fade;
496            options.transition = 'fade';
497        }
498
499        if (options.scale) {
500            options.scale = validScale(options.scale);
501        }
502
503        return processAlignOptions(options);
504    };
505
506    /* Process align options */
507    var processAlignOptions = function (options, required) {
508        if (options.alignX === 'left') {
509            options.alignX = 0.0;
510        } else if (options.alignX === 'center') {
511            options.alignX = 0.5;
512        } else if (options.alignX === 'right') {
513            options.alignX = 1.0;
514        } else {
515            if (options.alignX !== undefined || required) {
516                options.alignX = parseFloat(options.alignX);
517                if (isNaN(options.alignX)) {
518                    options.alignX = 0.5;
519                }
520            }
521        }
522
523        if (options.alignY === 'top') {
524            options.alignY = 0.0;
525        } else if (options.alignY === 'center') {
526            options.alignY = 0.5;
527        } else if (options.alignY === 'bottom') {
528            options.alignY = 1.0;
529        } else {
530            if (options.alignX !== undefined || required) {
531                options.alignY = parseFloat(options.alignY);
532                if (isNaN(options.alignY)) {
533                    options.alignY = 0.5;
534                }
535            }
536        }
537
538        return options;
539    };
540
541    var SUPPORTED_SCALE_OPTIONS = {
542        'cover': 'cover',
543        'fit': 'fit',
544        'fit-smaller': 'fit-smaller',
545        'fill': 'fill'
546    };
547
548    function validScale(scale) {
549        if (!SUPPORTED_SCALE_OPTIONS.hasOwnProperty(scale)) {
550            return 'cover';
551        }
552        return scale;
553    }
554
555    /* CLASS DEFINITION
556     * ========================= */
557    var Backstretch = function (container, images, options) {
558        this.options = $.extend({}, $.fn.backstretch.defaults, options || {});
559
560        this.firstShow = true;
561
562        // Process options
563        processOptions(this.options, true);
564
565        /* In its simplest form, we allow Backstretch to be called on an image path.
566         * e.g. $.backstretch('/path/to/image.jpg')
567         * So, we need to turn this back into an array.
568         */
569        this.images = processImagesArray($.isArray(images) ? images : [images]);
570
571        /**
572         * Paused-Option
573         */
574        if (this.options.paused) {
575            this.paused = true;
576        }
577
578        /**
579         * Start-Option (Index)
580         */
581        if (this.options.start >= this.images.length) {
582            this.options.start = this.images.length - 1;
583        }
584        if (this.options.start < 0) {
585            this.options.start = 0;
586        }
587
588        // Convenience reference to know if the container is body.
589        this.isBody = container === document.body;
590
591        /* We're keeping track of a few different elements
592         *
593         * Container: the element that Backstretch was called on.
594         * Wrap: a DIV that we place the image into, so we can hide the overflow.
595         * Root: Convenience reference to help calculate the correct height.
596         */
597        var $window = $(window);
598        this.$container = $(container);
599        this.$root = this.isBody ? supportsFixedPosition ? $window : $(document) : this.$container;
600
601        this.originalImages = this.images;
602        this.images = optimalSizeImages(
603            this.options.alwaysTestWindowResolution ? $window : this.$root,
604            this.originalImages);
605
606        /**
607         * Pre-Loading.
608         * This is the first image, so we will preload a minimum of 1 images.
609         */
610        preload(this.images, this.options.start || 0, this.options.preload || 1);
611
612        // Don't create a new wrap if one already exists (from a previous instance of Backstretch)
613        var $existing = this.$container.children(".backstretch").first();
614        this.$wrap = $existing.length ? $existing :
615            $('<div class="backstretch"></div>')
616            .css(this.options.bypassCss ? {} : styles.wrap)
617            .appendTo(this.$container);
618
619        if (!this.options.bypassCss) {
620
621            // Non-body elements need some style adjustments
622            if (!this.isBody) {
623                // If the container is statically positioned, we need to make it relative,
624                // and if no zIndex is defined, we should set it to zero.
625                var position = this.$container.css('position'),
626                    zIndex = this.$container.css('zIndex');
627
628                this.$container.css({
629                    position: position === 'static' ? 'relative' : position,
630                    zIndex: zIndex === 'auto' ? 0 : zIndex
631                });
632
633                // Needs a higher z-index
634                this.$wrap.css({
635                    zIndex: -999998
636                });
637            }
638
639            // Fixed or absolute positioning?
640            this.$wrap.css({
641                position: this.isBody && supportsFixedPosition ? 'fixed' : 'absolute'
642            });
643
644        }
645
646        // Set the first image
647        this.index = this.options.start;
648        this.show(this.index);
649
650        // Listen for resize
651        $window.on('resize.backstretch', $.proxy(this.resize, this))
652            .on('orientationchange.backstretch', $.proxy(function () {
653                // Need to do this in order to get the right window height
654                if (this.isBody && window.pageYOffset === 0) {
655                    window.scrollTo(0, 1);
656                    this.resize();
657                }
658            }, this));
659    };
660
661    var performTransition = function (options) {
662
663        var transition = options.transition || 'fade';
664
665        // Look for multiple options
666        if (typeof transition === 'string' && transition.indexOf('|') > -1) {
667            transition = transition.split('|');
668        }
669
670        if (transition instanceof Array) {
671            transition = transition[Math.round(Math.random() * (transition.length - 1))];
672        }
673
674        var $new = options['new'];
675        var $old = options['old'] ? options['old'] : $([]);
676
677        switch (transition.toString().toLowerCase()) {
678
679            default:
680            case 'fade':
681                $new.fadeIn({
682                    duration: options.duration,
683                    complete: options.complete,
684                    easing: options.easing || undefined
685                });
686                break;
687
688            case 'fadeinout':
689            case 'fade_in_out':
690
691                var fadeInNew = function () {
692                    $new.fadeIn({
693                        duration: options.duration / 2,
694                        complete: options.complete,
695                        easing: options.easing || undefined
696                    });
697                };
698
699                if ($old.length) {
700                    $old.fadeOut({
701                        duration: options.duration / 2,
702                        complete: fadeInNew,
703                        easing: options.easing || undefined
704                    });
705                } else {
706                    fadeInNew();
707                }
708
709                break;
710
711            case 'pushleft':
712            case 'push_left':
713            case 'pushright':
714            case 'push_right':
715            case 'pushup':
716            case 'push_up':
717            case 'pushdown':
718            case 'push_down':
719            case 'coverleft':
720            case 'cover_left':
721            case 'coverright':
722            case 'cover_right':
723            case 'coverup':
724            case 'cover_up':
725            case 'coverdown':
726            case 'cover_down':
727
728                var transitionParts = transition.match(/^(cover|push)_?(.*)$/);
729
730                var animProp = transitionParts[2] === 'left' ? 'right' :
731                    transitionParts[2] === 'right' ? 'left' :
732                    transitionParts[2] === 'down' ? 'top' :
733                    transitionParts[2] === 'up' ? 'bottom' :
734                    'right';
735
736                var newCssStart = {
737                        'display': ''
738                    },
739                    newCssAnim = {};
740                newCssStart[animProp] = '-100%';
741                newCssAnim[animProp] = 0;
742
743                $new
744                    .css(newCssStart)
745                    .animate(newCssAnim, {
746                        duration: options.duration,
747                        complete: function () {
748                            $new.css(animProp, '');
749                            options.complete.apply(this, arguments);
750                        },
751                        easing: options.easing || undefined
752                    });
753
754                if (transitionParts[1] === 'push' && $old.length) {
755                    var oldCssAnim = {};
756                    oldCssAnim[animProp] = '100%';
757
758                    $old
759                        .animate(oldCssAnim, {
760                            duration: options.duration,
761                            complete: function () {
762                                $old.css('display', 'none');
763                            },
764                            easing: options.easing || undefined
765                        });
766                }
767
768                break;
769        }
770
771    };
772
773    /* PUBLIC METHODS
774     * ========================= */
775    Backstretch.prototype = {
776
777        resize: function () {
778                try {
779
780                    // Check for a better suited image after the resize
781                    var $resTest = this.options.alwaysTestWindowResolution ? $(window) : this.$root;
782                    var newContainerWidth = $resTest.width();
783                    var newContainerHeight = $resTest.height();
784                    var changeRatioW = newContainerWidth / (this._lastResizeContainerWidth || 0);
785                    var changeRatioH = newContainerHeight / (this._lastResizeContainerHeight || 0
785);
786                    var resolutionChangeRatioThreshold = this.options.resolutionChangeRatioThreshold || 0.0;
787
788                    // check for big changes in container size
789                    if ((newContainerWidth !== this._lastResizeContainerWidth ||
790                            newContainerHeight !== this._lastResizeContainerHeight) &&
791                        ((Math.abs(changeRatioW - 1) >= resolutionChangeRatioThreshold || isNaN(changeRatioW)) ||
792                            (Math.abs(changeRatioH - 1) >= resolutionChangeRatioThreshold || isNaN(changeRatioH)))) {
793
794                        this._lastResizeContainerWidth = newContainerWidth;
795                        this._lastResizeContainerHeight = newContainerHeight;
796
797                        // Big change: rebuild the entire images array
798                        this.images = optimalSizeImages($resTest, this.originalImages);
799
800                        // Preload them (they will be automatically inserted on the next cycle)
801                        if (this.options.preload) {
802                            preload(this.images, (this.index + 1) % this.images.length, this.options.preload);
803                        }
804
805                        // In case there is no cycle and the new source is different than the current
806                        if (this.images.length === 1 &&
807                            this._currentImage.url !== this.images[0].url) {
808
809                            // Wait a little an update the image being showed
810                            var that = this;
811                            clearTimeout(that._selectAnotherResolutionTimeout);
812                            that._selectAnotherResolutionTimeout = setTimeout(function () {
813                                that.show(0);
814                            }, this.options.resolutionRefreshRate);
815                        }
816                    }
817
818                    var bgCSS = {
819                            left: 0,
820                            top: 0,
821                            right: 'auto',
822                            bottom: 'auto'
823                        }
824
825                        ,
826                        boxWidth = this.isBody ? this.$root.width() : this.$root.innerWidth(),
827                        boxHeight = this.isBody ?
828                        (window.innerHeight ? window.innerHeight : this.$root.height()) :
829                        this.$root.innerHeight()
830
831                        ,
832                        naturalWidth = this.$itemWrapper.data('width'),
833                        naturalHeight = this.$itemWrapper.data('height')
834
835                        ,
836                        ratio = (naturalWidth / naturalHeight) || 1
837
838                        ,
839                        alignX = this._currentImage.alignX === undefined ? this.options.alignX : this._currentImage.alignX,
840                        alignY = this._currentImage.alignY === undefined ? this.options.alignY : this._currentImage.alignY,
841                        scale = validScale(this._currentImage.scale || this.options.scale);
842
843                    var width, height;
844
845                    if (scale === 'fit' || scale === 'fit-smaller') {
846                        width = naturalWidth;
847                        height = naturalHeight;
848
849                        if (width > boxWidth ||
850                            height > boxHeight ||
851                            scale === 'fit-smaller') {
852                            var boxRatio = boxWidth / boxHeight;
853                            if (boxRatio > ratio) {
854                                width = Math.floor(boxHeight * ratio);
855                                height = boxHeight;
856                            } else if (boxRatio < ratio) {
857                                width = boxWidth;
858                                height = Math.floor(boxWidth / ratio);
859                            } else {
860                                width = boxWidth;
861                                height = boxHeight;
862                            }
863                        }
864                    } else if (scale === 'fill') {
865                        width = boxWidth;
866                        height = boxHeight;
867                    } else { // 'cover'
868                        width = Math.max(boxHeight * ratio, boxWidth);
869                        height = Math.max(width / ratio, boxHeight);
870                    }
871
872                    // Make adjustments based on image ratio
873                    bgCSS.top = -(height - boxHeight) * alignY;
874                    bgCSS.left = -(width - boxWidth) * alignX;
875                    bgCSS.width = width;
876                    bgCSS.height = height;
877
878                    if (!this.options.bypassCss) {
879
880                        this.$wrap
881                            .css({
882                                width: boxWidth,
883                                height: boxHeight
884                            })
885                            .find('>.backstretch-item').not('.deleteable')
886                            .each(function () {
887                                var $wrapper = $(this);
888                                $wrapper.find('img,video,iframe')
889                                    .css(bgCSS);
890                            });
891                    }
892
893                    var evt = $.Event('backstretch.resize', {
894                        relatedTarget: this.$container[0]
895                    });
896                    this.$container.trigger(evt, this);
897
898                } catch (err) {
899                    // IE7 seems to trigger resize before the image is loaded.
900                    // This try/catch block is a hack to let it fail gracefully.
901                }
902
903                return this;
904            }
905
906            // Show the slide at a certain position
907            ,
908        show: function (newIndex, overrideOptions) {
909
910                // Validate index
911                if (Math.abs(newIndex) > this.images.length - 1) {
912                    return;
913                }
914
915                // Vars
916                var that = this,
917                    $oldItemWrapper = that.$wrap.find('>.backstretch-item').addClass('deleteable'),
918                    oldVideoWrapper = that.videoWrapper,
919                    evtOptions = {
920                        relatedTarget: that.$container[0]
921                    };
922
923                // Trigger the "before" event
924                that.$container.trigger($.Event('backstretch.before', evtOptions), [that, newIndex]);
925
926                // Set the new frame index
927                this.index = newIndex;
928                var selectedImage = that.images[newIndex];
929
930                // Pause the slideshow
931                clearTimeout(that._cycleTimeout);
932
933                // New image
934
935                delete that.videoWrapper; // Current item may not be a video
936
937                var isVideo = isVideoSource(selectedImage);
938                if (isVideo) {
939                    that.videoWrapper = new VideoWrapper(selectedImage);
940                    that.$item = that.videoWrapper.$video.css('pointer-events', 'none');
941                } else {
942                    that.$item = $('<img />');
943                }
944
945                that.$itemWrapper = $('<div class="backstretch-item">')
946                    .append(that.$item);
947
948                if (this.options.bypassCss) {
949                    that.$itemWrapper.css({
950                        'display': 'none'
951                    });
952                } else {
953                    that.$itemWrapper.css(styles.itemWrapper);
954                    that.$item.css(styles.item);
955                }
956
957                that.$item.bind(isVideo ? 'canplay' : 'load', function (e) {
958                    var $this = $(this),
959                        $wrapper = $this.parent(),
960                        options = $wrapper.data('options');
961
962                    if (overrideOptions) {
963                        options = $.extend({}, options, overrideOptions);
964                    }
965
966                    var imgWidth = this.naturalWidth || this.videoWidth || this.width,
967                        imgHeight = this.naturalHeight || this.videoHeight || this.height;
968
969                    // Save the natural dimensions
970                    $wrapper
971                        .data('width', imgWidth)
972                        .data('height', imgHeight);
973
974                    var getOption = function (opt) {
975                        return options[opt] !== undefined ?
976                            options[opt] :
977                            that.options[opt];
978                    };
979
980                    var transition = getOption('transition');
981                    var transitionEasing = getOption('transitionEasing');
982                    var transitionDuration = getOption('transitionDuration');
983
984                    // Show the image, then delete the old one
985                    var bringInNextImage = function () {
986
987                        if (oldVideoWrapper) {
988                            oldVideoWrapper.stop();
989                            oldVideoWrapper.destroy();
990                        }
991
992                        $oldItemWrapper.remove();
993
994                        // Resume the slideshow
995                        if (!that.paused && that.images.length > 1) {
996                            that.cycle();
997                        }
998
999                        // Now we can clear the background on the element, to spare memory
1000                        if (!that.options.bypassCss && !that.isBody) {
1001                            that.$container.css('background-image', 'none');
1002                        }
1003
1004                        // Trigger the "after" and "show" events
1005                        // "show" is being deprecated
1006                        $(['after', 'show']).each(function () {
1007                            that.$container.trigger($.Event('backstretch.' + this, evtOptions), [that, newIndex]);
1008                        });
1009
1010                        if (isVideo) {
1011                            that.videoWrapper.play();
1012                        }
1013                    };
1014
1015                    if ((that.firstShow && !that.options.animateFirst) || !transitionDuration || !transition) {
1016                        // Avoid transition on first show or if there's no transitionDuration value
1017                        $wrapper.show();
1018                        bringInNextImage();
1019                    } else {
1020
1021                        performTransition({
1022                            'new': $wrapper,
1023                            old: $oldItemWrapper,
1024                            transition: transition,
1025                            duration: transitionDuration,
1026                            easing: transitionEasing,
1027                            complete: bringInNextImage
1028                        });
1029
1030                    }
1031
1032                    that.firstShow = false;
1033
1034                    // Resize
1035                    that.resize();
1036                });
1037
1038                that.$itemWrapper.appendTo(that.$wrap);
1039
1040                that.$item.attr('alt', selectedImage.alt || '');
1041                that.$itemWrapper.data('options', selectedImage);
1042
1043                if (!isVideo) {
1044                    that.$item.attr('src', selectedImage.url);
1045                }
1046
1047                that._currentImage = selectedImage;
1048
1049                return that;
1050            }
1051
1052            ,
1053        current: function () {
1054                return this.index;
1055            }
1056
1057            ,
1058        next: function () {
1059                var args = Array.prototype.slice.call(arguments, 0);
1060                args.unshift(this.index < this.images.length - 1 ? this.index + 1 : 0);
1061                return this.show.apply(this, args);
1062            }
1063
1064            ,
1065        prev: function () {
1066                var args = Array.prototype.slice.call(arguments, 0);
1067                args.unshift(this.index === 0 ? this.images.length - 1 : this.index - 1);
1068                return this.show.apply(this, args);
1069            }
1070
1071            ,
1072        pause: function () {
1073                // Pause the slideshow
1074                this.paused = true;
1075
1076                if (this.videoWrapper) {
1077                    this.videoWrapper.pause();
1078                }
1079
1080                return this;
1081            }
1082
1083            ,
1084        resume: function () {
1085                // Resume the slideshow
1086                this.paused = false;
1087
1088                if (this.videoWrapper) {
1089                    this.videoWrapper.play();
1090                }
1091
1092                this.cycle();
1093                return this;
1094            }
1095
1096            ,
1097        cycle: function () {
1098                // Start/resume the slideshow
1099                if (this.images.length > 1) {
1100                    // Clear the timeout, just in case
1101                    clearTimeout(this._cycleTimeout);
1102
1103                    var duration = (this._currentImage && this._currentImage.duration) || this.options.duration;
1104                    var isVideo = isVideoSource(this._currentImage);
1105
1106                    var callNext = function () {
1107                        this.$item.off('.cycle');
1108
1109                        // Check for paused slideshow
1110                        if (!this.paused) {
1111                            this.next();
1112                        }
1113                    };
1114
1115                    // Special video handling
1116                    if (isVideo) {
1117
1118                        // Leave video at last frame
1119                        if (!this._currentImage.loop) {
1120                            var lastFrameTimeout = 0;
1121
1122                            this.$item
1123                                .on('playing.cycle', function () {
1124                                    var player = $(this).data('player');
1125
1126                                    clearTimeout(lastFrameTimeout);
1127                                    lastFrameTimeout = setTimeout(function () {
1128                                        player.pause();
1129                                        player.$video.trigger('ended');
1130                                    }, (player.getDuration() - player.getCurrentTime()) * 1000);
1131                                })
1132                                .on('ended.cycle', function () {
1133                                    clearTimeout(lastFrameTimeout);
1134                                });
1135                        }
1136
1137                        // On error go to next
1138                        this.$item.on('error.cycle initerror.cycle', $.proxy(callNext, this));
1139                    }
1140
1141                    if (isVideo && !this._currentImage.duration) {
1142                        // It's a video - playing until end
1143                        this.$item.on('ended.cycle', $.proxy(callNext, this));
1144
1145                    } else {
1146                        // Cycling according to specified duration
1147                        this._cycleTimeout = setTimeout($.proxy(callNext, this), duration);
1148                    }
1149
1150                }
1151                return this;
1152            }
1153
1154            ,
1155        destroy: function (preserveBackground) {
1156            // Stop the resize events
1157            $(window).off('resize.backstretch orientationchange.backstretch');
1158
1159            // Stop any videos
1160            if (this.videoWrapper) {
1161                this.videoWrapper.destroy();
1162            }
1163
1164            // Clear the timeout
1165            clearTimeout(this._cycleTimeout);
1166
1167            // Remove Backstretch
1168            if (!preserveBackground) {
1169                this.$wrap.remove();
1170            }
1171            this.$container.removeData('backstretch');
1172        }
1173    };
1174
1175    /**
1176     * Video Abstraction Layer
1177     *
1178     * Static methods:
1179     * > VideoWrapper.loadYoutubeAPI() -> Call in order to load the Youtube API.
1180     *                                   An 'youtube_api_load' event will be triggered on $(window) when the API is loaded.
1181     *
1182     * Generic:
1183     * > player.type -> type of the video
1184     * > player.video / player.$video -> contains the element holding the video
1185     * > player.play() -> plays the video
1186     * > player.pause() -> pauses the video
1187     * > player.setCurrentTime(position) -> seeks to a position by seconds
1188     *
1189     * Youtube:
1190     * > player.ytId will contain the youtube ID if the source is a youtube url
1191     * > player.ytReady is a flag telling whether the youtube source is ready for playback
1192     * */
1193
1194    var VideoWrapper = function () {
1195        this.init.apply(this, arguments);
1196    };
1197
1198    /**
1199     * @param {Object} options
1200     * @param {String|Array<String>|Array<{{src: String, type: String?}}>} options.url
1201     * @param {Boolean} options.loop=false
1202     * @param {Boolean?} options.mute=true
1203     * @param {String?} options.poster
1204     * loop, mute, poster
1205     */
1206    VideoWrapper.prototype.init = function (options) {
1207
1208        var that = this;
1209
1210        var $video;
1211
1212        var setVideoElement = function () {
1213            that.$video = $video;
1214            that.video = $video[0];
1215        };
1216
1217        // Determine video type
1218
1219        var videoType = 'video';
1220
1221        if (!(options.url instanceof Array) &&
1222            YOUTUBE_REGEXP.test(options.url)) {
1223            videoType = 'youtube';
1224        }
1225
1226        that.type = videoType;
1227
1228        if (videoType === 'youtube') {
1229
1230            // Try to load the API in the meantime
1231            VideoWrapper.loadYoutubeAPI();
1232
1233            that.ytId = options.url.match(YOUTUBE_REGEXP)[2];
1234            var src = 'https://www.youtube.com/embed/' + that.ytId +
1235                '?rel=0&autoplay=0&showinfo=0&controls=0&modestbranding=1' +
1236                '&cc_load_policy=0&disablekb=1&iv_load_policy=3&loop=0' +
1237                '&enablejsapi=1&origin=' + encodeURIComponent(window.location.origin);
1238
1239            that.__ytStartMuted = !!options.mute || options.mute === undefined;
1240
1241            $video = $('<iframe />')
1242                .attr({
1243                    'src_to_load': src
1244                })
1245                .css({
1246                    'border': 0,
1247                    'margin': 0,
1248                    'padding': 0
1249                })
1250                .data('player', that);
1251
1252            if (options.loop) {
1253                $video.on('ended.loop', function () {
1254                    if (!that.__manuallyStopped) {
1255                        that.play();
1256                    }
1257                });
1258            }
1259
1260            that.ytReady = false;
1261
1262            setVideoElement();
1263
1264            if (window['YT'] && window['YT'].loaded) {
1265                that._initYoutube();
1266                $video.trigger('initsuccess');
1267            } else {
1268                $(window).one('youtube_api_load', function () {
1269                    that._initYoutube();
1270                    $video.trigger('initsuccess');
1271                });
1272            }
1273
1274        } else {
1275            // Traditional <video> tag with multiple sources
1276
1277            $video = $('<video>')
1278                .prop('autoplay', false)
1279                .prop('controls', false)
1280                .prop('loop', !!options.loop)
1281                .prop('muted', !!options.mute || options.mute === undefined)
1282
1283                // Let the first frames be available before playback, as we do transitions
1284                .prop('preload', 'auto')
1285                .prop('poster', options.poster || '');
1286
1287            var sources = (options.url instanceof Array) ? options.url : [options.url];
1288
1289            for (var i = 0; i < sources.length; i++) {
1290                var sourceItem = sources[i];
1291                if (typeof (sourceItem) === 'string') {
1292                    sourceItem = {
1293                        src: sourceItem
1294                    };
1295                }
1296                $('<source>')
1297                    .attr('src', sourceItem.src)
1298                    // Make sure to not specify type if unknown -
1299                    //   so the browser will try to autodetect.
1300                    .attr('type', sourceItem.type || null)
1301                    .appendTo($video);
1302            }
1303
1304            if (!$video[0].canPlayType || !sources.length) {
1305                $video.trigger('initerror');
1306            } else {
1307                $video.trigger('initsuccess');
1308            }
1309
1310            setVideoElement();
1311        }
1312
1313    };
1314
1315    VideoWrapper.prototype._initYoutube = function () {
1316        var that = this;
1317
1318        var YT = window['YT'];
1319
1320        that.$video
1321            .attr('src', that.$video.attr('src_to_load'))
1322            .removeAttr('src_to_load');
1323
1324        // It won't init if it's not in the DOM, so we emulate that
1325        var hasParent = !!that.$video[0].parentNode;
1326        if (!hasParent) {
1327            var $tmpParent = $('<div>').css('display', 'none !important').appendTo(document.body);
1328            that.$video.appendTo($tmpParent);
1329        }
1330
1331        var player = new YT.Player(that.video, {
1332            events: {
1333                'onReady': function () {
1334
1335                    if (that.__ytStartMuted) {
1336                        player.mute();
1337                    }
1338
1339                    if (!hasParent) {
1340                        // Restore parent to old state - without interrupting any changes
1341                        if (that.$video[0].parentNode === $tmpParent[0]) {
1342                            that.$video.detach();
1343                        }
1344                        $tmpParent.remove();
1345                    }
1346
1347                    that.ytReady = true;
1348                    that._updateYoutubeSize();
1349                    that.$video.trigger('canplay');
1350                },
1351                'onStateChange': function (event) {
1352                    switch (event.data) {
1353                        case YT.PlayerState.PLAYING:
1354                            that.$video.trigger('playing');
1355                            break;
1356                        case YT.PlayerState.ENDED:
1357                            that.$video.trigger('ended');
1358                            break;
1359                        case YT.PlayerState.PAUSED:
1360                            that.$video.trigger('pause');
1361                            break;
1362                        case YT.PlayerState.BUFFERING:
1363                            that.$video.trigger('waiting');
1364                            break;
1365                        case YT.PlayerState.CUED:
1366                            that.$video.trigger('canplay');
1367                            break;
1368                    }
1369                },
1370                'onPlaybackQualityChange': function () {
1371                    that._updateYoutubeSize();
1372                    that.$video.trigger('resize');
1373                },
1374                'onError': function (err) {
1375                    that.hasError = true;
1376                    that.$video.trigger({
1377                        'type': 'error',
1378                        'error': err
1379                    });
1380                }
1381            }
1382        });
1383
1384        that.ytPlayer = player;
1385
1386        return that;
1387    };
1388
1389    VideoWrapper.prototype._updateYoutubeSize = function () {
1390        var that = this;
1391
1392        switch (that.ytPlayer.getPlaybackQuality() || 'medium') {
1393            case 'small':
1394                that.video.videoWidth = 426;
1395                that.video.videoHeight = 240;
1396                break;
1397            case 'medium':
1398                that.video.videoWidth = 640;
1399                that.video.videoHeight = 360;
1400                break;
1401            default:
1402            case 'large':
1403                that.video.videoWidth = 854;
1404                that.video.videoHeight = 480;
1405                break;
1406            case 'hd720':
1407                that.video.videoWidth = 1280;
1408                that.video.videoHeight = 720;
1409                break;
1410            case 'hd1080':
1411                that.video.videoWidth = 1920;
1412                that.video.videoHeight = 1080;
1413                break;
1414            case 'highres':
1415                that.video.videoWidth = 2560;
1416                that.video.videoHeight = 1440;
1417                break;
1418        }
1419
1420        return that;
1421    };
1422
1423    VideoWrapper.prototype.play = function () {
1424        var that = this;
1425
1426        that.__manuallyStopped = false;
1427
1428        if (that.type === 'youtube') {
1429            if (that.ytReady) {
1430                that.$video.trigger('play');
1431                that.ytPlayer.playVideo();
1432            }
1433        } else {
1434            that.video.play();
1435        }
1436
1437        return that;
1438    };
1439
1440    VideoWrapper.prototype.pause = function () {
1441        var that = this;
1442
1443        that.__manuallyStopped = false;
1444
1445        if (that.type === 'youtube') {
1446            if (that.ytReady) {
1447                that.ytPlayer.pauseVideo();
1448            }
1449        } else {
1450            that.video.pause();
1451        }
1452
1453        return that;
1454    };
1455
1456    VideoWrapper.prototype.stop = function () {
1457        var that = this;
1458
1459        that.__manuallyStopped = true;
1460
1461        if (that.type === 'youtube') {
1462            if (that.ytReady) {
1463                that.ytPlayer.pauseVideo();
1464                that.ytPlayer.seekTo(0);
1465            }
1466        } else {
1467            that.video.pause();
1468            that.video.currentTime = 0;
1469        }
1470
1471        return that;
1472    };
1473
1474    VideoWrapper.prototype.destroy = function () {
1475        var that = this;
1476
1477        if (that.ytPlayer) {
1478            that.ytPlayer.destroy();
1479        }
1480
1481        that.$video.remove();
1482
1483        return that;
1484    };
1485
1486    VideoWrapper.prototype.getCurrentTime = function (seconds) {
1487        var that = this;
1488
1489        if (that.type === 'youtube') {
1490            if (that.ytReady) {
1491                return that.ytPlayer.getCurrentTime();
1492            }
1493        } else {
1494            return that.video.currentTime;
1495        }
1496
1497        return 0;
1498    };
1499
1500    VideoWrapper.prototype.setCurrentTime = function (seconds) {
1501        var that = this;
1502
1503        if (that.type === 'youtube') {
1504            if (that.ytReady) {
1505                that.ytPlayer.seekTo(seconds, true);
1506            }
1507        } else {
1508            that.video.currentTime = seconds;
1509        }
1510
1511        return that;
1512    };
1513
1514    VideoWrapper.prototype.getDuration = function () {
1515        var that = this;
1516
1517        if (that.type === 'youtube') {
1518            if (that.ytReady) {
1519                return that.ytPlayer.getDuration();
1520            }
1521        } else {
1522            return that.video.duration;
1523        }
1524
1525        return 0;
1526    };
1527
1528    /**
1529     * This will load the youtube API (if not loaded yet)
1530     * Use $(window).one('youtube_api_load', ...) to listen for API loaded event
1531     */
1532    VideoWrapper.loadYoutubeAPI = function () {
1533        if (window['YT'] && window['__yt_load_event_interval__']) {
1534            return;
1535        }
1536
1537        if (!window['YT'] && !$('script[src*=www\\.youtube\\.com\\/iframe_api]').length) {
1538            $('<script type="text/javascript" src="https://www.youtube.com/iframe_api">').appendTo('body');
1539        }
1540
1541        window['__yt_load_event_interval__'] = setInterval(function () {
1542            if (window['YT'] && window['YT'].loaded) {
1543                $(window).trigger('youtube_api_load');
1544                clearTimeout(window['__yt_load_event_interval__']);
1545                delete window['__yt_load_event_interval__'];
1546            }
1547        }, 50);
1548    };
1549
1550    var getDeviceOrientation = function () {
1551
1552        if ('matchMedia' in window) {
1553            if (window.matchMedia("(orientation: portrait)").matches) {
1554                return 'portrait';
1555            } else if (window.matchMedia("(orientation: landscape)").matches) {
1556                return 'landscape';
1557            }
1558        }
1559
1560        if (screen.height > screen.width) {
1561            return 'portrait';
1562        }
1563
1564        // Even square devices have orientation,
1565        //   but a desktop browser may be too old for `matchMedia`.
1566        // Defaulting to `landscape` for the VERY rare case of a square desktop screen is good enough.
1567        return 'landscape';
1568    };
1569
1570    var getWindowOrientation = function () {
1571        if (window.innerHeight > window.innerWidth) {
1572            return 'portrait';
1573        }
1574        if (window.innerWidth > window.innerHeight) {
1575            return 'landscape';
1576        }
1577
1578        return 'square';
1579    };
1580
1581    /* SUPPORTS FIXED POSITION?
1582     *
1583     * Based on code from jQuery Mobile 1.1.0
1584     * http://jquerymobile.com/
1585     *
1586     * In a nutshell, we need to figure out if fixed positioning is supported.
1587     * Unfortunately, this is very difficult to do on iOS, and usually involves
1588     * injecting content, scrolling the page, etc.. It's ugly.
1589     * jQuery Mobile uses this workaround. It's not ideal, but works.
1590     *
1591     * Modified to detect IE6
1592     * ========================= */
1593
1594    var supportsFixedPosition = (function () {
1595        var ua = navigator.userAgent,
1596            platform = navigator.platform
1597            // Rendering engine is Webkit, and capture major version
1598            ,
1599            wkmatch = ua.match(/AppleWebKit\/([0-9]+)/),
1600            wkversion = !!wkmatch && wkmatch[1],
1601            ffmatch = ua.match(/Fennec\/([0-9]+)/),
1602            ffversion = !!ffmatch && ffmatch[1],
1603            operammobilematch = ua.match(/Opera Mobi\/([0-9]+)/),
1604            omversion = !!operammobilematch && operammobilematch[1],
1605            iematch = ua.match(/MSIE ([0-9]+)/),
1606            ieversion = !!iematch && iematch[1];
1607
1608        return !(
1609            // iOS 4.3 and older : Platform is iPhone/Pad/Touch and Webkit version is less than 534 (ios5)
1610            ((platform.indexOf("iPhone") > -1 || platform.indexOf("iPad") > -1 || platform.indexOf(
1611                "iPod") > -1) && wkversion && wkversion < 534) ||
1612
1613            // Opera Mini
1614            (window.operamini && ({}).toString.call(window.operamini) === "[object OperaMini]") ||
1615            (operammobilematch && omversion < 7458) ||
1616
1617            //Android lte 2.1: Platform is Android and Webkit version is less than 533 (Android 2.2)
1618            (ua.indexOf("Android") > -1 && wkversion && wkversion < 533) ||
1619
1620            // Firefox Mobile before 6.0 -
1621            (ffversion && ffversion < 6) ||
1622
1623            // WebOS less than 3
1624            ("palmGetResource" in window && wkversion && wkversion < 534) ||
1625
1626            // MeeGo
1627            (ua.indexOf("MeeGo") > -1 && ua.indexOf("NokiaBrowser/8.5.0") > -1) ||
1628
1629            // IE6
1630            (ieversion && ieversion <= 6)
1631        );
1632    }());
1633
1634}(jQuery, window));
1635
1636/***********************************************************************************************
1637         scripts.js
1638**********************************************************************************************/
1639$(document).ready(function () {
1640    $(".nav--mobile").click(function () {
1641        $("#NavMobile li").removeClass("active"); //Remove class active from Any li when open Overlay
1642        $("#level3").empty(); //empty level3 when open Overlay
1643        $("#Overlay").css("position", "absolute");
1644        $(".open--menu").css("top", "-15px");
1645        /*********Window 575******/
1646        if (window.matchMedia('(max-width: 575px)').matches) {
1647            $(".open--menu").css("top", "-15px");
1648            /*********Window 400******/
1649        } else if (window.matchMedia('(max-width: 400px)').matches) {
1650            $(".open--menu").css("top", "-15px");
1651            $(".open--menu").css("padding", "0");
1652        }
1653        $(".open--menu").css("border", "none");
1654        $("#backtoTop").css("display", "none !important");
1655        $("#scrollUp i").css("display", "none !important");
1656        $(".open--menu").removeClass("hover");
1657        $(".open--menu").removeClass("toggleOverlay");
1658        $(".hamburger__line").css("margin", "4px auto 0");
1659        $(".hamburger__line").css("width", "1.7em");
1660        $(".close").css("display", "none"); // Close before toggle
1661        $(".close").css("top", "-100px"); // Close before toggle
1662        /*********Start Toggle Menu******/
1663        $('.hamburger__line').toggleClass("hamburger__line-active");
1664        $("#NavMobile >li>ul.submenu").hide();
1665        $("#NavMobile").css("display", "block");
1666        $("#level3").css("display", "none");
1667        $('.mobile--overlay').toggleClass("mobile--overlay_height");
1668        $('body').toggleClass("body--lock");
1669        /*********End Toggle Menu******/
1670        /*********Start Overlay hamburger and close button******/
1671        if ($(".hamburger__line").hasClass("hamburger__line-active")) {
1672            $(".open--menu").addClass("hover");
1673            $(".open--menu").addClass("toggleOverlay");
1674            // $(".open--menu").css("top", "30px");
1675            $(".open--menu").css("border", "1px solid #ffffff63");
1676            $(".open--menu").css("width", "41px");
1677            $(".open--menu").css("height", "41px");
1678            $(".hamburger__line").css("margin", "6px auto 0");
1679            $(".hamburger__line").css("width", "1.2em");
1680            $(".close").toggle(); // Close After toggle
1681            $(".close").css("top", "5px"); // Close After toggle
1682            $("span.open--menu.hover").css("top", "0px"); // Close After toggle
1683        }
1684        /*********End Overlay hamburger and close button******/
1685        /*********Window 776******/
1686        if (window.matchMedia('(max-width: 776px)').matches) {
1687            $("span.open--menu.hover").css("top", "-65px");
1688            $("span.open--menu.hover").css("left", "66px !important");
1689        }
1690    });
1691    /*********End Hamuger Menu******/
1692    /******************Arrows************/
1693    $('#NavMobile > li>ul.submenu').parent().find("i.ic").addClass("fa-solid fa-angle-right");
1694    $('li.child>a').after("<i class='fa-solid fa-angle-right'></i>");
1695    if ($('#NavMobile > li>ul.submenu>
1695li:not(.child)')) {
1696        $('#NavMobile > li>ul.submenu>li:not(.child)>a').after("<i class='icon-next'></i>");
1697    }
1698    /*********reset Overlay background******/
1699    $("#scrollUp").css("diplay", "block");
1700    $("#level3").empty();
1701    /*********reset Overlay background******/
1702    $("#NavMobile").on('click', 'li', function () {
1703        $("#Overlay").css("position", "fixed");
1704        $("#NavMobile >li>ul.submenu").hide();
1705        /*********start grey color******/
1706        $("#NavMobile >li ").addClass("grey");
1707        /*********max-776px******/
1708        if (window.matchMedia('(max-width: 776px)').matches) {
1709            $(".grey a").css("color", "#fff");
1710        } else {
1711            $(".grey a").css("color", "#a6adb3");
1712        }
1713        /*********End grey color******/
1714        $("#NavMobile >li.active").removeClass("active");
1715        $(this).siblings().find("li").removeClass("active"); // remove active class from any li @another submenu
1716        $(this).addClass("active"); //white
1717        /***************Startsubmenu******************/
1718        if ($(this).hasClass("active")) {
1719            $("#NavMobile >li.active >ul.submenu").show();
1720        } else {
1721            $("#NavMobile >li.active >ul.submenu").hide();
1722        }
1723        /***************Endsubmenu******************/
1724        /***************Display Level 3******************/
1725        if ($("#NavMobile >li.active").hasClass("hasBranch")) {
1726            $("#level3").css("display", "block");
1727        } else {
1728            $("#level3").css("display", "none");
1729        }
1730        /***************End Display Level 3******************/
1731        /**********Hide Level 3 when open another li*********/
1732        if ($("#NavMobile >li").hasClass("active")) {
1733            if (!$(this).hasClass("hasBranch")) {
1734                $("#level3").empty();
1735            }
1736        }
1737    }); //end click li
1738    /***********Start Random Overlay Background****************/
1739    $("li.nav-item >a").click(function (e) {
1740        e.preventDefault();
1741        var path = 'img/main-menu/';
1742        var bgArray = ['t0.webp', 't1.webp', 't2.webp', 't3.webp', 't4.webp', 't5.webp', 't6.webp', 't7.webp', 't8.webp', 't9.webp'];
1743        var bg = bgArray[Math.floor(Math.random() * bgArray.length)];
1744        $("#Overlay").backstretch([path + bg], {
1745            fade: 750,
1746            duration: 4000
1747        });
1748    });
1749    /***************End Overlay Background******************/
1750});
1751/*******************start Level 3****************************/
1752$("ul.submenu").on('click', 'li', function () {
1753    if (window.matchMedia('(max-width: 991px)').matches) {
1754        $(".reverse").css("display", "block");
1755        $(".reverse").html("<i class='fa-solid fa-angle-left'></i>");
1756    }
1757    $("#level3").empty();
1758    $("ul.submenu li ").addClass("grey");
1759    $("ul.submenu>li.active").removeClass("active");
1760    $(this).addClass("active"); //white
1761    /******start Print Title of level3*****/
1762    var secondLevel_li = $("ul.submenu li.active>a").text();
1763    $('.level3_Title').html("<i class='icon-next'></i>" + secondLevel_li);
1764    /******end Print Title of level3*****/
1765    if ($("ul.submenu>li.active").hasClass("child")) {
1766        var content = $("li.active.child>.branch").html();
1767        $("#level3").toggle().html(content);
1768    } else {
1769        $("#level3").empty();
1770    }
1771});
1772/*************************End Level 3********************************/
1773/*************start Move Level 3 Mobile*********************/
1774if (window.matchMedia('(max-width: 991px)').matches) {
1775    $("ul.submenu").on('click', 'li', function () {
1776        $("ul.submenu >li.active").removeClass("active");
1777        $("#level3").css("display", "none");
1778        // adding classname 'active' to current click li 
1779        $(this).addClass("active "); //white
1780        if ($("li.child").hasClass("active")) {
1781            $("#level3").css("display", "block");
1782            $("ul.submenu").addClass("wow fadeInLeft");
1783            $("#NavMobile").css("display", "none");
1784            $("ul.quickLinks2").addClass("absolute");
1785        }
1786    });
1787}
1788/*************************End Move Level 3**********************/

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.