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