-
-
Save evikza/51ace557584032355d59ac8676131ece to your computer and use it in GitHub Desktop.
| // ================================================== | |
| // fancyBox v3.5.7 | |
| // | |
| // Licensed GPLv3 for open source use | |
| // or fancyBox Commercial License for commercial use | |
| // | |
| // http://fancyapps.com/fancybox/ | |
| // Copyright 2022 fancyApps | |
| // | |
| // ================================================== | |
| (function (window, document, $, undefined) { | |
| 'use strict'; | |
| window.console = window.console || { | |
| info: function (stuff) {}, | |
| }; | |
| // If there's no jQuery, fancyBox can't work | |
| // ========================================= | |
| if (!$) { | |
| return; | |
| } | |
| // Check if fancyBox is already initialized | |
| // ======================================== | |
| if ($.fn.fancybox) { | |
| console.info('fancyBox already initialized'); | |
| return; | |
| } | |
| // Private default settings | |
| // ======================== | |
| var defaults = { | |
| // Close existing modals | |
| // Set this to false if you do not need to stack multiple instances | |
| closeExisting: false, | |
| // Enable infinite gallery navigation | |
| loop: false, | |
| // Horizontal space between slides | |
| gutter: 50, | |
| // Enable keyboard navigation | |
| keyboard: true, | |
| // Should allow caption to overlap the content | |
| preventCaptionOverlap: true, | |
| // Should display navigation arrows at the screen edges | |
| arrows: true, | |
| // Should display counter at the top left corner | |
| infobar: true, | |
| // Should display close button (using `btnTpl.smallBtn` template) over the content | |
| // Can be true, false, "auto" | |
| // If "auto" - will be automatically enabled for "html", "inline" or "ajax" items | |
| smallBtn: 'auto', | |
| // Should display toolbar (buttons at the top) | |
| // Can be true, false, "auto" | |
| // If "auto" - will be automatically hidden if "smallBtn" is enabled | |
| toolbar: 'auto', | |
| // What buttons should appear in the top right corner. | |
| // Buttons will be created using templates from `btnTpl` option | |
| // and they will be placed into toolbar (class="fancybox-toolbar"` element) | |
| buttons: [ | |
| 'zoom', | |
| //"share", | |
| 'slideShow', | |
| //"fullScreen", | |
| //"download", | |
| 'thumbs', | |
| 'close', | |
| ], | |
| // Detect "idle" time in seconds | |
| idleTime: 3, | |
| // Disable right-click and use simple image protection for images | |
| protect: false, | |
| // Shortcut to make content "modal" - disable keyboard navigtion, hide buttons, etc | |
| modal: false, | |
| image: { | |
| // Wait for images to load before displaying | |
| // true - wait for image to load and then display; | |
| // false - display thumbnail and load the full-sized image over top, | |
| // requires predefined image dimensions (`data-width` and `data-height` attributes) | |
| preload: false, | |
| }, | |
| ajax: { | |
| // Object containing settings for ajax request | |
| settings: { | |
| // This helps to indicate that request comes from the modal | |
| // Feel free to change naming | |
| data: { | |
| fancybox: true, | |
| }, | |
| }, | |
| }, | |
| iframe: { | |
| // Iframe template | |
| tpl: '<iframe id="fancybox-frame{rnd}" name="fancybox-frame{rnd}" class="fancybox-iframe" allowfullscreen="allowfullscreen" allow="autoplay; fullscreen" src=""></iframe>', | |
| // Preload iframe before displaying it | |
| // This allows to calculate iframe content width and height | |
| // (note: Due to "Same Origin Policy", you can't get cross domain data). | |
| preload: true, | |
| // Custom CSS styling for iframe wrapping element | |
| // You can use this to set custom iframe dimensions | |
| css: {}, | |
| // Iframe tag attributes | |
| attr: { | |
| scrolling: 'auto', | |
| }, | |
| }, | |
| // For HTML5 video only | |
| video: { | |
| tpl: | |
| '<video class="fancybox-video" controls controlsList="nodownload" poster="{{poster}}">' + | |
| '<source src="{{src}}" type="{{format}}" />' + | |
| 'Sorry, your browser doesn\'t support embedded videos, <a href="{{src}}">download</a> and watch with your favorite video player!' + | |
| '</video>', | |
| format: '', // custom video format | |
| autoStart: true, | |
| }, | |
| // Default content type if cannot be detected automatically | |
| defaultType: 'image', | |
| // Open/close animation type | |
| // Possible values: | |
| // false - disable | |
| // "zoom" - zoom images from/to thumbnail | |
| // "fade" | |
| // "zoom-in-out" | |
| // | |
| animationEffect: 'zoom', | |
| // Duration in ms for open/close animation | |
| animationDuration: 366, | |
| // Should image change opacity while zooming | |
| // If opacity is "auto", then opacity will be changed if image and thumbnail have different aspect ratios | |
| zoomOpacity: 'auto', | |
| // Transition effect between slides | |
| // | |
| // Possible values: | |
| // false - disable | |
| // "fade' | |
| // "slide' | |
| // "circular' | |
| // "tube' | |
| // "zoom-in-out' | |
| // "rotate' | |
| // | |
| transitionEffect: 'fade', | |
| // Duration in ms for transition animation | |
| transitionDuration: 366, | |
| // Custom CSS class for slide element | |
| slideClass: '', | |
| // Custom CSS class for layout | |
| baseClass: '', | |
| // Base template for layout | |
| baseTpl: | |
| '<div class="fancybox-container" role="dialog" tabindex="-1">' + | |
| '<div class="fancybox-bg"></div>' + | |
| '<div class="fancybox-inner">' + | |
| '<div class="fancybox-infobar"><span data-fancybox-index></span> / <span data-fancybox-count></span></div>' + | |
| '<div class="fancybox-toolbar">{{buttons}}</div>' + | |
| '<div class="fancybox-navigation">{{arrows}}</div>' + | |
| '<div class="fancybox-stage"></div>' + | |
| '<div class="fancybox-caption"><div class="fancybox-caption__body"></div></div>' + | |
| '</div>' + | |
| '</div>', | |
| // Loading indicator template | |
| spinnerTpl: '<div class="fancybox-loading"></div>', | |
| // Error message template | |
| errorTpl: '<div class="fancybox-error"><p>{{ERROR}}</p></div>', | |
| btnTpl: { | |
| download: | |
| '<a download data-fancybox-download class="fancybox-button fancybox-button--download" title="{{DOWNLOAD}}" href="javascript:;">' + | |
| '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18.62 17.09V19H5.38v-1.91zm-2.97-6.96L17 11.45l-5 4.87-5-4.87 1.36-1.32 2.68 2.64V5h1.92v7.77z"/></svg>' + | |
| '</a>', | |
| zoom: | |
| '<button data-fancybox-zoom class="fancybox-button fancybox-button--zoom" title="{{ZOOM}}">' + | |
| '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18.7 17.3l-3-3a5.9 5.9 0 0 0-.6-7.6 5.9 5.9 0 0 0-8.4 0 5.9 5.9 0 0 0 0 8.4 5.9 5.9 0 0 0 7.7.7l3 3a1 1 0 0 0 1.3 0c.4-.5.4-1 0-1.5zM8.1 13.8a4 4 0 0 1 0-5.7 4 4 0 0 1 5.7 0 4 4 0 0 1 0 5.7 4 4 0 0 1-5.7 0z"/></svg>' + | |
| '</button>', | |
| close: | |
| '<button data-fancybox-close class="fancybox-button fancybox-button--close" title="{{CLOSE}}">' + | |
| '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 10.6L6.6 5.2 5.2 6.6l5.4 5.4-5.4 5.4 1.4 1.4 5.4-5.4 5.4 5.4 1.4-1.4-5.4-5.4 5.4-5.4-1.4-1.4-5.4 5.4z"/></svg>' + | |
| '</button>', | |
| // Arrows | |
| arrowLeft: | |
| '<button data-fancybox-prev class="fancybox-button fancybox-button--arrow_left" title="{{PREV}}">' + | |
| '<div><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M11.28 15.7l-1.34 1.37L5 12l4.94-5.07 1.34 1.38-2.68 2.72H19v1.94H8.6z"/></svg></div>' + | |
| '</button>', | |
| arrowRight: | |
| '<button data-fancybox-next class="fancybox-button fancybox-button--arrow_right" title="{{NEXT}}">' + | |
| '<div><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M15.4 12.97l-2.68 2.72 1.34 1.38L19 12l-4.94-5.07-1.34 1.38 2.68 2.72H5v1.94z"/></svg></div>' + | |
| '</button>', | |
| // This small close button will be appended to your html/inline/ajax content by default, | |
| // if "smallBtn" option is not set to false | |
| smallBtn: | |
| '<button type="button" data-fancybox-close class="fancybox-button fancybox-close-small" title="{{CLOSE}}">' + | |
| '<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 24 24"><path d="M13 12l5-5-1-1-5 5-5-5-1 1 5 5-5 5 1 1 5-5 5 5 1-1z"/></svg>' + | |
| '</button>', | |
| }, | |
| // Container is injected into this element | |
| parentEl: 'body', | |
| // Hide browser vertical scrollbars; use at your own risk | |
| hideScrollbar: true, | |
| // Focus handling | |
| // ============== | |
| // Try to focus on the first focusable element after opening | |
| autoFocus: true, | |
| // Put focus back to active element after closing | |
| backFocus: true, | |
| // Do not let user to focus on element outside modal content | |
| trapFocus: true, | |
| // Module specific options | |
| // ======================= | |
| fullScreen: { | |
| autoStart: false, | |
| }, | |
| // Set `touch: false` to disable panning/swiping | |
| touch: { | |
| vertical: true, // Allow to drag content vertically | |
| momentum: true, // Continue movement after releasing mouse/touch when panning | |
| }, | |
| // Hash value when initializing manually, | |
| // set `false` to disable hash change | |
| hash: null, | |
| // Customize or add new media types | |
| // Example: | |
| /* | |
| media : { | |
| youtube : { | |
| params : { | |
| autoplay : 0 | |
| } | |
| } | |
| } | |
| */ | |
| media: {}, | |
| slideShow: { | |
| autoStart: false, | |
| speed: 3000, | |
| }, | |
| thumbs: { | |
| autoStart: false, // Display thumbnails on opening | |
| hideOnClose: true, // Hide thumbnail grid when closing animation starts | |
| parentEl: '.fancybox-container', // Container is injected into this element | |
| axis: 'y', // Vertical (y) or horizontal (x) scrolling | |
| }, | |
| // Use mousewheel to navigate gallery | |
| // If 'auto' - enabled for images only | |
| wheel: 'auto', | |
| // Callbacks | |
| //========== | |
| // See Documentation/API/Events for more information | |
| // Example: | |
| /* | |
| afterShow: function( instance, current ) { | |
| console.info( 'Clicked element:' ); | |
| console.info( current.opts.$orig ); | |
| } | |
| */ | |
| onInit: $.noop, // When instance has been initialized | |
| beforeLoad: $.noop, // Before the content of a slide is being loaded | |
| afterLoad: $.noop, // When the content of a slide is done loading | |
| beforeShow: $.noop, // Before open animation starts | |
| afterShow: $.noop, // When content is done loading and animating | |
| beforeClose: $.noop, // Before the instance attempts to close. Return false to cancel the close. | |
| afterClose: $.noop, // After instance has been closed | |
| onActivate: $.noop, // When instance is brought to front | |
| onDeactivate: $.noop, // When other instance has been activated | |
| // Interaction | |
| // =========== | |
| // Use options below to customize taken action when user clicks or double clicks on the fancyBox area, | |
| // each option can be string or method that returns value. | |
| // | |
| // Possible values: | |
| // "close" - close instance | |
| // "next" - move to next gallery item | |
| // "nextOrClose" - move to next gallery item or close if gallery has only one item | |
| // "toggleControls" - show/hide controls | |
| // "zoom" - zoom image (if loaded) | |
| // false - do nothing | |
| // Clicked on the content | |
| clickContent: function (current, event) { | |
| return current.type === 'image' ? 'zoom' : false; | |
| }, | |
| // Clicked on the slide | |
| clickSlide: 'close', | |
| // Clicked on the background (backdrop) element; | |
| // if you have not changed the layout, then most likely you need to use `clickSlide` option | |
| clickOutside: 'close', | |
| // Same as previous two, but for double click | |
| dblclickContent: false, | |
| dblclickSlide: false, | |
| dblclickOutside: false, | |
| // Custom options when mobile device is detected | |
| // ============================================= | |
| mobile: { | |
| preventCaptionOverlap: false, | |
| idleTime: false, | |
| clickContent: function (current, event) { | |
| return current.type === 'image' ? 'toggleControls' : false; | |
| }, | |
| clickSlide: function (current, event) { | |
| return current.type === 'image' ? 'toggleControls' : 'close'; | |
| }, | |
| dblclickContent: function (current, event) { | |
| return current.type === 'image' ? 'zoom' : false; | |
| }, | |
| dblclickSlide: function (current, event) { | |
| return current.type === 'image' ? 'zoom' : false; | |
| }, | |
| }, | |
| // Internationalization | |
| // ==================== | |
| lang: 'en', | |
| i18n: { | |
| en: { | |
| CLOSE: 'Close', | |
| NEXT: 'Next', | |
| PREV: 'Previous', | |
| ERROR: | |
| 'The requested content cannot be loaded. <br/> Please try again later.', | |
| PLAY_START: 'Start slideshow', | |
| PLAY_STOP: 'Pause slideshow', | |
| FULL_SCREEN: 'Full screen', | |
| THUMBS: 'Thumbnails', | |
| DOWNLOAD: 'Download', | |
| SHARE: 'Share', | |
| ZOOM: 'Zoom', | |
| }, | |
| de: { | |
| CLOSE: 'Schließen', | |
| NEXT: 'Weiter', | |
| PREV: 'Zurück', | |
| ERROR: | |
| 'Die angeforderten Daten konnten nicht geladen werden. <br/> Bitte versuchen Sie es später nochmal.', | |
| PLAY_START: 'Diaschau starten', | |
| PLAY_STOP: 'Diaschau beenden', | |
| FULL_SCREEN: 'Vollbild', | |
| THUMBS: 'Vorschaubilder', | |
| DOWNLOAD: 'Herunterladen', | |
| SHARE: 'Teilen', | |
| ZOOM: 'Vergrößern', | |
| }, | |
| }, | |
| }; | |
| // Few useful variables and methods | |
| // ================================ | |
| var $W = $(window); | |
| var $D = $(document); | |
| var called = 0; | |
| // Check if an object is a jQuery object and not a native JavaScript object | |
| // ======================================================================== | |
| var isQuery = function (obj) { | |
| return obj && obj.hasOwnProperty && obj instanceof $; | |
| }; | |
| // Handle multiple browsers for "requestAnimationFrame" and "cancelAnimationFrame" | |
| // =============================================================================== | |
| var requestAFrame = (function () { | |
| return ( | |
| window.requestAnimationFrame || | |
| window.webkitRequestAnimationFrame || | |
| window.mozRequestAnimationFrame || | |
| window.oRequestAnimationFrame || | |
| // if all else fails, use setTimeout | |
| function (callback) { | |
| return window.setTimeout(callback, 1000 / 60); | |
| } | |
| ); | |
| })(); | |
| var cancelAFrame = (function () { | |
| return ( | |
| window.cancelAnimationFrame || | |
| window.webkitCancelAnimationFrame || | |
| window.mozCancelAnimationFrame || | |
| window.oCancelAnimationFrame || | |
| function (id) { | |
| window.clearTimeout(id); | |
| } | |
| ); | |
| })(); | |
| // Detect the supported transition-end event property name | |
| // ======================================================= | |
| var transitionEnd = (function () { | |
| var el = document.createElement('fakeelement'), | |
| t; | |
| var transitions = { | |
| transition: 'transitionend', | |
| OTransition: 'oTransitionEnd', | |
| MozTransition: 'transitionend', | |
| WebkitTransition: 'webkitTransitionEnd', | |
| }; | |
| for (t in transitions) { | |
| if (el.style[t] !== undefined) { | |
| return transitions[t]; | |
| } | |
| } | |
| return 'transitionend'; | |
| })(); | |
| // Force redraw on an element. | |
| // This helps in cases where the browser doesn't redraw an updated element properly | |
| // ================================================================================ | |
| var forceRedraw = function ($el) { | |
| return $el && $el.length && $el[0].offsetHeight; | |
| }; | |
| // Exclude array (`buttons`) options from deep merging | |
| // =================================================== | |
| var mergeOpts = function (opts1, opts2) { | |
| var rez = $.extend(true, {}, opts1, opts2); | |
| $.each(opts2, function (key, value) { | |
| if ($.isArray(value)) { | |
| rez[key] = value; | |
| } | |
| }); | |
| return rez; | |
| }; | |
| // How much of an element is visible in viewport | |
| // ============================================= | |
| var inViewport = function (elem) { | |
| var elemCenter, rez; | |
| if (!elem || elem.ownerDocument !== document) { | |
| return false; | |
| } | |
| $('.fancybox-container').css('pointer-events', 'none'); | |
| elemCenter = { | |
| x: elem.getBoundingClientRect().left + elem.offsetWidth / 2, | |
| y: elem.getBoundingClientRect().top + elem.offsetHeight / 2, | |
| }; | |
| rez = document.elementFromPoint(elemCenter.x, elemCenter.y) === elem; | |
| $('.fancybox-container').css('pointer-events', ''); | |
| return rez; | |
| }; | |
| // Class definition | |
| // ================ | |
| var FancyBox = function (content, opts, index) { | |
| var self = this; | |
| self.opts = mergeOpts( | |
| { | |
| index: index, | |
| }, | |
| $.fancybox.defaults | |
| ); | |
| if ($.isPlainObject(opts)) { | |
| self.opts = mergeOpts(self.opts, opts); | |
| } | |
| if ($.fancybox.isMobile) { | |
| self.opts = mergeOpts(self.opts, self.opts.mobile); | |
| } | |
| self.id = self.opts.id || ++called; | |
| self.currIndex = parseInt(self.opts.index, 10) || 0; | |
| self.prevIndex = null; | |
| self.prevPos = null; | |
| self.currPos = 0; | |
| self.firstRun = true; | |
| // All group items | |
| self.group = []; | |
| // Existing slides (for current, next and previous gallery items) | |
| self.slides = {}; | |
| // Create group elements | |
| self.addContent(content); | |
| if (!self.group.length) { | |
| return; | |
| } | |
| self.init(); | |
| }; | |
| $.extend(FancyBox.prototype, { | |
| // Create DOM structure | |
| // ==================== | |
| init: function () { | |
| var self = this, | |
| firstItem = self.group[self.currIndex], | |
| firstItemOpts = firstItem.opts, | |
| $container, | |
| buttonStr; | |
| if (firstItemOpts.closeExisting) { | |
| $.fancybox.close(true); | |
| } | |
| // Hide scrollbars | |
| // =============== | |
| $('body').addClass('fancybox-active'); | |
| if ( | |
| !$.fancybox.getInstance() && | |
| firstItemOpts.hideScrollbar !== false && | |
| !$.fancybox.isMobile && | |
| document.body.scrollHeight > window.innerHeight | |
| ) { | |
| $('head').append( | |
| '<style id="fancybox-style-noscroll" type="text/css">.compensate-for-scrollbar{margin-right:' + | |
| (window.innerWidth - document.documentElement.clientWidth) + | |
| 'px;}</style>' | |
| ); | |
| $('body').addClass('compensate-for-scrollbar'); | |
| } | |
| // Build html markup and set references | |
| // ==================================== | |
| // Build html code for buttons and insert into main template | |
| buttonStr = ''; | |
| $.each(firstItemOpts.buttons, function (index, value) { | |
| buttonStr += firstItemOpts.btnTpl[value] || ''; | |
| }); | |
| // Create markup from base template, it will be initially hidden to | |
| // avoid unnecessary work like painting while initializing is not complete | |
| $container = $( | |
| self.translate( | |
| self, | |
| firstItemOpts.baseTpl | |
| .replace('{{buttons}}', buttonStr) | |
| .replace( | |
| '{{arrows}}', | |
| firstItemOpts.btnTpl.arrowLeft + firstItemOpts.btnTpl.arrowRight | |
| ) | |
| ) | |
| ) | |
| .attr('id', 'fancybox-container-' + self.id) | |
| .addClass(firstItemOpts.baseClass) | |
| .data('FancyBox', self) | |
| .appendTo(firstItemOpts.parentEl); | |
| // Create object holding references to jQuery wrapped nodes | |
| self.$refs = { | |
| container: $container, | |
| }; | |
| [ | |
| 'bg', | |
| 'inner', | |
| 'infobar', | |
| 'toolbar', | |
| 'stage', | |
| 'caption', | |
| 'navigation', | |
| ].forEach(function (item) { | |
| self.$refs[item] = $container.find('.fancybox-' + item); | |
| }); | |
| self.trigger('onInit'); | |
| // Enable events, deactive previous instances | |
| self.activate(); | |
| // Build slides, load and reveal content | |
| self.jumpTo(self.currIndex); | |
| }, | |
| // Simple i18n support - replaces object keys found in template | |
| // with corresponding values | |
| // ============================================================ | |
| translate: function (obj, str) { | |
| var arr = obj.opts.i18n[obj.opts.lang] || obj.opts.i18n.en; | |
| return str.replace(/\{\{(\w+)\}\}/g, function (match, n) { | |
| return arr[n] === undefined ? match : arr[n]; | |
| }); | |
| }, | |
| // Populate current group with fresh content | |
| // Check if each object has valid type and content | |
| // =============================================== | |
| addContent: function (content) { | |
| var self = this, | |
| items = $.makeArray(content), | |
| thumbs; | |
| $.each(items, function (i, item) { | |
| var obj = {}, | |
| opts = {}, | |
| $item, | |
| type, | |
| found, | |
| src, | |
| srcParts; | |
| // Step 1 - Make sure we have an object | |
| // ==================================== | |
| if ($.isPlainObject(item)) { | |
| // We probably have manual usage here, something like | |
| // $.fancybox.open( [ { src : "image.jpg", type : "image" } ] ) | |
| obj = item; | |
| opts = item.opts || item; | |
| } else if ($.type(item) === 'object' && $(item).length) { | |
| // Here we probably have jQuery collection returned by some selector | |
| $item = $(item); | |
| // Support attributes like `data-options='{"touch" : false}'` and `data-touch='false'` | |
| opts = $item.data() || {}; | |
| opts = $.extend(true, {}, opts, opts.options); | |
| // Here we store clicked element | |
| opts.$orig = $item; | |
| obj.src = self.opts.src || opts.src || $item.attr('href'); | |
| // Assume that simple syntax is used, for example: | |
| // `$.fancybox.open( $("#test"), {} );` | |
| if (!obj.type && !obj.src) { | |
| obj.type = 'inline'; | |
| obj.src = item; | |
| } | |
| } else { | |
| // Assume we have a simple html code, for example: | |
| // $.fancybox.open( '<div><h1>Hi!</h1></div>' ); | |
| obj = { | |
| type: 'html', | |
| src: item + '', | |
| }; | |
| } | |
| // Each gallery object has full collection of options | |
| obj.opts = $.extend(true, {}, self.opts, opts); | |
| // Do not merge buttons array | |
| if ($.isArray(opts.buttons)) { | |
| obj.opts.buttons = opts.buttons; | |
| } | |
| if ($.fancybox.isMobile && obj.opts.mobile) { | |
| obj.opts = mergeOpts(obj.opts, obj.opts.mobile); | |
| } | |
| // Step 2 - Make sure we have content type, if not - try to guess | |
| // ============================================================== | |
| type = obj.type || obj.opts.type; | |
| src = obj.src || ''; | |
| if (!type && src) { | |
| if ((found = src.match(/\.(mp4|mov|ogv|webm)((\?|#).*)?$/i))) { | |
| type = 'video'; | |
| if (!obj.opts.video.format) { | |
| obj.opts.video.format = | |
| 'video/' + (found[1] === 'ogv' ? 'ogg' : found[1]); | |
| } | |
| } else if ( | |
| src.match( | |
| /(^data:image\/[a-z0-9+\/=]*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg|ico)((\?|#).*)?$)/i | |
| ) | |
| ) { | |
| type = 'image'; | |
| } else if (src.match(/\.(pdf)((\?|#).*)?$/i)) { | |
| type = 'iframe'; | |
| obj = $.extend(true, obj, { | |
| contentType: 'pdf', | |
| opts: { | |
| iframe: { | |
| preload: false, | |
| }, | |
| }, | |
| }); | |
| } else if (src.charAt(0) === '#') { | |
| type = 'inline'; | |
| } | |
| } | |
| if (type) { | |
| obj.type = type; | |
| } else { | |
| self.trigger('objectNeedsType', obj); | |
| } | |
| if (!obj.contentType) { | |
| obj.contentType = | |
| $.inArray(obj.type, ['html', 'inline', 'ajax']) > -1 | |
| ? 'html' | |
| : obj.type; | |
| } | |
| // Step 3 - Some adjustments | |
| // ========================= | |
| obj.index = self.group.length; | |
| if (obj.opts.smallBtn == 'auto') { | |
| obj.opts.smallBtn = | |
| $.inArray(obj.type, ['html', 'inline', 'ajax']) > -1; | |
| } | |
| if (obj.opts.toolbar === 'auto') { | |
| obj.opts.toolbar = !obj.opts.smallBtn; | |
| } | |
| // Find thumbnail image, check if exists and if is in the viewport | |
| obj.$thumb = obj.opts.$thumb || null; | |
| if (obj.opts.$trigger && obj.index === self.opts.index) { | |
| obj.$thumb = obj.opts.$trigger.find('img:first'); | |
| if (obj.$thumb.length) { | |
| obj.opts.$orig = obj.opts.$trigger; | |
| } | |
| } | |
| if (!(obj.$thumb && obj.$thumb.length) && obj.opts.$orig) { | |
| obj.$thumb = obj.opts.$orig.find('img:first'); | |
| } | |
| if (obj.$thumb && !obj.$thumb.length) { | |
| obj.$thumb = null; | |
| } | |
| obj.thumb = obj.opts.thumb || (obj.$thumb ? obj.$thumb[0].src : null); | |
| // "caption" is a "special" option, it can be used to customize caption per gallery item | |
| if ($.type(obj.opts.caption) === 'function') { | |
| obj.opts.caption = obj.opts.caption.apply(item, [self, obj]); | |
| } | |
| if ($.type(self.opts.caption) === 'function') { | |
| obj.opts.caption = self.opts.caption.apply(item, [self, obj]); | |
| } | |
| // Make sure we have caption as a string or jQuery object | |
| if (!(obj.opts.caption instanceof $)) { | |
| obj.opts.caption = | |
| obj.opts.caption === undefined ? '' : obj.opts.caption + ''; | |
| } | |
| // Check if url contains "filter" used to filter the content | |
| // Example: "ajax.html #something" | |
| if (obj.type === 'ajax') { | |
| srcParts = src.split(/\s+/, 2); | |
| if (srcParts.length > 1) { | |
| obj.src = srcParts.shift(); | |
| obj.opts.filter = srcParts.shift(); | |
| } | |
| } | |
| // Hide all buttons and disable interactivity for modal items | |
| if (obj.opts.modal) { | |
| obj.opts = $.extend(true, obj.opts, { | |
| trapFocus: true, | |
| // Remove buttons | |
| infobar: 0, | |
| toolbar: 0, | |
| smallBtn: 0, | |
| // Disable keyboard navigation | |
| keyboard: 0, | |
| // Disable some modules | |
| slideShow: 0, | |
| fullScreen: 0, | |
| thumbs: 0, | |
| touch: 0, | |
| // Disable click event handlers | |
| clickContent: false, | |
| clickSlide: false, | |
| clickOutside: false, | |
| dblclickContent: false, | |
| dblclickSlide: false, | |
| dblclickOutside: false, | |
| }); | |
| } | |
| // Step 4 - Add processed object to group | |
| // ====================================== | |
| self.group.push(obj); | |
| }); | |
| // Update controls if gallery is already opened | |
| if (Object.keys(self.slides).length) { | |
| self.updateControls(); | |
| // Update thumbnails, if needed | |
| thumbs = self.Thumbs; | |
| if (thumbs && thumbs.isActive) { | |
| thumbs.create(); | |
| thumbs.focus(); | |
| } | |
| } | |
| }, | |
| // Attach an event handler functions for: | |
| // - navigation buttons | |
| // - browser scrolling, resizing; | |
| // - focusing | |
| // - keyboard | |
| // - detecting inactivity | |
| // ====================================== | |
| addEvents: function () { | |
| var self = this; | |
| self.removeEvents(); | |
| // Make navigation elements clickable | |
| // ================================== | |
| self.$refs.container | |
| .on('click.fb-close', '[data-fancybox-close]', function (e) { | |
| e.stopPropagation(); | |
| e.preventDefault(); | |
| self.close(e); | |
| }) | |
| .on( | |
| 'touchstart.fb-prev click.fb-prev', | |
| '[data-fancybox-prev]', | |
| function (e) { | |
| e.stopPropagation(); | |
| e.preventDefault(); | |
| self.previous(); | |
| } | |
| ) | |
| .on( | |
| 'touchstart.fb-next click.fb-next', | |
| '[data-fancybox-next]', | |
| function (e) { | |
| e.stopPropagation(); | |
| e.preventDefault(); | |
| self.next(); | |
| } | |
| ) | |
| .on('click.fb', '[data-fancybox-zoom]', function (e) { | |
| // Click handler for zoom button | |
| self[self.isScaledDown() ? 'scaleToActual' : 'scaleToFit'](); | |
| }); | |
| // Handle page scrolling and browser resizing | |
| // ========================================== | |
| $W.on('orientationchange.fb resize.fb', function (e) { | |
| if (e && e.originalEvent && e.originalEvent.type === 'resize') { | |
| if (self.requestId) { | |
| cancelAFrame(self.requestId); | |
| } | |
| self.requestId = requestAFrame(function () { | |
| self.update(e); | |
| }); | |
| } else { | |
| if (self.current && self.current.type === 'iframe') { | |
| self.$refs.stage.hide(); | |
| } | |
| setTimeout( | |
| function () { | |
| self.$refs.stage.show(); | |
| self.update(e); | |
| }, | |
| $.fancybox.isMobile ? 600 : 250 | |
| ); | |
| } | |
| }); | |
| $D.on('keydown.fb', function (e) { | |
| var instance = $.fancybox ? $.fancybox.getInstance() : null, | |
| current = instance.current, | |
| keycode = e.keyCode || e.which; | |
| // Trap keyboard focus inside of the modal | |
| // ======================================= | |
| if (keycode == 9) { | |
| if (current.opts.trapFocus) { | |
| self.focus(e); | |
| } | |
| return; | |
| } | |
| // Enable keyboard navigation | |
| // ========================== | |
| if ( | |
| !current.opts.keyboard || | |
| e.ctrlKey || | |
| e.altKey || | |
| e.shiftKey || | |
| $(e.target).is('input,textarea,video,audio,select') | |
| ) { | |
| return; | |
| } | |
| // Backspace and Esc keys | |
| if (keycode === 8 || keycode === 27) { | |
| e.preventDefault(); | |
| self.close(e); | |
| return; | |
| } | |
| // Left arrow and Up arrow | |
| if (keycode === 37 || keycode === 38) { | |
| e.preventDefault(); | |
| self.previous(); | |
| return; | |
| } | |
| // Righ arrow and Down arrow | |
| if (keycode === 39 || keycode === 40) { | |
| e.preventDefault(); | |
| self.next(); | |
| return; | |
| } | |
| self.trigger('afterKeydown', e, keycode); | |
| }); | |
| // Hide controls after some inactivity period | |
| if (self.group[self.currIndex].opts.idleTime) { | |
| self.idleSecondsCounter = 0; | |
| $D.on( | |
| 'mousemove.fb-idle mouseleave.fb-idle mousedown.fb-idle touchstart.fb-idle touchmove.fb-idle scroll.fb-idle keydown.fb-idle', | |
| function (e) { | |
| self.idleSecondsCounter = 0; | |
| if (self.isIdle) { | |
| self.showControls(); | |
| } | |
| self.isIdle = false; | |
| } | |
| ); | |
| self.idleInterval = window.setInterval(function () { | |
| self.idleSecondsCounter++; | |
| if ( | |
| self.idleSecondsCounter >= | |
| self.group[self.currIndex].opts.idleTime && | |
| !self.isDragging | |
| ) { | |
| self.isIdle = true; | |
| self.idleSecondsCounter = 0; | |
| self.hideControls(); | |
| } | |
| }, 1000); | |
| } | |
| }, | |
| // Remove events added by the core | |
| // =============================== | |
| removeEvents: function () { | |
| var self = this; | |
| $W.off('orientationchange.fb resize.fb'); | |
| $D.off('keydown.fb .fb-idle'); | |
| this.$refs.container.off('.fb-close .fb-prev .fb-next'); | |
| if (self.idleInterval) { | |
| window.clearInterval(self.idleInterval); | |
| self.idleInterval = null; | |
| } | |
| }, | |
| // Change to previous gallery item | |
| // =============================== | |
| previous: function (duration) { | |
| return this.jumpTo(this.currPos - 1, duration); | |
| }, | |
| // Change to next gallery item | |
| // =========================== | |
| next: function (duration) { | |
| return this.jumpTo(this.currPos + 1, duration); | |
| }, | |
| // Switch to selected gallery item | |
| // =============================== | |
| jumpTo: function (pos, duration) { | |
| var self = this, | |
| groupLen = self.group.length, | |
| firstRun, | |
| isMoved, | |
| loop, | |
| current, | |
| previous, | |
| slidePos, | |
| stagePos, | |
| prop, | |
| diff; | |
| if ( | |
| self.isDragging || | |
| self.isClosing || | |
| (self.isAnimating && self.firstRun) | |
| ) { | |
| return; | |
| } | |
| // Should loop? | |
| pos = parseInt(pos, 10); | |
| loop = self.current ? self.current.opts.loop : self.opts.loop; | |
| if (!loop && (pos < 0 || pos >= groupLen)) { | |
| return false; | |
| } | |
| // Check if opening for the first time; this helps to speed things up | |
| firstRun = self.firstRun = !Object.keys(self.slides).length; | |
| // Create slides | |
| previous = self.current; | |
| self.prevIndex = self.currIndex; | |
| self.prevPos = self.currPos; | |
| current = self.createSlide(pos); | |
| if (groupLen > 1) { | |
| if (loop || current.index < groupLen - 1) { | |
| self.createSlide(pos + 1); | |
| } | |
| if (loop || current.index > 0) { | |
| self.createSlide(pos - 1); | |
| } | |
| } | |
| self.current = current; | |
| self.currIndex = current.index; | |
| self.currPos = current.pos; | |
| self.trigger('beforeShow', firstRun); | |
| self.updateControls(); | |
| // Validate duration length | |
| current.forcedDuration = undefined; | |
| if ($.isNumeric(duration)) { | |
| current.forcedDuration = duration; | |
| } else { | |
| duration = | |
| current.opts[firstRun ? 'animationDuration' : 'transitionDuration']; | |
| } | |
| duration = parseInt(duration, 10); | |
| // Check if user has swiped the slides or if still animating | |
| isMoved = self.isMoved(current); | |
| // Make sure current slide is visible | |
| current.$slide.addClass('fancybox-slide--current'); | |
| // Fresh start - reveal container, current slide and start loading content | |
| if (firstRun) { | |
| if (current.opts.animationEffect && duration) { | |
| self.$refs.container.css('transition-duration', duration + 'ms'); | |
| } | |
| self.$refs.container.addClass('fancybox-is-open').trigger('focus'); | |
| // Attempt to load content into slide | |
| // This will later call `afterLoad` -> `revealContent` | |
| self.loadSlide(current); | |
| self.preload('image'); | |
| return; | |
| } | |
| // Get actual slide/stage positions (before cleaning up) | |
| slidePos = $.fancybox.getTranslate(previous.$slide); | |
| stagePos = $.fancybox.getTranslate(self.$refs.stage); | |
| // Clean up all slides | |
| $.each(self.slides, function (index, slide) { | |
| $.fancybox.stop(slide.$slide, true); | |
| }); | |
| if (previous.pos !== current.pos) { | |
| previous.isComplete = false; | |
| } | |
| previous.$slide.removeClass( | |
| 'fancybox-slide--complete fancybox-slide--current' | |
| ); | |
| // If slides are out of place, then animate them to correct position | |
| if (isMoved) { | |
| // Calculate horizontal swipe distance | |
| diff = | |
| slidePos.left - | |
| (previous.pos * slidePos.width + previous.pos * previous.opts.gutter); | |
| $.each(self.slides, function (index, slide) { | |
| slide.$slide | |
| .removeClass('fancybox-animated') | |
| .removeClass(function (index, className) { | |
| return (className.match(/(^|\s)fancybox-fx-\S+/g) || []).join( | |
| ' ' | |
| ); | |
| }); | |
| // Make sure that each slide is in equal distance | |
| // This is mostly needed for freshly added slides, because they are not yet positioned | |
| var leftPos = | |
| slide.pos * slidePos.width + slide.pos * slide.opts.gutter; | |
| $.fancybox.setTranslate(slide.$slide, { | |
| top: 0, | |
| left: leftPos - stagePos.left + diff, | |
| }); | |
| if (slide.pos !== current.pos) { | |
| slide.$slide.addClass( | |
| 'fancybox-slide--' + | |
| (slide.pos > current.pos ? 'next' : 'previous') | |
| ); | |
| } | |
| // Redraw to make sure that transition will start | |
| forceRedraw(slide.$slide); | |
| // Animate the slide | |
| $.fancybox.animate( | |
| slide.$slide, | |
| { | |
| top: 0, | |
| left: | |
| (slide.pos - current.pos) * slidePos.width + | |
| (slide.pos - current.pos) * slide.opts.gutter, | |
| }, | |
| duration, | |
| function () { | |
| slide.$slide | |
| .css({ | |
| transform: '', | |
| opacity: '', | |
| }) | |
| .removeClass('fancybox-slide--next fancybox-slide--previous'); | |
| if (slide.pos === self.currPos) { | |
| self.complete(); | |
| } | |
| } | |
| ); | |
| }); | |
| } else if (duration && current.opts.transitionEffect) { | |
| // Set transition effect for previously active slide | |
| prop = 'fancybox-animated fancybox-fx-' + current.opts.transitionEffect; | |
| previous.$slide.addClass( | |
| 'fancybox-slide--' + | |
| (previous.pos > current.pos ? 'next' : 'previous') | |
| ); | |
| $.fancybox.animate( | |
| previous.$slide, | |
| prop, | |
| duration, | |
| function () { | |
| previous.$slide | |
| .removeClass(prop) | |
| .removeClass('fancybox-slide--next fancybox-slide--previous'); | |
| }, | |
| false | |
| ); | |
| } | |
| if (current.isLoaded) { | |
| self.revealContent(current); | |
| } else { | |
| self.loadSlide(current); | |
| } | |
| self.preload('image'); | |
| }, | |
| // Create new "slide" element | |
| // These are gallery items that are actually added to DOM | |
| // ======================================================= | |
| createSlide: function (pos) { | |
| var self = this, | |
| $slide, | |
| index; | |
| index = pos % self.group.length; | |
| index = index < 0 ? self.group.length + index : index; | |
| if (!self.slides[pos] && self.group[index]) { | |
| $slide = $('<div class="fancybox-slide"></div>').appendTo( | |
| self.$refs.stage | |
| ); | |
| self.slides[pos] = $.extend(true, {}, self.group[index], { | |
| pos: pos, | |
| $slide: $slide, | |
| isLoaded: false, | |
| }); | |
| self.updateSlide(self.slides[pos]); | |
| } | |
| return self.slides[pos]; | |
| }, | |
| // Scale image to the actual size of the image; | |
| // x and y values should be relative to the slide | |
| // ============================================== | |
| scaleToActual: function (x, y, duration) { | |
| var self = this, | |
| current = self.current, | |
| $content = current.$content, | |
| canvasWidth = $.fancybox.getTranslate(current.$slide).width, | |
| canvasHeight = $.fancybox.getTranslate(current.$slide).height, | |
| newImgWidth = current.width, | |
| newImgHeight = current.height, | |
| imgPos, | |
| posX, | |
| posY, | |
| scaleX, | |
| scaleY; | |
| if ( | |
| self.isAnimating || | |
| self.isMoved() || | |
| !$content || | |
| !(current.type == 'image' && current.isLoaded && !current.hasError) | |
| ) { | |
| return; | |
| } | |
| self.isAnimating = true; | |
| $.fancybox.stop($content); | |
| x = x === undefined ? canvasWidth * 0.5 : x; | |
| y = y === undefined ? canvasHeight * 0.5 : y; | |
| imgPos = $.fancybox.getTranslate($content); | |
| imgPos.top -= $.fancybox.getTranslate(current.$slide).top; | |
| imgPos.left -= $.fancybox.getTranslate(current.$slide).left; | |
| scaleX = newImgWidth / imgPos.width; | |
| scaleY = newImgHeight / imgPos.height; | |
| // Get center position for original image | |
| posX = canvasWidth * 0.5 - newImgWidth * 0.5; | |
| posY = canvasHeight * 0.5 - newImgHeight * 0.5; | |
| // Make sure image does not move away from edges | |
| if (newImgWidth > canvasWidth) { | |
| posX = imgPos.left * scaleX - (x * scaleX - x); | |
| if (posX > 0) { | |
| posX = 0; | |
| } | |
| if (posX < canvasWidth - newImgWidth) { | |
| posX = canvasWidth - newImgWidth; | |
| } | |
| } | |
| if (newImgHeight > canvasHeight) { | |
| posY = imgPos.top * scaleY - (y * scaleY - y); | |
| if (posY > 0) { | |
| posY = 0; | |
| } | |
| if (posY < canvasHeight - newImgHeight) { | |
| posY = canvasHeight - newImgHeight; | |
| } | |
| } | |
| self.updateCursor(newImgWidth, newImgHeight); | |
| $.fancybox.animate( | |
| $content, | |
| { | |
| top: posY, | |
| left: posX, | |
| scaleX: scaleX, | |
| scaleY: scaleY, | |
| }, | |
| duration || 366, | |
| function () { | |
| self.isAnimating = false; | |
| } | |
| ); | |
| // Stop slideshow | |
| if (self.SlideShow && self.SlideShow.isActive) { | |
| self.SlideShow.stop(); | |
| } | |
| }, | |
| // Scale image to fit inside parent element | |
| // ======================================== | |
| scaleToFit: function (duration) { | |
| var self = this, | |
| current = self.current, | |
| $content = current.$content, | |
| end; | |
| if ( | |
| self.isAnimating || | |
| self.isMoved() || | |
| !$content || | |
| !(current.type == 'image' && current.isLoaded && !current.hasError) | |
| ) { | |
| return; | |
| } | |
| self.isAnimating = true; | |
| $.fancybox.stop($content); | |
| end = self.getFitPos(current); | |
| self.updateCursor(end.width, end.height); | |
| $.fancybox.animate( | |
| $content, | |
| { | |
| top: end.top, | |
| left: end.left, | |
| scaleX: end.width / $content.width(), | |
| scaleY: end.height / $content.height(), | |
| }, | |
| duration || 366, | |
| function () { | |
| self.isAnimating = false; | |
| } | |
| ); | |
| }, | |
| // Calculate image size to fit inside viewport | |
| // =========================================== | |
| getFitPos: function (slide) { | |
| var self = this, | |
| $content = slide.$content, | |
| $slide = slide.$slide, | |
| width = slide.width || slide.opts.width, | |
| height = slide.height || slide.opts.height, | |
| maxWidth, | |
| maxHeight, | |
| minRatio, | |
| aspectRatio, | |
| rez = {}; | |
| if (!slide.isLoaded || !$content || !$content.length) { | |
| return false; | |
| } | |
| maxWidth = $.fancybox.getTranslate(self.$refs.stage).width; | |
| maxHeight = $.fancybox.getTranslate(self.$refs.stage).height; | |
| maxWidth -= | |
| parseFloat($slide.css('paddingLeft')) + | |
| parseFloat($slide.css('paddingRight')) + | |
| parseFloat($content.css('marginLeft')) + | |
| parseFloat($content.css('marginRight')); | |
| maxHeight -= | |
| parseFloat($slide.css('paddingTop')) + | |
| parseFloat($slide.css('paddingBottom')) + | |
| parseFloat($content.css('marginTop')) + | |
| parseFloat($content.css('marginBottom')); | |
| if (!width || !height) { | |
| width = maxWidth; | |
| height = maxHeight; | |
| } | |
| minRatio = Math.min(1, maxWidth / width, maxHeight / height); | |
| width = minRatio * width; | |
| height = minRatio * height; | |
| // Adjust width/height to precisely fit into container | |
| if (width > maxWidth - 0.5) { | |
| width = maxWidth; | |
| } | |
| if (height > maxHeight - 0.5) { | |
| height = maxHeight; | |
| } | |
| if (slide.type === 'image') { | |
| rez.top = | |
| Math.floor((maxHeight - height) * 0.5) + | |
| parseFloat($slide.css('paddingTop')); | |
| rez.left = | |
| Math.floor((maxWidth - width) * 0.5) + | |
| parseFloat($slide.css('paddingLeft')); | |
| } else if (slide.contentType === 'video') { | |
| // Force aspect ratio for the video | |
| // "I say the whole world must learn of our peaceful ways… by force!" | |
| aspectRatio = | |
| slide.opts.width && slide.opts.height | |
| ? width / height | |
| : slide.opts.ratio || 16 / 9; | |
| if (height > width / aspectRatio) { | |
| height = width / aspectRatio; | |
| } else if (width > height * aspectRatio) { | |
| width = height * aspectRatio; | |
| } | |
| } | |
| rez.width = width; | |
| rez.height = height; | |
| return rez; | |
| }, | |
| // Update content size and position for all slides | |
| // ============================================== | |
| update: function (e) { | |
| var self = this; | |
| $.each(self.slides, function (key, slide) { | |
| self.updateSlide(slide, e); | |
| }); | |
| }, | |
| // Update slide content position and size | |
| // ====================================== | |
| updateSlide: function (slide, e) { | |
| var self = this, | |
| $content = slide && slide.$content, | |
| width = slide.width || slide.opts.width, | |
| height = slide.height || slide.opts.height, | |
| $slide = slide.$slide; | |
| // First, prevent caption overlap, if needed | |
| self.adjustCaption(slide); | |
| // Then resize content to fit inside the slide | |
| if ( | |
| $content && | |
| (width || height || slide.contentType === 'video') && | |
| !slide.hasError | |
| ) { | |
| $.fancybox.stop($content); | |
| $.fancybox.setTranslate($content, self.getFitPos(slide)); | |
| if (slide.pos === self.currPos) { | |
| self.isAnimating = false; | |
| self.updateCursor(); | |
| } | |
| } | |
| // Then some adjustments | |
| self.adjustLayout(slide); | |
| if ($slide.length) { | |
| $slide.trigger('refresh'); | |
| if (slide.pos === self.currPos) { | |
| self.$refs.toolbar | |
| .add(self.$refs.navigation.find('.fancybox-button--arrow_right')) | |
| .toggleClass( | |
| 'compensate-for-scrollbar', | |
| $slide.get(0).scrollHeight > $slide.get(0).clientHeight | |
| ); | |
| } | |
| } | |
| self.trigger('onUpdate', slide, e); | |
| }, | |
| // Horizontally center slide | |
| // ========================= | |
| centerSlide: function (duration) { | |
| var self = this, | |
| current = self.current, | |
| $slide = current.$slide; | |
| if (self.isClosing || !current) { | |
| return; | |
| } | |
| $slide.siblings().css({ | |
| transform: '', | |
| opacity: '', | |
| }); | |
| $slide | |
| .parent() | |
| .children() | |
| .removeClass('fancybox-slide--previous fancybox-slide--next'); | |
| $.fancybox.animate( | |
| $slide, | |
| { | |
| top: 0, | |
| left: 0, | |
| opacity: 1, | |
| }, | |
| duration === undefined ? 0 : duration, | |
| function () { | |
| // Clean up | |
| $slide.css({ | |
| transform: '', | |
| opacity: '', | |
| }); | |
| if (!current.isComplete) { | |
| self.complete(); | |
| } | |
| }, | |
| false | |
| ); | |
| }, | |
| // Check if current slide is moved (swiped) | |
| // ======================================== | |
| isMoved: function (slide) { | |
| var current = slide || this.current, | |
| slidePos, | |
| stagePos; | |
| if (!current) { | |
| return false; | |
| } | |
| stagePos = $.fancybox.getTranslate(this.$refs.stage); | |
| slidePos = $.fancybox.getTranslate(current.$slide); | |
| return ( | |
| !current.$slide.hasClass('fancybox-animated') && | |
| (Math.abs(slidePos.top - stagePos.top) > 0.5 || | |
| Math.abs(slidePos.left - stagePos.left) > 0.5) | |
| ); | |
| }, | |
| // Update cursor style depending if content can be zoomed | |
| // ====================================================== | |
| updateCursor: function (nextWidth, nextHeight) { | |
| var self = this, | |
| current = self.current, | |
| $container = self.$refs.container, | |
| canPan, | |
| isZoomable; | |
| if (!current || self.isClosing || !self.Guestures) { | |
| return; | |
| } | |
| $container.removeClass( | |
| 'fancybox-is-zoomable fancybox-can-zoomIn fancybox-can-zoomOut fancybox-can-swipe fancybox-can-pan' | |
| ); | |
| canPan = self.canPan(nextWidth, nextHeight); | |
| isZoomable = canPan ? true : self.isZoomable(); | |
| $container.toggleClass('fancybox-is-zoomable', isZoomable); | |
| $('[data-fancybox-zoom]').prop('disabled', !isZoomable); | |
| if (canPan) { | |
| $container.addClass('fancybox-can-pan'); | |
| } else if ( | |
| isZoomable && | |
| (current.opts.clickContent === 'zoom' || | |
| ($.isFunction(current.opts.clickContent) && | |
| current.opts.clickContent(current) == 'zoom')) | |
| ) { | |
| $container.addClass('fancybox-can-zoomIn'); | |
| } else if ( | |
| current.opts.touch && | |
| (current.opts.touch.vertical || self.group.length > 1) && | |
| current.contentType !== 'video' | |
| ) { | |
| $container.addClass('fancybox-can-swipe'); | |
| } | |
| }, | |
| // Check if current slide is zoomable | |
| // ================================== | |
| isZoomable: function () { | |
| var self = this, | |
| current = self.current, | |
| fitPos; | |
| // Assume that slide is zoomable if: | |
| // - image is still loading | |
| // - actual size of the image is smaller than available area | |
| if ( | |
| current && | |
| !self.isClosing && | |
| current.type === 'image' && | |
| !current.hasError | |
| ) { | |
| if (!current.isLoaded) { | |
| return true; | |
| } | |
| fitPos = self.getFitPos(current); | |
| if ( | |
| fitPos && | |
| (current.width > fitPos.width || current.height > fitPos.height) | |
| ) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| }, | |
| // Check if current image dimensions are smaller than actual | |
| // ========================================================= | |
| isScaledDown: function (nextWidth, nextHeight) { | |
| var self = this, | |
| rez = false, | |
| current = self.current, | |
| $content = current.$content; | |
| if (nextWidth !== undefined && nextHeight !== undefined) { | |
| rez = nextWidth < current.width && nextHeight < current.height; | |
| } else if ($content) { | |
| rez = $.fancybox.getTranslate($content); | |
| rez = rez.width < current.width && rez.height < current.height; | |
| } | |
| return rez; | |
| }, | |
| // Check if image dimensions exceed parent element | |
| // =============================================== | |
| canPan: function (nextWidth, nextHeight) { | |
| var self = this, | |
| current = self.current, | |
| pos = null, | |
| rez = false; | |
| if ( | |
| current.type === 'image' && | |
| (current.isComplete || (nextWidth && nextHeight)) && | |
| !current.hasError | |
| ) { | |
| rez = self.getFitPos(current); | |
| if (nextWidth !== undefined && nextHeight !== undefined) { | |
| pos = { | |
| width: nextWidth, | |
| height: nextHeight, | |
| }; | |
| } else if (current.isComplete) { | |
| pos = $.fancybox.getTranslate(current.$content); | |
| } | |
| if (pos && rez) { | |
| rez = | |
| Math.abs(pos.width - rez.width) > 1.5 || | |
| Math.abs(pos.height - rez.height) > 1.5; | |
| } | |
| } | |
| return rez; | |
| }, | |
| // Load content into the slide | |
| // =========================== | |
| loadSlide: function (slide) { | |
| var self = this, | |
| type, | |
| $slide, | |
| ajaxLoad; | |
| if (slide.isLoading || slide.isLoaded) { | |
| return; | |
| } | |
| slide.isLoading = true; | |
| if (self.trigger('beforeLoad', slide) === false) { | |
| slide.isLoading = false; | |
| return false; | |
| } | |
| type = slide.type; | |
| $slide = slide.$slide; | |
| $slide.off('refresh').trigger('onReset').addClass(slide.opts.slideClass); | |
| // Create content depending on the type | |
| switch (type) { | |
| case 'image': | |
| self.setImage(slide); | |
| break; | |
| case 'iframe': | |
| self.setIframe(slide); | |
| break; | |
| case 'html': | |
| self.setContent(slide, slide.src || slide.content); | |
| break; | |
| case 'video': | |
| self.setContent( | |
| slide, | |
| slide.opts.video.tpl | |
| .replace(/\{\{src\}\}/gi, slide.src) | |
| .replace( | |
| '{{format}}', | |
| slide.opts.videoFormat || slide.opts.video.format || '' | |
| ) | |
| .replace('{{poster}}', slide.thumb || '') | |
| ); | |
| break; | |
| case 'inline': | |
| if ($(slide.src).length) { | |
| self.setContent(slide, $(slide.src)); | |
| } else { | |
| self.setError(slide); | |
| } | |
| break; | |
| case 'ajax': | |
| self.showLoading(slide); | |
| ajaxLoad = $.ajax( | |
| $.extend({}, slide.opts.ajax.settings, { | |
| url: slide.src, | |
| success: function (data, textStatus) { | |
| if (textStatus === 'success') { | |
| self.setContent(slide, data); | |
| } | |
| }, | |
| error: function (jqXHR, textStatus) { | |
| if (jqXHR && textStatus !== 'abort') { | |
| self.setError(slide); | |
| } | |
| }, | |
| }) | |
| ); | |
| $slide.one('onReset', function () { | |
| ajaxLoad.abort(); | |
| }); | |
| break; | |
| default: | |
| self.setError(slide); | |
| break; | |
| } | |
| return true; | |
| }, | |
| // Use thumbnail image, if possible | |
| // ================================ | |
| setImage: function (slide) { | |
| var self = this, | |
| ghost; | |
| // Check if need to show loading icon | |
| setTimeout(function () { | |
| var $img = slide.$image; | |
| if ( | |
| !self.isClosing && | |
| slide.isLoading && | |
| (!$img || !$img.length || !$img[0].complete) && | |
| !slide.hasError | |
| ) { | |
| self.showLoading(slide); | |
| } | |
| }, 50); | |
| //Check if image has srcset | |
| self.checkSrcset(slide); | |
| // This will be wrapper containing both ghost and actual image | |
| slide.$content = $('<div class="fancybox-content"></div>') | |
| .addClass('fancybox-is-hidden') | |
| .appendTo(slide.$slide.addClass('fancybox-slide--image')); | |
| // If we have a thumbnail, we can display it while actual image is loading | |
| // Users will not stare at black screen and actual image will appear gradually | |
| if ( | |
| slide.opts.preload !== false && | |
| slide.opts.width && | |
| slide.opts.height && | |
| slide.thumb | |
| ) { | |
| slide.width = slide.opts.width; | |
| slide.height = slide.opts.height; | |
| ghost = document.createElement('img'); | |
| ghost.onerror = function () { | |
| $(this).remove(); | |
| slide.$ghost = null; | |
| }; | |
| ghost.onload = function () { | |
| self.afterLoad(slide); | |
| }; | |
| slide.$ghost = $(ghost) | |
| .addClass('fancybox-image') | |
| .appendTo(slide.$content) | |
| .attr('src', slide.thumb); | |
| } | |
| // Start loading actual image | |
| self.setBigImage(slide); | |
| }, | |
| // Check if image has srcset and get the source | |
| // ============================================ | |
| checkSrcset: function (slide) { | |
| var srcset = slide.opts.srcset || slide.opts.image.srcset, | |
| found, | |
| temp, | |
| pxRatio, | |
| windowWidth; | |
| // If we have "srcset", then we need to find first matching "src" value. | |
| // This is necessary, because when you set an src attribute, the browser will preload the image | |
| // before any javascript or even CSS is applied. | |
| if (srcset) { | |
| pxRatio = window.devicePixelRatio || 1; | |
| windowWidth = window.innerWidth * pxRatio; | |
| temp = srcset.split(',').map(function (el) { | |
| var ret = {}; | |
| el.trim() | |
| .split(/\s+/) | |
| .forEach(function (el, i) { | |
| var value = parseInt(el.substring(0, el.length - 1), 10); | |
| if (i === 0) { | |
| return (ret.url = el); | |
| } | |
| if (value) { | |
| ret.value = value; | |
| ret.postfix = el[el.length - 1]; | |
| } | |
| }); | |
| return ret; | |
| }); | |
| // Sort by value | |
| temp.sort(function (a, b) { | |
| return a.value - b.value; | |
| }); | |
| // Ok, now we have an array of all srcset values | |
| for (var j = 0; j < temp.length; j++) { | |
| var el = temp[j]; | |
| if ( | |
| (el.postfix === 'w' && el.value >= windowWidth) || | |
| (el.postfix === 'x' && el.value >= pxRatio) | |
| ) { | |
| found = el; | |
| break; | |
| } | |
| } | |
| // If not found, take the last one | |
| if (!found && temp.length) { | |
| found = temp[temp.length - 1]; | |
| } | |
| if (found) { | |
| slide.src = found.url; | |
| // If we have default width/height values, we can calculate height for matching source | |
| if (slide.width && slide.height && found.postfix == 'w') { | |
| slide.height = (slide.width / slide.height) * found.value; | |
| slide.width = found.value; | |
| } | |
| slide.opts.srcset = srcset; | |
| } | |
| } | |
| }, | |
| // Create full-size image | |
| // ====================== | |
| setBigImage: function (slide) { | |
| var self = this, | |
| img = document.createElement('img'), | |
| $img = $(img); | |
| slide.$image = $img | |
| .one('error', function () { | |
| self.setError(slide); | |
| }) | |
| .one('load', function () { | |
| var sizes; | |
| if (!slide.$ghost) { | |
| self.resolveImageSlideSize( | |
| slide, | |
| this.naturalWidth, | |
| this.naturalHeight | |
| ); | |
| self.afterLoad(slide); | |
| } | |
| if (self.isClosing) { | |
| return; | |
| } | |
| if (slide.opts.srcset) { | |
| sizes = slide.opts.sizes; | |
| if (!sizes || sizes === 'auto') { | |
| sizes = | |
| (slide.width / slide.height > 1 && $W.width() / $W.height() > 1 | |
| ? '100' | |
| : Math.round((slide.width / slide.height) * 100)) + 'vw'; | |
| } | |
| $img.attr('sizes', sizes).attr('srcset', slide.opts.srcset); | |
| } | |
| // Hide temporary image after some delay | |
| if (slide.$ghost) { | |
| setTimeout(function () { | |
| if (slide.$ghost && !self.isClosing) { | |
| slide.$ghost.hide(); | |
| } | |
| }, Math.min(300, Math.max(1000, slide.height / 1600))); | |
| } | |
| self.hideLoading(slide); | |
| }) | |
| .addClass('fancybox-image') | |
| .attr('src', slide.src) | |
| .appendTo(slide.$content); | |
| if ( | |
| (img.complete || img.readyState == 'complete') && | |
| $img.naturalWidth && | |
| $img.naturalHeight | |
| ) { | |
| $img.trigger('load'); | |
| } else if (img.error) { | |
| $img.trigger('error'); | |
| } | |
| }, | |
| // Computes the slide size from image size and maxWidth/maxHeight | |
| // ============================================================== | |
| resolveImageSlideSize: function (slide, imgWidth, imgHeight) { | |
| var maxWidth = parseInt(slide.opts.width, 10), | |
| maxHeight = parseInt(slide.opts.height, 10); | |
| // Sets the default values from the image | |
| slide.width = imgWidth; | |
| slide.height = imgHeight; | |
| if (maxWidth > 0) { | |
| slide.width = maxWidth; | |
| slide.height = Math.floor((maxWidth * imgHeight) / imgWidth); | |
| } | |
| if (maxHeight > 0) { | |
| slide.width = Math.floor((maxHeight * imgWidth) / imgHeight); | |
| slide.height = maxHeight; | |
| } | |
| }, | |
| // Create iframe wrapper, iframe and bindings | |
| // ========================================== | |
| setIframe: function (slide) { | |
| var self = this, | |
| opts = slide.opts.iframe, | |
| $slide = slide.$slide, | |
| $iframe; | |
| slide.$content = $( | |
| '<div class="fancybox-content' + | |
| (opts.preload ? ' fancybox-is-hidden' : '') + | |
| '"></div>' | |
| ) | |
| .css(opts.css) | |
| .appendTo($slide); | |
| $slide.addClass('fancybox-slide--' + slide.contentType); | |
| slide.$iframe = $iframe = $( | |
| opts.tpl.replace(/\{rnd\}/g, new Date().getTime()) | |
| ) | |
| .attr(opts.attr) | |
| .appendTo(slide.$content); | |
| if (opts.preload) { | |
| self.showLoading(slide); | |
| // Unfortunately, it is not always possible to determine if iframe is successfully loaded | |
| // (due to browser security policy) | |
| $iframe.on('load.fb error.fb', function (e) { | |
| this.isReady = 1; | |
| slide.$slide.trigger('refresh'); | |
| self.afterLoad(slide); | |
| }); | |
| // Recalculate iframe content size | |
| // =============================== | |
| $slide.on('refresh.fb', function () { | |
| var $content = slide.$content, | |
| frameWidth = opts.css.width, | |
| frameHeight = opts.css.height, | |
| $contents, | |
| $body; | |
| if ($iframe[0].isReady !== 1) { | |
| return; | |
| } | |
| try { | |
| $contents = $iframe.contents(); | |
| $body = $contents.find('body'); | |
| } catch (ignore) {} | |
| // Calculate content dimensions, if it is accessible | |
| if ($body && $body.length && $body.children().length) { | |
| // Avoid scrolling to top (if multiple instances) | |
| $slide.css('overflow', 'visible'); | |
| $content.css({ | |
| width: '100%', | |
| 'max-width': '100%', | |
| height: '9999px', | |
| }); | |
| if (frameWidth === undefined) { | |
| frameWidth = Math.ceil( | |
| Math.max($body[0].clientWidth, $body.outerWidth(true)) | |
| ); | |
| } | |
| $content | |
| .css('width', frameWidth ? frameWidth : '') | |
| .css('max-width', ''); | |
| if (frameHeight === undefined) { | |
| frameHeight = Math.ceil( | |
| Math.max($body[0].clientHeight, $body.outerHeight(true)) | |
| ); | |
| } | |
| $content.css('height', frameHeight ? frameHeight : ''); | |
| $slide.css('overflow', 'auto'); | |
| } | |
| $content.removeClass('fancybox-is-hidden'); | |
| }); | |
| } else { | |
| self.afterLoad(slide); | |
| } | |
| $iframe.attr('src', slide.src); | |
| // Remove iframe if closing or changing gallery item | |
| $slide.one('onReset', function () { | |
| // This helps IE not to throw errors when closing | |
| try { | |
| $(this).find('iframe').hide().unbind().attr('src', '//about:blank'); | |
| } catch (ignore) {} | |
| $(this).off('refresh.fb').empty(); | |
| slide.isLoaded = false; | |
| slide.isRevealed = false; | |
| }); | |
| }, | |
| // Wrap and append content to the slide | |
| // ====================================== | |
| setContent: function (slide, content) { | |
| var self = this; | |
| if (self.isClosing) { | |
| return; | |
| } | |
| self.hideLoading(slide); | |
| if (slide.$content) { | |
| $.fancybox.stop(slide.$content); | |
| } | |
| slide.$slide.empty(); | |
| // If content is a jQuery object, then it will be moved to the slide. | |
| // The placeholder is created so we will know where to put it back. | |
| if (isQuery(content) && content.parent().length) { | |
| // Make sure content is not already moved to fancyBox | |
| if ( | |
| content.hasClass('fancybox-content') || | |
| content.parent().hasClass('fancybox-content') | |
| ) { | |
| content.parents('.fancybox-slide').trigger('onReset'); | |
| } | |
| // Create temporary element marking original place of the content | |
| slide.$placeholder = $('<div>').hide().insertAfter(content); | |
| // Make sure content is visible | |
| content.css('display', 'inline-block'); | |
| } else if (!slide.hasError) { | |
| // If content is just a plain text, try to convert it to html | |
| if ($.type(content) === 'string') { | |
| content = $('<div>').append($.trim(content)).contents(); | |
| } | |
| // If "filter" option is provided, then filter content | |
| if (slide.opts.filter) { | |
| content = $('<div>').html(content).find(slide.opts.filter); | |
| } | |
| } | |
| slide.$slide.one('onReset', function () { | |
| // Pause all html5 video/audio | |
| $(this).find('video,audio').trigger('pause'); | |
| // Put content back | |
| if (slide.$placeholder) { | |
| slide.$placeholder | |
| .after(content.removeClass('fancybox-content').hide()) | |
| .remove(); | |
| slide.$placeholder = null; | |
| } | |
| // Remove custom close button | |
| if (slide.$smallBtn) { | |
| slide.$smallBtn.remove(); | |
| slide.$smallBtn = null; | |
| } | |
| // Remove content and mark slide as not loaded | |
| if (!slide.hasError) { | |
| $(this).empty(); | |
| slide.isLoaded = false; | |
| slide.isRevealed = false; | |
| } | |
| }); | |
| $(content).appendTo(slide.$slide); | |
| if ($(content).is('video,audio')) { | |
| $(content).addClass('fancybox-video'); | |
| $(content).wrap('<div></div>'); | |
| slide.contentType = 'video'; | |
| slide.opts.width = slide.opts.width || $(content).attr('width'); | |
| slide.opts.height = slide.opts.height || $(content).attr('height'); | |
| } | |
| slide.$content = slide.$slide | |
| .children() | |
| .filter('div,form,main,video,audio,article,.fancybox-content') | |
| .first(); | |
| slide.$content.siblings().hide(); | |
| // Re-check if there is a valid content | |
| // (in some cases, ajax response can contain various elements or plain text) | |
| if (!slide.$content.length) { | |
| slide.$content = slide.$slide | |
| .wrapInner('<div></div>') | |
| .children() | |
| .first(); | |
| } | |
| slide.$content.addClass('fancybox-content'); | |
| slide.$slide.addClass('fancybox-slide--' + slide.contentType); | |
| self.afterLoad(slide); | |
| }, | |
| // Display error message | |
| // ===================== | |
| setError: function (slide) { | |
| slide.hasError = true; | |
| slide.$slide | |
| .trigger('onReset') | |
| .removeClass('fancybox-slide--' + slide.contentType) | |
| .addClass('fancybox-slide--error'); | |
| slide.contentType = 'html'; | |
| this.setContent(slide, this.translate(slide, slide.opts.errorTpl)); | |
| if (slide.pos === this.currPos) { | |
| this.isAnimating = false; | |
| } | |
| }, | |
| // Show loading icon inside the slide | |
| // ================================== | |
| showLoading: function (slide) { | |
| var self = this; | |
| slide = slide || self.current; | |
| if (slide && !slide.$spinner) { | |
| slide.$spinner = $(self.translate(self, self.opts.spinnerTpl)) | |
| .appendTo(slide.$slide) | |
| .hide() | |
| .fadeIn('fast'); | |
| } | |
| }, | |
| // Remove loading icon from the slide | |
| // ================================== | |
| hideLoading: function (slide) { | |
| var self = this; | |
| slide = slide || self.current; | |
| if (slide && slide.$spinner) { | |
| slide.$spinner.stop().remove(); | |
| delete slide.$spinner; | |
| } | |
| }, | |
| // Adjustments after slide content has been loaded | |
| // =============================================== | |
| afterLoad: function (slide) { | |
| var self = this; | |
| if (self.isClosing) { | |
| return; | |
| } | |
| slide.isLoading = false; | |
| slide.isLoaded = true; | |
| self.trigger('afterLoad', slide); | |
| self.hideLoading(slide); | |
| // Add small close button | |
| if ( | |
| slide.opts.smallBtn && | |
| (!slide.$smallBtn || !slide.$smallBtn.length) | |
| ) { | |
| slide.$smallBtn = $( | |
| self.translate(slide, slide.opts.btnTpl.smallBtn) | |
| ).appendTo(slide.$content); | |
| } | |
| // Disable right click | |
| if (slide.opts.protect && slide.$content && !slide.hasError) { | |
| slide.$content.on('contextmenu.fb', function (e) { | |
| if (e.button == 2) { | |
| e.preventDefault(); | |
| } | |
| return true; | |
| }); | |
| // Add fake element on top of the image | |
| // This makes a bit harder for user to select image | |
| if (slide.type === 'image') { | |
| $('<div class="fancybox-spaceball"></div>').appendTo(slide.$content); | |
| } | |
| } | |
| self.adjustCaption(slide); | |
| self.adjustLayout(slide); | |
| if (slide.pos === self.currPos) { | |
| self.updateCursor(); | |
| } | |
| self.revealContent(slide); | |
| }, | |
| // Prevent caption overlap, | |
| // fix css inconsistency across browsers | |
| // ===================================== | |
| adjustCaption: function (slide) { | |
| var self = this, | |
| current = slide || self.current, | |
| caption = current.opts.caption, | |
| preventOverlap = current.opts.preventCaptionOverlap, | |
| $caption = self.$refs.caption, | |
| $clone, | |
| captionH = false; | |
| $caption.toggleClass('fancybox-caption--separate', preventOverlap); | |
| if (preventOverlap && caption && caption.length) { | |
| if (current.pos !== self.currPos) { | |
| $clone = $caption.clone().appendTo($caption.parent()); | |
| $clone.children().eq(0).empty().html(caption); | |
| captionH = $clone.outerHeight(true); | |
| $clone.empty().remove(); | |
| } else if (self.$caption) { | |
| captionH = self.$caption.outerHeight(true); | |
| } | |
| current.$slide.css('padding-bottom', captionH || ''); | |
| } | |
| }, | |
| // Simple hack to fix inconsistency across browsers, described here (affects Edge, too): | |
| // https://bugzilla.mozilla.org/show_bug.cgi?id=748518 | |
| // ==================================================================================== | |
| adjustLayout: function (slide) { | |
| var self = this, | |
| current = slide || self.current, | |
| scrollHeight, | |
| marginBottom, | |
| inlinePadding, | |
| actualPadding; | |
| if (current.isLoaded && current.opts.disableLayoutFix !== true) { | |
| current.$content.css('margin-bottom', ''); | |
| // If we would always set margin-bottom for the content, | |
| // then it would potentially break vertical align | |
| if (current.$content.outerHeight() > current.$slide.height() + 0.5) { | |
| inlinePadding = current.$slide[0].style['padding-bottom']; | |
| actualPadding = current.$slide.css('padding-bottom'); | |
| if (parseFloat(actualPadding) > 0) { | |
| scrollHeight = current.$slide[0].scrollHeight; | |
| current.$slide.css('padding-bottom', 0); | |
| if (Math.abs(scrollHeight - current.$slide[0].scrollHeight) < 1) { | |
| marginBottom = actualPadding; | |
| } | |
| current.$slide.css('padding-bottom', inlinePadding); | |
| } | |
| } | |
| current.$content.css('margin-bottom', marginBottom); | |
| } | |
| }, | |
| // Make content visible | |
| // This method is called right after content has been loaded or | |
| // user navigates gallery and transition should start | |
| // ============================================================ | |
| revealContent: function (slide) { | |
| var self = this, | |
| $slide = slide.$slide, | |
| end = false, | |
| start = false, | |
| isMoved = self.isMoved(slide), | |
| isRevealed = slide.isRevealed, | |
| effect, | |
| effectClassName, | |
| duration, | |
| opacity; | |
| slide.isRevealed = true; | |
| effect = | |
| slide.opts[self.firstRun ? 'animationEffect' : 'transitionEffect']; | |
| duration = | |
| slide.opts[self.firstRun ? 'animationDuration' : 'transitionDuration']; | |
| duration = parseInt( | |
| slide.forcedDuration === undefined ? duration : slide.forcedDuration, | |
| 10 | |
| ); | |
| if (isMoved || slide.pos !== self.currPos || !duration) { | |
| effect = false; | |
| } | |
| // Check if can zoom | |
| if (effect === 'zoom') { | |
| if ( | |
| slide.pos === self.currPos && | |
| duration && | |
| slide.type === 'image' && | |
| !slide.hasError && | |
| (start = self.getThumbPos(slide)) | |
| ) { | |
| end = self.getFitPos(slide); | |
| } else { | |
| effect = 'fade'; | |
| } | |
| } | |
| // Zoom animation | |
| // ============== | |
| if (effect === 'zoom') { | |
| self.isAnimating = true; | |
| end.scaleX = end.width / start.width; | |
| end.scaleY = end.height / start.height; | |
| // Check if we need to animate opacity | |
| opacity = slide.opts.zoomOpacity; | |
| if (opacity == 'auto') { | |
| opacity = | |
| Math.abs(slide.width / slide.height - start.width / start.height) > | |
| 0.1; | |
| } | |
| if (opacity) { | |
| start.opacity = 0.1; | |
| end.opacity = 1; | |
| } | |
| // Draw image at start position | |
| $.fancybox.setTranslate( | |
| slide.$content.removeClass('fancybox-is-hidden'), | |
| start | |
| ); | |
| forceRedraw(slide.$content); | |
| // Start animation | |
| $.fancybox.animate(slide.$content, end, duration, function () { | |
| self.isAnimating = false; | |
| self.complete(); | |
| }); | |
| return; | |
| } | |
| self.updateSlide(slide); | |
| // Simply show content if no effect | |
| // ================================ | |
| if (!effect) { | |
| slide.$content.removeClass('fancybox-is-hidden'); | |
| if ( | |
| !isRevealed && | |
| isMoved && | |
| slide.type === 'image' && | |
| !slide.hasError | |
| ) { | |
| slide.$content.hide().fadeIn('fast'); | |
| } | |
| if (slide.pos === self.currPos) { | |
| self.complete(); | |
| } | |
| return; | |
| } | |
| // Prepare for CSS transiton | |
| // ========================= | |
| $.fancybox.stop($slide); | |
| //effectClassName = "fancybox-animated fancybox-slide--" + (slide.pos >= self.prevPos ? "next" : "previous") + " fancybox-fx-" + effect; | |
| effectClassName = | |
| 'fancybox-slide--' + | |
| (slide.pos >= self.prevPos ? 'next' : 'previous') + | |
| ' fancybox-animated fancybox-fx-' + | |
| effect; | |
| $slide.addClass(effectClassName).removeClass('fancybox-slide--current'); //.addClass(effectClassName); | |
| slide.$content.removeClass('fancybox-is-hidden'); | |
| // Force reflow | |
| forceRedraw($slide); | |
| if (slide.type !== 'image') { | |
| slide.$content.hide().show(0); | |
| } | |
| $.fancybox.animate( | |
| $slide, | |
| 'fancybox-slide--current', | |
| duration, | |
| function () { | |
| $slide.removeClass(effectClassName).css({ | |
| transform: '', | |
| opacity: '', | |
| }); | |
| if (slide.pos === self.currPos) { | |
| self.complete(); | |
| } | |
| }, | |
| true | |
| ); | |
| }, | |
| // Check if we can and have to zoom from thumbnail | |
| //================================================ | |
| getThumbPos: function (slide) { | |
| var rez = false, | |
| $thumb = slide.$thumb, | |
| thumbPos, | |
| btw, | |
| brw, | |
| bbw, | |
| blw; | |
| if (!$thumb || !inViewport($thumb[0])) { | |
| return false; | |
| } | |
| thumbPos = $.fancybox.getTranslate($thumb); | |
| btw = parseFloat($thumb.css('border-top-width') || 0); | |
| brw = parseFloat($thumb.css('border-right-width') || 0); | |
| bbw = parseFloat($thumb.css('border-bottom-width') || 0); | |
| blw = parseFloat($thumb.css('border-left-width') || 0); | |
| rez = { | |
| top: thumbPos.top + btw, | |
| left: thumbPos.left + blw, | |
| width: thumbPos.width - brw - blw, | |
| height: thumbPos.height - btw - bbw, | |
| scaleX: 1, | |
| scaleY: 1, | |
| }; | |
| return thumbPos.width > 0 && thumbPos.height > 0 ? rez : false; | |
| }, | |
| // Final adjustments after current gallery item is moved to position | |
| // and it`s content is loaded | |
| // ================================================================== | |
| complete: function () { | |
| var self = this, | |
| current = self.current, | |
| slides = {}, | |
| $el; | |
| if (self.isMoved() || !current.isLoaded) { | |
| return; | |
| } | |
| if (!current.isComplete) { | |
| current.isComplete = true; | |
| current.$slide.siblings().trigger('onReset'); | |
| self.preload('inline'); | |
| // Trigger any CSS transiton inside the slide | |
| forceRedraw(current.$slide); | |
| current.$slide.addClass('fancybox-slide--complete'); | |
| // Remove unnecessary slides | |
| $.each(self.slides, function (key, slide) { | |
| if (slide.pos >= self.currPos - 1 && slide.pos <= self.currPos + 1) { | |
| slides[slide.pos] = slide; | |
| } else if (slide) { | |
| $.fancybox.stop(slide.$slide); | |
| slide.$slide.off().remove(); | |
| } | |
| }); | |
| self.slides = slides; | |
| } | |
| self.isAnimating = false; | |
| self.updateCursor(); | |
| self.trigger('afterShow'); | |
| // Autoplay first html5 video/audio | |
| if (!!current.opts.video.autoStart) { | |
| current.$slide | |
| .find('video,audio') | |
| .filter(':visible:first') | |
| .trigger('play') | |
| .one('ended', function () { | |
| if (Document.exitFullscreen) { | |
| Document.exitFullscreen(); | |
| } else if (this.webkitExitFullscreen) { | |
| this.webkitExitFullscreen(); | |
| } | |
| self.next(); | |
| }); | |
| } | |
| // Try to focus on the first focusable element | |
| if (current.opts.autoFocus && current.contentType === 'html') { | |
| // Look for the first input with autofocus attribute | |
| $el = current.$content.find('input[autofocus]:enabled:visible:first'); | |
| if ($el.length) { | |
| $el.trigger('focus'); | |
| } else { | |
| self.focus(null, true); | |
| } | |
| } | |
| // Avoid jumping | |
| current.$slide.scrollTop(0).scrollLeft(0); | |
| }, | |
| // Preload next and previous slides | |
| // ================================ | |
| preload: function (type) { | |
| var self = this, | |
| prev, | |
| next; | |
| if (self.group.length < 2) { | |
| return; | |
| } | |
| next = self.slides[self.currPos + 1]; | |
| prev = self.slides[self.currPos - 1]; | |
| if (prev && prev.type === type) { | |
| self.loadSlide(prev); | |
| } | |
| if (next && next.type === type) { | |
| self.loadSlide(next); | |
| } | |
| }, | |
| // Try to find and focus on the first focusable element | |
| // ==================================================== | |
| focus: function (e, firstRun) { | |
| var self = this, | |
| focusableStr = [ | |
| 'a[href]', | |
| 'area[href]', | |
| 'input:not([disabled]):not([type="hidden"]):not([aria-hidden])', | |
| 'select:not([disabled]):not([aria-hidden])', | |
| 'textarea:not([disabled]):not([aria-hidden])', | |
| 'button:not([disabled]):not([aria-hidden])', | |
| 'iframe', | |
| 'object', | |
| 'embed', | |
| 'video', | |
| 'audio', | |
| '[contenteditable]', | |
| '[tabindex]:not([tabindex^="-"])', | |
| ].join(','), | |
| focusableItems, | |
| focusedItemIndex; | |
| if (self.isClosing) { | |
| return; | |
| } | |
| if (e || !self.current || !self.current.isComplete) { | |
| // Focus on any element inside fancybox | |
| focusableItems = self.$refs.container.find('*:visible'); | |
| } else { | |
| // Focus inside current slide | |
| focusableItems = self.current.$slide.find( | |
| '*:visible' + (firstRun ? ':not(.fancybox-close-small)' : '') | |
| ); | |
| } | |
| focusableItems = focusableItems.filter(focusableStr).filter(function () { | |
| return ( | |
| $(this).css('visibility') !== 'hidden' && | |
| !$(this).hasClass('disabled') | |
| ); | |
| }); | |
| if (focusableItems.length) { | |
| focusedItemIndex = focusableItems.index(document.activeElement); | |
| if (e && e.shiftKey) { | |
| // Back tab | |
| if (focusedItemIndex < 0 || focusedItemIndex == 0) { | |
| e.preventDefault(); | |
| focusableItems.eq(focusableItems.length - 1).trigger('focus'); | |
| } | |
| } else { | |
| // Outside or Forward tab | |
| if ( | |
| focusedItemIndex < 0 || | |
| focusedItemIndex == focusableItems.length - 1 | |
| ) { | |
| if (e) { | |
| e.preventDefault(); | |
| } | |
| focusableItems.eq(0).trigger('focus'); | |
| } | |
| } | |
| } else { | |
| self.$refs.container.trigger('focus'); | |
| } | |
| }, | |
| // Activates current instance - brings container to the front and enables keyboard, | |
| // notifies other instances about deactivating | |
| // ================================================================================= | |
| activate: function () { | |
| var self = this; | |
| // Deactivate all instances | |
| $('.fancybox-container').each(function () { | |
| var instance = $(this).data('FancyBox'); | |
| // Skip self and closing instances | |
| if (instance && instance.id !== self.id && !instance.isClosing) { | |
| instance.trigger('onDeactivate'); | |
| instance.removeEvents(); | |
| instance.isVisible = false; | |
| } | |
| }); | |
| self.isVisible = true; | |
| if (self.current || self.isIdle) { | |
| self.update(); | |
| self.updateControls(); | |
| } | |
| self.trigger('onActivate'); | |
| self.addEvents(); | |
| }, | |
| // Start closing procedure | |
| // This will start "zoom-out" animation if needed and clean everything up afterwards | |
| // ================================================================================= | |
| close: function (e, d) { | |
| var self = this, | |
| current = self.current, | |
| effect, | |
| duration, | |
| $content, | |
| domRect, | |
| opacity, | |
| start, | |
| end; | |
| var done = function () { | |
| self.cleanUp(e); | |
| }; | |
| if (self.isClosing) { | |
| return false; | |
| } | |
| self.isClosing = true; | |
| // If beforeClose callback prevents closing, make sure content is centered | |
| if (self.trigger('beforeClose', e) === false) { | |
| self.isClosing = false; | |
| requestAFrame(function () { | |
| self.update(); | |
| }); | |
| return false; | |
| } | |
| // Remove all events | |
| // If there are multiple instances, they will be set again by "activate" method | |
| self.removeEvents(); | |
| $content = current.$content; | |
| effect = current.opts.animationEffect; | |
| duration = $.isNumeric(d) | |
| ? d | |
| : effect | |
| ? current.opts.animationDuration | |
| : 0; | |
| current.$slide.removeClass( | |
| 'fancybox-slide--complete fancybox-slide--next fancybox-slide--previous fancybox-animated' | |
| ); | |
| if (e !== true) { | |
| $.fancybox.stop(current.$slide); | |
| } else { | |
| effect = false; | |
| } | |
| // Remove other slides | |
| current.$slide.siblings().trigger('onReset').remove(); | |
| // Trigger animations | |
| if (duration) { | |
| self.$refs.container | |
| .removeClass('fancybox-is-open') | |
| .addClass('fancybox-is-closing') | |
| .css('transition-duration', duration + 'ms'); | |
| } | |
| // Clean up | |
| self.hideLoading(current); | |
| self.hideControls(true); | |
| self.updateCursor(); | |
| // Check if possible to zoom-out | |
| if ( | |
| effect === 'zoom' && | |
| !( | |
| $content && | |
| duration && | |
| current.type === 'image' && | |
| !self.isMoved() && | |
| !current.hasError && | |
| (end = self.getThumbPos(current)) | |
| ) | |
| ) { | |
| effect = 'fade'; | |
| } | |
| if (effect === 'zoom') { | |
| $.fancybox.stop($content); | |
| domRect = $.fancybox.getTranslate($content); | |
| start = { | |
| top: domRect.top, | |
| left: domRect.left, | |
| scaleX: domRect.width / end.width, | |
| scaleY: domRect.height / end.height, | |
| width: end.width, | |
| height: end.height, | |
| }; | |
| // Check if we need to animate opacity | |
| opacity = current.opts.zoomOpacity; | |
| if (opacity == 'auto') { | |
| opacity = | |
| Math.abs(current.width / current.height - end.width / end.height) > | |
| 0.1; | |
| } | |
| if (opacity) { | |
| end.opacity = 0; | |
| } | |
| $.fancybox.setTranslate($content, start); | |
| forceRedraw($content); | |
| $.fancybox.animate($content, end, duration, done); | |
| return true; | |
| } | |
| if (effect && duration) { | |
| $.fancybox.animate( | |
| current.$slide | |
| .addClass('fancybox-slide--previous') | |
| .removeClass('fancybox-slide--current'), | |
| 'fancybox-animated fancybox-fx-' + effect, | |
| duration, | |
| done | |
| ); | |
| } else { | |
| // If skip animation | |
| if (e === true) { | |
| setTimeout(done, duration); | |
| } else { | |
| done(); | |
| } | |
| } | |
| return true; | |
| }, | |
| // Final adjustments after removing the instance | |
| // ============================================= | |
| cleanUp: function (e) { | |
| var self = this, | |
| instance, | |
| $focus = self.current.opts.$orig, | |
| x, | |
| y; | |
| self.current.$slide.trigger('onReset'); | |
| self.$refs.container.empty().remove(); | |
| self.trigger('afterClose', e); | |
| // Place back focus | |
| if (!!self.current.opts.backFocus) { | |
| if (!$focus || !$focus.length || !$focus.is(':visible')) { | |
| $focus = self.$trigger; | |
| } | |
| if ($focus && $focus.length) { | |
| x = window.scrollX; | |
| y = window.scrollY; | |
| $focus.trigger('focus'); | |
| $('html, body').scrollTop(y).scrollLeft(x); | |
| } | |
| } | |
| self.current = null; | |
| // Check if there are other instances | |
| instance = $.fancybox.getInstance(); | |
| if (instance) { | |
| instance.activate(); | |
| } else { | |
| $('body').removeClass('fancybox-active compensate-for-scrollbar'); | |
| $('#fancybox-style-noscroll').remove(); | |
| } | |
| }, | |
| // Call callback and trigger an event | |
| // ================================== | |
| trigger: function (name, slide) { | |
| var args = Array.prototype.slice.call(arguments, 1), | |
| self = this, | |
| obj = slide && slide.opts ? slide : self.current, | |
| rez; | |
| if (obj) { | |
| args.unshift(obj); | |
| } else { | |
| obj = self; | |
| } | |
| args.unshift(self); | |
| if ($.isFunction(obj.opts[name])) { | |
| rez = obj.opts[name].apply(obj, args); | |
| } | |
| if (rez === false) { | |
| return rez; | |
| } | |
| if (name === 'afterClose' || !self.$refs) { | |
| $D.trigger(name + '.fb', args); | |
| } else { | |
| self.$refs.container.trigger(name + '.fb', args); | |
| } | |
| }, | |
| // Update infobar values, navigation button states and reveal caption | |
| // ================================================================== | |
| updateControls: function () { | |
| var self = this, | |
| current = self.current, | |
| index = current.index, | |
| $container = self.$refs.container, | |
| $caption = self.$refs.caption, | |
| caption = current.opts.caption; | |
| // Recalculate content dimensions | |
| current.$slide.trigger('refresh'); | |
| // Set caption | |
| if (caption && caption.length) { | |
| self.$caption = $caption; | |
| $caption.children().eq(0).html(caption); | |
| } else { | |
| self.$caption = null; | |
| } | |
| if (!self.hasHiddenControls && !self.isIdle) { | |
| self.showControls(); | |
| } | |
| // Update info and navigation elements | |
| $container.find('[data-fancybox-count]').html(self.group.length); | |
| $container.find('[data-fancybox-index]').html(index + 1); | |
| $container | |
| .find('[data-fancybox-prev]') | |
| .prop('disabled', !current.opts.loop && index <= 0); | |
| $container | |
| .find('[data-fancybox-next]') | |
| .prop('disabled', !current.opts.loop && index >= self.group.length - 1); | |
| if (current.type === 'image') { | |
| // Re-enable buttons; update download button source | |
| $container | |
| .find('[data-fancybox-zoom]') | |
| .show() | |
| .end() | |
| .find('[data-fancybox-download]') | |
| .attr('href', current.opts.image.src || current.src) | |
| .show(); | |
| } else if (current.opts.toolbar) { | |
| $container.find('[data-fancybox-download],[data-fancybox-zoom]').hide(); | |
| } | |
| // Make sure focus is not on disabled button/element | |
| if ($(document.activeElement).is(':hidden,[disabled]')) { | |
| self.$refs.container.trigger('focus'); | |
| } | |
| }, | |
| // Hide toolbar and caption | |
| // ======================== | |
| hideControls: function (andCaption) { | |
| var self = this, | |
| arr = ['infobar', 'toolbar', 'nav']; | |
| if (andCaption || !self.current.opts.preventCaptionOverlap) { | |
| arr.push('caption'); | |
| } | |
| this.$refs.container.removeClass( | |
| arr | |
| .map(function (i) { | |
| return 'fancybox-show-' + i; | |
| }) | |
| .join(' ') | |
| ); | |
| this.hasHiddenControls = true; | |
| }, | |
| showControls: function () { | |
| var self = this, | |
| opts = self.current ? self.current.opts : self.opts, | |
| $container = self.$refs.container; | |
| self.hasHiddenControls = false; | |
| self.idleSecondsCounter = 0; | |
| $container | |
| .toggleClass('fancybox-show-toolbar', !!(opts.toolbar && opts.buttons)) | |
| .toggleClass( | |
| 'fancybox-show-infobar', | |
| !!(opts.infobar && self.group.length > 1) | |
| ) | |
| .toggleClass('fancybox-show-caption', !!self.$caption) | |
| .toggleClass( | |
| 'fancybox-show-nav', | |
| !!(opts.arrows && self.group.length > 1) | |
| ) | |
| .toggleClass('fancybox-is-modal', !!opts.modal); | |
| }, | |
| // Toggle toolbar and caption | |
| // ========================== | |
| toggleControls: function () { | |
| if (this.hasHiddenControls) { | |
| this.showControls(); | |
| } else { | |
| this.hideControls(); | |
| } | |
| }, | |
| }); | |
| $.fancybox = { | |
| version: '3.5.7', | |
| defaults: defaults, | |
| // Get current instance and execute a command. | |
| // | |
| // Examples of usage: | |
| // | |
| // $instance = $.fancybox.getInstance(); | |
| // $.fancybox.getInstance().jumpTo( 1 ); | |
| // $.fancybox.getInstance( 'jumpTo', 1 ); | |
| // $.fancybox.getInstance( function() { | |
| // console.info( this.currIndex ); | |
| // }); | |
| // ====================================================== | |
| getInstance: function (command) { | |
| var instance = $( | |
| '.fancybox-container:not(".fancybox-is-closing"):last' | |
| ).data('FancyBox'), | |
| args = Array.prototype.slice.call(arguments, 1); | |
| if (instance instanceof FancyBox) { | |
| if ($.type(command) === 'string') { | |
| instance[command].apply(instance, args); | |
| } else if ($.type(command) === 'function') { | |
| command.apply(instance, args); | |
| } | |
| return instance; | |
| } | |
| return false; | |
| }, | |
| // Create new instance | |
| // =================== | |
| open: function (items, opts, index) { | |
| return new FancyBox(items, opts, index); | |
| }, | |
| // Close current or all instances | |
| // ============================== | |
| close: function (all) { | |
| var instance = this.getInstance(); | |
| if (instance) { | |
| instance.close(); | |
| // Try to find and close next instance | |
| if (all === true) { | |
| this.close(all); | |
| } | |
| } | |
| }, | |
| // Close all instances and unbind all events | |
| // ========================================= | |
| destroy: function () { | |
| this.close(true); | |
| $D.add('body').off('click.fb-start', '**'); | |
| }, | |
| // Try to detect mobile devices | |
| // ============================ | |
| isMobile: | |
| /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( | |
| navigator.userAgent | |
| ), | |
| // Detect if 'translate3d' support is available | |
| // ============================================ | |
| use3d: (function () { | |
| var div = document.createElement('div'); | |
| return ( | |
| window.getComputedStyle && | |
| window.getComputedStyle(div) && | |
| window.getComputedStyle(div).getPropertyValue('transform') && | |
| !(document.documentMode && document.documentMode < 11) | |
| ); | |
| })(), | |
| // Helper function to get current visual state of an element | |
| // returns array[ top, left, horizontal-scale, vertical-scale, opacity ] | |
| // ===================================================================== | |
| getTranslate: function ($el) { | |
| var domRect; | |
| if (!$el || !$el.length) { | |
| return false; | |
| } | |
| domRect = $el[0].getBoundingClientRect(); | |
| return { | |
| top: domRect.top || 0, | |
| left: domRect.left || 0, | |
| width: domRect.width, | |
| height: domRect.height, | |
| opacity: parseFloat($el.css('opacity')), | |
| }; | |
| }, | |
| // Shortcut for setting "translate3d" properties for element | |
| // Can set be used to set opacity, too | |
| // ======================================================== | |
| setTranslate: function ($el, props) { | |
| var str = '', | |
| css = {}; | |
| if (!$el || !props) { | |
| return; | |
| } | |
| if (props.left !== undefined || props.top !== undefined) { | |
| str = | |
| (props.left === undefined ? $el.position().left : props.left) + | |
| 'px, ' + | |
| (props.top === undefined ? $el.position().top : props.top) + | |
| 'px'; | |
| if (this.use3d) { | |
| str = 'translate3d(' + str + ', 0px)'; | |
| } else { | |
| str = 'translate(' + str + ')'; | |
| } | |
| } | |
| if (props.scaleX !== undefined && props.scaleY !== undefined) { | |
| str += ' scale(' + props.scaleX + ', ' + props.scaleY + ')'; | |
| } else if (props.scaleX !== undefined) { | |
| str += ' scaleX(' + props.scaleX + ')'; | |
| } | |
| if (str.length) { | |
| css.transform = str; | |
| } | |
| if (props.opacity !== undefined) { | |
| css.opacity = props.opacity; | |
| } | |
| if (props.width !== undefined) { | |
| css.width = props.width; | |
| } | |
| if (props.height !== undefined) { | |
| css.height = props.height; | |
| } | |
| return $el.css(css); | |
| }, | |
| // Simple CSS transition handler | |
| // ============================= | |
| animate: function ($el, to, duration, callback, leaveAnimationName) { | |
| var self = this, | |
| from; | |
| if ($.isFunction(duration)) { | |
| callback = duration; | |
| duration = null; | |
| } | |
| self.stop($el); | |
| from = self.getTranslate($el); | |
| $el.on(transitionEnd, function (e) { | |
| // Skip events from child elements and z-index change | |
| if ( | |
| e && | |
| e.originalEvent && | |
| (!$el.is(e.originalEvent.target) || | |
| e.originalEvent.propertyName == 'z-index') | |
| ) { | |
| return; | |
| } | |
| self.stop($el); | |
| if ($.isNumeric(duration)) { | |
| $el.css('transition-duration', ''); | |
| } | |
| if ($.isPlainObject(to)) { | |
| if (to.scaleX !== undefined && to.scaleY !== undefined) { | |
| self.setTranslate($el, { | |
| top: to.top, | |
| left: to.left, | |
| width: from.width * to.scaleX, | |
| height: from.height * to.scaleY, | |
| scaleX: 1, | |
| scaleY: 1, | |
| }); | |
| } | |
| } else if (leaveAnimationName !== true) { | |
| $el.removeClass(to); | |
| } | |
| if ($.isFunction(callback)) { | |
| callback(e); | |
| } | |
| }); | |
| if ($.isNumeric(duration)) { | |
| $el.css('transition-duration', duration + 'ms'); | |
| } | |
| // Start animation by changing CSS properties or class name | |
| if ($.isPlainObject(to)) { | |
| if (to.scaleX !== undefined && to.scaleY !== undefined) { | |
| delete to.width; | |
| delete to.height; | |
| if ($el.parent().hasClass('fancybox-slide--image')) { | |
| $el.parent().addClass('fancybox-is-scaling'); | |
| } | |
| } | |
| $.fancybox.setTranslate($el, to); | |
| } else { | |
| $el.addClass(to); | |
| } | |
| // Make sure that `transitionend` callback gets fired | |
| $el.data( | |
| 'timer', | |
| setTimeout(function () { | |
| $el.trigger(transitionEnd); | |
| }, duration + 33) | |
| ); | |
| }, | |
| stop: function ($el, callCallback) { | |
| if ($el && $el.length) { | |
| clearTimeout($el.data('timer')); | |
| if (callCallback) { | |
| $el.trigger(transitionEnd); | |
| } | |
| $el.off(transitionEnd).css('transition-duration', ''); | |
| $el.parent().removeClass('fancybox-is-scaling'); | |
| } | |
| }, | |
| }; | |
| // Default click handler for "fancyboxed" links | |
| // ============================================ | |
| function _run(e, opts) { | |
| var items = [], | |
| index = 0, | |
| $target, | |
| value, | |
| instance; | |
| // Avoid opening multiple times | |
| if (e && e.isDefaultPrevented()) { | |
| return; | |
| } | |
| e.preventDefault(); | |
| opts = opts || {}; | |
| if (e && e.data) { | |
| opts = mergeOpts(e.data.options, opts); | |
| } | |
| $target = opts.$target || $(e.currentTarget).trigger('blur'); | |
| instance = $.fancybox.getInstance(); | |
| if (instance && instance.$trigger && instance.$trigger.is($target)) { | |
| return; | |
| } | |
| if (opts.selector) { | |
| items = $(opts.selector); | |
| } else { | |
| // Get all related items and find index for clicked one | |
| value = $target.attr('data-fancybox') || ''; | |
| if (value) { | |
| items = e.data ? e.data.items : []; | |
| items = items.length | |
| ? items.filter('[data-fancybox="' + value + '"]') | |
| : $('[data-fancybox="' + value + '"]'); | |
| } else { | |
| items = [$target]; | |
| } | |
| } | |
| index = $(items).index($target); | |
| // Sometimes current item can not be found | |
| if (index < 0) { | |
| index = 0; | |
| } | |
| instance = $.fancybox.open(items, opts, index); | |
| // Save last active element | |
| instance.$trigger = $target; | |
| } | |
| // Create a jQuery plugin | |
| // ====================== | |
| $.fn.fancybox = function (options) { | |
| var selector; | |
| options = options || {}; | |
| selector = options.selector || false; | |
| if (selector) { | |
| // Use body element instead of document so it executes first | |
| $('body').off('click.fb-start', selector).on( | |
| 'click.fb-start', | |
| selector, | |
| { | |
| options: options, | |
| }, | |
| _run | |
| ); | |
| } else { | |
| this.off('click.fb-start').on( | |
| 'click.fb-start', | |
| { | |
| items: this, | |
| options: options, | |
| }, | |
| _run | |
| ); | |
| } | |
| return this; | |
| }; | |
| // Self initializing plugin for all elements having `data-fancybox` attribute | |
| // ========================================================================== | |
| $D.on('click.fb-start', '[data-fancybox]', _run); | |
| // Enable "trigger elements" | |
| // ========================= | |
| $D.on('click.fb-start', '[data-fancybox-trigger]', function (e) { | |
| $('[data-fancybox="' + $(this).attr('data-fancybox-trigger') + '"]') | |
| .eq($(this).attr('data-fancybox-index') || 0) | |
| .trigger('click.fb-start', { | |
| $trigger: $(this), | |
| }); | |
| }); | |
| // Track focus event for better accessibility styling | |
| // ================================================== | |
| (function () { | |
| var buttonStr = '.fancybox-button', | |
| focusStr = 'fancybox-focus', | |
| $pressed = null; | |
| $D.on('mousedown mouseup focus blur', buttonStr, function (e) { | |
| switch (e.type) { | |
| case 'mousedown': | |
| $pressed = $(this); | |
| break; | |
| case 'mouseup': | |
| $pressed = null; | |
| break; | |
| case 'focusin': | |
| $(buttonStr).removeClass(focusStr); | |
| if (!$(this).is($pressed) && !$(this).is('[disabled]')) { | |
| $(this).addClass(focusStr); | |
| } | |
| break; | |
| case 'focusout': | |
| $(buttonStr).removeClass(focusStr); | |
| break; | |
| } | |
| }); | |
| })(); | |
| })(window, document, jQuery); | |
| // ========================================================================== | |
| // | |
| // Media | |
| // Adds additional media type support | |
| // | |
| // ========================================================================== | |
| (function ($) { | |
| 'use strict'; | |
| // Object containing properties for each media type | |
| var defaults = { | |
| youtube: { | |
| matcher: | |
| /(youtube\.com|youtu\.be|youtube\-nocookie\.com)\/(watch\?(.*&)?v=|v\/|u\/|embed\/?)?(videoseries\?list=(.*)|[\w-]{11}|\?listType=(.*)&list=(.*))(.*)/i, | |
| params: { | |
| autoplay: 1, | |
| autohide: 1, | |
| fs: 1, | |
| rel: 0, | |
| hd: 1, | |
| wmode: 'transparent', | |
| enablejsapi: 1, | |
| html5: 1, | |
| }, | |
| paramPlace: 8, | |
| type: 'iframe', | |
| url: 'https://www.youtube-nocookie.com/embed/$4', | |
| thumb: 'https://img.youtube.com/vi/$4/hqdefault.jpg', | |
| }, | |
| vimeo: { | |
| matcher: /^.+vimeo.com\/(.*\/)?([\d]+)(.*)?/, | |
| params: { | |
| autoplay: 1, | |
| hd: 1, | |
| show_title: 1, | |
| show_byline: 1, | |
| show_portrait: 0, | |
| fullscreen: 1, | |
| }, | |
| paramPlace: 3, | |
| type: 'iframe', | |
| url: '//player.vimeo.com/video/$2', | |
| }, | |
| rutube: { | |
| matcher: /^.+rutube.ru\/.*\/(.*\/)?([\w]+)(.*)\/?/, | |
| params: { | |
| frameBorder: 0, | |
| }, | |
| paramPlace: 1, | |
| type: 'iframe', | |
| url: '//rutube.ru/play/embed/$2', | |
| thumb: 'https://rutube.ru/api/video/$2/thumbnail/?redirect=1', | |
| }, | |
| vk: { | |
| matcher: /^.+(vk\.com|vk\.ru|vkvideo\.ru).+video(?:\?z=video|)(-?\d+)_(\d+)/, | |
| params: { | |
| autoplay: 1, | |
| hd: 2, | |
| js_api: 1, | |
| frameBorder: 0, | |
| }, | |
| paramPlace: 1, | |
| type: 'iframe', | |
| url: 'https://vk.ru/video_ext.php?oid=$2&id=$3', | |
| }, | |
| instagram: { | |
| matcher: /(instagr\.am|instagram\.com)\/p\/([a-zA-Z0-9_\-]+)\/?/i, | |
| type: 'image', | |
| url: '//$1/p/$2/media/?size=l', | |
| }, | |
| // Examples: | |
| // http://maps.google.com/?ll=48.857995,2.294297&spn=0.007666,0.021136&t=m&z=16 | |
| // https://www.google.com/maps/@37.7852006,-122.4146355,14.65z | |
| // https://www.google.com/maps/@52.2111123,2.9237542,6.61z?hl=en | |
| // https://www.google.com/maps/place/Googleplex/@37.4220041,-122.0833494,17z/data=!4m5!3m4!1s0x0:0x6c296c66619367e0!8m2!3d37.4219998!4d-122.0840572 | |
| gmap_place: { | |
| matcher: | |
| /(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(((maps\/(place\/(.*)\/)?\@(.*),(\d+.?\d+?)z))|(\?ll=))(.*)?/i, | |
| type: 'iframe', | |
| url: function (rez) { | |
| return ( | |
| '//maps.google.' + | |
| rez[2] + | |
| '/?ll=' + | |
| (rez[9] | |
| ? rez[9] + | |
| '&z=' + | |
| Math.floor(rez[10]) + | |
| (rez[12] ? rez[12].replace(/^\//, '&') : '') | |
| : rez[12] + '' | |
| ).replace(/\?/, '&') + | |
| '&output=' + | |
| (rez[12] && rez[12].indexOf('layer=c') > 0 ? 'svembed' : 'embed') | |
| ); | |
| }, | |
| }, | |
| // Examples: | |
| // https://www.google.com/maps/search/Empire+State+Building/ | |
| // https://www.google.com/maps/search/?api=1&query=centurylink+field | |
| // https://www.google.com/maps/search/?api=1&query=47.5951518,-122.3316393 | |
| gmap_search: { | |
| matcher: | |
| /(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(maps\/search\/)(.*)/i, | |
| type: 'iframe', | |
| url: function (rez) { | |
| return ( | |
| '//maps.google.' + | |
| rez[2] + | |
| '/maps?q=' + | |
| rez[5].replace('query=', 'q=').replace('api=1', '') + | |
| '&output=embed' | |
| ); | |
| }, | |
| }, | |
| }; | |
| // Formats matching url to final form | |
| var format = function (url, rez, params) { | |
| if (!url) { | |
| return; | |
| } | |
| params = params || ''; | |
| if ($.type(params) === 'object') { | |
| params = $.param(params, true); | |
| } | |
| $.each(rez, function (key, value) { | |
| url = url.replace('$' + key, value || ''); | |
| }); | |
| if (params.length) { | |
| url += (url.indexOf('?') > 0 ? '&' : '?') + params; | |
| } | |
| return url; | |
| }; | |
| $(document).on('objectNeedsType.fb', function (e, instance, item) { | |
| var url = item.src || '', | |
| type = false, | |
| media, | |
| thumb, | |
| rez, | |
| params, | |
| urlParams, | |
| paramObj, | |
| provider; | |
| media = $.extend(true, {}, defaults, item.opts.media); | |
| // Look for any matching media type | |
| $.each(media, function (providerName, providerOpts) { | |
| rez = url.match(providerOpts.matcher); | |
| if (!rez) { | |
| return; | |
| } | |
| type = providerOpts.type; | |
| provider = providerName; | |
| paramObj = {}; | |
| if (providerOpts.paramPlace && rez[providerOpts.paramPlace]) { | |
| urlParams = rez[providerOpts.paramPlace]; | |
| if (urlParams[0] == '?') { | |
| urlParams = urlParams.substring(1); | |
| } | |
| urlParams = urlParams.split('&'); | |
| for (var m = 0; m < urlParams.length; ++m) { | |
| var p = urlParams[m].split('=', 2); | |
| if (p.length == 2) { | |
| paramObj[p[0]] = decodeURIComponent(p[1].replace(/\+/g, ' ')); | |
| } | |
| } | |
| } | |
| params = $.extend( | |
| true, | |
| {}, | |
| providerOpts.params, | |
| item.opts[providerName], | |
| paramObj | |
| ); | |
| url = | |
| $.type(providerOpts.url) === 'function' | |
| ? providerOpts.url.call(this, rez, params, item) | |
| : format(providerOpts.url, rez, params); | |
| thumb = | |
| $.type(providerOpts.thumb) === 'function' | |
| ? providerOpts.thumb.call(this, rez, params, item) | |
| : format(providerOpts.thumb, rez); | |
| if (providerName === 'youtube') { | |
| url = url.replace(/&t=((\d+)m)?(\d+)s/, function (match, p1, m, s) { | |
| return '&start=' + ((m ? parseInt(m, 10) * 60 : 0) + parseInt(s, 10)); | |
| }); | |
| } else if (providerName === 'vimeo') { | |
| url = url.replace('&%23', '#'); | |
| } else if (providerName === 'rutube') { | |
| url = url.replace('&%23', '#'); | |
| } | |
| return false; | |
| }); | |
| // If it is found, then change content type and update the url | |
| if (type) { | |
| if (!item.opts.thumb && !(item.opts.$thumb && item.opts.$thumb.length)) { | |
| item.opts.thumb = thumb; | |
| } | |
| if (type === 'iframe') { | |
| item.opts = $.extend(true, item.opts, { | |
| iframe: { | |
| preload: false, | |
| attr: { | |
| scrolling: 'no', | |
| }, | |
| }, | |
| }); | |
| } | |
| $.extend(item, { | |
| type: type, | |
| src: url, | |
| origSrc: item.src, | |
| contentSource: provider, | |
| contentType: | |
| type === 'image' | |
| ? 'image' | |
| : provider == 'gmap_place' || provider == 'gmap_search' | |
| ? 'map' | |
| : 'video', | |
| }); | |
| } else if (url) { | |
| item.type = item.opts.defaultType; | |
| } | |
| }); | |
| // Load YouTube/Video API on request to detect when video finished playing | |
| var VideoAPILoader = { | |
| youtube: { | |
| src: 'https://www.youtube.com/iframe_api', | |
| class: 'YT', | |
| loading: false, | |
| loaded: false, | |
| }, | |
| vimeo: { | |
| src: 'https://player.vimeo.com/api/player.js', | |
| class: 'Vimeo', | |
| loading: false, | |
| loaded: false, | |
| }, | |
| rutube: { | |
| src: 'https://cdn.jsdelivr.net/gh/evikza/rutube-player@178175923bd50cc759e2c3055c6b502e56b54e92/dist/fb.rt.js', | |
| class: 'Rutube', | |
| loading: false, | |
| loaded: false, | |
| }, | |
| vk: { | |
| src: 'https://vk.com/js/api/videoplayer.js', | |
| class: 'VK', | |
| loading: false, | |
| loaded: false, | |
| }, | |
| load: function (vendor) { | |
| var _this = this, | |
| script; | |
| if (this[vendor].loaded) { | |
| setTimeout(function () { | |
| _this.done(vendor); | |
| }); | |
| return; | |
| } | |
| if (this[vendor].loading) { | |
| return; | |
| } | |
| this[vendor].loading = true; | |
| script = document.createElement('script'); | |
| script.type = 'text/javascript'; | |
| script.src = this[vendor].src; | |
| if (vendor === 'youtube') { | |
| window.onYouTubeIframeAPIReady = function () { | |
| _this[vendor].loaded = true; | |
| _this.done(vendor); | |
| }; | |
| } else { | |
| script.onload = function () { | |
| _this[vendor].loaded = true; | |
| _this.done(vendor); | |
| }; | |
| } | |
| document.body.appendChild(script); | |
| }, | |
| done: function (vendor) { | |
| var instance, $el, player; | |
| if (vendor === 'youtube') { | |
| delete window.onYouTubeIframeAPIReady; | |
| } | |
| instance = $.fancybox.getInstance(); | |
| if (instance) { | |
| $el = instance.current.$content.find('iframe'); | |
| if (vendor === 'youtube' && YT !== undefined && YT) { | |
| player = new YT.Player($el.attr('id'), { | |
| events: { | |
| onStateChange: function (e) { | |
| if (e.data == 0) { | |
| instance.next(); | |
| } | |
| }, | |
| }, | |
| }); | |
| } else if (vendor === 'vimeo' && Vimeo !== undefined && Vimeo) { | |
| player = new Vimeo.Player($el); | |
| player.on('ended', function () { | |
| instance.next(); | |
| }); | |
| } else if (vendor === 'rutube') { | |
| player = new Rutube(); | |
| player.Player($el.attr('id'), { | |
| events: { | |
| onReady: function onPlayerReady() { | |
| setTimeout(player.play(), 700); | |
| }, | |
| onStateChange: function onPlayerStateChange(event) { | |
| if (event.playerState.ENDED) { | |
| instance.next(); | |
| } | |
| }, | |
| }, | |
| }); | |
| } else if (vendor === 'vk') { | |
| player = VK.VideoPlayer($el[0]); | |
| player.on('ended', function () { | |
| instance.next(); | |
| }); | |
| } | |
| } | |
| }, | |
| }; | |
| $(document).on({ | |
| 'afterShow.fb': function (e, instance, current) { | |
| if ( | |
| instance.group.length > 1 && | |
| (current.contentSource === 'youtube' || | |
| current.contentSource === 'vimeo' || | |
| current.contentSource === 'rutube' || | |
| current.contentSource === 'vk') | |
| ) { | |
| VideoAPILoader.load(current.contentSource); | |
| } else if (current.contentSource === 'rutube') { | |
| VideoAPILoader.load(current.contentSource); | |
| } | |
| }, | |
| }); | |
| })(jQuery); | |
| // ========================================================================== | |
| // | |
| // Guestures | |
| // Adds touch guestures, handles click and tap events | |
| // | |
| // ========================================================================== | |
| (function (window, document, $) { | |
| 'use strict'; | |
| var requestAFrame = (function () { | |
| return ( | |
| window.requestAnimationFrame || | |
| window.webkitRequestAnimationFrame || | |
| window.mozRequestAnimationFrame || | |
| window.oRequestAnimationFrame || | |
| // if all else fails, use setTimeout | |
| function (callback) { | |
| return window.setTimeout(callback, 1000 / 60); | |
| } | |
| ); | |
| })(); | |
| var cancelAFrame = (function () { | |
| return ( | |
| window.cancelAnimationFrame || | |
| window.webkitCancelAnimationFrame || | |
| window.mozCancelAnimationFrame || | |
| window.oCancelAnimationFrame || | |
| function (id) { | |
| window.clearTimeout(id); | |
| } | |
| ); | |
| })(); | |
| var getPointerXY = function (e) { | |
| var result = []; | |
| e = e.originalEvent || e || window.e; | |
| e = | |
| e.touches && e.touches.length | |
| ? e.touches | |
| : e.changedTouches && e.changedTouches.length | |
| ? e.changedTouches | |
| : [e]; | |
| for (var key in e) { | |
| if (e[key].pageX) { | |
| result.push({ | |
| x: e[key].pageX, | |
| y: e[key].pageY, | |
| }); | |
| } else if (e[key].clientX) { | |
| result.push({ | |
| x: e[key].clientX, | |
| y: e[key].clientY, | |
| }); | |
| } | |
| } | |
| return result; | |
| }; | |
| var distance = function (point2, point1, what) { | |
| if (!point1 || !point2) { | |
| return 0; | |
| } | |
| if (what === 'x') { | |
| return point2.x - point1.x; | |
| } else if (what === 'y') { | |
| return point2.y - point1.y; | |
| } | |
| return Math.sqrt( | |
| Math.pow(point2.x - point1.x, 2) + Math.pow(point2.y - point1.y, 2) | |
| ); | |
| }; | |
| var isClickable = function ($el) { | |
| if ( | |
| $el.is( | |
| 'a,area,button,[role="button"],input,label,select,summary,textarea,video,audio,iframe' | |
| ) || | |
| $.isFunction($el.get(0).onclick) || | |
| $el.data('selectable') | |
| ) { | |
| return true; | |
| } | |
| // Check for attributes like data-fancybox-next or data-fancybox-close | |
| for (var i = 0, atts = $el[0].attributes, n = atts.length; i < n; i++) { | |
| if (atts[i].nodeName.substr(0, 14) === 'data-fancybox-') { | |
| return true; | |
| } | |
| } | |
| return false; | |
| }; | |
| var hasScrollbars = function (el) { | |
| var overflowY = window.getComputedStyle(el)['overflow-y'], | |
| overflowX = window.getComputedStyle(el)['overflow-x'], | |
| vertical = | |
| (overflowY === 'scroll' || overflowY === 'auto') && | |
| el.scrollHeight > el.clientHeight, | |
| horizontal = | |
| (overflowX === 'scroll' || overflowX === 'auto') && | |
| el.scrollWidth > el.clientWidth; | |
| return vertical || horizontal; | |
| }; | |
| var isScrollable = function ($el) { | |
| var rez = false; | |
| while (true) { | |
| rez = hasScrollbars($el.get(0)); | |
| if (rez) { | |
| break; | |
| } | |
| $el = $el.parent(); | |
| if (!$el.length || $el.hasClass('fancybox-stage') || $el.is('body')) { | |
| break; | |
| } | |
| } | |
| return rez; | |
| }; | |
| var Guestures = function (instance) { | |
| var self = this; | |
| self.instance = instance; | |
| self.$bg = instance.$refs.bg; | |
| self.$stage = instance.$refs.stage; | |
| self.$container = instance.$refs.container; | |
| self.destroy(); | |
| self.$container.on( | |
| 'touchstart.fb.touch mousedown.fb.touch', | |
| $.proxy(self, 'ontouchstart') | |
| ); | |
| }; | |
| Guestures.prototype.destroy = function () { | |
| var self = this; | |
| self.$container.off('.fb.touch'); | |
| $(document).off('.fb.touch'); | |
| if (self.requestId) { | |
| cancelAFrame(self.requestId); | |
| self.requestId = null; | |
| } | |
| if (self.tapped) { | |
| clearTimeout(self.tapped); | |
| self.tapped = null; | |
| } | |
| }; | |
| Guestures.prototype.ontouchstart = function (e) { | |
| var self = this, | |
| $target = $(e.target), | |
| instance = self.instance, | |
| current = instance.current, | |
| $slide = current.$slide, | |
| $content = current.$content, | |
| isTouchDevice = e.type == 'touchstart'; | |
| // Do not respond to both (touch and mouse) events | |
| if (isTouchDevice) { | |
| self.$container.off('mousedown.fb.touch'); | |
| } | |
| // Ignore right click | |
| if (e.originalEvent && e.originalEvent.button == 2) { | |
| return; | |
| } | |
| // Ignore taping on links, buttons, input elements | |
| if ( | |
| !$slide.length || | |
| !$target.length || | |
| isClickable($target) || | |
| isClickable($target.parent()) | |
| ) { | |
| return; | |
| } | |
| // Ignore clicks on the scrollbar | |
| if ( | |
| !$target.is('img') && | |
| e.originalEvent.clientX > $target[0].clientWidth + $target.offset().left | |
| ) { | |
| return; | |
| } | |
| // Ignore clicks while zooming or closing | |
| if ( | |
| !current || | |
| instance.isAnimating || | |
| current.$slide.hasClass('fancybox-animated') | |
| ) { | |
| e.stopPropagation(); | |
| e.preventDefault(); | |
| return; | |
| } | |
| self.realPoints = self.startPoints = getPointerXY(e); | |
| if (!self.startPoints.length) { | |
| return; | |
| } | |
| // Allow other scripts to catch touch event if "touch" is set to false | |
| if (current.touch) { | |
| e.stopPropagation(); | |
| } | |
| self.startEvent = e; | |
| self.canTap = true; | |
| self.$target = $target; | |
| self.$content = $content; | |
| self.opts = current.opts.touch; | |
| self.isPanning = false; | |
| self.isSwiping = false; | |
| self.isZooming = false; | |
| self.isScrolling = false; | |
| self.canPan = instance.canPan(); | |
| self.startTime = new Date().getTime(); | |
| self.distanceX = self.distanceY = self.distance = 0; | |
| self.canvasWidth = Math.round($slide[0].clientWidth); | |
| self.canvasHeight = Math.round($slide[0].clientHeight); | |
| self.contentLastPos = null; | |
| self.contentStartPos = $.fancybox.getTranslate(self.$content) || { | |
| top: 0, | |
| left: 0, | |
| }; | |
| self.sliderStartPos = $.fancybox.getTranslate($slide); | |
| // Since position will be absolute, but we need to make it relative to the stage | |
| self.stagePos = $.fancybox.getTranslate(instance.$refs.stage); | |
| self.sliderStartPos.top -= self.stagePos.top; | |
| self.sliderStartPos.left -= self.stagePos.left; | |
| self.contentStartPos.top -= self.stagePos.top; | |
| self.contentStartPos.left -= self.stagePos.left; | |
| $(document) | |
| .off('.fb.touch') | |
| .on( | |
| isTouchDevice | |
| ? 'touchend.fb.touch touchcancel.fb.touch' | |
| : 'mouseup.fb.touch mouseleave.fb.touch', | |
| $.proxy(self, 'ontouchend') | |
| ) | |
| .on( | |
| isTouchDevice ? 'touchmove.fb.touch' : 'mousemove.fb.touch', | |
| $.proxy(self, 'ontouchmove') | |
| ); | |
| if ($.fancybox.isMobile) { | |
| document.addEventListener('scroll', self.onscroll, true); | |
| } | |
| // Skip if clicked outside the sliding area | |
| if ( | |
| !(self.opts || self.canPan) || | |
| !($target.is(self.$stage) || self.$stage.find($target).length) | |
| ) { | |
| if ($target.is('.fancybox-image')) { | |
| e.preventDefault(); | |
| } | |
| if ( | |
| !($.fancybox.isMobile && $target.parents('.fancybox-caption').length) | |
| ) { | |
| return; | |
| } | |
| } | |
| self.isScrollable = isScrollable($target) || isScrollable($target.parent()); | |
| // Check if element is scrollable and try to prevent default behavior (scrolling) | |
| if (!($.fancybox.isMobile && self.isScrollable)) { | |
| e.preventDefault(); | |
| } | |
| // One finger or mouse click - swipe or pan an image | |
| if (self.startPoints.length === 1 || current.hasError) { | |
| if (self.canPan) { | |
| $.fancybox.stop(self.$content); | |
| self.isPanning = true; | |
| } else { | |
| self.isSwiping = true; | |
| } | |
| self.$container.addClass('fancybox-is-grabbing'); | |
| } | |
| // Two fingers - zoom image | |
| if ( | |
| self.startPoints.length === 2 && | |
| current.type === 'image' && | |
| (current.isLoaded || current.$ghost) | |
| ) { | |
| self.canTap = false; | |
| self.isSwiping = false; | |
| self.isPanning = false; | |
| self.isZooming = true; | |
| $.fancybox.stop(self.$content); | |
| self.centerPointStartX = | |
| (self.startPoints[0].x + self.startPoints[1].x) * 0.5 - | |
| $(window).scrollLeft(); | |
| self.centerPointStartY = | |
| (self.startPoints[0].y + self.startPoints[1].y) * 0.5 - | |
| $(window).scrollTop(); | |
| self.percentageOfImageAtPinchPointX = | |
| (self.centerPointStartX - self.contentStartPos.left) / | |
| self.contentStartPos.width; | |
| self.percentageOfImageAtPinchPointY = | |
| (self.centerPointStartY - self.contentStartPos.top) / | |
| self.contentStartPos.height; | |
| self.startDistanceBetweenFingers = distance( | |
| self.startPoints[0], | |
| self.startPoints[1] | |
| ); | |
| } | |
| }; | |
| Guestures.prototype.onscroll = function (e) { | |
| var self = this; | |
| self.isScrolling = true; | |
| document.removeEventListener('scroll', self.onscroll, true); | |
| }; | |
| Guestures.prototype.ontouchmove = function (e) { | |
| var self = this; | |
| // Make sure user has not released over iframe or disabled element | |
| if ( | |
| e.originalEvent.buttons !== undefined && | |
| e.originalEvent.buttons === 0 | |
| ) { | |
| self.ontouchend(e); | |
| return; | |
| } | |
| if (self.isScrolling) { | |
| self.canTap = false; | |
| return; | |
| } | |
| self.newPoints = getPointerXY(e); | |
| if ( | |
| !(self.opts || self.canPan) || | |
| !self.newPoints.length || | |
| !self.newPoints.length | |
| ) { | |
| return; | |
| } | |
| if (!(self.isSwiping && self.isSwiping === true)) { | |
| e.preventDefault(); | |
| } | |
| self.distanceX = distance(self.newPoints[0], self.startPoints[0], 'x'); | |
| self.distanceY = distance(self.newPoints[0], self.startPoints[0], 'y'); | |
| self.distance = distance(self.newPoints[0], self.startPoints[0]); | |
| // Skip false ontouchmove events (Chrome) | |
| if (self.distance > 0) { | |
| if (self.isSwiping) { | |
| self.onSwipe(e); | |
| } else if (self.isPanning) { | |
| self.onPan(); | |
| } else if (self.isZooming) { | |
| self.onZoom(); | |
| } | |
| } | |
| }; | |
| Guestures.prototype.onSwipe = function (e) { | |
| var self = this, | |
| instance = self.instance, | |
| swiping = self.isSwiping, | |
| left = self.sliderStartPos.left || 0, | |
| angle; | |
| // If direction is not yet determined | |
| if (swiping === true) { | |
| // We need at least 10px distance to correctly calculate an angle | |
| if (Math.abs(self.distance) > 10) { | |
| self.canTap = false; | |
| if (instance.group.length < 2 && self.opts.vertical) { | |
| self.isSwiping = 'y'; | |
| } else if ( | |
| instance.isDragging || | |
| self.opts.vertical === false || | |
| (self.opts.vertical === 'auto' && $(window).width() > 800) | |
| ) { | |
| self.isSwiping = 'x'; | |
| } else { | |
| angle = Math.abs( | |
| (Math.atan2(self.distanceY, self.distanceX) * 180) / Math.PI | |
| ); | |
| self.isSwiping = angle > 45 && angle < 135 ? 'y' : 'x'; | |
| } | |
| if ( | |
| self.isSwiping === 'y' && | |
| $.fancybox.isMobile && | |
| self.isScrollable | |
| ) { | |
| self.isScrolling = true; | |
| return; | |
| } | |
| instance.isDragging = self.isSwiping; | |
| // Reset points to avoid jumping, because we dropped first swipes to calculate the angle | |
| self.startPoints = self.newPoints; | |
| $.each(instance.slides, function (index, slide) { | |
| var slidePos, stagePos; | |
| $.fancybox.stop(slide.$slide); | |
| slidePos = $.fancybox.getTranslate(slide.$slide); | |
| stagePos = $.fancybox.getTranslate(instance.$refs.stage); | |
| slide.$slide | |
| .css({ | |
| transform: '', | |
| opacity: '', | |
| 'transition-duration': '', | |
| }) | |
| .removeClass('fancybox-animated') | |
| .removeClass(function (index, className) { | |
| return (className.match(/(^|\s)fancybox-fx-\S+/g) || []).join( | |
| ' ' | |
| ); | |
| }); | |
| if (slide.pos === instance.current.pos) { | |
| self.sliderStartPos.top = slidePos.top - stagePos.top; | |
| self.sliderStartPos.left = slidePos.left - stagePos.left; | |
| } | |
| $.fancybox.setTranslate(slide.$slide, { | |
| top: slidePos.top - stagePos.top, | |
| left: slidePos.left - stagePos.left, | |
| }); | |
| }); | |
| // Stop slideshow | |
| if (instance.SlideShow && instance.SlideShow.isActive) { | |
| instance.SlideShow.stop(); | |
| } | |
| } | |
| return; | |
| } | |
| // Sticky edges | |
| if (swiping == 'x') { | |
| if ( | |
| self.distanceX > 0 && | |
| (self.instance.group.length < 2 || | |
| (self.instance.current.index === 0 && | |
| !self.instance.current.opts.loop)) | |
| ) { | |
| left = left + Math.pow(self.distanceX, 0.8); | |
| } else if ( | |
| self.distanceX < 0 && | |
| (self.instance.group.length < 2 || | |
| (self.instance.current.index === self.instance.group.length - 1 && | |
| !self.instance.current.opts.loop)) | |
| ) { | |
| left = left - Math.pow(-self.distanceX, 0.8); | |
| } else { | |
| left = left + self.distanceX; | |
| } | |
| } | |
| self.sliderLastPos = { | |
| top: swiping == 'x' ? 0 : self.sliderStartPos.top + self.distanceY, | |
| left: left, | |
| }; | |
| if (self.requestId) { | |
| cancelAFrame(self.requestId); | |
| self.requestId = null; | |
| } | |
| self.requestId = requestAFrame(function () { | |
| if (self.sliderLastPos) { | |
| $.each(self.instance.slides, function (index, slide) { | |
| var pos = slide.pos - self.instance.currPos; | |
| $.fancybox.setTranslate(slide.$slide, { | |
| top: self.sliderLastPos.top, | |
| left: | |
| self.sliderLastPos.left + | |
| pos * self.canvasWidth + | |
| pos * slide.opts.gutter, | |
| }); | |
| }); | |
| self.$container.addClass('fancybox-is-sliding'); | |
| } | |
| }); | |
| }; | |
| Guestures.prototype.onPan = function () { | |
| var self = this; | |
| // Prevent accidental movement (sometimes, when tapping casually, finger can move a bit) | |
| if ( | |
| distance(self.newPoints[0], self.realPoints[0]) < | |
| ($.fancybox.isMobile ? 10 : 5) | |
| ) { | |
| self.startPoints = self.newPoints; | |
| return; | |
| } | |
| self.canTap = false; | |
| self.contentLastPos = self.limitMovement(); | |
| if (self.requestId) { | |
| cancelAFrame(self.requestId); | |
| } | |
| self.requestId = requestAFrame(function () { | |
| $.fancybox.setTranslate(self.$content, self.contentLastPos); | |
| }); | |
| }; | |
| // Make panning sticky to the edges | |
| Guestures.prototype.limitMovement = function () { | |
| var self = this; | |
| var canvasWidth = self.canvasWidth; | |
| var canvasHeight = self.canvasHeight; | |
| var distanceX = self.distanceX; | |
| var distanceY = self.distanceY; | |
| var contentStartPos = self.contentStartPos; | |
| var currentOffsetX = contentStartPos.left; | |
| var currentOffsetY = contentStartPos.top; | |
| var currentWidth = contentStartPos.width; | |
| var currentHeight = contentStartPos.height; | |
| var minTranslateX, | |
| minTranslateY, | |
| maxTranslateX, | |
| maxTranslateY, | |
| newOffsetX, | |
| newOffsetY; | |
| if (currentWidth > canvasWidth) { | |
| newOffsetX = currentOffsetX + distanceX; | |
| } else { | |
| newOffsetX = currentOffsetX; | |
| } | |
| newOffsetY = currentOffsetY + distanceY; | |
| // Slow down proportionally to traveled distance | |
| minTranslateX = Math.max(0, canvasWidth * 0.5 - currentWidth * 0.5); | |
| minTranslateY = Math.max(0, canvasHeight * 0.5 - currentHeight * 0.5); | |
| maxTranslateX = Math.min( | |
| canvasWidth - currentWidth, | |
| canvasWidth * 0.5 - currentWidth * 0.5 | |
| ); | |
| maxTranslateY = Math.min( | |
| canvasHeight - currentHeight, | |
| canvasHeight * 0.5 - currentHeight * 0.5 | |
| ); | |
| // -> | |
| if (distanceX > 0 && newOffsetX > minTranslateX) { | |
| newOffsetX = | |
| minTranslateX - | |
| 1 + | |
| Math.pow(-minTranslateX + currentOffsetX + distanceX, 0.8) || 0; | |
| } | |
| // <- | |
| if (distanceX < 0 && newOffsetX < maxTranslateX) { | |
| newOffsetX = | |
| maxTranslateX + | |
| 1 - | |
| Math.pow(maxTranslateX - currentOffsetX - distanceX, 0.8) || 0; | |
| } | |
| // \/ | |
| if (distanceY > 0 && newOffsetY > minTranslateY) { | |
| newOffsetY = | |
| minTranslateY - | |
| 1 + | |
| Math.pow(-minTranslateY + currentOffsetY + distanceY, 0.8) || 0; | |
| } | |
| // /\ | |
| if (distanceY < 0 && newOffsetY < maxTranslateY) { | |
| newOffsetY = | |
| maxTranslateY + | |
| 1 - | |
| Math.pow(maxTranslateY - currentOffsetY - distanceY, 0.8) || 0; | |
| } | |
| return { | |
| top: newOffsetY, | |
| left: newOffsetX, | |
| }; | |
| }; | |
| Guestures.prototype.limitPosition = function ( | |
| newOffsetX, | |
| newOffsetY, | |
| newWidth, | |
| newHeight | |
| ) { | |
| var self = this; | |
| var canvasWidth = self.canvasWidth; | |
| var canvasHeight = self.canvasHeight; | |
| if (newWidth > canvasWidth) { | |
| newOffsetX = newOffsetX > 0 ? 0 : newOffsetX; | |
| newOffsetX = | |
| newOffsetX < canvasWidth - newWidth | |
| ? canvasWidth - newWidth | |
| : newOffsetX; | |
| } else { | |
| // Center horizontally | |
| newOffsetX = Math.max(0, canvasWidth / 2 - newWidth / 2); | |
| } | |
| if (newHeight > canvasHeight) { | |
| newOffsetY = newOffsetY > 0 ? 0 : newOffsetY; | |
| newOffsetY = | |
| newOffsetY < canvasHeight - newHeight | |
| ? canvasHeight - newHeight | |
| : newOffsetY; | |
| } else { | |
| // Center vertically | |
| newOffsetY = Math.max(0, canvasHeight / 2 - newHeight / 2); | |
| } | |
| return { | |
| top: newOffsetY, | |
| left: newOffsetX, | |
| }; | |
| }; | |
| Guestures.prototype.onZoom = function () { | |
| var self = this; | |
| // Calculate current distance between points to get pinch ratio and new width and height | |
| var contentStartPos = self.contentStartPos; | |
| var currentWidth = contentStartPos.width; | |
| var currentHeight = contentStartPos.height; | |
| var currentOffsetX = contentStartPos.left; | |
| var currentOffsetY = contentStartPos.top; | |
| var endDistanceBetweenFingers = distance( | |
| self.newPoints[0], | |
| self.newPoints[1] | |
| ); | |
| var pinchRatio = | |
| endDistanceBetweenFingers / self.startDistanceBetweenFingers; | |
| var newWidth = Math.floor(currentWidth * pinchRatio); | |
| var newHeight = Math.floor(currentHeight * pinchRatio); | |
| // This is the translation due to pinch-zooming | |
| var translateFromZoomingX = | |
| (currentWidth - newWidth) * self.percentageOfImageAtPinchPointX; | |
| var translateFromZoomingY = | |
| (currentHeight - newHeight) * self.percentageOfImageAtPinchPointY; | |
| // Point between the two touches | |
| var centerPointEndX = | |
| (self.newPoints[0].x + self.newPoints[1].x) / 2 - $(window).scrollLeft(); | |
| var centerPointEndY = | |
| (self.newPoints[0].y + self.newPoints[1].y) / 2 - $(window).scrollTop(); | |
| // And this is the translation due to translation of the centerpoint | |
| // between the two fingers | |
| var translateFromTranslatingX = centerPointEndX - self.centerPointStartX; | |
| var translateFromTranslatingY = centerPointEndY - self.centerPointStartY; | |
| // The new offset is the old/current one plus the total translation | |
| var newOffsetX = | |
| currentOffsetX + (translateFromZoomingX + translateFromTranslatingX); | |
| var newOffsetY = | |
| currentOffsetY + (translateFromZoomingY + translateFromTranslatingY); | |
| var newPos = { | |
| top: newOffsetY, | |
| left: newOffsetX, | |
| scaleX: pinchRatio, | |
| scaleY: pinchRatio, | |
| }; | |
| self.canTap = false; | |
| self.newWidth = newWidth; | |
| self.newHeight = newHeight; | |
| self.contentLastPos = newPos; | |
| if (self.requestId) { | |
| cancelAFrame(self.requestId); | |
| } | |
| self.requestId = requestAFrame(function () { | |
| $.fancybox.setTranslate(self.$content, self.contentLastPos); | |
| }); | |
| }; | |
| Guestures.prototype.ontouchend = function (e) { | |
| var self = this; | |
| var swiping = self.isSwiping; | |
| var panning = self.isPanning; | |
| var zooming = self.isZooming; | |
| var scrolling = self.isScrolling; | |
| self.endPoints = getPointerXY(e); | |
| self.dMs = Math.max(new Date().getTime() - self.startTime, 1); | |
| self.$container.removeClass('fancybox-is-grabbing'); | |
| $(document).off('.fb.touch'); | |
| document.removeEventListener('scroll', self.onscroll, true); | |
| if (self.requestId) { | |
| cancelAFrame(self.requestId); | |
| self.requestId = null; | |
| } | |
| self.isSwiping = false; | |
| self.isPanning = false; | |
| self.isZooming = false; | |
| self.isScrolling = false; | |
| self.instance.isDragging = false; | |
| if (self.canTap) { | |
| return self.onTap(e); | |
| } | |
| self.speed = 100; | |
| // Speed in px/ms | |
| self.velocityX = (self.distanceX / self.dMs) * 0.5; | |
| self.velocityY = (self.distanceY / self.dMs) * 0.5; | |
| if (panning) { | |
| self.endPanning(); | |
| } else if (zooming) { | |
| self.endZooming(); | |
| } else { | |
| self.endSwiping(swiping, scrolling); | |
| } | |
| return; | |
| }; | |
| Guestures.prototype.endSwiping = function (swiping, scrolling) { | |
| var self = this, | |
| ret = false, | |
| len = self.instance.group.length, | |
| distanceX = Math.abs(self.distanceX), | |
| canAdvance = | |
| swiping == 'x' && | |
| len > 1 && | |
| ((self.dMs > 130 && distanceX > 10) || distanceX > 50), | |
| speedX = 300; | |
| self.sliderLastPos = null; | |
| // Close if swiped vertically / navigate if horizontally | |
| if (swiping == 'y' && !scrolling && Math.abs(self.distanceY) > 50) { | |
| // Continue vertical movement | |
| $.fancybox.animate( | |
| self.instance.current.$slide, | |
| { | |
| top: self.sliderStartPos.top + self.distanceY + self.velocityY * 150, | |
| opacity: 0, | |
| }, | |
| 200 | |
| ); | |
| ret = self.instance.close(true, 250); | |
| } else if (canAdvance && self.distanceX > 0) { | |
| ret = self.instance.previous(speedX); | |
| } else if (canAdvance && self.distanceX < 0) { | |
| ret = self.instance.next(speedX); | |
| } | |
| if (ret === false && (swiping == 'x' || swiping == 'y')) { | |
| self.instance.centerSlide(200); | |
| } | |
| self.$container.removeClass('fancybox-is-sliding'); | |
| }; | |
| // Limit panning from edges | |
| // ======================== | |
| Guestures.prototype.endPanning = function () { | |
| var self = this, | |
| newOffsetX, | |
| newOffsetY, | |
| newPos; | |
| if (!self.contentLastPos) { | |
| return; | |
| } | |
| if (self.opts.momentum === false || self.dMs > 350) { | |
| newOffsetX = self.contentLastPos.left; | |
| newOffsetY = self.contentLastPos.top; | |
| } else { | |
| // Continue movement | |
| newOffsetX = self.contentLastPos.left + self.velocityX * 500; | |
| newOffsetY = self.contentLastPos.top + self.velocityY * 500; | |
| } | |
| newPos = self.limitPosition( | |
| newOffsetX, | |
| newOffsetY, | |
| self.contentStartPos.width, | |
| self.contentStartPos.height | |
| ); | |
| newPos.width = self.contentStartPos.width; | |
| newPos.height = self.contentStartPos.height; | |
| $.fancybox.animate(self.$content, newPos, 366); | |
| }; | |
| Guestures.prototype.endZooming = function () { | |
| var self = this; | |
| var current = self.instance.current; | |
| var newOffsetX, newOffsetY, newPos, reset; | |
| var newWidth = self.newWidth; | |
| var newHeight = self.newHeight; | |
| if (!self.contentLastPos) { | |
| return; | |
| } | |
| newOffsetX = self.contentLastPos.left; | |
| newOffsetY = self.contentLastPos.top; | |
| reset = { | |
| top: newOffsetY, | |
| left: newOffsetX, | |
| width: newWidth, | |
| height: newHeight, | |
| scaleX: 1, | |
| scaleY: 1, | |
| }; | |
| // Reset scalex/scaleY values; this helps for perfomance and does not break animation | |
| $.fancybox.setTranslate(self.$content, reset); | |
| if (newWidth < self.canvasWidth && newHeight < self.canvasHeight) { | |
| self.instance.scaleToFit(150); | |
| } else if (newWidth > current.width || newHeight > current.height) { | |
| self.instance.scaleToActual( | |
| self.centerPointStartX, | |
| self.centerPointStartY, | |
| 150 | |
| ); | |
| } else { | |
| newPos = self.limitPosition(newOffsetX, newOffsetY, newWidth, newHeight); | |
| $.fancybox.animate(self.$content, newPos, 150); | |
| } | |
| }; | |
| Guestures.prototype.onTap = function (e) { | |
| var self = this; | |
| var $target = $(e.target); | |
| var instance = self.instance; | |
| var current = instance.current; | |
| var endPoints = (e && getPointerXY(e)) || self.startPoints; | |
| var tapX = endPoints[0] | |
| ? endPoints[0].x - $(window).scrollLeft() - self.stagePos.left | |
| : 0; | |
| var tapY = endPoints[0] | |
| ? endPoints[0].y - $(window).scrollTop() - self.stagePos.top | |
| : 0; | |
| var where; | |
| var process = function (prefix) { | |
| var action = current.opts[prefix]; | |
| if ($.isFunction(action)) { | |
| action = action.apply(instance, [current, e]); | |
| } | |
| if (!action) { | |
| return; | |
| } | |
| switch (action) { | |
| case 'close': | |
| instance.close(self.startEvent); | |
| break; | |
| case 'toggleControls': | |
| instance.toggleControls(); | |
| break; | |
| case 'next': | |
| instance.next(); | |
| break; | |
| case 'nextOrClose': | |
| if (instance.group.length > 1) { | |
| instance.next(); | |
| } else { | |
| instance.close(self.startEvent); | |
| } | |
| break; | |
| case 'zoom': | |
| if (current.type == 'image' && (current.isLoaded || current.$ghost)) { | |
| if (instance.canPan()) { | |
| instance.scaleToFit(); | |
| } else if (instance.isScaledDown()) { | |
| instance.scaleToActual(tapX, tapY); | |
| } else if (instance.group.length < 2) { | |
| instance.close(self.startEvent); | |
| } | |
| } | |
| break; | |
| } | |
| }; | |
| // Ignore right click | |
| if (e.originalEvent && e.originalEvent.button == 2) { | |
| return; | |
| } | |
| // Skip if clicked on the scrollbar | |
| if ( | |
| !$target.is('img') && | |
| tapX > $target[0].clientWidth + $target.offset().left | |
| ) { | |
| return; | |
| } | |
| // Check where is clicked | |
| if ( | |
| $target.is( | |
| '.fancybox-bg,.fancybox-inner,.fancybox-outer,.fancybox-container' | |
| ) | |
| ) { | |
| where = 'Outside'; | |
| } else if ($target.is('.fancybox-slide')) { | |
| where = 'Slide'; | |
| } else if ( | |
| instance.current.$content && | |
| instance.current.$content.find($target).addBack().filter($target).length | |
| ) { | |
| where = 'Content'; | |
| } else { | |
| return; | |
| } | |
| // Check if this is a double tap | |
| if (self.tapped) { | |
| // Stop previously created single tap | |
| clearTimeout(self.tapped); | |
| self.tapped = null; | |
| // Skip if distance between taps is too big | |
| if (Math.abs(tapX - self.tapX) > 50 || Math.abs(tapY - self.tapY) > 50) { | |
| return this; | |
| } | |
| // OK, now we assume that this is a double-tap | |
| process('dblclick' + where); | |
| } else { | |
| // Single tap will be processed if user has not clicked second time within 300ms | |
| // or there is no need to wait for double-tap | |
| self.tapX = tapX; | |
| self.tapY = tapY; | |
| if ( | |
| current.opts['dblclick' + where] && | |
| current.opts['dblclick' + where] !== current.opts['click' + where] | |
| ) { | |
| self.tapped = setTimeout(function () { | |
| self.tapped = null; | |
| if (!instance.isAnimating) { | |
| process('click' + where); | |
| } | |
| }, 500); | |
| } else { | |
| process('click' + where); | |
| } | |
| } | |
| return this; | |
| }; | |
| $(document) | |
| .on('onActivate.fb', function (e, instance) { | |
| if (instance && !instance.Guestures) { | |
| instance.Guestures = new Guestures(instance); | |
| } | |
| }) | |
| .on('beforeClose.fb', function (e, instance) { | |
| if (instance && instance.Guestures) { | |
| instance.Guestures.destroy(); | |
| } | |
| }); | |
| })(window, document, jQuery); | |
| // ========================================================================== | |
| // | |
| // SlideShow | |
| // Enables slideshow functionality | |
| // | |
| // Example of usage: | |
| // $.fancybox.getInstance().SlideShow.start() | |
| // | |
| // ========================================================================== | |
| (function (document, $) { | |
| 'use strict'; | |
| $.extend(true, $.fancybox.defaults, { | |
| btnTpl: { | |
| slideShow: | |
| '<button data-fancybox-play class="fancybox-button fancybox-button--play" title="{{PLAY_START}}">' + | |
| '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M6.5 5.4v13.2l11-6.6z"/></svg>' + | |
| '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M8.33 5.75h2.2v12.5h-2.2V5.75zm5.15 0h2.2v12.5h-2.2V5.75z"/></svg>' + | |
| '</button>', | |
| }, | |
| slideShow: { | |
| autoStart: false, | |
| speed: 3000, | |
| progress: true, | |
| }, | |
| }); | |
| var SlideShow = function (instance) { | |
| this.instance = instance; | |
| this.init(); | |
| }; | |
| $.extend(SlideShow.prototype, { | |
| timer: null, | |
| isActive: false, | |
| $button: null, | |
| init: function () { | |
| var self = this, | |
| instance = self.instance, | |
| opts = instance.group[instance.currIndex].opts.slideShow; | |
| self.$button = instance.$refs.toolbar | |
| .find('[data-fancybox-play]') | |
| .on('click', function () { | |
| self.toggle(); | |
| }); | |
| if (instance.group.length < 2 || !opts) { | |
| self.$button.hide(); | |
| } else if (opts.progress) { | |
| self.$progress = $('<div class="fancybox-progress"></div>').appendTo( | |
| instance.$refs.inner | |
| ); | |
| } | |
| }, | |
| set: function (force) { | |
| var self = this, | |
| instance = self.instance, | |
| current = instance.current; | |
| // Check if reached last element | |
| if ( | |
| current && | |
| (force === true || | |
| current.opts.loop || | |
| instance.currIndex < instance.group.length - 1) | |
| ) { | |
| if (self.isActive && current.contentType !== 'video') { | |
| if (self.$progress) { | |
| $.fancybox.animate( | |
| self.$progress.show(), | |
| { | |
| scaleX: 1, | |
| }, | |
| current.opts.slideShow.speed | |
| ); | |
| } | |
| self.timer = setTimeout(function () { | |
| if ( | |
| !instance.current.opts.loop && | |
| instance.current.index == instance.group.length - 1 | |
| ) { | |
| instance.jumpTo(0); | |
| } else { | |
| instance.next(); | |
| } | |
| }, current.opts.slideShow.speed); | |
| } | |
| } else { | |
| self.stop(); | |
| instance.idleSecondsCounter = 0; | |
| instance.showControls(); | |
| } | |
| }, | |
| clear: function () { | |
| var self = this; | |
| clearTimeout(self.timer); | |
| self.timer = null; | |
| if (self.$progress) { | |
| self.$progress.removeAttr('style').hide(); | |
| } | |
| }, | |
| start: function () { | |
| var self = this, | |
| current = self.instance.current; | |
| if (current) { | |
| self.$button | |
| .attr( | |
| 'title', | |
| (current.opts.i18n[current.opts.lang] || current.opts.i18n.en) | |
| .PLAY_STOP | |
| ) | |
| .removeClass('fancybox-button--play') | |
| .addClass('fancybox-button--pause'); | |
| self.isActive = true; | |
| if (current.isComplete) { | |
| self.set(true); | |
| } | |
| self.instance.trigger('onSlideShowChange', true); | |
| } | |
| }, | |
| stop: function () { | |
| var self = this, | |
| current = self.instance.current; | |
| self.clear(); | |
| self.$button | |
| .attr( | |
| 'title', | |
| (current.opts.i18n[current.opts.lang] || current.opts.i18n.en) | |
| .PLAY_START | |
| ) | |
| .removeClass('fancybox-button--pause') | |
| .addClass('fancybox-button--play'); | |
| self.isActive = false; | |
| self.instance.trigger('onSlideShowChange', false); | |
| if (self.$progress) { | |
| self.$progress.removeAttr('style').hide(); | |
| } | |
| }, | |
| toggle: function () { | |
| var self = this; | |
| if (self.isActive) { | |
| self.stop(); | |
| } else { | |
| self.start(); | |
| } | |
| }, | |
| }); | |
| $(document).on({ | |
| 'onInit.fb': function (e, instance) { | |
| if (instance && !instance.SlideShow) { | |
| instance.SlideShow = new SlideShow(instance); | |
| } | |
| }, | |
| 'beforeShow.fb': function (e, instance, current, firstRun) { | |
| var SlideShow = instance && instance.SlideShow; | |
| if (firstRun) { | |
| if (SlideShow && current.opts.slideShow.autoStart) { | |
| SlideShow.start(); | |
| } | |
| } else if (SlideShow && SlideShow.isActive) { | |
| SlideShow.clear(); | |
| } | |
| }, | |
| 'afterShow.fb': function (e, instance, current) { | |
| var SlideShow = instance && instance.SlideShow; | |
| if (SlideShow && SlideShow.isActive) { | |
| SlideShow.set(); | |
| } | |
| }, | |
| 'afterKeydown.fb': function (e, instance, current, keypress, keycode) { | |
| var SlideShow = instance && instance.SlideShow; | |
| // "P" or Spacebar | |
| if ( | |
| SlideShow && | |
| current.opts.slideShow && | |
| (keycode === 80 || keycode === 32) && | |
| !$(document.activeElement).is('button,a,input') | |
| ) { | |
| keypress.preventDefault(); | |
| SlideShow.toggle(); | |
| } | |
| }, | |
| 'beforeClose.fb onDeactivate.fb': function (e, instance) { | |
| var SlideShow = instance && instance.SlideShow; | |
| if (SlideShow) { | |
| SlideShow.stop(); | |
| } | |
| }, | |
| }); | |
| // Page Visibility API to pause slideshow when window is not active | |
| $(document).on('visibilitychange', function () { | |
| var instance = $.fancybox.getInstance(), | |
| SlideShow = instance && instance.SlideShow; | |
| if (SlideShow && SlideShow.isActive) { | |
| if (document.hidden) { | |
| SlideShow.clear(); | |
| } else { | |
| SlideShow.set(); | |
| } | |
| } | |
| }); | |
| })(document, jQuery); | |
| // ========================================================================== | |
| // | |
| // FullScreen | |
| // Adds fullscreen functionality | |
| // | |
| // ========================================================================== | |
| (function (document, $) { | |
| 'use strict'; | |
| // Collection of methods supported by user browser | |
| var fn = (function () { | |
| var fnMap = [ | |
| [ | |
| 'requestFullscreen', | |
| 'exitFullscreen', | |
| 'fullscreenElement', | |
| 'fullscreenEnabled', | |
| 'fullscreenchange', | |
| 'fullscreenerror', | |
| ], | |
| // new WebKit | |
| [ | |
| 'webkitRequestFullscreen', | |
| 'webkitExitFullscreen', | |
| 'webkitFullscreenElement', | |
| 'webkitFullscreenEnabled', | |
| 'webkitfullscreenchange', | |
| 'webkitfullscreenerror', | |
| ], | |
| // old WebKit (Safari 5.1) | |
| [ | |
| 'webkitRequestFullScreen', | |
| 'webkitCancelFullScreen', | |
| 'webkitCurrentFullScreenElement', | |
| 'webkitCancelFullScreen', | |
| 'webkitfullscreenchange', | |
| 'webkitfullscreenerror', | |
| ], | |
| [ | |
| 'mozRequestFullScreen', | |
| 'mozCancelFullScreen', | |
| 'mozFullScreenElement', | |
| 'mozFullScreenEnabled', | |
| 'mozfullscreenchange', | |
| 'mozfullscreenerror', | |
| ], | |
| [ | |
| 'msRequestFullscreen', | |
| 'msExitFullscreen', | |
| 'msFullscreenElement', | |
| 'msFullscreenEnabled', | |
| 'MSFullscreenChange', | |
| 'MSFullscreenError', | |
| ], | |
| ]; | |
| var ret = {}; | |
| for (var i = 0; i < fnMap.length; i++) { | |
| var val = fnMap[i]; | |
| if (val && val[1] in document) { | |
| for (var j = 0; j < val.length; j++) { | |
| ret[fnMap[0][j]] = val[j]; | |
| } | |
| return ret; | |
| } | |
| } | |
| return false; | |
| })(); | |
| if (fn) { | |
| var FullScreen = { | |
| request: function (elem) { | |
| elem = elem || document.documentElement; | |
| elem[fn.requestFullscreen](elem.ALLOW_KEYBOARD_INPUT); | |
| }, | |
| exit: function () { | |
| document[fn.exitFullscreen](); | |
| }, | |
| toggle: function (elem) { | |
| elem = elem || document.documentElement; | |
| if (this.isFullscreen()) { | |
| this.exit(); | |
| } else { | |
| this.request(elem); | |
| } | |
| }, | |
| isFullscreen: function () { | |
| return Boolean(document[fn.fullscreenElement]); | |
| }, | |
| enabled: function () { | |
| return Boolean(document[fn.fullscreenEnabled]); | |
| }, | |
| }; | |
| $.extend(true, $.fancybox.defaults, { | |
| btnTpl: { | |
| fullScreen: | |
| '<button data-fancybox-fullscreen class="fancybox-button fancybox-button--fsenter" title="{{FULL_SCREEN}}">' + | |
| '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/></svg>' + | |
| '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M5 16h3v3h2v-5H5zm3-8H5v2h5V5H8zm6 11h2v-3h3v-2h-5zm2-11V5h-2v5h5V8z"/></svg>' + | |
| '</button>', | |
| }, | |
| fullScreen: { | |
| autoStart: false, | |
| }, | |
| }); | |
| $(document).on(fn.fullscreenchange, function () { | |
| var isFullscreen = FullScreen.isFullscreen(), | |
| instance = $.fancybox.getInstance(); | |
| if (instance) { | |
| // If image is zooming, then force to stop and reposition properly | |
| if ( | |
| instance.current && | |
| instance.current.type === 'image' && | |
| instance.isAnimating | |
| ) { | |
| instance.isAnimating = false; | |
| instance.update(true, true, 0); | |
| if (!instance.isComplete) { | |
| instance.complete(); | |
| } | |
| } | |
| instance.trigger('onFullscreenChange', isFullscreen); | |
| instance.$refs.container.toggleClass( | |
| 'fancybox-is-fullscreen', | |
| isFullscreen | |
| ); | |
| instance.$refs.toolbar | |
| .find('[data-fancybox-fullscreen]') | |
| .toggleClass('fancybox-button--fsenter', !isFullscreen) | |
| .toggleClass('fancybox-button--fsexit', isFullscreen); | |
| } | |
| }); | |
| } | |
| $(document).on({ | |
| 'onInit.fb': function (e, instance) { | |
| var $container; | |
| if (!fn) { | |
| instance.$refs.toolbar.find('[data-fancybox-fullscreen]').remove(); | |
| return; | |
| } | |
| if (instance && instance.group[instance.currIndex].opts.fullScreen) { | |
| $container = instance.$refs.container; | |
| $container.on( | |
| 'click.fb-fullscreen', | |
| '[data-fancybox-fullscreen]', | |
| function (e) { | |
| e.stopPropagation(); | |
| e.preventDefault(); | |
| FullScreen.toggle(); | |
| } | |
| ); | |
| if ( | |
| instance.opts.fullScreen && | |
| instance.opts.fullScreen.autoStart === true | |
| ) { | |
| FullScreen.request(); | |
| } | |
| // Expose API | |
| instance.FullScreen = FullScreen; | |
| } else if (instance) { | |
| instance.$refs.toolbar.find('[data-fancybox-fullscreen]').hide(); | |
| } | |
| }, | |
| 'afterKeydown.fb': function (e, instance, current, keypress, keycode) { | |
| // "F" | |
| if (instance && instance.FullScreen && keycode === 70) { | |
| keypress.preventDefault(); | |
| instance.FullScreen.toggle(); | |
| } | |
| }, | |
| 'beforeClose.fb': function (e, instance) { | |
| if ( | |
| instance && | |
| instance.FullScreen && | |
| instance.$refs.container.hasClass('fancybox-is-fullscreen') | |
| ) { | |
| FullScreen.exit(); | |
| } | |
| }, | |
| }); | |
| })(document, jQuery); | |
| // ========================================================================== | |
| // | |
| // Thumbs | |
| // Displays thumbnails in a grid | |
| // | |
| // ========================================================================== | |
| (function (document, $) { | |
| 'use strict'; | |
| var CLASS = 'fancybox-thumbs', | |
| CLASS_ACTIVE = CLASS + '-active'; | |
| // Make sure there are default values | |
| $.fancybox.defaults = $.extend( | |
| true, | |
| { | |
| btnTpl: { | |
| thumbs: | |
| '<button data-fancybox-thumbs class="fancybox-button fancybox-button--thumbs" title="{{THUMBS}}">' + | |
| '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M14.59 14.59h3.76v3.76h-3.76v-3.76zm-4.47 0h3.76v3.76h-3.76v-3.76zm-4.47 0h3.76v3.76H5.65v-3.76zm8.94-4.47h3.76v3.76h-3.76v-3.76zm-4.47 0h3.76v3.76h-3.76v-3.76zm-4.47 0h3.76v3.76H5.65v-3.76zm8.94-4.47h3.76v3.76h-3.76V5.65zm-4.47 0h3.76v3.76h-3.76V5.65zm-4.47 0h3.76v3.76H5.65V5.65z"/></svg>' + | |
| '</button>', | |
| }, | |
| thumbs: { | |
| autoStart: false, // Display thumbnails on opening | |
| hideOnClose: true, // Hide thumbnail grid when closing animation starts | |
| parentEl: '.fancybox-container', // Container is injected into this element | |
| axis: 'y', // Vertical (y) or horizontal (x) scrolling | |
| }, | |
| }, | |
| $.fancybox.defaults | |
| ); | |
| var FancyThumbs = function (instance) { | |
| this.init(instance); | |
| }; | |
| $.extend(FancyThumbs.prototype, { | |
| $button: null, | |
| $grid: null, | |
| $list: null, | |
| isVisible: false, | |
| isActive: false, | |
| init: function (instance) { | |
| var self = this, | |
| group = instance.group, | |
| enabled = 0; | |
| self.instance = instance; | |
| self.opts = group[instance.currIndex].opts.thumbs; | |
| instance.Thumbs = self; | |
| self.$button = instance.$refs.toolbar.find('[data-fancybox-thumbs]'); | |
| // Enable thumbs if at least two group items have thumbnails | |
| for (var i = 0, len = group.length; i < len; i++) { | |
| if (group[i].thumb) { | |
| enabled++; | |
| } | |
| if (enabled > 1) { | |
| break; | |
| } | |
| } | |
| if (enabled > 1 && !!self.opts) { | |
| self.$button.removeAttr('style').on('click', function () { | |
| self.toggle(); | |
| }); | |
| self.isActive = true; | |
| } else { | |
| self.$button.hide(); | |
| } | |
| }, | |
| create: function () { | |
| var self = this, | |
| instance = self.instance, | |
| parentEl = self.opts.parentEl, | |
| list = [], | |
| src; | |
| if (!self.$grid) { | |
| // Create main element | |
| self.$grid = $( | |
| '<div class="' + | |
| CLASS + | |
| ' ' + | |
| CLASS + | |
| '-' + | |
| self.opts.axis + | |
| '"></div>' | |
| ).appendTo( | |
| instance.$refs.container.find(parentEl).addBack().filter(parentEl) | |
| ); | |
| // Add "click" event that performs gallery navigation | |
| self.$grid.on('click', 'a', function () { | |
| instance.jumpTo($(this).attr('data-index')); | |
| }); | |
| } | |
| // Build the list | |
| if (!self.$list) { | |
| self.$list = $('<div class="' + CLASS + '__list">').appendTo( | |
| self.$grid | |
| ); | |
| } | |
| $.each(instance.group, function (i, item) { | |
| src = item.thumb; | |
| if (!src && item.type === 'image') { | |
| src = item.src; | |
| } | |
| list.push( | |
| '<a href="javascript:;" tabindex="0" data-index="' + | |
| i + | |
| '"' + | |
| (src && src.length | |
| ? ' style="background-image:url(' + src + ')"' | |
| : 'class="fancybox-thumbs-missing"') + | |
| '></a>' | |
| ); | |
| }); | |
| self.$list[0].innerHTML = list.join(''); | |
| if (self.opts.axis === 'x') { | |
| // Set fixed width for list element to enable horizontal scrolling | |
| self.$list.width( | |
| parseInt(self.$grid.css('padding-right'), 10) + | |
| instance.group.length * self.$list.children().eq(0).outerWidth(true) | |
| ); | |
| } | |
| }, | |
| focus: function (duration) { | |
| var self = this, | |
| $list = self.$list, | |
| $grid = self.$grid, | |
| thumb, | |
| thumbPos; | |
| if (!self.instance.current) { | |
| return; | |
| } | |
| thumb = $list | |
| .children() | |
| .removeClass(CLASS_ACTIVE) | |
| .filter('[data-index="' + self.instance.current.index + '"]') | |
| .addClass(CLASS_ACTIVE); | |
| thumbPos = thumb.position(); | |
| // Check if need to scroll to make current thumb visible | |
| if ( | |
| self.opts.axis === 'y' && | |
| (thumbPos.top < 0 || | |
| thumbPos.top > $list.height() - thumb.outerHeight()) | |
| ) { | |
| $list.stop().animate( | |
| { | |
| scrollTop: $list.scrollTop() + thumbPos.top, | |
| }, | |
| duration | |
| ); | |
| } else if ( | |
| self.opts.axis === 'x' && | |
| (thumbPos.left < $grid.scrollLeft() || | |
| thumbPos.left > | |
| $grid.scrollLeft() + ($grid.width() - thumb.outerWidth())) | |
| ) { | |
| $list.parent().stop().animate( | |
| { | |
| scrollLeft: thumbPos.left, | |
| }, | |
| duration | |
| ); | |
| } | |
| }, | |
| update: function () { | |
| var that = this; | |
| that.instance.$refs.container.toggleClass( | |
| 'fancybox-show-thumbs', | |
| this.isVisible | |
| ); | |
| if (that.isVisible) { | |
| if (!that.$grid) { | |
| that.create(); | |
| } | |
| that.instance.trigger('onThumbsShow'); | |
| that.focus(0); | |
| } else if (that.$grid) { | |
| that.instance.trigger('onThumbsHide'); | |
| } | |
| // Update content position | |
| that.instance.update(); | |
| }, | |
| hide: function () { | |
| this.isVisible = false; | |
| this.update(); | |
| }, | |
| show: function () { | |
| this.isVisible = true; | |
| this.update(); | |
| }, | |
| toggle: function () { | |
| this.isVisible = !this.isVisible; | |
| this.update(); | |
| }, | |
| }); | |
| $(document).on({ | |
| 'onInit.fb': function (e, instance) { | |
| var Thumbs; | |
| if (instance && !instance.Thumbs) { | |
| Thumbs = new FancyThumbs(instance); | |
| if (Thumbs.isActive && Thumbs.opts.autoStart === true) { | |
| Thumbs.show(); | |
| } | |
| } | |
| }, | |
| 'beforeShow.fb': function (e, instance, item, firstRun) { | |
| var Thumbs = instance && instance.Thumbs; | |
| if (Thumbs && Thumbs.isVisible) { | |
| Thumbs.focus(firstRun ? 0 : 250); | |
| } | |
| }, | |
| 'afterKeydown.fb': function (e, instance, current, keypress, keycode) { | |
| var Thumbs = instance && instance.Thumbs; | |
| // "G" | |
| if (Thumbs && Thumbs.isActive && keycode === 71) { | |
| keypress.preventDefault(); | |
| Thumbs.toggle(); | |
| } | |
| }, | |
| 'beforeClose.fb': function (e, instance) { | |
| var Thumbs = instance && instance.Thumbs; | |
| if (Thumbs && Thumbs.isVisible && Thumbs.opts.hideOnClose !== false) { | |
| Thumbs.$grid.hide(); | |
| } | |
| }, | |
| }); | |
| })(document, jQuery); | |
| // ========================================================================== | |
| // | |
| // Hash | |
| // Enables linking to each modal | |
| // | |
| // ========================================================================== | |
| (function (window, document, $) { | |
| 'use strict'; | |
| // Simple $.escapeSelector polyfill (for jQuery prior v3) | |
| if (!$.escapeSelector) { | |
| $.escapeSelector = function (sel) { | |
| var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g; | |
| var fcssescape = function (ch, asCodePoint) { | |
| if (asCodePoint) { | |
| // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER | |
| if (ch === '\0') { | |
| return '\uFFFD'; | |
| } | |
| // Control characters and (dependent upon position) numbers get escaped as code points | |
| return ( | |
| ch.slice(0, -1) + | |
| '\\' + | |
| ch.charCodeAt(ch.length - 1).toString(16) + | |
| ' ' | |
| ); | |
| } | |
| // Other potentially-special ASCII characters get backslash-escaped | |
| return '\\' + ch; | |
| }; | |
| return (sel + '').replace(rcssescape, fcssescape); | |
| }; | |
| } | |
| // Get info about gallery name and current index from url | |
| function parseUrl() { | |
| var hash = window.location.hash.substr(1), | |
| rez = hash.split('-'), | |
| index = | |
| rez.length > 1 && /^\+?\d+$/.test(rez[rez.length - 1]) | |
| ? parseInt(rez.pop(-1), 10) || 1 | |
| : 1, | |
| gallery = rez.join('-'); | |
| return { | |
| hash: hash, | |
| /* Index is starting from 1 */ | |
| index: index < 1 ? 1 : index, | |
| gallery: gallery, | |
| }; | |
| } | |
| // Trigger click evnt on links to open new fancyBox instance | |
| function triggerFromUrl(url) { | |
| if (url.gallery !== '') { | |
| // If we can find element matching 'data-fancybox' atribute, | |
| // then triggering click event should start fancyBox | |
| $("[data-fancybox='" + $.escapeSelector(url.gallery) + "']") | |
| .eq(url.index - 1) | |
| .focus() | |
| .trigger('click.fb-start'); | |
| } | |
| } | |
| // Get gallery name from current instance | |
| function getGalleryID(instance) { | |
| var opts, ret; | |
| if (!instance) { | |
| return false; | |
| } | |
| opts = instance.current ? instance.current.opts : instance.opts; | |
| ret = | |
| opts.hash || | |
| (opts.$orig | |
| ? opts.$orig.data('fancybox') || opts.$orig.data('fancybox-trigger') | |
| : ''); | |
| return ret === '' ? false : ret; | |
| } | |
| // Start when DOM becomes ready | |
| $(function () { | |
| // Check if user has disabled this module | |
| if ($.fancybox.defaults.hash === false) { | |
| return; | |
| } | |
| // Update hash when opening/closing fancyBox | |
| $(document).on({ | |
| 'onInit.fb': function (e, instance) { | |
| var url, gallery; | |
| if (instance.group[instance.currIndex].opts.hash === false) { | |
| return; | |
| } | |
| url = parseUrl(); | |
| gallery = getGalleryID(instance); | |
| // Make sure gallery start index matches index from hash | |
| if (gallery && url.gallery && gallery == url.gallery) { | |
| instance.currIndex = url.index - 1; | |
| } | |
| }, | |
| 'beforeShow.fb': function (e, instance, current, firstRun) { | |
| var gallery; | |
| if (!current || current.opts.hash === false) { | |
| return; | |
| } | |
| // Check if need to update window hash | |
| gallery = getGalleryID(instance); | |
| if (!gallery) { | |
| return; | |
| } | |
| // Variable containing last hash value set by fancyBox | |
| // It will be used to determine if fancyBox needs to close after hash change is detected | |
| instance.currentHash = | |
| gallery + | |
| (instance.group.length > 1 ? '-' + (current.index + 1) : ''); | |
| // If current hash is the same (this instance most likely is opened by hashchange), then do nothing | |
| if (window.location.hash === '#' + instance.currentHash) { | |
| return; | |
| } | |
| if (firstRun && !instance.origHash) { | |
| instance.origHash = window.location.hash; | |
| } | |
| if (instance.hashTimer) { | |
| clearTimeout(instance.hashTimer); | |
| } | |
| // Update hash | |
| instance.hashTimer = setTimeout(function () { | |
| if ('replaceState' in window.history) { | |
| window.history[firstRun ? 'pushState' : 'replaceState']( | |
| {}, | |
| document.title, | |
| window.location.pathname + | |
| window.location.search + | |
| '#' + | |
| instance.currentHash | |
| ); | |
| if (firstRun) { | |
| instance.hasCreatedHistory = true; | |
| } | |
| } else { | |
| window.location.hash = instance.currentHash; | |
| } | |
| instance.hashTimer = null; | |
| }, 300); | |
| }, | |
| 'beforeClose.fb': function (e, instance, current) { | |
| if (!current || current.opts.hash === false) { | |
| return; | |
| } | |
| clearTimeout(instance.hashTimer); | |
| // Goto previous history entry | |
| if (instance.currentHash && instance.hasCreatedHistory) { | |
| window.history.back(); | |
| } else if (instance.currentHash) { | |
| if ('replaceState' in window.history) { | |
| window.history.replaceState( | |
| {}, | |
| document.title, | |
| window.location.pathname + | |
| window.location.search + | |
| (instance.origHash || '') | |
| ); | |
| } else { | |
| window.location.hash = instance.origHash; | |
| } | |
| } | |
| instance.currentHash = null; | |
| }, | |
| }); | |
| // Check if need to start/close after url has changed | |
| $(window).on('hashchange.fb', function () { | |
| var url = parseUrl(), | |
| fb = null; | |
| // Find last fancyBox instance that has "hash" | |
| $.each($('.fancybox-container').get().reverse(), function (index, value) { | |
| var tmp = $(value).data('FancyBox'); | |
| if (tmp && tmp.currentHash) { | |
| fb = tmp; | |
| return false; | |
| } | |
| }); | |
| if (fb) { | |
| // Now, compare hash values | |
| if ( | |
| fb.currentHash !== url.gallery + '-' + url.index && | |
| !(url.index === 1 && fb.currentHash == url.gallery) | |
| ) { | |
| fb.currentHash = null; | |
| fb.close(); | |
| } | |
| } else if (url.gallery !== '') { | |
| triggerFromUrl(url); | |
| } | |
| }); | |
| // Check current hash and trigger click event on matching element to start fancyBox, if needed | |
| setTimeout(function () { | |
| if (!$.fancybox.getInstance()) { | |
| triggerFromUrl(parseUrl()); | |
| } | |
| }, 50); | |
| }); | |
| })(window, document, jQuery); | |
| // ========================================================================== | |
| // | |
| // Wheel | |
| // Basic mouse weheel support for gallery navigation | |
| // | |
| // ========================================================================== | |
| (function (document, $) { | |
| 'use strict'; | |
| var prevTime = new Date().getTime(); | |
| $(document).on({ | |
| 'onInit.fb': function (e, instance, current) { | |
| instance.$refs.stage.on( | |
| 'mousewheel DOMMouseScroll wheel MozMousePixelScroll', | |
| function (e) { | |
| var current = instance.current, | |
| currTime = new Date().getTime(); | |
| if ( | |
| instance.group.length < 2 || | |
| current.opts.wheel === false || | |
| (current.opts.wheel === 'auto' && current.type !== 'image') | |
| ) { | |
| return; | |
| } | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| if (current.$slide.hasClass('fancybox-animated')) { | |
| return; | |
| } | |
| e = e.originalEvent || e; | |
| if (currTime - prevTime < 250) { | |
| return; | |
| } | |
| prevTime = currTime; | |
| instance[ | |
| (-e.deltaY || -e.deltaX || e.wheelDelta || -e.detail) < 0 | |
| ? 'next' | |
| : 'previous' | |
| ](); | |
| } | |
| ); | |
| }, | |
| }); | |
| })(document, jQuery); |
| // ================================================== | |
| // fancyBox v3.5.7 | |
| // | |
| // Licensed GPLv3 for open source use | |
| // or fancyBox Commercial License for commercial use | |
| // | |
| // http://fancyapps.com/fancybox/ | |
| // Copyright 2022 fancyApps | |
| // | |
| // ================================================== | |
| !function(t,e,n,o){"use strict";if(t.console=t.console||{info:function(t){}},n)if(n.fn.fancybox)console.info("fancyBox already initialized");else{var i,s,a={closeExisting:!1,loop:!1,gutter:50,keyboard:!0,preventCaptionOverlap:!0,arrows:!0,infobar:!0,smallBtn:"auto",toolbar:"auto",buttons:["zoom","slideShow","thumbs","close"],idleTime:3,protect:!1,modal:!1,image:{preload:!1},ajax:{settings:{data:{fancybox:!0}}},iframe:{tpl:'<iframe id="fancybox-frame{rnd}" name="fancybox-frame{rnd}" class="fancybox-iframe" allowfullscreen="allowfullscreen" allow="autoplay; fullscreen" src=""></iframe>',preload:!0,css:{},attr:{scrolling:"auto"}},video:{tpl:'<video class="fancybox-video" controls controlsList="nodownload" poster="{{poster}}"><source src="{{src}}" type="{{format}}" />Sorry, your browser doesn\'t support embedded videos, <a href="{{src}}">download</a> and watch with your favorite video player!</video>',format:"",autoStart:!0},defaultType:"image",animationEffect:"zoom",animationDuration:366,zoomOpacity:"auto",transitionEffect:"fade",transitionDuration:366,slideClass:"",baseClass:"",baseTpl:'<div class="fancybox-container" role="dialog" tabindex="-1"><div class="fancybox-bg"></div><div class="fancybox-inner"><div class="fancybox-infobar"><span data-fancybox-index></span> / <span data-fancybox-count></span></div><div class="fancybox-toolbar">{{buttons}}</div><div class="fancybox-navigation">{{arrows}}</div><div class="fancybox-stage"></div><div class="fancybox-caption"><div class="fancybox-caption__body"></div></div></div></div>',spinnerTpl:'<div class="fancybox-loading"></div>',errorTpl:'<div class="fancybox-error"><p>{{ERROR}}</p></div>',btnTpl:{download:'<a download data-fancybox-download class="fancybox-button fancybox-button--download" title="{{DOWNLOAD}}" href="javascript:;"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18.62 17.09V19H5.38v-1.91zm-2.97-6.96L17 11.45l-5 4.87-5-4.87 1.36-1.32 2.68 2.64V5h1.92v7.77z"/></svg></a>',zoom:'<button data-fancybox-zoom class="fancybox-button fancybox-button--zoom" title="{{ZOOM}}"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18.7 17.3l-3-3a5.9 5.9 0 0 0-.6-7.6 5.9 5.9 0 0 0-8.4 0 5.9 5.9 0 0 0 0 8.4 5.9 5.9 0 0 0 7.7.7l3 3a1 1 0 0 0 1.3 0c.4-.5.4-1 0-1.5zM8.1 13.8a4 4 0 0 1 0-5.7 4 4 0 0 1 5.7 0 4 4 0 0 1 0 5.7 4 4 0 0 1-5.7 0z"/></svg></button>',close:'<button data-fancybox-close class="fancybox-button fancybox-button--close" title="{{CLOSE}}"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 10.6L6.6 5.2 5.2 6.6l5.4 5.4-5.4 5.4 1.4 1.4 5.4-5.4 5.4 5.4 1.4-1.4-5.4-5.4 5.4-5.4-1.4-1.4-5.4 5.4z"/></svg></button>',arrowLeft:'<button data-fancybox-prev class="fancybox-button fancybox-button--arrow_left" title="{{PREV}}"><div><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M11.28 15.7l-1.34 1.37L5 12l4.94-5.07 1.34 1.38-2.68 2.72H19v1.94H8.6z"/></svg></div></button>',arrowRight:'<button data-fancybox-next class="fancybox-button fancybox-button--arrow_right" title="{{NEXT}}"><div><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M15.4 12.97l-2.68 2.72 1.34 1.38L19 12l-4.94-5.07-1.34 1.38 2.68 2.72H5v1.94z"/></svg></div></button>',smallBtn:'<button type="button" data-fancybox-close class="fancybox-button fancybox-close-small" title="{{CLOSE}}"><svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 24 24"><path d="M13 12l5-5-1-1-5 5-5-5-1 1 5 5-5 5 1 1 5-5 5 5 1-1z"/></svg></button>'},parentEl:"body",hideScrollbar:!0,autoFocus:!0,backFocus:!0,trapFocus:!0,fullScreen:{autoStart:!1},touch:{vertical:!0,momentum:!0},hash:null,media:{},slideShow:{autoStart:!1,speed:3e3},thumbs:{autoStart:!1,hideOnClose:!0,parentEl:".fancybox-container",axis:"y"},wheel:"auto",onInit:n.noop,beforeLoad:n.noop,afterLoad:n.noop,beforeShow:n.noop,afterShow:n.noop,beforeClose:n.noop,afterClose:n.noop,onActivate:n.noop,onDeactivate:n.noop,clickContent:function(t,e){return"image"===t.type&&"zoom"},clickSlide:"close",clickOutside:"close",dblclickContent:!1,dblclickSlide:!1,dblclickOutside:!1,mobile:{preventCaptionOverlap:!1,idleTime:!1,clickContent:function(t,e){return"image"===t.type&&"toggleControls"},clickSlide:function(t,e){return"image"===t.type?"toggleControls":"close"},dblclickContent:function(t,e){return"image"===t.type&&"zoom"},dblclickSlide:function(t,e){return"image"===t.type&&"zoom"}},lang:"en",i18n:{en:{CLOSE:"Close",NEXT:"Next",PREV:"Previous",ERROR:"The requested content cannot be loaded. <br/> Please try again later.",PLAY_START:"Start slideshow",PLAY_STOP:"Pause slideshow",FULL_SCREEN:"Full screen",THUMBS:"Thumbnails",DOWNLOAD:"Download",SHARE:"Share",ZOOM:"Zoom"},de:{CLOSE:"Schließen",NEXT:"Weiter",PREV:"Zurück",ERROR:"Die angeforderten Daten konnten nicht geladen werden. <br/> Bitte versuchen Sie es später nochmal.",PLAY_START:"Diaschau starten",PLAY_STOP:"Diaschau beenden",FULL_SCREEN:"Vollbild",THUMBS:"Vorschaubilder",DOWNLOAD:"Herunterladen",SHARE:"Teilen",ZOOM:"Vergrößern"}}},r=n(t),c=n(e),l=0,h=t.requestAnimationFrame||t.webkitRequestAnimationFrame||t.mozRequestAnimationFrame||t.oRequestAnimationFrame||function(e){return t.setTimeout(e,1e3/60)},d=t.cancelAnimationFrame||t.webkitCancelAnimationFrame||t.mozCancelAnimationFrame||t.oCancelAnimationFrame||function(e){t.clearTimeout(e)},u=function(){var t,n=e.createElement("fakeelement"),o={transition:"transitionend",OTransition:"oTransitionEnd",MozTransition:"transitionend",WebkitTransition:"webkitTransitionEnd"};for(t in o)if(void 0!==n.style[t])return o[t];return"transitionend"}(),f=function(t){return t&&t.length&&t[0].offsetHeight},p=function(t,e){var o=n.extend(!0,{},t,e);return n.each(e,function(t,e){n.isArray(e)&&(o[t]=e)}),o},g=function(t,e,o){this.opts=p({index:o},n.fancybox.defaults),n.isPlainObject(e)&&(this.opts=p(this.opts,e)),n.fancybox.isMobile&&(this.opts=p(this.opts,this.opts.mobile)),this.id=this.opts.id||++l,this.currIndex=parseInt(this.opts.index,10)||0,this.prevIndex=null,this.prevPos=null,this.currPos=0,this.firstRun=!0,this.group=[],this.slides={},this.addContent(t),this.group.length&&this.init()};n.extend(g.prototype,{init:function(){var o,i,s=this,a=s.group[s.currIndex].opts;a.closeExisting&&n.fancybox.close(!0),n("body").addClass("fancybox-active"),!n.fancybox.getInstance()&&!1!==a.hideScrollbar&&!n.fancybox.isMobile&&e.body.scrollHeight>t.innerHeight&&(n("head").append('<style id="fancybox-style-noscroll" type="text/css">.compensate-for-scrollbar{margin-right:'+(t.innerWidth-e.documentElement.clientWidth)+"px;}</style>"),n("body").addClass("compensate-for-scrollbar")),i="",n.each(a.buttons,function(t,e){i+=a.btnTpl[e]||""}),o=n(s.translate(s,a.baseTpl.replace("{{buttons}}",i).replace("{{arrows}}",a.btnTpl.arrowLeft+a.btnTpl.arrowRight))).attr("id","fancybox-container-"+s.id).addClass(a.baseClass).data("FancyBox",s).appendTo(a.parentEl),s.$refs={container:o},["bg","inner","infobar","toolbar","stage","caption","navigation"].forEach(function(t){s.$refs[t]=o.find(".fancybox-"+t)}),s.trigger("onInit"),s.activate(),s.jumpTo(s.currIndex)},translate:function(t,e){var n=t.opts.i18n[t.opts.lang]||t.opts.i18n.en;return e.replace(/\{\{(\w+)\}\}/g,function(t,e){return void 0===n[e]?t:n[e]})},addContent:function(t){var e,o=this,i=n.makeArray(t);n.each(i,function(t,e){var i,s,a,r,c,l={},h={};n.isPlainObject(e)?(l=e,h=e.opts||e):"object"===n.type(e)&&n(e).length?(h=(i=n(e)).data()||{},(h=n.extend(!0,{},h,h.options)).$orig=i,l.src=o.opts.src||h.src||i.attr("href"),l.type||l.src||(l.type="inline",l.src=e)):l={type:"html",src:e+""},l.opts=n.extend(!0,{},o.opts,h),n.isArray(h.buttons)&&(l.opts.buttons=h.buttons),n.fancybox.isMobile&&l.opts.mobile&&(l.opts=p(l.opts,l.opts.mobile)),s=l.type||l.opts.type,r=l.src||"",!s&&r&&((a=r.match(/\.(mp4|mov|ogv|webm)((\?|#).*)?$/i))?(s="video",l.opts.video.format||(l.opts.video.format="video/"+("ogv"===a[1]?"ogg":a[1]))):r.match(/(^data:image\/[a-z0-9+\/=]*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg|ico)((\?|#).*)?$)/i)?s="image":r.match(/\.(pdf)((\?|#).*)?$/i)?(s="iframe",l=n.extend(!0,l,{contentType:"pdf",opts:{iframe:{preload:!1}}})):"#"===r.charAt(0)&&(s="inline")),s?l.type=s:o.trigger("objectNeedsType",l),l.contentType||(l.contentType=n.inArray(l.type,["html","inline","ajax"])>-1?"html":l.type),l.index=o.group.length,"auto"==l.opts.smallBtn&&(l.opts.smallBtn=n.inArray(l.type,["html","inline","ajax"])>-1),"auto"===l.opts.toolbar&&(l.opts.toolbar=!l.opts.smallBtn),l.$thumb=l.opts.$thumb||null,l.opts.$trigger&&l.index===o.opts.index&&(l.$thumb=l.opts.$trigger.find("img:first"),l.$thumb.length&&(l.opts.$orig=l.opts.$trigger)),l.$thumb&&l.$thumb.length||!l.opts.$orig||(l.$thumb=l.opts.$orig.find("img:first")),l.$thumb&&!l.$thumb.length&&(l.$thumb=null),l.thumb=l.opts.thumb||(l.$thumb?l.$thumb[0].src:null),"function"===n.type(l.opts.caption)&&(l.opts.caption=l.opts.caption.apply(e,[o,l])),"function"===n.type(o.opts.caption)&&(l.opts.caption=o.opts.caption.apply(e,[o,l])),l.opts.caption instanceof n||(l.opts.caption=void 0===l.opts.caption?"":l.opts.caption+""),"ajax"===l.type&&(c=r.split(/\s+/,2)).length>1&&(l.src=c.shift(),l.opts.filter=c.shift()),l.opts.modal&&(l.opts=n.extend(!0,l.opts,{trapFocus:!0,infobar:0,toolbar:0,smallBtn:0,keyboard:0,slideShow:0,fullScreen:0,thumbs:0,touch:0,clickContent:!1,clickSlide:!1,clickOutside:!1,dblclickContent:!1,dblclickSlide:!1,dblclickOutside:!1})),o.group.push(l)}),Object.keys(o.slides).length&&(o.updateControls(),(e=o.Thumbs)&&e.isActive&&(e.create(),e.focus()))},addEvents:function(){var e=this;e.removeEvents(),e.$refs.container.on("click.fb-close","[data-fancybox-close]",function(t){t.stopPropagation(),t.preventDefault(),e.close(t)}).on("touchstart.fb-prev click.fb-prev","[data-fancybox-prev]",function(t){t.stopPropagation(),t.preventDefault(),e.previous()}).on("touchstart.fb-next click.fb-next","[data-fancybox-next]",function(t){t.stopPropagation(),t.preventDefault(),e.next()}).on("click.fb","[data-fancybox-zoom]",function(t){e[e.isScaledDown()?"scaleToActual":"scaleToFit"]()}),r.on("orientationchange.fb resize.fb",function(t){t&&t.originalEvent&&"resize"===t.originalEvent.type?(e.requestId&&d(e.requestId),e.requestId=h(function(){e.update(t)})):(e.current&&"iframe"===e.current.type&&e.$refs.stage.hide(),setTimeout(function(){e.$refs.stage.show(),e.update(t)},n.fancybox.isMobile?600:250))}),c.on("keydown.fb",function(t){var o=(n.fancybox?n.fancybox.getInstance():null).current,i=t.keyCode||t.which;if(9!=i){if(!(!o.opts.keyboard||t.ctrlKey||t.altKey||t.shiftKey||n(t.target).is("input,textarea,video,audio,select")))return 8===i||27===i?(t.preventDefault(),void e.close(t)):37===i||38===i?(t.preventDefault(),void e.previous()):39===i||40===i?(t.preventDefault(),void e.next()):void e.trigger("afterKeydown",t,i)}else o.opts.trapFocus&&e.focus(t)}),e.group[e.currIndex].opts.idleTime&&(e.idleSecondsCounter=0,c.on("mousemove.fb-idle mouseleave.fb-idle mousedown.fb-idle touchstart.fb-idle touchmove.fb-idle scroll.fb-idle keydown.fb-idle",function(t){e.idleSecondsCounter=0,e.isIdle&&e.showControls(),e.isIdle=!1}),e.idleInterval=t.setInterval(function(){e.idleSecondsCounter++,e.idleSecondsCounter>=e.group[e.currIndex].opts.idleTime&&!e.isDragging&&(e.isIdle=!0,e.idleSecondsCounter=0,e.hideControls())},1e3))},removeEvents:function(){r.off("orientationchange.fb resize.fb"),c.off("keydown.fb .fb-idle"),this.$refs.container.off(".fb-close .fb-prev .fb-next"),this.idleInterval&&(t.clearInterval(this.idleInterval),this.idleInterval=null)},previous:function(t){return this.jumpTo(this.currPos-1,t)},next:function(t){return this.jumpTo(this.currPos+1,t)},jumpTo:function(t,e){var o,i,s,a,r,c,l,h,d,u=this,p=u.group.length;if(!(u.isDragging||u.isClosing||u.isAnimating&&u.firstRun)){if(t=parseInt(t,10),!(s=u.current?u.current.opts.loop:u.opts.loop)&&(t<0||t>=p))return!1;if(o=u.firstRun=!Object.keys(u.slides).length,r=u.current,u.prevIndex=u.currIndex,u.prevPos=u.currPos,a=u.createSlide(t),p>1&&((s||a.index<p-1)&&u.createSlide(t+1),(s||a.index>0)&&u.createSlide(t-1)),u.current=a,u.currIndex=a.index,u.currPos=a.pos,u.trigger("beforeShow",o),u.updateControls(),a.forcedDuration=void 0,n.isNumeric(e)?a.forcedDuration=e:e=a.opts[o?"animationDuration":"transitionDuration"],e=parseInt(e,10),i=u.isMoved(a),a.$slide.addClass("fancybox-slide--current"),o)return a.opts.animationEffect&&e&&u.$refs.container.css("transition-duration",e+"ms"),u.$refs.container.addClass("fancybox-is-open").trigger("focus"),u.loadSlide(a),void u.preload("image");c=n.fancybox.getTranslate(r.$slide),l=n.fancybox.getTranslate(u.$refs.stage),n.each(u.slides,function(t,e){n.fancybox.stop(e.$slide,!0)}),r.pos!==a.pos&&(r.isComplete=!1),r.$slide.removeClass("fancybox-slide--complete fancybox-slide--current"),i?(d=c.left-(r.pos*c.width+r.pos*r.opts.gutter),n.each(u.slides,function(t,o){o.$slide.removeClass("fancybox-animated").removeClass(function(t,e){return(e.match(/(^|\s)fancybox-fx-\S+/g)||[]).join(" ")});var i=o.pos*c.width+o.pos*o.opts.gutter;n.fancybox.setTranslate(o.$slide,{top:0,left:i-l.left+d}),o.pos!==a.pos&&o.$slide.addClass("fancybox-slide--"+(o.pos>a.pos?"next":"previous")),f(o.$slide),n.fancybox.animate(o.$slide,{top:0,left:(o.pos-a.pos)*c.width+(o.pos-a.pos)*o.opts.gutter},e,function(){o.$slide.css({transform:"",opacity:""}).removeClass("fancybox-slide--next fancybox-slide--previous"),o.pos===u.currPos&&u.complete()})})):e&&a.opts.transitionEffect&&(h="fancybox-animated fancybox-fx-"+a.opts.transitionEffect,r.$slide.addClass("fancybox-slide--"+(r.pos>a.pos?"next":"previous")),n.fancybox.animate(r.$slide,h,e,function(){r.$slide.removeClass(h).removeClass("fancybox-slide--next fancybox-slide--previous")},!1)),a.isLoaded?u.revealContent(a):u.loadSlide(a),u.preload("image")}},createSlide:function(t){var e,o;return o=(o=t%this.group.length)<0?this.group.length+o:o,!this.slides[t]&&this.group[o]&&(e=n('<div class="fancybox-slide"></div>').appendTo(this.$refs.stage),this.slides[t]=n.extend(!0,{},this.group[o],{pos:t,$slide:e,isLoaded:!1}),this.updateSlide(this.slides[t])),this.slides[t]},scaleToActual:function(t,e,o){var i,s,a,r,c,l=this,h=l.current,d=h.$content,u=n.fancybox.getTranslate(h.$slide).width,f=n.fancybox.getTranslate(h.$slide).height,p=h.width,g=h.height;l.isAnimating||l.isMoved()||!d||"image"!=h.type||!h.isLoaded||h.hasError||(l.isAnimating=!0,n.fancybox.stop(d),t=void 0===t?.5*u:t,e=void 0===e?.5*f:e,(i=n.fancybox.getTranslate(d)).top-=n.fancybox.getTranslate(h.$slide).top,i.left-=n.fancybox.getTranslate(h.$slide).left,r=p/i.width,c=g/i.height,s=.5*u-.5*p,a=.5*f-.5*g,p>u&&((s=i.left*r-(t*r-t))>0&&(s=0),s<u-p&&(s=u-p)),g>f&&((a=i.top*c-(e*c-e))>0&&(a=0),a<f-g&&(a=f-g)),l.updateCursor(p,g),n.fancybox.animate(d,{top:a,left:s,scaleX:r,scaleY:c},o||366,function(){l.isAnimating=!1}),l.SlideShow&&l.SlideShow.isActive&&l.SlideShow.stop())},scaleToFit:function(t){var e,o=this,i=o.current,s=i.$content;o.isAnimating||o.isMoved()||!s||"image"!=i.type||!i.isLoaded||i.hasError||(o.isAnimating=!0,n.fancybox.stop(s),e=o.getFitPos(i),o.updateCursor(e.width,e.height),n.fancybox.animate(s,{top:e.top,left:e.left,scaleX:e.width/s.width(),scaleY:e.height/s.height()},t||366,function(){o.isAnimating=!1}))},getFitPos:function(t){var e,o,i,s,a=t.$content,r=t.$slide,c=t.width||t.opts.width,l=t.height||t.opts.height,h={};return!!(t.isLoaded&&a&&a.length)&&(e=n.fancybox.getTranslate(this.$refs.stage).width,o=n.fancybox.getTranslate(this.$refs.stage).height,e-=parseFloat(r.css("paddingLeft"))+parseFloat(r.css("paddingRight"))+parseFloat(a.css("marginLeft"))+parseFloat(a.css("marginRight")),o-=parseFloat(r.css("paddingTop"))+parseFloat(r.css("paddingBottom"))+parseFloat(a.css("marginTop"))+parseFloat(a.css("marginBottom")),c&&l||(c=e,l=o),(c*=i=Math.min(1,e/c,o/l))>e-.5&&(c=e),(l*=i)>o-.5&&(l=o),"image"===t.type?(h.top=Math.floor(.5*(o-l))+parseFloat(r.css("paddingTop")),h.left=Math.floor(.5*(e-c))+parseFloat(r.css("paddingLeft"))):"video"===t.contentType&&(l>c/(s=t.opts.width&&t.opts.height?c/l:t.opts.ratio||16/9)?l=c/s:c>l*s&&(c=l*s)),h.width=c,h.height=l,h)},update:function(t){var e=this;n.each(e.slides,function(n,o){e.updateSlide(o,t)})},updateSlide:function(t,e){var o=t&&t.$content,i=t.width||t.opts.width,s=t.height||t.opts.height,a=t.$slide;this.adjustCaption(t),o&&(i||s||"video"===t.contentType)&&!t.hasError&&(n.fancybox.stop(o),n.fancybox.setTranslate(o,this.getFitPos(t)),t.pos===this.currPos&&(this.isAnimating=!1,this.updateCursor())),this.adjustLayout(t),a.length&&(a.trigger("refresh"),t.pos===this.currPos&&this.$refs.toolbar.add(this.$refs.navigation.find(".fancybox-button--arrow_right")).toggleClass("compensate-for-scrollbar",a.get(0).scrollHeight>a.get(0).clientHeight)),this.trigger("onUpdate",t,e)},centerSlide:function(t){var e=this,o=e.current,i=o.$slide;!e.isClosing&&o&&(i.siblings().css({transform:"",opacity:""}),i.parent().children().removeClass("fancybox-slide--previous fancybox-slide--next"),n.fancybox.animate(i,{top:0,left:0,opacity:1},void 0===t?0:t,function(){i.css({transform:"",opacity:""}),o.isComplete||e.complete()},!1))},isMoved:function(t){var e,o,i=t||this.current;return!!i&&(o=n.fancybox.getTranslate(this.$refs.stage),e=n.fancybox.getTranslate(i.$slide),!i.$slide.hasClass("fancybox-animated")&&(Math.abs(e.top-o.top)>.5||Math.abs(e.left-o.left)>.5))},updateCursor:function(t,e){var o,i,s=this.current,a=this.$refs.container;s&&!this.isClosing&&this.Guestures&&(a.removeClass("fancybox-is-zoomable fancybox-can-zoomIn fancybox-can-zoomOut fancybox-can-swipe fancybox-can-pan"),i=!!(o=this.canPan(t,e))||this.isZoomable(),a.toggleClass("fancybox-is-zoomable",i),n("[data-fancybox-zoom]").prop("disabled",!i),o?a.addClass("fancybox-can-pan"):i&&("zoom"===s.opts.clickContent||n.isFunction(s.opts.clickContent)&&"zoom"==s.opts.clickContent(s))?a.addClass("fancybox-can-zoomIn"):s.opts.touch&&(s.opts.touch.vertical||this.group.length>1)&&"video"!==s.contentType&&a.addClass("fancybox-can-swipe"))},isZoomable:function(){var t,e=this.current;if(e&&!this.isClosing&&"image"===e.type&&!e.hasError){if(!e.isLoaded)return!0;if((t=this.getFitPos(e))&&(e.width>t.width||e.height>t.height))return!0}return!1},isScaledDown:function(t,e){var o=!1,i=this.current,s=i.$content;return void 0!==t&&void 0!==e?o=t<i.width&&e<i.height:s&&(o=(o=n.fancybox.getTranslate(s)).width<i.width&&o.height<i.height),o},canPan:function(t,e){var o=this.current,i=null,s=!1;return"image"===o.type&&(o.isComplete||t&&e)&&!o.hasError&&(s=this.getFitPos(o),void 0!==t&&void 0!==e?i={width:t,height:e}:o.isComplete&&(i=n.fancybox.getTranslate(o.$content)),i&&s&&(s=Math.abs(i.width-s.width)>1.5||Math.abs(i.height-s.height)>1.5)),s},loadSlide:function(t){var e,o,i,s=this;if(!t.isLoading&&!t.isLoaded){if(t.isLoading=!0,!1===s.trigger("beforeLoad",t))return t.isLoading=!1,!1;switch(e=t.type,(o=t.$slide).off("refresh").trigger("onReset").addClass(t.opts.slideClass),e){case"image":s.setImage(t);break;case"iframe":s.setIframe(t);break;case"html":s.setContent(t,t.src||t.content);break;case"video":s.setContent(t,t.opts.video.tpl.replace(/\{\{src\}\}/gi,t.src).replace("{{format}}",t.opts.videoFormat||t.opts.video.format||"").replace("{{poster}}",t.thumb||""));break;case"inline":n(t.src).length?s.setContent(t,n(t.src)):s.setError(t);break;case"ajax":s.showLoading(t),i=n.ajax(n.extend({},t.opts.ajax.settings,{url:t.src,success:function(e,n){"success"===n&&s.setContent(t,e)},error:function(e,n){e&&"abort"!==n&&s.setError(t)}})),o.one("onReset",function(){i.abort()});break;default:s.setError(t)}return!0}},setImage:function(t){var o,i=this;setTimeout(function(){var e=t.$image;i.isClosing||!t.isLoading||e&&e.length&&e[0].complete||t.hasError||i.showLoading(t)},50),i.checkSrcset(t),t.$content=n('<div class="fancybox-content"></div>').addClass("fancybox-is-hidden").appendTo(t.$slide.addClass("fancybox-slide--image")),!1!==t.opts.preload&&t.opts.width&&t.opts.height&&t.thumb&&(t.width=t.opts.width,t.height=t.opts.height,(o=e.createElement("img")).onerror=function(){n(this).remove(),t.$ghost=null},o.onload=function(){i.afterLoad(t)},t.$ghost=n(o).addClass("fancybox-image").appendTo(t.$content).attr("src",t.thumb)),i.setBigImage(t)},checkSrcset:function(e){var n,o,i,s,a=e.opts.srcset||e.opts.image.srcset;if(a){i=t.devicePixelRatio||1,s=t.innerWidth*i,(o=a.split(",").map(function(t){var e={};return t.trim().split(/\s+/).forEach(function(t,n){var o=parseInt(t.substring(0,t.length-1),10);if(0===n)return e.url=t;o&&(e.value=o,e.postfix=t[t.length-1])}),e})).sort(function(t,e){return t.value-e.value});for(var r=0;r<o.length;r++){var c=o[r];if("w"===c.postfix&&c.value>=s||"x"===c.postfix&&c.value>=i){n=c;break}}!n&&o.length&&(n=o[o.length-1]),n&&(e.src=n.url,e.width&&e.height&&"w"==n.postfix&&(e.height=e.width/e.height*n.value,e.width=n.value),e.opts.srcset=a)}},setBigImage:function(t){var o=this,i=e.createElement("img"),s=n(i);t.$image=s.one("error",function(){o.setError(t)}).one("load",function(){var e;t.$ghost||(o.resolveImageSlideSize(t,this.naturalWidth,this.naturalHeight),o.afterLoad(t)),o.isClosing||(t.opts.srcset&&((e=t.opts.sizes)&&"auto"!==e||(e=(t.width/t.height>1&&r.width()/r.height()>1?"100":Math.round(t.width/t.height*100))+"vw"),s.attr("sizes",e).attr("srcset",t.opts.srcset)),t.$ghost&&setTimeout(function(){t.$ghost&&!o.isClosing&&t.$ghost.hide()},Math.min(300,Math.max(1e3,t.height/1600))),o.hideLoading(t))}).addClass("fancybox-image").attr("src",t.src).appendTo(t.$content),(i.complete||"complete"==i.readyState)&&s.naturalWidth&&s.naturalHeight?s.trigger("load"):i.error&&s.trigger("error")},resolveImageSlideSize:function(t,e,n){var o=parseInt(t.opts.width,10),i=parseInt(t.opts.height,10);t.width=e,t.height=n,o>0&&(t.width=o,t.height=Math.floor(o*n/e)),i>0&&(t.width=Math.floor(i*e/n),t.height=i)},setIframe:function(t){var e,o=this,i=t.opts.iframe,s=t.$slide;t.$content=n('<div class="fancybox-content'+(i.preload?" fancybox-is-hidden":"")+'"></div>').css(i.css).appendTo(s),s.addClass("fancybox-slide--"+t.contentType),t.$iframe=e=n(i.tpl.replace(/\{rnd\}/g,(new Date).getTime())).attr(i.attr).appendTo(t.$content),i.preload?(o.showLoading(t),e.on("load.fb error.fb",function(e){this.isReady=1,t.$slide.trigger("refresh"),o.afterLoad(t)}),s.on("refresh.fb",function(){var n,o=t.$content,a=i.css.width,r=i.css.height;if(1===e[0].isReady){try{n=e.contents().find("body")}catch(t){}n&&n.length&&n.children().length&&(s.css("overflow","visible"),o.css({width:"100%","max-width":"100%",height:"9999px"}),void 0===a&&(a=Math.ceil(Math.max(n[0].clientWidth,n.outerWidth(!0)))),o.css("width",a||"").css("max-width",""),void 0===r&&(r=Math.ceil(Math.max(n[0].clientHeight,n.outerHeight(!0)))),o.css("height",r||""),s.css("overflow","auto")),o.removeClass("fancybox-is-hidden")}})):o.afterLoad(t),e.attr("src",t.src),s.one("onReset",function(){try{n(this).find("iframe").hide().unbind().attr("src","//about:blank")}catch(t){}n(this).off("refresh.fb").empty(),t.isLoaded=!1,t.isRevealed=!1})},setContent:function(t,e){var o;this.isClosing||(this.hideLoading(t),t.$content&&n.fancybox.stop(t.$content),t.$slide.empty(),(o=e)&&o.hasOwnProperty&&o instanceof n&&e.parent().length?((e.hasClass("fancybox-content")||e.parent().hasClass("fancybox-content"))&&e.parents(".fancybox-slide").trigger("onReset"),t.$placeholder=n("<div>").hide().insertAfter(e),e.css("display","inline-block")):t.hasError||("string"===n.type(e)&&(e=n("<div>").append(n.trim(e)).contents()),t.opts.filter&&(e=n("<div>").html(e).find(t.opts.filter))),t.$slide.one("onReset",function(){n(this).find("video,audio").trigger("pause"),t.$placeholder&&(t.$placeholder.after(e.removeClass("fancybox-content").hide()).remove(),t.$placeholder=null),t.$smallBtn&&(t.$smallBtn.remove(),t.$smallBtn=null),t.hasError||(n(this).empty(),t.isLoaded=!1,t.isRevealed=!1)}),n(e).appendTo(t.$slide),n(e).is("video,audio")&&(n(e).addClass("fancybox-video"),n(e).wrap("<div></div>"),t.contentType="video",t.opts.width=t.opts.width||n(e).attr("width"),t.opts.height=t.opts.height||n(e).attr("height")),t.$content=t.$slide.children().filter("div,form,main,video,audio,article,.fancybox-content").first(),t.$content.siblings().hide(),t.$content.length||(t.$content=t.$slide.wrapInner("<div></div>").children().first()),t.$content.addClass("fancybox-content"),t.$slide.addClass("fancybox-slide--"+t.contentType),this.afterLoad(t))},setError:function(t){t.hasError=!0,t.$slide.trigger("onReset").removeClass("fancybox-slide--"+t.contentType).addClass("fancybox-slide--error"),t.contentType="html",this.setContent(t,this.translate(t,t.opts.errorTpl)),t.pos===this.currPos&&(this.isAnimating=!1)},showLoading:function(t){(t=t||this.current)&&!t.$spinner&&(t.$spinner=n(this.translate(this,this.opts.spinnerTpl)).appendTo(t.$slide).hide().fadeIn("fast"))},hideLoading:function(t){(t=t||this.current)&&t.$spinner&&(t.$spinner.stop().remove(),delete t.$spinner)},afterLoad:function(t){this.isClosing||(t.isLoading=!1,t.isLoaded=!0,this.trigger("afterLoad",t),this.hideLoading(t),!t.opts.smallBtn||t.$smallBtn&&t.$smallBtn.length||(t.$smallBtn=n(this.translate(t,t.opts.btnTpl.smallBtn)).appendTo(t.$content)),t.opts.protect&&t.$content&&!t.hasError&&(t.$content.on("contextmenu.fb",function(t){return 2==t.button&&t.preventDefault(),!0}),"image"===t.type&&n('<div class="fancybox-spaceball"></div>').appendTo(t.$content)),this.adjustCaption(t),this.adjustLayout(t),t.pos===this.currPos&&this.updateCursor(),this.revealContent(t))},adjustCaption:function(t){var e,n=t||this.current,o=n.opts.caption,i=n.opts.preventCaptionOverlap,s=this.$refs.caption,a=!1;s.toggleClass("fancybox-caption--separate",i),i&&o&&o.length&&(n.pos!==this.currPos?((e=s.clone().appendTo(s.parent())).children().eq(0).empty().html(o),a=e.outerHeight(!0),e.empty().remove()):this.$caption&&(a=this.$caption.outerHeight(!0)),n.$slide.css("padding-bottom",a||""))},adjustLayout:function(t){var e,n,o,i,s=t||this.current;s.isLoaded&&!0!==s.opts.disableLayoutFix&&(s.$content.css("margin-bottom",""),s.$content.outerHeight()>s.$slide.height()+.5&&(o=s.$slide[0].style["padding-bottom"],i=s.$slide.css("padding-bottom"),parseFloat(i)>0&&(e=s.$slide[0].scrollHeight,s.$slide.css("padding-bottom",0),Math.abs(e-s.$slide[0].scrollHeight)<1&&(n=i),s.$slide.css("padding-bottom",o))),s.$content.css("margin-bottom",n))},revealContent:function(t){var e,o,i,s,a=this,r=t.$slide,c=!1,l=!1,h=a.isMoved(t),d=t.isRevealed;return t.isRevealed=!0,e=t.opts[a.firstRun?"animationEffect":"transitionEffect"],i=t.opts[a.firstRun?"animationDuration":"transitionDuration"],i=parseInt(void 0===t.forcedDuration?i:t.forcedDuration,10),!h&&t.pos===a.currPos&&i||(e=!1),"zoom"===e&&(t.pos===a.currPos&&i&&"image"===t.type&&!t.hasError&&(l=a.getThumbPos(t))?c=a.getFitPos(t):e="fade"),"zoom"===e?(a.isAnimating=!0,c.scaleX=c.width/l.width,c.scaleY=c.height/l.height,"auto"==(s=t.opts.zoomOpacity)&&(s=Math.abs(t.width/t.height-l.width/l.height)>.1),s&&(l.opacity=.1,c.opacity=1),n.fancybox.setTranslate(t.$content.removeClass("fancybox-is-hidden"),l),f(t.$content),void n.fancybox.animate(t.$content,c,i,function(){a.isAnimating=!1,a.complete()})):(a.updateSlide(t),e?(n.fancybox.stop(r),o="fancybox-slide--"+(t.pos>=a.prevPos?"next":"previous")+" fancybox-animated fancybox-fx-"+e,r.addClass(o).removeClass("fancybox-slide--current"),t.$content.removeClass("fancybox-is-hidden"),f(r),"image"!==t.type&&t.$content.hide().show(0),void n.fancybox.animate(r,"fancybox-slide--current",i,function(){r.removeClass(o).css({transform:"",opacity:""}),t.pos===a.currPos&&a.complete()},!0)):(t.$content.removeClass("fancybox-is-hidden"),d||!h||"image"!==t.type||t.hasError||t.$content.hide().fadeIn("fast"),void(t.pos===a.currPos&&a.complete())))},getThumbPos:function(t){var o,i,s,a,r,c,l=t.$thumb;return!(!l||!function(t){var o,i;return!(!t||t.ownerDocument!==e)&&(n(".fancybox-container").css("pointer-events","none"),o={x:t.getBoundingClientRect().left+t.offsetWidth/2,y:t.getBoundingClientRect().top+t.offsetHeight/2},i=e.elementFromPoint(o.x,o.y)===t,n(".fancybox-container").css("pointer-events",""),i)}(l[0]))&&(i=n.fancybox.getTranslate(l),s=parseFloat(l.css("border-top-width")||0),a=parseFloat(l.css("border-right-width")||0),r=parseFloat(l.css("border-bottom-width")||0),c=parseFloat(l.css("border-left-width")||0),o={top:i.top+s,left:i.left+c,width:i.width-a-c,height:i.height-s-r,scaleX:1,scaleY:1},i.width>0&&i.height>0&&o)},complete:function(){var t,e=this,o=e.current,i={};!e.isMoved()&&o.isLoaded&&(o.isComplete||(o.isComplete=!0,o.$slide.siblings().trigger("onReset"),e.preload("inline"),f(o.$slide),o.$slide.addClass("fancybox-slide--complete"),n.each(e.slides,function(t,o){o.pos>=e.currPos-1&&o.pos<=e.currPos+1?i[o.pos]=o:o&&(n.fancybox.stop(o.$slide),o.$slide.off().remove())}),e.slides=i),e.isAnimating=!1,e.updateCursor(),e.trigger("afterShow"),o.opts.video.autoStart&&o.$slide.find("video,audio").filter(":visible:first").trigger("play").one("ended",function(){Document.exitFullscreen?Document.exitFullscreen():this.webkitExitFullscreen&&this.webkitExitFullscreen(),e.next()}),o.opts.autoFocus&&"html"===o.contentType&&((t=o.$content.find("input[autofocus]:enabled:visible:first")).length?t.trigger("focus"):e.focus(null,!0)),o.$slide.scrollTop(0).scrollLeft(0))},preload:function(t){var e,n;this.group.length<2||(n=this.slides[this.currPos+1],(e=this.slides[this.currPos-1])&&e.type===t&&this.loadSlide(e),n&&n.type===t&&this.loadSlide(n))},focus:function(t,o){var i,s,a=["a[href]","area[href]",'input:not([disabled]):not([type="hidden"]):not([aria-hidden])',"select:not([disabled]):not([aria-hidden])","textarea:not([disabled]):not([aria-hidden])","button:not([disabled]):not([aria-hidden])","iframe","object","embed","video","audio","[contenteditable]",'[tabindex]:not([tabindex^="-"])'].join(",");this.isClosing||((i=(i=!t&&this.current&&this.current.isComplete?this.current.$slide.find("*:visible"+(o?":not(.fancybox-close-small)":"")):this.$refs.container.find("*:visible")).filter(a).filter(function(){return"hidden"!==n(this).css("visibility")&&!n(this).hasClass("disabled")})).length?(s=i.index(e.activeElement),t&&t.shiftKey?(s<0||0==s)&&(t.preventDefault(),i.eq(i.length-1).trigger("focus")):(s<0||s==i.length-1)&&(t&&t.preventDefault(),i.eq(0).trigger("focus"))):this.$refs.container.trigger("focus"))},activate:function(){var t=this;n(".fancybox-container").each(function(){var e=n(this).data("FancyBox");e&&e.id!==t.id&&!e.isClosing&&(e.trigger("onDeactivate"),e.removeEvents(),e.isVisible=!1)}),t.isVisible=!0,(t.current||t.isIdle)&&(t.update(),t.updateControls()),t.trigger("onActivate"),t.addEvents()},close:function(t,e){var o,i,s,a,r,c,l,d=this,u=d.current,p=function(){d.cleanUp(t)};return!d.isClosing&&(d.isClosing=!0,!1===d.trigger("beforeClose",t)?(d.isClosing=!1,h(function(){d.update()}),!1):(d.removeEvents(),s=u.$content,o=u.opts.animationEffect,i=n.isNumeric(e)?e:o?u.opts.animationDuration:0,u.$slide.removeClass("fancybox-slide--complete fancybox-slide--next fancybox-slide--previous fancybox-animated"),!0!==t?n.fancybox.stop(u.$slide):o=!1,u.$slide.siblings().trigger("onReset").remove(),i&&d.$refs.container.removeClass("fancybox-is-open").addClass("fancybox-is-closing").css("transition-duration",i+"ms"),d.hideLoading(u),d.hideControls(!0),d.updateCursor(),"zoom"!==o||s&&i&&"image"===u.type&&!d.isMoved()&&!u.hasError&&(l=d.getThumbPos(u))||(o="fade"),"zoom"===o?(n.fancybox.stop(s),c={top:(a=n.fancybox.getTranslate(s)).top,left:a.left,scaleX:a.width/l.width,scaleY:a.height/l.height,width:l.width,height:l.height},"auto"==(r=u.opts.zoomOpacity)&&(r=Math.abs(u.width/u.height-l.width/l.height)>.1),r&&(l.opacity=0),n.fancybox.setTranslate(s,c),f(s),n.fancybox.animate(s,l,i,p),!0):(o&&i?n.fancybox.animate(u.$slide.addClass("fancybox-slide--previous").removeClass("fancybox-slide--current"),"fancybox-animated fancybox-fx-"+o,i,p):!0===t?setTimeout(p,i):p(),!0)))},cleanUp:function(e){var o,i,s,a=this.current.opts.$orig;this.current.$slide.trigger("onReset"),this.$refs.container.empty().remove(),this.trigger("afterClose",e),this.current.opts.backFocus&&(a&&a.length&&a.is(":visible")||(a=this.$trigger),a&&a.length&&(i=t.scrollX,s=t.scrollY,a.trigger("focus"),n("html, body").scrollTop(s).scrollLeft(i))),this.current=null,(o=n.fancybox.getInstance())?o.activate():(n("body").removeClass("fancybox-active compensate-for-scrollbar"),n("#fancybox-style-noscroll").remove())},trigger:function(t,e){var o,i=Array.prototype.slice.call(arguments,1),s=e&&e.opts?e:this.current;if(s?i.unshift(s):s=this,i.unshift(this),n.isFunction(s.opts[t])&&(o=s.opts[t].apply(s,i)),!1===o)return o;"afterClose"!==t&&this.$refs?this.$refs.container.trigger(t+".fb",i):c.trigger(t+".fb",i)},updateControls:function(){var t=this.current,o=t.index,i=this.$refs.container,s=this.$refs.caption,a=t.opts.caption;t.$slide.trigger("refresh"),a&&a.length?(this.$caption=s,s.children().eq(0).html(a)):this.$caption=null,this.hasHiddenControls||this.isIdle||this.showControls(),i.find("[data-fancybox-count]").html(this.group.length),i.find("[data-fancybox-index]").html(o+1),i.find("[data-fancybox-prev]").prop("disabled",!t.opts.loop&&o<=0),i.find("[data-fancybox-next]").prop("disabled",!t.opts.loop&&o>=this.group.length-1),"image"===t.type?i.find("[data-fancybox-zoom]").show().end().find("[data-fancybox-download]").attr("href",t.opts.image.src||t.src).show():t.opts.toolbar&&i.find("[data-fancybox-download],[data-fancybox-zoom]").hide(),n(e.activeElement).is(":hidden,[disabled]")&&this.$refs.container.trigger("focus")},hideControls:function(t){var e=["infobar","toolbar","nav"];!t&&this.current.opts.preventCaptionOverlap||e.push("caption"),this.$refs.container.removeClass(e.map(function(t){return"fancybox-show-"+t}).join(" ")),this.hasHiddenControls=!0},showControls:function(){var t=this.current?this.current.opts:this.opts,e=this.$refs.container;this.hasHiddenControls=!1,this.idleSecondsCounter=0,e.toggleClass("fancybox-show-toolbar",!(!t.toolbar||!t.buttons)).toggleClass("fancybox-show-infobar",!!(t.infobar&&this.group.length>1)).toggleClass("fancybox-show-caption",!!this.$caption).toggleClass("fancybox-show-nav",!!(t.arrows&&this.group.length>1)).toggleClass("fancybox-is-modal",!!t.modal)},toggleControls:function(){this.hasHiddenControls?this.showControls():this.hideControls()}}),n.fancybox={version:"3.5.7",defaults:a,getInstance:function(t){var e=n('.fancybox-container:not(".fancybox-is-closing"):last').data("FancyBox"),o=Array.prototype.slice.call(arguments,1);return e instanceof g&&("string"===n.type(t)?e[t].apply(e,o):"function"===n.type(t)&&t.apply(e,o),e)},open:function(t,e,n){return new g(t,e,n)},close:function(t){var e=this.getInstance();e&&(e.close(),!0===t&&this.close(t))},destroy:function(){this.close(!0),c.add("body").off("click.fb-start","**")},isMobile:/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent),use3d:(i=e.createElement("div"),t.getComputedStyle&&t.getComputedStyle(i)&&t.getComputedStyle(i).getPropertyValue("transform")&&!(e.documentMode&&e.documentMode<11)),getTranslate:function(t){var e;return!(!t||!t.length)&&{top:(e=t[0].getBoundingClientRect()).top||0,left:e.left||0,width:e.width,height:e.height,opacity:parseFloat(t.css("opacity"))}},setTranslate:function(t,e){var n="",o={};if(t&&e)return void 0===e.left&&void 0===e.top||(n=(void 0===e.left?t.position().left:e.left)+"px, "+(void 0===e.top?t.position().top:e.top)+"px",n=this.use3d?"translate3d("+n+", 0px)":"translate("+n+")"),void 0!==e.scaleX&&void 0!==e.scaleY?n+=" scale("+e.scaleX+", "+e.scaleY+")":void 0!==e.scaleX&&(n+=" scaleX("+e.scaleX+")"),n.length&&(o.transform=n),void 0!==e.opacity&&(o.opacity=e.opacity),void 0!==e.width&&(o.width=e.width),void 0!==e.height&&(o.height=e.height),t.css(o)},animate:function(t,e,o,i,s){var a,r=this;n.isFunction(o)&&(i=o,o=null),r.stop(t),a=r.getTranslate(t),t.on(u,function(c){(!c||!c.originalEvent||t.is(c.originalEvent.target)&&"z-index"!=c.originalEvent.propertyName)&&(r.stop(t),n.isNumeric(o)&&t.css("transition-duration",""),n.isPlainObject(e)?void 0!==e.scaleX&&void 0!==e.scaleY&&r.setTranslate(t,{top:e.top,left:e.left,width:a.width*e.scaleX,height:a.height*e.scaleY,scaleX:1,scaleY:1}):!0!==s&&t.removeClass(e),n.isFunction(i)&&i(c))}),n.isNumeric(o)&&t.css("transition-duration",o+"ms"),n.isPlainObject(e)?(void 0!==e.scaleX&&void 0!==e.scaleY&&(delete e.width,delete e.height,t.parent().hasClass("fancybox-slide--image")&&t.parent().addClass("fancybox-is-scaling")),n.fancybox.setTranslate(t,e)):t.addClass(e),t.data("timer",setTimeout(function(){t.trigger(u)},o+33))},stop:function(t,e){t&&t.length&&(clearTimeout(t.data("timer")),e&&t.trigger(u),t.off(u).css("transition-duration",""),t.parent().removeClass("fancybox-is-scaling"))}},n.fn.fancybox=function(t){var e;return(e=(t=t||{}).selector||!1)?n("body").off("click.fb-start",e).on("click.fb-start",e,{options:t},b):this.off("click.fb-start").on("click.fb-start",{items:this,options:t},b),this},c.on("click.fb-start","[data-fancybox]",b),c.on("click.fb-start","[data-fancybox-trigger]",function(t){n('[data-fancybox="'+n(this).attr("data-fancybox-trigger")+'"]').eq(n(this).attr("data-fancybox-index")||0).trigger("click.fb-start",{$trigger:n(this)})}),s=null,c.on("mousedown mouseup focus blur",".fancybox-button",function(t){switch(t.type){case"mousedown":s=n(this);break;case"mouseup":s=null;break;case"focusin":n(".fancybox-button").removeClass("fancybox-focus"),n(this).is(s)||n(this).is("[disabled]")||n(this).addClass("fancybox-focus");break;case"focusout":n(".fancybox-button").removeClass("fancybox-focus")}})}function b(t,e){var o,i,s,a=[],r=0;t&&t.isDefaultPrevented()||(t.preventDefault(),e=e||{},t&&t.data&&(e=p(t.data.options,e)),o=e.$target||n(t.currentTarget).trigger("blur"),(s=n.fancybox.getInstance())&&s.$trigger&&s.$trigger.is(o)||(a=e.selector?n(e.selector):(i=o.attr("data-fancybox")||"")?(a=t.data?t.data.items:[]).length?a.filter('[data-fancybox="'+i+'"]'):n('[data-fancybox="'+i+'"]'):[o],(r=n(a).index(o))<0&&(r=0),(s=n.fancybox.open(a,e,r)).$trigger=o))}}(window,document,jQuery),function(t){"use strict";var e={youtube:{matcher:/(youtube\.com|youtu\.be|youtube\-nocookie\.com)\/(watch\?(.*&)?v=|v\/|u\/|embed\/?)?(videoseries\?list=(.*)|[\w-]{11}|\?listType=(.*)&list=(.*))(.*)/i,params:{autoplay:1,autohide:1,fs:1,rel:0,hd:1,wmode:"transparent",enablejsapi:1,html5:1},paramPlace:8,type:"iframe",url:"https://www.youtube-nocookie.com/embed/$4",thumb:"https://img.youtube.com/vi/$4/hqdefault.jpg"},vimeo:{matcher:/^.+vimeo.com\/(.*\/)?([\d]+)(.*)?/,params:{autoplay:1,hd:1,show_title:1,show_byline:1,show_portrait:0,fullscreen:1},paramPlace:3,type:"iframe",url:"//player.vimeo.com/video/$2"},rutube:{matcher:/^.+rutube.ru\/.*\/(.*\/)?([\w]+)(.*)\/?/,params:{frameBorder:0},paramPlace:1,type:"iframe",url:"//rutube.ru/play/embed/$2",thumb:"https://rutube.ru/api/video/$2/thumbnail/?redirect=1"},vk:{matcher:/^.+(vk\.com|vk\.ru|vkvideo\.ru).+video(?:\?z=video|)(-?\d+)_(\d+)/,params:{autoplay:1,hd:2,js_api:1,frameBorder:0},paramPlace:1,type:"iframe",url:"https://vk.ru/video_ext.php?oid=$2&id=$3"},instagram:{matcher:/(instagr\.am|instagram\.com)\/p\/([a-zA-Z0-9_\-]+)\/?/i,type:"image",url:"//$1/p/$2/media/?size=l"},gmap_place:{matcher:/(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(((maps\/(place\/(.*)\/)?\@(.*),(\d+.?\d+?)z))|(\?ll=))(.*)?/i,type:"iframe",url:function(t){return"//maps.google."+t[2]+"/?ll="+(t[9]?t[9]+"&z="+Math.floor(t[10])+(t[12]?t[12].replace(/^\//,"&"):""):t[12]+"").replace(/\?/,"&")+"&output="+(t[12]&&t[12].indexOf("layer=c")>0?"svembed":"embed")}},gmap_search:{matcher:/(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(maps\/search\/)(.*)/i,type:"iframe",url:function(t){return"//maps.google."+t[2]+"/maps?q="+t[5].replace("query=","q=").replace("api=1","")+"&output=embed"}}},n=function(e,n,o){if(e)return o=o||"","object"===t.type(o)&&(o=t.param(o,!0)),t.each(n,function(t,n){e=e.replace("$"+t,n||"")}),o.length&&(e+=(e.indexOf("?")>0?"&":"?")+o),e};t(document).on("objectNeedsType.fb",function(o,i,s){var a,r,c,l,h,d,u,f=s.src||"",p=!1;a=t.extend(!0,{},e,s.opts.media),t.each(a,function(e,o){if(c=f.match(o.matcher)){if(p=o.type,u=e,d={},o.paramPlace&&c[o.paramPlace]){"?"==(h=c[o.paramPlace])[0]&&(h=h.substring(1)),h=h.split("&");for(var i=0;i<h.length;++i){var a=h[i].split("=",2);2==a.length&&(d[a[0]]=decodeURIComponent(a[1].replace(/\+/g," ")))}}return l=t.extend(!0,{},o.params,s.opts[e],d),f="function"===t.type(o.url)?o.url.call(this,c,l,s):n(o.url,c,l),r="function"===t.type(o.thumb)?o.thumb.call(this,c,l,s):n(o.thumb,c),"youtube"===e?f=f.replace(/&t=((\d+)m)?(\d+)s/,function(t,e,n,o){return"&start="+((n?60*parseInt(n,10):0)+parseInt(o,10))}):"vimeo"===e?f=f.replace("&%23","#"):"rutube"===e&&(f=f.replace("&%23","#")),!1}}),p?(s.opts.thumb||s.opts.$thumb&&s.opts.$thumb.length||(s.opts.thumb=r),"iframe"===p&&(s.opts=t.extend(!0,s.opts,{iframe:{preload:!1,attr:{scrolling:"no"}}})),t.extend(s,{type:p,src:f,origSrc:s.src,contentSource:u,contentType:"image"===p?"image":"gmap_place"==u||"gmap_search"==u?"map":"video"})):f&&(s.type=s.opts.defaultType)});var o={youtube:{src:"https://www.youtube.com/iframe_api",class:"YT",loading:!1,loaded:!1},vimeo:{src:"https://player.vimeo.com/api/player.js",class:"Vimeo",loading:!1,loaded:!1},rutube:{src:"https://cdn.jsdelivr.net/gh/evikza/rutube-player@178175923bd50cc759e2c3055c6b502e56b54e92/dist/fb.rt.js",class:"Rutube",loading:!1,loaded:!1},vk:{src:"https://vk.com/js/api/videoplayer.js",class:"VK",loading:!1,loaded:!1},load:function(t){var e,n=this;this[t].loaded?setTimeout(function(){n.done(t)}):this[t].loading||(this[t].loading=!0,(e=document.createElement("script")).type="text/javascript",e.src=this[t].src,"youtube"===t?window.onYouTubeIframeAPIReady=function(){n[t].loaded=!0,n.done(t)}:e.onload=function(){n[t].loaded=!0,n.done(t)},document.body.appendChild(e))},done:function(e){var n,o,i;"youtube"===e&&delete window.onYouTubeIframeAPIReady,(n=t.fancybox.getInstance())&&(o=n.current.$content.find("iframe"),"youtube"===e&&void 0!==YT&&YT?i=new YT.Player(o.attr("id"),{events:{onStateChange:function(t){0==t.data&&n.next()}}}):"vimeo"===e&&void 0!==Vimeo&&Vimeo?(i=new Vimeo.Player(o)).on("ended",function(){n.next()}):"rutube"===e?(i=new Rutube).Player(o.attr("id"),{events:{onReady:function(){setTimeout(i.play(),700)},onStateChange:function(t){t.playerState.ENDED&&n.next()}}}):"vk"===e&&(i=VK.VideoPlayer(o[0])).on("ended",function(){n.next()}))}};t(document).on({"afterShow.fb":function(t,e,n){e.group.length>1&&("youtube"===n.contentSource||"vimeo"===n.contentSource||"rutube"===n.contentSource||"vk"===n.contentSource)?o.load(n.contentSource):"rutube"===n.contentSource&&o.load(n.contentSource)}})}(jQuery),function(t,e,n){"use strict";var o=t.requestAnimationFrame||t.webkitRequestAnimationFrame||t.mozRequestAnimationFrame||t.oRequestAnimationFrame||function(e){return t.setTimeout(e,1e3/60)},i=t.cancelAnimationFrame||t.webkitCancelAnimationFrame||t.mozCancelAnimationFrame||t.oCancelAnimationFrame||function(e){t.clearTimeout(e)},s=function(e){var n=[];for(var o in e=(e=e.originalEvent||e||t.e).touches&&e.touches.length?e.touches:e.changedTouches&&e.changedTouches.length?e.changedTouches:[e])e[o].pageX?n.push({x:e[o].pageX,y:e[o].pageY}):e[o].clientX&&n.push({x:e[o].clientX,y:e[o].clientY});return n},a=function(t,e,n){return e&&t?"x"===n?t.x-e.x:"y"===n?t.y-e.y:Math.sqrt(Math.pow(t.x-e.x,2)+Math.pow(t.y-e.y,2)):0},r=function(t){if(t.is('a,area,button,[role="button"],input,label,select,summary,textarea,video,audio,iframe')||n.isFunction(t.get(0).onclick)||t.data("selectable"))return!0;for(var e=0,o=t[0].attributes,i=o.length;e<i;e++)if("data-fancybox-"===o[e].nodeName.substr(0,14))return!0;return!1},c=function(e){for(var n,o,i,s,a,r=!1;n=e.get(0),o=void 0,i=void 0,s=void 0,a=void 0,o=t.getComputedStyle(n)["overflow-y"],i=t.getComputedStyle(n)["overflow-x"],s=("scroll"===o||"auto"===o)&&n.scrollHeight>n.clientHeight,a=("scroll"===i||"auto"===i)&&n.scrollWidth>n.clientWidth,!(r=s||a)&&(e=e.parent()).length&&!e.hasClass("fancybox-stage")&&!e.is("body"););return r},l=function(t){this.instance=t,this.$bg=t.$refs.bg,this.$stage=t.$refs.stage,this.$container=t.$refs.container,this.destroy(),this.$container.on("touchstart.fb.touch mousedown.fb.touch",n.proxy(this,"ontouchstart"))};l.prototype.destroy=function(){this.$container.off(".fb.touch"),n(e).off(".fb.touch"),this.requestId&&(i(this.requestId),this.requestId=null),this.tapped&&(clearTimeout(this.tapped),this.tapped=null)},l.prototype.ontouchstart=function(o){var i=n(o.target),l=this.instance,h=l.current,d=h.$slide,u=h.$content,f="touchstart"==o.type;if(f&&this.$container.off("mousedown.fb.touch"),(!o.originalEvent||2!=o.originalEvent.button)&&d.length&&i.length&&!r(i)&&!r(i.parent())&&(i.is("img")||!(o.originalEvent.clientX>i[0].clientWidth+i.offset().left))){if(!h||l.isAnimating||h.$slide.hasClass("fancybox-animated"))return o.stopPropagation(),void o.preventDefault();this.realPoints=this.startPoints=s(o),this.startPoints.length&&(h.touch&&o.stopPropagation(),this.startEvent=o,this.canTap=!0,this.$target=i,this.$content=u,this.opts=h.opts.touch,this.isPanning=!1,this.isSwiping=!1,this.isZooming=!1,this.isScrolling=!1,this.canPan=l.canPan(),this.startTime=(new Date).getTime(),this.distanceX=this.distanceY=this.distance=0,this.canvasWidth=Math.round(d[0].clientWidth),this.canvasHeight=Math.round(d[0].clientHeight),this.contentLastPos=null,this.contentStartPos=n.fancybox.getTranslate(this.$content)||{top:0,left:0},this.sliderStartPos=n.fancybox.getTranslate(d),this.stagePos=n.fancybox.getTranslate(l.$refs.stage),this.sliderStartPos.top-=this.stagePos.top,this.sliderStartPos.left-=this.stagePos.left,this.contentStartPos.top-=this.stagePos.top,this.contentStartPos.left-=this.stagePos.left,n(e).off(".fb.touch").on(f?"touchend.fb.touch touchcancel.fb.touch":"mouseup.fb.touch mouseleave.fb.touch",n.proxy(this,"ontouchend")).on(f?"touchmove.fb.touch":"mousemove.fb.touch",n.proxy(this,"ontouchmove")),n.fancybox.isMobile&&e.addEventListener("scroll",this.onscroll,!0),((this.opts||this.canPan)&&(i.is(this.$stage)||this.$stage.find(i).length)||(i.is(".fancybox-image")&&o.preventDefault(),n.fancybox.isMobile&&i.parents(".fancybox-caption").length))&&(this.isScrollable=c(i)||c(i.parent()),n.fancybox.isMobile&&this.isScrollable||o.preventDefault(),(1===this.startPoints.length||h.hasError)&&(this.canPan?(n.fancybox.stop(this.$content),this.isPanning=!0):this.isSwiping=!0,this.$container.addClass("fancybox-is-grabbing")),2===this.startPoints.length&&"image"===h.type&&(h.isLoaded||h.$ghost)&&(this.canTap=!1,this.isSwiping=!1,this.isPanning=!1,this.isZooming=!0,n.fancybox.stop(this.$content),this.centerPointStartX=.5*(this.startPoints[0].x+this.startPoints[1].x)-n(t).scrollLeft(),this.centerPointStartY=.5*(this.startPoints[0].y+this.startPoints[1].y)-n(t).scrollTop(),this.percentageOfImageAtPinchPointX=(this.centerPointStartX-this.contentStartPos.left)/this.contentStartPos.width,this.percentageOfImageAtPinchPointY=(this.centerPointStartY-this.contentStartPos.top)/this.contentStartPos.height,this.startDistanceBetweenFingers=a(this.startPoints[0],this.startPoints[1]))))}},l.prototype.onscroll=function(t){this.isScrolling=!0,e.removeEventListener("scroll",this.onscroll,!0)},l.prototype.ontouchmove=function(t){void 0===t.originalEvent.buttons||0!==t.originalEvent.buttons?this.isScrolling?this.canTap=!1:(this.newPoints=s(t),(this.opts||this.canPan)&&this.newPoints.length&&this.newPoints.length&&(this.isSwiping&&!0===this.isSwiping||t.preventDefault(),this.distanceX=a(this.newPoints[0],this.startPoints[0],"x"),this.distanceY=a(this.newPoints[0],this.startPoints[0],"y"),this.distance=a(this.newPoints[0],this.startPoints[0]),this.distance>0&&(this.isSwiping?this.onSwipe(t):this.isPanning?this.onPan():this.isZooming&&this.onZoom()))):this.ontouchend(t)},l.prototype.onSwipe=function(e){var s,a=this,r=a.instance,c=a.isSwiping,l=a.sliderStartPos.left||0;if(!0!==c)"x"==c&&(a.distanceX>0&&(a.instance.group.length<2||0===a.instance.current.index&&!a.instance.current.opts.loop)?l+=Math.pow(a.distanceX,.8):a.distanceX<0&&(a.instance.group.length<2||a.instance.current.index===a.instance.group.length-1&&!a.instance.current.opts.loop)?l-=Math.pow(-a.distanceX,.8):l+=a.distanceX),a.sliderLastPos={top:"x"==c?0:a.sliderStartPos.top+a.distanceY,left:l},a.requestId&&(i(a.requestId),a.requestId=null),a.requestId=o(function(){a.sliderLastPos&&(n.each(a.instance.slides,function(t,e){var o=e.pos-a.instance.currPos;n.fancybox.setTranslate(e.$slide,{top:a.sliderLastPos.top,left:a.sliderLastPos.left+o*a.canvasWidth+o*e.opts.gutter})}),a.$container.addClass("fancybox-is-sliding"))});else if(Math.abs(a.distance)>10){if(a.canTap=!1,r.group.length<2&&a.opts.vertical?a.isSwiping="y":r.isDragging||!1===a.opts.vertical||"auto"===a.opts.vertical&&n(t).width()>800?a.isSwiping="x":(s=Math.abs(180*Math.atan2(a.distanceY,a.distanceX)/Math.PI),a.isSwiping=s>45&&s<135?"y":"x"),"y"===a.isSwiping&&n.fancybox.isMobile&&a.isScrollable)return void(a.isScrolling=!0);r.isDragging=a.isSwiping,a.startPoints=a.newPoints,n.each(r.slides,function(t,e){var o,i;n.fancybox.stop(e.$slide),o=n.fancybox.getTranslate(e.$slide),i=n.fancybox.getTranslate(r.$refs.stage),e.$slide.css({transform:"",opacity:"","transition-duration":""}).removeClass("fancybox-animated").removeClass(function(t,e){return(e.match(/(^|\s)fancybox-fx-\S+/g)||[]).join(" ")}),e.pos===r.current.pos&&(a.sliderStartPos.top=o.top-i.top,a.sliderStartPos.left=o.left-i.left),n.fancybox.setTranslate(e.$slide,{top:o.top-i.top,left:o.left-i.left})}),r.SlideShow&&r.SlideShow.isActive&&r.SlideShow.stop()}},l.prototype.onPan=function(){var t=this;a(t.newPoints[0],t.realPoints[0])<(n.fancybox.isMobile?10:5)?t.startPoints=t.newPoints:(t.canTap=!1,t.contentLastPos=t.limitMovement(),t.requestId&&i(t.requestId),t.requestId=o(function(){n.fancybox.setTranslate(t.$content,t.contentLastPos)}))},l.prototype.limitMovement=function(){var t,e,n,o,i,s,a=this.canvasWidth,r=this.canvasHeight,c=this.distanceX,l=this.distanceY,h=this.contentStartPos,d=h.left,u=h.top,f=h.width,p=h.height;return i=f>a?d+c:d,s=u+l,t=Math.max(0,.5*a-.5*f),e=Math.max(0,.5*r-.5*p),n=Math.min(a-f,.5*a-.5*f),o=Math.min(r-p,.5*r-.5*p),c>0&&i>t&&(i=t-1+Math.pow(-t+d+c,.8)||0),c<0&&i<n&&(i=n+1-Math.pow(n-d-c,.8)||0),l>0&&s>e&&(s=e-1+Math.pow(-e+u+l,.8)||0),l<0&&s<o&&(s=o+1-Math.pow(o-u-l,.8)||0),{top:s,left:i}},l.prototype.limitPosition=function(t,e,n,o){var i=this.canvasWidth,s=this.canvasHeight;return t=n>i?(t=t>0?0:t)<i-n?i-n:t:Math.max(0,i/2-n/2),{top:e=o>s?(e=e>0?0:e)<s-o?s-o:e:Math.max(0,s/2-o/2),left:t}},l.prototype.onZoom=function(){var e=this,s=e.contentStartPos,r=s.width,c=s.height,l=s.left,h=s.top,d=a(e.newPoints[0],e.newPoints[1])/e.startDistanceBetweenFingers,u=Math.floor(r*d),f=Math.floor(c*d),p=(r-u)*e.percentageOfImageAtPinchPointX,g=(c-f)*e.percentageOfImageAtPinchPointY,b=(e.newPoints[0].x+e.newPoints[1].x)/2-n(t).scrollLeft(),m=(e.newPoints[0].y+e.newPoints[1].y)/2-n(t).scrollTop(),v=b-e.centerPointStartX,y={top:h+(g+(m-e.centerPointStartY)),left:l+(p+v),scaleX:d,scaleY:d};e.canTap=!1,e.newWidth=u,e.newHeight=f,e.contentLastPos=y,e.requestId&&i(e.requestId),e.requestId=o(function(){n.fancybox.setTranslate(e.$content,e.contentLastPos)})},l.prototype.ontouchend=function(t){var o=this.isSwiping,a=this.isPanning,r=this.isZooming,c=this.isScrolling;if(this.endPoints=s(t),this.dMs=Math.max((new Date).getTime()-this.startTime,1),this.$container.removeClass("fancybox-is-grabbing"),n(e).off(".fb.touch"),e.removeEventListener("scroll",this.onscroll,!0),this.requestId&&(i(this.requestId),this.requestId=null),this.isSwiping=!1,this.isPanning=!1,this.isZooming=!1,this.isScrolling=!1,this.instance.isDragging=!1,this.canTap)return this.onTap(t);this.speed=100,this.velocityX=this.distanceX/this.dMs*.5,this.velocityY=this.distanceY/this.dMs*.5,a?this.endPanning():r?this.endZooming():this.endSwiping(o,c)},l.prototype.endSwiping=function(t,e){var o=!1,i=this.instance.group.length,s=Math.abs(this.distanceX),a="x"==t&&i>1&&(this.dMs>130&&s>10||s>50);this.sliderLastPos=null,"y"==t&&!e&&Math.abs(this.distanceY)>50?(n.fancybox.animate(this.instance.current.$slide,{top:this.sliderStartPos.top+this.distanceY+150*this.velocityY,opacity:0},200),o=this.instance.close(!0,250)):a&&this.distanceX>0?o=this.instance.previous(300):a&&this.distanceX<0&&(o=this.instance.next(300)),!1!==o||"x"!=t&&"y"!=t||this.instance.centerSlide(200),this.$container.removeClass("fancybox-is-sliding")},l.prototype.endPanning=function(){var t,e,o;this.contentLastPos&&(!1===this.opts.momentum||this.dMs>350?(t=this.contentLastPos.left,e=this.contentLastPos.top):(t=this.contentLastPos.left+500*this.velocityX,e=this.contentLastPos.top+500*this.velocityY),(o=this.limitPosition(t,e,this.contentStartPos.width,this.contentStartPos.height)).width=this.contentStartPos.width,o.height=this.contentStartPos.height,n.fancybox.animate(this.$content,o,366))},l.prototype.endZooming=function(){var t,e,o,i,s=this.instance.current,a=this.newWidth,r=this.newHeight;this.contentLastPos&&(t=this.contentLastPos.left,i={top:e=this.contentLastPos.top,left:t,width:a,height:r,scaleX:1,scaleY:1},n.fancybox.setTranslate(this.$content,i),a<this.canvasWidth&&r<this.canvasHeight?this.instance.scaleToFit(150):a>s.width||r>s.height?this.instance.scaleToActual(this.centerPointStartX,this.centerPointStartY,150):(o=this.limitPosition(t,e,a,r),n.fancybox.animate(this.$content,o,150)))},l.prototype.onTap=function(e){var o,i=this,a=n(e.target),r=i.instance,c=r.current,l=e&&s(e)||i.startPoints,h=l[0]?l[0].x-n(t).scrollLeft()-i.stagePos.left:0,d=l[0]?l[0].y-n(t).scrollTop()-i.stagePos.top:0,u=function(t){var o=c.opts[t];if(n.isFunction(o)&&(o=o.apply(r,[c,e])),o)switch(o){case"close":r.close(i.startEvent);break;case"toggleControls":r.toggleControls();break;case"next":r.next();break;case"nextOrClose":r.group.length>1?r.next():r.close(i.startEvent);break;case"zoom":"image"==c.type&&(c.isLoaded||c.$ghost)&&(r.canPan()?r.scaleToFit():r.isScaledDown()?r.scaleToActual(h,d):r.group.length<2&&r.close(i.startEvent))}};if((!e.originalEvent||2!=e.originalEvent.button)&&(a.is("img")||!(h>a[0].clientWidth+a.offset().left))){if(a.is(".fancybox-bg,.fancybox-inner,.fancybox-outer,.fancybox-container"))o="Outside";else if(a.is(".fancybox-slide"))o="Slide";else{if(!r.current.$content||!r.current.$content.find(a).addBack().filter(a).length)return;o="Content"}if(i.tapped){if(clearTimeout(i.tapped),i.tapped=null,Math.abs(h-i.tapX)>50||Math.abs(d-i.tapY)>50)return this;u("dblclick"+o)}else i.tapX=h,i.tapY=d,c.opts["dblclick"+o]&&c.opts["dblclick"+o]!==c.opts["click"+o]?i.tapped=setTimeout(function(){i.tapped=null,r.isAnimating||u("click"+o)},500):u("click"+o);return this}},n(e).on("onActivate.fb",function(t,e){e&&!e.Guestures&&(e.Guestures=new l(e))}).on("beforeClose.fb",function(t,e){e&&e.Guestures&&e.Guestures.destroy()})}(window,document,jQuery),function(t,e){"use strict";e.extend(!0,e.fancybox.defaults,{btnTpl:{slideShow:'<button data-fancybox-play class="fancybox-button fancybox-button--play" title="{{PLAY_START}}"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M6.5 5.4v13.2l11-6.6z"/></svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M8.33 5.75h2.2v12.5h-2.2V5.75zm5.15 0h2.2v12.5h-2.2V5.75z"/></svg></button>'},slideShow:{autoStart:!1,speed:3e3,progress:!0}});var n=function(t){this.instance=t,this.init()};e.extend(n.prototype,{timer:null,isActive:!1,$button:null,init:function(){var t=this,n=t.instance,o=n.group[n.currIndex].opts.slideShow;t.$button=n.$refs.toolbar.find("[data-fancybox-play]").on("click",function(){t.toggle()}),n.group.length<2||!o?t.$button.hide():o.progress&&(t.$progress=e('<div class="fancybox-progress"></div>').appendTo(n.$refs.inner))},set:function(t){var n=this.instance,o=n.current;o&&(!0===t||o.opts.loop||n.currIndex<n.group.length-1)?this.isActive&&"video"!==o.contentType&&(this.$progress&&e.fancybox.animate(this.$progress.show(),{scaleX:1},o.opts.slideShow.speed),this.timer=setTimeout(function(){n.current.opts.loop||n.current.index!=n.group.length-1?n.next():n.jumpTo(0)},o.opts.slideShow.speed)):(this.stop(),n.idleSecondsCounter=0,n.showControls())},clear:function(){clearTimeout(this.timer),this.timer=null,this.$progress&&this.$progress.removeAttr("style").hide()},start:function(){var t=this.instance.current;t&&(this.$button.attr("title",(t.opts.i18n[t.opts.lang]||t.opts.i18n.en).PLAY_STOP).removeClass("fancybox-button--play").addClass("fancybox-button--pause"),this.isActive=!0,t.isComplete&&this.set(!0),this.instance.trigger("onSlideShowChange",!0))},stop:function(){var t=this.instance.current;this.clear(),this.$button.attr("title",(t.opts.i18n[t.opts.lang]||t.opts.i18n.en).PLAY_START).removeClass("fancybox-button--pause").addClass("fancybox-button--play"),this.isActive=!1,this.instance.trigger("onSlideShowChange",!1),this.$progress&&this.$progress.removeAttr("style").hide()},toggle:function(){this.isActive?this.stop():this.start()}}),e(t).on({"onInit.fb":function(t,e){e&&!e.SlideShow&&(e.SlideShow=new n(e))},"beforeShow.fb":function(t,e,n,o){var i=e&&e.SlideShow;o?i&&n.opts.slideShow.autoStart&&i.start():i&&i.isActive&&i.clear()},"afterShow.fb":function(t,e,n){var o=e&&e.SlideShow;o&&o.isActive&&o.set()},"afterKeydown.fb":function(n,o,i,s,a){var r=o&&o.SlideShow;!r||!i.opts.slideShow||80!==a&&32!==a||e(t.activeElement).is("button,a,input")||(s.preventDefault(),r.toggle())},"beforeClose.fb onDeactivate.fb":function(t,e){var n=e&&e.SlideShow;n&&n.stop()}}),e(t).on("visibilitychange",function(){var n=e.fancybox.getInstance(),o=n&&n.SlideShow;o&&o.isActive&&(t.hidden?o.clear():o.set())})}(document,jQuery),function(t,e){"use strict";var n=function(){for(var e=[["requestFullscreen","exitFullscreen","fullscreenElement","fullscreenEnabled","fullscreenchange","fullscreenerror"],["webkitRequestFullscreen","webkitExitFullscreen","webkitFullscreenElement","webkitFullscreenEnabled","webkitfullscreenchange","webkitfullscreenerror"],["webkitRequestFullScreen","webkitCancelFullScreen","webkitCurrentFullScreenElement","webkitCancelFullScreen","webkitfullscreenchange","webkitfullscreenerror"],["mozRequestFullScreen","mozCancelFullScreen","mozFullScreenElement","mozFullScreenEnabled","mozfullscreenchange","mozfullscreenerror"],["msRequestFullscreen","msExitFullscreen","msFullscreenElement","msFullscreenEnabled","MSFullscreenChange","MSFullscreenError"]],n={},o=0;o<e.length;o++){var i=e[o];if(i&&i[1]in t){for(var s=0;s<i.length;s++)n[e[0][s]]=i[s];return n}}return!1}();if(n){var o={request:function(e){(e=e||t.documentElement)[n.requestFullscreen](e.ALLOW_KEYBOARD_INPUT)},exit:function(){t[n.exitFullscreen]()},toggle:function(e){e=e||t.documentElement,this.isFullscreen()?this.exit():this.request(e)},isFullscreen:function(){return Boolean(t[n.fullscreenElement])},enabled:function(){return Boolean(t[n.fullscreenEnabled])}};e.extend(!0,e.fancybox.defaults,{btnTpl:{fullScreen:'<button data-fancybox-fullscreen class="fancybox-button fancybox-button--fsenter" title="{{FULL_SCREEN}}"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/></svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M5 16h3v3h2v-5H5zm3-8H5v2h5V5H8zm6 11h2v-3h3v-2h-5zm2-11V5h-2v5h5V8z"/></svg></button>'},fullScreen:{autoStart:!1}}),e(t).on(n.fullscreenchange,function(){var t=o.isFullscreen(),n=e.fancybox.getInstance();n&&(n.current&&"image"===n.current.type&&n.isAnimating&&(n.isAnimating=!1,n.update(!0,!0,0),n.isComplete||n.complete()),n.trigger("onFullscreenChange",t),n.$refs.container.toggleClass("fancybox-is-fullscreen",t),n.$refs.toolbar.find("[data-fancybox-fullscreen]").toggleClass("fancybox-button--fsenter",!t).toggleClass("fancybox-button--fsexit",t))})}e(t).on({"onInit.fb":function(t,e){n?e&&e.group[e.currIndex].opts.fullScreen?(e.$refs.container.on("click.fb-fullscreen","[data-fancybox-fullscreen]",function(t){t.stopPropagation(),t.preventDefault(),o.toggle()}),e.opts.fullScreen&&!0===e.opts.fullScreen.autoStart&&o.request(),e.FullScreen=o):e&&e.$refs.toolbar.find("[data-fancybox-fullscreen]").hide():e.$refs.toolbar.find("[data-fancybox-fullscreen]").remove()},"afterKeydown.fb":function(t,e,n,o,i){e&&e.FullScreen&&70===i&&(o.preventDefault(),e.FullScreen.toggle())},"beforeClose.fb":function(t,e){e&&e.FullScreen&&e.$refs.container.hasClass("fancybox-is-fullscreen")&&o.exit()}})}(document,jQuery),function(t,e){"use strict";var n="fancybox-thumbs";e.fancybox.defaults=e.extend(!0,{btnTpl:{thumbs:'<button data-fancybox-thumbs class="fancybox-button fancybox-button--thumbs" title="{{THUMBS}}"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M14.59 14.59h3.76v3.76h-3.76v-3.76zm-4.47 0h3.76v3.76h-3.76v-3.76zm-4.47 0h3.76v3.76H5.65v-3.76zm8.94-4.47h3.76v3.76h-3.76v-3.76zm-4.47 0h3.76v3.76h-3.76v-3.76zm-4.47 0h3.76v3.76H5.65v-3.76zm8.94-4.47h3.76v3.76h-3.76V5.65zm-4.47 0h3.76v3.76h-3.76V5.65zm-4.47 0h3.76v3.76H5.65V5.65z"/></svg></button>'},thumbs:{autoStart:!1,hideOnClose:!0,parentEl:".fancybox-container",axis:"y"}},e.fancybox.defaults);var o=function(t){this.init(t)};e.extend(o.prototype,{$button:null,$grid:null,$list:null,isVisible:!1,isActive:!1,init:function(t){var e=this,n=t.group,o=0;e.instance=t,e.opts=n[t.currIndex].opts.thumbs,t.Thumbs=e,e.$button=t.$refs.toolbar.find("[data-fancybox-thumbs]");for(var i=0,s=n.length;i<s&&(n[i].thumb&&o++,!(o>1));i++);o>1&&e.opts?(e.$button.removeAttr("style").on("click",function(){e.toggle()}),e.isActive=!0):e.$button.hide()},create:function(){var t,o=this.instance,i=this.opts.parentEl,s=[];this.$grid||(this.$grid=e('<div class="'+n+" "+n+"-"+this.opts.axis+'"></div>').appendTo(o.$refs.container.find(i).addBack().filter(i)),this.$grid.on("click","a",function(){o.jumpTo(e(this).attr("data-index"))})),this.$list||(this.$list=e('<div class="'+n+'__list">').appendTo(this.$grid)),e.each(o.group,function(e,n){(t=n.thumb)||"image"!==n.type||(t=n.src),s.push('<a href="javascript:;" tabindex="0" data-index="'+e+'"'+(t&&t.length?' style="background-image:url('+t+')"':'class="fancybox-thumbs-missing"')+"></a>")}),this.$list[0].innerHTML=s.join(""),"x"===this.opts.axis&&this.$list.width(parseInt(this.$grid.css("padding-right"),10)+o.group.length*this.$list.children().eq(0).outerWidth(!0))},focus:function(t){var e,n,o=this.$list,i=this.$grid;this.instance.current&&(n=(e=o.children().removeClass("fancybox-thumbs-active").filter('[data-index="'+this.instance.current.index+'"]').addClass("fancybox-thumbs-active")).position(),"y"===this.opts.axis&&(n.top<0||n.top>o.height()-e.outerHeight())?o.stop().animate({scrollTop:o.scrollTop()+n.top},t):"x"===this.opts.axis&&(n.left<i.scrollLeft()||n.left>i.scrollLeft()+(i.width()-e.outerWidth()))&&o.parent().stop().animate({scrollLeft:n.left},t))},update:function(){this.instance.$refs.container.toggleClass("fancybox-show-thumbs",this.isVisible),this.isVisible?(this.$grid||this.create(),this.instance.trigger("onThumbsShow"),this.focus(0)):this.$grid&&this.instance.trigger("onThumbsHide"),this.instance.update()},hide:function(){this.isVisible=!1,this.update()},show:function(){this.isVisible=!0,this.update()},toggle:function(){this.isVisible=!this.isVisible,this.update()}}),e(t).on({"onInit.fb":function(t,e){var n;e&&!e.Thumbs&&(n=new o(e)).isActive&&!0===n.opts.autoStart&&n.show()},"beforeShow.fb":function(t,e,n,o){var i=e&&e.Thumbs;i&&i.isVisible&&i.focus(o?0:250)},"afterKeydown.fb":function(t,e,n,o,i){var s=e&&e.Thumbs;s&&s.isActive&&71===i&&(o.preventDefault(),s.toggle())},"beforeClose.fb":function(t,e){var n=e&&e.Thumbs;n&&n.isVisible&&!1!==n.opts.hideOnClose&&n.$grid.hide()}})}(document,jQuery),function(t,e,n){"use strict";function o(){var e=t.location.hash.substr(1),n=e.split("-"),o=n.length>1&&/^\+?\d+$/.test(n[n.length-1])&&parseInt(n.pop(-1),10)||1;return{hash:e,index:o<1?1:o,gallery:n.join("-")}}function i(t){""!==t.gallery&&n("[data-fancybox='"+n.escapeSelector(t.gallery)+"']").eq(t.index-1).focus().trigger("click.fb-start")}function s(t){var e,n;return!!t&&(""!==(n=(e=t.current?t.current.opts:t.opts).hash||(e.$orig?e.$orig.data("fancybox")||e.$orig.data("fancybox-trigger"):""))&&n)}n.escapeSelector||(n.escapeSelector=function(t){return(t+"").replace(/([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g,function(t,e){return e?"\0"===t?"�":t.slice(0,-1)+"\\"+t.charCodeAt(t.length-1).toString(16)+" ":"\\"+t})}),n(function(){!1!==n.fancybox.defaults.hash&&(n(e).on({"onInit.fb":function(t,e){var n,i;!1!==e.group[e.currIndex].opts.hash&&(n=o(),(i=s(e))&&n.gallery&&i==n.gallery&&(e.currIndex=n.index-1))},"beforeShow.fb":function(n,o,i,a){var r;i&&!1!==i.opts.hash&&(r=s(o))&&(o.currentHash=r+(o.group.length>1?"-"+(i.index+1):""),t.location.hash!=="#"+o.currentHash&&(a&&!o.origHash&&(o.origHash=t.location.hash),o.hashTimer&&clearTimeout(o.hashTimer),o.hashTimer=setTimeout(function(){"replaceState"in t.history?(t.history[a?"pushState":"replaceState"]({},e.title,t.location.pathname+t.location.search+"#"+o.currentHash),a&&(o.hasCreatedHistory=!0)):t.location.hash=o.currentHash,o.hashTimer=null},300)))},"beforeClose.fb":function(n,o,i){i&&!1!==i.opts.hash&&(clearTimeout(o.hashTimer),o.currentHash&&o.hasCreatedHistory?t.history.back():o.currentHash&&("replaceState"in t.history?t.history.replaceState({},e.title,t.location.pathname+t.location.search+(o.origHash||"")):t.location.hash=o.origHash),o.currentHash=null)}}),n(t).on("hashchange.fb",function(){var t=o(),e=null;n.each(n(".fancybox-container").get().reverse(),function(t,o){var i=n(o).data("FancyBox");if(i&&i.currentHash)return e=i,!1}),e?e.currentHash===t.gallery+"-"+t.index||1===t.index&&e.currentHash==t.gallery||(e.currentHash=null,e.close()):""!==t.gallery&&i(t)}),setTimeout(function(){n.fancybox.getInstance()||i(o())},50))})}(window,document,jQuery),function(t,e){"use strict";var n=(new Date).getTime();e(t).on({"onInit.fb":function(t,e,o){e.$refs.stage.on("mousewheel DOMMouseScroll wheel MozMousePixelScroll",function(t){var o=e.current,i=(new Date).getTime();e.group.length<2||!1===o.opts.wheel||"auto"===o.opts.wheel&&"image"!==o.type||(t.preventDefault(),t.stopPropagation(),o.$slide.hasClass("fancybox-animated")||(t=t.originalEvent||t,i-n<250||(n=i,e[(-t.deltaY||-t.deltaX||t.wheelDelta||-t.detail)<0?"next":"previous"]())))})}})}(document,jQuery); |
@Valentine13 здравствуйте.
Мы же с Вами общались в теме о Rutube на cyberforum? Я там ответил насчет обложки видео. Но, Вы так и не заходили. 😔
@evikza простите ( Я там уже исправилась! Временно появлялась надежда, что всё-таки обойдется без блокировки YouTube), но теперь стало очевидно, что не пронесёт :))) Думаю, ваше решение еще многим поможет! Спасибо еще раз!
Спасибо за сделанную доработку!
Добрый день, а как можно еще добавить поддержку VK Видео?
@Alex4Codes Здравствуйте.
На днях постараюсь добавить такую возможность.
@evikza Здравствуйте!
Я уже решал такую задачу, вот код:
vk: {
matcher: /^.+vk.com\/video([-0-9]*)_([-0-9]*)?/,
params: {
frameBorder: 0,
autoplay: 1,
},
paramPlace: 1,
type: 'iframe',
url: 'https://vk.com/video_ext.php?oid=$1&id=$2',
},
@Djekswon Добрый вечер. Увидел Ваше сообщение, когда уже добавил правки в код. Но, спасибо большое за участие. 😊
@Alex4Codes добавлена поддержка VK Видео.
Здравствуйте! Можно ли настроить автовоспроизведение для rutube?
@levongziryan Здравствуйте. 👋
Теперь работает автоматическое воспроизведение для Rutube.
Так же, если видео находится в галерее data-fancybox="video-gallery" — после окончания происходит запуск следующего видео.
@levongziryan Здравствуйте. 👋
Теперь работает автоматическое воспроизведение для Rutube.
Так же, если видео находится в галерее
data-fancybox="video-gallery"— после окончания происходит запуск следующего видео.
у меня через раз автовоспризведение срабатывает
Здравствуйте, поломалось воспроизведение видео вконтакте (скорее всего, из-за того, что они переехали на vk.ru) Подскажите что в скрипте и как поменять надо. Просто поменть com на ru не сработало - видимо там сам формат ссылок поменялся
Спустя минут 20 нашел сам: (с 3715 строчки)
vk: {
matcher: /^.+(vkvideo.ru).+video(?:\?z=video|)(-?\d+)_(\d+)/,
params: {
autoplay: 1,
hd: 2,
js_api: 1,
frameBorder: 0,
},
paramPlace: 1,
type: 'iframe',
url: 'https://vkvideo.ru/video_ext.php?oid=$2&id=$3',
},
кроме js файла надо подключить стили:
<link href="https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.css" rel="stylesheet">
вызов на странице:
<a data-fancybox href="[ссылка на видео типа https://vkvideo.ru/video-ХХХХХХХХХ_хХХХХХхХХ">
@uhogorlonos Здравствуйте! Спасибо, что опубликовали код. Пригодился 🙏
Обновил файл. Поддерживаются ссылки VK вида:
https://vk.com/username?z=videoXXXXXXXXX_XXXXXXXXX
https://vk.ru/vkvideo?z=video-XXXXXXXXX_XXXXXXXXX
https://vkvideo.ru/video-XXXXXXXXX_XXXXXXXXX
Всю благодарность за этот апдейт для RuTube не передать словами. Автор, вы спасли столько нервных клеток, спасибо!