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 if ( $(target).attr( 'data-fancybox' ) ) { 2626 value = $(target).data( 'fancybox' ); 2627 } 2628 2629 if ( value ) { 2630 items = items.length ? items.filter( '[data-fancybox="' + value + '"]' ) : $( '[data-fancybox=' + value + ']' ); 2631 index = items.index( target ); 2632 2633 } else { 2634 items = [ target ]; 2635 } 2636 2637 $.fancybox.open( items, opts, index ); 2638 } 2639 2640 2641 // Create a jQuery plugin 2642 // ====================== 2643 2644 $.fn.fancybox = function (options) { 2645 2646 this.off('click.fb-start').on('click.fb-start', { 2647 items : this, 2648 options : options || {} 2649 }, _run); 2650 2651 return this; 2652 2653 }; 2654 2655 2656 // Self initializing plugin 2657 // ======================== 2658 2659 $(document).on('click.fb-start', '[data-fancybox]', _run); 2660 2661}(window, document, window.jQuery)); 2662 2663// ========================================================================== 2664// 2665// Media 2666// Adds additional media type support 2667// 2668// ========================================================================== 2669;(function ($) { 2670 2671 'use strict'; 2672 2673 // Formats matching url to final form 2674 2675 var format = function (url, rez, params) { 2676 if ( !url ) { 2677 return; 2678 } 2679 2680 params = params || ''; 2681 2682 if ( $.type(params) === "object" ) { 2683 params = $.param(params, true); 2684 } 2685 2686 $.each(rez, function (key, value) { 2687 url = url.replace('$' + key, value || ''); 2688 }); 2689 2690 if (params.length) { 2691 url += (url.indexOf('?') > 0 ? '&' : '?') + params; 2692 } 2693 2694 return url; 2695 }; 2696 2697 // Object containing properties for each media type 2698 2699 var media = { 2700 youtube: { 2701 matcher: /(youtube\.com|youtu\.be|youtube\-nocookie\.com)\/(watch\?(.*&)?v=|v\/|u\/|embed\/?)?(videoseries\?list=(.*)|[\w-]{11}|\?listType=(.*)&list=(.*))(.*)/i, 2702 params: { 2703 autoplay: 1, 2704 autohide: 1, 2705 fs: 1, 2706 rel: 0, 2707 hd: 1, 2708 wmode: 'transparent', 2709 enablejsapi: 1, 2710 html5: 1 2711 }, 2712 paramPlace : 8, 2713 type: 'iframe', 2714 url: '//www.youtube.com/embed/$4', 2715 thumb: '//img.youtube.com/vi/$4/hqdefault.jpg' 2716 }, 2717 2718 vimeo: { 2719 matcher: /^.+vimeo.com\/(.*\/)?([\d]+)(.*)?/, 2720 params: { 2721 autoplay: 1, 2722 hd: 1, 2723 show_title: 1, 2724 show_byline: 1, 2725 show_portrait: 0, 2726 fullscreen: 1, 2727 api: 1 2728 }, 2729 paramPlace : 3, 2730 type: 'iframe', 2731 url: '//player.vimeo.com/video/$2' 2732 }, 2733 2734 metacafe: { 2735 matcher: /metacafe.com\/watch\/(\d+)\/(.*)?/, 2736 type: 'iframe', 2737 url: '//www.metacafe.com/embed/$1/?ap=1' 2738 }, 2739 2740 dailymotion: { 2741 matcher: /dailymotion.com\/video\/(.*)\/?(.*)/, 2742 params: { 2743 additionalInfos: 0, 2744 autoStart: 1 2745 }, 2746 type: 'iframe', 2747 url: '//www.dailymotion.com/embed/video/$1' 2748 }, 2749 2750 vine: { 2751 matcher: /vine.co\/v\/([a-zA-Z0-9\?\=\-]+)/, 2752 type: 'iframe', 2753 url: '//vine.co/v/$1/embed/simple' 2754 }, 2755 2756 instagram: { 2757 matcher: /(instagr\.am|instagram\.com)\/p\/([a-zA-Z0-9_\-]+)\/?/i, 2758 type: 'image', 2759 url: '//$1/p/$2/media/?size=l' 2760 }, 2761 2762 // Examples: 2763 // http://maps.google.com/?ll=48.857995,2.294297&spn=0.007666,0.021136&t=m&z=16 2764 // http://maps.google.com/?ll=48.857995,2.294297&spn=0.007666,0.021136&t=m&z=16 2765 // https://www.google.lv/maps/place/Googleplex/@37.4220041,-122.0833494,17z/data=!4m5!3m4!1s0x0:0x6c296c66619367e0!8m2!3d37.4219998!4d-122.0840572 2766 google_maps: { 2767 matcher: /(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(((maps\/(place\/(.*)\/)?\@(.*),(\d+.?\d+?)z))|(\?ll=))(.*)?/i, 2768 type: 'iframe', 2769 url: function (rez) { 2770 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' ); 2771 } 2772 } 2773 }; 2774 2775 $(document).on('onInit.fb', function (e, instance) { 2776 2777 $.each(instance.group, function( i, item ) { 2778 2779 var url = item.src || '', 2780 type = false, 2781 thumb, 2782 rez, 2783 params,
2784 urlParams, 2785 o, 2786 provider; 2787 2788 // Skip items that already have content type 2789 if ( item.type ) { 2790 return; 2791 } 2792 2793 // Look for any matching media type 2794 2795 $.each(media, function ( n, el ) { 2796 rez = url.match(el.matcher); 2797 o = {}; 2798 provider = n; 2799 2800 if (!rez) { 2801 return; 2802 } 2803 2804 type = el.type; 2805 2806 if ( el.paramPlace && rez[ el.paramPlace ] ) { 2807 urlParams = rez[ el.paramPlace ]; 2808 2809 if ( urlParams[ 0 ] == '?' ) { 2810 urlParams = urlParams.substring(1); 2811 } 2812 2813 urlParams = urlParams.split('&'); 2814 2815 for ( var m = 0; m < urlParams.length; ++m ) { 2816 var p = urlParams[ m ].split('=', 2); 2817 2818 if ( p.length == 2 ) { 2819 o[ p[0] ] = decodeURIComponent( p[1].replace(/\+/g, " ") ); 2820 } 2821 } 2822 } 2823 2824 params = $.extend( true, {}, el.params, item.opts[ n ], o ); 2825 2826 url = $.type(el.url) === "function" ? el.url.call(this, rez, params, item) : format(el.url, rez, params); 2827 thumb = $.type(el.thumb) === "function" ? el.thumb.call(this, rez, params, item) : format(el.thumb, rez); 2828 2829 if ( provider === 'vimeo' ) { 2830 url = url.replace('&%23', '#'); 2831 } 2832 2833 return false; 2834 }); 2835 2836 // If it is found, then change content type and update the url 2837 2838 if ( type ) { 2839 item.src = url; 2840 item.type = type; 2841 2842 if ( !item.opts.thumb && !(item.opts.$thumb && item.opts.$thumb.length ) ) { 2843 item.opts.thumb = thumb; 2844 } 2845 2846 if ( type === 'iframe' ) { 2847 $.extend(true, item.opts, { 2848 iframe : { 2849 preload : false, 2850 scrolling : "no" 2851 }, 2852 smallBtn : false, 2853 closeBtn : true, 2854 fullScreen : false, 2855 slideShow : false 2856 }); 2857 2858 item.opts.slideClass += ' fancybox-slide--video'; 2859 } 2860 2861 } else { 2862 2863 // If no content type is found, then set it to `iframe` as fallback 2864 item.type = 'iframe'; 2865 2866 } 2867 2868 }); 2869 2870 }); 2871 2872}(window.jQuery)); 2873 2874// ========================================================================== 2875// 2876// Guestures 2877// Adds touch guestures, handles click and tap events 2878// 2879// ========================================================================== 2880;(function (window, document, $) { 2881 'use strict'; 2882 2883 var requestAFrame = (function() { 2884 return window.requestAnimationFrame || 2885 window.webkitRequestAnimationFrame || 2886 window.mozRequestAnimationFrame || 2887 function( callback ) { 2888 window.setTimeout(callback, 1000 / 60); }; 2889 })(); 2890 2891 2892 var pointers = function( e ) { 2893 var result = []; 2894 2895 e = e.originalEvent || e || window.e; 2896 e = e.touches && e.touches.length ? e.touches : ( e.changedTouches && e.changedTouches.length ? e.changedTouches : [ e ] ); 2897 2898 for ( var key in e ) { 2899 2900 if ( e[ key ].pageX ) { 2901 result.push( { x : e[ key ].pageX, y : e[ key ].pageY } ); 2902 2903 } else if ( e[ key ].clientX ) { 2904 result.push( { x : e[ key ].clientX, y : e[ key ].clientY } ); 2905 } 2906 } 2907 2908 return result; 2909 }; 2910 2911 var distance = function( point2, point1, what ) { 2912 2913 if ( !point1 || !point2 ) { 2914 return 0; 2915 } 2916 2917 if ( what === 'x' ) { 2918 return point2.x - point1.x; 2919 2920 } else if ( what === 'y' ) { 2921 return point2.y - point1.y; 2922 } 2923 2924 return Math.sqrt( Math.pow( point2.x - point1.x, 2 ) + Math.pow( point2.y - point1.y, 2 ) ); 2925 2926 }; 2927 2928 var isClickable = function( $el ) { 2929 2930 return $el.is('a') || $el.is('button') || $el.is('input') || $el.is('select') || $el.is('textarea') || $.isFunction( $el.get(0).onclick ); 2931 2932 }; 2933 2934 var hasScrollbars = function( el ) { 2935 var overflowY = window.getComputedStyle( el )['overflow-y']; 2936 var overflowX = window.getComputedStyle( el )['overflow-x']; 2937 2938 var vertical = (overflowY === 'scroll' || overflowY === 'auto') && el.scrollHeight > el.clientHeight; 2939 var horizontal = (overflowX === 'scroll' || overflowX === 'auto') && el.scrollWidth > el.clientWidth; 2940 2941 return vertical || horizontal; 2942 }; 2943 2944 var isScrollable = function ( $el ) { 2945 2946 var rez = false; 2947 2948 while ( true ) { 2949 rez = hasScrollbars( $el.get(0) ); 2950 2951 if ( rez ) { 2952 break; 2953 } 2954 2955 $el = $el.parent(); 2956 2957 if ( !$el.length || $el.hasClass('fancybox-slider') || $el.is('body') ) { 2958 break; 2959 } 2960 2961 } 2962 2963 return rez; 2964 2965 }; 2966 2967 2968 var Guestures = function ( instance ) { 2969 2970 var self = this; 2971 2972 self.instance = instance; 2973 2974 self.$wrap = instance.$refs.slider_wrap; 2975 self.$slider = instance.$refs.slider; 2976 self.$container = instance.$refs.container; 2977 2978 self.destroy(); 2979 2980 self.$wrap.on('touchstart.fb mousedown.fb', $.proxy(self, "ontouchstart")); 2981 2982 }; 2983 2984 Guestures.prototype.destroy = function() { 2985 2986 this.$wrap.off('touchstart.fb mousedown.fb touchmove.fb mousemove.fb touchend.fb touchcancel.fb mouseup.fb mouseleave.fb'); 2987 2988 }; 2989 2990 Guestures.prototype.ontouchstart = function( e ) { 2991 2992 var self = this; 2993 2994 var $target = $( e.target ); 2995 var instance = self.instance; 2996 var current = instance.current; 2997 var $content = current.$content || current.$placeholder; 2998 2999 self.startPoints = pointers( e ); 3000 3001 self.$target = $target; 3002 self.$content = $content; 3003 3004 self.canvasWidth = Math.round( current.$slide[0].clientWidth ); 3005 self.canvasHeight = Math.round( current.$slide[0].clientHeight ); 3006 3007 self.startEvent = e; 3008 3009 // Skip if clicked on the scrollbar 3010 if ( e.originalEvent.clientX > self.canvasWidth + current.$slide.offset().left ) { 3011 return true; 3012 } 3013 3014 // Ignore taping on links, buttons and scrollable items 3015 if ( isClickable( $target ) || isClickable( $target.parent() ) || ( isScrollable( $target ) ) ) { 3016 return; 3017 } 3018 3019 // If "touch" is disabled, then handle click event 3020 if ( !current.opts.touch ) { 3021 self.endPoints = self.startPoints; 3022 3023 return self.ontap(); 3024 } 3025 3026 // Ignore right click 3027 if ( e.originalEvent && e.originalEvent.button == 2 ) { 3028 return; 3029 } 3030 3031 e.stopPropagation(); 3032 e.preventDefault(); 3033 3034 if ( !current || self.instance.isAnimating || self.instance.isClosing ) { 3035 return; 3036 } 3037 3038 // Prevent zooming if already swiping 3039 if ( !self.startPoints || ( self.startPoints.length > 1 && !current.isMoved ) ) { 3040 return; 3041 } 3042 3043 self.$wrap.off('touchmove.fb mousemove.fb', $.proxy(self, "ontouchmove")); 3044 self.$wrap.off('touchend.fb touchcancel.fb mouseup.fb mouseleave.fb', $.proxy(self, "ontouchend")); 3045 3046 self.$wrap.on('touchend.fb touchcancel.fb mouseup.fb mouseleave.fb', $.proxy(self, "ontouchend")); 3047 self.$wrap.on('touchmove.fb mousemove.fb', $.proxy(self, "ontouchmove")); 3048 3049 self.startTime = new Date().getTime(); 3050 self.distanceX = self.distanceY = self.distance = 0; 3051 3052 self.canTap = false;
3053 self.isPanning = false; 3054 self.isSwiping = false; 3055 self.isZooming = false; 3056 3057 self.sliderStartPos = $.fancybox.getTranslate( self.$slider ); 3058 3059 self.contentStartPos = $.fancybox.getTranslate( self.$content ); 3060 self.contentLastPos = null; 3061 3062 if ( self.startPoints.length === 1 && !self.isZooming ) { 3063 self.canTap = current.isMoved; 3064 3065 if ( current.type === 'image' && ( self.contentStartPos.width > self.canvasWidth + 1 || self.contentStartPos.height > self.canvasHeight + 1 ) ) { 3066 3067 $.fancybox.stop( self.$content ); 3068 3069 self.isPanning = true; 3070 3071 } else { 3072 3073 $.fancybox.stop( self.$slider ); 3074 3075 self.isSwiping = true; 3076 } 3077 3078 self.$container.addClass('fancybox-controls--isGrabbing'); 3079 3080 } 3081 3082 if ( self.startPoints.length === 2 && current.isMoved && !current.hasError && current.type === 'image' && ( current.isLoaded || current.$ghost ) ) { 3083 3084 self.isZooming = true; 3085 3086 self.isSwiping = false; 3087 self.isPanning = false; 3088 3089 $.fancybox.stop( self.$content ); 3090 3091 self.centerPointStartX = ( ( self.startPoints[0].x + self.startPoints[1].x ) * 0.5 ) - $(window).scrollLeft(); 3092 self.centerPointStartY = ( ( self.startPoints[0].y + self.startPoints[1].y ) * 0.5 ) - $(window).scrollTop(); 3093 3094 self.percentageOfImageAtPinchPointX = ( self.centerPointStartX - self.contentStartPos.left ) / self.contentStartPos.width; 3095 self.percentageOfImageAtPinchPointY = ( self.centerPointStartY - self.contentStartPos.top ) / self.contentStartPos.height; 3096 3097 self.startDistanceBetweenFingers = distance( self.startPoints[0], self.startPoints[1] ); 3098 } 3099 3100 }; 3101 3102 Guestures.prototype.ontouchmove = function( e ) { 3103 3104 var self = this; 3105 3106 e.preventDefault(); 3107 3108 self.newPoints = pointers( e ); 3109 3110 if ( !self.newPoints || !self.newPoints.length ) { 3111 return; 3112 } 3113 3114 self.distanceX = distance( self.newPoints[0], self.startPoints[0], 'x' ); 3115 self.distanceY = distance( self.newPoints[0], self.startPoints[0], 'y' ); 3116 3117 self.distance = distance( self.newPoints[0], self.startPoints[0] ); 3118 3119 // Skip false ontouchmove events (Chrome) 3120 if ( self.distance > 0 ) { 3121 3122 if ( self.isSwiping ) { 3123 self.onSwipe(); 3124 3125 } else if ( self.isPanning ) { 3126 self.onPan(); 3127 3128 } else if ( self.isZooming ) { 3129 self.onZoom(); 3130 } 3131 3132 } 3133 3134 }; 3135 3136 Guestures.prototype.onSwipe = function() { 3137 3138 var self = this; 3139 3140 var swiping = self.isSwiping; 3141 var left = self.sliderStartPos.left; 3142 var angle; 3143 3144 if ( swiping === true ) { 3145 3146 if ( Math.abs( self.distance ) > 10 ) { 3147 3148 if ( self.instance.group.length < 2 ) { 3149 self.isSwiping = 'y'; 3150 3151 } else if ( !self.instance.current.isMoved || self.instance.opts.touch.vertical === false || ( self.instance.opts.touch.vertical === 'auto' && $( window ).width() > 800 ) ) { 3152 self.isSwiping = 'x'; 3153 3154 } else { 3155 angle = Math.abs( Math.atan2( self.distanceY, self.distanceX ) * 180 / Math.PI ); 3156 3157 self.isSwiping = ( angle > 45 && angle < 135 ) ? 'y' : 'x'; 3158 } 3159 3160 self.canTap = false; 3161 3162 self.instance.current.isMoved = false; 3163 3164 // Reset points to avoid jumping, because we dropped first swipes to calculate the angle 3165 self.startPoints = self.newPoints; 3166 } 3167 3168 } else { 3169 3170 if ( swiping == 'x' ) { 3171 3172 // Sticky edges 3173 if ( !self.instance.current.opts.loop && self.instance.current.index === 0 && self.distanceX > 0 ) { 3174 left = left + Math.pow( self.distanceX, 0.8 ); 3175 3176 } else if ( !self.instance.current.opts.loop &&self.instance.current.index === self.instance.group.length - 1 && self.distanceX < 0 ) { 3177 left = left - Math.pow( -self.distanceX, 0.8 ); 3178 3179 } else { 3180 left = left + self.distanceX; 3181 } 3182 3183 } 3184 3185 self.sliderLastPos = { 3186 top : swiping == 'x' ? 0 : self.sliderStartPos.top + self.distanceY, 3187 left : left 3188 }; 3189 3190 requestAFrame(function() { 3191 $.fancybox.setTranslate( self.$slider, self.sliderLastPos ); 3192 }); 3193 } 3194 3195 }; 3196 3197 Guestures.prototype.onPan = function() { 3198 3199 var self = this; 3200 3201 var newOffsetX, newOffsetY, newPos; 3202 3203 self.canTap = false; 3204 3205 if ( self.contentStartPos.width > self.canvasWidth ) { 3206 newOffsetX = self.contentStartPos.left + self.distanceX; 3207 3208 } else { 3209 newOffsetX = self.contentStartPos.left; 3210 } 3211 3212 newOffsetY = self.contentStartPos.top + self.distanceY; 3213 3214 newPos = self.limitMovement( newOffsetX, newOffsetY, self.contentStartPos.width, self.contentStartPos.height ); 3215 3216 newPos.scaleX = self.contentStartPos.scaleX; 3217 newPos.scaleY = self.contentStartPos.scaleY; 3218 3219 self.contentLastPos = newPos; 3220 3221 requestAFrame(function() { 3222 $.fancybox.setTranslate( self.$content, self.contentLastPos ); 3223 }); 3224 }; 3225 3226 // Make panning sticky to the edges 3227 Guestures.prototype.limitMovement = function( newOffsetX, newOffsetY, newWidth, newHeight ) { 3228 3229 var self = this; 3230 3231 var minTranslateX, minTranslateY, maxTranslateX, maxTranslateY; 3232 3233 var canvasWidth = self.canvasWidth; 3234 var canvasHeight = self.canvasHeight; 3235 3236 var currentOffsetX = self.contentStartPos.left; 3237 var currentOffsetY = self.contentStartPos.top; 3238 3239 var distanceX = self.distanceX; 3240 var distanceY = self.distanceY; 3241 3242 // Slow down proportionally to traveled distance 3243 3244 minTranslateX = Math.max(0, canvasWidth * 0.5 - newWidth * 0.5 ); 3245 minTranslateY = Math.max(0, canvasHeight * 0.5 - newHeight * 0.5 ); 3246 3247 maxTranslateX = Math.min( canvasWidth - newWidth, canvasWidth * 0.5 - newWidth * 0.5 ); 3248 maxTranslateY = Math.min( canvasHeight - newHeight, canvasHeight * 0.5 - newHeight * 0.5 ); 3249 3250 if ( newWidth > canvasWidth ) { 3251 3252 // -> 3253 if ( distanceX > 0 && newOffsetX > minTranslateX ) { 3254 newOffsetX = minTranslateX - 1 + Math.pow( -minTranslateX + currentOffsetX + distanceX, 0.8 ) || 0; 3255 } 3256 3257 // <- 3258 if ( distanceX < 0 && newOffsetX < maxTranslateX ) { 3259 newOffsetX = maxTranslateX + 1 - Math.pow( maxTranslateX - currentOffsetX - distanceX, 0.8 ) || 0; 3260 } 3261 3262 } 3263 3264 if ( newHeight > canvasHeight ) { 3265 3266 // \/ 3267 if ( distanceY > 0 && newOffsetY > minTranslateY ) { 3268 newOffsetY = minTranslateY - 1 + Math.pow(-minTranslateY + currentOffsetY + distanceY, 0.8 ) || 0; 3269 } 3270 3271 // /\ 3272 if ( distanceY < 0 && newOffsetY < maxTranslateY ) { 3273 newOffsetY = maxTranslateY + 1 - Math.pow ( maxTranslateY - currentOffsetY - distanceY, 0.8 ) || 0; 3274 } 3275 3276 } 3277 3278 return { 3279 top : newOffsetY, 3280 left : newOffsetX 3281 }; 3282 3283 }; 3284 3285 3286 Guestures.prototype.limitPosition = function( newOffsetX, newOffsetY, newWidth, newHeight ) { 3287 3288 var self = this; 3289 3290 var canvasWidth = self.canvasWidth; 3291 var canvasHeight = self.canvasHeight; 3292 3293 if ( newWidth > canvasWidth ) { 3294 newOffsetX = newOffsetX > 0 ? 0 : newOffsetX; 3295 newOffsetX = newOffsetX < canvasWidth - newWidth ? canvasWidth - newWidth : newOffsetX; 3296 3297 } else { 3298 3299 // Center horizontally 3300 newOffsetX = Math.max( 0, canvasWidth / 2 - newWidth / 2 ); 3301 3302 } 3303 3304 if ( newHeight > canvasHeight ) { 3305 newOffsetY = newOffsetY > 0 ? 0 : newOffsetY; 3306 newOffsetY = newOffsetY < canvasHeight - newHeight ? canvasHeight - newHeight : newOffsetY; 3307 3308 } else { 3309 3310 // Center vertically 3311 newOffsetY = Math.max( 0, canvasHeight / 2 - newHeight / 2 ); 3312 3313 } 3314 3315 return { 3316 top : newOffsetY, 3317 left : newOffsetX 3318 }; 3319 3320 }; 3321 3322 Guestures.prototype.onZoom = function() { 3323 3324 var self = this; 3325 3326 // Calculate current distance between points to get pinch ratio and new width and height 3327 3328 var currentWidth = self.contentStartPos.width; 3329 var currentHeight = self.contentStartPos.height; 3330 3331 var currentOffsetX = self.contentStartPos.left; 3332 var currentOffsetY = self.contentStartPos.top; 3333 3334 var endDistanceBetweenFingers = distance( self.newPoints[0], self.newPoints[1] ); 3335 3336 var pinchRatio = endDistanceBetweenFingers / self.startDistanceBetweenFingers; 3337 3338 var newWidth = Math.floor( currentWidth * pinchRatio ); 3339 var newHeight = Math.floor( currentHeight * pinchRatio ); 3340 3341 // This is the translation due to pinch-zooming 3342 var translateFromZoomingX = (currentWidth - newWidth) * self.percentageOfImageAtPinchPointX; 3343 var translateFromZoomingY = (currentHeight - newHeight) * self.percentageOfImageAtPinchPointY; 3344 3345 //Point between the two touches 3346 3347 var centerPointEndX = ((self.newPoints[0].x + self.newPoints[1].x) / 2) - $(window).scrollLeft(); 3348 var centerPointEndY = ((self.newPoints[0].y + self.newPoints[1].y) / 2) - $(window).scrollTop(); 3349 3350 // And this is the translation due to translation of the centerpoint 3351 // between the two fingers 3352 3353 var translateFromTranslatingX = centerPointEndX - self.centerPointStartX; 3354 var translateFromTranslatingY = centerPointEndY - self.centerPointStartY; 3355 3356 // The new offset is the old/current one plus the total translation 3357 3358 var newOffsetX = currentOffsetX + ( translateFromZoomingX + translateFromTranslatingX ); 3359 var newOffsetY = currentOffsetY + ( translateFromZoomingY + translateFromTranslatingY ); 3360 3361 var newPos = { 3362 top : newOffsetY, 3363 left : newOffsetX, 3364 scaleX : self.contentStartPos.scaleX * pinchRatio, 3365 scaleY : self.contentStartPos.scaleY * pinchRatio 3366 }; 3367 3368 self.canTap = false;
3369 3370 self.newWidth = newWidth; 3371 self.newHeight = newHeight; 3372 3373 self.contentLastPos = newPos; 3374 3375 requestAFrame(function() { 3376 $.fancybox.setTranslate( self.$content, self.contentLastPos ); 3377 }); 3378 3379 }; 3380 3381 Guestures.prototype.ontouchend = function( e ) { 3382 3383 var self = this; 3384 3385 var current = self.instance.current; 3386 3387 var dMs = Math.max( (new Date().getTime() ) - self.startTime, 1); 3388 3389 var swiping = self.isSwiping; 3390 var panning = self.isPanning; 3391 var zooming = self.isZooming; 3392 3393 self.endPoints = pointers( e ); 3394 3395 self.$container.removeClass('fancybox-controls--isGrabbing'); 3396 3397 self.$wrap.off('touchmove.fb mousemove.fb', $.proxy(this, "ontouchmove")); 3398 self.$wrap.off('touchend.fb touchcancel.fb mouseup.fb mouseleave.fb', $.proxy(this, "ontouchend")); 3399 3400 self.isSwiping = false; 3401 self.isPanning = false; 3402 self.isZooming = false; 3403 3404 if ( self.canTap ) { 3405 return self.ontap(); 3406 } 3407 3408 // Speed in px/ms 3409 self.velocityX = self.distanceX / dMs * 0.5; 3410 self.velocityY = self.distanceY / dMs * 0.5; 3411 3412 self.speed = current.opts.speed || 330; 3413 3414 self.speedX = Math.max( self.speed * 0.75, Math.min( self.speed * 1.5, ( 1 / Math.abs( self.velocityX ) ) * self.speed ) ); 3415 self.speedY = Math.max( self.speed * 0.75, Math.min( self.speed * 1.5, ( 1 / Math.abs( self.velocityY ) ) * self.speed ) ); 3416 3417 if ( panning ) { 3418 self.endPanning(); 3419 3420 } else if ( zooming ) { 3421 self.endZooming(); 3422 3423 } else { 3424 self.endSwiping( swiping ); 3425 } 3426 3427 return; 3428 }; 3429 3430 Guestures.prototype.endSwiping = function( swiping ) { 3431 3432 var self = this; 3433 3434 // Close if swiped vertically / navigate if horizontally 3435 3436 if ( swiping == 'y' && Math.abs( self.distanceY ) > 50 ) { 3437 3438 // Continue vertical movement 3439 3440 $.fancybox.animate( self.$slider, null, { 3441 top : self.sliderStartPos.top + self.distanceY + self.velocityY * 150, 3442 left : self.sliderStartPos.left, 3443 opacity : 0 3444 }, self.speedY ); 3445 3446 self.instance.close( true ); 3447 3448 } else if ( swiping == 'x' && self.distanceX > 50 ) { 3449 self.instance.previous( self.speedX ); 3450 3451 } else if ( swiping == 'x' && self.distanceX < -50 ) { 3452 self.instance.next( self.speedX ); 3453 3454 } else { 3455 3456 // Move back to center 3457 self.instance.update( false, true, 150 ); 3458 3459 } 3460 3461 }; 3462 3463 Guestures.prototype.endPanning = function() { 3464 3465 var self = this; 3466 var newOffsetX, newOffsetY, newPos; 3467 3468 if ( !self.contentLastPos ) { 3469 return; 3470 } 3471 3472 newOffsetX = self.contentLastPos.left + ( self.velocityX * self.speed * 2 ); 3473 newOffsetY = self.contentLastPos.top + ( self.velocityY * self.speed * 2 ); 3474 3475 newPos = self.limitPosition( newOffsetX, newOffsetY, self.contentStartPos.width, self.contentStartPos.height ); 3476 3477 newPos.width = self.contentStartPos.width; 3478 newPos.height = self.contentStartPos.height; 3479 3480 $.fancybox.animate( self.$content, null, newPos, self.speed, "easeOutSine" ); 3481 3482 }; 3483 3484 3485 Guestures.prototype.endZooming = function() { 3486 3487 var self = this; 3488 3489 var current = self.instance.current; 3490 3491 var newOffsetX, newOffsetY, newPos, reset; 3492 3493 var newWidth = self.newWidth; 3494 var newHeight = self.newHeight; 3495 3496 if ( !self.contentLastPos ) { 3497 return; 3498 } 3499 3500 newOffsetX = self.contentLastPos.left; 3501 newOffsetY = self.contentLastPos.top; 3502 3503 reset = { 3504 top : newOffsetY, 3505 left : newOffsetX, 3506 width : newWidth, 3507 height : newHeight, 3508 scaleX : 1, 3509 scaleY : 1 3510 }; 3511 3512 // Reset scalex/scaleY values; this helps for perfomance and does not break animation 3513 $.fancybox.setTranslate( self.$content, reset ); 3514 3515 if ( newWidth < self.canvasWidth && newHeight < self.canvasHeight ) { 3516 self.instance.scaleToFit( 150 ); 3517 3518 } else if ( newWidth > current.width || newHeight > current.height ) { 3519 self.instance.scaleToActual( self.centerPointStartX, self.centerPointStartY, 150 ); 3520 3521 } else { 3522 3523 newPos = self.limitPosition( newOffsetX, newOffsetY, newWidth, newHeight ); 3524 3525 $.fancybox.animate( self.$content, null, newPos, self.speed, "easeOutSine" ); 3526 3527 } 3528 3529 }; 3530 3531 Guestures.prototype.ontap = function() { 3532 3533 var self = this; 3534 3535 var instance = self.instance; 3536 var current = instance.current; 3537 3538 var x = self.endPoints[0].x; 3539 var y = self.endPoints[0].y; 3540 3541 x = x - self.$wrap.offset().left; 3542 y = y - self.$wrap.offset().top; 3543 3544 // Stop slideshow 3545 if ( instance.SlideShow && instance.SlideShow.isActive ) { 3546 instance.SlideShow.stop(); 3547 } 3548 3549 if ( !$.fancybox.isTouch ) { 3550 3551 if ( current.opts.closeClickOutside && self.$target.is('.fancybox-slide') ) { 3552 instance.close( self.startEvent ); 3553 3554 return; 3555 } 3556 3557 if ( current.type == 'image' && current.isMoved ) { 3558 3559 if ( instance.canPan() ) { 3560 instance.scaleToFit(); 3561 3562 } else if ( instance.isScaledDown() ) { 3563 instance.scaleToActual( x, y ); 3564 3565 } else if ( instance.group.length < 2 ) { 3566 instance.close( self.startEvent ); 3567 } 3568 3569 } 3570 3571 return; 3572 } 3573 3574 3575 // Double tap 3576 if ( self.tapped ) { 3577 3578 clearTimeout( self.tapped ); 3579 3580 self.tapped = null; 3581 3582 if ( Math.abs( x - self.x ) > 50 || Math.abs( y - self.y ) > 50 || !current.isMoved ) { 3583 return this; 3584 } 3585 3586 if ( current.type == 'image' && ( current.isLoaded || current.$ghost ) ) { 3587 3588 if ( instance.canPan() ) { 3589 instance.scaleToFit(); 3590 3591 } else if ( instance.isScaledDown() ) { 3592 instance.scaleToActual( x, y ); 3593 3594 } 3595 3596 } 3597 3598 } else { 3599 3600 // Single tap 3601 3602 self.x = x; 3603 self.y = y; 3604 3605 self.tapped = setTimeout(function() { 3606 self.tapped = null; 3607 3608 instance.toggleControls( true ); 3609 3610 }, 300); 3611 } 3612 3613 return this; 3614 }; 3615 3616 $(document).on('onActivate.fb', function (e, instance) { 3617 3618 if ( instance && !instance.Guestures ) { 3619 instance.Guestures = new Guestures( instance ); 3620 } 3621 3622 }); 3623 3624 $(document).on('beforeClose.fb', function (e, instance) { 3625 3626 if ( instance && instance.Guestures ) { 3627 instance.Guestures.destroy(); 3628 } 3629 3630 }); 3631 3632 3633}(window, document, window.jQuery)); 3634 3635// ========================================================================== 3636// 3637// SlideShow 3638// Enables slideshow functionality 3639// 3640// Example of usage: 3641// $.fancybox.getInstance().slideShow.start() 3642// 3643// ========================================================================== 3644;(function (document, $) { 3645 'use strict'; 3646 3647 var SlideShow = function( instance ) { 3648 3649 this.instance = instance; 3650 3651 this.init(); 3652 3653 }; 3654 3655 $.extend( SlideShow.prototype, { 3656 timer : null, 3657 isActive : false, 3658 $button : null, 3659 speed : 3000, 3660 3661 init : function() { 3662 var self = this; 3663 3664 self.$button = $('<button data-fancybox-play class="fancybox-button fancybox-button--play" title="Slideshow (P)"></button>') 3665 .appendTo( self.instance.$refs.buttons ); 3666 3667 self.instance.$refs.container.on('click', '[data-fancybox-play]', function() {
3668 self.toggle(); 3669 }); 3670 3671 }, 3672 3673 set : function() { 3674 var self = this; 3675 3676 // Check if reached last element 3677 if ( self.instance && self.instance.current && (self.instance.current.opts.loop || self.instance.currIndex < self.instance.group.length - 1 )) { 3678 3679 self.timer = setTimeout(function() { 3680 self.instance.next(); 3681 3682 }, self.instance.current.opts.slideShow.speed || self.speed); 3683 3684 } else { 3685 self.stop(); 3686 } 3687 }, 3688 3689 clear : function() { 3690 var self = this; 3691 3692 clearTimeout( self.timer ); 3693 3694 self.timer = null; 3695 }, 3696 3697 start : function() { 3698 var self = this; 3699 3700 self.stop(); 3701 3702 if ( self.instance && self.instance.current && ( self.instance.current.opts.loop || self.instance.currIndex < self.instance.group.length - 1 )) { 3703 3704 self.instance.$refs.container.on({ 3705 'beforeLoad.fb.player' : $.proxy(self, "clear"), 3706 'onComplete.fb.player' : $.proxy(self, "set"), 3707 }); 3708 3709 self.isActive = true; 3710 3711 if ( self.instance.current.isComplete ) { 3712 self.set(); 3713 } 3714 3715 self.instance.$refs.container.trigger('onPlayStart'); 3716 3717 self.$button.addClass('fancybox-button--pause'); 3718 } 3719 3720 }, 3721 3722 stop: function() { 3723 var self = this; 3724 3725 self.clear(); 3726 3727 self.instance.$refs.container 3728 .trigger('onPlayEnd') 3729 .off('.player'); 3730 3731 self.$button.removeClass('fancybox-button--pause'); 3732 3733 self.isActive = false; 3734 }, 3735 3736 toggle : function() { 3737 var self = this; 3738 3739 if ( self.isActive ) { 3740 self.stop(); 3741 3742 } else { 3743 self.start(); 3744 } 3745 } 3746 3747 }); 3748 3749 $(document).on('onInit.fb', function(e, instance) { 3750 3751 if ( instance && instance.group.length > 1 && !!instance.opts.slideShow && !instance.SlideShow ) { 3752 instance.SlideShow = new SlideShow( instance ); 3753 } 3754 3755 }); 3756 3757 $(document).on('beforeClose.fb onDeactivate.fb', function(e, instance) { 3758 3759 if ( instance && instance.SlideShow ) { 3760 instance.SlideShow.stop(); 3761 } 3762 3763 }); 3764 3765}(document, window.jQuery)); 3766 3767// ========================================================================== 3768// 3769// FullScreen 3770// Adds fullscreen functionality 3771// 3772// ========================================================================== 3773;(function (document, $) { 3774 'use strict'; 3775 3776 // Collection of methods supported by user browser 3777 var fn = (function () { 3778 3779 var fnMap = [ 3780 [ 3781 'requestFullscreen', 3782 'exitFullscreen', 3783 'fullscreenElement', 3784 'fullscreenEnabled', 3785 'fullscreenchange', 3786 'fullscreenerror' 3787 ], 3788 // new WebKit 3789 [ 3790 'webkitRequestFullscreen', 3791 'webkitExitFullscreen', 3792 'webkitFullscreenElement', 3793 'webkitFullscreenEnabled', 3794 'webkitfullscreenchange', 3795 'webkitfullscreenerror' 3796 3797 ], 3798 // old WebKit (Safari 5.1) 3799 [ 3800 'webkitRequestFullScreen', 3801 'webkitCancelFullScreen', 3802 'webkitCurrentFullScreenElement', 3803 'webkitCancelFullScreen', 3804 'webkitfullscreenchange', 3805 'webkitfullscreenerror' 3806 3807 ], 3808 [ 3809 'mozRequestFullScreen', 3810 'mozCancelFullScreen', 3811 'mozFullScreenElement', 3812 'mozFullScreenEnabled', 3813 'mozfullscreenchange', 3814 'mozfullscreenerror' 3815 ], 3816 [ 3817 'msRequestFullscreen', 3818 'msExitFullscreen', 3819 'msFullscreenElement', 3820 'msFullscreenEnabled', 3821 'MSFullscreenChange', 3822 'MSFullscreenError' 3823 ] 3824 ]; 3825 3826 var val; 3827 var ret = {}; 3828 var i, j; 3829 3830 for ( i = 0; i < fnMap.length; i++ ) { 3831 val = fnMap[ i ]; 3832 3833 if ( val && val[ 1 ] in document ) { 3834 for ( j = 0; j < val.length; j++ ) { 3835 ret[ fnMap[ 0 ][ j ] ] = val[ j ]; 3836 } 3837 3838 return ret; 3839 } 3840 } 3841 3842 return false; 3843 })(); 3844 3845 if ( !fn ) { 3846 return; 3847 } 3848 3849 var FullScreen = { 3850 request : function ( elem ) { 3851 3852 elem = elem || document.documentElement; 3853 3854 elem[ fn.requestFullscreen ]( elem.ALLOW_KEYBOARD_INPUT ); 3855 3856 }, 3857 exit : function () { 3858 document[ fn.exitFullscreen ](); 3859 }, 3860 toggle : function ( elem ) { 3861 3862 if ( this.isFullscreen() ) { 3863 this.exit(); 3864 } else { 3865 this.request( elem ); 3866 } 3867 3868 }, 3869 isFullscreen : function() { 3870 return Boolean( document[ fn.fullscreenElement ] ); 3871 }, 3872 enabled : function() { 3873 return Boolean( document[ fn.fullscreenEnabled ] ); 3874 } 3875 }; 3876 3877 $(document).on({ 3878 'onInit.fb' : function(e, instance) { 3879 var $container; 3880 3881 if ( instance && !!instance.opts.fullScreen && !instance.FullScreen) { 3882 $container = instance.$refs.container; 3883 3884 instance.$refs.button_fs = $('<button data-fancybox-fullscreen class="fancybox-button fancybox-button--fullscreen" title="Full screen (F)"></button>') 3885 .appendTo( instance.$refs.buttons ); 3886 3887 $container.on('click.fb-fullscreen', '[data-fancybox-fullscreen]', function(e) { 3888 3889 e.stopPropagation(); 3890 e.preventDefault(); 3891 3892 FullScreen.toggle( $container[ 0 ] ); 3893 3894 }); 3895 3896 if ( instance.opts.fullScreen.requestOnStart === true ) { 3897 FullScreen.request( $container[ 0 ] ); 3898 } 3899 3900 } 3901 3902 }, 'beforeMove.fb' : function(e, instance) { 3903 3904 if ( instance && instance.$refs.button_fs ) { 3905 instance.$refs.button_fs.toggle( !!instance.current.opts.fullScreen ); 3906 } 3907 3908 }, 'beforeClose.fb': function() { 3909 FullScreen.exit(); 3910 } 3911 }); 3912 3913 $(document).on(fn.fullscreenchange, function() { 3914 var instance = $.fancybox.getInstance(); 3915 var $what = instance ? instance.current.$placeholder : null; 3916 3917 if ( $what ) { 3918 3919 // If image is zooming, then this will force it to stop and reposition properly 3920 $what.css( 'transition', 'none' ); 3921 3922 instance.isAnimating = false;
3923 3924 instance.update( true, true, 0 ); 3925 } 3926 3927 }); 3928 3929}(document, window.jQuery)); 3930 3931// ========================================================================== 3932// 3933// Thumbs 3934// Displays thumbnails in a grid 3935// 3936// ========================================================================== 3937;(function (document, $) { 3938 'use strict'; 3939 3940 var FancyThumbs = function( instance ) { 3941 3942 this.instance = instance; 3943 3944 this.init(); 3945 3946 }; 3947 3948 $.extend( FancyThumbs.prototype, { 3949 3950 $button : null, 3951 $grid : null, 3952 $list : null, 3953 isVisible : false, 3954 3955 init : function() { 3956 var self = this; 3957 3958 self.$button = $('<button data-fancybox-thumbs class="fancybox-button fancybox-button--thumbs" title="Thumbnails (G)"></button>') 3959 .appendTo( this.instance.$refs.buttons ) 3960 .on('touchend click', function(e) { 3961 e.stopPropagation(); 3962 e.preventDefault(); 3963 3964 self.toggle(); 3965 }); 3966 3967 }, 3968 3969 create : function() { 3970 var instance = this.instance, 3971 list, 3972 src; 3973 3974 this.$grid = $('<div class="fancybox-thumbs"></div>').appendTo( instance.$refs.container ); 3975 3976 list = '<ul>'; 3977 3978 $.each(instance.group, function( i, item ) { 3979 3980 src = item.opts.thumb || ( item.opts.$thumb ? item.opts.$thumb.attr('src') : null ); 3981 3982 if ( !src && item.type === 'image' ) { 3983 src = item.src; 3984 } 3985 3986 if ( src && src.length ) { 3987 list += '<li data-index="' + i + '" tabindex="0" class="fancybox-thumbs-loading"><img data-src="' + src + '" /></li>'; 3988 } 3989 3990 }); 3991 3992 list += '</ul>'; 3993 3994 this.$list = $( list ).appendTo( this.$grid ).on('click touchstart', 'li', function() { 3995 3996 instance.jumpTo( $(this).data('index') ); 3997 3998 }); 3999 4000 this.$list.find('img').hide().one('load', function() { 4001 4002 var $parent = $(this).parent().removeClass('fancybox-thumbs-loading'), 4003 thumbWidth = $parent.outerWidth(), 4004 thumbHeight = $parent.outerHeight(), 4005 width, 4006 height, 4007 widthRatio, 4008 heightRatio; 4009 4010 width = this.naturalWidth || this.width; 4011 height = this.naturalHeight || this.height; 4012 4013 //Calculate thumbnail width/height and center it 4014 4015 widthRatio = width / thumbWidth; 4016 heightRatio = height / thumbHeight; 4017 4018 if (widthRatio >= 1 && heightRatio >= 1) { 4019 if (widthRatio > heightRatio) { 4020 width = width / heightRatio; 4021 height = thumbHeight; 4022 4023 } else { 4024 width = thumbWidth; 4025 height = height / widthRatio; 4026 } 4027 } 4028 4029 $(this).css({ 4030 width : Math.floor(width), 4031 height : Math.floor(height), 4032 'margin-top' : Math.min( 0, Math.floor(thumbHeight * 0.3 - he
4032ight * 0.3 ) ), 4033 'margin-left' : Math.min( 0, Math.floor(thumbWidth * 0.5 - width * 0.5 ) ) 4034 }).show(); 4035 4036 }) 4037 .each(function() { 4038 this.src = $( this ).data( 'src' ); 4039 }); 4040 4041 }, 4042 4043 focus : function() { 4044 4045 if ( this.instance.current ) { 4046 this.$list 4047 .children() 4048 .removeClass('fancybox-thumbs-active') 4049 .filter('[data-index="' + this.instance.current.index + '"]') 4050 .addClass('fancybox-thumbs-active') 4051 .focus(); 4052 } 4053 4054 }, 4055 4056 close : function() { 4057 4058 this.$grid.hide(); 4059 4060 }, 4061 4062 update : function() { 4063 4064 this.instance.$refs.container.toggleClass('fancybox-container--thumbs', this.isVisible); 4065 4066 if ( this.isVisible ) { 4067 4068 if ( !this.$grid ) { 4069 this.create(); 4070 } 4071 4072 this.$grid.show(); 4073 4074 this.focus(); 4075 4076 } else if ( this.$grid ) { 4077 this.$grid.hide(); 4078 } 4079 4080 this.instance.update(); 4081 4082 }, 4083 4084 hide : function() { 4085 4086 this.isVisible = false; 4087 4088 this.update(); 4089 4090 }, 4091 4092 show : function() { 4093 4094 this.isVisible = true; 4095 4096 this.update(); 4097 4098 }, 4099 4100 toggle : function() { 4101 4102 if ( this.isVisible ) { 4103 this.hide(); 4104 4105 } else { 4106 this.show(); 4107 } 4108 } 4109 4110 }); 4111 4112 $(document).on('onInit.fb', function(e, instance) { 4113 var first = instance.group[0], 4114 second = instance.group[1]; 4115 4116 if ( !!instance.opts.thumbs && !instance.Thumbs && instance.group.length > 1 && ( 4117 ( first.type == 'image' || first.opts.thumb || first.opts.$thumb ) && 4118 ( second.type == 'image' || second.opts.thumb || second.opts.$thumb ) 4119 ) 4120 ) { 4121 4122 instance.Thumbs = new FancyThumbs( instance ); 4123 } 4124 4125 }); 4126 4127 $(document).on('beforeMove.fb', function(e, instance, item) { 4128 var self = instance && instance.Thumbs; 4129 4130 if ( !self ) { 4131 return; 4132 } 4133 4134 if ( item.modal ) { 4135 4136 self.$button.hide(); 4137 4138 self.hide(); 4139 4140 } else { 4141 4142 if ( instance.opts.thumbs.showOnStart === true && instance.firstRun ) { 4143 self.show(); 4144 4145 } 4146 4147 self.$button.show(); 4148 4149 if ( self.isVisible ) { 4150 self.focus(); 4151 } 4152 4153 } 4154 4155 }); 4156 4157 $(document).on('beforeClose.fb', function(e, instance) { 4158 4159 if ( instance && instance.Thumbs) { 4160 if ( instance.Thumbs.isVisible && instance.opts.thumbs.hideOnClosing !== false ) { 4161 instance.Thumbs.close(); 4162 } 4163 4164 instance.Thumbs = null; 4165 } 4166 4167 }); 4168 4169}(document, window.jQuery)); 4170 4171// ========================================================================== 4172// 4173// Hash 4174// Enables linking to each modal 4175// 4176// ========================================================================== 4177;(function (document, window, $) { 4178 'use strict'; 4179 4180 // Simple $.escapeSelector polyfill (for jQuery prior v3) 4181 if ( !$.escapeSelector ) { 4182 $.escapeSelector = function( sel ) { 4183 var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g; 4184 var fcssescape = function( ch, asCodePoint ) { 4185 if ( asCodePoint ) { 4186 // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER 4187 if ( ch === "\0" ) { 4188 return "\uFFFD"; 4189 } 4190 4191 // Control characters and (dependent upon position) numbers get escaped as code points 4192 return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; 4193 } 4194 4195 // Other potentially-special ASCII characters get backslash-escaped 4196 return "\\" + ch; 4197 }; 4198 4199 return ( sel + "" ).replace( rcssescape, fcssescape ); 4200 }; 4201 } 4202 4203 // Variable containing last hash value set by fancyBox 4204 // It will be used to determine if fancyBox needs to close after hash change is detected 4205 var currentHash = null; 4206 4207 // Get info about gallery name and current index from url 4208 function parseUrl() { 4209 var hash = window.location.hash.substr( 1 ); 4210 var rez = hash.split( '-' ); 4211 var index = rez.length > 1 && /^\+?\d+$/.test( rez[ rez.length - 1 ] ) ? parseInt( rez.pop( -1 ), 10 ) || 1 : 1; 4212 var gallery = rez.join( '-' ); 4213 4214 // Index is starting from 1 4215 if ( index < 1 ) { 4216 index = 1; 4217 } 4218 4219 return { 4220 hash : hash, 4221 index : index, 4222 gallery : gallery 4223 }; 4224 } 4225 4226 // Trigger click evnt on links to open new fancyBox instance 4227 function triggerFromUrl( url ) { 4228 var $el; 4229 4230 if ( url.gallery !== '' ) { 4231 4232 // If we can find element matching 'data-fancybox' atribute, then trigger click event for that .. 4233 $el = $( "[data-fancybox='" + $.escapeSelector( url.gallery ) + "']" ).eq( url.index - 1 ); 4234 4235 if ( $el.length ) { 4236 $el.trigger( 'click' ); 4237 4238 } else { 4239 4240 // .. if not, try finding element by ID 4241 $( "#" + $.escapeSelector( url.gallery ) + "" ).trigger( 'click' ); 4242 4243 } 4244 4245 } 4246 } 4247 4248 // Get gallery name from current instance 4249 function getGallery( instance ) { 4250 var opts; 4251 4252 if ( !instance ) { 4253 return false;
4254 } 4255 4256 opts = instance.current ? instance.current.opts : instance.opts; 4257 4258 return opts.$orig ? opts.$orig.data( 'fancybox' ) : ( opts.hash || '' ); 4259 } 4260 4261 // Star when DOM becomes ready 4262 $(function() { 4263 4264 // Small delay is used to allow other scripts to process "dom ready" event 4265 setTimeout(function() { 4266 4267 // Check if this module is not disabled 4268 if ( $.fancybox.defaults.hash === false ) { 4269 return; 4270 } 4271 4272 // Check if need to close after url has changed 4273 $(window).on('hashchange.fb', function() { 4274 var url = parseUrl(); 4275 4276 if ( $.fancybox.getInstance() ) { 4277 if ( currentHash && currentHash !== url.gallery + '-' + url.index ) { 4278 currentHash = null; 4279 4280 $.fancybox.close(); 4281 } 4282 4283 } else if ( url.gallery !== '' ) { 4284 triggerFromUrl( url ); 4285 } 4286 4287 }); 4288 4289 // Update hash when opening/closing fancyBox 4290 $(document).on({ 4291 'onInit.fb' : function( e, instance ) { 4292 var url = parseUrl(); 4293 var gallery = getGallery( instance ); 4294 4295 // Make sure gallery start index matches index from hash 4296 if ( gallery && url.gallery && gallery == url.gallery ) { 4297 instance.currIndex = url.index - 1; 4298 } 4299 4300 }, 'beforeMove.fb' : function( e, instance, current ) { 4301 var gallery = getGallery( instance ); 4302 4303 // Update window hash 4304 if ( gallery && gallery !== '' ) { 4305 4306 if ( window.location.hash.indexOf( gallery ) < 0 ) { 4307 instance.opts.origHash = window.location.hash; 4308 } 4309 4310 currentHash = gallery + ( instance.group.length > 1 ? '-' + ( current.index + 1 ) : '' ); 4311 4312 if ( "pushState" in history ) { 4313 history.pushState( '', document.title, window.location.pathname + window.location.search + '#' + currentHash ); 4314 4315 } else { 4316 window.location.hash = currentHash; 4317 } 4318 4319 } 4320 4321 }, 'beforeClose.fb' : function( e, instance, current ) { 4322 var gallery = getGallery( instance ); 4323 var origHash = instance && instance.opts.origHash ? instance.opts.origHash : ''; 4324 4325 // Remove hash from location bar 4326 if ( gallery && gallery !== '' ) { 4327 if ( "pushState" in history ) { 4328 history.pushState( '', document.title, window.location.pathname + window.location.search + origHash ); 4329 4330 } else { 4331 window.location.hash = origHash; 4332 } 4333 } 4334 4335 currentHash = null; 4336 } 4337 }); 4338 4339 // Check current hash and trigger click event on matching element to start fancyBox, if needed 4340 triggerFromUrl( parseUrl() ); 4341 4342 }, 50); 4343 }); 4344 4345 4346}(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.