1// ================================================== 2// fancyBox v3.0.47 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 undefined; 19 } 20 21 // Private default settings 22 // ======================== 23 24 var defaults = { 25 26 // Animation duration in ms 27 speed : 330, 28 29 // Enable infinite gallery navigation 30 loop : true, 31 32 // Should zoom animation change opacity, too 33 // If opacity is 'auto', then fade-out if image and thumbnail have different aspect ratios 34 opacity : 'auto', 35 36 // Space around image, ignored if zoomed-in or viewport smaller than 800px 37 margin : [44, 0], 38 39 // Horizontal space between slides 40 gutter : 30, 41 42 // Should display toolbars 43 infobar : true, 44 buttons : true, 45 46 // What buttons should appear in the toolbar 47 slideShow : true, 48 fullScreen : true, 49 thumbs : true, 50 closeBtn : true, 51 52 // Should apply small close button at top right corner of the content 53 // If 'auto' - will be set for content having type 'html', 'inline' or 'ajax' 54 smallBtn : 'auto', 55 56 image : { 57 58 // Wait for images to load before displaying 59 // Requires predefined image dimensions 60 // If 'auto' - will zoom in thumbnail if 'width' and 'height' attributes are found 61 preload : "auto", 62 63 // Protect an image from downloading by right-click 64 protect : false 65 66 }, 67 68 ajax : { 69 70 // Object containing settings for ajax request 71 settings : { 72 73 // This helps to indicate that request comes from the modal 74 // Feel free to change naming 75 data : { 76 fancybox : true 77 } 78 } 79 80 }, 81 82 iframe : { 83 84 // Iframe template 85 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>', 86 87 // Preload iframe before displaying it 88 // This allows to calculate iframe content width and height 89 // (note: Due to "Same Origin Policy", you can't get cross domain data). 90 preload : true, 91 92 // Scrolling attribute for iframe tag 93 scrolling : 'no', 94 95 // Custom CSS styling for iframe wrapping element 96 css : {} 97 98 }, 99 100 // Custom CSS class for layout 101 baseClass : '', 102 103 // Custom CSS class for slide element 104 slideClass : '', 105 106 // Base template for layout 107 baseTpl : '<div class="fancybox-container" role="dialog" tabindex="-1">' + 108 '<div class="fancybox-bg"></div>' + 109 '<div class="fancybox-controls">' + 110 '<div class="fancybox-infobar">' + 111 '<button data-fancybox-previous class="fancybox-button fancybox-button--left" title="Previous"></button>' + 112 '<div class="fancybox-infobar__body">' + 113 '<span class="js-fancybox-index"></span> / <span class="js-fancybox-count"></span>' + 114 '</div>' + 115 '<button data-fancybox-next class="fancybox-button fancybox-button--right" title="Next"></button>' + 116 '</div>' + 117 '<div class="fancybox-buttons">' + 118 '<button data-fancybox-close class="fancybox-button fancybox-button--close" title="Close (Esc)"></button>' + 119 '</div>' + 120 '</div>' + 121 '<div class="fancybox-slider-wrap">' + 122 '<div class="fancybox-slider"></div>' + 123 '</div>' + 124 '<div class="fancybox-caption-wrap"><div class="fancybox-caption"></div></div>' + 125 '</div>', 126 127 // Loading indicator template 128 spinnerTpl : '<div class="fancybox-loading"></div>', 129 130 // Error message template 131 errorTpl : '<div class="fancybox-error"><p>The requested content cannot be loaded. <br /> Please try again later.<p></div>', 132 133 // This will be appended to html content, if "smallBtn" option is not set to false 134 closeTpl : '<button data-fancybox-close class="fancybox-close-small"></button>', 135 136 // Container is injected into this element 137 parentEl : 'body', 138 139 // Enable gestures (tap, zoom, pan and pinch) 140 touch : true, 141 142 // Enable keyboard navigation 143 keyboard : true, 144 145 // Try to focus on first focusable element after opening 146 focus : true, 147 148 // Close when clicked outside of the content 149 closeClickOutside : true, 150 151 // Callbacks 152 beforeLoad : $.noop, 153 afterLoad : $.noop, 154 beforeMove : $.noop, 155 afterMove : $.noop, 156 onComplete : $.noop, 157 158 onInit : $.noop, 159 beforeClose : $.noop, 160 afterClose : $.noop, 161 onActivate : $.noop, 162 onDeactivate : $.noop 163 164 }; 165 166 var $W = $(window); 167 var $D = $(document); 168 169 var called = 0; 170 171 // Check if an object is a jQuery object and not a native JavaScript object 172 // ======================================================================== 173 174 var isQuery = function (obj) { 175 return obj && obj.hasOwnProperty && obj instanceof $; 176 }; 177 178 // Handle multiple browsers for requestAnimationFrame() 179 // ==================================================== 180 181 var requestAFrame = (function() { 182 return window.requestAnimationFrame || 183 window.webkitRequestAnimationFrame || 184 window.mozRequestAnimationFrame || 185 function( callback ) { 186 window.setTimeout(callback, 1000 / 60); }; 187 })(); 188 189 190 // Check if element is inside the viewport by at least 1 pixel 191 // =========================================================== 192 193 var isElementInViewport = function( el ) { 194 var rect; 195 196 if ( typeof $ === "function" && el instanceof $ ) { 197 el = el[0]; 198 } 199 200 rect = el.getBoundingClientRect(); 201 202 return rect.bottom > 0 && rect.right > 0 && 203 rect.left < (window.innerWidth || document.documentElement.clientWidth) && 204 rect.top < (window.innerHeight || document.documentElement.clientHeight); 205 }; 206 207 208 // Class definition 209 // ================ 210 211 var FancyBox = function( content, opts, index ) { 212 var self = this; 213 214 self.opts = $.extend( true, { index : index }, defaults, opts || {} ); 215 self.id = self.opts.id || ++called; 216 self.group = []; 217 218 self.currIndex = parseInt( self.opts.index, 10 ) || 0; 219 self.prevIndex = null; 220 221 self.prevPos = null; 222 self.currPos = 0; 223 224 self.firstRun = null; 225 226 // Create group elements from original item collection 227 self.createGroup( content ); 228 229 if ( !self.group.length ) { 230 return; 231 } 232 233 // Save last active element and current scroll position 234 self.$lastFocus = $(document.activeElement).blur(); 235 236 // Collection of gallery objects 237 self.slides = {}; 238 239 self.init( content ); 240 241 }; 242 243 $.extend(FancyBox.prototype, { 244 245 // Create DOM structure 246 // ==================== 247 248 init : function() { 249 var self = this; 250 251 var galleryHasHtml = false;
252 253 var testWidth; 254 var $container; 255 256 self.scrollTop = $D.scrollTop(); 257 self.scrollLeft = $D.scrollLeft(); 258 259 if ( !$.fancybox.getInstance() ) { 260 testWidth = $( 'body' ).width(); 261 262 $( 'html' ).addClass( 'fancybox-enabled' ); 263 264 if ( $.fancybox.isTouch ) { 265 266 // Ugly workaround for iOS page shifting issue (when inputs get focus) 267 // Do not apply for images, otherwise top/bottom bars will appear 268 $.each( self.group, function( key, item ) { 269 if ( item.type !== 'image' && item.type !== 'iframe' ) { 270 galleryHasHtml = true; 271 return false; 272 } 273 }); 274 275 if ( galleryHasHtml ) { 276 $('body').css({ 277 position : 'fixed', 278 width : testWidth, 279 top : self.scrollTop * -1 280 }); 281 } 282 283 } else { 284 285 // Compare page width after adding "overflow:hidden" 286 testWidth = $( 'body' ).width() - testWidth; 287
288 // Width has changed - compensate missing scrollbars 289 if ( testWidth > 1 ) { 290 $( '<style id="fancybox-noscroll" type="text/css">' ).html( '.compensate-for-scrollbar, .fancybox-enabled body { margin-right: ' + testWidth + 'px; }' ).appendTo( 'head' ); 291 } 292 293 } 294 } 295 296 $container = $( self.opts.baseTpl ) 297 .attr('id', 'fancybox-container-' + self.id) 298 .data( 'FancyBox', self ) 299 .addClass( self.opts.baseClass ) 300 .hide() 301 .prependTo( self.opts.parentEl ); 302 303 // Create object holding references to jQuery wrapped nodes 304 self.$refs = { 305 container : $container, 306 bg : $container.find('.fancybox-bg'), 307 controls : $container.find('.fancybox-controls'), 308 buttons : $container.find('.fancybox-buttons'), 309 slider_wrap : $container.find('.fancybox-slider-wrap'), 310 slider : $container.find('.fancybox-slider'), 311 caption : $container.find('.fancybox-caption') 312 }; 313 314 self.trigger( 'onInit' ); 315 316 // Bring to front and enable events 317 self.activate(); 318 319 // Try to avoid running multiple times 320 if ( self.current ) { 321 return; 322 } 323 324 self.jumpTo( self.currIndex ); 325 326 }, 327 328 329 // Create array of gally item objects 330 // Check if each object has valid type and content 331 // =============================================== 332 333 createGroup : function ( content ) { 334 var self = this; 335 var items = $.makeArray( content ); 336 337 $.each(items, function( i, item ) { 338 var obj = {}, 339 opts = {}, 340 data = [], 341 $item, 342 type, 343 src, 344 srcParts; 345 346 // Step 1 - Make sure we have an object 347 348 if ( $.isPlainObject( item ) ) { 349 350 obj = item; 351 opts = item.opts || {}; 352 353 } else if ( $.type( item ) === 'object' && $( item ).length ) { 354 355 $item = $( item ); 356 data = $item.data(); 357 358 opts = 'options' in data ? data.options : {}; 359 360 opts = $.type( opts ) === 'object' ? opts : {}; 361 362 obj.type = 'type' in data ? data.type : opts.type; 363 obj.src = 'src' in data ? data.src : ( opts.src || $item.attr( 'href' ) ); 364 365 opts.width = 'width' in data ? data.width : opts.width; 366 opts.height = 'height' in data ? data.height : opts.height; 367 opts.thumb = 'thumb' in data ? data.thumb : opts.thumb; 368 369 opts.selector = 'selector' in data ? data.selector : opts.selector; 370 371 if ( 'srcset' in data ) { 372 opts.image = { srcset : data.srcset }; 373 } 374 375 opts.$orig = $item; 376 377 } else { 378 379 obj = { 380 type : 'html', 381 content : item + '' 382 }; 383 384 } 385 386 obj.opts = $.extend( true, {}, self.opts, opts ); 387 388 // Step 2 - Make sure we have supported content type 389 390 type = obj.type; 391 src = obj.src || ''; 392 393 if ( !type ) { 394 395 if ( obj.content ) { 396 type = 'html'; 397 398 } else if ( src.match(/(^data:image\/[a-z0-9+\/=]*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg|ico)((\?|#).*)?$)/i) ) { 399 type = 'image'; 400 401 } else if ( src.match(/\.(pdf)((\?|#).*)?$/i) ) { 402 type = 'pdf'; 403 404 } else if ( src.charAt(0) === '#' ) { 405 type = 'inline'; 406 407 } 408 409 obj.type = type; 410 411 } 412 413 // Step 3 - Some adjustments 414 415 obj.index = self.group.length; 416 417 // Check if $orig and $thumb objects exist 418 if ( obj.opts.$orig && !obj.opts.$orig.length ) { 419 delete obj.opts.$orig; 420 } 421 422 if ( !obj.opts.$thumb && obj.opts.$orig ) {
423 obj.opts.$thumb = obj.opts.$orig.find( 'img:first' ); 424 } 425 426 if ( obj.opts.$thumb && !obj.opts.$thumb.length ) { 427 delete obj.opts.$thumb; 428 } 429 430 // Caption is a "special" option, it can be passed as a method 431 if ( $.type( obj.opts.caption ) === 'function' ) { 432 obj.opts.caption = obj.opts.caption.apply( item, [ self, obj ] ); 433 434 } else if ( 'caption' in data ) { 435 obj.opts.caption = data.caption; 436 437 } else if ( opts.$orig ) { 438 obj.opts.caption = $item.attr( 'title' ); 439 } 440 441 // Make sure we have caption as a string 442 obj.opts.caption = obj.opts.caption === undefined ? '' : obj.opts.caption + ''; 443 444 // Check if url contains selector used to filter the content 445 // Example: "ajax.html #something" 446 if ( type === 'ajax' ) { 447 srcParts = src.split(/\s+/, 2); 448 449 if ( srcParts.length > 1 ) { 450 obj.src = srcParts.shift(); 451 452 obj.opts.selector = srcParts.shift(); 453 } 454 } 455 456 if ( obj.opts.smallBtn == 'auto' ) { 457 458 if ( $.inArray( type, ['html', 'inline', 'ajax'] ) > -1 ) { 459 obj.opts.buttons = false; 460 obj.opts.smallBtn = true; 461 462 } else { 463 obj.opts.smallBtn = false; 464 } 465 466 } 467 468 if ( type === 'pdf' ) { 469 470 obj.type = 'iframe'; 471 472 obj.opts.closeBtn = true; 473 obj.opts.smallBtn = false; 474 475 obj.opts.iframe.preload = false; 476 477 } 478 479 if ( obj.opts.modal ) { 480 481 $.extend(true, obj.opts, { 482 infobar : 0, 483 buttons : 0, 484 keyboard : 0, 485 slideShow : 0, 486 fullScreen : 0, 487 closeClickOutside : 0 488 }); 489 490 } 491 492 self.group.push( obj ); 493 494 }); 495 496 }, 497 498 499 // Attach an event handler functions for: 500 // - navigation elements 501 // - browser scrolling, resizing; 502 // - focusing 503 // - keyboard 504 // ================= 505 506 addEvents : function() { 507 var self = this; 508 509 self.removeEvents(); 510 511 // Make navigation elements clickable 512 513 self.$refs.container.on('click.fb-close', '[data-fancybox-close]', function(e) { 514 e.stopPropagation(); 515 e.preventDefault(); 516 517 self.close( e ); 518 519 }).on('click.fb-previous', '[data-fancybox-previous]', function(e) { 520 e.stopPropagation(); 521 e.preventDefault(); 522 523 self.previous(); 524 525 }).on('click.fb-next', '[data-fancybox-next]', function(e) { 526 e.stopPropagation(); 527 e.preventDefault(); 528 529 self.next(); 530 }); 531 532 533 // Handle page scrolling and browser resizing 534 535 $( window ).on('orientationchange.fb resize.fb', function(e) { 536 requestAFrame(function() { 537 538 if ( e && e.originalEvent && e.originalEvent.type === "resize" ) { 539 self.update(); 540 541 } else { 542 self.$refs.slider_wrap.hide(); 543 544 requestAFrame(function () { 545 self.$refs.slider_wrap.show(); 546 547 self.update(); 548 }); 549 550 } 551 552 }); 553 }); 554 555 556 // Trap focus 557 558 $D.on('focusin.fb', function(e) { 559 var instance = $.fancybox ? $.fancybox.getInstance() : null; 560 561 if ( instance && !$( e.target ).hasClass( 'fancybox-container' ) && !$.contains( instance.$refs.container[0], e.target ) ) { 562 e.stopPropagation(); 563 564 instance.focus(); 565 566 // Sometimes page gets scrolled, set it back 567 $W.scrollTop( self.scrollTop ).scrollLeft( self.scrollLeft ); 568 } 569 570 }); 571 572 // Enable keyboard navigation 573 574 $D.on('keydown.fb', function (e) { 575 var current = self.current, 576 keycode = e.keyCode || e.which; 577 578 if ( !current || !current.opts.keyboard ) { 579 return; 580 } 581 582 if ( $(e.target).is('input') || $(e.target).is('textarea') ) { 583 return; 584 } 585 586 // Backspace and Esc keys 587 if ( keycode === 8 || keycode === 27 ) { 588 e.preventDefault(); 589 590 self.close( e ); 591 592 return; 593 } 594 595 switch ( keycode ) { 596 597 case 37: // Left arrow 598 case 38: // Up arrow 599 600 e.preventDefault(); 601 602 self.previous(); 603 604 break; 605 606 case 39: // Right arrow 607 case 40: // Down arrow 608 609 e.preventDefault(); 610 611 self.next(); 612 613 break; 614 615 case 80: // "P" 616 case 32: // Spacebar 617 618 e.preventDefault(); 619 620 if ( self.SlideShow ) { 621 e.preventDefault(); 622 623 self.SlideShow.toggle(); 624 } 625 626 break; 627 628 case 70: // "F" 629 630 if ( self.FullScreen ) { 631 e.preventDefault(); 632 633 self.FullScreen.toggle(); 634 } 635 636 break; 637 638 case 71: // "G" 639 640 if ( self.Thumbs ) { 641 e.preventDefault(); 642 643 self.Thumbs.toggle(); 644 } 645 646 break; 647 } 648 }); 649 650 651 }, 652 653 654 // Remove events added by the core 655 // =============================== 656 657 removeEvents : function () { 658 659 $W.off( 'scroll.fb resize.fb orientationchange.fb' ); 660 $D.off( 'keydown.fb focusin.fb click.fb-close' ); 661 662 this.$refs.container.off('click.fb-close click.fb-previous click.fb-next'); 663 }, 664 665 666 // Slide to left 667 // ================== 668 669 previous : function( duration ) { 670 this.jumpTo( this.currIndex - 1, duration ); 671 }, 672 673 674 // Slide to right 675 // =================== 676 677 next : function( duration ) { 678 this.jumpTo( this.currIndex + 1, duration ); 679 }, 680 681 682 // Display current gallery item, move slider to current position 683 // ============================================================= 684 685 jumpTo : function ( to, duration ) { 686 var self = this, 687 firstRun, 688 index, 689 pos, 690 loop; 691 692 firstRun = self.firstRun = ( self.firstRun === null ); 693 694 index = pos = to = parseInt( to, 10 ); 695 loop = self.current ? self.current.opts.loop : false;
696 697 if ( self.isAnimating || ( index == self.currIndex && !firstRun ) ) { 698 return; 699 } 700 701 if ( self.group.length > 1 && loop ) { 702 703 index = index % self.group.length; 704 index = index < 0 ? self.group.length + index : index; 705 706 // Calculate closest position of upcoming item from the current one 707 if ( self.group.length == 2 ) { 708 pos = to - self.currIndex + self.currPos; 709 710 } else { 711 pos = index - self.currIndex + self.currPos; 712 713 if ( Math.abs( self.currPos - ( pos + self.group.length ) ) < Math.abs( self.currPos - pos ) ) { 714 pos = pos + self.group.length; 715 716 } else if ( Math.abs( self.currPos - ( pos - self.group.length ) ) < Math.abs( self.currPos - pos ) ) { 717 pos = pos - self.group.length; 718 719 } 720 } 721 722 } else if ( !self.group[ index ] ) { 723 self.update( false, false, duration ); 724 725 return; 726 } 727 728 if ( self.current ) { 729 self.current.$slide.removeClass('fancybox-slide--current fancybox-slide--complete'); 730 731 self.updateSlide( self.current, true ); 732 } 733 734 self.prevIndex = self.currIndex; 735 self.prevPos = self.currPos; 736 737 self.currIndex = index; 738 self.currPos = pos; 739 740 // Create slides 741 742 self.current = self.createSlide( pos ); 743 744 if ( self.group.length > 1 ) { 745 746 if ( self.opts.loop || pos - 1 >= 0 ) { 747 self.createSlide( pos - 1 ); 748 } 749 750 if ( self.opts.loop || pos + 1 < self.group.length ) { 751 self.createSlide( pos + 1 ); 752 } 753 } 754 755 self.current.isMoved = false; 756 self.current.isComplete = false; 757 758 duration = parseInt( duration === undefined ? self.current.opts.speed * 1.5 : duration, 10 ); 759 760 // Move slider to the next position 761 // Note: the content might still be loading 762 self.trigger( 'beforeMove' ); 763 764 self.updateControls(); 765 766 if ( firstRun ) { 767 self.current.$slide.addClass('fancybox-slide--current'); 768 769 self.$refs.container.show(); 770 771 requestAFrame(function() { 772 self.$refs.bg.css('transition-duration', self.current.opts.speed + 'ms'); 773 774 self.$refs.container.addClass( 'fancybox-container--ready' ); 775 }); 776 } 777 778 // Set position immediately on first opening 779 self.update( true, false, firstRun ? 0 : duration, function() { 780 self.afterMove(); 781 }); 782 783 self.loadSlide( self.current ); 784 785 if ( !( firstRun && self.current.$ghost ) ) { 786 self.preload(); 787 } 788 789 }, 790 791 792 // Create new "slide" element 793 // These are gallery items that are actually added to DOM 794 // ======================================================= 795 796 createSlide : function( pos ) { 797 798 var self = this; 799 var $slide; 800 var index; 801 var found; 802 803 index = pos % self.group.length; 804 index = index < 0 ? self.group.length + index : index; 805 806 if ( !self.slides[ pos ] && self.group[ index ] ) { 807 808 // If we are looping and slide with that index already exists, then reuse it 809 if ( self.opts.loop && self.group.length > 2 ) { 810 for (var key in self.slides) { 811 if ( self.slides[ key ].index === index ) { 812 found = self.slides[ key ]; 813 found.pos = pos; 814 815 self.slides[ pos ] = found; 816 817 delete self.slides[ key ]; 818 819 self.updateSlide( found ); 820 821 return found; 822 } 823 } 824 } 825
826 $slide = $('<div class="fancybox-slide"></div>').appendTo( self.$refs.slider ); 827 828 self.slides[ pos ] = $.extend( true, {}, self.group[ index ], { 829 pos : pos, 830 $slide : $slide, 831 isMoved : false, 832 isLoaded : false 833 }); 834 835 } 836 837 return self.slides[ pos ]; 838 839 }, 840 841 zoomInOut : function( type, duration, callback ) { 842 843 var self = this; 844 var current = self.current; 845 var $what = current.$placeholder; 846 var opacity = current.opts.opacity; 847 var $thumb = current.opts.$thumb; 848 var thumbPos = $thumb ? $thumb.offset() : 0; 849 var slidePos = current.$slide.offset(); 850 var props; 851 var start; 852 var end; 853 854 if ( !$what || !current.isMoved || !thumbPos || !isElementInViewport( $thumb ) ) { 855 return false; 856 } 857 858 if ( type === 'In' && !self.firstRun ) { 859 return false; 860 } 861 862 $.fancybox.stop( $what ); 863 864 self.isAnimating = true; 865 866 props = { 867 top : thumbPos.top - slidePos.top + parseFloat( $thumb.css( "border-top-width" ) || 0 ), 868 left : thumbPos.left - slidePos.left + parseFloat( $thumb.css( "border-left-width" ) || 0 ), 869 width : $thumb.width(), 870 height : $thumb.height(), 871 scaleX : 1, 872 scaleY : 1 873 }; 874 875 // Check if we need to animate opacity 876 if ( opacity == 'auto' ) { 877 opacity = Math.abs( current.width / current.height - props.width / props.height ) > 0.1; 878 } 879 880 if ( type === 'In' ) { 881 start = props; 882 end = self.getFitPos( current ); 883 884 end.scaleX = end.width / start.width; 885 end.scaleY = end.height / start.height; 886 887 if ( opacity ) { 888 start.opacity = 0.1; 889 end.opacity = 1; 890 } 891 892 } else { 893 894 start = $.fancybox.getTranslate( $what ); 895 end = props; 896 897 // Switch to thumbnail image to improve animation performance 898 if ( current.$ghost ) { 899 current.$ghost.show(); 900 901 if ( current.$image ) { 902 current.$image.remove(); 903 } 904 } 905 906 start.scaleX = start.width / end.width; 907 start.scaleY = start.height / end.height; 908 909 start.width = end.width; 910 start.height = end.height; 911 912 if ( opacity ) { 913 end.opacity = 0; 914 } 915 916 } 917 918 self.updateCursor( end.width, end.height ); 919 920 // There is no need to animate width/height properties 921 delete end.width; 922 delete end.height; 923 924 $.fancybox.setTranslate( $what, start ); 925 926 $what.show(); 927 928 self.trigger( 'beforeZoom' + type ); 929 930 $what.css( 'transition', 'all ' + duration + 'ms' ); 931 932 $.fancybox.setTranslate( $what, end ); 933 934 setTimeout(function() { 935 var reset; 936 937 $what.css( 'transition', 'none' ); 938 939 reset = $.fancybox.getTranslate( $what ); 940 941 reset.scaleX = 1; 942 reset.scaleY = 1; 943 944 // Reset scalex/scaleY values; this helps for perfomance 945 $.fancybox.setTranslate( $what, reset ); 946 947 self.trigger( 'afterZoom' + type ); 948 949 callback.apply( self ); 950 951 self.isAnimating = false; 952 953 }, duration); 954 955 956 return true; 957 958 }, 959 960 // Check if image dimensions exceed parent element 961 // =============================================== 962 963 canPan : function() { 964 965 var self = this; 966 967 var current = self.current; 968 var $what = current.$placeholder; 969 970 var rez = false;
971 972 if ( $what ) { 973 rez = self.getFitPos( current ); 974 rez = Math.abs( $what.width() - rez.width ) > 1 || Math.abs( $what.height() - rez.height ) > 1; 975 976 } 977 978 return rez; 979 980 }, 981 982 983 // Check if current image dimensions are smaller than actual 984 // ========================================================= 985 986 isScaledDown : function() { 987 988 var self = this; 989 990 var current = self.current; 991 var $what = current.$placeholder; 992 993 var rez = false; 994 995 if ( $what ) { 996 rez = $.fancybox.getTranslate( $what ); 997 rez = rez.width < current.width || rez.height < current.height; 998 } 999 1000 return rez; 1001 1002 }, 1003 1004 1005 // Scale image to the actual size of the image 1006 // =========================================== 1007 1008 scaleToActual : function( x, y, duration ) { 1009 1010 var self = this; 1011 1012 var current = self.current; 1013 var $what = current.$placeholder; 1014 1015 var imgPos, posX, posY, scaleX, scaleY; 1016 1017 var canvasWidth = parseInt( current.$slide.width(), 10 ); 1018 var canvasHeight = parseInt( current.$slide.height(), 10 ); 1019 1020 var newImgWidth = current.width; 1021 var newImgHeight = current.height; 1022 1023 if ( !$what ) { 1024 return; 1025 } 1026 1027 self.isAnimating = true; 1028 1029 x = x === undefined ? canvasWidth * 0.5 : x; 1030 y = y === undefined ? canvasHeight * 0.5 : y; 1031 1032 imgPos = $.fancybox.getTranslate( $what ); 1033 1034 scaleX = newImgWidth / imgPos.width; 1035 scaleY = newImgHeight / imgPos.height; 1036 1037 // Get center position for original image 1038 posX = ( canvasWidth * 0.5 - newImgWidth * 0.5 ); 1039 posY = ( canvasHeight * 0.5 - newImgHeight * 0.5 ); 1040 1041 // Make sure image does not move away from edges 1042 1043 if ( newImgWidth > canvasWidth ) { 1044 posX = imgPos.left * scaleX - ( ( x * scaleX ) - x ); 1045 1046 if ( posX > 0 ) { 1047 posX = 0; 1048 } 1049 1050 if ( posX < canvasWidth - newImgWidth ) { 1051 posX = canvasWidth - newImgWidth; 1052 } 1053 } 1054 1055 if ( newImgHeight > canvasHeight) { 1056 posY = imgPos.top * scaleY - ( ( y * scaleY ) - y ); 1057 1058 if ( posY > 0 ) { 1059 posY = 0; 1060 } 1061 1062 if ( posY < canvasHeight - newImgHeight ) { 1063 posY = canvasHeight - newImgHeight; 1064 } 1065 } 1066 1067 self.updateCursor( newImgWidth, newImgHeight ); 1068 1069 $.fancybox.animate( $what, null, { 1070 top : posY, 1071 left : posX, 1072 scaleX : scaleX, 1073 scaleY : scaleY 1074 }, duration || current.opts.speed, function() { 1075 self.isAnimating = false; 1076 }); 1077 1078 }, 1079 1080 1081 // Scale image to fit inside parent element 1082 // ======================================== 1083 1084 scaleToFit : function( duration ) { 1085 1086 var self = this; 1087 1088 var current = self.current; 1089 var $what = current.$placeholder; 1090 var end; 1091 1092 if ( !$what ) { 1093 return; 1094 } 1095 1096 self.isAnimating = true; 1097 1098 end = self.getFitPos( current ); 1099 1100 self.updateCursor( end.width, end.height ); 1101 1102 $.fancybox.animate( $what, null, { 1103 top : end.top, 1104 left : end.left, 1105 scaleX : end.width / $what.width(), 1106 scaleY : end.height / $what.height() 1107 }, duration || current.opts.speed, function() { 1108 self.isAnimating = false; 1109 }); 1110 1111 }, 1112 1113 // Calculate image size to fit inside viewport 1114 // =========================================== 1115 1116 getFitPos : function( slide ) { 1117 var $what = slide.$placeholder || slide.$content; 1118 1119 var imgWidth = slide.width; 1120 var imgHeight = slide.height; 1121 1122 var margin = slide.opts.margin; 1123 1124 var canvasWidth, canvasHeight, minRatio, top, left, width, height; 1125 1126 if ( !$what || !$what.length || ( !imgWidth && !imgHeight) ) { 1127 return false;
1128 } 1129 1130 // Convert "margin to CSS style: [ top, right, bottom, left ] 1131 if ( $.type( margin ) === "number" ) { 1132 margin = [ margin, margin ]; 1133 } 1134 1135 if ( margin.length == 2 ) { 1136 margin = [ margin[0], margin[1], margin[0], margin[1] ]; 1137 } 1138 1139 if ( $W.width() < 800 ) { 1140 margin = [0, 0, 0, 0]; 1141 } 1142 1143 canvasWidth = parseInt( slide.$slide.width(), 10 ) - ( margin[ 1 ] + margin[ 3 ] ); 1144 canvasHeight = parseInt( slide.$slide.height(), 10 ) - ( margin[ 0 ] + margin[ 2 ] ); 1145 1146 minRatio = Math.min(1, canvasWidth / imgWidth, canvasHeight / imgHeight ); 1147 1148 // Use floor rounding to make sure it really fits 1149 1150 width = Math.floor( minRatio * imgWidth ); 1151 height = Math.floor( minRatio * imgHeight ); 1152 1153 top = Math.floor( ( canvasHeight - height ) * 0.5 ) + margin[ 0 ]; 1154 left = Math.floor( ( canvasWidth - width ) * 0.5 ) + margin[ 3 ]; 1155 1156 return { 1157 top : top, 1158 left : left, 1159 width : width, 1160 height : height 1161 }; 1162 1163 }, 1164 1165 // Move slider to current position 1166 // Update all slides (and their content) 1167 // ===================================== 1168 1169 update : function( andSlides, andContent, duration, callback ) { 1170 1171 var self = this; 1172 var leftValue; 1173 1174 if ( self.isAnimating === true || !self.current ) { 1175 return; 1176 } 1177 1178 leftValue = ( self.current.pos * Math.floor( self.current.$slide.width() ) * -1 ) - ( self.current.pos * self.current.opts.gutter ); 1179 duration = parseInt( duration, 10 ) || 0; 1180 1181 $.fancybox.stop( self.$refs.slider ); 1182 1183 if ( andSlides === false ) { 1184 self.updateSlide( self.current, andContent ); 1185 1186 } else { 1187 1188 $.each( self.slides, function( key, slide ) { 1189 self.updateSlide( slide, andContent ); 1190 }); 1191 1192 } 1193 1194 if ( duration ) { 1195 1196 $.fancybox.animate( self.$refs.slider, null, { 1197 top : 0, 1198 left : leftValue 1199 }, duration, function() { 1200 self.current.isMoved = true; 1201 1202 if ( $.type( callback ) === 'function' ) { 1203 callback.apply( self ); 1204 } 1205 1206 }); 1207 1208 } else { 1209 1210 $.fancybox.setTranslate( self.$refs.slider, { top : 0, left : leftValue } ); 1211 1212 self.current.isMoved = true; 1213 1214 if ( $.type( callback ) === 'function' ) { 1215 callback.apply( self ); 1216 } 1217 1218 } 1219 1220 }, 1221 1222 1223 // Update slide position and scale content to fit 1224 // ============================================== 1225 1226 updateSlide : function( slide, andContent ) { 1227 1228 var self = this; 1229 var $what = slide.$placeholder; 1230 var leftPos; 1231 1232 slide = slide || self.current; 1233 1234 if ( !slide || self.isClosing ) { 1235 return; 1236 } 1237 1238 leftPos = ( slide.pos * Math.floor( slide.$slide.width() ) ) + ( slide.pos * slide.opts.gutter); 1239 1240 if ( leftPos !== slide.leftPos ) { 1241 $.fancybox.setTranslate( slide.$slide, { top: 0, left : leftPos } ); 1242 1243 slide.leftPos = leftPos; 1244 } 1245 1246 if ( andContent !== false && $what ) { 1247 $.fancybox.setTranslate( $what, self.getFitPos( slide ) ); 1248 1249 if ( slide.pos === self.currPos ) { 1250 self.updateCursor(); 1251 } 1252 } 1253 1254 slide.$slide.trigger( 'refresh' ); 1255 1256 self.trigger( 'onUpdate', slide ); 1257 }, 1258 1259 // Update cursor style depending if content can be zoomed 1260 // ====================================================== 1261 1262 updateCursor : function( nextWidth, nextHeight ) { 1263 1264 var self = this; 1265 var canScale; 1266 1267 var $container = self.$refs.container.removeClass('fancybox-controls--canzoomIn fancybox-controls--canzoomOut fancybox-controls--canGrab'); 1268 1269 if ( self.isClosing || !self.opts.touch ) { 1270 return; 1271 } 1272 1273 if ( nextWidth !== undefined && nextHeight !== undefined ) { 1274 canScale = nextWidth < self.current.width && nextHeight < self.current.height; 1275 1276 } else { 1277 canScale = self.isScaledDown(); 1278 } 1279 1280 if ( canScale ) { 1281 $container.addClass('fancybox-controls--canzoomIn'); 1282 1283 } else if ( self.group.length < 2 ) { 1284 $container.addClass('fancybox-controls--canzoomOut'); 1285 1286 } else { 1287 $container.addClass('fancybox-controls--canGrab'); 1288 } 1289 1290 }, 1291 1292 // Load content into the slide 1293 // =========================== 1294 1295 loadSlide : function( slide ) { 1296 1297 var self = this, type, $slide; 1298 var ajaxLoad; 1299 1300 if ( !slide || slide.isLoaded || slide.isLoading ) { 1301 return; 1302 } 1303 1304 slide.isLoading = true; 1305 1306 self.trigger( 'beforeLoad', slide ); 1307 1308 type = slide.type; 1309 $slide = slide.$slide; 1310 1311 $slide 1312 .off( 'refresh' ) 1313 .trigger( 'onReset' ) 1314 .addClass( 'fancybox-slide--' + ( type || 'unknown' ) ) 1315 .addClass( slide.opts.slideClass ); 1316 1317 // Create content depending on the type 1318 1319 switch ( type ) { 1320 1321 case 'image': 1322 1323 self.setImage( slide ); 1324 1325 break; 1326 1327 case 'iframe': 1328 1329 self.setIframe( slide ); 1330 1331 break; 1332 1333 case 'html': 1334 1335 self.setContent( slide, slide.content ); 1336 1337 break; 1338 1339 case 'inline': 1340 1341 if ( $( slide.src ).length ) { 1342 self.setContent( slide, $( slide.src ) ); 1343 1344 } else { 1345 self.setError( slide ); 1346 } 1347 1348 break; 1349 1350 case 'ajax': 1351 1352 self.showLoading( slide ); 1353 1354 ajaxLoad = $.ajax( $.extend( {}, slide.opts.ajax.settings, { 1355 1356 url: slide.src, 1357 1358 success: function ( data, textStatus ) { 1359 1360 if ( textStatus === 'success' ) { 1361 self.setContent( slide, data ); 1362 } 1363 1364 }, 1365 1366 error: function ( jqXHR, textStatus ) { 1367 1368 if ( jqXHR && textStatus !== 'abort' ) { 1369 self.setError( slide ); 1370 } 1371 1372 } 1373 1374 })); 1375 1376 $slide.one( 'onReset', function () { 1377 ajaxLoad.abort(); 1378 }); 1379 1380 break; 1381 1382 default: 1383 1384 self.setError( slide ); 1385 1386 break; 1387 1388 } 1389 1390 return true; 1391 1392 }, 1393 1394 1395 // Use thumbnail image, if possible 1396 // ================================ 1397 1398 setImage : function( slide ) { 1399 1400 var self = this; 1401 var srcset = slide.opts.image.srcset; 1402
1403 var found, temp, pxRatio, windowWidth; 1404 1405 if ( slide.isLoaded && !slide.hasError ) { 1406 self.afterLoad( slide ); 1407 1408 return; 1409 } 1410 1411 // If we have "srcset", then we need to find matching "src" value. 1412 // This is necessary, because when you set an src attribute, the browser will preload the image 1413 // before any javascript or even CSS is applied. 1414 if ( srcset ) { 1415 pxRatio = window.devicePixelRatio || 1; 1416 windowWidth = window.innerWidth * pxRatio; 1417 1418 temp = srcset.split(',').map(function (el) { 1419 var ret = {}; 1420 1421 el.trim().split(/\s+/).forEach(function (el, i) { 1422 var value = parseInt(el.substring(0, el.length - 1), 10); 1423 1424 if ( i === 0 ) { 1425 return (ret.url = el); 1426 } 1427 1428 if ( value ) { 1429 ret.value = value; 1430 ret.postfix = el[el.length - 1]; 1431 } 1432 1433 }); 1434 1435 return ret; 1436 }); 1437 1438 // Sort by value 1439 temp.sort(function (a, b) { 1440 return a.value - b.value; 1441 }); 1442 1443 // Ok, now we have an array of all srcset values 1444 for ( var j = 0; j < temp.length; j++ ) { 1445 var el = temp[ j ]; 1446 1447 if ( ( el.postfix === 'w' && el.value >= windowWidth ) || ( el.postfix === 'x' && el.value >= pxRatio ) ) { 1448 found = el; 1449 break; 1450 } 1451 } 1452 1453 // If not found, take the last one 1454 if ( !found && temp.length ) { 1455 found = temp[ temp.length - 1 ]; 1456 } 1457 1458 if ( found ) { 1459 slide.src = found.url; 1460 1461 // If we have default width/height values, we can calculate height for matching source 1462 if ( slide.width && slide.height && found.postfix == 'w' ) { 1463 slide.height = ( slide.width / slide.height ) * found.value; 1464 slide.width = found.value; 1465 } 1466 } 1467 } 1468 1469 slide.$placeholder = $('<div class="fancybox-placeholder"></div>') 1470 .hide() 1471 .appendTo( slide.$slide ); 1472 1473 if ( slide.opts.preload !== false && slide.opts.width && slide.opts.height && ( slide.opts.thumb || slide.opts.$thumb ) ) { 1474 1475 slide.width = slide.opts.width; 1476 slide.height = slide.opts.height; 1477 1478 slide.$ghost = $('<img />') 1479 .one('load error', function() { 1480 1481 if ( self.isClosing ) { 1482 return; 1483 } 1484 1485 // Start preloading full size image 1486 $('<img/>')[0].src = slide.src; 1487 1488 // zoomIn or just show 1489 self.revealImage( slide, function() { 1490 1491 self.setBigImage( slide ); 1492 1493 if ( self.firstRun && slide.index === self.currIndex ) { 1494 self.preload(); 1495 } 1496 }); 1497 1498 }) 1499 .addClass( 'fancybox-image' ) 1500 .appendTo( slide.$placeholder ) 1501 .attr( 'src', slide.opts.thumb || slide.opts.$thumb.attr( 'src' ) ); 1502 1503 } else { 1504 1505 self.setBigImage( slide ); 1506 1507 } 1508 1509 }, 1510 1511 1512 // Create full-size image 1513 // ====================== 1514 1515 setBigImage : function ( slide ) { 1516 var self = this; 1517 var $img = $('<img />'); 1518 1519 slide.$image = $img 1520 .one('error', function() { 1521 1522 self.setError( slide ); 1523 1524 }) 1525 .one('load', function() { 1526 1527 // Clear timeout that checks if loading icon needs to be displayed 1528 clearTimeout( slide.timouts ); 1529 1530 slide.timouts = null; 1531 1532 if ( self.isClosing ) { 1533 return; 1534 } 1535
1536 slide.width = this.naturalWidth; 1537 slide.height = this.naturalHeight; 1538 1539 if ( slide.opts.image.srcset ) { 1540 $img.attr('sizes', '100vw').attr('srcset', slide.opts.image.srcset); 1541 } 1542 1543 self.afterLoad( slide ); 1544 1545 if ( slide.$ghost ) { 1546 slide.timouts = setTimeout(function() { 1547 slide.$ghost.hide(); 1548 1549 }, 350); 1550 } 1551 1552 }) 1553 .addClass('fancybox-image') 1554 .attr('src', slide.src) 1555 .appendTo( slide.$placeholder ); 1556 1557 if ( $img[0].complete ) { 1558 $img.trigger('load'); 1559 1560 } else if( $img[0].error ) { 1561 $img.trigger('error'); 1562 1563 } else { 1564 1565 slide.timouts = setTimeout(function() { 1566 if ( !$img[0].complete && !slide.hasError ) { 1567 self.showLoading( slide ); 1568 } 1569 1570 }, 150); 1571 1572 } 1573 1574 if ( slide.opts.image.protect ) { 1575 $('<div class="fancybox-spaceball"></div>').appendTo( slide.$placeholder ).on('contextmenu.fb',function(e){ 1576 if ( e.button == 2 ) { 1577 e.preventDefault(); 1578 } 1579 1580 return true; 1581 }); 1582 } 1583 1584 }, 1585 1586 // Simply show image holder without animation 1587 // It has been hidden initially to avoid flickering 1588 // ================================================ 1589 1590 revealImage : function( slide, callback ) { 1591 1592 var self = this; 1593 1594 callback = callback || $.noop; 1595 1596 if ( slide.type !== 'image' || slide.hasError || slide.isRevealed === true ) { 1597 1598 callback.apply( self ); 1599 1600 return; 1601 } 1602 1603 slide.isRevealed = true; 1604 1605 if ( !( slide.pos === self.currPos && self.zoomInOut( 'In', slide.opts.speed, callback ) ) ) { 1606 1607 if ( slide.$ghost && !slide.isLoaded ) { 1608 self.updateSlide( slide, true ); 1609 } 1610 1611 if ( slide.pos === self.currPos ) { 1612 $.fancybox.animate( slide.$placeholder, { opacity: 0 }, { opacity: 1 }, 300, callback ); 1613 1614 } else { 1615 slide.$placeholder.show(); 1616 } 1617 1618 callback.apply( self ); 1619 1620 } 1621 1622 }, 1623 1624 // Create iframe wrapper, iframe and bindings 1625 // ========================================== 1626 1627 setIframe : function( slide ) { 1628 var self = this, 1629 opts = slide.opts.iframe, 1630 $slide = slide.$slide, 1631 $iframe; 1632 1633 slide.$content = $('<div class="fancybox-content"></div>') 1634 .css( opts.css ) 1635 .appendTo( $slide ); 1636 1637 $iframe = $( opts.tpl.replace(/\{rnd\}/g, new Date().getTime()) ) 1638 .attr('scrolling', $.fancybox.isTouch ? 'auto' : opts.scrolling) 1639 .appendTo( slide.$content ); 1640 1641 if ( opts.preload ) { 1642 slide.$content.addClass( 'fancybox-tmp' ); 1643 1644 self.showLoading( slide ); 1645 1646 // Unfortunately, it is not always possible to determine if iframe is successfully loaded 1647 // (due to browser security policy) 1648 1649 $iframe.on('load.fb error.fb', function(e) { 1650 this.isReady = 1; 1651 1652 slide.$slide.trigger( 'refresh' ); 1653 1654 self.afterLoad( slide ); 1655 1656 }); 1657 1658 // Recalculate iframe content size 1659 1660 $slide.on('refresh.fb', function() { 1661 var $wrap = slide.$content, 1662 $contents, 1663 $body, 1664 scrollWidth, 1665 frameWidth, 1666 frameHeight; 1667 1668 if ( $iframe[0].isReady !== 1 ) { 1669 return; 1670 } 1671 1672 // Check if content is accessible, 1673 // it will fail if frame is not with the same origin 1674 1675 try { 1676 $contents = $iframe.contents(); 1677 $body = $contents.find('body'); 1678 1679 } catch (ignore) {} 1680 1681 // Calculate dimensions for the wrapper 1682 1683 if ( $body && $body.length && !( opts.css.width !== undefined && opts.css.height !== undefined ) ) { 1684 1685 scrollWidth = $iframe[0].contentWindow.document.documentElement.scrollWidth; 1686 1687 frameWidth = Math.ceil( $body.outerWidth(true) + ( $wrap.width() - scrollWidth ) );
1688 frameHeight = Math.ceil( $body.outerHeight(true) ); 1689 1690 // Resize wrapper to fit iframe content 1691 1692 $wrap.css({ 1693 'width' : opts.css.width === undefined ? frameWidth + ( $wrap.outerWidth() - $wrap.innerWidth() ) : opts.css.width, 1694 'height' : opts.css.height === undefined ? frameHeight + ( $wrap.outerHeight() - $wrap.innerHeight() ) : opts.css.height 1695 }); 1696 1697 } 1698 1699 $wrap.removeClass( 'fancybox-tmp' ); 1700 1701 }); 1702 1703 } else { 1704 1705 this.afterLoad( slide ); 1706 1707 } 1708 1709 $iframe.attr( 'src', slide.src ); 1710 1711 if ( slide.opts.smallBtn ) { 1712 slide.$content.prepend( slide.opts.closeTpl ); 1713 } 1714 1715 // Remove iframe if closing or changing gallery item 1716 1717 $slide.one('onReset', function () { 1718 1719 // This helps IE not to throw errors when closing 1720 1721 try { 1722 1723 $(this).find('iframe').hide().attr('src', '//about:blank'); 1724 1725 } catch (ignore) {} 1726 1727 $(this).empty(); 1728 1729 slide.isLoaded = false; 1730 1731 }); 1732 1733 }, 1734 1735 1736 // Wrap and append content to the slide 1737 // ====================================== 1738 1739 setContent : function ( slide, content ) { 1740 1741 var self = this; 1742 1743 if ( self.isClosing ) { 1744 return; 1745 } 1746 1747 self.hideLoading( slide ); 1748 1749 slide.$slide.empty(); 1750 1751 if ( isQuery( content ) && content.parent().length ) { 1752 1753 // If it is a jQuery object, then it will be moved to the box. 1754 // The placeholder is created so we will know where to put it back. 1755 // If user is navigating gallery fast, then the content might be already moved to the box 1756 1757 if ( content.data( 'placeholder' ) ) { 1758 content.parents('.fancybox-slide').trigger( 'onReset' ); 1759 } 1760 1761 content.data({'placeholder' : $('<div></div>' ).hide().insertAfter( content ) }).css('display', 'inline-block'); 1762 1763 } else { 1764 1765 if ( $.type( content ) === 'string' ) { 1766 1767 content = $('<div>').append( content ).contents(); 1768 1769 if ( content[0].nodeType === 3 ) { 1770 content = $('<div>').html( content ); 1771 } 1772 1773 } 1774 1775 if ( slide.opts.selector ) { 1776 content = $('<div>').html( content ).find( slide.opts.selector ); 1777 } 1778 1779 } 1780 1781 slide.$slide.one('onReset', function () { 1782 var placeholder = isQuery( content ) ? content.data('placeholder') : 0; 1783 1784 if ( placeholder ) { 1785 content.hide().replaceAll( placeholder ); 1786 1787 content.data( 'placeholder', null ); 1788 } 1789 1790 if ( !slide.hasError ) { 1791 $(this).empty(); 1792 1793 slide.isLoaded = false; 1794 } 1795 1796 }); 1797 1798 slide.$content = $( content ).appendTo( slide.$slide ); 1799 1800 if ( slide.opts.smallBtn === true ) { 1801 slide.$content.find( '.fancybox-close-small' ).remove().end().eq(0).append( slide.opts.closeTpl ); 1802 } 1803 1804 this.afterLoad( slide ); 1805 1806 }, 1807 1808 // Display error message 1809 // ===================== 1810 1811 setError : function ( slide ) { 1812 1813 slide.hasError = true; 1814 1815 this.setContent( slide, slide.opts.errorTpl ); 1816 1817 }, 1818 1819 1820 showLoading : function( slide ) { 1821 var self = this; 1822 1823 slide = slide || self.current; 1824 1825 if ( slide && !slide.$spinner ) { 1826 slide.$spinner = $( self.opts.spinnerTpl ).appendTo( slide.$slide ); 1827 } 1828 1829 }, 1830 1831 hideLoading : function( slide ) { 1832 1833 var self = this; 1834 1835 slide = slide || self.current; 1836 1837 if ( slide && slide.$spinner ) { 1838 slide.$spinner.remove(); 1839 1840 delete slide.$spinner; 1841 } 1842 1843 }, 1844 1845 afterMove : function() { 1846 1847 var self = this; 1848 var current = self.current; 1849 var slides = {}; 1850 1851 if ( !current ) { 1852 return; 1853 } 1854 1855 current.$slide.siblings().trigger( 'onReset' ); 1856 1857 // Remove unnecessary slides 1858 $.each( self.slides, function( key, slide ) { 1859 1860 if ( slide.pos >= self.currPos - 1 && slide.pos <= self.currPos + 1 ) { 1861 slides[ slide.pos ] = slide; 1862 1863 } else if ( slide ) { 1864 slide.$slide.remove(); 1865 } 1866 1867 }); 1868 1869 self.slides = slides; 1870 1871 self.trigger( 'afterMove' ); 1872 1873 if ( current.isLoaded ) { 1874 self.complete(); 1875 } 1876 1877 }, 1878 1879 // Adjustments after slide has been loaded 1880 // ======================================= 1881 1882 afterLoad : function( slide ) { 1883 1884 var self = this; 1885 1886 if ( self.isClosing ) { 1887 return; 1888 } 1889 1890 slide.isLoading = false;
1891 slide.isLoaded = true; 1892 1893 self.trigger( 'afterLoad', slide ); 1894 1895 self.hideLoading( slide ); 1896 1897 // Resize content to fit inside slide 1898 // Skip if slide has an $ghost element, because then it has been already processed 1899 if ( !slide.$ghost ) { 1900 self.updateSlide( slide, true ); 1901 } 1902 1903 if ( slide.index === self.currIndex && slide.isMoved ) { 1904 self.complete(); 1905 1906 } else if ( !slide.$ghost ) { 1907 self.revealImage( slide ); 1908 } 1909 1910 }, 1911 1912 1913 // Final adjustments after current gallery item is moved to position 1914 // and it`s content is loaded 1915 // ================================================================== 1916 1917 complete : function() { 1918 1919 var self = this; 1920 1921 var current = self.current; 1922 1923 self.revealImage( current, function() { 1924 current.isComplete = true; 1925 1926 current.$slide.addClass('fancybox-slide--complete'); 1927 1928 self.updateCursor(); 1929 1930 self.trigger( 'onComplete' ); 1931 1932 // Try to focus on the first focusable element, skip for images and iframes 1933 if ( current.opts.focus && !( current.type === 'image' || current.type === 'iframe' ) ) { 1934 self.focus(); 1935 } 1936 1937 }); 1938 1939 }, 1940 1941 1942 // Preload next and previous slides 1943 // ================================ 1944 1945 preload : function() { 1946 var self = this; 1947 var next, prev; 1948 1949 if ( self.group.length < 2 ) { 1950 return; 1951 } 1952 1953 next = self.slides[ self.currPos + 1 ]; 1954 prev = self.slides[ self.currPos - 1 ]; 1955 1956 if ( next && next.type === 'image' ) { 1957 self.loadSlide( next ); 1958 } 1959 1960 if ( prev && prev.type === 'image' ) { 1961 self.loadSlide( prev ); 1962 } 1963 1964 }, 1965 1966 1967 // Try to find and focus on the first focusable element 1968 // ==================================================== 1969 1970 focus : function() { 1971 var current = this.current; 1972 var $el; 1973 1974 $el = current && current.isComplete ? current.$slide.find('button,:input,[tabindex],a:not(".disabled")').filter(':visible:first') : null; 1975 1976 if ( !$el || !$el.length ) { 1977 $el = this.$refs.container; 1978 } 1979 1980 $el.focus(); 1981 1982 // Scroll position of wrapper element sometimes changes after focusing (IE) 1983 this.$refs.slider_wrap.scrollLeft(0); 1984 1985 // And the same goes for slide element 1986 if ( current ) { 1987 current.$slide.scrollTop(0); 1988 } 1989 1990 }, 1991 1992 1993 // Activates current instance - brings container to the front and enables keyboard, 1994 // notifies other instances about deactivating 1995 // ================================================================================= 1996 1997 activate : function () { 1998 var self = this; 1999 2000 // Deactivate all instances 2001 2002 $( '.fancybox-container' ).each(function () { 2003 var instance = $(this).data( 'FancyBox' ); 2004 2005 // Skip self and closing instances 2006 2007 if (instance && instance.uid !== self.uid && !instance.isClosing) { 2008 instance.trigger( 'onDeactivate' ); 2009 } 2010 2011 }); 2012 2013 if ( self.current ) { 2014 2015 if ( self.$refs.container.index() > 0 ) { 2016 self.$refs.container.prependTo( document.body ); 2017 } 2018 2019 self.updateControls(); 2020 } 2021 2022 self.trigger( 'onActivate' ); 2023 2024 self.addEvents(); 2025 2026 }, 2027 2028 2029 // Start closing procedure 2030 // This will start "zoom-out" animation if needed and clean everything up afterwards 2031 // ================================================================================= 2032 2033 close : function( e ) { 2034 2035 var self = this; 2036 var current = self.current; 2037 var duration = current.opts.speed; 2038 2039 var done = $.proxy(function() { 2040 2041 self.cleanUp( e ); // Now "this" is again our instance 2042 2043 }, this); 2044 2045 if ( self.isAnimating || self.isClosing ) { 2046 return false;
2047 } 2048 2049 // If beforeClose callback prevents closing, make sure content is centered 2050 if ( self.trigger( 'beforeClose', e ) === false ) { 2051 $.fancybox.stop( self.$refs.slider ); 2052 2053 requestAFrame(function() { 2054 self.update( true, true, 150 ); 2055 }); 2056 2057 return; 2058 } 2059 2060 self.isClosing = true; 2061 2062 if ( current.timouts ) { 2063 clearTimeout( current.timouts ); 2064 } 2065 2066 if ( e !== true) { 2067 $.fancybox.stop( self.$refs.slider ); 2068 } 2069 2070 self.$refs.container 2071 .removeClass('fancybox-container--active') 2072 .addClass('fancybox-container--closing'); 2073 2074 current.$slide 2075 .removeClass('fancybox-slide--complete') 2076 .siblings() 2077 .remove(); 2078 2079 2080 if ( !current.isMoved ) { 2081 current.$slide.css('overflow', 'visible'); 2082 } 2083 2084 // Remove all events 2085 // If there are multiple instances, they will be set again by "activate" method 2086 2087 self.removeEvents(); 2088 2089 // Clean up 2090 2091 self.hideLoading( current ); 2092 2093 self.hideControls(); 2094 2095 self.updateCursor(); 2096 2097 self.$refs.bg.css('transition-duration', duration + 'ms'); 2098 2099 this.$refs.container.removeClass( 'fancybox-container--ready' ); 2100 2101 if ( e === true ) { 2102 setTimeout( done, duration ); 2103 2104 } else if ( !self.zoomInOut( 'Out', duration, done ) ) { 2105 $.fancybox.animate( self.$refs.container, null, { opacity : 0 }, duration, "easeInSine", done ); 2106 } 2107 2108 }, 2109 2110 2111 // Final adjustments after removing the instance 2112 // ============================================= 2113 2114 cleanUp : function( e ) { 2115 var self = this, 2116 instance; 2117 2118 self.$refs.slider.children().trigger( 'onReset' ); 2119 2120 self.$refs.container.empty().remove(); 2121 2122 self.trigger( 'afterClose', e ); 2123 2124 self.current = null; 2125 2126 // Check if there are other instances 2127 instance = $.fancybox.getInstance(); 2128 2129 if ( instance ) { 2130 instance.activate(); 2131 2132 } else { 2133 2134 $( 'html' ).removeClass( 'fancybox-enabled' ); 2135 $( 'body' ).removeAttr( 'style' ); 2136 2137 $W.scrollTop( self.scrollTop ).scrollLeft( self.scrollLeft ); 2138 2139 $( '#fancybox-noscroll' ).remove(); 2140 2141 } 2142 2143 // Place back focus 2144 if ( self.$lastFocus ) { 2145 self.$lastFocus.focus(); 2146 } 2147 2148 }, 2149 2150 2151 // Call callback and trigger an event 2152 // ================================== 2153 2154 trigger : function( name, slide ) { 2155 var args = Array.prototype.slice.call(arguments, 1), 2156 self = this, 2157 obj = slide && slide.opts ? slide : self.current, 2158 rez; 2159 2160 if ( obj ) { 2161 args.unshift( obj ); 2162 2163 } else { 2164 obj = self; 2165 } 2166 2167 args.unshift( self ); 2168 2169 if ( $.isFunction( obj.opts[ name ] ) ) { 2170 rez = obj.opts[ name ].apply( obj, args ); 2171 } 2172 2173 if ( rez === false ) { 2174 return rez; 2175 } 2176 2177 if ( name === 'afterClose' ) { 2178 $( document ).trigger( name + '.fb', args ); 2179 2180 } else { 2181 self.$refs.container.trigger( name + '.fb', args ); 2182 } 2183 2184 }, 2185 2186 2187 // Toggle toolbar and caption 2188 // ========================== 2189 2190 toggleControls : function( force ) { 2191 2192 if ( this.isHiddenControls ) { 2193 this.updateControls( force ); 2194 2195 } else { 2196 this.hideControls(); 2197 } 2198 2199 2200 }, 2201 2202 2203 // Hide toolbar and caption 2204 // ======================== 2205 2206 hideControls : function () { 2207
2208 this.isHiddenControls = true; 2209 2210 this.$refs.container.removeClass('fancybox-show-controls'); 2211 2212 this.$refs.container.removeClass('fancybox-show-caption'); 2213 2214 }, 2215 2216 2217 // Update infobar values, navigation button states and reveal caption 2218 // ================================================================== 2219 2220 updateControls : function ( force ) { 2221 2222 var self = this; 2223 2224 var $container = self.$refs.container; 2225 var $caption = self.$refs.caption; 2226 2227 // Toggle infobar and buttons 2228 2229 var current = self.current; 2230 var index = current.index; 2231 var opts = current.opts; 2232 var caption = opts.caption; 2233 2234 if ( this.isHiddenControls && force !== true ) { 2235 return; 2236 } 2237 2238 this.isHiddenControls = false; 2239 2240 $container 2241 .addClass('fancybox-show-controls') 2242 .toggleClass('fancybox-show-infobar', !!opts.infobar && self.group.length > 1) 2243 .toggleClass('fancybox-show-buttons', !!opts.buttons ) 2244 .toggleClass('fancybox-is-modal', !!opts.modal ); 2245 2246 $('.fancybox-button--left', $container).toggleClass( 'fancybox-button--disabled', (!opts.loop && index <= 0 ) ); 2247 $('.fancybox-button--right', $container).toggleClass( 'fancybox-button--disabled', (!opts.loop && index >= self.group.length - 1) ); 2248 2249 $('.fancybox-button--play', $container).toggle( !!( opts.slideShow && self.group.length > 1) ); 2250 $('.fancybox-button--close', $container).toggle( !!opts.closeBtn ); 2251 2252 // Update infobar values 2253 2254 $('.js-fancybox-count', $container).html( self.group.length ); 2255 $('.js-fancybox-index', $container).html( index + 1 ); 2256 2257 // Recalculate content dimensions 2258 current.$slide.trigger( 'refresh' ); 2259 2260 // Reveal or create new caption 2261 if ( $caption ) { 2262 $caption.empty(); 2263 } 2264 2265 if ( caption && caption.length ) { 2266 $caption.html( caption ); 2267 2268 this.$refs.container.addClass( 'fancybox-show-caption '); 2269 2270 self.$caption = $caption; 2271 2272 } else { 2273 this.$refs.container.removeClass( 'fancybox-show-caption' ); 2274 } 2275 2276 } 2277 2278 }); 2279 2280 2281 $.fancybox = { 2282 2283 version : "3.0.47", 2284 defaults : defaults, 2285 2286 2287 // Get current instance and execute a command. 2288 // 2289 // Examples of usage: 2290 // 2291 // $instance = $.fancybox.getInstance(); 2292 // $.fancybox.getInstance().jumpTo( 1 ); 2293 // $.fancybox.getInstance( 'jumpTo', 1 ); 2294 // $.fancybox.getInstance( function() { 2295 // console.info( this.currIndex ); 2296 // }); 2297 // ====================================================== 2298 2299 getInstance : function ( command ) { 2300 var instance = $('.fancybox-container:not(".fancybox-container--closing"):first').data( 'FancyBox' ); 2301 var args = Array.prototype.slice.call(arguments, 1); 2302 2303 if ( instance instanceof FancyBox ) { 2304 2305 if ( $.type( command ) === 'string' ) { 2306 instance[ command ].apply( instance, args ); 2307 2308 } else if ( $.type( command ) === 'function' ) { 2309 command.apply( instance, args ); 2310 2311 } 2312 2313 return instance; 2314 } 2315 2316 return false; 2317 2318 }, 2319 2320 2321 // Create new instance 2322 // =================== 2323 2324 open : function ( items, opts, index ) { 2325 return new FancyBox( items, opts, index ); 2326 }, 2327 2328 2329 // Close current or all instances 2330 // ============================== 2331 2332 close : function ( all ) { 2333 var instance = this.getInstance(); 2334 2335 if ( instance ) { 2336 instance.close(); 2337 2338 // Try to find and close next instance 2339 2340 if ( all === true ) { 2341 this.close(); 2342 } 2343 } 2344 2345 }, 2346 2347 2348 // Test for the existence of touch events in the browser 2349 // Limit to mobile devices 2350 // ==================================================== 2351 2352 isTouch : document.createTouch !== undefined && /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent), 2353 2354 2355 // Detect if 'translate3d' support is available 2356 // ============================================ 2357 2358 use3d : (function() { 2359 var div = document.createElement('div'); 2360 2361 return window.getComputedStyle( div ).getPropertyValue('transform') && !(document.documentMode && document.documentMode <= 11); 2362 }()), 2363 2364 2365 // Helper function to get current visual state of an element 2366 // returns array[ top, left, horizontal-scale, vertical-scale, opacity ] 2367 // ===================================================================== 2368 2369 getTranslate : function( $el ) { 2370 var position, matrix; 2371 2372 if ( !$el || !$el.length ) { 2373 return false;
2374 } 2375 2376 position = $el.get( 0 ).getBoundingClientRect(); 2377 matrix = $el.eq( 0 ).css('transform'); 2378 2379 if ( matrix && matrix.indexOf( 'matrix' ) !== -1 ) { 2380 matrix = matrix.split('(')[1]; 2381 matrix = matrix.split(')')[0]; 2382 matrix = matrix.split(','); 2383 } else { 2384 matrix = []; 2385 } 2386 2387 if ( matrix.length ) { 2388 2389 // If IE 2390 if ( matrix.length > 10 ) { 2391 matrix = [ matrix[13], matrix[12], matrix[0], matrix[5] ]; 2392 2393 } else { 2394 matrix = [ matrix[5], matrix[4], matrix[0], matrix[3]]; 2395 } 2396 2397 matrix = matrix.map(parseFloat); 2398 2399 } else { 2400 matrix = [ 0, 0, 1, 1 ]; 2401 } 2402 2403 return { 2404 top : matrix[ 0 ], 2405 left : matrix[ 1 ], 2406 scaleX : matrix[ 2 ], 2407 scaleY : matrix[ 3 ], 2408 opacity : parseFloat( $el.css('opacity') ), 2409 width : position.width, 2410 height : position.height 2411 }; 2412 2413 }, 2414 2415 2416 // Shortcut for setting "translate3d" properties for element 2417 // Can set be used to set opacity, too 2418 // ======================================================== 2419 2420 setTranslate : function( $el, props ) { 2421 var str = ''; 2422 var css = {}; 2423 2424 if ( !$el || !props ) { 2425 return; 2426 } 2427 2428 if ( props.left !== undefined || props.top !== undefined ) { 2429 2430 str = ( props.left === undefined ? $el.position().top : props.left ) + 'px, ' + ( props.top === undefined ? $el.position().top : props.top ) + 'px'; 2431 2432 if ( this.use3d ) { 2433 str = 'translate3d(' + str + ', 0px)'; 2434 2435 } else { 2436 str = 'translate(' + str + ')'; 2437 } 2438 2439 } 2440 2441 if ( props.scaleX !== undefined && props.scaleY !== undefined ) { 2442 str = (str.length ? str + ' ' : '') + 'scale(' + props.scaleX + ', ' + props.scaleY + ')'; 2443 } 2444 2445 if ( str.length ) { 2446 css.transform = str; 2447 } 2448 2449 if ( props.opacity !== undefined ) { 2450 css.opacity = props.opacity; 2451 } 2452 2453 if ( props.width !== undefined ) { 2454 css.width = props.width; 2455 } 2456 2457 if ( props.height !== undefined ) { 2458 css.height = props.height; 2459 } 2460 2461 return $el.css( css ); 2462 2463 }, 2464 2465 2466 // Common easings for entrances and exits 2467 // t: current time, b: begInnIng value, c: change In value, d: duration 2468 // ==================================================================== 2469 2470 easing : { 2471 easeOutCubic : function (t, b, c, d) { 2472 return c * ((t=t/d-1)*t*t + 1) + b; 2473 }, 2474 easeInCubic : function (t, b, c, d) { 2475 return c * (t/=d)*t*t + b; 2476 }, 2477 easeOutSine : function (t, b, c, d) { 2478 return c * Math.sin(t/d * (Math.PI/2)) + b; 2479 }, 2480 easeInSine : function (t, b, c, d) { 2481 return -c * Math.cos(t/d * (Math.PI/2)) + c + b; 2482 } 2483 }, 2484 2485 2486 // Stop fancyBox animation 2487 // ======================= 2488 2489 stop : function( $el ) { 2490 2491 $el.removeData( 'animateID' ); 2492 2493 }, 2494 2495 // Animate element using "translate3d" 2496 // Usage: 2497 // animate( element, start properties, end properties, duration, easing, callback ) 2498 // or 2499 // animate( element, start properties, end properties, duration, callback ) 2500 // ================================================================================= 2501 2502 animate : function( $el, from, to, duration, easing, done ) { 2503 2504 var self = this; 2505 2506 var lastTime = null; 2507 var animTime = 0; 2508 2509 var curr; 2510 var diff; 2511 var id; 2512 2513 var finish = function() { 2514 if ( to.scaleX !== undefined && to.scaleY !== undefined && from && from.width !== undefined && from.height !== undefined ) { 2515 to.width = from.width * to.scaleX; 2516 to.height = from.height * to.scaleY; 2517 2518 to.scaleX = 1; 2519 to.scaleY = 1; 2520 } 2521 2522 self.setTranslate( $el, to ); 2523 2524 done(); 2525 }; 2526 2527 var frame = function ( timestamp ) { 2528 curr = []; 2529 diff = 0; 2530 2531 // If "stop" method has been called on this element, then just stop 2532 if ( !$el.length || $el.data( 'animateID' ) !== id ) { 2533 return; 2534 } 2535 2536 timestamp = timestamp || Date.now(); 2537 2538 if ( lastTime ) { 2539 diff = timestamp - lastTime; 2540 } 2541 2542 lastTime = timestamp; 2543 animTime += diff; 2544 2545 // Are we done? 2546 if ( animTime >= duration ) { 2547 2548 finish(); 2549 2550 return; 2551 } 2552 2553 for ( var prop in to ) { 2554 2555 if ( to.hasOwnProperty( prop ) && from[ prop ] !== undefined ) { 2556 2557 if ( from[ prop ] == to[ prop ] ) { 2558 curr[ prop ] = to[ prop ]; 2559 2560 } else { 2561 curr[ prop ] = self.easing[ easing ]( animTime, from[ prop ], to[ prop ] - from[ prop ], duration ); 2562 } 2563 2564 } 2565 } 2566 2567 self.setTranslate( $el, curr ); 2568 2569 requestAFrame( frame ); 2570 }; 2571 2572 self.animateID = id = self.animateID === undefined ? 1 : self.animateID + 1; 2573 2574 $el.data( 'animateID', id ); 2575 2576 if ( done === undefined && $.type(easing) == 'function' ) { 2577 done = easing; 2578 easing = undefined; 2579 } 2580 2581 if ( !easing ) { 2582 easing = "easeOutCubic"; 2583 } 2584 2585 done = done || $.noop; 2586 2587 if ( from ) { 2588 this.setTranslate( $el, from ); 2589 2590 } else { 2591 2592 // We need current values to calculate change in time 2593 from = this.getTranslate( $el ); 2594 } 2595 2596 if ( duration ) { 2597 $el.show(); 2598 2599 requestAFrame( frame ); 2600 2601 } else { 2602 finish(); 2603 } 2604 2605 } 2606 2607 }; 2608 2609 2610 // Event handler for click event to "fancyboxed" links 2611 // =================================================== 2612 2613 function _run( e ) { 2614 var target = e.currentTarget, 2615 opts = e.data ? e.data.options : {}, 2616 items = e.data ? e.data.items : [], 2617 value = '', 2618 index = 0; 2619 2620 e.preventDefault(); 2621 e.stopPropagation(); 2622 2623 // Get all related items and find index for clicked one 2624 2625 /* 2626 if ( $(target).attr( 'data-fancybox' ) ) { 2627 value = $(target).data( 'fancybox' ); 2628 } 2629 2630 if ( value ) { 2631 items = items.length ? items.filter( '[data-fancybox="' + value + '"]' ) : $( '[data-fancybox=' + value + ']' ); 2632 index = items.index( target ); 2633 2634 } else { 2635 items = [ target ]; 2636 } 2637 */ 2638 /* arturo pataisyta */ 2639 items = items.length ? items.filter( '[data-fancybox]' ) : $( '[data-fancybox]' ); 2640 index = items.index( target ); 2641 2642 $.fancybox.open( items, opts, index ); 2643 } 2644 2645 2646 // Create a jQuery plugin 2647 // ====================== 2648 2649 $.fn.fancybox = function (options) { 2650 2651 this.off('click.fb-start').on('click.fb-start', { 2652 items : this, 2653 options : options || {} 2654 }, _run); 2655 2656 return this; 2657 2658 }; 2659 2660 2661 // Self initializing plugin 2662 // ======================== 2663 2664 $(document).on('click.fb-start', '[data-fancybox]', _run); 2665 2666}(window, document, window.jQuery)); 2667 2668// ========================================================================== 2669// 2670// Media 2671// Adds additional media type support 2672// 2673// ========================================================================== 2674;(function ($) { 2675 2676 'use strict'; 2677 2678 // Formats matching url to final form 2679 2680 var format = function (url, rez, params) { 2681 if ( !url ) { 2682 return; 2683 } 2684 2685 params = params || ''; 2686 2687 if ( $.type(params) === "object" ) { 2688 params = $.param(params, true); 2689 } 2690 2691 $.each(rez, function (key, value) { 2692 url = url.replace('$' + key, value || ''); 2693 }); 2694 2695 if (params.length) { 2696 url += (url.indexOf('?') > 0 ? '&' : '?') + params; 2697 } 2698 2699 return url; 2700 }; 2701 2702 // Object containing properties for each media type 2703 2704 var media = { 2705 youtube: { 2706 matcher: /(youtube\.com|youtu\.be|youtube\-nocookie\.com)\/(watch\?(.*&)?v=|v\/|u\/|embed\/?)?(videoseries\?list=(.*)|[\w-]{11}|\?listType=(.*)&list=(.*))(.*)/i, 2707 params: { 2708 autoplay: 1, 2709 autohide: 1, 2710 fs: 1, 2711 rel: 0, 2712 hd: 1, 2713 wmode: 'transparent', 2714 enablejsapi: 1, 2715 html5: 1 2716 }, 2717 paramPlace : 8, 2718 type: 'iframe', 2719 url: '//www.youtube.com/embed/$4', 2720 thumb: '//img.youtube.com/vi/$4/hqdefault.jpg' 2721 }, 2722 2723 vimeo: { 2724 matcher: /^.+vimeo.com\/(.*\/)?([\d]+)(.*)?/, 2725 params: { 2726 autoplay: 1, 2727 hd: 1, 2728 show_title: 1, 2729 show_byline: 1, 2730 show_portrait: 0, 2731 fullscreen: 1, 2732 api: 1 2733 }, 2734 paramPlace : 3, 2735 type: 'iframe', 2736 url: '//player.vimeo.com/video/$2' 2737 }, 2738 2739 metacafe: { 2740 matcher: /metacafe.com\/watch\/(\d+)\/(.*)?/, 2741 type: 'iframe', 2742 url: '//www.metacafe.com/embed/$1/?ap=1' 2743 }, 2744 2745 dailymotion: { 2746 matcher: /dailymotion.com\/video\/(.*)\/?(.*)/, 2747 params: { 2748 additionalInfos: 0, 2749 autoStart: 1 2750 }, 2751 type: 'iframe', 2752 url: '//www.dailymotion.com/embed/video/$1' 2753 }, 2754 2755 vine: { 2756 matcher: /vine.co\/v\/([a-zA-Z0-9\?\=\-]+)/, 2757 type: 'iframe', 2758 url: '//vine.co/v/$1/embed/simple' 2759 }, 2760 2761 instagram: { 2762 matcher: /(instagr\.am|instagram\.com)\/p\/([a-zA-Z0-9_\-]+)\/?/i, 2763 type: 'image', 2764 url: '//$1/p/$2/media/?size=l' 2765 }, 2766 2767 // Examples: 2768 // http://maps.google.com/?ll=48.857995,2.294297&spn=0.007666,0.021136&t=m&z=16 2769 // http://maps.google.com/?ll=48.857995,2.294297&spn=0.007666,0.021136&t=m&z=16 2770 // https://www.google.lv/maps/place/Googleplex/@37.4220041,-122.0833494,17z/data=!4m5!3m4!1s0x0:0x6c296c66619367e0!8m2!3d37.4219998!4d-122.0840572 2771 google_maps: { 2772 matcher: /(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(((maps\/(place\/(.*)\/)?\@(.*),(\d+.?\d+?)z))|(\?ll=))(.*)?/i, 2773 type: 'iframe', 2774 url: function (rez) { 2775 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' ); 2776 } 2777 } 2778 }; 2779 2780 $(document).on('onInit.fb', function (e, instance) { 2781 2782 $.each(instance.group, function( i, item ) { 2783 2784 var url = item.src || '', 2785 type = false, 2786 thumb, 2787 rez, 2788 params,
2789 urlParams, 2790 o, 2791 provider; 2792 2793 // Skip items that already have content type 2794 if ( item.type ) { 2795 return; 2796 } 2797 2798 // Look for any matching media type 2799 2800 $.each(media, function ( n, el ) { 2801 rez = url.match(el.matcher); 2802 o = {}; 2803 provider = n; 2804 2805 if (!rez) { 2806 return; 2807 } 2808 2809 type = el.type; 2810 2811 if ( el.paramPlace && rez[ el.paramPlace ] ) { 2812 urlParams = rez[ el.paramPlace ]; 2813 2814 if ( urlParams[ 0 ] == '?' ) { 2815 urlParams = urlParams.substring(1); 2816 } 2817 2818 urlParams = urlParams.split('&'); 2819 2820 for ( var m = 0; m < urlParams.length; ++m ) { 2821 var p = urlParams[ m ].split('=', 2); 2822 2823 if ( p.length == 2 ) { 2824 o[ p[0] ] = decodeURIComponent( p[1].replace(/\+/g, " ") ); 2825 } 2826 } 2827 } 2828 2829 params = $.extend( true, {}, el.params, item.opts[ n ], o ); 2830 2831 url = $.type(el.url) === "function" ? el.url.call(this, rez, params, item) : format(el.url, rez, params); 2832 thumb = $.type(el.thumb) === "function" ? el.thumb.call(this, rez, params, item) : format(el.thumb, rez); 2833 2834 if ( provider === 'vimeo' ) { 2835 url = url.replace('&%23', '#'); 2836 } 2837 2838 return false; 2839 }); 2840 2841 // If it is found, then change content type and update the url 2842 2843 if ( type ) { 2844 item.src = url; 2845 item.type = type; 2846 2847 if ( !item.opts.thumb && !(item.opts.$thumb && item.opts.$thumb.length ) ) { 2848 item.opts.thumb = thumb; 2849 } 2850 2851 if ( type === 'iframe' ) { 2852 $.extend(true, item.opts, { 2853 iframe : { 2854 preload : false, 2855 scrolling : "no" 2856 }, 2857 smallBtn : false, 2858 closeBtn : true, 2859 fullScreen : false, 2860 slideShow : false 2861 }); 2862 2863 item.opts.slideClass += ' fancybox-slide--video'; 2864 } 2865 2866 } else { 2867 2868 // If no content type is found, then set it to `iframe` as fallback 2869 item.type = 'iframe'; 2870 2871 } 2872 2873 }); 2874 2875 }); 2876 2877}(window.jQuery)); 2878 2879// ========================================================================== 2880// 2881// Guestures 2882// Adds touch guestures, handles click and tap events 2883// 2884// ========================================================================== 2885;(function (window, document, $) { 2886 'use strict'; 2887 2888 var requestAFrame = (function() { 2889 return window.requestAnimationFrame || 2890 window.webkitRequestAnimationFrame || 2891 window.mozRequestAnimationFrame || 2892 function( callback ) { 2893 window.setTimeout(callback, 1000 / 60); }; 2894 })(); 2895 2896 2897 var pointers = function( e ) { 2898 var result = []; 2899 2900 e = e.originalEvent || e || window.e; 2901 e = e.touches && e.touches.length ? e.touches : ( e.changedTouches && e.changedTouches.length ? e.changedTouches : [ e ] ); 2902 2903 for ( var key in e ) { 2904 2905 if ( e[ key ].pageX ) { 2906 result.push( { x : e[ key ].pageX, y : e[ key ].pageY } ); 2907 2908 } else if ( e[ key ].clientX ) { 2909 result.push( { x : e[ key ].clientX, y : e[ key ].clientY } ); 2910 } 2911 } 2912 2913 return result; 2914 }; 2915 2916 var distance = function( point2, point1, what ) { 2917 2918 if ( !point1 || !point2 ) { 2919 return 0; 2920 } 2921 2922 if ( what === 'x' ) { 2923 return point2.x - point1.x; 2924 2925 } else if ( what === 'y' ) { 2926 return point2.y - point1.y; 2927 } 2928 2929 return Math.sqrt( Math.pow( point2.x - point1.x, 2 ) + Math.pow( point2.y - point1.y, 2 ) ); 2930 2931 }; 2932 2933 var isClickable = function( $el ) { 2934 2935 return $el.is('a') || $el.is('button') || $el.is('input') || $el.is('select') || $el.is('textarea') || $.isFunction( $el.get(0).onclick ); 2936 2937 }; 2938 2939 var hasScrollbars = function( el ) { 2940 var overflowY = window.getComputedStyle( el )['overflow-y']; 2941 var overflowX = window.getComputedStyle( el )['overflow-x']; 2942 2943 var vertical = (overflowY === 'scroll' || overflowY === 'auto') && el.scrollHeight > el.clientHeight; 2944 var horizontal = (overflowX === 'scroll' || overflowX === 'auto') && el.scrollWidth > el.clientWidth; 2945 2946 return vertical || horizontal; 2947 }; 2948 2949 var isScrollable = function ( $el ) { 2950 2951 var rez = false; 2952 2953 while ( true ) { 2954 rez = hasScrollbars( $el.get(0) ); 2955 2956 if ( rez ) { 2957 break; 2958 } 2959 2960 $el = $el.parent(); 2961 2962 if ( !$el.length || $el.hasClass('fancybox-slider') || $el.is('body') ) { 2963 break; 2964 } 2965 2966 } 2967 2968 return rez; 2969 2970 }; 2971 2972 2973 var Guestures = function ( instance ) { 2974 2975 var self = this; 2976 2977 self.instance = instance; 2978 2979 self.$wrap = instance.$refs.slider_wrap; 2980 self.$slider = instance.$refs.slider; 2981 self.$container = instance.$refs.container; 2982 2983 self.destroy(); 2984 2985 self.$wrap.on('touchstart.fb mousedown.fb', $.proxy(self, "ontouchstart")); 2986 2987 }; 2988 2989 Guestures.prototype.destroy = function() { 2990 2991 this.$wrap.off('touchstart.fb mousedown.fb touchmove.fb mousemove.fb touchend.fb touchcancel.fb mouseup.fb mouseleave.fb'); 2992 2993 }; 2994 2995 Guestures.prototype.ontouchstart = function( e ) { 2996 2997 var self = this; 2998 2999 var $target = $( e.target ); 3000 var instance = self.instance; 3001 var current = instance.current; 3002 var $content = current.$content || current.$placeholder; 3003 3004 self.startPoints = pointers( e ); 3005 3006 self.$target = $target; 3007 self.$content = $content; 3008 3009 self.canvasWidth = Math.round( current.$slide[0].clientWidth ); 3010 self.canvasHeight = Math.round( current.$slide[0].clientHeight ); 3011 3012 self.startEvent = e; 3013 3014 // Skip if clicked on the scrollbar 3015 if ( e.originalEvent.clientX > self.canvasWidth + current.$slide.offset().left ) { 3016 return true; 3017 } 3018 3019 // Ignore taping on links, buttons and scrollable items 3020 if ( isClickable( $target ) || isClickable( $target.parent() ) || ( isScrollable( $target ) ) ) { 3021 return; 3022 } 3023 3024 // If "touch" is disabled, then handle click event 3025 if ( !current.opts.touch ) { 3026 self.endPoints = self.startPoints; 3027 3028 return self.ontap(); 3029 } 3030 3031 // Ignore right click 3032 if ( e.originalEvent && e.originalEvent.button == 2 ) { 3033 return; 3034 } 3035 3036 e.stopPropagation(); 3037 e.preventDefault(); 3038 3039 if ( !current || self.instance.isAnimating || self.instance.isClosing ) { 3040 return; 3041 } 3042 3043 // Prevent zooming if already swiping 3044 if ( !self.startPoints || ( self.startPoints.length > 1 && !current.isMoved ) ) { 3045 return; 3046 } 3047 3048 self.$wrap.off('touchmove.fb mousemove.fb', $.proxy(self, "ontouchmove")); 3049 self.$wrap.off('touchend.fb touchcancel.fb mouseup.fb mouseleave.fb', $.proxy(self, "ontouchend")); 3050 3051 self.$wrap.on('touchend.fb touchcancel.fb mouseup.fb mouseleave.fb', $.proxy(self, "ontouchend")); 3052 self.$wrap.on('touchmove.fb mousemove.fb', $.proxy(self, "ontouchmove")); 3053 3054 self.startTime = new Date().getTime(); 3055 self.distanceX = self.distanceY = self.distance = 0; 3056 3057 self.canTap = false;
3058 self.isPanning = false; 3059 self.isSwiping = false; 3060 self.isZooming = false; 3061 3062 self.sliderStartPos = $.fancybox.getTranslate( self.$slider ); 3063 3064 self.contentStartPos = $.fancybox.getTranslate( self.$content ); 3065 self.contentLastPos = null; 3066 3067 if ( self.startPoints.length === 1 && !self.isZooming ) { 3068 self.canTap = current.isMoved; 3069 3070 if ( current.type === 'image' && ( self.contentStartPos.width > self.canvasWidth + 1 || self.contentStartPos.height > self.canvasHeight + 1 ) ) { 3071 3072 $.fancybox.stop( self.$content ); 3073 3074 self.isPanning = true; 3075 3076 } else { 3077 3078 $.fancybox.stop( self.$slider ); 3079 3080 self.isSwiping = true; 3081 } 3082 3083 self.$container.addClass('fancybox-controls--isGrabbing'); 3084 3085 } 3086 3087 if ( self.startPoints.length === 2 && current.isMoved && !current.hasError && current.type === 'image' && ( current.isLoaded || current.$ghost ) ) { 3088 3089 self.isZooming = true; 3090 3091 self.isSwiping = false; 3092 self.isPanning = false; 3093 3094 $.fancybox.stop( self.$content ); 3095 3096 self.centerPointStartX = ( ( self.startPoints[0].x + self.startPoints[1].x ) * 0.5 ) - $(window).scrollLeft(); 3097 self.centerPointStartY = ( ( self.startPoints[0].y + self.startPoints[1].y ) * 0.5 ) - $(window).scrollTop(); 3098 3099 self.percentageOfImageAtPinchPointX = ( self.centerPointStartX - self.contentStartPos.left ) / self.contentStartPos.width; 3100 self.percentageOfImageAtPinchPointY = ( self.centerPointStartY - self.contentStartPos.top ) / self.contentStartPos.height; 3101 3102 self.startDistanceBetweenFingers = distance( self.startPoints[0], self.startPoints[1] ); 3103 } 3104 3105 }; 3106 3107 Guestures.prototype.ontouchmove = function( e ) { 3108 3109 var self = this; 3110 3111 e.preventDefault(); 3112 3113 self.newPoints = pointers( e ); 3114 3115 if ( !self.newPoints || !self.newPoints.length ) { 3116 return; 3117 } 3118 3119 self.distanceX = distance( self.newPoints[0], self.startPoints[0], 'x' ); 3120 self.distanceY = distance( self.newPoints[0], self.startPoints[0], 'y' ); 3121 3122 self.distance = distance( self.newPoints[0], self.startPoints[0] ); 3123 3124 // Skip false ontouchmove events (Chrome) 3125 if ( self.distance > 0 ) { 3126 3127 if ( self.isSwiping ) { 3128 self.onSwipe(); 3129 3130 } else if ( self.isPanning ) { 3131 self.onPan(); 3132 3133 } else if ( self.isZooming ) { 3134 self.onZoom(); 3135 } 3136 3137 } 3138 3139 }; 3140 3141 Guestures.prototype.onSwipe = function() { 3142 3143 var self = this; 3144 3145 var swiping = self.isSwiping; 3146 var left = self.sliderStartPos.left; 3147 var angle; 3148 3149 if ( swiping === true ) { 3150 3151 if ( Math.abs( self.distance ) > 10 ) { 3152 3153 if ( self.instance.group.length < 2 ) { 3154 self.isSwiping = 'y'; 3155 3156 } else if ( !self.instance.current.isMoved || self.instance.opts.touch.vertical === false || ( self.instance.opts.touch.vertical === 'auto' && $( window ).width() > 800 ) ) { 3157 self.isSwiping = 'x'; 3158 3159 } else { 3160 angle = Math.abs( Math.atan2( self.distanceY, self.distanceX ) * 180 / Math.PI ); 3161 3162 self.isSwiping = ( angle > 45 && angle < 135 ) ? 'y' : 'x'; 3163 } 3164 3165 self.canTap = false; 3166 3167 self.instance.current.isMoved = false; 3168 3169 // Reset points to avoid jumping, because we dropped first swipes to calculate the angle 3170 self.startPoints = self.newPoints; 3171 } 3172 3173 } else { 3174 3175 if ( swiping == 'x' ) { 3176 3177 // Sticky edges 3178 if ( !self.instance.current.opts.loop && self.instance.current.index === 0 && self.distanceX > 0 ) { 3179 left = left + Math.pow( self.distanceX, 0.8 ); 3180 3181 } else if ( !self.instance.current.opts.loop &&self.instance.current.index === self.instance.group.length - 1 && self.distanceX < 0 ) { 3182 left = left - Math.pow( -self.distanceX, 0.8 ); 3183 3184 } else { 3185 left = left + self.distanceX; 3186 } 3187 3188 } 3189 3190 self.sliderLastPos = { 3191 top : swiping == 'x' ? 0 : self.sliderStartPos.top + self.distanceY, 3192 left : left 3193 }; 3194 3195 requestAFrame(function() { 3196 $.fancybox.setTranslate( self.$slider, self.sliderLastPos ); 3197 }); 3198 } 3199 3200 }; 3201 3202 Guestures.prototype.onPan = function() { 3203 3204 var self = this; 3205 3206 var newOffsetX, newOffsetY, newPos; 3207 3208 self.canTap = false; 3209 3210 if ( self.contentStartPos.width > self.canvasWidth ) { 3211 newOffsetX = self.contentStartPos.left + self.distanceX; 3212 3213 } else { 3214 newOffsetX = self.contentStartPos.left; 3215 } 3216 3217 newOffsetY = self.contentStartPos.top + self.distanceY; 3218 3219 newPos = self.limitMovement( newOffsetX, newOffsetY, self.contentStartPos.width, self.contentStartPos.height ); 3220 3221 newPos.scaleX = self.contentStartPos.scaleX; 3222 newPos.scaleY = self.contentStartPos.scaleY; 3223 3224 self.contentLastPos = newPos; 3225 3226 requestAFrame(function() { 3227 $.fancybox.setTranslate( self.$content, self.contentLastPos ); 3228 }); 3229 }; 3230 3231 // Make panning sticky to the edges 3232 Guestures.prototype.limitMovement = function( newOffsetX, newOffsetY, newWidth, newHeight ) { 3233 3234 var self = this; 3235 3236 var minTranslateX, minTranslateY, maxTranslateX, maxTranslateY; 3237 3238 var canvasWidth = self.canvasWidth; 3239 var canvasHeight = self.canvasHeight; 3240 3241 var currentOffsetX = self.contentStartPos.left; 3242 var currentOffsetY = self.contentStartPos.top; 3243 3244 var distanceX = self.distanceX; 3245 var distanceY = self.distanceY; 3246 3247 // Slow down proportionally to traveled distance 3248 3249 minTranslateX = Math.max(0, canvasWidth * 0.5 - newWidth * 0.5 ); 3250 minTranslateY = Math.max(0, canvasHeight * 0.5 - newHeight * 0.5 ); 3251 3252 maxTranslateX = Math.min( canvasWidth - newWidth, canvasWidth * 0.5 - newWidth * 0.5 ); 3253 maxTranslateY = Math.min( canvasHeight - newHeight, canvasHeight * 0.5 - newHeight * 0.5 ); 3254 3255 if ( newWidth > canvasWidth ) { 3256 3257 // -> 3258 if ( distanceX > 0 && newOffsetX > minTranslateX ) { 3259 newOffsetX = minTranslateX - 1 + Math.pow( -minTranslateX + currentOffsetX + distanceX, 0.8 ) || 0; 3260 } 3261 3262 // <- 3263 if ( distanceX < 0 && newOffsetX < maxTranslateX ) { 3264 newOffsetX = maxTranslateX + 1 - Math.pow( maxTranslateX - currentOffsetX - distanceX, 0.8 ) || 0; 3265 } 3266 3267 } 3268 3269 if ( newHeight > canvasHeight ) { 3270 3271 // \/ 3272 if ( distanceY > 0 && newOffsetY > minTranslateY ) { 3273 newOffsetY = minTranslateY - 1 + Math.pow(-minTranslateY + currentOffsetY + distanceY, 0.8 ) || 0; 3274 } 3275 3276 // /\ 3277 if ( distanceY < 0 && newOffsetY < maxTranslateY ) { 3278 newOffsetY = maxTranslateY + 1 - Math.pow ( maxTranslateY - currentOffsetY - distanceY, 0.8 ) || 0; 3279 } 3280 3281 } 3282 3283 return { 3284 top : newOffsetY, 3285 left : newOffsetX 3286 }; 3287 3288 }; 3289 3290 3291 Guestures.prototype.limitPosition = function( newOffsetX, newOffsetY, newWidth, newHeight ) { 3292 3293 var self = this; 3294 3295 var canvasWidth = self.canvasWidth; 3296 var canvasHeight = self.canvasHeight; 3297 3298 if ( newWidth > canvasWidth ) { 3299 newOffsetX = newOffsetX > 0 ? 0 : newOffsetX; 3300 newOffsetX = newOffsetX < canvasWidth - newWidth ? canvasWidth - newWidth : newOffsetX; 3301 3302 } else { 3303 3304 // Center horizontally 3305 newOffsetX = Math.max( 0, canvasWidth / 2 - newWidth / 2 ); 3306 3307 } 3308 3309 if ( newHeight > canvasHeight ) { 3310 newOffsetY = newOffsetY > 0 ? 0 : newOffsetY; 3311 newOffsetY = newOffsetY < canvasHeight - newHeight ? canvasHeight - newHeight : newOffsetY; 3312 3313 } else { 3314 3315 // Center vertically 3316 newOffsetY = Math.max( 0, canvasHeight / 2 - newHeight / 2 ); 3317 3318 } 3319 3320 return { 3321 top : newOffsetY, 3322 left : newOffsetX 3323 }; 3324 3325 }; 3326 3327 Guestures.prototype.onZoom = function() { 3328 3329 var self = this; 3330 3331 // Calculate current distance between points to get pinch ratio and new width and height 3332 3333 var currentWidth = self.contentStartPos.width; 3334 var currentHeight = self.contentStartPos.height; 3335 3336 var currentOffsetX = self.contentStartPos.left; 3337 var currentOffsetY = self.contentStartPos.top; 3338 3339 var endDistanceBetweenFingers = distance( self.newPoints[0], self.newPoints[1] ); 3340 3341 var pinchRatio = endDistanceBetweenFingers / self.startDistanceBetweenFingers; 3342 3343 var newWidth = Math.floor( currentWidth * pinchRatio ); 3344 var newHeight = Math.floor( currentHeight * pinchRatio ); 3345 3346 // This is the translation due to pinch-zooming 3347 var translateFromZoomingX = (currentWidth - newWidth) * self.percentageOfImageAtPinchPointX; 3348 var translateFromZoomingY = (currentHeight - newHeight) * self.percentageOfImageAtPinchPointY; 3349 3350 //Point between the two touches 3351 3352 var centerPointEndX = ((self.newPoints[0].x + self.newPoints[1].x) / 2) - $(window).scrollLeft(); 3353 var centerPointEndY = ((self.newPoints[0].y + self.newPoints[1].y) / 2) - $(window).scrollTop(); 3354 3355 // And this is the translation due to translation of the centerpoint 3356 // between the two fingers 3357 3358 var translateFromTranslatingX = centerPointEndX - self.centerPointStartX; 3359 var translateFromTranslatingY = centerPointEndY - self.centerPointStartY; 3360 3361 // The new offset is the old/current one plus the total translation 3362 3363 var newOffsetX = currentOffsetX + ( translateFromZoomingX + translateFromTranslatingX ); 3364 var newOffsetY = currentOffsetY + ( translateFromZoomingY + translateFromTranslatingY ); 3365 3366 var newPos = { 3367 top : newOffsetY, 3368 left : newOffsetX, 3369 scaleX : self.contentStartPos.scaleX * pinchRatio, 3370 scaleY : self.contentStartPos.scaleY * pinchRatio 3371 }; 3372 3373 self.canTap = false;
3374 3375 self.newWidth = newWidth; 3376 self.newHeight = newHeight; 3377 3378 self.contentLastPos = newPos; 3379 3380 requestAFrame(function() { 3381 $.fancybox.setTranslate( self.$content, self.contentLastPos ); 3382 }); 3383 3384 }; 3385 3386 Guestures.prototype.ontouchend = function( e ) { 3387 3388 var self = this; 3389 3390 var current = self.instance.current; 3391 3392 var dMs = Math.max( (new Date().getTime() ) - self.startTime, 1); 3393 3394 var swiping = self.isSwiping; 3395 var panning = self.isPanning; 3396 var zooming = self.isZooming; 3397 3398 self.endPoints = pointers( e ); 3399 3400 self.$container.removeClass('fancybox-controls--isGrabbing'); 3401 3402 self.$wrap.off('touchmove.fb mousemove.fb', $.proxy(this, "ontouchmove")); 3403 self.$wrap.off('touchend.fb touchcancel.fb mouseup.fb mouseleave.fb', $.proxy(this, "ontouchend")); 3404 3405 self.isSwiping = false; 3406 self.isPanning = false; 3407 self.isZooming = false; 3408 3409 if ( self.canTap ) { 3410 return self.ontap(); 3411 } 3412 3413 // Speed in px/ms 3414 self.velocityX = self.distanceX / dMs * 0.5; 3415 self.velocityY = self.distanceY / dMs * 0.5; 3416 3417 self.speed = current.opts.speed || 330; 3418 3419 self.speedX = Math.max( self.speed * 0.75, Math.min( self.speed * 1.5, ( 1 / Math.abs( self.velocityX ) ) * self.speed ) ); 3420 self.speedY = Math.max( self.speed * 0.75, Math.min( self.speed * 1.5, ( 1 / Math.abs( self.velocityY ) ) * self.speed ) ); 3421 3422 if ( panning ) { 3423 self.endPanning(); 3424 3425 } else if ( zooming ) { 3426 self.endZooming(); 3427 3428 } else { 3429 self.endSwiping( swiping ); 3430 } 3431 3432 return; 3433 }; 3434 3435 Guestures.prototype.endSwiping = function( swiping ) { 3436 3437 var self = this; 3438 3439 // Close if swiped vertically / navigate if horizontally 3440 3441 if ( swiping == 'y' && Math.abs( self.distanceY ) > 50 ) { 3442 3443 // Continue vertical movement 3444 3445 $.fancybox.animate( self.$slider, null, { 3446 top : self.sliderStartPos.top + self.distanceY + self.velocityY * 150, 3447 left : self.sliderStartPos.left, 3448 opacity : 0 3449 }, self.speedY ); 3450 3451 self.instance.close( true ); 3452 3453 } else if ( swiping == 'x' && self.distanceX > 50 ) { 3454 self.instance.previous( self.speedX ); 3455 3456 } else if ( swiping == 'x' && self.distanceX < -50 ) { 3457 self.instance.next( self.speedX ); 3458 3459 } else { 3460 3461 // Move back to center 3462 self.instance.update( false, true, 150 ); 3463 3464 } 3465 3466 }; 3467 3468 Guestures.prototype.endPanning = function() { 3469 3470 var self = this; 3471 var newOffsetX, newOffsetY, newPos; 3472 3473 if ( !self.contentLastPos ) { 3474 return; 3475 } 3476 3477 newOffsetX = self.contentLastPos.left + ( self.velocityX * self.speed * 2 ); 3478 newOffsetY = self.contentLastPos.top + ( self.velocityY * self.speed * 2 ); 3479 3480 newPos = self.limitPosition( newOffsetX, newOffsetY, self.contentStartPos.width, self.contentStartPos.height ); 3481 3482 newPos.width = self.contentStartPos.width; 3483 newPos.height = self.contentStartPos.height; 3484 3485 $.fancybox.animate( self.$content, null, newPos, self.speed, "easeOutSine" ); 3486 3487 }; 3488 3489 3490 Guestures.prototype.endZooming = function() { 3491 3492 var self = this; 3493 3494 var current = self.instance.current; 3495 3496 var newOffsetX, newOffsetY, newPos, reset; 3497 3498 var newWidth = self.newWidth; 3499 var newHeight = self.newHeight; 3500 3501 if ( !self.contentLastPos ) { 3502 return; 3503 } 3504 3505 newOffsetX = self.contentLastPos.left; 3506 newOffsetY = self.contentLastPos.top; 3507 3508 reset = { 3509 top : newOffsetY, 3510 left : newOffsetX, 3511 width : newWidth, 3512 height : newHeight, 3513 scaleX : 1, 3514 scaleY : 1 3515 }; 3516 3517 // Reset scalex/scaleY values; this helps for perfomance and does not break animation 3518 $.fancybox.setTranslate( self.$content, reset ); 3519 3520 if ( newWidth < self.canvasWidth && newHeight < self.canvasHeight ) { 3521 self.instance.scaleToFit( 150 ); 3522 3523 } else if ( newWidth > current.width || newHeight > current.height ) { 3524 self.instance.scaleToActual( self.centerPointStartX, self.centerPointStartY, 150 ); 3525 3526 } else { 3527 3528 newPos = self.limitPosition( newOffsetX, newOffsetY, newWidth, newHeight ); 3529 3530 $.fancybox.animate( self.$content, null, newPos, self.speed, "easeOutSine" ); 3531 3532 } 3533 3534 }; 3535 3536 Guestures.prototype.ontap = function() { 3537 3538 var self = this; 3539 3540 var instance = self.instance; 3541 var current = instance.current; 3542 3543 var x = self.endPoints[0].x; 3544 var y = self.endPoints[0].y; 3545 3546 x = x - self.$wrap.offset().left; 3547 y = y - self.$wrap.offset().top; 3548 3549 // Stop slideshow 3550 if ( instance.SlideShow && instance.SlideShow.isActive ) { 3551 instance.SlideShow.stop(); 3552 } 3553 3554 if ( !$.fancybox.isTouch ) { 3555 3556 if ( current.opts.closeClickOutside && self.$target.is('.fancybox-slide') ) { 3557 instance.close( self.startEvent ); 3558 3559 return; 3560 } 3561 3562 if ( current.type == 'image' && current.isMoved ) { 3563 3564 if ( instance.canPan() ) { 3565 instance.scaleToFit(); 3566 3567 } else if ( instance.isScaledDown() ) { 3568 instance.scaleToActual( x, y ); 3569 3570 } else if ( instance.group.length < 2 ) { 3571 instance.close( self.startEvent ); 3572 } 3573 3574 } 3575 3576 return; 3577 } 3578 3579 3580 // Double tap 3581 if ( self.tapped ) { 3582 3583 clearTimeout( self.tapped ); 3584 3585 self.tapped = null; 3586 3587 if ( Math.abs( x - self.x ) > 50 || Math.abs( y - self.y ) > 50 || !current.isMoved ) { 3588 return this; 3589 } 3590 3591 if ( current.type == 'image' && ( current.isLoaded || current.$ghost ) ) { 3592 3593 if ( instance.canPan() ) { 3594 instance.scaleToFit(); 3595 3596 } else if ( instance.isScaledDown() ) { 3597 instance.scaleToActual( x, y ); 3598 3599 } 3600 3601 } 3602 3603 } else { 3604 3605 // Single tap 3606 3607 self.x = x; 3608 self.y = y; 3609 3610 self.tapped = setTimeout(function() { 3611 self.tapped = null; 3612 3613 instance.toggleControls( true ); 3614 3615 }, 300); 3616 } 3617 3618 return this; 3619 }; 3620 3621 $(document).on('onActivate.fb', function (e, instance) { 3622 3623 if ( instance && !instance.Guestures ) { 3624 instance.Guestures = new Guestures( instance ); 3625 } 3626 3627 }); 3628 3629 $(document).on('beforeClose.fb', function (e, instance) { 3630 3631 if ( instance && instance.Guestures ) { 3632 instance.Guestures.destroy(); 3633 } 3634 3635 }); 3636 3637 3638}(window, document, window.jQuery)); 3639 3640// ========================================================================== 3641// 3642// SlideShow 3643// Enables slideshow functionality 3644// 3645// Example of usage: 3646// $.fancybox.getInstance().slideShow.start() 3647// 3648// ========================================================================== 3649;(function (document, $) { 3650 'use strict'; 3651 3652 var SlideShow = function( instance ) { 3653 3654 this.instance = instance; 3655 3656 this.init(); 3657 3658 }; 3659 3660 $.extend( SlideShow.prototype, { 3661 timer : null, 3662 isActive : false, 3663 $button : null, 3664 speed : 3000, 3665 3666 init : function() { 3667 var self = this; 3668 3669 self.$button = $('<button data-fancybox-play class="fancybox-button fancybox-button--play" title="Slideshow (P)"></button>') 3670 .appendTo( self.instance.$refs.buttons ); 3671 3672 self.instance.$refs.container.on('click', '[data-fancybox-play]', function() {
3673 self.toggle(); 3674 }); 3675 3676 }, 3677 3678 set : function() { 3679 var self = this; 3680 3681 // Check if reached last element 3682 if ( self.instance && self.instance.current && (self.instance.current.opts.loop || self.instance.currIndex < self.instance.group.length - 1 )) { 3683 3684 self.timer = setTimeout(function() { 3685 self.instance.next(); 3686 3687 }, self.instance.current.opts.slideShow.speed || self.speed); 3688 3689 } else { 3690 self.stop(); 3691 } 3692 }, 3693 3694 clear : function() { 3695 var self = this; 3696 3697 clearTimeout( self.timer ); 3698 3699 self.timer = null; 3700 }, 3701 3702 start : function() { 3703 var self = this; 3704 3705 self.stop(); 3706 3707 if ( self.instance && self.instance.current && ( self.instance.current.opts.loop || self.instance.currIndex < self.instance.group.length - 1 )) { 3708 3709 self.instance.$refs.container.on({ 3710 'beforeLoad.fb.player' : $.proxy(self, "clear"), 3711 'onComplete.fb.player' : $.proxy(self, "set"), 3712 }); 3713 3714 self.isActive = true; 3715 3716 if ( self.instance.current.isComplete ) { 3717 self.set(); 3718 } 3719 3720 self.instance.$refs.container.trigger('onPlayStart'); 3721 3722 self.$button.addClass('fancybox-button--pause'); 3723 } 3724 3725 }, 3726 3727 stop: function() { 3728 var self = this; 3729 3730 self.clear(); 3731 3732 self.instance.$refs.container 3733 .trigger('onPlayEnd') 3734 .off('.player'); 3735 3736 self.$button.removeClass('fancybox-button--pause'); 3737 3738 self.isActive = false; 3739 }, 3740 3741 toggle : function() { 3742 var self = this; 3743 3744 if ( self.isActive ) { 3745 self.stop(); 3746 3747 } else { 3748 self.start(); 3749 } 3750 } 3751 3752 }); 3753 3754 $(document).on('onInit.fb', function(e, instance) { 3755 3756 if ( instance && instance.group.length > 1 && !!instance.opts.slideShow && !instance.SlideShow ) { 3757 instance.SlideShow = new SlideShow( instance ); 3758 } 3759 3760 }); 3761 3762 $(document).on('beforeClose.fb onDeactivate.fb', function(e, instance) { 3763 3764 if ( instance && instance.SlideShow ) { 3765 instance.SlideShow.stop(); 3766 } 3767 3768 }); 3769 3770}(document, window.jQuery)); 3771 3772// ========================================================================== 3773// 3774// FullScreen 3775// Adds fullscreen functionality 3776// 3777// ========================================================================== 3778;(function (document, $) { 3779 'use strict'; 3780 3781 // Collection of methods supported by user browser 3782 var fn = (function () { 3783 3784 var fnMap = [ 3785 [ 3786 'requestFullscreen', 3787 'exitFullscreen', 3788 'fullscreenElement', 3789 'fullscreenEnabled', 3790 'fullscreenchange', 3791 'fullscreenerror' 3792 ], 3793 // new WebKit 3794 [ 3795 'webkitRequestFullscreen', 3796 'webkitExitFullscreen', 3797 'webkitFullscreenElement', 3798 'webkitFullscreenEnabled', 3799 'webkitfullscreenchange', 3800 'webkitfullscreenerror' 3801 3802 ], 3803 // old WebKit (Safari 5.1) 3804 [ 3805 'webkitRequestFullScreen', 3806 'webkitCancelFullScreen', 3807 'webkitCurrentFullScreenElement', 3808 'webkitCancelFullScreen', 3809 'webkitfullscreenchange', 3810 'webkitfullscreenerror' 3811 3812 ], 3813 [ 3814 'mozRequestFullScreen', 3815 'mozCancelFullScreen', 3816 'mozFullScreenElement', 3817 'mozFullScreenEnabled', 3818 'mozfullscreenchange', 3819 'mozfullscreenerror' 3820 ], 3821 [ 3822 'msRequestFullscreen', 3823 'msExitFullscreen', 3824 'msFullscreenElement', 3825 'msFullscreenEnabled', 3826 'MSFullscreenChange', 3827 'MSFullscreenError' 3828 ] 3829 ]; 3830 3831 var val; 3832 var ret = {}; 3833 var i, j; 3834 3835 for ( i = 0; i < fnMap.length; i++ ) { 3836 val = fnMap[ i ]; 3837 3838 if ( val && val[ 1 ] in document ) { 3839 for ( j = 0; j < val.length; j++ ) { 3840 ret[ fnMap[ 0 ][ j ] ] = val[ j ]; 3841 } 3842 3843 return ret; 3844 } 3845 } 3846 3847 return false; 3848 })(); 3849 3850 if ( !fn ) { 3851 return; 3852 } 3853 3854 var FullScreen = { 3855 request : function ( elem ) { 3856 3857 elem = elem || document.documentElement; 3858 3859 elem[ fn.requestFullscreen ]( elem.ALLOW_KEYBOARD_INPUT ); 3860 3861 }, 3862 exit : function () { 3863 document[ fn.exitFullscreen ](); 3864 }, 3865 toggle : function ( elem ) { 3866 3867 if ( this.isFullscreen() ) { 3868 this.exit(); 3869 } else { 3870 this.request( elem ); 3871 } 3872 3873 }, 3874 isFullscreen : function() { 3875 return Boolean( document[ fn.fullscreenElement ] ); 3876 }, 3877 enabled : function() { 3878 return Boolean( document[ fn.fullscreenEnabled ] ); 3879 } 3880 }; 3881 3882 $(document).on({ 3883 'onInit.fb' : function(e, instance) { 3884 var $container; 3885 3886 if ( instance && !!instance.opts.fullScreen && !instance.FullScreen) { 3887 $container = instance.$refs.container; 3888 3889 instance.$refs.button_fs = $('<button data-fancybox-fullscreen class="fancybox-button fancybox-button--fullscreen" title="Full screen (F)"></button>') 3890 .appendTo( instance.$refs.buttons ); 3891 3892 $container.on('click.fb-fullscreen', '[data-fancybox-fullscreen]', function(e) { 3893 3894 e.stopPropagation(); 3895 e.preventDefault(); 3896 3897 FullScreen.toggle( $container[ 0 ] ); 3898 3899 }); 3900 3901 if ( instance.opts.fullScreen.requestOnStart === true ) { 3902 FullScreen.request( $container[ 0 ] ); 3903 } 3904 3905 } 3906 3907 }, 'beforeMove.fb' : function(e, instance) { 3908 3909 if ( instance && instance.$refs.button_fs ) { 3910 instance.$refs.button_fs.toggle( !!instance.current.opts.fullScreen ); 3911 } 3912 3913 }, 'beforeClose.fb': function() { 3914 FullScreen.exit(); 3915 } 3916 }); 3917 3918 $(document).on(fn.fullscreenchange, function() { 3919 var instance = $.fancybox.getInstance(); 3920 var $what = instance ? instance.current.$placeholder : null; 3921 3922 if ( $what ) { 3923 3924 // If image is zooming, then this will force it to stop and reposition properly 3925 $what.css( 'transition', 'none' ); 3926 3927 instance.isAnimating = false;
3928 3929 instance.update( true, true, 0 ); 3930 } 3931 3932 }); 3933 3934}(document, window.jQuery)); 3935 3936// ========================================================================== 3937// 3938// Thumbs 3939// Displays thumbnails in a grid 3940// 3941// ========================================================================== 3942;(function (document, $) { 3943 'use strict'; 3944 3945 var FancyThumbs = function( instance ) { 3946 3947 this.instance = instance; 3948 3949 this.init(); 3950 3951 }; 3952 3953 $.extend( FancyThumbs.prototype, { 3954 3955 $button : null, 3956 $grid : null, 3957 $list : null, 3958 isVisible : false, 3959 3960 init : function() { 3961 var self = this; 3962 3963 self.$button = $('<button data-fancybox-thumbs class="fancybox-button fancybox-button--thumbs" title="Thumbnails (G)"></button>') 3964 .appendTo( this.instance.$refs.buttons ) 3965 .on('touchend click', function(e) { 3966 e.stopPropagation(); 3967 e.preventDefault(); 3968 3969 self.toggle(); 3970 }); 3971 3972 }, 3973 3974 create : function() { 3975 var instance = this.instance, 3976 list, 3977 src; 3978 3979 this.$grid = $('<div class="fancybox-thumbs"></div>').appendTo( instance.$refs.container ); 3980 3981 list = '<ul>'; 3982 3983 $.each(instance.group, function( i, item ) { 3984 3985 src = item.opts.thumb || ( item.opts.$thumb ? item.opts.$thumb.attr('src') : null ); 3986 3987 if ( !src && item.type === 'image' ) { 3988 src = item.src; 3989 } 3990 3991 if ( src && src.length ) { 3992 list += '<li data-index="' + i + '" tabindex="0" class="fancybox-thumbs-loading"><img data-src="' + src + '" /></li>'; 3993 } 3994 3995 }); 3996 3997 list += '</ul>'; 3998 3999 this.$list = $( list ).appendTo( this.$grid ).on('click touchstart', 'li', function() { 4000 4001 instance.jumpTo( $(this).data('index') ); 4002 4003 }); 4004 4005 this.$list.find('img').hide().one('load', function() { 4006 4007 var $parent = $(this).parent().removeClass('fancybox-thumbs-loading'), 4008 thumbWidth = $parent.outerWidth(), 4009 thumbHeight = $parent.outerHeight(), 4010 width, 4011 height, 4012 widthRatio, 4013 heightRatio; 4014 4015 width = this.naturalWidth || this.width; 4016 height = this.naturalHeight || this.height; 4017 4018 //Calculate thumbnail width/height and center it 4019 4020 widthRatio = width / thumbWidth; 4021 heightRatio = height / thumbHeight; 4022 4023 if (widthRatio >= 1 && heightRatio >= 1) { 4024 if (widthRatio > heightRatio) { 4025 width = width / heightRatio; 4026 height = thumbHeight; 4027 4028 } else { 4029 width = thumbWidth; 4030 height = height / widthRatio; 4031 } 4032 } 4033 4034 $(this).css({ 4035 width : Math.floor(width), 4036 height : Math.floor(height), 4037 'margin-top' : Math.min( 0, Math.floor(thumbHeight * 0.3 - he
4037ight * 0.3 ) ), 4038 'margin-left' : Math.min( 0, Math.floor(thumbWidth * 0.5 - width * 0.5 ) ) 4039 }).show(); 4040 4041 }) 4042 .each(function() { 4043 this.src = $( this ).data( 'src' ); 4044 }); 4045 4046 }, 4047 4048 focus : function() { 4049 4050 if ( this.instance.current ) { 4051 this.$list 4052 .children() 4053 .removeClass('fancybox-thumbs-active') 4054 .filter('[data-index="' + this.instance.current.index + '"]') 4055 .addClass('fancybox-thumbs-active') 4056 .focus(); 4057 } 4058 4059 }, 4060 4061 close : function() { 4062 4063 this.$grid.hide(); 4064 4065 }, 4066 4067 update : function() { 4068 4069 this.instance.$refs.container.toggleClass('fancybox-container--thumbs', this.isVisible); 4070 4071 if ( this.isVisible ) { 4072 4073 if ( !this.$grid ) { 4074 this.create(); 4075 } 4076 4077 this.$grid.show(); 4078 4079 this.focus(); 4080 4081 } else if ( this.$grid ) { 4082 this.$grid.hide(); 4083 } 4084 4085 this.instance.update(); 4086 4087 }, 4088 4089 hide : function() { 4090 4091 this.isVisible = false; 4092 4093 this.update(); 4094 4095 }, 4096 4097 show : function() { 4098 4099 this.isVisible = true; 4100 4101 this.update(); 4102 4103 }, 4104 4105 toggle : function() { 4106 4107 if ( this.isVisible ) { 4108 this.hide(); 4109 4110 } else { 4111 this.show(); 4112 } 4113 } 4114 4115 }); 4116 4117 $(document).on('onInit.fb', function(e, instance) { 4118 var first = instance.group[0], 4119 second = instance.group[1]; 4120 4121 if ( !!instance.opts.thumbs && !instance.Thumbs && instance.group.length > 1 && ( 4122 ( first.type == 'image' || first.opts.thumb || first.opts.$thumb ) && 4123 ( second.type == 'image' || second.opts.thumb || second.opts.$thumb ) 4124 ) 4125 ) { 4126 4127 instance.Thumbs = new FancyThumbs( instance ); 4128 } 4129 4130 }); 4131 4132 $(document).on('beforeMove.fb', function(e, instance, item) { 4133 var self = instance && instance.Thumbs; 4134 4135 if ( !self ) { 4136 return; 4137 } 4138 4139 if ( item.modal ) { 4140 4141 self.$button.hide(); 4142 4143 self.hide(); 4144 4145 } else { 4146 4147 if ( instance.opts.thumbs.showOnStart === true && instance.firstRun ) { 4148 self.show(); 4149 4150 } 4151 4152 self.$button.show(); 4153 4154 if ( self.isVisible ) { 4155 self.focus(); 4156 } 4157 4158 } 4159 4160 }); 4161 4162 $(document).on('beforeClose.fb', function(e, instance) { 4163 4164 if ( instance && instance.Thumbs) { 4165 if ( instance.Thumbs.isVisible && instance.opts.thumbs.hideOnClosing !== false ) { 4166 instance.Thumbs.close(); 4167 } 4168 4169 instance.Thumbs = null; 4170 } 4171 4172 }); 4173 4174}(document, window.jQuery)); 4175 4176// ========================================================================== 4177// 4178// Hash 4179// Enables linking to each modal 4180// 4181// ========================================================================== 4182;(function (document, window, $) { 4183 'use strict'; 4184 4185 // Simple $.escapeSelector polyfill (for jQuery prior v3) 4186 if ( !$.escapeSelector ) { 4187 $.escapeSelector = function( sel ) { 4188 var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g; 4189 var fcssescape = function( ch, asCodePoint ) { 4190 if ( asCodePoint ) { 4191 // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER 4192 if ( ch === "\0" ) { 4193 return "\uFFFD"; 4194 } 4195 4196 // Control characters and (dependent upon position) numbers get escaped as code points 4197 return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; 4198 } 4199 4200 // Other potentially-special ASCII characters get backslash-escaped 4201 return "\\" + ch; 4202 }; 4203 4204 return ( sel + "" ).replace( rcssescape, fcssescape ); 4205 }; 4206 } 4207 4208 // Variable containing last hash value set by fancyBox 4209 // It will be used to determine if fancyBox needs to close after hash change is detected 4210 var currentHash = null; 4211 4212 // Get info about gallery name and current index from url 4213 function parseUrl() { 4214 var hash = window.location.hash.substr( 1 ); 4215 var rez = hash.split( '-' ); 4216 var index = rez.length > 1 && /^\+?\d+$/.test( rez[ rez.length - 1 ] ) ? parseInt( rez.pop( -1 ), 10 ) || 1 : 1; 4217 var gallery = rez.join( '-' ); 4218 4219 // Index is starting from 1 4220 if ( index < 1 ) { 4221 index = 1; 4222 } 4223 4224 return { 4225 hash : hash, 4226 index : index, 4227 gallery : gallery 4228 }; 4229 } 4230 4231 // Trigger click evnt on links to open new fancyBox instance 4232 function triggerFromUrl( url ) { 4233 var $el; 4234 4235 if ( url.gallery !== '' ) { 4236 4237 // If we can find element matching 'data-fancybox' atribute, then trigger click event for that .. 4238 $el = $( "[data-fancybox='" + $.escapeSelector( url.gallery ) + "']" ).eq( url.index - 1 ); 4239 4240 if ( $el.length ) { 4241 $el.trigger( 'click' ); 4242 4243 } else { 4244 4245 // .. if not, try finding element by ID 4246 $( "#" + $.escapeSelector( url.gallery ) + "" ).trigger( 'click' ); 4247 4248 } 4249 4250 } 4251 } 4252 4253 // Get gallery name from current instance 4254 function getGallery( instance ) { 4255 var opts; 4256 4257 if ( !instance ) { 4258 return false;
4259 } 4260 4261 opts = instance.current ? instance.current.opts : instance.opts; 4262 4263 return opts.$orig ? opts.$orig.data( 'fancybox' ) : ( opts.hash || '' ); 4264 } 4265 4266 // Star when DOM becomes ready 4267 $(function() { 4268 4269 // Small delay is used to allow other scripts to process "dom ready" event 4270 setTimeout(function() { 4271 4272 // Check if this module is not disabled 4273 if ( $.fancybox.defaults.hash === false ) { 4274 return; 4275 } 4276 4277 // Check if need to close after url has changed 4278 $(window).on('hashchange.fb', function() { 4279 var url = parseUrl(); 4280 4281 if ( $.fancybox.getInstance() ) { 4282 if ( currentHash && currentHash !== url.gallery + '-' + url.index ) { 4283 currentHash = null; 4284 4285 $.fancybox.close(); 4286 } 4287 4288 } else if ( url.gallery !== '' ) { 4289 triggerFromUrl( url ); 4290 } 4291 4292 }); 4293 4294 // Update hash when opening/closing fancyBox 4295 $(document).on({ 4296 'onInit.fb' : function( e, instance ) { 4297 var url = parseUrl(); 4298 var gallery = getGallery( instance ); 4299 4300 // Make sure gallery start index matches index from hash 4301 if ( gallery && url.gallery && gallery == url.gallery ) { 4302 instance.currIndex = url.index - 1; 4303 } 4304 4305 }, 'beforeMove.fb' : function( e, instance, current ) { 4306 var gallery = getGallery( instance ); 4307 4308 // Update window hash 4309 if ( gallery && gallery !== '' ) { 4310 4311 if ( window.location.hash.indexOf( gallery ) < 0 ) { 4312 instance.opts.origHash = window.location.hash; 4313 } 4314 4315 currentHash = gallery + ( instance.group.length > 1 ? '-' + ( current.index + 1 ) : '' ); 4316 4317 if ( "pushState" in history ) { 4318 history.pushState( '', document.title, window.location.pathname + window.location.search + '#' + currentHash ); 4319 4320 } else { 4321 window.location.hash = currentHash; 4322 } 4323 4324 } 4325 4326 }, 'beforeClose.fb' : function( e, instance, current ) { 4327 var gallery = getGallery( instance ); 4328 var origHash = instance && instance.opts.origHash ? instance.opts.origHash : ''; 4329 4330 // Remove hash from location bar 4331 if ( gallery && gallery !== '' ) { 4332 if ( "pushState" in history ) { 4333 history.pushState( '', document.title, window.location.pathname + window.location.search + origHash ); 4334 4335 } else { 4336 window.location.hash = origHash; 4337 } 4338 } 4339 4340 currentHash = null; 4341 } 4342 }); 4343 4344 // Check current hash and trigger click event on matching element to start fancyBox, if needed 4345 triggerFromUrl( parseUrl() ); 4346 4347 }, 50); 4348 }); 4349 4350 4351}(document, window, window.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.