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