1/*! 2 * fancyBox - jQuery Plugin 3 * version: 3.0.0 Beta 1 (Tue, 29 Jan 2013) 4 * @requires jQuery v1.7 or later 5 * 6 * Examples at http://fancyapps.com/fancybox/ 7 * License: www.fancyapps.com/fancybox/#license 8 * 9 * Copyright 2013 Janis Skarnelis - [email protected] 10 * 11 */ 12 13(function (window, document, $, undefined) { 14 "use strict"; 15 16 var W = $(window), 17 D = $(document), 18 H = $('html'); 19 20 var F = $.fancybox = function () { 21 F.open.apply( this, arguments ); 22 }; 23 24 var isTouch = F.isTouch = (document.createTouch !== undefined || window.ontouchstart !== undefined); 25 26 var isQuery = function(object) { 27 return object && object.hasOwnProperty && object instanceof $; 28 }; 29 30 var isString = function(str) { 31 return str && $.type(str) === "string"; 32 }; 33 34 var isPercentage = function(str) { 35 return isString(str) && str.indexOf('%') > 0; 36 }; 37 38 var getScalar = function(orig, dim) { 39 var value = parseFloat(orig, 10) || 0; 40 41 if (dim && isPercentage(orig)) { 42 value = F.getViewport()[ dim ] / 100 * value; 43 } 44 45 return Math.ceil(value); 46 }; 47 48 var getValue = function(value, dim) { 49 return getScalar(value, dim) + 'px'; 50 }; 51 52 var getTime = Date.now || function() { 53 return +new Date; 54 }; 55 56 var removeWrap = function(what) { 57 var el = isString(what) ? $(what) : what; 58 59 if (el && el.length) { 60 el.removeClass('fancybox-wrap').stop(true).trigger('onReset').hide().unbind(); 61 62 try { 63 el.find('iframe').unbind().attr('src', isTouch ? '' : '//about:blank'); 64 65 // Give the document in the iframe to get a chance to unload properly before remove 66 setTimeout(function () { 67 el.empty().remove(); 68 69 // Remove the lock if there are no elements 70 if (F.lock && !F.coming && !F.current) { 71 var scrollV, scrollH; 72 73 $('.fancybox-margin').removeClass('fancybox-margin'); 74 75 scrollV = W.scrollTop(); 76 scrollH = W.scrollLeft(); 77 78 H.removeClass('fancybox-lock'); 79 80 F.lock.remove(); 81 82 F.lock = null; 83 84 W.scrollTop( scrollV ).scrollLeft( scrollH ); 85 } 86 }, 150); 87 88 } catch(e) {} 89 } 90 }; 91 92 $.extend(F, { 93 // The current version of fancyBox 94 version: '3.0.0', 95 96 defaults: { 97 theme : 'default', // 'default', dark', 'light' 98 padding : 0, // space inside box, around content 99 margin : [30, 55, 30, 55], // space between viewport and the box 100 loop : true, // Continuous gallery item loop 101 102 arrows : true, 103 closeBtn : true, 104 expander : !isTouch, 105 106 caption : { 107 type : 'over' // 'float', 'inside', 'outside' or 'over', 108 }, 109 110 overlay : { 111 closeClick : true, // if true, fancyBox will be closed when user clicks on the overlay 112 speedIn : 0, // duration of fadeIn animation 113 speedOut : 250, // duration of fadeOut animation 114 showEarly : true, // indicates if should be opened immediately or wait until the content is ready 115 css : {} // custom CSS properties 116 }, 117 118 helpers : {}, // list of enabled helpers 119 120 // Dimensions 121 width : 800, 122 height : 450, 123 minWidth : 100, 124 minHeight : 100, 125 maxWidth : 99999, 126 maxHeight : 99999, 127 aspectRatio : false, 128 fitToView : true, 129 130 autoHeight : true, 131 autoWidth : true, 132 autoResize : true, 133 134 // Location 135 autoCenter : !isTouch, 136 topRatio : 0.5, 137 leftRatio : 0.5, 138 139 // Opening animation 140 openEffect : 'elastic', // 'elastic', 'fade', 'drop' or 'none' 141 openSpeed : 350, 142 openEasing : 'easeOutQuad', 143 144 // Closing animation 145 closeEffect : 'elastic', // 'elastic', 'fade', 'drop' or 'none' 146 closeSpeed : 350, 147 closeEasing : 'easeOutQuad', 148 149 // Animation for next gallery item 150 nextEffect : 'elastic', // 'elastic', 'fade', 'drop' or 'none' 151 nextSpeed : 350, 152 nextEasing : 'easeOutQuad', 153 154 // Animation for previous gallery item 155 prevEffect : 'elastic', // 'elastic', 'fade', 'drop' or 'none' 156 prevSpeed : 350, 157 prevEasing : 'easeOutQuad', 158 159 // Slideshow 160 autoPlay : false, 161 playSpeed : 3000, 162 163 /* 164 Advanced 165 */ 166 167 // Callbacks 168 onCancel : $.noop, // If canceling 169 beforeLoad : $.noop, // Before loading 170 afterLoad : $.noop, // After loading 171 beforeShow : $.noop, // Before changing in current item 172 afterShow : $.noop, // After opening 173 beforeClose : $.noop, // Before closing 174 afterClose : $.noop, // After closing 175 176 // Properties specific to content type 177 ajax : { 178 dataType : 'html', 179 headers : { 'X-fancyBox': true } 180 }, 181 182 iframe : { 183 scrolling : 'auto', 184 preload : true 185 }, 186 187 swf : { 188 wmode : 'transparent', 189 allowfullscreen : 'true', 190 allowscriptaccess : 'always' 191 }, 192 193 // Default keyboard 194 keys : { 195 next : { 196 13 : 'left', // enter 197 34 : 'up', // page down 198 39 : 'left', // right arrow 199 40 : 'up' // down arrow 200 }, 201 prev : { 202 8 : 'right', // backspace 203 33 : 'down', // page up 204 37 : 'right', // left arrow 205 38 : 'down' // up arrow 206 }, 207 close : [27], // escape key 208 play : [32], // space - start/stop slideshow 209 toggle : [70] // letter "f" - toggle fullscreen 210 }, 211 212 // Default direction 213 direction : { 214 next : 'left', 215 prev : 'right' 216 }, 217 218 // HTML templates 219 tpl: { 220 wrap : '<div class="fancybox-wrap" tabIndex="-1"><div class="fancybox-inner"></div></div>', 221 iframe : '<iframe id="fancybox-frame{rnd}" name="fancybox-frame{rnd}" class="fancybox-iframe" frameborder="0" vspace="0" hspace="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen allowtransparency="true"></iframe>', 222 error : '<p class="fancybox-error">{{ERROR}}</p>', 223 closeBtn : '<a title="{{CLOSE}}" class="fancybox-close" href="javascript:;"></a>', 224 next : '<a title="{{NEXT}}" class="fancybox-nav fancybox-next" href="javascript:;"><span></span></a>', 225 prev : '<a title="{{PREV}}" class="fancybox-nav fancybox-prev" href="javascript:;"><span></span></a>' 226 }, 227 228 // Localization 229 locale : 'en', 230 locales : { 231 'en' : { 232 CLOSE : 'Close', 233 NEXT : 'Next', 234 PREV : 'Previous', 235 ERROR : 'The requested content cannot be loaded. <br/> Please try again later.', 236 EXPAND : 'Display actual size', 237 SHRINK : 'Fit to the viewport', 238 PLAY_START : 'Start slideshow', 239 PLAY_STOP : 'Pause slideshow' 240 }, 241 'de' : { 242 CLOSE : 'Schliessen', 243 NEXT : 'Vorwärts', 244 PREV : 'Zurück', 245 ERROR : 'Die angeforderten Daten konnten nicht geladen werden. <br/>
245 Bitte versuchen Sie es später nochmal.', 246 EXPAND : '', 247 SHRINK : '', 248 PLAY_START : '', 249 PLAY_STOP : '' 250 } 251 }, 252 253 // Override some properties 254 index : 0, 255 content : null, 256 href : null, 257 258 // Various 259 wrapCSS : '', // CSS class name for the box 260 modal : false, 261 locked : false, 262 preload : 3, // Number of gallery images to preload 263 mouseWheel : true, // Enable or disable mousewheel support 264 scrolling : 'auto', // 'yes', 'no', any valid value for CSS "overflow" property 265 scrollOutside : true // If trye, fancyBox will try to set scrollbars outside the content 266 }, 267 268 // Current state 269 current : null, 270 coming : null, 271 group : [], 272 index : 0, 273 isActive : false, // Is activated 274 isOpen : false, // Is currently open 275 isOpened : false, // Have been fully opened at least once 276 isMaximized : false, 277 278 player : { 279 timer : null, 280 isActive : false 281 }, 282 283 // Loaders 284 ajaxLoad : null, 285 imgPreload : null, 286 287 // Object containing all helpers 288 helpers : {}, 289 290 // Open fancyBox 291 open: function( items, options ) { 292 if (!items) { 293 return; 294 } 295 296 // Close if already active 297 if (false === F.close(true)) { 298 return; 299 } 300 301 if (!$.isPlainObject( options )) { 302 options = {}; 303 } 304 305 F.opts = $.extend(true, {}, F.defaults, options); 306 307 F.populate( items ); 308 309 if (F.group.length) { 310 F._start( F.opts.index ); 311 } 312 }, 313 314 // Add new items to the group 315 populate : function( items ) { 316 var group = []; 317 318 if ( !$.isArray( items )) { 319 items = [ items ]; 320 } 321 322 // Build group array, each item is object containing element 323 // and most important attributes - href, title and type 324 $.each(items, function(i, element) { 325 var defaults = $.extend(true, {}, F.opts), 326 item, 327 obj, 328 type, 329 margin, 330 padding; 331 332 if ($.isPlainObject(element)) { 333 item = element; 334 335 } else if (isString(element)) { 336 item = { href : element }; 337 338 } else if (isQuery(element) || $.type(element) === 'object' && element.nodeType) { 339 obj = $(element); 340 item = $(obj).get(0); 341 342 if (!item.href) { 343 item = { href : element }; 344 } 345 346 item = $.extend({ 347 href : obj.data('fancybox-href') || obj.attr('href') || item.href, 348 title : obj.data('fancybox-title') || obj.attr('title') || item.title, 349 type : obj.data('fancybox-type'), 350 element : obj 351 }, obj.data('fancybox-options') ); 352 353 } else { 354 return; 355 } 356 357 // If the type has not specified, then try to guess 358 if (!item.type && (item.content || item.href)) { 359 item.type = item.content ? "html" : F.guessType( obj, item.href ); 360 } 361 362 // Adjust some defaults depending on content type 363 type = item.type || F.opts.type; 364 365 if (type === 'image' || type === 'swf') { 366 defaults.autoWidth = defaults.autoHeight = false; 367 defaults.scrolling = 'visible'; 368 } 369 370 if (type === 'image') { 371 defaults.aspectRatio = true; 372 } 373 374 if (type === 'iframe') { 375 defaults.autoWidth = false; 376 defaults.scrolling = isTouch ? 'scroll' : 'visible'; 377 } 378 379 if (items.length < 2) { 380 defaults.margin = 30; 381 } 382 383 item = $.extend(true, {}, defaults, item); 384 385 // Recheck some parameters 386 margin = item.margin; 387 padding = item.padding; 388 389 // Convert margin and padding properties to array - top, right, bottom, left 390 if ($.type(margin) === 'number') { 391 item.margin = [margin, margin, margin, margin]; 392 } 393 394 if ($.type(padding) === 'number') { 395 item.padding = [padding, padding, padding, padding]; 396 } 397 398 // 'modal' propery is just a shortcut 399 if (item.modal) { 400 $.extend(true, item, { 401 closeBtn : false, 402 closeClick : false, 403 nextClick : false, 404 arrows : false, 405 mouseWheel : false, 406 keys : null, 407 overlay : { 408 closeClick : false 409 } 410 }); 411 } 412 413 if (item.autoSize !== undefined) { 414 item.autoWidth = item.autoHeight = !!item.autoSize; 415 } 416 417 if (item.width === 'auto') { 418 item.autoWidth = true; 419 } 420 421 if (item.height === 'auto') { 422 item.autoHeight = true; 423 } 424 425 group.push( item ); 426 }); 427 428 F.group = F.group.concat( group ); 429 }, 430 431 // Cancel image loading and abort ajax request 432 cancel: function () { 433 var coming = F.coming; 434 435 if (!coming || false === F.trigger('onCancel')) { 436 return; 437 } 438 439 F.hideLoading(); 440 441 if (F.ajaxLoad) { 442 F.ajaxLoad.abort(); 443 } 444 445 if (F.imgPreload) { 446 F.imgPreload.onload = F.imgPreload.onerror = null; 447 } 448 449 if (coming.wrap) { 450 removeWrap( coming.wrap ); 451 } 452 453 F.ajaxLoad = F.imgPreload = F.coming = null; 454 455 // If the first item has been canceled, then clear everything 456 if (!F.current) { 457 F._afterZoomOut( coming ); 458 } 459 }, 460 461 // Start closing or remove immediately if is opening/closing 462 close: function (e) { 463 if (e && $.type(e) === 'object') { 464 e.preventDefault(); 465 } 466 467 F.cancel(); 468
469 // Do not close if: 470 // - the script has not been activated 471 // - cancel event has triggered opening a new item 472 // - "beforeClose" trigger has returned false 473 if (!F.isActive || F.coming || false === F.trigger('beforeClose')) { 474 return; 475 } 476 477 F.unbind(); 478 479 F.isClosing = true; 480 481 if (F.lock) { 482 F.lock.css('overflow', 'hidden'); 483 } 484 485 if (!F.isOpen || e === true) { 486 F._afterZoomOut(); 487 488 } else { 489 F.isOpen = F.isOpened = false; 490 491 F.transitions.close(); 492 } 493 }, 494 495 prev : function( direction ) { 496 var current = F.current; 497 498 if (current) { 499 F.jumpto( current.index - 1, (isString(direction) ? direction : current.direction.prev) ); 500 } 501 }, 502 503 next : function( direction ) { 504 var current = F.current; 505 506 if (current) { 507 F.jumpto( current.index + 1, (isString(direction) ? direction : current.direction.next) ); 508 } 509 }, 510 511 jumpto : function( index, direction ) { 512 var current = F.current; 513 514 if (!(F.coming && F.coming.index === index)) { 515 F.cancel(); 516 517 if (current.index == index) { 518 direction = null; 519 520 } else if (!direction) { 521 direction = current.direction[ index > current.index ? 'next' : 'prev' ]; 522 } 523 524 F.direction = direction; 525 526 F._start( index ); 527 } 528 } 529 }); 530 531 $.extend(F, { 532 guessType : function(item, href) { 533 var rez = item && item.prop('class') ? item.prop('class').match(/fancybox\.(\w+)/) : 0, 534 type = false;
535 536 if (rez) { 537 return rez[1]; 538 } 539 540 if (isString(href)) { 541 if (href.match(/(^data:image\/.*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp)((\?|#).*)?$)/i)) { 542 type = 'image'; 543 544 } else if (href.match(/\.(swf)((\?|#).*)?$/i)) { 545 type = 'swf'; 546 547 } else if (href.charAt(0) === '#') { 548 type = 'inline'; 549 } 550 551 } else if (isString(item)) { 552 type = 'html'; 553 } 554 555 return type; 556 }, 557 558 trigger: function (event, o) { 559 var ret, obj = o || F.coming || F.current; 560 561 if (!obj) { 562 return; 563 } 564 565 if ($.isFunction( obj[event] )) { 566 ret = obj[event].apply(obj, Array.prototype.slice.call(arguments, 1)); 567 } 568 569 // Cancel further execution if afterClose callback has opened new instance 570 if (ret === false || (event === 'afterClose' && F.isActive) ) { 571 return false; 572 } 573 574 if (obj.helpers) { 575 $.each(obj.helpers, function (helper, opts) { 576 var helperObject = F.helpers[helper], 577 helperOpts; 578 579 if (opts && helperObject && $.isFunction(helperObject[event])) { 580 helperOpts = $.extend(true, {}, helperObject.defaults, opts); 581 582 helperObject.opts = helperOpts; 583 584 helperObject[event](helperOpts, obj ); 585 } 586 }); 587 } 588 589 $.event.trigger(event); 590 }, 591 592 // Center inside viewport 593 reposition: function (e, object) { 594 var obj = object || F.current, 595 wrap = obj && obj.wrap, 596 pos; 597 598 if (F.isOpen && wrap) { 599 pos = F._getPosition( obj ); 600 601 if (e === false || (e && e.type === 'scroll')) { 602 wrap.stop(true).animate(pos, 200).css('overflow', 'visible'); 603 604 } else { 605 wrap.css(pos); 606 } 607 } 608 }, 609 610 update: function (e) { 611 var type = (e && e.type), 612 timeNow = getTime(), 613 current = F.current, 614 width; 615 616 if (!current || !F.isOpen ) { 617 return; 618 } 619 620 if (type === 'scroll') { 621 if (F.wrap.outerHeight(true) > F.getViewport().h) { 622 return; 623 } 624 625 if (F.didUpdate) { 626 clearTimeout( F.didUpdate ); 627 } 628 629 F.didUpdate = setTimeout(function() { 630 F.reposition(e); 631 632 F.didUpdate = null; 633 }, 50); 634 635 return; 636 } 637 638 if (F.lock) { 639 F.lock.css('overflow', 'hidden'); 640 } 641 642 F._setDimension(); 643 644 F.reposition(e); 645 646 if (F.lock) { 647 F.lock.css('overflow', 'auto'); 648 } 649 650 // Re-center float type caption 651 if (current.caption.type === 'float') { 652 width = F.getViewport().w - (F.wrap.outerWidth(true) - F.inner.width() ); 653 654 current.caption.wrap.css('width', width).css('marginLeft', (width * 0.5 - F.inner.width() * 0.5) * -1 ); 655 } 656 657 if (current.expander) { 658 if ( current.canShrink) { 659 $(".fancybox-expand").show().attr('title', current.locales[ current.locale ].SHRINK ); 660 661 } else if (current.canExpand) { 662 $(".fancybox-expand").show().attr('title', current.locales[ current.locale ].EXPAND ); 663 664 } else { 665 $(".fancybox-expand").hide(); 666 } 667 } 668 669 F.trigger('onUpdate'); 670 }, 671 672 // Shrink content to fit inside viewport or restore if resized 673 toggle: function ( action ) { 674 var current = F.current; 675 676 if (current && F.isOpen) { 677 F.current.fitToView = $.type(action) === "boolean" ? action : !F.current.fitToView; 678 679 F.update( true ); 680 } 681 }, 682 683 hideLoading: function () { 684 $('#fancybox-loading').remove(); 685 }, 686 687 showLoading: function () { 688 var el, view; 689 690 F.hideLoading(); 691 692 el = $('<div id="fancybox-loading"></div>').click(F.cancel).appendTo('body'); 693 694 if (!F.defaults.fixed) { 695 view = F.getViewport(); 696 697 el.css({ 698 position : 'absolute', 699 top : (view.h * 0.5) + view.y, 700 left : (view.w * 0.5) + view.x 701 }); 702 } 703 }, 704 705 getViewport: function () { 706 var rez; 707 708 if (F.lock) { 709 rez = { 710 x: F.lock.scrollLeft(), 711 y: F.lock.scrollTop(), 712 w: F.lock[0].clientWidth, 713 h: F.lock[0].clientHeight 714 }; 715 716 } else { 717 rez = { 718 x: W.scrollLeft(), 719 y: W.scrollTop(), 720 721 // See http://bugs.jquery.com/ticket/6724 722 w : isTouch && window.innerWidth ? window.innerWidth : W.width(), 723 h : isTouch && window.innerHeight ? window.innerHeight : W.height() 724 }; 725 } 726 727 return rez; 728 }, 729 730 unbind : function() { 731 if (isQuery(F.wrap)) { 732 F.wrap.unbind('.fb'); 733 } 734 735 if (isQuery(F.inner)) { 736 F.inner.unbind('.fb'); 737 } 738 739 D.unbind('.fb'); 740 W.unbind('.fb'); 741 }, 742 743 rebind: function () { 744 var current = F.current, 745 keys; 746 747 F.unbind(); 748 749 if (!current || !F.isOpen) { 750 return; 751 } 752 753 // Changing document height on iOS devices triggers a 'resize' event, 754 // that can change document height... repeating infinitely 755 W.bind('orientationchange.fb' + (isTouch ? '' : ' resize.fb') + (current.autoCenter && !current.locked ? ' scroll.fb' : ''), F.update); 756 757 keys = current.keys; 758 759 if (keys) { 760 D.bind('keydown.fb', function (e) { 761 var code = e.which || e.keyCode, 762 target = e.target || e.srcElement; 763 764 // Skip esc key if loading, because showLoading will cancel preloading 765 if (code === 27 && F.coming) { 766 return false;
767 } 768 769 // Ignore key combinations and key events within form elements 770 if (!e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey && !(target && (target.type || $(target).is('[contenteditable]')))) { 771 $.each(keys, function(i, val) { 772 //if (current.group.length > 1 && val[ code ] !== undefined) { 773 if (val[ code ] !== undefined) { 774 e.preventDefault(); 775 776 if (current.group.length > 1) { 777 F[ i ]( val[ code ] ); 778 } 779 780 return false; 781 } 782 783 if ($.inArray(code, val) > -1) { 784 e.preventDefault(); 785 786 if (i === 'play') { 787 F.slideshow.toggle(); 788 } else { 789 F[ i ] (); 790 } 791 792 return false; 793 } 794 }); 795 } 796 }); 797 } 798 799 F.lastScroll = getTime(); 800 801 if (current.mouseWheel && F.group.length > 1) { 802 F.wrap.bind('DOMMouseScroll.fb mousewheel.fb MozMousePixelScroll.fb', function (event) { 803 var e = event.originalEvent, 804 el = e.target || 0, 805 delta = (e.wheelDelta || e.detail || 0), 806 deltaX = e.wheelDeltaX || 0, 807 deltaY = e.wheelDeltaY || 0, 808 now = getTime(); 809 810 if (((el && el.style && !(el.style.overflow && el.style.overflow === 'hidden') && ((el.clientWidth && el.scrollWidth > el.clientWidth) || (el.clientHeight && el.scrollHeight > el.clientHeight)))) ) { 811 return; 812 } 813 814 if (delta === 0 || (F.current && F.current.canShrink)) { 815 return; 816 } 817 818 e.stopPropagation(); 819 820 if (F.lastScroll && (now - F.lastScroll) < 80) { 821 F.lastScroll = now; 822 return; 823 } 824 825 F.lastScroll = now; 826 827 if (e.axis) { 828 if (e.axis === e.HORIZONTAL_AXIS) { 829 deltaX = delta * -1; 830 831 } else if (e.axis === e.VERTICAL_AXIS) { 832 deltaY = delta * -1; 833 } 834 } 835 836 if ( deltaX === 0 ) { 837 if (deltaY > 0) { 838 F.prev( 'down' ); 839 840 } else { 841 F.next( 'up' ); 842 } 843 844 } else { 845 if (deltaX > 0) { 846 F.prev( 'right' ); 847 848 } else { 849 F.next( 'left' ); 850 } 851 } 852 }); 853 } 854 855 F.touch.init(); 856 }, 857 858 rebuild : function() { 859 var current = F.current; 860 861 current.wrap.find('.fancybox-nav, .fancybox-close, .fancybox-expand').remove(); 862 863 // Create navigation arrows 864 if (current.arrows && F.group.length > 1) { 865 if (current.loop || current.index > 0) { 866 $( F._translate( current.tpl.prev) ).appendTo(F.inner).bind('click.fb', F.prev); 867 } 868 869 if (current.loop || current.index < F.group.length - 1) { 870 $( F._translate( current.tpl.next) ).appendTo(F.inner).bind('click.fb', F.next); 871 } 872 } 873 874 // Create a close button 875 if (current.closeBtn) { 876 $( F._translate( current.tpl.closeBtn) ).appendTo(F.wrap).bind('click.fb', F.close); 877 } 878 879 // Add expand button to image 880 if (current.expander && current.type === 'image') { 881 $('<a title="Expand image" class="fancybox-expand" href="javascript:;"></a>') 882 .appendTo( F.inner ) 883 .bind('click.fb', F.toggle); 884 885 if ( !current.canShrink && !current.canExpand) { 886 887 } 888 } 889 }, 890 891 // Create upcoming object and prepare for loading the content 892 _start: function( index ) { 893 var coming, 894 type; 895 896 // Check index and get object from the groups 897 if (F.opts.loop) { 898 if (index < 0) { 899 index = F.group.length + (index % F.group.length); 900 } 901 902 index = index % F.group.length; 903 } 904 905 coming = F.group[ index ]; 906 907 if (!coming) { 908 return false; 909 } 910 911 // Add all properties 912 coming = $.extend(true, {}, F.opts, coming); 913 914 /* 915 * Add reference to the group, so it`s possible to access from callbacks, example: 916 * afterLoad : function() { 917 * this.title = 'Image ' + (this.index + 1) + ' of ' + this.group.length + (this.title ? ' - ' + this.title : ''); 918 * } 919 */ 920 921 coming.group = F.group; 922 coming.index = index; 923 924 // Give a chance for callback or helpers to update coming item (type, title, etc) 925 F.coming = coming; 926 927 if (false === F.trigger('beforeLoad')) { 928 F.coming = null; 929 930 return; 931 } 932 933 F.isActive = true; 934 935 // Build the neccessary markup 936 F._build(); 937 938 // If user will press the escape-button, the request will be canceled 939 D.bind('keydown.loading', function(e) { 940 if ((e.which || e.keyCode) === 27) { 941 D.unbind('.loading'); 942 943 e.preventDefault(); 944 945 F.cancel(); 946 } 947 }); 948 949 // Show overlay 950 if (coming.overlay && coming.overlay.showEarly) { 951 F.overlay.open( coming.overlay ); 952 } 953 954 // Load content 955 type = coming.type; 956 957 if (type === 'image') { 958 F._loadImage(); 959 960 } else if (type === 'ajax') { 961 F._loadAjax(); 962 963 } else if (type === 'iframe') { 964 F._loadIframe(); 965 966 } else if (type === 'inline') { 967 F._loadInline(); 968 969 } else if (type === 'html' || type === 'swf') { 970 F._afterLoad(); 971 972 } else { 973 F._error(); 974 } 975 }, 976 977 _build : function() { 978 var coming = F.coming, 979 captionType = coming.caption.type, 980 wrap, 981 inner, 982 scrollV, 983 scrollH; 984
985 coming.wrap = wrap = $('<div class="fancybox-wrap"></div>').appendTo( coming.parent || 'body' ).addClass('fancybox-' + coming.theme); 986 coming.inner = inner = $('<div class="fancybox-inner"></div>').appendTo( wrap ); 987 988 coming[ captionType === 'outside' || captionType === 'float' ? 'inner' : 'wrap' ].addClass('fancybox-skin fancybox-' + coming.theme + '-skin'); 989 990 if (coming.locked && coming.overlay && F.defaults.fixed) { 991 if (!F.lock) { 992 F.lock = $('<div id="fancybox-lock"></div>').appendTo( wrap.parent() ); 993 } 994 995 F.lock.unbind().append( wrap ); 996 997 if (coming.overlay.closeClick) { 998 F.lock.click(function(e) { 999 if ($(e.target).is(F.lock)) { 1000 F.close(); 1001 } 1002 }); 1003 } 1004 1005 // Compensate missing page scrolling by increasing margin 1006 if (D.height() > W.height() || H.css('overflow-y') === 'scroll') { 1007 $('*:visible').filter(function(){ 1008 return ($(this).css('position') === 'fixed' && !$(this).hasClass("fancybox-overlay") && $(this).attr('id') !== "fancybox-lock"); 1009 }).addClass('fancybox-margin'); 1010 1011 H.addClass('fancybox-margin'); 1012 } 1013 1014 // Workaround for FF jumping bug 1015 scrollV = W.scrollTop(); 1016 scrollH = W.scrollLeft(); 1017 1018 H.addClass('fancybox-lock'); 1019 1020 W.scrollTop( scrollV ).scrollLeft( scrollH ); 1021 } 1022 1023 F.trigger('onReady'); 1024 }, 1025 1026 _error: function ( type ) { 1027 if (!F.coming) { 1028 return; 1029 } 1030 1031 $.extend(F.coming, { 1032 type : 'html', 1033 autoWidth : true, 1034 autoHeight : true, 1035 closeBtn : true, 1036 minWidth : 0, 1037 minHeight : 0, 1038 padding : [15, 15, 15, 15], 1039 scrolling : 'visible', 1040 hasError : type, 1041 content : F._translate( F.coming.tpl.error ) 1042 }); 1043 1044 F._afterLoad(); 1045 }, 1046 1047 _loadImage: function () { 1048 // Reset preload image so it is later possible to check "complete" property 1049 var img = F.imgPreload = new Image(); 1050 1051 img.onload = function () { 1052 this.onload = this.onerror = null; 1053 1054 $.extend(F.coming, { 1055 width : this.width, 1056 height : this.height, 1057 content : $(this).addClass('fancybox-image') 1058 }); 1059 1060 F._afterLoad(); 1061 }; 1062 1063 img.onerror = function () { 1064 this.onload = this.onerror = null; 1065 1066 F._error( 'image' ); 1067 }; 1068 1069 img.src = F.coming.href; 1070 1071 if (img.complete !== true || img.width < 1) { 1072 F.showLoading(); 1073 } 1074 }, 1075 1076 _loadAjax: function () { 1077 var coming = F.coming, 1078 href = coming.href, 1079 hrefParts, 1080 selector; 1081 1082 hrefParts = href.split(/\s+/, 2); 1083 href = hrefParts.shift(); 1084 selector = hrefParts.shift(); 1085 1086 F.showLoading(); 1087 1088 F.ajaxLoad = $.ajax($.extend({}, coming.ajax, { 1089 url: coming.href, 1090 error: function (jqXHR, textStatus) { 1091 if (F.coming && textStatus !== 'abort') { 1092 F._error( 'ajax', jqXHR ); 1093 1094 } else { 1095 F.hideLoading(); 1096 } 1097 }, 1098 success: function (data, textStatus) { 1099 if (textStatus === 'success') { 1100 if (selector) { 1101 data = $('<div>').html(data).find(selector); 1102 } 1103 1104 coming.content = data; 1105 1106 F._afterLoad(); 1107 } 1108 } 1109 })); 1110 }, 1111 1112 _loadIframe: function() { 1113 var coming = F.coming, 1114 iframe; 1115 1116 coming.content = iframe = $(coming.tpl.iframe.replace(/\{rnd\}/g, new Date().getTime())) 1117 .attr('scrolling', isTouch ? 'auto' : coming.iframe.scrolling); 1118 1119 if (coming.iframe.preload) { 1120 F.showLoading(); 1121 1122 F._setDimension( coming ); 1123 1124 coming.wrap.addClass('fancybox-tmp'); 1125 1126 iframe.one('load.fb', function() { 1127 if (coming.iframe.preload) { 1128 $(this).data('ready', 1); 1129 1130 $(this).bind('load.fb', F.update); 1131 1132 F._afterLoad(); 1133 } 1134 }); 1135 } 1136 1137 iframe.attr('src', coming.href).appendTo(coming.inner); 1138 1139 if (!coming.iframe.preload) { 1140 F._afterLoad(); 1141 1142 } else if (iframe.data('ready') !== 1) { 1143 F.showLoading(); 1144 } 1145 }, 1146 1147 _loadInline : function() { 1148 var coming = F.coming, 1149 href = coming.href; 1150 1151 coming.content = $( isString(href) ? href.replace(/.*(?=#[^\s]+$)/, '') : href ); //strip for ie7 1152 1153 if (coming.content.length) { 1154 F._afterLoad(); 1155 1156 } else { 1157 F._error(); 1158 } 1159 }, 1160 1161 _preloadImages: function() { 1162 var group = F.group, 1163 current = F.current, 1164 len = group.length, 1165 cnt = current.preload ? Math.min(current.preload, len - 1) : 0, 1166 item, 1167 i; 1168 1169 for (i = 1; i <= cnt; i += 1) { 1170 item = group[ (current.index + i ) % len ]; 1171 1172 if (item && item.type === 'image' && item.href) { 1173 new Image().src = item.href; 1174 } 1175 } 1176 }, 1177 1178 _afterLoad : function() { 1179 var current = F.coming, 1180 previous = F.current; 1181 1182 D.unbind('.loading'); 1183 1184 if (!current || F.isActive === false || false === F.trigger('afterLoad', current, previous)) { 1185 F.hideLoading(); 1186 1187 if (current && current.wrap) { 1188 removeWrap( current.wrap ); 1189 } 1190 1191 if (!previous) { 1192 F._afterZoomOut( current ); 1193 } 1194 1195 F.coming = null; 1196 1197 return; 1198 } 1199 1200 $.extend(F, { 1201 wrap : current.wrap.addClass('fancybox-type-' + current.type + ' fancybox-' + (isTouch ? 'mobile' : 'desktop') + ' fancybox-' + current.theme + '-' + (isTouch ? 'mobile' : 'desktop') + ' ' + current.wrapCSS), 1202 inner : current.inner, 1203 current : current, 1204 previous : previous 1205 }); 1206 1207 // Set content, margin/padding, caption, etc 1208 F._prepare(); 1209 1210 // Give a chance for helpers or callbacks to update elements 1211 F.trigger('beforeShow', current, previous); 1212 1213 F.isOpen = false;
1214 F.coming = null; 1215 1216 // Set initial dimension 1217 F._setDimension(); 1218 1219 F.hideLoading(); 1220 1221 // Open overlay if is not yet open 1222 if (current.overlay && !F.overlay.el) { 1223 F.overlay.open( current.overlay ); 1224 } 1225 1226 F.transitions.open(); 1227 }, 1228 1229 _prepare : function() { 1230 var current = F.current, 1231 content = current.content || '', 1232 wrap = current.wrap, 1233 inner = current.inner, 1234 margin = current.margin, 1235 padding = current.padding, 1236 href = current.href, 1237 type = current.type, 1238 scrolling = current.scrolling, 1239 caption = current.caption, 1240 captionText = current.title, 1241 captionType = caption.type, 1242 placeholder = 'fancybox-placeholder', 1243 display = 'fancybox-display', 1244 embed; 1245 1246 if (type !== 'iframe' && isQuery(content) && content.length) { 1247 if (!content.data(placeholder)) { 1248 content.data(display, content.css('display')) 1249 .data(placeholder, $('<div class="' + placeholder + '"></div>').insertAfter( content ).hide() ); 1250 } 1251 1252 content = content.show().detach(); 1253 1254 current.wrap.bind('onReset', function () { 1255 if ($(this).find(content).length) { 1256 content.css('display', content.data(display)) 1257 .replaceAll( content.data(placeholder) ) 1258 .data(placeholder, false) 1259 .data(display, false); 1260 } 1261 }); 1262 } 1263 1264 if (type === 'swf') { 1265 content = '<object id="fancybox-swf" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="100%" height="100%"><param name="movie" value="' + href + '"></param>'; 1266 embed = ''; 1267 1268 $.each(current.swf, function(name, val) { 1269 content += '<param name="' + name + '" value="' + val + '"></param>'; 1270 embed += ' ' + name + '="' + val + '"'; 1271 }); 1272 1273 content += '<embed src="' + href + '" type="application/x-shockwave-flash" width="100%" height="100%"' + embed + '></embed></object>'; 1274 } 1275 1276 if (!(isQuery(content) && content.parent().is(current.inner))) { 1277 current.inner.append( content ); 1278 1279 current.content = current.inner.children(':last'); 1280 } 1281 1282 // Add margin / padding 1283 $.each(["Top", "Right", "Bottom", "Left"], function(i, v) { 1284 if (margin[ i ]) { 1285 wrap.css('margin' + v, getValue(margin[ i ])); 1286 } 1287 1288 if (padding[ i ]) { 1289 if (!(v === 'Bottom' && captionType === 'outside')) { 1290 wrap.css('padding' + v, getValue(padding[ i ]) ); 1291 } 1292 1293 if (captionType === 'outside' || captionType === 'float') { 1294 1295 inner.css('border' + v + 'Width', getValue(padding[ i ])); 1296 1297 if (v === 'Top' || v === 'Left') { 1298 inner.css('margin' + v, getValue(padding[ i ] * -1)); 1299 } 1300 } 1301 } 1302 }); 1303 1304 // Add caption 1305 if ($.isFunction(captionText)) { 1306 captionText = captionText.call(current.element, current); 1307 } 1308 1309 if (isString(captionText) && $.trim(captionText) !== '') { 1310 current.caption.wrap = $('<div class="fancybox-title fancybox-title-' + captionType + '-wrap">' + captionText + '</div>').appendTo( current[ captionType === 'over' ? 'inner' : 'wrap' ] ); 1311 1312 if (captionType === 'float') { 1313 current.caption.wrap.width( F.getViewport().w - (F.wrap.outerWidth(true) - F.inner.width() ) ).wrapInner('<div></div>'); 1314 } 1315 } 1316 }, 1317 1318 _setDimension: function( object ) { 1319 var view = F.getViewport(), 1320 current = object || F.current, 1321 wrap = current.wrap, 1322 inner = current.inner, 1323 width = current.width, 1324 height = current.height, 1325 minWidth = current.minWidth, 1326 minHeight = current.minHeight, 1327 maxWidth = current.maxWidth, 1328 maxHeight = current.maxHeight, 1329 margin = current.margin, 1330 scrollOut = current.scrollOutside ? current.scrollbarWidth : 0, 1331 margin = current.margin, 1332 padding = current.padding, 1333 scrolling = current.scrolling, 1334 steps = 1, 1335 scrollingX, 1336 scrollingY, 1337 hSpace, 1338 wSpace, 1339 origWidth, 1340 origHeight, 1341 ratio, 1342 iframe, 1343 body, 1344 maxWidth_, 1345 maxHeight_, 1346 width_, 1347 height_, 1348 canShrink, 1349 canExpand; 1350 1351 // Set scrolling 1352 scrolling = scrolling.split(','); 1353 scrollingX = scrolling[0]; 1354 scrollingY = scrolling[1] || scrollingX; 1355 1356 current.inner 1357 .css('overflow-x', scrollingX === 'yes' ? 'scroll' : (scrollingX === 'no' ? 'hidden' : scrollingX)) 1358 .css('overflow-y', scrollingY === 'yes' ? 'scroll' : (scrollingY === 'no' ? 'hidden' : scrollingY)); 1359 1360 wSpace = margin[1] + margin[3] + padding[1] + padding[3]; 1361 hSpace = margin[0] + margin[2] + padding[0] + padding[2]; 1362 1363 // Calculations for the content 1364 minWidth = getScalar( isPercentage(minWidth) ? getScalar(minWidth, 'w') - wSpace : minWidth ); 1365 maxWidth = getScalar( isPercentage(maxWidth) ? getScalar(maxWidth, 'w') - wSpace : maxWidth ); 1366 1367 minHeight = getScalar( isPercentage(minHeight) ? getScalar(minHeight, 'h') - hSpace : minHeight ); 1368 maxHeight = getScalar( isPercentage(maxHeight) ? getScalar(maxHeight, 'h') - hSpace : maxHeight ); 1369 1370 origWidth = getScalar( isPercentage(width) ? getScalar(width, 'w') - wSpace : width ); 1371 origHeight = getScalar( isPercentage(height) ? getScalar(height, 'h') - hSpace : height ); 1372 1373 if (current.fitToView) { 1374 maxWidth = Math.min(maxWidth, getScalar('100%', 'w') - wSpace ); 1375 maxHeight = Math.min(maxHeight, getScalar('100%', 'h') - hSpace ); 1376 } 1377 1378 maxWidth_ = view.w; 1379 maxHeight_ = view.h; 1380 1381 if (current.type === 'iframe') { 1382 iframe = current.content; 1383 1384 wrap.removeClass('fancybox-tmp'); 1385 1386 if ((current.autoWidth || current.autoHeight) && iframe && iframe.data('ready') === 1) { 1387 1388 try { 1389 if (iframe[0].contentWindow && iframe[0].contentWindow.document.location) { 1390 body = iframe.contents().find('body'); 1391 1392 inner.addClass( 'fancybox-tmp' ); 1393 1394 inner.width( screen.width - wSpace ).height( 99999 ); 1395 1396 if (scrollOut) { 1397 body.css('overflow-x', 'hidden'); 1398 } 1399 1400 if (current.autoWidth) { 1401 origWidth = body.outerWidth(true); 1402 } 1403 1404 if (current.autoHeight) { 1405 origHeight = body.outerHeight(true); 1406 } 1407 1408 inner.removeClass( 'fancybox-tmp' ); 1409 } 1410 1411 } catch (e) {} 1412 } 1413 1414 } else if ( (current.autoWidth || current.autoHeight) && !(current.type === 'image' || current.type === 'swf') ) { 1415 inner.addClass( 'fancybox-tmp' ); 1416 1417 // Set width or height in case we need to calculate only one dimension 1418 if (current.autoWidth) { 1419 inner.width( 'auto' ); 1420 1421 } else { 1422 inner.width( maxWidth ); 1423 } 1424 1425 if (current.autoHeight) { 1426 inner.height( 'auto' ); 1427 1428 } else { 1429 inner.height( maxHeight ); 1430 } 1431 1432 if (current.autoWidth) { 1433 origWidth = inner[0].scrollWidth || inner.width(); 1434 } 1435 1436 if (current.autoHeight) { 1437 origHeight = inner[0].scrollHeight || inner.height(); 1438 } 1439 1440 inner.removeClass( 'fancybox-tmp' ); 1441 } 1442 1443 width = origWidth; 1444 height = origHeight; 1445 ratio = origWidth / origHeight; 1446 1447 if (!current.autoResize) { 1448 wrap.css({ 1449 width : getValue( width ), 1450 height : 'auto' 1451 }); 1452 1453 inner.css({ 1454 width : getValue( width ), 1455 height : getValue( height ) 1456 }); 1457 return; 1458 } 1459 1460 if (current.aspectRatio) { 1461 if (width > maxWidth) { 1462 width = maxWidth; 1463 height = width / ratio; 1464 } 1465 1466 if (height > maxHeight) { 1467 height = maxHeight; 1468 width = height * ratio; 1469 } 1470 1471 if (width < minWidth) { 1472 width = minWidth; 1473 height = width / ratio; 1474 } 1475 1476 if (height < minHeight) { 1477 height = minHeight; 1478 width = height * ratio; 1479 } 1480 1481 } else { 1482 width = Math.max(minWidth, Math.min(width, maxWidth)); 1483
1484 if (current.autoHeight && current.type !== 'iframe') { 1485 inner.width( width ); 1486 1487 origHeight = height = inner[0].scrollHeight; 1488 } 1489 1490 height = Math.max(minHeight, Math.min(height, maxHeight)); 1491 } 1492 1493 // Wrap element has to have fixed width, because long title can expand it 1494 wrap.css({ 1495 width : getValue( width ), 1496 height : 'auto' 1497 }); 1498 1499 inner.css({ 1500 width : getValue( width ), 1501 height : getValue( height ) 1502 }); 1503 1504 width_ = getScalar( wrap.outerWidth(true) ); 1505 height_ = getScalar( wrap.outerHeight(true) ); 1506 1507 if (current.fitToView) { 1508 // Since we do not know how many lines will be at the final, we need to 1509 // resize box till it fits inside max dimensions 1510 if (current.aspectRatio) { 1511 while ((width_ > maxWidth_ || height_ > maxHeight_) && width > minWidth && height > minHeight) { 1512 if (steps++ > 30) { 1513 break; 1514 } 1515 1516 height = Math.max(minHeight, Math.min(maxHeight, height - 10)); 1517 width = getScalar(height * ratio); 1518 1519 if (width < minWidth) { 1520 width = minWidth; 1521 height = getScalar(width / ratio); 1522 } 1523 1524 if (width > maxWidth) { 1525 width = maxWidth; 1526 height = getScalar(width / ratio); 1527 } 1528 1529 wrap.css({ 1530 width : getValue( width ) 1531 }); 1532 1533 inner.css({ 1534 width : getValue( width ), 1535 height : getValue( height ) 1536 }); 1537 1538 width_ = getScalar( wrap.outerWidth(true) ); 1539 height_ = getScalar( wrap.outerHeight(true) ); 1540 } 1541 1542 } else { 1543 width = Math.max(minWidth, Math.min(width, width - (width_ - maxWidth_ ))); 1544 height = Math.max(minHeight, Math.min(height, height - (height_ - maxHeight_ ))); 1545 } 1546 } 1547 1548 1549 if (scrollOut && scrollingX === 'auto' && (height < inner[0].scrollHeight || (isQuery(current.content) && current.content[0] && height < current.content[0].offsetHeight)) && (width + wSpace + scrollOut) < maxWidth) { 1550 width += scrollOut; 1551 } 1552 1553 wrap.css({ 1554 width : width 1555 }); 1556 1557 inner.css({ 1558 width : getValue( width ), 1559 height : getValue( height ) 1560 }); 1561 1562 width_ = getScalar( wrap.outerWidth(true) ); 1563 height_ = getScalar( wrap.outerHeight(true) ); 1564 1565 canShrink = (width_ > maxWidth_ || height_ > maxHeight_) && width > minWidth && height > minHeight; 1566 canExpand = (width_ < maxWidth_ || height_ < maxHeight_) && ( current.aspectRatio ? (width < maxWidth && height < maxHeight && width < origWidth && height < origHeight) : ((width < maxWidth || height < maxHeight) && (width < origWidth || height < origHeight)) ); 1567 1568 current.canShrink = canShrink; 1569 current.canExpand = canExpand; 1570 1571 if (!iframe && current.autoHeight && height > minHeight && height < maxHeight && !canExpand) { 1572 inner.height('auto'); 1573 } 1574 }, 1575 1576 _getPosition: function( object ) { 1577 var obj = object || F.current, 1578 wrap = obj.wrap, 1579 view = F.getViewport(), 1580 pos = {}, 1581 top = view.y, 1582 left = view.x; 1583 1584 pos = { 1585 top : getValue( Math.max(top, top + ((view.h - wrap.outerHeight(true)) * obj.topRatio)) ), 1586 left : getValue( Math.max(left, left + ((view.w - wrap.outerWidth(true)) * obj.leftRatio)) ), 1587 width : getValue( wrap.width() ), 1588 height : getValue( wrap.height() ) 1589 }; 1590 1591 return pos; 1592 }, 1593 1594 _afterZoomIn : function() { 1595 var current = F.current; 1596 1597 if (!current) { 1598 return; 1599 } 1600 1601 if (F.lock) { 1602 F.lock.css('overflow', 'auto'); 1603 } 1604 1605 F.isOpen = F.isOpened = true; 1606 1607 F.rebuild(); 1608 1609 F.rebind(); 1610 1611 if (current.caption && current.caption.wrap) { 1612 current.caption.wrap.show().css({ 1613 'visibility' : 'visible', 1614 'opacity' : 0, 1615 'left' : 0 1616 }) 1617 .animate({opacity:1}, "fast"); 1618 } 1619 1620 F.update(); 1621 1622 F.wrap.css('overflow', 'visible').addClass('fancybox-open').focus(); 1623 1624 F[ F.wrap.hasClass('fancybox-skin') ? 'wrap' : 'inner' ].addClass('fancybox-' + current.theme + '-skin-open'); 1625 1626 if (current.caption && current.caption.wrap) { 1627 current.caption.wrap.show().css('left', 0).animate({opacity:1}, "fast"); 1628 } 1629 1630 // Add empty element to simulate bottom margin, this trick helps to avoid extra element 1631 if (current.margin[2] > 0) { 1632 $('<div class="fancybox-spacer"></div>').css('height', getValue( current.margin[2] - 2) ).appendTo( F.wrap ); 1633 } 1634 1635 F.trigger('afterShow'); 1636 1637 F._preloadImages(); 1638 1639 if (current.autoPlay && !F.slideshow.isActive) { 1640 F.slideshow.start(); 1641 } 1642 }, 1643 1644 _afterZoomOut : function(obj) { 1645 var cleanup = function() { 1646 removeWrap('.fancybox-wrap'); 1647 }; 1648 1649 F.hideLoading(); 1650 1651 obj = obj || F.current; 1652 1653 if (obj && obj.wrap) { 1654 obj.wrap.hide(); 1655 } 1656 1657 $.extend(F, { 1658 group : [], 1659 opts : {}, 1660 coming : null, 1661 current : null, 1662 isActive : false, 1663 isOpened : false, 1664 isOpen : false, 1665 isClosing : false, 1666 wrap : null, 1667 skin : null, 1668 inner : null 1669 }); 1670 1671 F.trigger('afterClose', obj); 1672 1673 if (!F.coming && !F.current) { 1674 if (obj.overlay) { 1675 F.overlay.close( obj.overlay, cleanup ); 1676 } else { 1677 cleanup(); 1678 } 1679 } 1680 }, 1681 1682 _translate : function( str ) { 1683 var obj = F.coming || F.current, 1684 arr = obj.locales[ obj.locale ]; 1685 1686 return str.replace(/\{\{(\w+)\}\}/g, function(match, n) { 1687 var value = arr[n]; 1688 1689 if (value === undefined) { 1690 return match; 1691 } 1692 1693 return value; 1694 }); 1695 } 1696 }); 1697 1698 /* 1699 * Transition object 1700 */ 1701 1702 F.transitions = { 1703 _getOrig: function( object ) { 1704 var obj = object || F.current, 1705 wrap = obj.wrap, 1706 element = obj.element, 1707 orig = obj.orig, 1708 view = F.getViewport(), 1709 pos = {}, 1710 width = 50, 1711 height = 50; 1712 1713 if (!orig && element && element.is(':visible')) { 1714 orig = element.find('img:first:visible'); 1715 1716 if (!orig.length) { 1717 orig = element; 1718 } 1719 } 1720 1721 // If there is no orig element, maybe only the first thumbnail is visible 1722 if (!orig && obj.group[0].element) { 1723 orig = obj.group[0].element.find('img:visible:first'); 1724 } 1725 1726 if (isQuery(orig) && orig.is(':visible')) { 1727 pos = orig.offset(); 1728 1729 if (orig.is('img')) { 1730 width = orig.outerWidth(); 1731 height = orig.outerHeight(); 1732 } 1733 1734 if (F.lock) { 1735 pos.top -= W.scrollTop(); 1736 pos.left -= W.scrollLeft(); 1737 } 1738 1739 } else { 1740 pos.top = view.y + (view.h - height) * obj.topRatio; 1741 pos.left = view.x + (view.w - width) * obj.leftRatio; 1742 } 1743 1744 pos = { 1745 top : getValue( pos.top - (wrap.outerHeight(true) - wrap.height() ) * 0.5 ), 1746 left : getValue( pos.left - (wrap.outerWidth(true) - wrap.width() ) * 0.5 ), 1747 width : getValue( width ), 1748 height : getValue( height ) 1749 }; 1750 1751 return pos; 1752 }, 1753 1754 _getCenter: function( object ) { 1755 var obj = object || F.current, 1756 wrap = obj.wrap, 1757 view = F.getViewport(), 1758 pos = {}, 1759 top = view.y, 1760 left = view.x; 1761 1762 pos = { 1763 top : getValue( Math.max(top, top + ((view.h - wrap.outerHeight(true)) * obj.topRatio)) ), 1764 left : getValue( Math.max(left, left + ((view.w - wrap.outerWidth(true)) * obj.leftRatio)) ), 1765 width : getValue( wrap.width() ), 1766 height : getValue( wrap.height() ) 1767 }; 1768 1769 return pos; 1770 }, 1771 1772 _prepare : function( object, forClosing ) { 1773 var obj = object || F.current, 1774 wrap = obj.wrap, 1775 inner = obj.inner; 1776 1777 // Little trick to avoid animating both elements and to improve performance 1778 wrap.height( wrap.height() ); 1779 1780 inner.css({ 1781 'width' : (inner.width() * 100 / wrap.width() ) + '%', 1782 'height' : (Math.floor( (inner.height() * 100 / wrap.height() ) * 100 ) / 100 ) + '%' 1783 }); 1784 1785 if (forClosing === true) { 1786 wrap.find('.fancybox-title, .fancybox-spacer, .fancybox-close, .fancybox-nav').remove(); 1787 } 1788 1789 inner.css('overflow', 'hidden'); 1790 }, 1791 1792 fade : function( object, stage ) { 1793 var pos = this._getCenter( object ), 1794 opa = {opacity: 0}; 1795 1796 return ((stage === 'open' || stage === 'changeIn') ? [ $.extend(pos, opa), {opacity: 1} ] : [ {}, opa ]); 1797 }, 1798 1799 drop : function( object, stage ) { 1800 var a = $.extend(this._getCenter( object ), {opacity: 1}), 1801 b = $.extend({}, a, {opacity: 0, top: getValue( Math.max( F.getViewport().y - object.margin[0], getScalar( a.top ) - 200 ) )}); 1802 1803 return ((stage === 'open' || stage === 'changeIn') ? [ b, a ] : [ {}, b ]); 1804 }, 1805 1806 elastic : function( object, stage ) { 1807 var wrap = object.wrap, 1808 margin = object.margin, 1809 view = F.getViewport(), 1810 direction = F.direction, 1811 pos = this._getCenter( object ), 1812 from = $.extend({}, pos), 1813 to = $.extend({}, pos), 1814 prop, 1815 amount, 1816 value; 1817 1818 if (stage === 'open') { 1819 from = this._getOrig( object ); 1820 1821 } else if (stage === 'close') { 1822 from = {}; 1823 to = this._getOrig( object ); 1824 1825 } else if (direction) { 1826 // Calculate max distance and try to avoid scrollbars 1827 prop = (direction === 'up' || direction === 'down') ? 'top' : 'left'; 1828 amount = (direction === 'up' || direction === 'left') ? 200 : -200; 1829 1830 if (stage === 'changeIn') { 1831 value = getScalar(from[ prop ]) + amount; 1832 1833 if (direction === 'left') { 1834 // from viewport right to center 1835 value = Math.min( value, view.x + view.w - margin[3] - wrap.outerWidth() - 1 ); 1836 1837 } else if (direction === 'right') { 1838 // from viewport left to center 1839 value = Math.max( value, view.x - margin[1] ); 1840 1841 } else if (direction === 'up') { 1842 // from viewport bottom to center 1843 value = Math.min( value, view.y + view.h - margin[0] - wrap.outerHeight() - 1); 1844 1845 } else { 1846 // down - from viewport top to center 1847 value = Math.max( value, view.y - margin[2] ); 1848 } 1849 1850 from[ prop ] = value; 1851 1852 } else { 1853 value = getScalar(wrap.css(prop)) - amount; 1854 from = {}; 1855 1856 if (direction === 'left') { 1857 // from viewport center to left 1858 value = Math.max( value, view.x - margin[3] ); 1859 1860 } else if (direction === 'right') { 1861 // from viewport center to right 1862 value = Math.min( value, view.x + view.w - margin[1] - wrap.outerWidth() - 1 ); 1863 1864 } else if (direction === 'up') { 1865 // from viewport center to top 1866 value = Math.max( value, view.y - margin[0] ); 1867 1868 } else { 1869 // down - from center to bottom 1870 value = Math.min( value, view.y + view.h - margin[2] - wrap.outerHeight() - 1 ); 1871 } 1872 1873 to[ prop ] = value; 1874 } 1875 } 1876 1877 if (stage === 'open' || stage === 'changeIn') { 1878 from.opacity = 0; 1879 to.opacity = 1; 1880 1881 } else { 1882 to.opacity = 0; 1883 } 1884 1885 return [ from, to ]; 1886 }, 1887 1888 open : function() { 1889 var current = F.current, 1890 previous = F.previous, 1891 direction = F.direction, 1892 effect, 1893 pos, 1894 speed, 1895 easing, 1896 stage; 1897 1898 if (previous) { 1899 previous.wrap.stop(true).removeClass('fancybox-opened'); 1900 } 1901 1902 if ( F.isOpened ) { 1903 effect = current.nextEffect, 1904 speed = current.nextSpeed; 1905 easing = current.nextEasing; 1906 stage = 'changeIn'; 1907 1908 } else { 1909 effect = current.openEffect; 1910 speed = current.openSpeed; 1911 easing = current.openEasing; 1912 stage = 'open'; 1913 } 1914 1915 /* 1916 * Open current item 1917 */ 1918 1919 if (effect === 'none') { 1920 F._afterZoomIn(); 1921 1922 } else { 1923 pos = this[ effect ]( current, stage ); 1924 1925 if (effect === 'elastic') { 1926 this._prepare( current ); 1927 } 1928 1929 current.wrap.css( pos[ 0 ] ); 1930 1931 current.wrap.animate( 1932 pos[ 1 ], 1933 speed, 1934 easing, 1935 F._afterZoomIn 1936 ); 1937 } 1938 1939 /* 1940 * Close previous item 1941 */ 1942 if (previous) { 1943 if (!F.isOpened || previous.prevEffect === 'none') { 1944 // Remove previous item if it has not fully opened 1945 removeWrap( $('.fancybox-wrap').not( current.wrap ) ); 1946 1947 } else { 1948 previous.wrap.stop(true).removeClass('fancybox-opened'); 1949 1950 pos = this[ previous.prevEffect ]( previous, 'changeOut' ); 1951 1952 this._prepare( previous, true ); 1953 1954 previous.wrap.animate( 1955 pos[ 1 ], 1956 previous.prevSpeed, 1957 previous.prevEasing, 1958 function() { 1959 removeWrap( previous.wrap ); 1960 } 1961 ); 1962 } 1963 } 1964 }, 1965 1966 close : function() { 1967 var current = F.current, 1968 wrap = current.wrap.stop(true).removeClass('fancybox-opened'), 1969 effect = current.closeEffect, 1970 pos; 1971 1972 if (effect === 'none') { 1973 return F._afterZoomOut(); 1974 } 1975 1976 this._prepare( current, true ); 1977 1978 pos = this[ effect ]( current, 'close' ); 1979 1980 wrap.addClass('fancybox-animating') 1981 .animate( 1982 pos[ 1 ], 1983 current.closeSpeed, 1984 current.closeEasing, 1985 F._afterZoomOut 1986 ); 1987 } 1988 }; 1989 1990 /* 1991 * Slideshow object 1992 */ 1993 1994 F.slideshow = { 1995 _clear : function () { 1996 if (this._timer) { 1997 clearTimeout(this._timer); 1998 } 1999 }, 2000 _set : function () { 2001 this._clear(); 2002 2003 if (F.current && this.isActive) { 2004 this._timer = setTimeout(F.next, this._speed); 2005 } 2006 }, 2007 2008 _timer : null, 2009 _speed : null, 2010 2011 isActive : false, 2012 2013 start : function ( speed ) { 2014 var current = F.current; 2015 2016 if (current && (current.loop || current.index < current.group.length - 1)) { 2017 this.stop(); 2018 2019 this.isActive = true; 2020 this._speed = speed || current.playSpeed; 2021 2022 D.bind({ 2023 'beforeLoad.player' : $.proxy(this._clear, this), 2024 'onUpdate.player' : $.proxy(this._set, this), 2025 'onCancel.player beforeClose.player' : $.proxy(this.stop, this) 2026 }); 2027 2028 this._set(); 2029 2030 F.trigger('onPlayStart'); 2031 } 2032 }, 2033 2034 stop : function () { 2035 this._clear(); 2036 2037 D.unbind('.player'); 2038 2039 this.isActive = false;
2040 this._timer = this._speed = null; 2041 2042 F.trigger('onPlayEnd'); 2043 }, 2044 2045 toggle : function() { 2046 if (this.isActive) { 2047 this.stop(); 2048 2049 } else { 2050 this.start.apply(this, arguments ); 2051 } 2052 } 2053 }; 2054 2055 /* 2056 * Overlay object 2057 */ 2058 2059 F.overlay = { 2060 el : null, // current handle 2061 theme : '', // current theme 2062 2063 // Public methods 2064 open : function(opts) { 2065 var that = this, 2066 el = this.el, 2067 fixed = F.defaults.fixed, 2068 opacity, 2069 theme; 2070 2071 opts = $.extend({}, F.defaults.overlay, opts); 2072 2073 if (el) { 2074 el.stop(true).removeAttr('style').unbind('.overlay'); 2075 2076 } else { 2077 el = $('<div class="fancybox-overlay' + (fixed ? ' fancybox-overlay-fixed' : '' ) + '"></div>').appendTo( opts.parent || 'body' ); 2078 } 2079 2080 if (opts.closeClick) { 2081 el.bind('click.overlay', function(e) { 2082 // fix Android touch event bubbling issue 2083 if (F.lastTouch && (getTime() - F.lastTouch) < 300) { 2084 return false; 2085 } 2086 2087 if (F.isActive) { 2088 F.close(); 2089 } else { 2090 that.close(); 2091 } 2092 2093 return false; 2094 }); 2095 } 2096 2097 theme = opts.theme || (F.coming ? F.coming.theme : 'default'); 2098 2099 if (theme !== this.theme) { 2100 el.removeClass('fancybox-' + this.theme + '-overlay') 2101 } 2102 2103 this.theme = theme; 2104 2105 el.addClass('fancybox-' + theme + '-overlay').css( opts.css ); 2106 2107 opacity = el.css('opacity'); 2108 2109 if (!this.el && opacity < 1 && opts.speedIn) { 2110 el.css({ 2111 opacity : 0, 2112 filter : 'alpha(opacity=0)' // This fixes IE flickering 2113 }) 2114 .fadeTo( opts.speedIn, opacity ); 2115 } 2116 2117 this.el = el; 2118 2119 if (!fixed) { 2120 W.bind('resize.overlay', $.proxy( this.update, this) ); 2121 2122 this.update(); 2123 } 2124 }, 2125 2126 close : function(opts, callback) { 2127 opts = $.extend({}, F.defaults.overlay, opts); 2128 2129 if (this.el) { 2130 this.el.stop(true).fadeOut(opts.speedOut, function() { 2131 W.unbind('resize.overlay'); 2132 2133 $('.fancybox-overlay').remove(); 2134 2135 F.overlay.el = null; 2136 2137 if ($.isFunction(callback)) { 2138 callback(); 2139 } 2140 }); 2141 } 2142 }, 2143 2144 update : function () { 2145 // Reset width/height so it will not mess 2146 this.el.css({width: '100%', height: '100%'}); 2147 2148 this.el.width(D.width()).height(D.height()); 2149 } 2150 }; 2151 2152 /* 2153 * Touch object - adds swipe left/right events 2154 */ 2155 2156 F.touch = { 2157 startX : 0, 2158 wrapX : 0, 2159 dx : 0, 2160 isMoving : false, 2161 2162 _start : function(e) { 2163 var current = F.current, 2164 data = e.originalEvent.touches ? e.originalEvent.touches[0] : e, 2165 now = getTime(); 2166 2167 if (!F.isOpen || F.wrap.is(':animated') || !( $(e.target).is(F.inner) || $(e.target).parent().is(F.inner) )) { 2168 return; 2169 } 2170 2171 if (F.lastTouch && (now - F.lastTouch) < 300) { 2172 e.preventDefault(); 2173 2174 F.lastTouch = now; 2175 2176 this._cancel(true); 2177 2178 F.toggle(); 2179 2180 return false; 2181 } 2182 2183 F.lastTouch = now; 2184 2185 if (F.wrap && F.wrap.outerWidth() > F.getViewport().w) { 2186 return; 2187 } 2188 2189 e.preventDefault(); 2190 2191 if (data && F.wrap && F.wrap.outerWidth() < F.getViewport().w) { 2192 this.startX = data.pageX; 2193 this.wrapX = F.wrap.position().left; 2194 this.isMoving = true; 2195 2196 F.inner 2197 .bind('touchmove.fb', $.proxy(this._move, this) ) 2198 .one("touchend.fb touchcancel.fb", $.proxy(this._cancel, this) ); 2199 } 2200 }, 2201 2202 _move : function(e) { 2203 var data = e.originalEvent.touches ? e.originalEvent.touches[0] : e, 2204 dx = this.startX - data.pageX; 2205 2206 if (!this.isMoving || !F.isOpen) { 2207 return; 2208 } 2209 2210 this.dx = dx; 2211 2212 if (F.current.wrap.outerWidth(true) <= W.width()) { 2213 2214 if (Math.abs(dx) >= 50) { 2215 e.preventDefault(); 2216 2217 this.last = 0; 2218 2219 this._cancel(true); 2220 2221 if (dx > 0) { 2222 F.next('left'); 2223 2224 } else { 2225 F.prev('right'); 2226 } 2227 2228 } else if (Math.abs(dx) > 3) { 2229 e.preventDefault(); 2230 2231 this.last = 0; 2232 2233 F.wrap.css('left', this.wrapX - dx); 2234 } 2235 } 2236 }, 2237 2238 _clear : function() { 2239 this.startX = this.wrapX = this.dx = 0; 2240 this.isMoving = false; 2241 }, 2242 2243 _cancel : function( stop ) { 2244 if (F.inner) { 2245 F.inner.unbind('touchmove.fb'); 2246 } 2247 2248 if (F.isOpen && Math.abs(this.dx) > 3) { 2249 F.reposition(false); 2250 } 2251 2252 this._clear(); 2253 }, 2254 2255 init : function() { 2256 var that = this; 2257 2258 if (F.inner && F.touch) { 2259 this._cancel(true); 2260 2261 F.inner.bind('touchstart.fb', $.proxy(this._start, this)); 2262 } 2263 } 2264 }; 2265 2266 /* 2267 * Add default easing 2268 */ 2269 2270 if (!$.easing.easeOutQuad) { 2271 $.easing.easeOutQuad = function (x, t, b, c, d) { 2272 return -c *(t/=d)*(t-2) + b; 2273 }; 2274 } 2275 2276 /* 2277 * 2278 */ 2279 2280 D.ready(function() { 2281 var w1, w2, scrollV, scrollH; 2282 2283 // Tests that need a body at doc ready 2284 if ( $.scrollbarWidth === undefined ) { 2285 $.scrollbarWidth = function() { 2286 var parent = $('<div style="width:50px;height:50px;overflow:auto"><div/></div>').appendTo('body'), 2287 child = parent.children(), 2288 width = child.innerWidth() - child.height( 99 ).innerWidth(); 2289 2290 parent.remove(); 2291 2292 return width; 2293 }; 2294 } 2295 2296 if ( $.support.fixedPosition === undefined ) { 2297 $.support.fixedPosition = (function() { 2298 var elem = $('<div style="position:fixed;top:20px;padding:0;margin:0;border:0;"></div>').appendTo('body'), 2299 fixed = elem.css( 'position' ) === 'fixed' && ((elem[0].offsetTop > 18 && elem[0].offsetTop < 22) || elem[0].offsetTop === 15 ); 2300 2301 elem.remove(); 2302 2303 return fixed; 2304 }()); 2305 } 2306 2307 $.extend(F.defaults, { 2308 scrollbarWidth : $.scrollbarWidth(), 2309 fixed : $.support.fixedPosition, 2310 parent : $('body') 2311 }); 2312 2313 // Quick and dirty code to get page scroll-bar width and create CSS style 2314 // Workaround for FF jumping bug 2315 scrollV = W.scrollTop(); 2316 scrollH = W.scrollLeft(); 2317 2318 w1 = $(window).width(); 2319 2320 H.addClass('fancybox-lock-test'); 2321 2322 w2 = $(window).width(); 2323 2324 H.removeClass('fancybox-lock-test'); 2325 2326 W.scrollTop( scrollV ).scrollLeft( scrollH ); 2327 2328 F.lockMargin = (w2 - w1); 2329 2330 $("<style type='text/css'>.fancybox-margin{margin-right:" + F.lockMargin + "px;}</style>").appendTo("head"); 2331 2332 2333 }); 2334 2335 // jQuery plugin initialization 2336 $.fn.fancybox = function (options) { 2337 var that = this, 2338 selector = this.length ? this.selector : false, 2339 live = (selector && selector.indexOf('()') < 0 && !(options && options.live === false)); 2340 2341 var handler = function(e) { 2342 var collection = live ? $(selector) : that, 2343 group = $(this).blur(), 2344 param = options.groupAttr || 'data-fancybox-group', 2345 value = group.attr( param ), 2346 tmp = this.rel; 2347 2348 if (!value && tmp && tmp !== 'nofollow') { 2349 param = 'rel'; 2350 value = tmp; 2351 } 2352 2353 if (value) { 2354 group = collection.filter('[' + param + '="' + value + '"]'); 2355 2356 options.index = group.index( this ); 2357 } 2358 2359 if (group.length) { 2360 e.preventDefault(); 2361 2362 F.open(group.get(), options); 2363 } 2364 }; 2365 2366 options = options || {}; 2367 2368 if (live) { 2369 D.undelegate(selector, 'click.fb-start').delegate(selector + ":not('.fancybox-close,.fancybox-nav,.fancybox-wrap')", 'click.fb-start', handler); 2370 2371 } else { 2372 that.unbind('click.fb-start').bind('click.fb-start', handler); 2373 } 2374 2375 return this; 2376 }; 2377 2378}(window, document, 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.