1// ================================================== 2// fancyBox v3.2.10 3// 4// Licensed GPLv3 for open source use 5// or fancyBox Commercial License for commercial use 6// 7// http://fancyapps.com/fancybox/ 8// Copyright 2017 fancyApps 9// 10// ================================================== 11;(function (window, document, $, undefined) { 12 'use strict'; 13 14 // If there's no jQuery, fancyBox can't work 15 // ========================================= 16 17 if ( !$ ) { 18 return; 19 } 20 21 // Check if fancyBox is already initialized 22 // ======================================== 23 24 if ( $.fn.fancybox ) { 25 26 if ( 'console' in window ) { 27 console.log( 'fancyBox already initialized' ); 28 } 29 30 return; 31 } 32 33 // Private default settings 34 // ======================== 35 36 var defaults = { 37 38 // Enable infinite gallery navigation 39 loop : false, 40 41 // Space around image, ignored if zoomed-in or viewport width is smaller than 800px 42 margin : [44, 0], 43 44 // Horizontal space between slides 45 gutter : 50, 46 47 // Enable keyboard navigation 48 keyboard : true, 49 50 // Should display navigation arrows at the screen edges 51 arrows : true, 52 53 // Should display infobar (counter and arrows at the top) 54 infobar : true, 55 56 // Should display toolbar (buttons at the top) 57 toolbar : true, 58 59 // What buttons should appear in the top right corner. 60 // Buttons will be created using templates from `btnTpl` option 61 // and they will be placed into toolbar (class="fancybox-toolbar"` element) 62 buttons : [ 63 'slideShow', 64 //'fullScreen', 65 //'thumbs', 66 //'share', 67 //'download', 68 'zoom', 69 'close' 70 ], 71 72 // Detect "idle" time in seconds 73 idleTime : 3, 74 75 // Should display buttons at top right corner of the content 76 // If 'auto' - they will be created for content having type 'html', 'inline' or 'ajax' 77 // Use template from `btnTpl.smallBtn` for customization 78 smallBtn : 'auto', 79 80 // Disable right-click and use simple image protection for images 81 protect : false, 82 83 // Shortcut to make content "modal" - disable keyboard navigtion, hide buttons, etc 84 modal : false, 85 86 image : { 87 88 // Wait for images to load before displaying 89 // Requires predefined image dimensions 90 // If 'auto' - will zoom in thumbnail if 'width' and 'height' attributes are found 91 preload : "auto" 92 93 }, 94 95 ajax : { 96 97 // Object containing settings for ajax request 98 settings : { 99 100 // This helps to indicate that request comes from the modal 101 // Feel free to change naming 102 data : { 103 fancybox : true 104 } 105 } 106 107 }, 108 109 iframe : { 110 111 // Iframe template 112 tpl : '<iframe id="fancybox-frame{rnd}" name="fancybox-frame{rnd}" class="fancybox-iframe" frameborder="0" vspace="0" hspace="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen allowtransparency="true" src=""></iframe>', 113 114 // Preload iframe before displaying it 115 // This allows to calculate iframe content width and height 116 // (note: Due to "Same Origin Policy", you can't get cross domain data). 117 preload : true, 118 119 // Custom CSS styling for iframe wrapping element 120 // You can use this to set custom iframe dimensions 121 css : {}, 122 123 // Iframe tag attributes 124 attr : { 125 scrolling : 'auto' 126 } 127 128 }, 129 130 // Default content type if cannot be detected automatically 131 defaultType : 'image', 132 133 // Open/close animation type 134 // Possible values: 135 // false - disable 136 // "zoom" - zoom images from/to thumbnail 137 // "fade" 138 // "zoom-in-out" 139 // 140 animationEffect : "zoom", 141 142 // Duration in ms for open/close animation 143 animationDuration : 500, 144 145 // Should image change opacity while zooming 146 // If opacity is "auto", then opacity will be changed if image and thumbnail have different aspect ratios 147 zoomOpacity : "auto", 148 149 // Transition effect between slides 150 // 151 // Possible values: 152 // false - disable 153 // "fade' 154 // "slide' 155 // "circular' 156 // "tube' 157 // "zoom-in-out' 158 // "rotate' 159 // 160 transitionEffect : "fade", 161 162 // Duration in ms for transition animation 163 transitionDuration : 366, 164 165 // Custom CSS class for slide element 166 slideClass : '', 167 168 // Custom CSS class for layout 169 baseClass : '', 170 171 // Base template for layout 172 baseTpl : 173 '<div class="fancybox-container" role="dialog" tabindex="-1">' + 174 '<div class="fancybox-bg"></div>' + 175 '<div class="fancybox-inner">' + 176 '<div class="fancybox-infobar">' + 177 '<span data-fancybox-index></span> / <span data-fancybox-count></span>' + 178 '</div>' + 179 '<div class="fancybox-toolbar">{{buttons}}</div>' + 180 '<div class="fancybox-navigation">{{arrows}}</div>' + 181 '<div class="fancybox-stage"></div>' + 182 '<div class="fancybox-caption-wrap"><div class="fancybox-caption"></div></div>' + 183 '</div>' + 184 '</div>', 185 186 // Loading indicator template 187 spinnerTpl : '<div class="fancybox-loading"></div>', 188 189 // Error message template 190 errorTpl : '<div class="fancybox-error"><p>{{ERROR}}<p></div>', 191 192 btnTpl : { 193 194 download : '<a download data-fancybox-download class="fancybox-button fancybox-button--download" title="{{DOWNLOAD}}">' + 195 '<svg viewBox="0 0 40 40">' + 196 '<path d="M20,23 L20,8 L20,23 L13,16 L20,23 L27,16 L20,23 M26,28 L13,28 L27,28 L14,28" />' + 197 '</svg>' + 198 '</a>', 199 200 zoom : '<button data-fancybox-zoom class="fancybox-button fancybox-button--zoom" title="{{ZOOM}}">' + 201 '<svg viewBox="0 0 40 40">' + 202 '<path d="M 18,17 m-8,0 a 8,8 0 1,0 16,0 a 8,8 0 1,0 -16,0 M25,23 L31,29 L25,23" />' + 203 '</svg>' + 204 '</button>', 205 206 close : '<button data-fancybox-close class="fancybox-button fancybox-button--close" title="{{CLOSE}}">' + 207 '<svg viewBox="0 0 40 40">' + 208 '<path d="M10,10 L30,30 M30,10 L10,30" />' + 209 '</svg>' + 210 '</button>', 211 212 // This small close button will be appended to your html/inline/ajax content by default, 213 // if "smallBtn" option is not set to false 214 smallBtn : '<button data-fancybox-close class="fancybox-close-small" title="{{CLOSE}}"></button>', 215 216 // Arrows 217 arrowLeft : '<button data-fancybox-prev class="fancybox-button fancybox-button--arrow_left" title="{{PREV}}">' + 218 '<svg viewBox="0 0 40 40">' + 219 '<path d="M10,20 L30,20 L10,20 L18,28 L10,20 L18,12 L10,20"></path>' + 220 '</svg>' + 221 '</button>', 222 223 arrowRight : '<button data-fancybox-next class="fancybox-button fancybox-button--arrow_right" title="{{NEXT}}">' + 224 '<svg viewBox="0 0 40 40">' + 225 '<path d="M30,20 L10,20 L30,20 L22,28 L30,20 L22,12 L30,20"></path>' + 226 '</svg>' + 227 '</button>' 228 }, 229 230 // Container is injected into this element 231 parentEl : 'body', 232 233 234 // Focus handling 235 // ============== 236 237 // Try to focus on the first focusable element after opening 238 autoFocus : false, 239 240 // Put focus back to active element after closing 241 backFocus : true, 242
vendor: 4,138 bytes, lines 243-365
243 // Do not let user to focus on element outside modal content 244 trapFocus : true, 245 246 247 // Module specific options 248 // ======================= 249 250 fullScreen : { 251 autoStart : false, 252 }, 253 254 // Set `touch: false` to disable dragging/swiping 255 touch : { 256 vertical : true, // Allow to drag content vertically 257 momentum : true // Continue movement after releasing mouse/touch when panning 258 }, 259 260 // Hash value when initializing manually, 261 // set `false` to disable hash change 262 hash : null, 263 264 // Customize or add new media types 265 // Example: 266 /* 267 media : { 268 youtube : { 269 params : { 270 autoplay : 0 271 } 272 } 273 } 274 */ 275 media : {}, 276 277 slideShow : { 278 autoStart : false, 279 speed : 4000 280 }, 281 282 thumbs : { 283 autoStart : false, // Display thumbnails on opening 284 hideOnClose : true, // Hide thumbnail grid when closing animation starts 285 parentEl : '.fancybox-container', // Container is injected into this element 286 axis : 'y' // Vertical (y) or horizontal (x) scrolling 287 }, 288 289 // Use mousewheel to navigate gallery 290 // If 'auto' - enabled for images only 291 wheel : 'auto', 292 293 // Callbacks 294 //========== 295 296 // See Documentation/API/Events for more information 297 // Example: 298 /* 299 afterShow: function( instance, current ) { 300 console.info( 'Clicked element:' ); 301 console.info( current.opts.$orig ); 302 } 303 */ 304 305 onInit : $.noop, // When instance has been initialized 306 307 beforeLoad : $.noop, // Before the content of a slide is being loaded 308 afterLoad : $.noop, // When the content of a slide is done loading 309 310 beforeShow : $.noop, // Before open animation starts 311 afterShow : $.noop, // When content is done loading and animating 312 313 beforeClose : $.noop, // Before the instance attempts to close. Return false to cancel the close. 314 afterClose : $.noop, // After instance has been closed 315 316 onActivate : $.noop, // When instance is brought to front 317 onDeactivate : $.noop, // When other instance has been activated 318 319 320 // Interaction 321 // =========== 322 323 // Use options below to customize taken action when user clicks or double clicks on the fancyBox area, 324 // each option can be string or method that returns value. 325 // 326 // Possible values: 327 // "close" - close instance 328 // "next" - move to next gallery item 329 // "nextOrClose" - move to next gallery item or close if gallery has only one item 330 // "toggleControls" - show/hide controls 331 // "zoom" - zoom image (if loaded) 332 // false - do nothing 333 334 // Clicked on the content 335 clickContent : function( current, event ) { 336 return current.type === 'image' ? 'zoom' : false; 337 }, 338 339 // Clicked on the slide 340 clickSlide : 'close', 341 342 // Clicked on the background (backdrop) element 343 clickOutside : 'close', 344 345 // Same as previous two, but for double click 346 dblclickContent : false, 347 dblclickSlide : false, 348 dblclickOutside : false, 349 350 351 // Custom options when mobile device is detected 352 // ============================================= 353 354 mobile : { 355 idleTime : false, 356 margin : 0, 357 358 clickContent : function( current, event ) { 359 return current.type === 'image' ? 'toggleControls' : false; 360 }, 361 clickSlide : function( current, event ) { 362 return current.type === 'image' ? 'toggleControls' : 'close'; 363 }, 364 dblclickContent : function( current, event ) { 365 return current.type === 'image' ? 'zoom' : false;
366 }, 367 dblclickSlide : function( current, event ) { 368 return current.type === 'image' ? 'zoom' : false; 369 } 370 }, 371 372 373 // Internationalization 374 // ============ 375 376 lang : 'ru', 377 i18n : { 378 'en' : { 379 CLOSE : 'Close', 380 NEXT : 'Next', 381 PREV : 'Previous', 382 ERROR : 'The requested content cannot be loaded. <br/> Please try again later.', 383 PLAY_START : 'Start slideshow', 384 PLAY_STOP : 'Pause slideshow', 385 FULL_SCREEN : 'Full screen', 386 THUMBS : 'Thumbnails', 387 DOWNLOAD : 'Download', 388 SHARE : 'Share', 389 ZOOM : 'Zoom' 390 }, 391 'de' : { 392 CLOSE : 'Schliessen', 393 NEXT : 'Weiter', 394 PREV : 'Zurück', 395 ERROR : 'Die angeforderten Daten konnten nicht geladen werden. <br/>
395 Bitte versuchen Sie es später nochmal.', 396 PLAY_START : 'Diaschau starten', 397 PLAY_STOP : 'Diaschau beenden', 398 FULL_SCREEN : 'Vollbild', 399 THUMBS : 'Vorschaubilder', 400 DOWNLOAD : 'Herunterladen', 401 SHARE : 'Teilen', 402 ZOOM : 'MaÃstab' 403 }, 404 'ru' : { 405 CLOSE : 'ÐакÑÑÑÑ', 406 NEXT : 'СледÑÑÑий', 407 PREV : 'ÐÑедÑдÑÑий', 408 ERROR : 'ÐапÑоÑеннÑй конÑÐµÐ½Ñ Ð½Ðµ Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð·Ð°Ð³ÑÑжен. <br/> ÐожалÑйÑÑа, повÑоÑиÑе попÑÑÐºÑ Ð¿Ð¾Ð·Ð¶Ðµ.', 409 PLAY_START : 'ÐапÑÑÑиÑÑ Ñлайд-ÑоÑ', 410 PLAY_STOP : 'ÐÑиоÑÑановиÑÑ Ñлайд-ÑоÑ', 411 FULL_SCREEN : 'ÐолноÑкÑаннÑй Ñежим', 412 THUMBS : 'ÐÑкизÑ', 413 DOWNLOAD : 'СкаÑаÑÑ', 414 SHARE : 'ÐоделиÑÑÑÑ', 415 ZOOM : 'УвелиÑение' 416 } 417 } 418 419 }; 420 421 // Few useful variables and methods 422 // ================================ 423 424 var $W = $(window); 425 var $D = $(document); 426 427 var called = 0; 428 429 430 // Check if an object is a jQuery object and not a native JavaScript object 431 // ======================================================================== 432 433 var isQuery = function ( obj ) { 434 return obj && obj.hasOwnProperty && obj instanceof $; 435 }; 436 437 438 // Handle multiple browsers for "requestAnimationFrame" and "cancelAnimationFrame" 439 // =============================================================================== 440 441 var requestAFrame = (function () { 442 return window.requestAnimationFrame || 443 window.webkitRequestAnimationFrame || 444 window.mozRequestAnimationFrame || 445 window.oRequestAnimationFrame || 446 // if all else fails, use setTimeout 447 function (callback) { 448 return window.setTimeout(callback, 1000 / 60); 449 }; 450 })(); 451 452 453 // Detect the supported transition-end event property name 454 // ======================================================= 455 456 var transitionEnd = (function () { 457 var t, el = document.createElement("fakeelement"); 458 459 var transitions = { 460 "transition" : "transitionend", 461 "OTransition" : "oTransitionEnd", 462 "MozTransition" : "transitionend", 463 "WebkitTransition": "webkitTransitionEnd" 464 }; 465 466 for (t in transitions) { 467 if (el.style[t] !== undefined){ 468 return transitions[t]; 469 } 470 } 471 472 return 'transitionend'; 473 })(); 474 475 476 // Force redraw on an element. 477 // This helps in cases where the browser doesn't redraw an updated element properly. 478 // ================================================================================= 479 480 var forceRedraw = function( $el ) { 481 return ( $el && $el.length && $el[0].offsetHeight ); 482 }; 483 484 485 // Class definition 486 // ================ 487 488 var FancyBox = function( content, opts, index ) { 489 var self = this; 490 491 self.opts = $.extend( true, { index : index }, $.fancybox.defaults, opts || {} ); 492 493 if ( $.fancybox.isMobile ) { 494 self.opts = $.extend( true, {}, self.opts, self.opts.mobile ); 495 } 496 497 // Exclude buttons option from deep merging 498 if ( opts && $.isArray( opts.buttons ) ) { 499 self.opts.buttons = opts.buttons; 500 } 501 502 self.id = self.opts.id || ++called; 503 self.group = []; 504 505 self.currIndex = parseInt( self.opts.index, 10 ) || 0; 506 self.prevIndex = null; 507 508 self.prevPos = null; 509 self.currPos = 0; 510 511 self.firstRun = null; 512 513 // Create group elements from original item collection 514 self.createGroup( content ); 515 516 if ( !self.group.length ) { 517 return; 518 } 519 520 // Save last active element and current scroll position 521 self.$lastFocus = $(document.activeElement).blur(); 522 523 // Collection of gallery objects 524 self.slides = {}; 525 526 self.init(); 527 }; 528 529 $.extend(FancyBox.prototype, { 530 531 // Create DOM structure 532 // ==================== 533 534 init : function() { 535 var self = this, 536 firstItem = self.group[ self.currIndex ], 537 firstItemOpts = firstItem.opts, 538 scrollbarWidth = $.fancybox.scrollbarWidth, 539 $scrollDiv, 540 $container, 541 buttonStr; 542 543 self.scrollTop = $D.scrollTop(); 544 self.scrollLeft = $D.scrollLeft(); 545 546 547 // Hide scrollbars 548 // =============== 549 550 if ( !$.fancybox.getInstance() ) { 551 552 $( 'body' ).addClass( 'fancybox-active' ); 553 554 // iOS hack 555 if ( /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream ) { 556 557 // iOS has problems for input elements inside fixed containers, 558 // the workaround is to apply `position: fixed` to `<body>` element, 559 // unfortunately, this makes it lose the scrollbars and forces address bar to appear. 560
561 if ( firstItem.type !== 'image' ) { 562 $( 'body' ).css( 'top', $( 'body' ).scrollTop() * -1 ).addClass( 'fancybox-iosfix' ); 563 } 564 565 } else if ( !$.fancybox.isMobile && document.body.scrollHeight > window.innerHeight ) { 566 567 if ( scrollbarWidth === undefined ) { 568 $scrollDiv = $('<div style="width:50px;height:50px;overflow:scroll;" />').appendTo( 'body' ); 569 570 scrollbarWidth = $.fancybox.scrollbarWidth = $scrollDiv[0].offsetWidth - $scrollDiv[0].clientWidth; 571 572 $scrollDiv.remove(); 573 } 574 575 $( 'head' ).append( '<style id="fancybox-style-noscroll" type="text/css">.compensate-for-scrollbar { margin-right: ' + scrollbarWidth + 'px; }</style>' ); 576 $( 'body' ).addClass( 'compensate-for-scrollbar' ); 577 } 578 } 579 580 581 // Build html markup and set references 582 // ==================================== 583 584 // Build html code for buttons and insert into main template 585 buttonStr = ''; 586 587 $.each( firstItemOpts.buttons, function( index, value ) { 588 buttonStr += ( firstItemOpts.btnTpl[ value ] || '' ); 589 }); 590 591 // Create markup from base template, it will be initially hidden to 592 // avoid unnecessary work like painting while initializing is not complete 593 $container = $( 594 self.translate( self, 595 firstItemOpts.baseTpl 596 .replace( '\{\{buttons\}\}', buttonStr ) 597 .replace( '\{\{arrows\}\}', firstItemOpts.btnTpl.arrowLeft + firstItemOpts.btnTpl.arrowRight ) 598 ) 599 ) 600 .attr( 'id', 'fancybox-container-' + self.id ) 601 .addClass( 'fancybox-is-hidden' ) 602 .addClass( firstItemOpts.baseClass ) 603 .data( 'FancyBox', self ) 604 .appendTo( firstItemOpts.parentEl ); 605 606 // Create object holding references to jQuery wrapped nodes 607 self.$refs = { 608 container : $container 609 }; 610 611 [ 'bg', 'inner', 'infobar', 'toolbar', 'stage', 'caption', 'navigation' ].forEach(function(item) { 612 self.$refs[ item ] = $container.find( '.fancybox-' + item ); 613 }); 614 615 self.trigger( 'onInit' ); 616 617 // Enable events, deactive previous instances 618 self.activate(); 619 620 // Build slides, load and reveal content 621 self.jumpTo( self.currIndex ); 622 }, 623 624 625 // Simple i18n support - replaces object keys found in template 626 // with corresponding values 627 // ============================================================ 628 629 translate : function( obj, str ) { 630 var arr = obj.opts.i18n[ obj.opts.lang ]; 631 632 return str.replace(/\{\{(\w+)\}\}/g, function(match, n) { 633 var value = arr[n]; 634 635 if ( value === undefined ) { 636 return match; 637 } 638 639 return value; 640 }); 641 }, 642 643 // Create array of gally item objects 644 // Check if each object has valid type and content 645 // =============================================== 646 647 createGroup : function ( content ) { 648 var self = this; 649 var items = $.makeArray( content ); 650 651 $.each(items, function( i, item ) { 652 var obj = {}, 653 opts = {}, 654 $item, 655 type, 656 found, 657 src, 658 srcParts; 659 660 // Step 1 - Make sure we have an object 661 // ==================================== 662 663 if ( $.isPlainObject( item ) ) { 664 665 // We probably have manual usage here, something like 666 // $.fancybox.open( [ { src : "image.jpg", type : "image" } ] ) 667 668 obj = item; 669 opts = item.opts || item; 670 671 } else if ( $.type( item ) === 'object' && $( item ).length ) { 672 673 // Here we probably have jQuery collection returned by some selector 674 $item = $( item ); 675 676 opts = $item.data(); 677 opts = $.extend( {}, opts, opts.options || {} ); 678 679 // Here we store clicked element 680 opts.$orig = $item; 681 682 obj.src = opts.src || $item.attr( 'href' ); 683 684 // Assume that simple syntax is used, for example: 685 // `$.fancybox.open( $("#test"), {} );` 686 if ( !obj.type && !obj.src ) { 687 obj.type = 'inline'; 688 obj.src = item; 689 } 690 691 } else { 692 693 // Assume we have a simple html code, for example: 694 // $.fancybox.open( '<div><h1>Hi!</h1></div>' ); 695 696 obj = { 697 type : 'html', 698 src : item + '' 699 }; 700 701 } 702 703 // Each gallery object has full collection of options 704 obj.opts = $.extend( true, {}, self.opts, opts ); 705
706 // Do not merge buttons array 707 if ( $.isArray( opts.buttons ) ) { 708 obj.opts.buttons = opts.buttons; 709 } 710 711 712 // Step 2 - Make sure we have content type, if not - try to guess 713 // ============================================================== 714 715 type = obj.type || obj.opts.type; 716 src = obj.src || ''; 717 718 if ( !type && src ) { 719 if ( src.match(/(^data:image\/[a-z0-9+\/=]*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg|ico)((\?|#).*)?$)/i) ) { 720 type = 'image'; 721 722 } else if ( src.match(/\.(pdf)((\?|#).*)?$/i) ) { 723 type = 'pdf'; 724 725 } else if ( found = src.match(/\.(mp4|mov|ogv)((\?|#).*)?$/i) ) { 726 type = 'video'; 727 728 if ( !obj.opts.videoFormat ) { 729 obj.opts.videoFormat = 'video/' + ( found[1] === 'ogv' ? 'ogg' : found[1] ); 730 } 731 732 } else if ( src.charAt(0) === '#' ) { 733 type = 'inline'; 734 } 735 } 736 737 if ( type ) { 738 obj.type = type; 739 740 } else { 741 self.trigger( 'objectNeedsType', obj ); 742 } 743 744 745 // Step 3 - Some adjustments 746 // ========================= 747 748 obj.index = self.group.length; 749 750 // Check if $orig and $thumb objects exist 751 if ( obj.opts.$orig && !obj.opts.$orig.length ) { 752 delete obj.opts.$orig; 753 } 754 755 if ( !obj.opts.$thumb && obj.opts.$orig ) { 756 obj.opts.$thumb = obj.opts.$orig.find( 'img:first' ); 757 } 758 759 if ( obj.opts.$thumb && !obj.opts.$thumb.length ) { 760 delete obj.opts.$thumb; 761 } 762 763 // "caption" is a "special" option, it can be used to customize caption per gallery item .. 764 if ( $.type( obj.opts.caption ) === 'function' ) { 765 obj.opts.caption = obj.opts.caption.apply( item, [ self, obj ] ); 766 } 767 768 if ( $.type( self.opts.caption ) === 'function' ) { 769 obj.opts.caption = self.opts.caption.apply( item, [ self, obj ] ); 770 } 771 772 // Make sure we have caption as a string or jQuery object 773 if ( !( obj.opts.caption instanceof $ ) ) { 774 obj.opts.caption = obj.opts.caption === undefined ? '' : obj.opts.caption + ''; 775 } 776 777 // Check if url contains "filter" used to filter the content 778 // Example: "ajax.html #something" 779 if ( type === 'ajax' ) { 780 srcParts = src.split(/\s+/, 2); 781 782 if ( srcParts.length > 1 ) { 783 obj.src = srcParts.shift(); 784 785 obj.opts.filter = srcParts.shift(); 786 } 787 } 788 789 if ( obj.opts.smallBtn == 'auto' ) { 790 791 if ( $.inArray( type, ['html', 'inline', 'ajax'] ) > -1 ) { 792 obj.opts.toolbar = false; 793 obj.opts.smallBtn = true; 794 795 } else { 796 obj.opts.smallBtn = false; 797 } 798 799 } 800 801 // If the type is "pdf", then simply load file into iframe 802 if ( type === 'pdf' ) { 803 obj.type = 'iframe'; 804 805 obj.opts.iframe.preload = false; 806 } 807 808 // Hide all buttons and disable interactivity for modal items 809 if ( obj.opts.modal ) { 810 811 obj.opts = $.extend(true, obj.opts, { 812 // Remove buttons 813 infobar : 0, 814 toolbar : 0, 815 816 smallBtn : 0, 817 818 // Disable keyboard navigation 819 keyboard : 0, 820 821 // Disable some modules 822 slideShow : 0, 823 fullScreen : 0, 824 thumbs : 0, 825 touch : 0, 826 827 // Disable click event handlers 828 clickContent : false, 829 clickSlide : false, 830 clickOutside : false, 831 dblclickContent : false, 832 dblclickSlide : false, 833 dblclickOutside : false 834 }); 835 836 } 837 838 // Step 4 - Add processed object to group 839 // ====================================== 840 841 self.group.push( obj ); 842 843 }); 844 845 }, 846 847 848 // Attach an event handler functions for: 849 // - navigation buttons 850 // - browser scrolling, resizing; 851 // - focusing 852 // - keyboard 853 // - detect idle 854 // ====================================== 855 856 addEvents : function() { 857 var self = this; 858 859 self.removeEvents(); 860 861 // Make navigation elements clickable 862 self.$refs.container.on('click.fb-close', '[data-fancybox-close]', function(e) { 863 e.stopPropagation(); 864 e.preventDefault(); 865 866 self.close( e ); 867 868 }).on( 'click.fb-prev touchend.fb-prev', '[data-fancybox-prev]', function(e) { 869 e.stopPropagation(); 870 e.preventDefault(); 871 872 self.previous(); 873 874 }).on( 'click.fb-next touchend.fb-next', '[data-fancybox-next]', function(e) { 875 e.stopPropagation(); 876 e.preventDefault(); 877 878 self.next(); 879 880 }).on( 'click.fb', '[data-fancybox-zoom]', function(e) { 881 // Click handler for zoom button 882 self[ self.isScaledDown() ? 'scaleToActual' : 'scaleToFit' ](); 883 }); 884 885 886 // Handle page scrolling and browser resizing 887 $W.on('orientationchange.fb resize.fb', function(e) { 888 889 if ( e && e.originalEvent && e.originalEvent.type === "resize" ) { 890 891 requestAFrame(function() {
892 self.update(); 893 }); 894 895 } else { 896 897 self.$refs.stage.hide(); 898 899 setTimeout(function() { 900 self.$refs.stage.show(); 901 902 self.update(); 903 }, 600); 904 905 } 906 907 }); 908 909 // Trap keyboard focus inside of the modal, so the user does not accidentally tab outside of the modal 910 // (a.k.a. "escaping the modal") 911 $D.on('focusin.fb', function(e) { 912 var instance = $.fancybox ? $.fancybox.getInstance() : null; 913 914 if ( instance.isClosing || !instance.current || !instance.current.opts.trapFocus || $( e.target ).hasClass( 'fancybox-container' ) || $( e.target ).is( document ) ) { 915 return; 916 } 917 918 if ( instance && $( e.target ).css( 'position' ) !== 'fixed' && !instance.$refs.container.has( e.target ).length ) { 919 e.stopPropagation(); 920 921 instance.focus(); 922 923 // Sometimes page gets scrolled, set it back 924 $W.scrollTop( self.scrollTop ).scrollLeft( self.scrollLeft ); 925 } 926 }); 927 928 929 // Enable keyboard navigation 930 $D.on('keydown.fb', function (e) { 931 var current = self.current, 932 keycode = e.keyCode || e.which; 933 934 if ( !current || !current.opts.keyboard ) { 935 return; 936 } 937 938 if ( $(e.target).is('input') || $(e.target).is('textarea') ) { 939 return; 940 } 941 942 // Backspace and Esc keys 943 if ( keycode === 8 || keycode === 27 ) { 944 e.preventDefault(); 945 946 self.close( e ); 947 948 return; 949 } 950 951 // Left arrow and Up arrow 952 if ( keycode === 37 || keycode === 38 ) { 953 e.preventDefault(); 954 955 self.previous(); 956 957 return; 958 } 959 960 // Righ arrow and Down arrow 961 if ( keycode === 39 || keycode === 40 ) { 962 e.preventDefault(); 963 964 self.next(); 965 966 return; 967 } 968 969 self.trigger('afterKeydown', e, keycode); 970 }); 971 972 973 // Hide controls after some inactivity period 974 if ( self.group[ self.currIndex ].opts.idleTime ) { 975 self.idleSecondsCounter = 0; 976 977 $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) { 978 self.idleSecondsCounter = 0; 979 980 if ( self.isIdle ) { 981 self.showControls(); 982 } 983 984 self.isIdle = false; 985 }); 986 987 self.idleInterval = window.setInterval(function() { 988 self.idleSecondsCounter++; 989 990 if ( self.idleSecondsCounter >= self.group[ self.currIndex ].opts.idleTime && !self.isDragging ) { 991 self.isIdle = true; 992 self.idleSecondsCounter = 0; 993 994 self.hideControls(); 995 } 996 997 }, 1000); 998 } 999 1000 }, 1001 1002 1003 // Remove events added by the core 1004 // =============================== 1005 1006 removeEvents : function() { 1007 var self = this; 1008 1009 $W.off( 'orientationchange.fb resize.fb' ); 1010 $D.off( 'focusin.fb keydown.fb .fb-idle' ); 1011 1012 this.$refs.container.off( '.fb-close .fb-prev .fb-next' ); 1013 1014 if ( self.idleInterval ) { 1015 window.clearInterval( self.idleInterval ); 1016 1017 self.idleInterval = null; 1018 } 1019 }, 1020 1021 1022 // Change to previous gallery item 1023 // =============================== 1024 1025 previous : function( duration ) { 1026 return this.jumpTo( this.currPos - 1, duration ); 1027 }, 1028 1029 1030 // Change to next gallery item 1031 // =========================== 1032 1033 next : function( duration ) { 1034 return this.jumpTo( this.currPos + 1, duration ); 1035 }, 1036 1037 1038 // Switch to selected gallery item 1039 // =============================== 1040 1041 jumpTo : function ( pos, duration, slide ) { 1042 var self = this, 1043 firstRun, 1044 loop, 1045 current, 1046 previous, 1047 canvasWidth, 1048 currentPos, 1049 transitionProps; 1050 1051 var groupLen = self.group.length; 1052 1053 if ( self.isDragging || self.isClosing || ( self.isAnimating && self.firstRun ) ) { 1054 return; 1055 } 1056 1057 pos = parseInt( pos, 10 ); 1058 loop = self.current ? self.current.opts.loop : self.opts.loop; 1059 1060 if ( !loop && ( pos < 0 || pos >= groupLen ) ) { 1061 return false;
1062 } 1063 1064 firstRun = self.firstRun = ( self.firstRun === null ); 1065 1066 if ( groupLen < 2 && !firstRun && !!self.isDragging ) { 1067 return; 1068 } 1069 1070 previous = self.current; 1071 1072 self.prevIndex = self.currIndex; 1073 self.prevPos = self.currPos; 1074 1075 // Create slides 1076 current = self.createSlide( pos ); 1077 1078 if ( groupLen > 1 ) { 1079 if ( loop || current.index > 0 ) { 1080 self.createSlide( pos - 1 ); 1081 } 1082 1083 if ( loop || current.index < groupLen - 1 ) { 1084 self.createSlide( pos + 1 ); 1085 } 1086 } 1087 1088 self.current = current; 1089 self.currIndex = current.index; 1090 self.currPos = current.pos; 1091 1092 self.trigger( 'beforeShow', firstRun ); 1093 1094 self.updateControls(); 1095 1096 currentPos = $.fancybox.getTranslate( current.$slide ); 1097 1098 current.isMoved = ( currentPos.left !== 0 || currentPos.top !== 0 ) && !current.$slide.hasClass( 'fancybox-animated' ); 1099 current.forcedDuration = undefined; 1100 1101 if ( $.isNumeric( duration ) ) { 1102 current.forcedDuration = duration; 1103 } else { 1104 duration = current.opts[ firstRun ? 'animationDuration' : 'transitionDuration' ]; 1105 } 1106 1107 duration = parseInt( duration, 10 ); 1108 1109 // Fresh start - reveal container, current slide and start loading content 1110 if ( firstRun ) { 1111 1112 if ( current.opts.animationEffect && duration ) { 1113 self.$refs.container.css( 'transition-duration', duration + 'ms' ); 1114 } 1115 1116 self.$refs.container.removeClass( 'fancybox-is-hidden' ); 1117 1118 forceRedraw( self.$refs.container ); 1119 1120 self.$refs.container.addClass( 'fancybox-is-open' ); 1121 1122 // Make first slide visible (to display loading icon, if needed) 1123 current.$slide.addClass( 'fancybox-slide--current' ); 1124 1125 self.loadSlide( current ); 1126 1127 self.preload( 'image' ); 1128 1129 return; 1130 } 1131 1132 // Clean up 1133 $.each(self.slides, function( index, slide ) { 1134 $.fancybox.stop( slide.$slide ); 1135 }); 1136 1137 // Make current that slide is visible even if content is still loading 1138 current.$slide.removeClass( 'fancybox-slide--next fancybox-slide--previous' ).addClass( 'fancybox-slide--current' ); 1139 1140 // If slides have been dragged, animate them to correct position 1141 if ( current.isMoved ) { 1142 canvasWidth = Math.round( current.$slide.width() ); 1143 1144 $.each(self.slides, function( index, slide ) { 1145 var pos = slide.pos - current.pos; 1146 1147 $.fancybox.animate( slide.$slide, { 1148 top : 0, 1149 left : ( pos * canvasWidth ) + ( pos * slide.opts.gutter ) 1150 }, duration, function() { 1151 1152 slide.$slide.removeAttr('style').removeClass( 'fancybox-slide--next fancybox-slide--previous' ); 1153 1154 if ( slide.pos === self.currPos ) { 1155 current.isMoved = false; 1156 1157 self.complete(); 1158 } 1159 }); 1160 }); 1161 1162 } else { 1163 self.$refs.stage.children().removeAttr( 'style' ); 1164 } 1165 1166 // Start transition that reveals current content 1167 // or wait when it will be loaded 1168 1169 if ( current.isLoaded ) { 1170 self.revealContent( current ); 1171 1172 } else { 1173 self.loadSlide( current ); 1174 } 1175 1176 self.preload( 'image' ); 1177 1178 if ( previous.pos === current.pos ) { 1179 return; 1180 } 1181 1182 // Handle previous slide 1183 // ===================== 1184 1185 transitionProps = 'fancybox-slide--' + ( previous.pos > current.pos ? 'next' : 'previous' ); 1186 1187 previous.$slide.removeClass( 'fancybox-slide--complete fancybox-slide--current fancybox-slide--next fancybox-slide--previous' ); 1188 1189 previous.isComplete = false;
1190 1191 if ( !duration || ( !current.isMoved && !current.opts.transitionEffect ) ) { 1192 return; 1193 } 1194 1195 if ( current.isMoved ) { 1196 previous.$slide.addClass( transitionProps ); 1197 1198 } else { 1199 1200 transitionProps = 'fancybox-animated ' + transitionProps + ' fancybox-fx-' + current.opts.transitionEffect; 1201 1202 $.fancybox.animate( previous.$slide, transitionProps, duration, function() { 1203 previous.$slide.removeClass( transitionProps ).removeAttr( 'style' ); 1204 }); 1205 1206 } 1207 1208 }, 1209 1210 1211 // Create new "slide" element 1212 // These are gallery items that are actually added to DOM 1213 // ======================================================= 1214 1215 createSlide : function( pos ) { 1216 1217 var self = this; 1218 var $slide; 1219 var index; 1220 1221 index = pos % self.group.length; 1222 index = index < 0 ? self.group.length + index : index; 1223 1224 if ( !self.slides[ pos ] && self.group[ index ] ) { 1225 $slide = $('<div class="fancybox-slide"></div>').appendTo( self.$refs.stage ); 1226 1227 self.slides[ pos ] = $.extend( true, {}, self.group[ index ], { 1228 pos : pos, 1229 $slide : $slide, 1230 isLoaded : false, 1231 }); 1232 1233 self.updateSlide( self.slides[ pos ] ); 1234 } 1235 1236 return self.slides[ pos ]; 1237 }, 1238 1239 1240 // Scale image to the actual size of the image 1241 // =========================================== 1242 1243 scaleToActual : function( x, y, duration ) { 1244 1245 var self = this; 1246 1247 var current = self.current; 1248 var $what = current.$content; 1249 1250 var imgPos, posX, posY, scaleX, scaleY; 1251 1252 var canvasWidth = parseInt( current.$slide.width(), 10 ); 1253 var canvasHeight = parseInt( current.$slide.height(), 10 ); 1254 1255 var newImgWidth = current.width; 1256 var newImgHeight = current.height; 1257 1258 if ( !( current.type == 'image' && !current.hasError) || !$what || self.isAnimating ) { 1259 return; 1260 } 1261 1262 $.fancybox.stop( $what ); 1263 1264 self.isAnimating = true; 1265 1266 x = x === undefined ? canvasWidth * 0.5 : x; 1267 y = y === undefined ? canvasHeight * 0.5 : y; 1268 1269 imgPos = $.fancybox.getTranslate( $what ); 1270 1271 scaleX = newImgWidth / imgPos.width; 1272 scaleY = newImgHeight / imgPos.height; 1273 1274 // Get center position for original image 1275 posX = ( canvasWidth * 0.5 - newImgWidth * 0.5 ); 1276 posY = ( canvasHeight * 0.5 - newImgHeight * 0.5 ); 1277 1278 // Make sure image does not move away from edges 1279 if ( newImgWidth > canvasWidth ) { 1280 posX = imgPos.left * scaleX - ( ( x * scaleX ) - x ); 1281 1282 if ( posX > 0 ) { 1283 posX = 0; 1284 } 1285 1286 if ( posX < canvasWidth - newImgWidth ) { 1287 posX = canvasWidth - newImgWidth; 1288 } 1289 } 1290 1291 if ( newImgHeight > canvasHeight) { 1292 posY = imgPos.top * scaleY - ( ( y * scaleY ) - y ); 1293 1294 if ( posY > 0 ) { 1295 posY = 0; 1296 } 1297 1298 if ( posY < canvasHeight - newImgHeight ) { 1299 posY = canvasHeight - newImgHeight; 1300 } 1301 } 1302 1303 self.updateCursor( newImgWidth, newImgHeight ); 1304 1305 $.fancybox.animate( $what, { 1306 top : posY, 1307 left : posX, 1308 scaleX : scaleX, 1309 scaleY : scaleY 1310 }, duration || 330, function() { 1311 self.isAnimating = false; 1312 }); 1313 1314 // Stop slideshow 1315 if ( self.SlideShow && self.SlideShow.isActive ) { 1316 self.SlideShow.stop(); 1317 } 1318 }, 1319 1320 1321 // Scale image to fit inside parent element 1322 // ======================================== 1323 1324 scaleToFit : function( duration ) { 1325 1326 var self = this; 1327 1328 var current = self.current; 1329 var $what = current.$content; 1330 var end; 1331 1332 if ( !( current.type == 'image' && !current.hasError) || !$what || self.isAnimating ) { 1333 return; 1334 } 1335 1336 $.fancybox.stop( $what ); 1337 1338 self.isAnimating = true; 1339 1340 end = self.getFitPos( current ); 1341 1342 self.updateCursor( end.width, end.height ); 1343 1344 $.fancybox.animate( $what, { 1345 top : end.top, 1346 left : end.left, 1347 scaleX : end.width / $what.width(), 1348 scaleY : end.height / $what.height() 1349 }, duration || 330, function() { 1350 self.isAnimating = false;
1351 }); 1352 1353 }, 1354 1355 // Calculate image size to fit inside viewport 1356 // =========================================== 1357 1358 getFitPos : function( slide ) { 1359 var self = this; 1360 var $what = slide.$content; 1361 1362 var imgWidth = slide.width; 1363 var imgHeight = slide.height; 1364 1365 var margin = slide.opts.margin; 1366 1367 var canvasWidth, canvasHeight, minRatio, width, height; 1368 1369 if ( !$what || !$what.length || ( !imgWidth && !imgHeight) ) { 1370 return false; 1371 } 1372 1373 // Convert "margin to CSS style: [ top, right, bottom, left ] 1374 if ( $.type( margin ) === "number" ) { 1375 margin = [ margin, margin ]; 1376 } 1377 1378 if ( margin.length == 2 ) { 1379 margin = [ margin[0], margin[1], margin[0], margin[1] ]; 1380 } 1381 1382 // We can not use $slide width here, because it can have different diemensions while in transiton 1383 canvasWidth = parseInt( self.$refs.stage.width(), 10 ) - ( margin[ 1 ] + margin[ 3 ] ); 1384 canvasHeight = parseInt( self.$refs.stage.height(), 10 ) - ( margin[ 0 ] + margin[ 2 ] ); 1385 1386 minRatio = Math.min(1, canvasWidth / imgWidth, canvasHeight / imgHeight ); 1387 1388 width = Math.floor( minRatio * imgWidth ); 1389 height = Math.floor( minRatio * imgHeight ); 1390 1391 // Use floor rounding to make sure it really fits 1392 return { 1393 top : Math.floor( ( canvasHeight - height ) * 0.5 ) + margin[ 0 ], 1394 left : Math.floor( ( canvasWidth - width ) * 0.5 ) + margin[ 3 ], 1395 width : width, 1396 height : height 1397 }; 1398 1399 }, 1400 1401 1402 // Update content size and position for all slides 1403 // ============================================== 1404 1405 update : function() { 1406 var self = this; 1407 1408 $.each( self.slides, function( key, slide ) { 1409 self.updateSlide( slide ); 1410 }); 1411 }, 1412 1413 1414 // Update slide content position and size 1415 // ====================================== 1416 1417 updateSlide : function( slide, duration ) { 1418 var self = this, 1419 $what = slide && slide.$content; 1420 1421 if ( $what && ( slide.width || slide.height ) ) { 1422 self.isAnimating = false; 1423 1424 $.fancybox.stop( $what ); 1425 1426 $.fancybox.setTranslate( $what, self.getFitPos( slide ) ); 1427 1428 if ( slide.pos === self.currPos ) { 1429 self.updateCursor(); 1430 } 1431 } 1432 1433 slide.$slide.trigger( 'refresh' ); 1434 1435 self.trigger( 'onUpdate', slide ); 1436 1437 }, 1438 1439 1440 // Horizontally center slide 1441 // ========================= 1442 1443 centerSlide : function( slide, duration ) { 1444 var self = this, canvasWidth, pos; 1445 1446 if ( self.current ) { 1447 canvasWidth = Math.round( slide.$slide.width() ); 1448 pos = slide.pos - self.current.pos; 1449 1450 $.fancybox.animate( slide.$slide, { 1451 top : 0, 1452 left : ( pos * canvasWidth ) + ( pos * slide.opts.gutter ), 1453 opacity : 1 1454 }, duration === undefined ? 0 : duration, null, false); 1455 } 1456 }, 1457 1458 1459 // Update cursor style depending if content can be zoomed 1460 // ====================================================== 1461 1462 updateCursor : function( nextWidth, nextHeight ) { 1463 1464 var self = this; 1465 var isScaledDown; 1466 1467 var $container = self.$refs.container.removeClass( 'fancybox-is-zoomable fancybox-can-zoomIn fancybox-can-drag fancybox-can-zoomOut' ); 1468 1469 if ( !self.current || self.isClosing ) { 1470 return; 1471 } 1472 1473 if ( self.isZoomable() ) { 1474 1475 $container.addClass( 'fancybox-is-zoomable' ); 1476 1477 if ( nextWidth !== undefined && nextHeight !== undefined ) { 1478 isScaledDown = nextWidth < self.current.width && nextHeight < self.current.height; 1479 1480 } else { 1481 isScaledDown = self.isScaledDown(); 1482 } 1483 1484 if ( isScaledDown ) { 1485 1486 // If image is scaled down, then, obviously, it can be zoomed to full size 1487 $container.addClass( 'fancybox-can-zoomIn' ); 1488 1489 } else { 1490 1491 if ( self.current.opts.touch ) { 1492 1493 // If image size ir largen than available available and touch module is not disable, 1494 // then user can do panning 1495 $container.addClass( 'fancybox-can-drag' ); 1496 1497 } else { 1498 $container.addClass( 'fancybox-can-zoomOut' ); 1499 } 1500 1501 } 1502 1503 } else if ( self.current.opts.touch ) { 1504 $container.addClass( 'fancybox-can-drag' ); 1505 } 1506 1507 }, 1508 1509 1510 // Check if current slide is zoomable 1511 // ================================== 1512 1513 isZoomable : function() { 1514 1515 var self = this; 1516 1517 var current = self.current; 1518 var fitPos; 1519 1520 if ( !current || self.isClosing ) { 1521 return; 1522 } 1523 1524 // Assume that slide is zoomable if 1525 // - image is loaded successfuly 1526 // - click action is "zoom" 1527 // - actual size of the image is smaller than available area 1528 if ( current.type === 'image' && current.isLoaded && !current.hasError && 1529 ( current.opts.clickContent === 'zoom' || ( $.isFunction( current.opts.clickContent ) && current.opts.clickContent( current ) === "zoom" ) ) 1530 ) { 1531 1532 fitPos = self.getFitPos( current ); 1533 1534 if ( current.width > fitPos.width || current.height > fitPos.height ) { 1535 return true; 1536 } 1537 1538 } 1539 1540 return false;
1541 1542 }, 1543 1544 1545 // Check if current image dimensions are smaller than actual 1546 // ========================================================= 1547 1548 isScaledDown : function() { 1549 1550 var self = this; 1551 1552 var current = self.current; 1553 var $what = current.$content; 1554 1555 var rez = false; 1556 1557 if ( $what ) { 1558 rez = $.fancybox.getTranslate( $what ); 1559 rez = rez.width < current.width || rez.height < current.height; 1560 } 1561 1562 return rez; 1563 1564 }, 1565 1566 1567 // Check if image dimensions exceed parent element 1568 // =============================================== 1569 1570 canPan : function() { 1571 1572 var self = this; 1573 1574 var current = self.current; 1575 var $what = current.$content; 1576 1577 var rez = false; 1578 1579 if ( $what ) { 1580 rez = self.getFitPos( current ); 1581 rez = Math.abs( $what.width() - rez.width ) > 1 || Math.abs( $what.height() - rez.height ) > 1; 1582 } 1583 1584 return rez; 1585 1586 }, 1587 1588 1589 // Load content into the slide 1590 // =========================== 1591 1592 loadSlide : function( slide ) { 1593 1594 var self = this, type, $slide; 1595 var ajaxLoad; 1596 1597 if ( slide.isLoading ) { 1598 return; 1599 } 1600 1601 if ( slide.isLoaded ) { 1602 return; 1603 } 1604 1605 slide.isLoading = true; 1606 1607 self.trigger( 'beforeLoad', slide ); 1608 1609 type = slide.type; 1610 $slide = slide.$slide; 1611 1612 $slide 1613 .off( 'refresh' ) 1614 .trigger( 'onReset' ) 1615 .addClass( 'fancybox-slide--' + ( type || 'unknown' ) ) 1616 .addClass( slide.opts.slideClass ); 1617 1618 // Create content depending on the type 1619 1620 switch ( type ) { 1621 1622 case 'image': 1623 1624 self.setImage( slide ); 1625 1626 break; 1627 1628 case 'iframe': 1629 1630 self.setIframe( slide ); 1631 1632 break; 1633 1634 case 'html': 1635 1636 self.setContent( slide, slide.src || slide.content ); 1637 1638 break; 1639 1640 case 'inline': 1641 1642 if ( $( slide.src ).length ) { 1643 self.setContent( slide, $( slide.src ) ); 1644 1645 } else { 1646 self.setError( slide ); 1647 } 1648 1649 break; 1650 1651 case 'ajax': 1652 1653 self.showLoading( slide ); 1654 1655 ajaxLoad = $.ajax( $.extend( {}, slide.opts.ajax.settings, { 1656 url : slide.src, 1657 success : function ( data, textStatus ) { 1658 1659 if ( textStatus === 'success' ) { 1660 self.setContent( slide, data ); 1661 } 1662 1663 }, 1664 error : function ( jqXHR, textStatus ) { 1665 1666 if ( jqXHR && textStatus !== 'abort' ) { 1667 self.setError( slide ); 1668 } 1669 1670 } 1671 })); 1672 1673 $slide.one( 'onReset', function () { 1674 ajaxLoad.abort(); 1675 }); 1676 1677 break; 1678 1679 case 'video' : 1680 1681 self.setContent( slide, 1682 '<video controls>' + 1683 '<source src="' + slide.src + '" type="' + slide.opts.videoFormat + '">' + 1684 'Your browser doesn\'t support HTML5 video' + 1685 '</video>' 1686 ); 1687 1688 break; 1689 1690 default: 1691 1692 self.setError( slide ); 1693 1694 break; 1695 1696 } 1697 1698 return true; 1699 1700 }, 1701 1702 1703 // Use thumbnail image, if possible 1704 // ================================ 1705 1706 setImage : function( slide ) { 1707 1708 var self = this; 1709 var srcset = slide.opts.srcset || slide.opts.image.srcset; 1710
vendor: 4,273 bytes, lines 1711-1838
1711 var found, temp, pxRatio, windowWidth; 1712 1713 // If we have "srcset", then we need to find matching "src" value. 1714 // This is necessary, because when you set an src attribute, the browser will preload the image 1715 // before any javascript or even CSS is applied. 1716 if ( srcset ) { 1717 pxRatio = window.devicePixelRatio || 1; 1718 windowWidth = window.innerWidth * pxRatio; 1719 1720 temp = srcset.split(',').map(function ( el ) { 1721 var ret = {}; 1722 1723 el.trim().split(/\s+/).forEach(function ( el, i ) { 1724 var value = parseInt( el.substring(0, el.length - 1), 10 ); 1725 1726 if ( i === 0 ) { 1727 return ( ret.url = el ); 1728 } 1729 1730 if ( value ) { 1731 ret.value = value; 1732 ret.postfix = el[ el.length - 1 ]; 1733 } 1734 1735 }); 1736 1737 return ret; 1738 }); 1739 1740 // Sort by value 1741 temp.sort(function (a, b) { 1742 return a.value - b.value; 1743 }); 1744 1745 // Ok, now we have an array of all srcset values 1746 for ( var j = 0; j < temp.length; j++ ) { 1747 var el = temp[ j ]; 1748 1749 if ( ( el.postfix === 'w' && el.value >= windowWidth ) || ( el.postfix === 'x' && el.value >= pxRatio ) ) { 1750 found = el; 1751 break; 1752 } 1753 } 1754 1755 // If not found, take the last one 1756 if ( !found && temp.length ) { 1757 found = temp[ temp.length - 1 ]; 1758 } 1759 1760 if ( found ) { 1761 slide.src = found.url; 1762 1763 // If we have default width/height values, we can calculate height for matching source 1764 if ( slide.width && slide.height && found.postfix == 'w' ) { 1765 slide.height = ( slide.width / slide.height ) * found.value; 1766 slide.width = found.value; 1767 } 1768 } 1769 } 1770 1771 // This will be wrapper containing both ghost and actual image 1772 slide.$content = $('<div class="fancybox-image-wrap"></div>') 1773 .addClass( 'fancybox-is-hidden' ) 1774 .appendTo( slide.$slide ); 1775 1776 1777 // If we have a thumbnail, we can display it while actual image is loading 1778 // Users will not stare at black screen and actual image will appear gradually 1779 if ( slide.opts.preload !== false && slide.opts.width && slide.opts.height && ( slide.opts.thumb || slide.opts.$thumb ) ) { 1780 1781 slide.width = slide.opts.width; 1782 slide.height = slide.opts.height; 1783 1784 slide.$ghost = $('<img />') 1785 .one('error', function() { 1786 1787 $(this).remove(); 1788 1789 slide.$ghost = null; 1790 1791 self.setBigImage( slide ); 1792 1793 }) 1794 .one('load', function() { 1795 1796 self.afterLoad( slide ); 1797 1798 self.setBigImage( slide ); 1799 1800 }) 1801 .addClass( 'fancybox-image' ) 1802 .appendTo( slide.$content ) 1803 .attr( 'src', slide.opts.thumb || slide.opts.$thumb.attr( 'src' ) ); 1804 1805 } else { 1806 1807 self.setBigImage( slide ); 1808 1809 } 1810 1811 }, 1812 1813 1814 // Create full-size image 1815 // ====================== 1816 1817 setBigImage : function ( slide ) { 1818 var self = this; 1819 var $img = $('<img />'); 1820 1821 slide.$image = $img 1822 .one('error', function() { 1823 1824 self.setError( slide ); 1825 1826 }) 1827 .one('load', function() { 1828 1829 // Clear timeout that checks if loading icon needs to be displayed 1830 clearTimeout( slide.timouts ); 1831 1832 slide.timouts = null; 1833 1834 if ( self.isClosing ) { 1835 return; 1836 } 1837 1838 slide.width = slide.opts.width || this.naturalWi
1838dth; 1839 slide.height = slide.opts.height || this.naturalHeight; 1840 1841 if ( slide.opts.image.srcset ) { 1842 $img.attr( 'sizes', '100vw' ).attr( 'srcset', slide.opts.image.srcset ); 1843 } 1844 1845 self.hideLoading( slide ); 1846 1847 if ( slide.$ghost ) { 1848 1849 slide.timouts = setTimeout(function() { 1850 slide.timouts = null; 1851 1852 slide.$ghost.hide(); 1853 1854 }, Math.min( 300, Math.max( 1000, slide.height / 1600 ) ) ); 1855 1856 } else { 1857 self.afterLoad( slide ); 1858 } 1859 1860 }) 1861 .addClass( 'fancybox-image' ) 1862 .attr('src', slide.src) 1863 .appendTo( slide.$content ); 1864 1865 if ( ( $img[0].complete || $img[0].readyState == "complete" ) && $img[0].naturalWidth && $img[0].naturalHeight ) { 1866 $img.trigger( 'load' ); 1867 1868 } else if( $img[0].error ) { 1869 $img.trigger( 'error' ); 1870 1871 } else { 1872 1873 slide.timouts = setTimeout(function() { 1874 if ( !$img[0].complete && !slide.hasError ) { 1875 self.showLoading( slide ); 1876 } 1877 1878 }, 100); 1879 1880 } 1881 1882 }, 1883 1884 1885 // Create iframe wrapper, iframe and bindings 1886 // ========================================== 1887 1888 setIframe : function( slide ) { 1889 var self = this, 1890 opts = slide.opts.iframe, 1891 $slide = slide.$slide, 1892 $iframe; 1893 1894 slide.$content = $('<div class="fancybox-content' + ( opts.preload ? ' fancybox-is-hidden' : '' ) + '"></div>') 1895 .css( opts.css ) 1896 .appendTo( $slide ); 1897 1898 $iframe = $( opts.tpl.replace(/\{rnd\}/g, new Date().getTime()) ) 1899 .attr( opts.attr ) 1900 .appendTo( slide.$content ); 1901 1902 if ( opts.preload ) { 1903 1904 self.showLoading( slide ); 1905 1906 // Unfortunately, it is not always possible to determine if iframe is successfully loaded 1907 // (due to browser security policy) 1908 1909 $iframe.on('load.fb error.fb', function(e) { 1910 this.isReady = 1; 1911 1912 slide.$slide.trigger( 'refresh' ); 1913 1914 self.afterLoad( slide ); 1915 }); 1916 1917 // Recalculate iframe content size 1918 // =============================== 1919 1920 $slide.on('refresh.fb', function() { 1921 var $wrap = slide.$content, 1922 frameWidth = opts.css.width, 1923 frameHeight = opts.css.height, 1924 scrollWidth, 1925 $contents, 1926 $body; 1927 1928 if ( $iframe[0].isReady !== 1 ) { 1929 return; 1930 } 1931 1932 // Check if content is accessible, 1933 // it will fail if frame is not with the same origin 1934 1935 try { 1936 $contents = $iframe.contents(); 1937 $body = $contents.find('body'); 1938 1939 } catch (ignore) {} 1940 1941 // Calculate dimensions for the wrapper 1942 if ( $body && $body.length ) { 1943 1944 if ( frameWidth === undefined ) { 1945 scrollWidth = $iframe[0].contentWindow.document.documentElement.scrollWidth; 1946 1947 frameWidth = Math.ceil( $body.outerWidth(true) + ( $wrap.width() - scrollWidth ) ); 1948 frameWidth += $wrap.outerWidth() - $wrap.innerWidth(); 1949 } 1950 1951 if ( frameHeight === undefined ) { 1952 frameHeight = Math.ceil( $body.outerHeight(true) ); 1953 frameHeight += $wrap.outerHeight() - $wrap.innerHeight(); 1954 } 1955 1956 // Resize wrapper to fit iframe content 1957 if ( frameWidth ) { 1958 $wrap.width( frameWidth ); 1959 } 1960 1961 if ( frameHeight ) { 1962 $wrap.height( frameHeight ); 1963 } 1964 } 1965 1966 $wrap.removeClass( 'fancybox-is-hidden' ); 1967 1968 }); 1969 1970 } else { 1971 1972 this.afterLoad( slide ); 1973 1974 } 1975 1976 $iframe.attr( 'src', slide.src ); 1977 1978 if ( slide.opts.smallBtn === true ) {
1979 slide.$content.prepend( self.translate( slide, slide.opts.btnTpl.smallBtn ) ); 1980 } 1981 1982 // Remove iframe if closing or changing gallery item 1983 $slide.one( 'onReset', function () { 1984 1985 // This helps IE not to throw errors when closing 1986 try { 1987 1988 $( this ).find( 'iframe' ).hide().attr( 'src', '//about:blank' ); 1989 1990 } catch ( ignore ) {} 1991 1992 $( this ).empty(); 1993 1994 slide.isLoaded = false; 1995 1996 }); 1997 1998 }, 1999 2000 2001 // Wrap and append content to the slide 2002 // ====================================== 2003 2004 setContent : function ( slide, content ) { 2005 2006 var self = this; 2007 2008 if ( self.isClosing ) { 2009 return; 2010 } 2011 2012 self.hideLoading( slide ); 2013 2014 slide.$slide.empty(); 2015 2016 if ( isQuery( content ) && content.parent().length ) { 2017 2018 // If content is a jQuery object, then it will be moved to the slide. 2019 // The placeholder is created so we will know where to put it back. 2020 // If user is navigating gallery fast, then the content might be already inside fancyBox 2021 // ===================================================================================== 2022 2023 // Make sure content is not already moved to fancyBox 2024 content.parent( '.fancybox-slide--inline' ).trigger( 'onReset' ); 2025 2026 // Create temporary element marking original place of the content 2027 slide.$placeholder = $( '<div></div>' ).hide().insertAfter( content ); 2028 2029 // Make sure content is visible 2030 content.css('display', 'inline-block'); 2031 2032 } else if ( !slide.hasError ) { 2033 2034 // If content is just a plain text, try to convert it to html 2035 if ( $.type( content ) === 'string' ) { 2036 content = $('<div>').append( $.trim( content ) ).contents(); 2037 2038 // If we have text node, then add wrapping element to make vertical alignment work 2039 if ( content[0].nodeType === 3 ) { 2040 content = $('<div>').html( content ); 2041 } 2042 } 2043 2044 // If "filter" option is provided, then filter content 2045 if ( slide.opts.filter ) { 2046 content = $('<div>').html( content ).find( slide.opts.filter ); 2047 } 2048 2049 } 2050 2051 slide.$slide.one('onReset', function () { 2052 2053 // Pause all html5 video/audio 2054 $( this ).find( 'video,audio' ).trigger( 'pause' ); 2055 2056 // Put content back 2057 if ( slide.$placeholder ) { 2058 slide.$placeholder.after( content.hide() ).remove(); 2059 2060 slide.$placeholder = null; 2061 } 2062 2063 // Remove custom close button 2064 if ( slide.$smallBtn ) { 2065 slide.$smallBtn.remove(); 2066 2067 slide.$smallBtn = null; 2068 } 2069 2070 // Remove content and mark slide as not loaded 2071 if ( !slide.hasError ) { 2072 $(this).empty(); 2073 2074 slide.isLoaded = false; 2075 } 2076 2077 }); 2078 2079 slide.$content = $( content ).appendTo( slide.$slide ); 2080 2081 this.afterLoad( slide ); 2082 }, 2083 2084 // Display error message 2085 // ===================== 2086 2087 setError : function ( slide ) { 2088 2089 slide.hasError = true; 2090 2091 slide.$slide.removeClass( 'fancybox-slide--' + slide.type ); 2092 2093 this.setContent( slide, this.translate( slide, slide.opts.errorTpl ) ); 2094 2095 }, 2096 2097 2098 // Show loading icon inside the slide 2099 // ================================== 2100 2101 showLoading : function( slide ) { 2102 2103 var self = this; 2104 2105 slide = slide || self.current; 2106 2107 if ( slide && !slide.$spinner ) { 2108 slide.$spinner = $( self.opts.spinnerTpl ).appendTo( slide.$slide ); 2109 } 2110 2111 }, 2112 2113 // Remove loading icon from the slide 2114 // ================================== 2115 2116 hideLoading : function( slide ) { 2117 2118 var self = this; 2119 2120 slide = slide || self.current; 2121 2122 if ( slide && slide.$spinner ) { 2123 slide.$spinner.remove(); 2124 2125 delete slide.$spinner; 2126 } 2127 2128 }, 2129 2130 2131 // Adjustments after slide content has been loaded 2132 // =============================================== 2133 2134 afterLoad : function( slide ) { 2135 2136 var self = this; 2137 2138 if ( self.isClosing ) { 2139 return; 2140 } 2141 2142 slide.isLoading = false;
2143 slide.isLoaded = true; 2144 2145 self.trigger( 'afterLoad', slide ); 2146 2147 self.hideLoading( slide ); 2148 2149 if ( slide.opts.smallBtn && !slide.$smallBtn ) { 2150 slide.$smallBtn = $( self.translate( slide, slide.opts.btnTpl.smallBtn ) ).appendTo( slide.$content.filter('div,form').first() ); 2151 } 2152 2153 if ( slide.opts.protect && slide.$content && !slide.hasError ) { 2154 2155 // Disable right click 2156 slide.$content.on( 'contextmenu.fb', function( e ) { 2157 if ( e.button == 2 ) { 2158 e.preventDefault(); 2159 } 2160 2161 return true; 2162 }); 2163 2164 // Add fake element on top of the image 2165 // This makes a bit harder for user to select image 2166 if ( slide.type === 'image' ) { 2167 $( '<div class="fancybox-spaceball"></div>' ).appendTo( slide.$content ); 2168 } 2169 2170 } 2171 2172 self.revealContent( slide ); 2173 2174 }, 2175 2176 2177 // Make content visible 2178 // This method is called right after content has been loaded or 2179 // user navigates gallery and transition should start 2180 // ============================================================ 2181 2182 revealContent : function( slide ) { 2183 2184 var self = this; 2185 var $slide = slide.$slide; 2186 2187 var effect, effectClassName, duration, opacity, end, start = false;
2188 2189 effect = slide.opts[ self.firstRun ? 'animationEffect' : 'transitionEffect' ]; 2190 duration = slide.opts[ self.firstRun ? 'animationDuration' : 'transitionDuration' ]; 2191 2192 duration = parseInt( slide.forcedDuration === undefined ? duration : slide.forcedDuration, 10 ); 2193 2194 if ( slide.isMoved || slide.pos !== self.currPos || !duration ) { 2195 effect = false; 2196 } 2197 2198 // Check if can zoom 2199 if ( effect === 'zoom' && !( slide.pos === self.currPos && duration && slide.type === 'image' && !slide.hasError && ( start = self.getThumbPos( slide ) ) ) ) { 2200 effect = 'fade'; 2201 } 2202 2203 // Zoom animation 2204 // ============== 2205 2206 if ( effect === 'zoom' ) { 2207 end = self.getFitPos( slide ); 2208 2209 end.scaleX = end.width / start.width; 2210 end.scaleY = end.height / start.height; 2211 2212 delete end.width; 2213 delete end.height; 2214 2215 // Check if we need to animate opacity 2216 opacity = slide.opts.zoomOpacity; 2217 2218 if ( opacity == 'auto' ) { 2219 opacity = Math.abs( slide.width / slide.height - start.width / start.height ) > 0.1; 2220 } 2221 2222 if ( opacity ) { 2223 start.opacity = 0.1; 2224 end.opacity = 1; 2225 } 2226 2227 // Draw image at start position 2228 $.fancybox.setTranslate( slide.$content.removeClass( 'fancybox-is-hidden' ), start ); 2229 2230 forceRedraw( slide.$content ); 2231 2232 // Start animation 2233 $.fancybox.animate( slide.$content, end, duration, function() { 2234 self.complete(); 2235 }); 2236 2237 return; 2238 } 2239 2240 self.updateSlide( slide ); 2241 2242 2243 // Simply show content 2244 // =================== 2245 2246 if ( !effect ) { 2247 forceRedraw( $slide ); 2248 2249 slide.$content.removeClass( 'fancybox-is-hidden' ); 2250 2251 if ( slide.pos === self.currPos ) { 2252 self.complete(); 2253 } 2254 2255 return; 2256 } 2257 2258 $.fancybox.stop( $slide ); 2259 2260 effectClassName = 'fancybox-animated fancybox-slide--' + ( slide.pos >= self.prevPos ? 'next' : 'previous' ) + ' fancybox-fx-' + effect; 2261 2262 $slide.removeAttr( 'style' ).removeClass( 'fancybox-slide--current fancybox-slide--next fancybox-slide--previous' ).addClass( effectClassName ); 2263 2264 slide.$content.removeClass( 'fancybox-is-hidden' ); 2265 2266 //Force reflow for CSS3 transitions 2267 forceRedraw( $slide ); 2268 2269 $.fancybox.animate( $slide, 'fancybox-slide--current', duration, function(e) { 2270 $slide.removeClass( effectClassName ).removeAttr( 'style' ); 2271 2272 if ( slide.pos === self.currPos ) { 2273 self.complete(); 2274 } 2275 2276 }, true); 2277 2278 }, 2279 2280 2281 // Check if we can and have to zoom from thumbnail 2282 //================================================ 2283 2284 getThumbPos : function( slide ) { 2285 2286 var self = this; 2287 var rez = false; 2288 2289 // Check if element is inside the viewport by at least 1 pixel 2290 var isElementVisible = function( $el ) { 2291 var element = $el[0]; 2292 2293 var elementRect = element.getBoundingClientRect(); 2294 var parentRects = []; 2295 2296 var visibleInAllParents; 2297 2298 while ( element.parentElement !== null ) { 2299 if ( $(element.parentElement).css('overflow') === 'hidden' || $(element.parentElement).css('overflow') === 'auto' ) { 2300 parentRects.push(element.parentElement.getBoundingClientRect()); 2301 } 2302 2303 element = element.parentElement; 2304 } 2305 2306 visibleInAllParents = parentRects.every(function(parentRect){ 2307 var visiblePixelX = Math.min(elementRect.right, parentRect.right) - Math.max(elementRect.left, parentRect.left); 2308 var visiblePixelY = Math.min(elementRect.bottom, parentRect.bottom) - Math.max(elementRect.top, parentRect.top); 2309 2310 return visiblePixelX > 0 && visiblePixelY > 0; 2311 }); 2312 2313 return visibleInAllParents && 2314 elementRect.bottom > 0 && elementRect.right > 0 && 2315 elementRect.left < $(window).width() && elementRect.top < $(window).height(); 2316 }; 2317 2318 var $thumb = slide.opts.$thumb; 2319 var thumbPos = $thumb ? $thumb.offset() : 0; 2320 var slidePos; 2321 2322 if ( thumbPos && $thumb[0].ownerDocument === document && isElementVisible( $thumb ) ) { 2323 slidePos = self.$refs.stage.offset(); 2324 2325 rez = { 2326 top : thumbPos.top - slidePos.top + parseFloat( $thumb.css( "border-top-width" ) || 0 ), 2327 left : thumbPos.left - slidePos.left + parseFloat( $thumb.css( "border-left-width" ) || 0 ), 2328 width : $thumb.width(), 2329 height : $thumb.height(), 2330 scaleX : 1, 2331 scaleY : 1 2332 }; 2333 } 2334 2335 return rez; 2336 }, 2337 2338 2339 // Final adjustments after current gallery item is moved to position 2340 // and it`s content is loaded 2341 // ================================================================== 2342 2343 complete : function() { 2344 var self = this, 2345 current = self.current, 2346 slides = {}, 2347 promise; 2348 2349 if ( current.isMoved || !current.isLoaded || current.isComplete ) { 2350 return; 2351 } 2352 2353 current.isComplete = true; 2354 2355 current.$slide.siblings().trigger( 'onReset' ); 2356 2357 self.preload( 'inline' ); 2358 2359 // Trigger any CSS3 transiton inside the slide 2360 forceRedraw( current.$slide ); 2361 2362 current.$slide.addClass( 'fancybox-slide--complete' ); 2363 2364 // Remove unnecessary slides 2365 $.each( self.slides, function( key, slide ) { 2366 if ( slide.pos >= self.currPos - 1 && slide.pos <= self.currPos + 1 ) { 2367 slides[ slide.pos ] = slide; 2368 2369 } else if ( slide ) { 2370 $.fancybox.stop( slide.$slide ); 2371 2372 slide.$slide.off().remove(); 2373 } 2374 }); 2375 2376 self.slides = slides; 2377
2378 self.updateCursor(); 2379 2380 self.trigger( 'afterShow' ); 2381 2382 // Play first html5 video/audio 2383 current.$slide.find( 'video,audio' ).first().trigger( 'play' ); 2384 2385 // Try to focus on the first focusable element 2386 if ( $( document.activeElement ).is( '[disabled]' ) || ( current.opts.autoFocus && !( current.type == 'image' || current.type === 'iframe' ) ) ) { 2387 self.focus(); 2388 } 2389 2390 }, 2391 2392 2393 // Preload next and previous slides 2394 // ================================ 2395 2396 preload : function( type ) { 2397 var self = this, 2398 next = self.slides[ self.currPos + 1 ], 2399 prev = self.slides[ self.currPos - 1 ]; 2400 2401 if ( next && next.type === type ) { 2402 self.loadSlide( next ); 2403 } 2404 2405 if ( prev && prev.type === type ) { 2406 self.loadSlide( prev ); 2407 } 2408 }, 2409 2410 2411 // Try to find and focus on the first focusable element 2412 // ==================================================== 2413 2414 focus : function() { 2415 var current = this.current; 2416 var $el; 2417 2418 if ( this.isClosing ) { 2419 return; 2420 } 2421 2422 if ( current && current.isComplete ) { 2423 2424 // Look for first input with autofocus attribute 2425 $el = current.$slide.find('input[autofocus]:enabled:visible:first'); 2426 2427 if ( !$el.length ) { 2428 $el = current.$slide.find('button,:input,[tabindex],a').filter(':enabled:visible:first'); 2429 } 2430 } 2431 2432 $el = $el && $el.length ? $el : this.$refs.container; 2433 2434 $el.focus(); 2435 }, 2436 2437 2438 // Activates current instance - brings container to the front and enables keyboard, 2439 // notifies other instances about deactivating 2440 // ================================================================================= 2441 2442 activate : function () { 2443 var self = this; 2444 2445 // Deactivate all instances 2446 $( '.fancybox-container' ).each(function () { 2447 var instance = $(this).data( 'FancyBox' ); 2448 2449 // Skip self and closing instances 2450 if (instance && instance.id !== self.id && !instance.isClosing) { 2451 instance.trigger( 'onDeactivate' ); 2452 2453 instance.removeEvents(); 2454 2455 instance.isVisible = false; 2456 } 2457 2458 }); 2459 2460 self.isVisible = true; 2461 2462 if ( self.current || self.isIdle ) { 2463 self.update(); 2464 2465 self.updateControls(); 2466 } 2467 2468 self.trigger( 'onActivate' ); 2469 2470 self.addEvents(); 2471 }, 2472 2473 2474 // Start closing procedure 2475 // This will start "zoom-out" animation if needed and clean everything up afterwards 2476 // ================================================================================= 2477 2478 close : function( e, d ) { 2479 2480 var self = this; 2481 var current = self.current; 2482 2483 var effect, duration; 2484 var $what, opacity, start, end; 2485 2486 var done = function() { 2487 self.cleanUp( e ); 2488 }; 2489 2490 if ( self.isClosing ) { 2491 return false; 2492 } 2493 2494 self.isClosing = true; 2495 2496 // If beforeClose callback prevents closing, make sure content is centered 2497 if ( self.trigger( 'beforeClose', e ) === false ) { 2498 self.isClosing = false; 2499 2500 requestAFrame(function() { 2501 self.update(); 2502 }); 2503 2504 return false; 2505 } 2506 2507 // Remove all events 2508 // If there are multiple instances, they will be set again by "activate" method 2509 self.removeEvents(); 2510 2511 if ( current.timouts ) { 2512 clearTimeout( current.timouts ); 2513 } 2514 2515 $what = current.$content; 2516 effect = current.opts.animationEffect; 2517 duration = $.isNumeric( d ) ? d : ( effect ? current.opts.animationDuration : 0 ); 2518 2519 // Remove other slides 2520 current.$slide.off( transitionEnd ).removeClass( 'fancybox-slide--complete fancybox-slide--next fancybox-slide--previous fancybox-animated' ); 2521 2522 current.$slide.siblings().trigger( 'onReset' ).remove(); 2523 2524 // Trigger animations 2525 if ( duration ) { 2526 self.$refs.container.removeClass( 'fancybox-is-open' ).addClass( 'fancybox-is-closing' ); 2527 } 2528 2529 // Clean up 2530 self.hideLoading( current ); 2531 2532 self.hideControls(); 2533
2534 self.updateCursor(); 2535 2536 // Check if possible to zoom-out 2537 if ( effect === 'zoom' && !( e !== true && $what && duration && current.type === 'image' && !current.hasError && ( end = self.getThumbPos( current ) ) ) ) { 2538 effect = 'fade'; 2539 } 2540 2541 if ( effect === 'zoom' ) { 2542 $.fancybox.stop( $what ); 2543 2544 start = $.fancybox.getTranslate( $what ); 2545 2546 start.width = start.width * start.scaleX; 2547 start.height = start.height * start.scaleY; 2548 2549 // Check if we need to animate opacity 2550 opacity = current.opts.zoomOpacity; 2551 2552 if ( opacity == 'auto' ) { 2553 opacity = Math.abs( current.width / current.height - end.width / end.height ) > 0.1; 2554 } 2555 2556 if ( opacity ) { 2557 end.opacity = 0; 2558 } 2559 2560 start.scaleX = start.width / end.width; 2561 start.scaleY = start.height / end.height; 2562 2563 start.width = end.width; 2564 start.height = end.height; 2565 2566 $.fancybox.setTranslate( current.$content, start ); 2567 2568 forceRedraw( current.$content ); 2569 2570 $.fancybox.animate( current.$content, end, duration, done ); 2571 2572 return true; 2573 } 2574 2575 if ( effect && duration ) { 2576 2577 // If skip animation 2578 if ( e === true ) { 2579 setTimeout( done, duration ); 2580 2581 } else { 2582 $.fancybox.animate( current.$slide.removeClass( 'fancybox-slide--current' ), 'fancybox-animated fancybox-slide--previous fancybox-fx-' + effect, duration, done ); 2583 } 2584 2585 } else { 2586 done(); 2587 } 2588 2589 return true; 2590 }, 2591 2592 2593 // Final adjustments after removing the instance 2594 // ============================================= 2595 2596 cleanUp : function( e ) { 2597 var self = this, 2598 $body = $( 'body' ), 2599 instance, 2600 offset; 2601 2602 self.current.$slide.trigger( 'onReset' ); 2603 2604 self.$refs.container.empty().remove(); 2605 2606 self.trigger( 'afterClose', e ); 2607 2608 // Place back focus 2609 if ( self.$lastFocus && !!self.current.opts.backFocus ) { 2610 self.$lastFocus.focus(); 2611 } 2612 2613 self.current = null; 2614 2615 // Check if there are other instances 2616 instance = $.fancybox.getInstance(); 2617 2618 if ( instance ) { 2619 instance.activate(); 2620 2621 } else { 2622 2623 $W.scrollTop( self.scrollTop ).scrollLeft( self.scrollLeft ); 2624 2625 $body.removeClass( 'fancybox-active compensate-for-scrollbar' ); 2626 2627 if ( $body.hasClass( 'fancybox-iosfix' ) ) { 2628 offset = parseInt(document.body.style.top, 10); 2629 2630 $body.removeClass( 'fancybox-iosfix' ).css( 'top', '' ).scrollTop( offset * -1 ); 2631 } 2632 2633 $( '#fancybox-style-noscroll' ).remove(); 2634 2635 } 2636 2637 }, 2638 2639 2640 // Call callback and trigger an event 2641 // ================================== 2642 2643 trigger : function( name, slide ) { 2644 var args = Array.prototype.slice.call(arguments, 1), 2645 self = this, 2646 obj = slide && slide.opts ? slide : self.current, 2647 rez; 2648 2649 if ( obj ) { 2650 args.unshift( obj ); 2651 2652 } else { 2653 obj = self; 2654 } 2655 2656 args.unshift( self ); 2657 2658 if ( $.isFunction( obj.opts[ name ] ) ) { 2659 rez = obj.opts[ name ].apply( obj, args ); 2660 } 2661 2662 if ( rez === false ) { 2663 return rez; 2664 } 2665 2666 if ( name === 'afterClose' || !self.$refs ) { 2667 $D.trigger( name + '.fb', args ); 2668 2669 } else { 2670 self.$refs.container.trigger( name + '.fb', args ); 2671 } 2672 2673 }, 2674 2675 2676 // Update infobar values, navigation button states and reveal caption 2677 // ================================================================== 2678 2679 updateControls : function ( force ) { 2680 2681 var self = this; 2682 2683 var current = self.current, 2684 index = current.index, 2685 caption = current.opts.caption, 2686 $container = self.$refs.container, 2687 $caption = self.$refs.caption; 2688 2689 // Recalculate content dimensions 2690 current.$slide.trigger( 'refresh' ); 2691 2692 self.$caption = caption && caption.length ? $caption.html( caption ) : null; 2693
2694 if ( !self.isHiddenControls && !self.isIdle ) { 2695 self.showControls(); 2696 } 2697 2698 // Update info and navigation elements 2699 $container.find('[data-fancybox-count]').html( self.group.length ); 2700 $container.find('[data-fancybox-index]').html( index + 1 ); 2701 2702 $container.find('[data-fancybox-prev]').prop( 'disabled', ( !current.opts.loop && index <= 0 ) ); 2703 $container.find('[data-fancybox-next]').prop( 'disabled', ( !current.opts.loop && index >= self.group.length - 1 ) ); 2704 2705 if ( current.type === 'image' ) { 2706 2707 // Update download button source 2708 $container.find('[data-fancybox-download]').attr( 'href', current.opts.image.src || current.src ).show(); 2709 2710 } else { 2711 $container.find('[data-fancybox-download],[data-fancybox-zoom]').hide(); 2712 } 2713 }, 2714 2715 // Hide toolbar and caption 2716 // ======================== 2717 2718 hideControls : function () { 2719 2720 this.isHiddenControls = true; 2721 2722 this.$refs.container.removeClass( 'fancybox-show-infobar fancybox-show-toolbar fancybox-show-caption fancybox-show-nav' ); 2723 2724 }, 2725 2726 showControls : function() { 2727 var self = this; 2728 var opts = self.current ? self.current.opts : self.opts; 2729 var $container = self.$refs.container; 2730 2731 self.isHiddenControls = false; 2732 self.idleSecondsCounter = 0; 2733 2734 $container 2735 .toggleClass( 'fancybox-show-toolbar', !!( opts.toolbar && opts.buttons ) ) 2736 .toggleClass( 'fancybox-show-infobar', !!( opts.infobar && self.group.length > 1 ) ) 2737 .toggleClass( 'fancybox-show-nav', !!( opts.arrows && self.group.length > 1 ) ) 2738 .toggleClass( 'fancybox-is-modal', !!opts.modal ); 2739 2740 if ( self.$caption ) { 2741 $container.addClass( 'fancybox-show-caption '); 2742 2743 } else { 2744 $container.removeClass( 'fancybox-show-caption' ); 2745 } 2746 2747 }, 2748 2749 2750 // Toggle toolbar and caption 2751 // ========================== 2752 2753 toggleControls : function() { 2754 if ( this.isHiddenControls ) { 2755 this.showControls(); 2756 2757 } else { 2758 this.hideControls(); 2759 } 2760 2761 }, 2762 2763 2764 }); 2765 2766 2767 $.fancybox = { 2768 2769 version : "3.2.10", 2770 defaults : defaults, 2771 2772 2773 // Get current instance and execute a command. 2774 // 2775 // Examples of usage: 2776 // 2777 // $instance = $.fancybox.getInstance(); 2778 // $.fancybox.getInstance().jumpTo( 1 ); 2779 // $.fancybox.getInstance( 'jumpTo', 1 ); 2780 // $.fancybox.getInstance( function() { 2781 // console.info( this.currIndex ); 2782 // }); 2783 // ====================================================== 2784 2785 getInstance : function ( command ) { 2786 var instance = $('.fancybox-container:not(".fancybox-is-closing"):last').data( 'FancyBox' ); 2787 var args = Array.prototype.slice.call(arguments, 1); 2788 2789 if ( instance instanceof FancyBox ) { 2790 2791 if ( $.type( command ) === 'string' ) { 2792 instance[ command ].apply( instance, args ); 2793 2794 } else if ( $.type( command ) === 'function' ) { 2795 command.apply( instance, args ); 2796 } 2797 2798 return instance; 2799 } 2800 2801 return false; 2802 2803 }, 2804 2805 2806 // Create new instance 2807 // =================== 2808 2809 open : function ( items, opts, index ) { 2810 return new FancyBox( items, opts, index ); 2811 }, 2812 2813 2814 // Close current or all instances 2815 // ============================== 2816 2817 close : function ( all ) { 2818 var instance = this.getInstance(); 2819 2820 if ( instance ) { 2821 instance.close(); 2822 2823 // Try to find and close next instance 2824 2825 if ( all === true ) { 2826 this.close(); 2827 } 2828 } 2829 2830 }, 2831 2832 // Close instances and unbind all events 2833 // ============================== 2834 2835 destroy : function() { 2836 2837 this.close( true ); 2838 2839 $D.off( 'click.fb-start' ); 2840 2841 }, 2842 2843 2844 // Try to detect mobile devices 2845 // ============================ 2846 2847 isMobile : document.createTouch !== undefined && /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent), 2848 2849 2850 // Detect if 'translate3d' support is available 2851 // ============================================ 2852 2853 use3d : (function() { 2854 var div = document.createElement('div'); 2855 2856 return window.getComputedStyle && window.getComputedStyle( div ).getPropertyValue('transform') && !(document.documentMode && document.documentMode < 11); 2857 }()), 2858 2859 // Helper function to get current visual state of an element 2860 // returns array[ top, left, horizontal-scale, vertical-scale, opacity ] 2861 // ===================================================================== 2862 2863 getTranslate : function( $el ) { 2864 var matrix; 2865 2866 if ( !$el || !$el.length ) { 2867 return false;
2868 } 2869 2870 matrix = $el.eq( 0 ).css('transform'); 2871 2872 if ( matrix && matrix.indexOf( 'matrix' ) !== -1 ) { 2873 matrix = matrix.split('(')[1]; 2874 matrix = matrix.split(')')[0]; 2875 matrix = matrix.split(','); 2876 } else { 2877 matrix = []; 2878 } 2879 2880 if ( matrix.length ) { 2881 2882 // If IE 2883 if ( matrix.length > 10 ) { 2884 matrix = [ matrix[13], matrix[12], matrix[0], matrix[5] ]; 2885 2886 } else { 2887 matrix = [ matrix[5], matrix[4], matrix[0], matrix[3]]; 2888 } 2889 2890 matrix = matrix.map(parseFloat); 2891 2892 } else { 2893 matrix = [ 0, 0, 1, 1 ]; 2894 2895 var transRegex = /\.*translate\((.*)px,(.*)px\)/i; 2896 var transRez = transRegex.exec( $el.eq( 0 ).attr('style') ); 2897 2898 if ( transRez ) { 2899 matrix[ 0 ] = parseFloat( transRez[2] ); 2900 matrix[ 1 ] = parseFloat( transRez[1] ); 2901 } 2902 } 2903 2904 return { 2905 top : matrix[ 0 ], 2906 left : matrix[ 1 ], 2907 scaleX : matrix[ 2 ], 2908 scaleY : matrix[ 3 ], 2909 opacity : parseFloat( $el.css('opacity') ), 2910 width : $el.width(), 2911 height : $el.height() 2912 }; 2913 2914 }, 2915 2916 2917 // Shortcut for setting "translate3d" properties for element 2918 // Can set be used to set opacity, too 2919 // ======================================================== 2920 2921 setTranslate : function( $el, props ) { 2922 var str = ''; 2923 var css = {}; 2924 2925 if ( !$el || !props ) { 2926 return; 2927 } 2928 2929 if ( props.left !== undefined || props.top !== undefined ) { 2930 str = ( props.left === undefined ? $el.position().left : props.left ) + 'px, ' + ( props.top === undefined ? $el.position().top : props.top ) + 'px'; 2931 2932 if ( this.use3d ) { 2933 str = 'translate3d(' + str + ', 0px)'; 2934 2935 } else { 2936 str = 'translate(' + str + ')'; 2937 } 2938 } 2939 2940 if ( props.scaleX !== undefined && props.scaleY !== undefined ) { 2941 str = (str.length ? str + ' ' : '') + 'scale(' + props.scaleX + ', ' + props.scaleY + ')'; 2942 } 2943 2944 if ( str.length ) { 2945 css.transform = str; 2946 } 2947 2948 if ( props.opacity !== undefined ) { 2949 css.opacity = props.opacity; 2950 } 2951 2952 if ( props.width !== undefined ) { 2953 css.width = props.width; 2954 } 2955 2956 if ( props.height !== undefined ) { 2957 css.height = props.height; 2958 } 2959 2960 return $el.css( css ); 2961 }, 2962 2963 2964 // Simple CSS transition handler 2965 // ============================= 2966 2967 animate : function ( $el, to, duration, callback, leaveAnimationName ) { 2968 if ( $.isFunction( duration ) ) { 2969 callback = duration; 2970 duration = null; 2971 } 2972 2973 if ( !$.isPlainObject( to ) ) { 2974 $el.removeAttr( 'style' ); 2975 } 2976 2977 $el.on( transitionEnd, function(e) { 2978 2979 // Skip events from child elements and z-index change 2980 if ( e && e.originalEvent && ( !$el.is( e.originalEvent.target ) || e.originalEvent.propertyName == 'z-index' ) ) { 2981 return; 2982 } 2983 2984 $.fancybox.stop( $el ); 2985 2986 if ( $.isPlainObject( to ) ) { 2987 2988 if ( to.scaleX !== undefined && to.scaleY !== undefined ) { 2989 $el.css( 'transition-duration', '' ); 2990 2991 to.width = Math.round( $el.width() * to.scaleX ); 2992 to.height = Math.round( $el.height() * to.scaleY ); 2993 2994 to.scaleX = 1; 2995 to.scaleY = 1; 2996 2997 $.fancybox.setTranslate( $el, to ); 2998 } 2999 3000 if ( leaveAnimationName === false ) { 3001 $el.removeAttr( 'style' ); 3002 } 3003 3004 } else if ( leaveAnimationName !== true ) { 3005 $el.removeClass( to ); 3006 } 3007 3008 if ( $.isFunction( callback ) ) { 3009 callback( e ); 3010 } 3011 3012 }); 3013 3014 if ( $.isNumeric( duration ) ) { 3015 $el.css( 'transition-duration', duration + 'ms' ); 3016 } 3017 3018 if ( $.isPlainObject( to ) ) { 3019 $.fancybox.setTranslate( $el, to ); 3020 3021 } else { 3022 $el.addClass( to ); 3023 } 3024 3025 if ( to.scaleX && $el.hasClass( 'fancybox-image-wrap' ) ) { 3026 $el.parent().addClass( 'fancybox-is-scaling' ); 3027 } 3028 3029 // Make sure that `transitionend` callback gets fired 3030 $el.data("timer", setTimeout(function() { 3031 $el.trigger( 'transitionend' ); 3032 }, duration + 16)); 3033 3034 }, 3035 3036 stop : function( $el ) { 3037 clearTimeout( $el.data("timer") ); 3038 3039 $el.off( 'transitionend' ).css( 'transition-duration', '' ); 3040 3041 if ( $el.hasClass( 'fancybox-image-wrap' ) ) { 3042 $el.parent().removeClass( 'fancybox-is-scaling' ); 3043 } 3044 } 3045 3046 }; 3047 3048 3049 // Default click handler for "fancyboxed" links 3050 // ============================================ 3051 3052 function _run( e ) { 3053 var $target = $( e.currentTarget ), 3054 opts = e.data ? e.data.options : {}, 3055 value = $target.attr( 'data-fancybox' ) || '', 3056 index = 0, 3057 items = []; 3058 3059 // Avoid opening multiple times 3060 if ( e.isDefaultPrevented() ) { 3061 return; 3062 } 3063 3064 e.preventDefault(); 3065 3066 // Get all related items and find index for clicked one 3067 if ( value ) { 3068 items = opts.selector ? $( opts.selector ) : ( e.data ? e.data.items : [] ); 3069 items = items.length ? items.filter( '[data-fancybox="' + value + '"]' ) : $( '[data-fancybox="' + value + '"]' ); 3070 3071 index = items.index( $target ); 3072 3073 // Sometimes current item can not be found 3074 // (for example, when slider clones items) 3075 if ( index < 0 ) { 3076 index = 0; 3077 } 3078 3079 } else { 3080 items = [ $target ]; 3081 } 3082 3083 $.fancybox.open( items, opts, index ); 3084 } 3085 3086 3087 // Create a jQuery plugin 3088 // ====================== 3089 3090 $.fn.fancybox = function (options) { 3091 var selector; 3092 3093 options = options || {}; 3094 selector = options.selector || false; 3095 3096 if ( selector ) { 3097 3098 $( 'body' ).off( 'click.fb-start', selector ).on( 'click.fb-start', selector, { 3099 options : options 3100 }, _run ); 3101 3102 } else { 3103 3104 this.off( 'click.fb-start' ).on( 'click.fb-start', { 3105 items : this, 3106 options : options 3107 }, _run); 3108 3109 } 3110 3111 return this; 3112 }; 3113 3114 3115 // Self initializing plugin 3116 // ======================== 3117 3118 $D.on( 'click.fb-start', '[data-fancybox]', _run ); 3119 3120}( window, document, window.jQuery || jQuery )); 3121 3122// ========================================================================== 3123// 3124// Media 3125// Adds additional media type support 3126// 3127// ========================================================================== 3128;(function ($) { 3129 3130 'use strict'; 3131 3132 // Formats matching url to final form 3133 3134 var format = function (url, rez, params) { 3135 if ( !url ) { 3136 return; 3137 } 3138 3139 params = params || ''; 3140 3141 if ( $.type(params) === "object" ) { 3142 params = $.param(params, true); 3143 } 3144 3145 $.each(rez, function (key, value) { 3146 url = url.replace('$' + key, value || ''); 3147 }); 3148 3149 if (params.length) { 3150 url += (url.indexOf('?') > 0 ? '&' : '?') + params; 3151 } 3152 3153 return url; 3154 }; 3155 3156 // Object containing properties for each media type 3157 3158 var defaults = { 3159 youtube : { 3160 matcher : /(youtube\.com|youtu\.be|youtube\-nocookie\.com)\/(watch\?(.*&)?v=|v\/|u\/|embed\/?)?(videoseries\?list=(.*)|[\w-]{11}|\?listType=(.*)&list=(.*))(.*)/i, 3161 params : { 3162 autoplay : 1, 3163 autohide : 1, 3164 fs : 1, 3165 rel : 0, 3166 hd : 1, 3167 wmode : 'transparent', 3168 enablejsapi : 1, 3169 html5 : 1 3170 }, 3171 paramPlace : 8, 3172 type : 'iframe', 3173 url : '//www.youtube.com/embed/$4', 3174 thumb : '//img.youtube.com/vi/$4/hqdefault.jpg' 3175 }, 3176 3177 vimeo : { 3178 matcher : /^.+vimeo.com\/(.*\/)?([\d]+)(.*)?/, 3179 params : { 3180 autoplay : 1, 3181 hd : 1, 3182 show_title : 1, 3183 show_byline : 1, 3184 show_portrait : 0, 3185 fullscreen : 1, 3186 api : 1 3187 }, 3188 paramPlace : 3, 3189 type : 'iframe', 3190 url : '//player.vimeo.com/video/$2' 3191 }, 3192 3193 metacafe : { 3194 matcher : /metacafe.com\/watch\/(\d+)\/(.*)?/, 3195 type : 'iframe', 3196 url : '//www.metacafe.com/embed/$1/?ap=1' 3197 }, 3198 3199 dailymotion : { 3200 matcher : /dailymotion.com\/video\/(.*)\/?(.*)/, 3201 params : { 3202 additionalInfos : 0, 3203 autoStart : 1 3204 }, 3205 type : 'iframe', 3206 url : '//www.dailymotion.com/embed/video/$1' 3207 }, 3208 3209 vine : { 3210 matcher : /vine.co\/v\/([a-zA-Z0-9\?\=\-]+)/, 3211 type : 'iframe', 3212 url : '//vine.co/v/$1/embed/simple' 3213 }, 3214 3215 instagram : { 3216 matcher : /(instagr\.am|instagram\.com)\/p\/([a-zA-Z0-9_\-]+)\/?/i, 3217 type : 'image', 3218 url : '//$1/p/$2/media/?size=l' 3219 }, 3220 3221 // Examples: 3222 // http://maps.google.com/?ll=48.857995,2.294297&spn=0.007666,0.021136&t=m&z=16 3223 // https://www.google.com/maps/@37.7852006,-122.4146355,14.65z 3224 // https://www.google.com/maps/place/Googleplex/@37.4220041,-122.0833494,17z/data=!4m5!3m4!1s0x0:0x6c296c66619367e0!8m2!3d37.4219998!4d-122.0840572 3225 gmap_place : { 3226 matcher : /(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(((maps\/(place\/(.*)\/)?\@(.*),(\d+.?\d+?)z))|(\?ll=))(.*)?/i, 3227 type : 'iframe', 3228 url : function (rez) { 3229 return '//maps.google.' + rez[2] + '/?ll=' + ( rez[9] ? rez[9] + '&z=' + Math.floor( rez[10] ) + ( rez[12] ? rez[12].replace(/^\//, "&") : '' ) : rez[12] ) + '&output=' + ( rez[12] && rez[12].indexOf('layer=c') > 0 ? 'svembed' : 'embed' ); 3230 } 3231 }, 3232 3233 // Examples: 3234 // https://www.google.com/maps/search/Empire+State+Building/ 3235 // https://www.google.com/maps/search/?api=1&query=centurylink+field 3236 // https://www.google.com/maps/search/?api=1&query=47.5951518,-122.3316393 3237 gmap_search : { 3238 matcher : /(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(maps\/search\/)(.*)/i, 3239 type : 'iframe', 3240 url : function (rez) { 3241 return '//maps.google.' + rez[2] + '/maps?q=' + rez[5].replace('query=', 'q=').replace('api=1', '') + '&output=embed'; 3242 } 3243 } 3244 }; 3245 3246 $(document).on('objectNeedsType.fb', function (e, instance, item) { 3247 3248 var url = item.src || '', 3249 type = false, 3250 media, 3251 thumb, 3252 rez, 3253 params,
vendor: 4,540 bytes, lines 3254-3424
3254 urlParams, 3255 paramObj, 3256 provider; 3257 3258 media = $.extend( true, {}, defaults, item.opts.media ); 3259 3260 // Look for any matching media type 3261 $.each(media, function ( providerName, providerOpts ) { 3262 rez = url.match( providerOpts.matcher ); 3263 3264 if ( !rez ) { 3265 return; 3266 } 3267 3268 type = providerOpts.type; 3269 paramObj = {}; 3270 3271 if ( providerOpts.paramPlace && rez[ providerOpts.paramPlace ] ) { 3272 urlParams = rez[ providerOpts.paramPlace ]; 3273 3274 if ( urlParams[ 0 ] == '?' ) { 3275 urlParams = urlParams.substring(1); 3276 } 3277 3278 urlParams = urlParams.split('&'); 3279 3280 for ( var m = 0; m < urlParams.length; ++m ) { 3281 var p = urlParams[ m ].split('=', 2); 3282 3283 if ( p.length == 2 ) { 3284 paramObj[ p[0] ] = decodeURIComponent( p[1].replace(/\+/g, " ") ); 3285 } 3286 } 3287 } 3288 3289 params = $.extend( true, {}, providerOpts.params, item.opts[ providerName ], paramObj ); 3290 3291 url = $.type( providerOpts.url ) === "function" ? providerOpts.url.call( this, rez, params, item ) : format( providerOpts.url, rez, params ); 3292 thumb = $.type( providerOpts.thumb ) === "function" ? providerOpts.thumb.call( this, rez, params, item ) : format( providerOpts.thumb, rez ); 3293 3294 if ( providerName === 'vimeo' ) { 3295 url = url.replace('&%23', '#'); 3296 } 3297 3298 return false; 3299 }); 3300 3301 // If it is found, then change content type and update the url 3302 3303 if ( type ) { 3304 item.src = url; 3305 item.type = type; 3306 3307 if ( !item.opts.thumb && !( item.opts.$thumb && item.opts.$thumb.length ) ) { 3308 item.opts.thumb = thumb; 3309 } 3310 3311 if ( type === 'iframe' ) { 3312 $.extend(true, item.opts, { 3313 iframe : { 3314 preload : false, 3315 attr : { 3316 scrolling : "no" 3317 } 3318 } 3319 }); 3320 3321 item.contentProvider = provider; 3322 3323 item.opts.slideClass += ' fancybox-slide--' + ( provider == 'gmap_place' || provider == 'gmap_search' ? 'map' : 'video' ); 3324 } 3325 3326 } else if ( url ) { 3327 item.type = item.opts.defaultType; 3328 } 3329 3330 }); 3331 3332}( window.jQuery || jQuery )); 3333 3334// ========================================================================== 3335// 3336// Guestures 3337// Adds touch guestures, handles click and tap events 3338// 3339// ========================================================================== 3340;(function (window, document, $) { 3341 'use strict'; 3342 3343 var requestAFrame = (function () { 3344 return window.requestAnimationFrame || 3345 window.webkitRequestAnimationFrame || 3346 window.mozRequestAnimationFrame || 3347 window.oRequestAnimationFrame || 3348 // if all else fails, use setTimeout 3349 function (callback) { 3350 return window.setTimeout(callback, 1000 / 60); 3351 }; 3352 })(); 3353 3354 var cancelAFrame = (function () { 3355 return window.cancelAnimationFrame || 3356 window.webkitCancelAnimationFrame || 3357 window.mozCancelAnimationFrame || 3358 window.oCancelAnimationFrame || 3359 function (id) { 3360 window.clearTimeout(id); 3361 }; 3362 })(); 3363 3364 var pointers = function( e ) { 3365 var result = []; 3366 3367 e = e.originalEvent || e || window.e; 3368 e = e.touches && e.touches.length ? e.touches : ( e.changedTouches && e.changedTouches.length ? e.changedTouches : [ e ] ); 3369 3370 for ( var key in e ) { 3371 3372 if ( e[ key ].pageX ) { 3373 result.push( { x : e[ key ].pageX, y : e[ key ].pageY } ); 3374 3375 } else if ( e[ key ].clientX ) { 3376 result.push( { x : e[ key ].clientX, y : e[ key ].clientY } ); 3377 } 3378 } 3379 3380 return result; 3381 }; 3382 3383 var distance = function( point2, point1, what ) { 3384 if ( !point1 || !point2 ) { 3385 return 0; 3386 } 3387 3388 if ( what === 'x' ) { 3389 return point2.x - point1.x; 3390 3391 } else if ( what === 'y' ) { 3392 return point2.y - point1.y; 3393 } 3394 3395 return Math.sqrt( Math.pow( point2.x - point1.x, 2 ) + Math.pow( point2.y - point1.y, 2 ) ); 3396 }; 3397 3398 var isClickable = function( $el ) { 3399 if ( $el.is('a,area,button,[role="button"],input,label,select,summary,textarea') || $.isFunction( $el.get(0).onclick ) || $el.data('selectable') ) { 3400 return true; 3401 } 3402 3403 // Check for attributes like data-fancybox-next or data-fancybox-close 3404 for ( var i = 0, atts = $el[0].attributes, n = atts.length; i < n; i++ ) { 3405 if ( atts[i].nodeName.substr(0, 14) === 'data-fancybox-' ) { 3406 return true; 3407 } 3408 } 3409 3410 return false; 3411 }; 3412 3413 var hasScrollbars = function( el ) { 3414 var overflowY = window.getComputedStyle( el )['overflow-y']; 3415 var overflowX = window.getComputedStyle( el )['overflow-x']; 3416 3417 var vertical = (overflowY === 'scroll' || overflowY === 'auto') && el.scrollHeight > el.clientHeight; 3418 var horizontal = (overflowX === 'scroll' || overflowX === 'auto') && el.scrollWidth > el.clientWidth; 3419 3420 return vertical || horizontal; 3421 }; 3422 3423 var isScrollable = function ( $el ) { 3424 var rez = false;
3425 3426 while ( true ) { 3427 rez = hasScrollbars( $el.get(0) ); 3428 3429 if ( rez ) { 3430 break; 3431 } 3432 3433 $el = $el.parent(); 3434 3435 if ( !$el.length || $el.hasClass( 'fancybox-stage' ) || $el.is( 'body' ) ) { 3436 break; 3437 } 3438 } 3439 3440 return rez; 3441 }; 3442 3443 3444 var Guestures = function ( instance ) { 3445 var self = this; 3446 3447 self.instance = instance; 3448 3449 self.$bg = instance.$refs.bg; 3450 self.$stage = instance.$refs.stage; 3451 self.$container = instance.$refs.container; 3452 3453 self.destroy(); 3454 3455 self.$container.on( 'touchstart.fb.touch mousedown.fb.touch', $.proxy(self, 'ontouchstart') ); 3456 }; 3457 3458 Guestures.prototype.destroy = function() { 3459 this.$container.off( '.fb.touch' ); 3460 }; 3461 3462 Guestures.prototype.ontouchstart = function( e ) { 3463 var self = this; 3464 3465 var $target = $( e.target ); 3466 var instance = self.instance; 3467 var current = instance.current; 3468 var $content = current.$content; 3469 3470 var isTouchDevice = ( e.type == 'touchstart' ); 3471 3472 // Do not respond to both (touch and mouse) events 3473 if ( isTouchDevice ) { 3474 self.$container.off( 'mousedown.fb.touch' ); 3475 } 3476 3477 // Ignore right click 3478 if ( e.originalEvent && e.originalEvent.button == 2 ) { 3479 return; 3480 } 3481 3482 // Ignore taping on links, buttons, input elements 3483 if ( !$target.length || isClickable( $target ) || isClickable( $target.parent() ) ) { 3484 return; 3485 } 3486 3487 // Ignore clicks on the scrollbar 3488 if ( !$target.is('img') && e.originalEvent.clientX > $target[0].clientWidth + $target.offset().left ) { 3489 return; 3490 } 3491 3492 // Ignore clicks while zooming or closing 3493 if ( !current || self.instance.isAnimating || self.instance.isClosing ) { 3494 e.stopPropagation(); 3495 e.preventDefault(); 3496 3497 return; 3498 } 3499 3500 self.realPoints = self.startPoints = pointers( e ); 3501 3502 if ( !self.startPoints ) { 3503 return; 3504 } 3505 3506 e.stopPropagation(); 3507 3508 self.startEvent = e; 3509 3510 self.canTap = true; 3511 self.$target = $target; 3512 self.$content = $content; 3513 self.opts = current.opts.touch; 3514 3515 self.isPanning = false; 3516 self.isSwiping = false; 3517 self.isZooming = false; 3518 self.isScrolling = false; 3519 3520 self.sliderStartPos = self.sliderLastPos || { top: 0, left: 0 }; 3521 self.contentStartPos = $.fancybox.getTranslate( self.$content ); 3522 self.contentLastPos = null; 3523 3524 self.startTime = new Date().getTime(); 3525 self.distanceX = self.distanceY = self.distance = 0; 3526 3527 self.canvasWidth = Math.round( current.$slide[0].clientWidth ); 3528 self.canvasHeight = Math.round( current.$slide[0].clientHeight ); 3529 3530 $(document) 3531 .off( '.fb.touch' ) 3532 .on( isTouchDevice ? 'touchend.fb.touch touchcancel.fb.touch' : 'mouseup.fb.touch mouseleave.fb.touch', $.proxy(self, "ontouchend")) 3533 .on( isTouchDevice ? 'touchmove.fb.touch' : 'mousemove.fb.touch', $.proxy(self, "ontouchmove")); 3534 3535 if ( $.fancybox.isMobile ) { 3536 document.addEventListener('scroll', self.onscroll, true); 3537 } 3538 3539 if ( !(self.opts || instance.canPan() ) || !( $target.is( self.$stage ) || self.$stage.find( $target ).length ) ) { 3540 3541 // Prevent image ghosting while dragging 3542 if ( $target.is('img') ) { 3543 e.preventDefault(); 3544 } 3545 3546 return; 3547 } 3548 3549 if ( !( $.fancybox.isMobile && ( isScrollable( $target ) || isScrollable( $target.parent() ) ) ) ) { 3550 e.preventDefault(); 3551 } 3552 3553 if ( self.startPoints.length === 1 ) { 3554 if ( current.type === 'image' && ( self.contentStartPos.width > self.canvasWidth + 1 || self.contentStartPos.height > self.canvasHeight + 1 ) ) { 3555 $.fancybox.stop( self.$content ); 3556 3557 self.$content.css( 'transition-duration', '' ); 3558 3559 self.isPanning = true; 3560 3561 } else { 3562 self.isSwiping = true; 3563 } 3564 3565 self.$container.addClass( 'fancybox-controls--isGrabbing' ); 3566 } 3567 3568 if ( self.startPoints.length === 2 && !instance.isAnimating && !current.hasError && current.type === 'image' && ( current.isLoaded || current.$ghost ) ) { 3569 self.canTap = false; 3570 self.isSwiping = false; 3571 self.isPanning = false; 3572 3573 self.isZooming = true; 3574 3575 $.fancybox.stop( self.$content ); 3576 3577 self.$content.css( 'transition-duration', '' ); 3578 3579 self.centerPointStartX = ( ( self.startPoints[0].x + self.startPoints[1].x ) * 0.5 ) - $(window).scrollLeft(); 3580 self.centerPointStartY = ( ( self.startPoints[0].y + self.startPoints[1].y ) * 0.5 ) - $(window).scrollTop(); 3581 3582 self.percentageOfImageAtPinchPointX = ( self.centerPointStartX - self.contentStartPos.left ) / self.contentStartPos.width; 3583 self.percentageOfImageAtPinchPointY = ( self.centerPointStartY - self.contentStartPos.top ) / self.contentStartPos.height; 3584 3585 self.startDistanceBetweenFingers = distance( self.startPoints[0], self.startPoints[1] ); 3586 } 3587 3588 }; 3589 3590 Guestures.prototype.onscroll = function(e) { 3591 self.isScrolling = true; 3592 }; 3593 3594 Guestures.prototype.ontouchmove = function( e ) { 3595 var self = this, 3596 $target = $(e.target); 3597 3598 if ( self.isScrolling || !( $target.is( self.$stage ) || self.$stage.find( $target ).length ) ) { 3599 self.canTap = false;
vendor: 8,959 bytes, lines 3600-3915
3600 3601 return; 3602 } 3603 3604 self.newPoints = pointers( e ); 3605 3606 if ( !( self.opts || self.instance.canPan() ) || !self.newPoints || !self.newPoints.length ) { 3607 return; 3608 } 3609 3610 if ( !(self.isSwiping && self.isSwiping === true) ) { 3611 e.preventDefault(); 3612 } 3613 3614 self.distanceX = distance( self.newPoints[0], self.startPoints[0], 'x' ); 3615 self.distanceY = distance( self.newPoints[0], self.startPoints[0], 'y' ); 3616 3617 self.distance = distance( self.newPoints[0], self.startPoints[0] ) 3618 3619 // Skip false ontouchmove events (Chrome) 3620 if ( self.distance > 0 ) { 3621 if ( self.isSwiping ) { 3622 self.onSwipe(e); 3623 3624 } else if ( self.isPanning ) { 3625 self.onPan(); 3626 3627 } else if ( self.isZooming ) { 3628 self.onZoom(); 3629 } 3630 } 3631 3632 }; 3633 3634 Guestures.prototype.onSwipe = function(e) { 3635 var self = this, 3636 swiping = self.isSwiping, 3637 left = self.sliderStartPos.left || 0, 3638 angle; 3639 3640 // If direction is not yet determined 3641 if ( swiping === true ) { 3642 3643 // We need at least 10px distance to correctly calculate an angle 3644 if ( Math.abs( self.distance ) > 10 ) { 3645 self.canTap = false; 3646 3647 if ( self.instance.group.length < 2 && self.opts.vertical ) { 3648 self.isSwiping = 'y'; 3649 3650 } else if ( self.instance.isDragging || self.opts.vertical === false || ( self.opts.vertical === 'auto' && $( window ).width() > 800 ) ) { 3651 self.isSwiping = 'x'; 3652 3653 } else { 3654 angle = Math.abs( Math.atan2( self.distanceY, self.distanceX ) * 180 / Math.PI ); 3655 3656 self.isSwiping = ( angle > 45 && angle < 135 ) ? 'y' : 'x'; 3657 } 3658 3659 self.canTap = false; 3660 3661 if ( self.isSwiping === 'y' && $.fancybox.isMobile && ( isScrollable( self.$target ) || isScrollable( self.$target.parent() ) ) ) { 3662 self.isScrolling = true; 3663 3664 return; 3665 } 3666 3667 self.instance.isDragging = self.isSwiping; 3668 3669 // Reset points to avoid jumping, because we dropped first swipes to calculate the angle 3670 self.startPoints = self.newPoints; 3671 3672 $.each(self.instance.slides, function( index, slide ) { 3673 $.fancybox.stop( slide.$slide ); 3674 3675 slide.$slide.css( 'transition-duration', '' ); 3676 3677 slide.inTransition = false; 3678 3679 if ( slide.pos === self.instance.current.pos ) { 3680 self.sliderStartPos.left = $.fancybox.getTranslate( slide.$slide ).left; 3681 } 3682 }); 3683 3684 // Stop slideshow 3685 if ( self.instance.SlideShow && self.instance.SlideShow.isActive ) { 3686 self.instance.SlideShow.stop(); 3687 } 3688 } 3689 3690 return; 3691 } 3692 3693 // Sticky edges 3694 if ( swiping == 'x' ) { 3695 if ( self.distanceX > 0 && ( self.instance.group.length < 2 || ( self.instance.current.index === 0 && !self.instance.current.opts.loop ) ) ) { 3696 left = left + Math.pow( self.distanceX, 0.8 ); 3697 3698 } else if ( self.distanceX < 0 && ( self.instance.group.length < 2 || ( self.instance.current.index === self.instance.group.length - 1 && !self.instance.current.opts.loop ) ) ) { 3699 left = left - Math.pow( -self.distanceX, 0.8 ); 3700 3701 } else { 3702 left = left + self.distanceX; 3703 } 3704 } 3705 3706 self.sliderLastPos = { 3707 top : swiping == 'x' ? 0 : self.sliderStartPos.top + self.distanceY, 3708 left : left 3709 }; 3710 3711 if ( self.requestId ) { 3712 cancelAFrame( self.requestId ); 3713 3714 self.requestId = null; 3715 } 3716 3717 self.requestId = requestAFrame(function() { 3718 3719 if ( self.sliderLastPos ) { 3720 $.each(self.instance.slides, function( index, slide ) { 3721 var pos = slide.pos - self.instance.currPos; 3722 3723 $.fancybox.setTranslate( slide.$slide, { 3724 top : self.sliderLastPos.top, 3725 left : self.sliderLastPos.left + ( pos * self.canvasWidth ) + ( pos * slide.opts.gutter ) 3726 }); 3727 }); 3728 3729 self.$container.addClass( 'fancybox-is-sliding' ); 3730 } 3731 3732 }); 3733 3734 }; 3735 3736 Guestures.prototype.onPan = function() { 3737 var self = this; 3738 3739 // Sometimes, when tapping causally, image can move a bit and that breaks double tapping 3740 if ( distance( self.newPoints[0], self.realPoints[0] ) < ($.fancybox.isMobile ? 10 : 5) ) { 3741 self.startPoints = self.newPoints; 3742 return; 3743 } 3744 3745 self.canTap = false; 3746 3747 self.contentLastPos = self.limitMovement(); 3748 3749 if ( self.requestId ) { 3750 cancelAFrame( self.requestId ); 3751 3752 self.requestId = null; 3753 } 3754 3755 self.requestId = requestAFrame(function() { 3756 $.fancybox.setTranslate( self.$content, self.contentLastPos ); 3757 }); 3758 }; 3759 3760 // Make panning sticky to the edges 3761 Guestures.prototype.limitMovement = function() { 3762 var self = this; 3763 3764 var canvasWidth = self.canvasWidth; 3765 var canvasHeight = self.canvasHeight; 3766 3767 var distanceX = self.distanceX; 3768 var distanceY = self.distanceY; 3769 3770 var contentStartPos = self.contentStartPos; 3771 3772 var currentOffsetX = contentStartPos.left; 3773 var currentOffsetY = contentStartPos.top; 3774 3775 var currentWidth = contentStartPos.width; 3776 var currentHeight = contentStartPos.height; 3777 3778 var minTranslateX, minTranslateY, 3779 maxTranslateX, maxTranslateY, 3780 newOffsetX, newOffsetY; 3781 3782 if ( currentWidth > canvasWidth ) { 3783 newOffsetX = currentOffsetX + distanceX; 3784 3785 } else { 3786 newOffsetX = currentOffsetX; 3787 } 3788 3789 newOffsetY = currentOffsetY + distanceY; 3790 3791 // Slow down proportionally to traveled distance 3792 minTranslateX = Math.max( 0, canvasWidth * 0.5 - currentWidth * 0.5 ); 3793 minTranslateY = Math.max( 0, canvasHeight * 0.5 - currentHeight * 0.5 ); 3794 3795 maxTranslateX = Math.min( canvasWidth - currentWidth, canvasWidth * 0.5 - currentWidth * 0.5 ); 3796 maxTranslateY = Math.min( canvasHeight - currentHeight, canvasHeight * 0.5 - currentHeight * 0.5 ); 3797 3798 if ( currentWidth > canvasWidth ) { 3799 3800 // -> 3801 if ( distanceX > 0 && newOffsetX > minTranslateX ) { 3802 newOffsetX = minTranslateX - 1 + Math.pow( -minTranslateX + currentOffsetX + distanceX, 0.8 ) || 0; 3803 } 3804 3805 // <- 3806 if ( distanceX < 0 && newOffsetX < maxTranslateX ) { 3807 newOffsetX = maxTranslateX + 1 - Math.pow( maxTranslateX - currentOffsetX - distanceX, 0.8 ) || 0; 3808 } 3809 3810 } 3811 3812 if ( currentHeight > canvasHeight ) { 3813 3814 // \/ 3815 if ( distanceY > 0 && newOffsetY > minTranslateY ) { 3816 newOffsetY = minTranslateY - 1 + Math.pow(-minTranslateY + currentOffsetY + distanceY, 0.8 ) || 0; 3817 } 3818 3819 // /\ 3820 if ( distanceY < 0 && newOffsetY < maxTranslateY ) { 3821 newOffsetY = maxTranslateY + 1 - Math.pow ( maxTranslateY - currentOffsetY - distanceY, 0.8 ) || 0; 3822 } 3823 3824 } 3825 3826 return { 3827 top : newOffsetY, 3828 left : newOffsetX, 3829 scaleX : contentStartPos.scaleX, 3830 scaleY : contentStartPos.scaleY 3831 }; 3832 3833 }; 3834 3835 Guestures.prototype.limitPosition = function( newOffsetX, newOffsetY, newWidth, newHeight ) { 3836 var self = this; 3837 3838 var canvasWidth = self.canvasWidth; 3839 var canvasHeight = self.canvasHeight; 3840 3841 if ( newWidth > canvasWidth ) { 3842 newOffsetX = newOffsetX > 0 ? 0 : newOffsetX; 3843 newOffsetX = newOffsetX < canvasWidth - newWidth ? canvasWidth - newWidth : newOffsetX; 3844 3845 } else { 3846 3847 // Center horizontally 3848 newOffsetX = Math.max( 0, canvasWidth / 2 - newWidth / 2 ); 3849 3850 } 3851 3852 if ( newHeight > canvasHeight ) { 3853 newOffsetY = newOffsetY > 0 ? 0 : newOffsetY; 3854 newOffsetY = newOffsetY < canvasHeight - newHeight ? canvasHeight - newHeight : newOffsetY; 3855 3856 } else { 3857 3858 // Center vertically 3859 newOffsetY = Math.max( 0, canvasHeight / 2 - newHeight / 2 ); 3860 3861 } 3862 3863 return { 3864 top : newOffsetY, 3865 left : newOffsetX 3866 }; 3867 3868 }; 3869 3870 Guestures.prototype.onZoom = function() { 3871 var self = this; 3872 3873 // Calculate current distance between points to get pinch ratio and new width and height 3874 3875 var currentWidth = self.contentStartPos.width; 3876 var currentHeight = self.contentStartPos.height; 3877 3878 var currentOffsetX = self.contentStartPos.left; 3879 var currentOffsetY = self.contentStartPos.top; 3880 3881 var endDistanceBetweenFingers = distance( self.newPoints[0], self.newPoints[1] ); 3882 3883 var pinchRatio = endDistanceBetweenFingers / self.startDistanceBetweenFingers; 3884 3885 var newWidth = Math.floor( currentWidth * pinchRatio ); 3886 var newHeight = Math.floor( currentHeight * pinchRatio ); 3887 3888 // This is the translation due to pinch-zooming 3889 var translateFromZoomingX = (currentWidth - newWidth) * self.percentageOfImageAtPinchPointX; 3890 var translateFromZoomingY = (currentHeight - newHeight) * self.percentageOfImageAtPinchPointY; 3891 3892 //Point between the two touches 3893 3894 var centerPointEndX = ((self.newPoints[0].x + self.newPoints[1].x) / 2) - $(window).scrollLeft(); 3895 var centerPointEndY = ((self.newPoints[0].y + self.newPoints[1].y) / 2) - $(window).scrollTop(); 3896 3897 // And this is the translation due to translation of the centerpoint 3898 // between the two fingers 3899 3900 var translateFromTranslatingX = centerPointEndX - self.centerPointStartX; 3901 var translateFromTranslatingY = centerPointEndY - self.centerPointStartY; 3902 3903 // The new offset is the old/current one plus the total translation 3904 3905 var newOffsetX = currentOffsetX + ( translateFromZoomingX + translateFromTranslatingX ); 3906 var newOffsetY = currentOffsetY + ( translateFromZoomingY + translateFromTranslatingY ); 3907 3908 var newPos = { 3909 top : newOffsetY, 3910 left : newOffsetX, 3911 scaleX : self.contentStartPos.scaleX * pinchRatio, 3912 scaleY : self.contentStartPos.scaleY * pinchRatio 3913 }; 3914 3915 self.canTap = false;
3916 3917 self.newWidth = newWidth; 3918 self.newHeight = newHeight; 3919 3920 self.contentLastPos = newPos; 3921 3922 if ( self.requestId ) { 3923 cancelAFrame( self.requestId ); 3924 3925 self.requestId = null; 3926 } 3927 3928 self.requestId = requestAFrame(function() { 3929 $.fancybox.setTranslate( self.$content, self.contentLastPos ); 3930 }); 3931 3932 }; 3933 3934 Guestures.prototype.ontouchend = function( e ) { 3935 var self = this; 3936 var dMs = Math.max( (new Date().getTime() ) - self.startTime, 1); 3937 3938 var swiping = self.isSwiping; 3939 var panning = self.isPanning; 3940 var zooming = self.isZooming; 3941 var scrolling = self.isScrolling; 3942 3943 self.endPoints = pointers( e ); 3944 3945 self.$container.removeClass( 'fancybox-controls--isGrabbing' ); 3946 3947 $(document).off( '.fb.touch' ); 3948 3949 document.removeEventListener('scroll', self.onscroll, true); 3950 3951 if ( self.requestId ) { 3952 cancelAFrame( self.requestId ); 3953 3954 self.requestId = null; 3955 } 3956 3957 self.isSwiping = false; 3958 self.isPanning = false; 3959 self.isZooming = false; 3960 self.isScrolling = false; 3961 3962 self.instance.isDragging = false; 3963 3964 if ( self.canTap ) { 3965 return self.onTap( e ); 3966 } 3967 3968 self.speed = 366; 3969 3970 // Speed in px/ms 3971 self.velocityX = self.distanceX / dMs * 0.5; 3972 self.velocityY = self.distanceY / dMs * 0.5; 3973 3974 self.speedX = Math.max( self.speed * 0.5, Math.min( self.speed * 1.5, ( 1 / Math.abs( self.velocityX ) ) * self.speed ) ); 3975 3976 if ( panning ) { 3977 self.endPanning(); 3978 3979 } else if ( zooming ) { 3980 self.endZooming(); 3981 3982 } else { 3983 self.endSwiping( swiping, scrolling ); 3984 } 3985 3986 return; 3987 }; 3988 3989 Guestures.prototype.endSwiping = function( swiping, scrolling ) { 3990 var self = this, 3991 ret = false, 3992 len = self.instance.group.length; 3993 3994 self.sliderLastPos = null; 3995 3996 // Close if swiped vertically / navigate if horizontally 3997 if ( swiping == 'y' && !scrolling && Math.abs( self.distanceY ) > 50 ) { 3998 3999 // Continue vertical movement 4000 $.fancybox.animate( self.instance.current.$slide, { 4001 top : self.sliderStartPos.top + self.distanceY + ( self.velocityY * 150 ), 4002 opacity : 0 4003 }, 150 ); 4004 4005 ret = self.instance.close( true, 300 ); 4006 4007 } else if ( swiping == 'x' && self.distanceX > 50 && len > 1 ) { 4008 ret = self.instance.previous( self.speedX ); 4009 4010 } else if ( swiping == 'x' && self.distanceX < -50 && len > 1 ) { 4011 ret = self.instance.next( self.speedX ); 4012 } 4013 4014 if ( ret === false && ( swiping == 'x' || swiping == 'y' ) ) { 4015 if ( scrolling || len < 2 ) { 4016 self.instance.centerSlide( self.instance.current, 150 ); 4017 } else { 4018 self.instance.jumpTo( self.instance.current.index ); 4019 } 4020 } 4021 4022 self.$container.removeClass( 'fancybox-is-sliding' ); 4023 4024 }; 4025 4026 // Limit panning from edges 4027 // ======================== 4028 4029 Guestures.prototype.endPanning = function() { 4030 4031 var self = this; 4032 var newOffsetX, newOffsetY, newPos; 4033 4034 if ( !self.contentLastPos ) { 4035 return; 4036 } 4037 4038 if ( self.opts.momentum === false ) { 4039 newOffsetX = self.contentLastPos.left; 4040 newOffsetY = self.contentLastPos.top; 4041 4042 } else { 4043 4044 // Continue movement 4045 newOffsetX = self.contentLastPos.left + ( self.velocityX * self.speed ); 4046 newOffsetY = self.contentLastPos.top + ( self.velocityY * self.speed ); 4047 } 4048 4049 newPos = self.limitPosition( newOffsetX, newOffsetY, self.contentStartPos.width, self.contentStartPos.height ); 4050 4051 newPos.width = self.contentStartPos.width; 4052 newPos.height = self.contentStartPos.height; 4053 4054 $.fancybox.animate( self.$content, newPos, 330 ); 4055 }; 4056 4057 4058 Guestures.prototype.endZooming = function() { 4059 var self = this; 4060 4061 var current = self.instance.current; 4062 4063 var newOffsetX, newOffsetY, newPos, reset; 4064 4065 var newWidth = self.newWidth; 4066 var newHeight = self.newHeight; 4067 4068 if ( !self.contentLastPos ) { 4069 return; 4070 } 4071 4072 newOffsetX = self.contentLastPos.left; 4073 newOffsetY = self.contentLastPos.top; 4074 4075 reset = { 4076 top : newOffsetY, 4077 left : newOffsetX, 4078 width : newWidth, 4079 height : newHeight, 4080 scaleX : 1, 4081 scaleY : 1 4082 }; 4083 4084 // Reset scalex/scaleY values; this helps for perfomance and does not break animation 4085 $.fancybox.setTranslate( self.$content, reset ); 4086 4087 if ( newWidth < self.canvasWidth && newHeight < self.canvasHeight ) { 4088 self.instance.scaleToFit( 150 ); 4089 4090 } else if ( newWidth > current.width || newHeight > current.height ) { 4091 self.instance.scaleToActual( self.centerPointStartX, self.centerPointStartY, 150 ); 4092 4093 } else { 4094 4095 newPos = self.limitPosition( newOffsetX, newOffsetY, newWidth, newHeight ); 4096 4097 // Switch from scale() to width/height or animation will not work correctly 4098 $.fancybox.setTranslate( self.content, $.fancybox.getTranslate( self.$content ) ); 4099 4100 $.fancybox.animate( self.$content, newPos, 150 ); 4101 } 4102 4103 }; 4104 4105 Guestures.prototype.onTap = function(e) { 4106 var self = this; 4107 var $target = $( e.target ); 4108 4109 var instance = self.instance; 4110 var current = instance.current; 4111 4112 var endPoints = ( e && pointers( e ) ) || self.startPoints; 4113
vendor: 2,371 bytes, lines 4114-4227
4114 var tapX = endPoints[0] ? endPoints[0].x - self.$stage.offset().left : 0; 4115 var tapY = endPoints[0] ? endPoints[0].y - self.$stage.offset().top : 0; 4116 4117 var where; 4118 4119 var process = function ( prefix ) { 4120 4121 var action = current.opts[ prefix ]; 4122 4123 if ( $.isFunction( action ) ) { 4124 action = action.apply( instance, [ current, e ] ); 4125 } 4126 4127 if ( !action) { 4128 return; 4129 } 4130 4131 switch ( action ) { 4132 4133 case "close" : 4134 4135 instance.close( self.startEvent ); 4136 4137 break; 4138 4139 case "toggleControls" : 4140 4141 instance.toggleControls( true ); 4142 4143 break; 4144 4145 case "next" : 4146 4147 instance.next(); 4148 4149 break; 4150 4151 case "nextOrClose" : 4152 4153 if ( instance.group.length > 1 ) { 4154 instance.next(); 4155 4156 } else { 4157 instance.close( self.startEvent ); 4158 } 4159 4160 break; 4161 4162 case "zoom" : 4163 4164 if ( current.type == 'image' && ( current.isLoaded || current.$ghost ) ) { 4165 4166 if ( instance.canPan() ) { 4167 instance.scaleToFit(); 4168 4169 } else if ( instance.isScaledDown() ) { 4170 instance.scaleToActual( tapX, tapY ); 4171 4172 } else if ( instance.group.length < 2 ) { 4173 instance.close( self.startEvent ); 4174 } 4175 } 4176 4177 break; 4178 } 4179 4180 }; 4181 4182 // Ignore right click 4183 if ( e.originalEvent && e.originalEvent.button == 2 ) { 4184 return; 4185 } 4186 4187 // Skip if clicked on the scrollbar 4188 if ( !$target.is('img') && tapX > $target[0].clientWidth + $target.offset().left ) { 4189 return; 4190 } 4191 4192 // Check where is clicked 4193 if ( $target.is( '.fancybox-bg,.fancybox-inner,.fancybox-outer,.fancybox-container' ) ) { 4194 where = 'Outside'; 4195 4196 } else if ( $target.is( '.fancybox-slide' ) ) { 4197 where = 'Slide'; 4198 4199 } else if ( instance.current.$content && instance.current.$content.find( $target ).addBack().filter( $target ).length ) { 4200 where = 'Content'; 4201 4202 } else { 4203 return; 4204 } 4205 4206 // Check if this is a double tap 4207 if ( self.tapped ) { 4208 4209 // Stop previously created single tap 4210 clearTimeout( self.tapped ); 4211 self.tapped = null; 4212 4213 // Skip if distance between taps is too big 4214 if ( Math.abs( tapX - self.tapX ) > 50 || Math.abs( tapY - self.tapY ) > 50 ) { 4215 return this; 4216 } 4217 4218 // OK, now we assume that this is a double-tap 4219 process( 'dblclick' + where ); 4220 4221 } else { 4222 4223 // Single tap will be processed if user has not clicked second time within 300ms 4224 // or there is no need to wait for double-tap 4225 self.tapX = tapX; 4226 self.tapY = tapY; 4227
4228 if ( current.opts[ 'dblclick' + where ] && current.opts[ 'dblclick' + where ] !== current.opts[ 'click' + where ] ) { 4229 4230 self.tapped = setTimeout(function() { 4231 self.tapped = null; 4232 4233 process( 'click' + where ); 4234 4235 }, 500); 4236 4237 } else { 4238 process( 'click' + where ); 4239 } 4240 4241 } 4242 4243 return this; 4244 }; 4245 4246 $(document).on('onActivate.fb', function (e, instance) { 4247 if ( instance && !instance.Guestures ) { 4248 instance.Guestures = new Guestures( instance ); 4249 } 4250 }); 4251 4252}( window, document, window.jQuery || jQuery )); 4253 4254// ========================================================================== 4255// 4256// SlideShow 4257// Enables slideshow functionality 4258// 4259// Example of usage: 4260// $.fancybox.getInstance().SlideShow.start() 4261// 4262// ========================================================================== 4263;(function (document, $) { 4264 'use strict'; 4265 4266 $.extend(true, $.fancybox.defaults, { 4267 btnTpl : { 4268 slideShow : 4269 '<button data-fancybox-play class="fancybox-button fancybox-button--play" title="{{PLAY_START}}">' + 4270 '<svg viewBox="0 0 40 40">' + 4271 '<path d="M13,12 L27,20 L13,27 Z" />' + 4272 '<path d="M15,10 v19 M23,10 v19" />' + 4273 '</svg>' + 4274 '</button>' 4275 }, 4276 slideShow : { 4277 autoStart : false, 4278 speed : 3000 4279 } 4280 }); 4281 4282 var SlideShow = function( instance ) { 4283 this.instance = instance; 4284 this.init(); 4285 }; 4286 4287 $.extend( SlideShow.prototype, { 4288 timer : null, 4289 isActive : false, 4290 $button : null, 4291 4292 init : function() { 4293 var self = this; 4294 4295 self.$button = self.instance.$refs.toolbar.find('[data-fancybox-play]').on('click', function() { 4296 self.toggle(); 4297 }); 4298 4299 if ( self.instance.group.length < 2 || !self.instance.group[ self.instance.currIndex ].opts.slideShow ) { 4300 self.$button.hide(); 4301 } 4302 }, 4303 4304 set : function( force ) { 4305 var self = this; 4306 4307 // Check if reached last element 4308 if ( self.instance && self.instance.current && (force === true || self.instance.current.opts.loop || self.instance.currIndex < self.instance.group.length - 1 )) { 4309 self.timer = setTimeout(function() { 4310 if ( self.isActive ) { 4311 self.instance.jumpTo( (self.instance.currIndex + 1) % self.instance.group.length ); 4312 } 4313 4314 }, self.instance.current.opts.slideShow.speed); 4315 4316 } else { 4317 self.stop(); 4318 self.instance.idleSecondsCounter = 0; 4319 self.instance.showControls(); 4320 } 4321 }, 4322 4323 clear : function() { 4324 var self = this; 4325 4326 clearTimeout( self.timer ); 4327 4328 self.timer = null; 4329 }, 4330 4331 start : function() { 4332 var self = this; 4333 var current = self.instance.current; 4334 4335 if ( current ) { 4336 self.isActive = true; 4337 4338 self.$button 4339 .attr( 'title', current.opts.i18n[ current.opts.lang ].PLAY_STOP ) 4340 .removeClass( 'fancybox-button--play' ) 4341 .addClass( 'fancybox-button--pause' ); 4342 4343 self.set( true ); 4344 } 4345 }, 4346 4347 stop : function() { 4348 var self = this; 4349 var current = self.instance.current; 4350 4351 self.clear(); 4352 4353 self.$button 4354 .attr( 'title', current.opts.i18n[ current.opts.lang ].PLAY_START ) 4355 .removeClass( 'fancybox-button--pause' ) 4356 .addClass( 'fancybox-button--play' ); 4357 4358 self.isActive = false; 4359 }, 4360 4361 toggle : function() { 4362 var self = this; 4363 4364 if ( self.isActive ) { 4365 self.stop(); 4366 4367 } else { 4368 self.start(); 4369 } 4370 } 4371 4372 }); 4373 4374 $(document).on({ 4375 'onInit.fb' : function(e, instance) { 4376 if ( instance && !instance.SlideShow ) { 4377 instance.SlideShow = new SlideShow( instance ); 4378 } 4379 }, 4380 4381 'beforeShow.fb' : function(e, instance, current, firstRun) { 4382 var SlideShow = instance && instance.SlideShow; 4383 4384 if ( firstRun ) { 4385 4386 if ( SlideShow && current.opts.slideShow.autoStart ) { 4387 SlideShow.start(); 4388 } 4389 4390 } else if ( SlideShow && SlideShow.isActive ) { 4391 SlideShow.clear(); 4392 } 4393 }, 4394 4395 'afterShow.fb' : function(e, instance, current) { 4396 var SlideShow = instance && instance.SlideShow; 4397 4398 if ( SlideShow && SlideShow.isActive ) { 4399 SlideShow.set(); 4400 } 4401 }, 4402 4403 'afterKeydown.fb' : function(e, instance, current, keypress, keycode) { 4404 var SlideShow = instance && instance.SlideShow; 4405 4406 // "P" or Spacebar 4407 if ( SlideShow && current.opts.slideShow && ( keycode === 80 || keycode === 32 ) && !$(document.activeElement).is( 'button,a,input' ) ) { 4408 keypress.preventDefault(); 4409 4410 SlideShow.toggle(); 4411 } 4412 }, 4413 4414 'beforeClose.fb onDeactivate.fb' : function(e, instance) { 4415 var SlideShow = instance && instance.SlideShow; 4416 4417 if ( SlideShow ) { 4418 SlideShow.stop(); 4419 } 4420 } 4421 }); 4422 4423 // Page Visibility API to pause slideshow when window is not active 4424 $(document).on("visibilitychange", function() { 4425 var instance = $.fancybox.getInstance(); 4426 var SlideShow = instance && instance.SlideShow; 4427 4428 if ( SlideShow && SlideShow.isActive ) { 4429 if ( document.hidden ) { 4430 SlideShow.clear(); 4431 4432 } else { 4433 SlideShow.set(); 4434 } 4435 } 4436 }); 4437 4438}( document, window.jQuery || jQuery )); 4439 4440// ========================================================================== 4441// 4442// FullScreen 4443// Adds fullscreen functionality 4444// 4445// ========================================================================== 4446;(function (document, $) { 4447 'use strict'; 4448 4449 // Collection of methods supported by user browser 4450 var fn = (function () { 4451 4452 var fnMap = [ 4453 [ 4454 'requestFullscreen', 4455 'exitFullscreen', 4456 'fullscreenElement', 4457 'fullscreenEnabled', 4458 'fullscreenchange', 4459 'fullscreenerror' 4460 ], 4461 // new WebKit 4462 [ 4463 'webkitRequestFullscreen', 4464 'webkitExitFullscreen', 4465 'webkitFullscreenElement', 4466 'webkitFullscreenEnabled', 4467 'webkitfullscreenchange', 4468 'webkitfullscreenerror' 4469 4470 ], 4471 // old WebKit (Safari 5.1) 4472 [ 4473 'webkitRequestFullScreen', 4474 'webkitCancelFullScreen', 4475 'webkitCurrentFullScreenElement', 4476 'webkitCancelFullScreen', 4477 'webkitfullscreenchange', 4478 'webkitfullscreenerror' 4479 4480 ], 4481 [ 4482 'mozRequestFullScreen', 4483 'mozCancelFullScreen', 4484 'mozFullScreenElement', 4485 'mozFullScreenEnabled', 4486 'mozfullscreenchange', 4487 'mozfullscreenerror' 4488 ], 4489 [ 4490 'msRequestFullscreen', 4491 'msExitFullscreen', 4492 'msFullscreenElement', 4493 'msFullscreenEnabled', 4494 'MSFullscreenChange', 4495 'MSFullscreenError' 4496 ] 4497 ]; 4498 4499 var val; 4500 var ret = {}; 4501 var i, j; 4502 4503 for ( i = 0; i < fnMap.length; i++ ) { 4504 val = fnMap[ i ]; 4505 4506 if ( val && val[ 1 ] in document ) { 4507 for ( j = 0; j < val.length; j++ ) { 4508 ret[ fnMap[ 0 ][ j ] ] = val[ j ]; 4509 } 4510 4511 return ret; 4512 } 4513 } 4514 4515 return false;
4516 })(); 4517 4518 // If browser does not have Full Screen API, then simply unset default button template and stop 4519 if ( !fn ) { 4520 4521 if ( $ && $.fancybox ) { 4522 $.fancybox.defaults.btnTpl.fullScreen = false; 4523 } 4524 4525 return; 4526 } 4527 4528 var FullScreen = { 4529 4530 request : function ( elem ) { 4531 4532 elem = elem || document.documentElement; 4533 4534 elem[ fn.requestFullscreen ]( elem.ALLOW_KEYBOARD_INPUT ); 4535 4536 }, 4537 exit : function () { 4538 4539 document[ fn.exitFullscreen ](); 4540 4541 }, 4542 toggle : function ( elem ) { 4543 4544 elem = elem || document.documentElement; 4545 4546 if ( this.isFullscreen() ) { 4547 this.exit(); 4548 4549 } else { 4550 this.request( elem ); 4551 } 4552 4553 }, 4554 isFullscreen : function() { 4555 4556 return Boolean( document[ fn.fullscreenElement ] ); 4557 4558 }, 4559 enabled : function() { 4560 4561 return Boolean( document[ fn.fullscreenEnabled ] ); 4562 4563 } 4564 }; 4565 4566 $.extend(true, $.fancybox.defaults, { 4567 btnTpl : { 4568 fullScreen : 4569 '<button data-fancybox-fullscreen class="fancybox-button fancybox-button--fullscreen" title="{{FULL_SCREEN}}">' + 4570 '<svg viewBox="0 0 40 40">' + 4571 '<path d="M9,12 h22 v16 h-22 v-16 v16 h22 v-16 Z" />' + 4572 '</svg>' + 4573 '</button>' 4574 }, 4575 fullScreen : { 4576 autoStart : false 4577 } 4578 }); 4579 4580 $(document).on({ 4581 'onInit.fb' : function(e, instance) { 4582 var $container; 4583 4584 if ( instance && instance.group[ instance.currIndex ].opts.fullScreen ) { 4585 $container = instance.$refs.container; 4586 4587 $container.on('click.fb-fullscreen', '[data-fancybox-fullscreen]', function(e) { 4588 4589 e.stopPropagation(); 4590 e.preventDefault(); 4591 4592 FullScreen.toggle( $container[ 0 ] ); 4593 4594 }); 4595 4596 if ( instance.opts.fullScreen && instance.opts.fullScreen.autoStart === true ) { 4597 FullScreen.request( $container[ 0 ] ); 4598 } 4599 4600 // Expose API 4601 instance.FullScreen = FullScreen; 4602 4603 } else if ( instance ) { 4604 instance.$refs.toolbar.find('[data-fancybox-fullscreen]').hide(); 4605 } 4606 4607 }, 4608 4609 'afterKeydown.fb' : function(e, instance, current, keypress, keycode) { 4610 4611 // "P" or Spacebar 4612 if ( instance && instance.FullScreen && keycode === 70 ) { 4613 keypress.preventDefault(); 4614 4615 instance.FullScreen.toggle( instance.$refs.container[ 0 ] ); 4616 } 4617 4618 }, 4619 4620 'beforeClose.fb' : function( instance ) { 4621 if ( instance && instance.FullScreen ) { 4622 FullScreen.exit(); 4623 } 4624 } 4625 }); 4626 4627 $(document).on(fn.fullscreenchange, function() { 4628 var isFullscreen = FullScreen.isFullscreen(), 4629 instance = $.fancybox.getInstance(); 4630 4631 if ( instance ) { 4632 4633 // If image is zooming, then force to stop and reposition properly 4634 if ( instance.current && instance.current.type === 'image' && instance.isAnimating ) { 4635 instance.current.$content.css( 'transition', 'none' ); 4636 4637 instance.isAnimating = false; 4638 4639 instance.update( true, true, 0 ); 4640 } 4641 4642 instance.trigger( 'onFullscreenChange', isFullscreen ); 4643 4644 instance.$refs.container.toggleClass( 'fancybox-is-fullscreen', isFullscreen ); 4645 } 4646 4647 }); 4648 4649}( document, window.jQuery || jQuery )); 4650 4651// ========================================================================== 4652// 4653// Thumbs 4654// Displays thumbnails in a grid 4655// 4656// ========================================================================== 4657;(function (document, $) { 4658 'use strict'; 4659 4660 // Make sure there are default values 4661 $.fancybox.defaults = $.extend(true, { 4662 btnTpl : { 4663 thumbs : 4664 '<button data-fancybox-thumbs class="fancybox-button fancybox-button--thumbs" title="{{THUMBS}}">' + 4665 '<svg viewBox="0 0 120 120">' + 4666 '<path d="M30,30 h14 v14 h-14 Z M50,30 h14 v14 h-14 Z M70,30 h14 v14 h-14 Z M30,50 h14 v14 h-14 Z M50,50 h14 v14 h-14 Z M70,50 h14 v14 h-14 Z M30,70 h14 v14 h-14 Z M50,70 h14 v14 h-14 Z M70,70 h14 v14 h-14 Z" />' + 4667 '</svg>' + 4668 '</button>' 4669 }, 4670 thumbs : { 4671 autoStart : false, // Display thumbnails on opening 4672 hideOnClose : true, // Hide thumbnail grid when closing animation starts 4673 parentEl : '.fancybox-container', // Container is injected into this element 4674 axis : 'y' // Vertical (y) or horizontal (x) scrolling 4675 } 4676 }, $.fancybox.defaults); 4677 4678 var FancyThumbs = function( instance ) { 4679 this.init( instance ); 4680 }; 4681 4682 $.extend( FancyThumbs.prototype, { 4683 4684 $button : null, 4685 $grid : null, 4686 $list : null, 4687 isVisible : false, 4688 isActive : false, 4689 4690 init : function( instance ) { 4691 var self = this; 4692 4693 self.instance = instance; 4694 4695 instance.Thumbs = self; 4696 4697 // Enable thumbs if at least two group items have thumbnails 4698 var first = instance.group[0], 4699 second = instance.group[1]; 4700 4701 self.opts = instance.group[ instance.currIndex ].opts.thumbs; 4702 4703 self.$button = instance.$refs.toolbar.find( '[data-fancybox-thumbs]' ); 4704 4705 if ( self.opts && first && second && ( 4706 ( first.type == 'image' || first.opts.thumb || first.opts.$thumb ) && 4707 ( second.type == 'image' || second.opts.thumb || second.opts.$thumb ) 4708 )) { 4709 4710 self.$button.show().on('click', function() {
4711 self.toggle(); 4712 }); 4713 4714 self.isActive = true; 4715 4716 } else { 4717 self.$button.hide(); 4718 } 4719 }, 4720 4721 create : function() { 4722 var self = this, 4723 instance = self.instance, 4724 parentEl = self.opts.parentEl, 4725 list, 4726 src; 4727 4728 self.$grid = $('<div class="fancybox-thumbs fancybox-thumbs-' + self.opts.axis + '"></div>').appendTo( instance.$refs.container.find( parentEl ).addBack().filter( parentEl ) ); 4729 4730 // Build list HTML 4731 list = '<ul>'; 4732 4733 $.each(instance.group, function( i, item ) { 4734 src = item.opts.thumb || ( item.opts.$thumb ? item.opts.$thumb.attr( 'src' ) : null ); 4735 4736 if ( !src && item.type === 'image' ) { 4737 src = item.src; 4738 } 4739 4740 if ( src && src.length ) { 4741 list += '<li data-index="' + i + '" tabindex="0" class="fancybox-thumbs-loading"><img data-src="' + src + '" /></li>'; 4742 } 4743 }); 4744 4745 list += '</ul>'; 4746 4747 self.$list = $( list ).appendTo( self.$grid ).on('click', 'li', function() { 4748 instance.jumpTo( $(this).data('index') ); 4749 }); 4750 4751 self.$list.find( 'img' ).hide().one('load', function() { 4752 var $parent = $(this).parent().removeClass( 'fancybox-thumbs-loading' ), 4753 thumbWidth = $parent.outerWidth(), 4754 thumbHeight = $parent.outerHeight(), 4755 width, 4756 height, 4757 widthRatio, 4758 heightRatio; 4759 4760 width = this.naturalWidth || this.width; 4761 height = this.naturalHeight || this.height; 4762 4763 // Calculate thumbnail dimensions; center vertically and horizontally 4764 widthRatio = width / thumbWidth; 4765 heightRatio = height / thumbHeight; 4766 4767 if (widthRatio >= 1 && heightRatio >= 1) { 4768 if (widthRatio > heightRatio) { 4769 width = width / heightRatio; 4770 height = thumbHeight; 4771 4772 } else { 4773 width = thumbWidth; 4774 height = height / widthRatio; 4775 } 4776 } 4777 4778 $(this).css({ 4779 width : Math.floor(width), 4780 height : Math.floor(height), 4781 'margin-top' : height >
4781 thumbHeight ? ( Math.floor(thumbHeight * 0.3 - height * 0.3 ) ) : Math.floor(thumbHeight * 0.5 - height * 0.5 ), 4782 'margin-left' : Math.floor(thumbWidth * 0.5 - width * 0.5 ) 4783 }).show(); 4784 4785 }) 4786 .each(function() { 4787 this.src = $( this ).data( 'src' ); 4788 }); 4789 4790 if ( self.opts.axis === 'x' ) { 4791 self.$list.width( parseInt( self.$grid.css("padding-right") ) + ( instance.group.length * self.$list.children().eq(0).outerWidth(true) ) + 'px' ); 4792 } 4793 }, 4794 4795 focus : function( duration ) { 4796 var self = this, 4797 $list = self.$list, 4798 thumb, 4799 thumbPos; 4800 4801 if ( self.instance.current ) { 4802 thumb = $list.children() 4803 .removeClass( 'fancybox-thumbs-active' ) 4804 .filter('[data-index="' + self.instance.current.index + '"]') 4805 .addClass('fancybox-thumbs-active'); 4806 4807 thumbPos = thumb.position(); 4808 4809 // Check if need to scroll to make current thumb visible 4810 if ( self.opts.axis === 'y' && ( thumbPos.top < 0 || thumbPos.top > ( $list.height() - thumb.outerHeight() ) ) ) { 4811 $list.stop().animate({ 'scrollTop' : $list.scrollTop() + thumbPos.top }, duration); 4812 4813 } else if ( self.opts.axis === 'x' && ( 4814 thumbPos.left < $list.parent().scrollLeft() || 4815 thumbPos.left > ( $list.parent().scrollLeft() + ( $list.parent().width() - thumb.outerWidth() ) ) 4816 ) 4817 ) { 4818 $list.parent().stop().animate({ 'scrollLeft' : thumbPos.left }, duration); 4819 } 4820 } 4821 }, 4822 4823 update : function() { 4824 this.instance.$refs.container.toggleClass( 'fancybox-show-thumbs', this.isVisible ); 4825 4826 if ( this.isVisible ) { 4827 if ( !this.$grid ) { 4828 this.create(); 4829 } 4830 4831 this.instance.trigger( 'onThumbsShow' ); 4832 4833 this.focus( 0 ); 4834 4835 } else if ( this.$grid ) { 4836 this.instance.trigger( 'onThumbsHide' ); 4837 } 4838 4839 // Update content position 4840 this.instance.update(); 4841 }, 4842 4843 hide : function() { 4844 this.isVisible = false; 4845 this.update(); 4846 }, 4847 4848 show : function() { 4849 this.isVisible = true; 4850 this.update(); 4851 }, 4852 4853 toggle : function() { 4854 this.isVisible = !this.isVisible; 4855 this.update(); 4856 } 4857 }); 4858 4859 $(document).on({ 4860 4861 'onInit.fb' : function(e, instance) { 4862 var Thumbs; 4863 4864 if ( instance && !instance.Thumbs ) { 4865 Thumbs = new FancyThumbs( instance ); 4866 4867 if ( Thumbs.isActive && Thumbs.opts.autoStart === true ) { 4868 Thumbs.show(); 4869 } 4870 } 4871 }, 4872 4873 'beforeShow.fb' : function(e, instance, item, firstRun) { 4874 var Thumbs = instance && instance.Thumbs; 4875 4876 if ( Thumbs && Thumbs.isVisible ) { 4877 Thumbs.focus( firstRun ? 0 : 250 ); 4878 } 4879 }, 4880 4881 'afterKeydown.fb' : function(e, instance, current, keypress, keycode) { 4882 var Thumbs = instance && instance.Thumbs; 4883 4884 // "G" 4885 if ( Thumbs && Thumbs.isActive && keycode === 71 ) { 4886 keypress.preventDefault(); 4887 4888 Thumbs.toggle(); 4889 } 4890 }, 4891 4892 'beforeClose.fb' : function( e, instance ) { 4893 var Thumbs = instance && instance.Thumbs; 4894 4895 if ( Thumbs && Thumbs.isVisible && Thumbs.opts.hideOnClose !== false ) { 4896 Thumbs.$grid.hide(); 4897 } 4898 } 4899 4900 }); 4901 4902}(document, window.jQuery)); 4903 4904//// ========================================================================== 4905// 4906// Share 4907// Displays simple form for sharing current url 4908// 4909// ========================================================================== 4910;(function (document, $) { 4911 'use strict'; 4912 4913 $.extend(true, $.fancybox.defaults, { 4914 btnTpl : { 4915 share : 4916 '<button data-fancybox-share class="fancybox-button fancybox-button--share" title="{{SHARE}}">' + 4917 '<svg viewBox="0 0 40 40">' + 4918 '<path d="M6,30 C8,18 19,16 23,16 L23,16 L23,10 L33,20 L23,29 L23,24 C19,24 8,27 6,30 Z">' + 4919 '</svg>' + 4920 '</button>' 4921 }, 4922 share : { 4923 tpl : 4924 '<div class="fancybox-share">' + 4925 '<h1>{{SHARE}}</h1>' + 4926 '<p class="fancybox-share__links">' + 4927 '<a class="fancybox-share__button fancybox-share__button--fb" href="https://www.facebook.com/sharer/sharer.php?u={{url}}">' + 4928 '<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="m287 456v-299c0-21 6-35 35-35h38v-63c-7-1-29-3-55-3-54 0-91 33-91 94v306m143-254h-205v72h196" /></svg>
4928' + 4929 '<span>Facebook</span>' + 4930 '</a>' + 4931 '<a class="fancybox-share__button fancybox-share__button--pt" href="https://www.pinterest.com/pin/create/button/?url={{url}}&description={{descr}}&media={{media}}">' + 4932 '<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="m265 56c-109 0-164 78-164 144 0 39 15 74 47 87 5 2 10 0 12-5l4-19c2-6 1-8-3-13-9-11-15-25-15-45 0-58 43-110 113-110 62 0 96 38 96 88 0 67-30 122-73 122-24 0-42-19-36-44 6-29 20-60 20-81 0-19-10-35-31-35-25 0-44 26-44 60 0 21 7 36 7 36l-30 125c-8 37-1 83 0 87 0 3 4 4 5 2 2-3 32-39 42-75l16-64c8 16 31 29 56 29 74 0 124-67 124-157 0-69-58-132-146-132z" fill="#fff"/></svg>' + 4933 '<span>Pinterest</span>' + 4934 '</a>' + 4935 '<a class="fancybox-share__button fancybox-share__button--tw" href="https://twitter.com/intent/tweet?url={{url}}&text={{descr}}">' + 4936 '<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="m456 133c-14 7-31 11-47 13 17-10 30-27 37-46-15 10-34 16-52 20-61-62-157-7-141 75-68-3-129-35-169-85-22 37-11 86 26 109-13 0-26-4-37-9 0 39 28 72 65 80-12 3-25 4-37 2 10 33 41 57 77 57-42 30-77 38-122 34 170 111 378-32 359-208 16-11 30-25 41-42z" /></svg>' + 4937 '<span>Twitter</span>' + 4938 '</a>' + 4939 '</p>' + 4940 '<p><input class="fancybox-share__input" type="text" value="{{url_raw}}" /></p>' + 4941 '</div>' 4942 } 4943 }); 4944 4945 function escapeHtml(string) { 4946 var entityMap = { 4947 '&': '&', 4948 '<': '<', 4949 '>': '>', 4950 '"': '"', 4951 "'": ''', 4952 '/': '/', 4953 '`': '`', 4954 '=': '=' 4955 }; 4956 4957 return String(string).replace(/[&<>"'`=\/]/g, function (s) { 4958 return entityMap[s]; 4959 }); 4960 } 4961 4962 $(document).on('click', '[data-fancybox-share]', function() { 4963 var f = $.fancybox.getInstance(), 4964 url, 4965 tpl; 4966 4967 if ( f ) { 4968 url = f.current.opts.hash === false ? f.current.src : window.location; 4969 tpl = f.current.opts.share.tpl 4970 .replace( /\{\{media\}\}/g, f.current.type === 'image' ? encodeURIComponent( f.current.src ) : '' ) 4971 .replace( /\{\{url\}\}/g, encodeURIComponent( url ) ) 4972 .replace( /\{\{url_raw\}\}/g, escapeHtml( url ) ) 4973 .replace( /\{\{descr\}\}/g, f.$caption ? encodeURIComponent( f.$caption.text() ) : '' ); 4974 4975 $.fancybox.open({ 4976 src : f.translate( f, tpl ), 4977 type : 'html', 4978 opts : { 4979 animationEffect : "fade", 4980 animationDuration : 250, 4981 afterLoad : function(instance, current) { 4982 // Opening links in a popup window 4983 current.$content.find('.fancybox-share__links a').click(function() { 4984 window.open(this.href, "Share", "width=550, height=450"); 4985 return false; 4986 }); 4987 } 4988 } 4989 }); 4990 } 4991 4992 }); 4993 4994}( document, window.jQuery || jQuery )); 4995 4996// ========================================================================== 4997// 4998// Hash 4999// Enables linking to each modal 5000// 5001// ========================================================================== 5002;(function (document, window, $) { 5003 'use strict'; 5004 5005 // Simple $.escapeSelector polyfill (for jQuery prior v3) 5006 if ( !$.escapeSelector ) { 5007 $.escapeSelector = function( sel ) { 5008 var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g; 5009 var fcssescape = function( ch, asCodePoint ) { 5010 if ( asCodePoint ) { 5011 // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER 5012 if ( ch === "\0" ) { 5013 return "\uFFFD"; 5014 } 5015 5016 // Control characters and (dependent upon position) numbers get escaped as code points 5017 return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; 5018 } 5019 5020 // Other potentially-special ASCII characters get backslash-escaped 5021 return "\\" + ch; 5022 }; 5023 5024 return ( sel + "" ).replace( rcssescape, fcssescape ); 5025 }; 5026 } 5027 5028 // Create new history entry only once 5029 var shouldCreateHistory = true; 5030 5031 // Variable containing last hash value set by fancyBox 5032 // It will be used to determine if fancyBox needs to close after hash change is detected 5033 var currentHash = null; 5034 5035 // Throttling the history change 5036 var timerID = null; 5037 5038 // Get info about gallery name and current index from url 5039 function parseUrl() { 5040 var hash = window.location.hash.substr( 1 ); 5041 var rez = hash.split( '-' ); 5042 var index = rez.length > 1 && /^\+?\d+$/.test( rez[ rez.length - 1 ] ) ? parseInt( rez.pop( -1 ), 10 ) || 1 : 1; 5043 var gallery = rez.join( '-' ); 5044 5045 // Index is starting from 1 5046 if ( index < 1 ) { 5047 index = 1; 5048 } 5049 5050 return { 5051 hash : hash, 5052 index : index, 5053 gallery : gallery 5054 }; 5055 } 5056 5057 // Trigger click evnt on links to open new fancyBox instance 5058 function triggerFromUrl( url ) { 5059 var $el; 5060 5061 if ( url.gallery !== '' ) { 5062 5063 // If we can find element matching 'data-fancybox' atribute, then trigger click event for that .. 5064 $el = $( "[data-fancybox='" + $.escapeSelector( url.gallery ) + "']" ).eq( url.index - 1 ); 5065 5066 if ( !$el.length ) { 5067 // .. if not, try finding element by ID 5068 $el = $( "#" + $.escapeSelector( url.gallery ) + "" ); 5069 } 5070 5071 if ( $el.length ) { 5072 shouldCreateHistory = false;
5073 5074 $el.trigger( 'click' ); 5075 } 5076 5077 } 5078 } 5079 5080 // Get gallery name from current instance 5081 function getGalleryID( instance ) { 5082 var opts; 5083 5084 if ( !instance ) { 5085 return false; 5086 } 5087 5088 opts = instance.current ? instance.current.opts : instance.opts; 5089 5090 return opts.hash || ( opts.$orig ? opts.$orig.data( 'fancybox' ) : '' ); 5091 } 5092 5093 // Start when DOM becomes ready 5094 $(function() { 5095 5096 // Check if user has disabled this module 5097 if ( $.fancybox.defaults.hash === false ) { 5098 return; 5099 } 5100 5101 // Update hash when opening/closing fancyBox 5102 $(document).on({ 5103 'onInit.fb' : function( e, instance ) { 5104 var url, gallery; 5105 5106 if ( instance.group[ instance.currIndex ].opts.hash === false ) { 5107 return; 5108 } 5109 5110 url = parseUrl(); 5111 gallery = getGalleryID( instance ); 5112 5113 // Make sure gallery start index matches index from hash 5114 if ( gallery && url.gallery && gallery == url.gallery ) { 5115 instance.currIndex = url.index - 1; 5116 } 5117 }, 5118 5119 'beforeShow.fb' : function( e, instance, current ) { 5120 var gallery; 5121 5122 if ( !current || current.opts.hash === false ) { 5123 return; 5124 } 5125 5126 gallery = getGalleryID( instance ); 5127 5128 // Update window hash 5129 if ( gallery && gallery !== '' ) { 5130 5131 if ( window.location.hash.indexOf( gallery ) < 0 ) { 5132 instance.opts.origHash = window.location.hash; 5133 } 5134 5135 currentHash = gallery + ( instance.group.length > 1 ? '-' + ( current.index + 1 ) : '' ); 5136 5137 if ( 'replaceState' in window.history ) { 5138 if ( timerID ) { 5139 clearTimeout( timerID ); 5140 } 5141 5142 timerID = setTimeout(function() { 5143 window.history[ shouldCreateHistory ? 'pushState' : 'replaceState' ]( {} , document.title, window.location.pathname + window.location.search + '#' + currentHash ); 5144 5145 timerID = null; 5146 5147 shouldCreateHistory = false; 5148 5149 }, 300); 5150 5151 } else { 5152 window.location.hash = currentHash; 5153 } 5154 5155 } 5156 5157 }, 5158 5159 'beforeClose.fb' : function( e, instance, current ) { 5160 var gallery, origHash; 5161 5162 if ( timerID ) { 5163 clearTimeout( timerID ); 5164 } 5165 5166 if ( current.opts.hash === false ) { 5167 return; 5168 } 5169 5170 gallery = getGalleryID( instance ); 5171 origHash = instance && instance.opts.origHash ? instance.opts.origHash : ''; 5172 5173 // Remove hash from location bar 5174 if ( gallery && gallery !== '' ) { 5175 5176 if ( 'replaceState' in history ) { 5177 window.history.replaceState( {} , document.title, window.location.pathname + window.location.search + origHash ); 5178 5179 } else { 5180 window.location.hash = origHash; 5181 5182 // Keep original scroll position 5183 $( window ).scrollTop( instance.scrollTop ).scrollLeft( instance.scrollLeft ); 5184 } 5185 } 5186 5187 currentHash = null; 5188 } 5189 }); 5190 5191 // Check if need to close after url has changed 5192 $(window).on('hashchange.fb', function() { 5193 var url = parseUrl(); 5194 5195 if ( $.fancybox.getInstance() ) { 5196 if ( currentHash && currentHash !== url.gallery + '-' + url.index && !( url.index === 1 && currentHash == url.gallery ) ) { 5197 currentHash = null; 5198 5199 $.fancybox.close(); 5200 } 5201 5202 } else if ( url.gallery !== '' ) { 5203 triggerFromUrl( url ); 5204 } 5205 }); 5206 5207 // Check current hash and trigger click event on matching element to start fancyBox, if needed 5208 setTimeout(function() { 5209 triggerFromUrl( parseUrl() ); 5210 }, 50); 5211 }); 5212 5213}( document, window, window.jQuery || jQuery )); 5214 5215;(function (document, $) { 5216 'use strict'; 5217 5218 var prevTime = new Date().getTime(); 5219 5220 $(document).on({ 5221 'onInit.fb' : function( e, instance, current ) { 5222 instance.$refs.stage.on('mousewheel DOMMouseScroll wheel MozMousePixelScroll', function(e) { 5223 var current = instance.current, 5224 currTime = new Date().getTime(); 5225 5226 if ( instance.group.length < 1 || current.opts.wheel === false || ( current.opts.wheel === 'auto' && current.type !== 'image' ) ) { 5227 return; 5228 } 5229 5230 e.preventDefault(); 5231 e.stopPropagation(); 5232 5233 if ( current.$slide.hasClass( 'fancybox-animated' ) ) { 5234 return; 5235 } 5236 5237 e = e.originalEvent || e; 5238 5239 if ( currTime - prevTime < 250 ) { 5240 return; 5241 } 5242 5243 prevTime = currTime; 5244 5245 instance[ ( -e.deltaY || -e.deltaX || e.wheelDelta || -e.detail ) < 0 ? 'next' : 'previous' ](); 5246 5247 }); 5248 } 5249 }); 5250 5251}( document, window.jQuery || jQuery ));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.