blob: 7a0f8acb09700cc6608ffef57e4e730251dc2602 [file] [log] [blame]
Tang Cheng60ba8032016-06-20 15:31:17 +08001/*!
2 * fancyBox - jQuery Plugin
3 * version: 2.1.5 (Fri, 14 Jun 2013)
4 * requires jQuery v1.6 or later
5 *
6 * Examples at http://fancyapps.com/fancybox/
7 * License: www.fancyapps.com/fancybox/#license
8 *
9 * Copyright 2012 Janis Skarnelis - janis@fancyapps.com
10 *
11 */
12
13;(function (window, document, $, undefined) {
14 "use strict";
15
16 var H = $("html"),
17 W = $(window),
18 D = $(document),
19 F = $.fancybox = function () {
20 F.open.apply( this, arguments );
21 },
22 IE = navigator.userAgent.match(/msie/i),
23 didUpdate = null,
24 isTouch = document.createTouch !== undefined,
25
26 isQuery = function(obj) {
27 return obj && obj.hasOwnProperty && obj instanceof $;
28 },
29 isString = function(str) {
30 return str && $.type(str) === "string";
31 },
32 isPercentage = function(str) {
33 return isString(str) && str.indexOf('%') > 0;
34 },
35 isScrollable = function(el) {
36 return (el && !(el.style.overflow && el.style.overflow === 'hidden') && ((el.clientWidth && el.scrollWidth > el.clientWidth) || (el.clientHeight && el.scrollHeight > el.clientHeight)));
37 },
38 getScalar = function(orig, dim) {
39 var value = parseInt(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 getValue = function(value, dim) {
48 return getScalar(value, dim) + 'px';
49 };
50
51 $.extend(F, {
52 // The current version of fancyBox
53 version: '2.1.5',
54
55 defaults: {
56 padding : 15,
57 margin : 20,
58
59 width : 800,
60 height : 600,
61 minWidth : 100,
62 minHeight : 100,
63 maxWidth : 9999,
64 maxHeight : 9999,
65 pixelRatio: 1, // Set to 2 for retina display support
66
67 autoSize : true,
68 autoHeight : false,
69 autoWidth : false,
70
71 autoResize : true,
72 autoCenter : !isTouch,
73 fitToView : true,
74 aspectRatio : false,
75 topRatio : 0.5,
76 leftRatio : 0.5,
77
78 scrolling : 'auto', // 'auto', 'yes' or 'no'
79 wrapCSS : '',
80
81 arrows : true,
82 closeBtn : true,
83 closeClick : false,
84 nextClick : false,
85 mouseWheel : true,
86 autoPlay : false,
87 playSpeed : 3000,
88 preload : 3,
89 modal : false,
90 loop : true,
91
92 ajax : {
93 dataType : 'html',
94 headers : { 'X-fancyBox': true }
95 },
96 iframe : {
97 scrolling : 'auto',
98 preload : true
99 },
100 swf : {
101 wmode: 'transparent',
102 allowfullscreen : 'true',
103 allowscriptaccess : 'always'
104 },
105
106 keys : {
107 next : {
108 13 : 'left', // enter
109 34 : 'up', // page down
110 39 : 'left', // right arrow
111 40 : 'up' // down arrow
112 },
113 prev : {
114 8 : 'right', // backspace
115 33 : 'down', // page up
116 37 : 'right', // left arrow
117 38 : 'down' // up arrow
118 },
119 close : [27], // escape key
120 play : [32], // space - start/stop slideshow
121 toggle : [70] // letter "f" - toggle fullscreen
122 },
123
124 direction : {
125 next : 'left',
126 prev : 'right'
127 },
128
129 scrollOutside : true,
130
131 // Override some properties
132 index : 0,
133 type : null,
134 href : null,
135 content : null,
136 title : null,
137
138 // HTML templates
139 tpl: {
140 wrap : '<div class="fancybox-wrap" tabIndex="-1"><div class="fancybox-skin"><div class="fancybox-outer"><div class="fancybox-inner"></div></div></div></div>',
141 image : '<img class="fancybox-image" src="{href}" alt="" />',
142 iframe : '<iframe id="fancybox-frame{rnd}" name="fancybox-frame{rnd}" class="fancybox-iframe" frameborder="0" vspace="0" hspace="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen' + (IE ? ' allowtransparency="true"' : '') + '></iframe>',
143 error : '<p class="fancybox-error">The requested content cannot be loaded.<br/>Please try again later.</p>',
144 closeBtn : '<a title="Close" class="fancybox-item fancybox-close" href="javascript:;"></a>',
145 next : '<a title="Next" class="fancybox-nav fancybox-next" href="javascript:;"><span></span></a>',
146 prev : '<a title="Previous" class="fancybox-nav fancybox-prev" href="javascript:;"><span></span></a>'
147 },
148
149 // Properties for each animation type
150 // Opening fancyBox
151 openEffect : 'fade', // 'elastic', 'fade' or 'none'
152 openSpeed : 250,
153 openEasing : 'swing',
154 openOpacity : true,
155 openMethod : 'zoomIn',
156
157 // Closing fancyBox
158 closeEffect : 'fade', // 'elastic', 'fade' or 'none'
159 closeSpeed : 250,
160 closeEasing : 'swing',
161 closeOpacity : true,
162 closeMethod : 'zoomOut',
163
164 // Changing next gallery item
165 nextEffect : 'elastic', // 'elastic', 'fade' or 'none'
166 nextSpeed : 250,
167 nextEasing : 'swing',
168 nextMethod : 'changeIn',
169
170 // Changing previous gallery item
171 prevEffect : 'elastic', // 'elastic', 'fade' or 'none'
172 prevSpeed : 250,
173 prevEasing : 'swing',
174 prevMethod : 'changeOut',
175
176 // Enable default helpers
177 helpers : {
178 overlay : true,
179 title : true
180 },
181
182 // Callbacks
183 onCancel : $.noop, // If canceling
184 beforeLoad : $.noop, // Before loading
185 afterLoad : $.noop, // After loading
186 beforeShow : $.noop, // Before changing in current item
187 afterShow : $.noop, // After opening
188 beforeChange : $.noop, // Before changing gallery item
189 beforeClose : $.noop, // Before closing
190 afterClose : $.noop // After closing
191 },
192
193 //Current state
194 group : {}, // Selected group
195 opts : {}, // Group options
196 previous : null, // Previous element
197 coming : null, // Element being loaded
198 current : null, // Currently loaded element
199 isActive : false, // Is activated
200 isOpen : false, // Is currently open
201 isOpened : false, // Have been fully opened at least once
202
203 wrap : null,
204 skin : null,
205 outer : null,
206 inner : null,
207
208 player : {
209 timer : null,
210 isActive : false
211 },
212
213 // Loaders
214 ajaxLoad : null,
215 imgPreload : null,
216
217 // Some collections
218 transitions : {},
219 helpers : {},
220
221 /*
222 * Static methods
223 */
224
225 open: function (group, opts) {
226 if (!group) {
227 return;
228 }
229
230 if (!$.isPlainObject(opts)) {
231 opts = {};
232 }
233
234 // Close if already active
235 if (false === F.close(true)) {
236 return;
237 }
238
239 // Normalize group
240 if (!$.isArray(group)) {
241 group = isQuery(group) ? $(group).get() : [group];
242 }
243
244 // Recheck if the type of each element is `object` and set content type (image, ajax, etc)
245 $.each(group, function(i, element) {
246 var obj = {},
247 href,
248 title,
249 content,
250 type,
251 rez,
252 hrefParts,
253 selector;
254
255 if ($.type(element) === "object") {
256 // Check if is DOM element
257 if (element.nodeType) {
258 element = $(element);
259 }
260
261 if (isQuery(element)) {
262 obj = {
263 href : element.data('fancybox-href') || element.attr('href'),
264 title : $('<div/>').text( element.data('fancybox-title') || element.attr('title') ).html(),
265 isDom : true,
266 element : element
267 };
268
269 if ($.metadata) {
270 $.extend(true, obj, element.metadata());
271 }
272
273 } else {
274 obj = element;
275 }
276 }
277
278 href = opts.href || obj.href || (isString(element) ? element : null);
279 title = opts.title !== undefined ? opts.title : obj.title || '';
280
281 content = opts.content || obj.content;
282 type = content ? 'html' : (opts.type || obj.type);
283
284 if (!type && obj.isDom) {
285 type = element.data('fancybox-type');
286
287 if (!type) {
288 rez = element.prop('class').match(/fancybox\.(\w+)/);
289 type = rez ? rez[1] : null;
290 }
291 }
292
293 if (isString(href)) {
294 // Try to guess the content type
295 if (!type) {
296 if (F.isImage(href)) {
297 type = 'image';
298
299 } else if (F.isSWF(href)) {
300 type = 'swf';
301
302 } else if (href.charAt(0) === '#') {
303 type = 'inline';
304
305 } else if (isString(element)) {
306 type = 'html';
307 content = element;
308 }
309 }
310
311 // Split url into two pieces with source url and content selector, e.g,
312 // "/mypage.html #my_id" will load "/mypage.html" and display element having id "my_id"
313 if (type === 'ajax') {
314 hrefParts = href.split(/\s+/, 2);
315 href = hrefParts.shift();
316 selector = hrefParts.shift();
317 }
318 }
319
320 if (!content) {
321 if (type === 'inline') {
322 if (href) {
323 content = $( isString(href) ? href.replace(/.*(?=#[^\s]+$)/, '') : href ); //strip for ie7
324
325 } else if (obj.isDom) {
326 content = element;
327 }
328
329 } else if (type === 'html') {
330 content = href;
331
332 } else if (!type && !href && obj.isDom) {
333 type = 'inline';
334 content = element;
335 }
336 }
337
338 $.extend(obj, {
339 href : href,
340 type : type,
341 content : content,
342 title : title,
343 selector : selector
344 });
345
346 group[ i ] = obj;
347 });
348
349 // Extend the defaults
350 F.opts = $.extend(true, {}, F.defaults, opts);
351
352 // All options are merged recursive except keys
353 if (opts.keys !== undefined) {
354 F.opts.keys = opts.keys ? $.extend({}, F.defaults.keys, opts.keys) : false;
355 }
356
357 F.group = group;
358
359 return F._start(F.opts.index);
360 },
361
362 // Cancel image loading or abort ajax request
363 cancel: function () {
364 var coming = F.coming;
365
366 if (coming && false === F.trigger('onCancel')) {
367 return;
368 }
369
370 F.hideLoading();
371
372 if (!coming) {
373 return;
374 }
375
376 if (F.ajaxLoad) {
377 F.ajaxLoad.abort();
378 }
379
380 F.ajaxLoad = null;
381
382 if (F.imgPreload) {
383 F.imgPreload.onload = F.imgPreload.onerror = null;
384 }
385
386 if (coming.wrap) {
387 coming.wrap.stop(true, true).trigger('onReset').remove();
388 }
389
390 F.coming = null;
391
392 // If the first item has been canceled, then clear everything
393 if (!F.current) {
394 F._afterZoomOut( coming );
395 }
396 },
397
398 // Start closing animation if is open; remove immediately if opening/closing
399 close: function (event) {
400 F.cancel();
401
402 if (false === F.trigger('beforeClose')) {
403 return;
404 }
405
406 F.unbindEvents();
407
408 if (!F.isActive) {
409 return;
410 }
411
412 if (!F.isOpen || event === true) {
413 $('.fancybox-wrap').stop(true).trigger('onReset').remove();
414
415 F._afterZoomOut();
416
417 } else {
418 F.isOpen = F.isOpened = false;
419 F.isClosing = true;
420
421 $('.fancybox-item, .fancybox-nav').remove();
422
423 F.wrap.stop(true, true).removeClass('fancybox-opened');
424
425 F.transitions[ F.current.closeMethod ]();
426 }
427 },
428
429 // Manage slideshow:
430 // $.fancybox.play(); - toggle slideshow
431 // $.fancybox.play( true ); - start
432 // $.fancybox.play( false ); - stop
433 play: function ( action ) {
434 var clear = function () {
435 clearTimeout(F.player.timer);
436 },
437 set = function () {
438 clear();
439
440 if (F.current && F.player.isActive) {
441 F.player.timer = setTimeout(F.next, F.current.playSpeed);
442 }
443 },
444 stop = function () {
445 clear();
446
447 D.unbind('.player');
448
449 F.player.isActive = false;
450
451 F.trigger('onPlayEnd');
452 },
453 start = function () {
454 if (F.current && (F.current.loop || F.current.index < F.group.length - 1)) {
455 F.player.isActive = true;
456
457 D.bind({
458 'onCancel.player beforeClose.player' : stop,
459 'onUpdate.player' : set,
460 'beforeLoad.player' : clear
461 });
462
463 set();
464
465 F.trigger('onPlayStart');
466 }
467 };
468
469 if (action === true || (!F.player.isActive && action !== false)) {
470 start();
471 } else {
472 stop();
473 }
474 },
475
476 // Navigate to next gallery item
477 next: function ( direction ) {
478 var current = F.current;
479
480 if (current) {
481 if (!isString(direction)) {
482 direction = current.direction.next;
483 }
484
485 F.jumpto(current.index + 1, direction, 'next');
486 }
487 },
488
489 // Navigate to previous gallery item
490 prev: function ( direction ) {
491 var current = F.current;
492
493 if (current) {
494 if (!isString(direction)) {
495 direction = current.direction.prev;
496 }
497
498 F.jumpto(current.index - 1, direction, 'prev');
499 }
500 },
501
502 // Navigate to gallery item by index
503 jumpto: function ( index, direction, router ) {
504 var current = F.current;
505
506 if (!current) {
507 return;
508 }
509
510 index = getScalar(index);
511
512 F.direction = direction || current.direction[ (index >= current.index ? 'next' : 'prev') ];
513 F.router = router || 'jumpto';
514
515 if (current.loop) {
516 if (index < 0) {
517 index = current.group.length + (index % current.group.length);
518 }
519
520 index = index % current.group.length;
521 }
522
523 if (current.group[ index ] !== undefined) {
524 F.cancel();
525
526 F._start(index);
527 }
528 },
529
530 // Center inside viewport and toggle position type to fixed or absolute if needed
531 reposition: function (e, onlyAbsolute) {
532 var current = F.current,
533 wrap = current ? current.wrap : null,
534 pos;
535
536 if (wrap) {
537 pos = F._getPosition(onlyAbsolute);
538
539 if (e && e.type === 'scroll') {
540 delete pos.position;
541
542 wrap.stop(true, true).animate(pos, 200);
543
544 } else {
545 wrap.css(pos);
546
547 current.pos = $.extend({}, current.dim, pos);
548 }
549 }
550 },
551
552 update: function (e) {
553 var type = (e && e.originalEvent && e.originalEvent.type),
554 anyway = !type || type === 'orientationchange';
555
556 if (anyway) {
557 clearTimeout(didUpdate);
558
559 didUpdate = null;
560 }
561
562 if (!F.isOpen || didUpdate) {
563 return;
564 }
565
566 didUpdate = setTimeout(function() {
567 var current = F.current;
568
569 if (!current || F.isClosing) {
570 return;
571 }
572
573 F.wrap.removeClass('fancybox-tmp');
574
575 if (anyway || type === 'load' || (type === 'resize' && current.autoResize)) {
576 F._setDimension();
577 }
578
579 if (!(type === 'scroll' && current.canShrink)) {
580 F.reposition(e);
581 }
582
583 F.trigger('onUpdate');
584
585 didUpdate = null;
586
587 }, (anyway && !isTouch ? 0 : 300));
588 },
589
590 // Shrink content to fit inside viewport or restore if resized
591 toggle: function ( action ) {
592 if (F.isOpen) {
593 F.current.fitToView = $.type(action) === "boolean" ? action : !F.current.fitToView;
594
595 // Help browser to restore document dimensions
596 if (isTouch) {
597 F.wrap.removeAttr('style').addClass('fancybox-tmp');
598
599 F.trigger('onUpdate');
600 }
601
602 F.update();
603 }
604 },
605
606 hideLoading: function () {
607 D.unbind('.loading');
608
609 $('#fancybox-loading').remove();
610 },
611
612 showLoading: function () {
613 var el, viewport;
614
615 F.hideLoading();
616
617 el = $('<div id="fancybox-loading"><div></div></div>').click(F.cancel).appendTo('body');
618
619 // If user will press the escape-button, the request will be canceled
620 D.bind('keydown.loading', function(e) {
621 if ((e.which || e.keyCode) === 27) {
622 e.preventDefault();
623
624 F.cancel();
625 }
626 });
627
628 if (!F.defaults.fixed) {
629 viewport = F.getViewport();
630
631 el.css({
632 position : 'absolute',
633 top : (viewport.h * 0.5) + viewport.y,
634 left : (viewport.w * 0.5) + viewport.x
635 });
636 }
637
638 F.trigger('onLoading');
639 },
640
641 getViewport: function () {
642 var locked = (F.current && F.current.locked) || false,
643 rez = {
644 x: W.scrollLeft(),
645 y: W.scrollTop()
646 };
647
648 if (locked && locked.length) {
649 rez.w = locked[0].clientWidth;
650 rez.h = locked[0].clientHeight;
651
652 } else {
653 // See http://bugs.jquery.com/ticket/6724
654 rez.w = isTouch && window.innerWidth ? window.innerWidth : W.width();
655 rez.h = isTouch && window.innerHeight ? window.innerHeight : W.height();
656 }
657
658 return rez;
659 },
660
661 // Unbind the keyboard / clicking actions
662 unbindEvents: function () {
663 if (F.wrap && isQuery(F.wrap)) {
664 F.wrap.unbind('.fb');
665 }
666
667 D.unbind('.fb');
668 W.unbind('.fb');
669 },
670
671 bindEvents: function () {
672 var current = F.current,
673 keys;
674
675 if (!current) {
676 return;
677 }
678
679 // Changing document height on iOS devices triggers a 'resize' event,
680 // that can change document height... repeating infinitely
681 W.bind('orientationchange.fb' + (isTouch ? '' : ' resize.fb') + (current.autoCenter && !current.locked ? ' scroll.fb' : ''), F.update);
682
683 keys = current.keys;
684
685 if (keys) {
686 D.bind('keydown.fb', function (e) {
687 var code = e.which || e.keyCode,
688 target = e.target || e.srcElement;
689
690 // Skip esc key if loading, because showLoading will cancel preloading
691 if (code === 27 && F.coming) {
692 return false;
693 }
694
695 // Ignore key combinations and key events within form elements
696 if (!e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey && !(target && (target.type || $(target).is('[contenteditable]')))) {
697 $.each(keys, function(i, val) {
698 if (current.group.length > 1 && val[ code ] !== undefined) {
699 F[ i ]( val[ code ] );
700
701 e.preventDefault();
702 return false;
703 }
704
705 if ($.inArray(code, val) > -1) {
706 F[ i ] ();
707
708 e.preventDefault();
709 return false;
710 }
711 });
712 }
713 });
714 }
715
716 if ($.fn.mousewheel && current.mouseWheel) {
717 F.wrap.bind('mousewheel.fb', function (e, delta, deltaX, deltaY) {
718 var target = e.target || null,
719 parent = $(target),
720 canScroll = false;
721
722 while (parent.length) {
723 if (canScroll || parent.is('.fancybox-skin') || parent.is('.fancybox-wrap')) {
724 break;
725 }
726
727 canScroll = isScrollable( parent[0] );
728 parent = $(parent).parent();
729 }
730
731 if (delta !== 0 && !canScroll) {
732 if (F.group.length > 1 && !current.canShrink) {
733 if (deltaY > 0 || deltaX > 0) {
734 F.prev( deltaY > 0 ? 'down' : 'left' );
735
736 } else if (deltaY < 0 || deltaX < 0) {
737 F.next( deltaY < 0 ? 'up' : 'right' );
738 }
739
740 e.preventDefault();
741 }
742 }
743 });
744 }
745 },
746
747 trigger: function (event, o) {
748 var ret, obj = o || F.coming || F.current;
749
750 if (obj) {
751 if ($.isFunction( obj[event] )) {
752 ret = obj[event].apply(obj, Array.prototype.slice.call(arguments, 1));
753 }
754
755 if (ret === false) {
756 return false;
757 }
758
759 if (obj.helpers) {
760 $.each(obj.helpers, function (helper, opts) {
761 if (opts && F.helpers[helper] && $.isFunction(F.helpers[helper][event])) {
762 F.helpers[helper][event]($.extend(true, {}, F.helpers[helper].defaults, opts), obj);
763 }
764 });
765 }
766 }
767
768 D.trigger(event);
769 },
770
771 isImage: function (str) {
772 return isString(str) && str.match(/(^data:image\/.*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg)((\?|#).*)?$)/i);
773 },
774
775 isSWF: function (str) {
776 return isString(str) && str.match(/\.(swf)((\?|#).*)?$/i);
777 },
778
779 _start: function (index) {
780 var coming = {},
781 obj,
782 href,
783 type,
784 margin,
785 padding;
786
787 index = getScalar( index );
788 obj = F.group[ index ] || null;
789
790 if (!obj) {
791 return false;
792 }
793
794 coming = $.extend(true, {}, F.opts, obj);
795
796 // Convert margin and padding properties to array - top, right, bottom, left
797 margin = coming.margin;
798 padding = coming.padding;
799
800 if ($.type(margin) === 'number') {
801 coming.margin = [margin, margin, margin, margin];
802 }
803
804 if ($.type(padding) === 'number') {
805 coming.padding = [padding, padding, padding, padding];
806 }
807
808 // 'modal' propery is just a shortcut
809 if (coming.modal) {
810 $.extend(true, coming, {
811 closeBtn : false,
812 closeClick : false,
813 nextClick : false,
814 arrows : false,
815 mouseWheel : false,
816 keys : null,
817 helpers: {
818 overlay : {
819 closeClick : false
820 }
821 }
822 });
823 }
824
825 // 'autoSize' property is a shortcut, too
826 if (coming.autoSize) {
827 coming.autoWidth = coming.autoHeight = true;
828 }
829
830 if (coming.width === 'auto') {
831 coming.autoWidth = true;
832 }
833
834 if (coming.height === 'auto') {
835 coming.autoHeight = true;
836 }
837
838 /*
839 * Add reference to the group, so it`s possible to access from callbacks, example:
840 * afterLoad : function() {
841 * this.title = 'Image ' + (this.index + 1) + ' of ' + this.group.length + (this.title ? ' - ' + this.title : '');
842 * }
843 */
844
845 coming.group = F.group;
846 coming.index = index;
847
848 // Give a chance for callback or helpers to update coming item (type, title, etc)
849 F.coming = coming;
850
851 if (false === F.trigger('beforeLoad')) {
852 F.coming = null;
853
854 return;
855 }
856
857 type = coming.type;
858 href = coming.href;
859
860 if (!type) {
861 F.coming = null;
862
863 //If we can not determine content type then drop silently or display next/prev item if looping through gallery
864 if (F.current && F.router && F.router !== 'jumpto') {
865 F.current.index = index;
866
867 return F[ F.router ]( F.direction );
868 }
869
870 return false;
871 }
872
873 F.isActive = true;
874
875 if (type === 'image' || type === 'swf') {
876 coming.autoHeight = coming.autoWidth = false;
877 coming.scrolling = 'visible';
878 }
879
880 if (type === 'image') {
881 coming.aspectRatio = true;
882 }
883
884 if (type === 'iframe' && isTouch) {
885 coming.scrolling = 'scroll';
886 }
887
888 // Build the neccessary markup
889 coming.wrap = $(coming.tpl.wrap).addClass('fancybox-' + (isTouch ? 'mobile' : 'desktop') + ' fancybox-type-' + type + ' fancybox-tmp ' + coming.wrapCSS).appendTo( coming.parent || 'body' );
890
891 $.extend(coming, {
892 skin : $('.fancybox-skin', coming.wrap),
893 outer : $('.fancybox-outer', coming.wrap),
894 inner : $('.fancybox-inner', coming.wrap)
895 });
896
897 $.each(["Top", "Right", "Bottom", "Left"], function(i, v) {
898 coming.skin.css('padding' + v, getValue(coming.padding[ i ]));
899 });
900
901 F.trigger('onReady');
902
903 // Check before try to load; 'inline' and 'html' types need content, others - href
904 if (type === 'inline' || type === 'html') {
905 if (!coming.content || !coming.content.length) {
906 return F._error( 'content' );
907 }
908
909 } else if (!href) {
910 return F._error( 'href' );
911 }
912
913 if (type === 'image') {
914 F._loadImage();
915
916 } else if (type === 'ajax') {
917 F._loadAjax();
918
919 } else if (type === 'iframe') {
920 F._loadIframe();
921
922 } else {
923 F._afterLoad();
924 }
925 },
926
927 _error: function ( type ) {
928 $.extend(F.coming, {
929 type : 'html',
930 autoWidth : true,
931 autoHeight : true,
932 minWidth : 0,
933 minHeight : 0,
934 scrolling : 'no',
935 hasError : type,
936 content : F.coming.tpl.error
937 });
938
939 F._afterLoad();
940 },
941
942 _loadImage: function () {
943 // Reset preload image so it is later possible to check "complete" property
944 var img = F.imgPreload = new Image();
945
946 img.onload = function () {
947 this.onload = this.onerror = null;
948
949 F.coming.width = this.width / F.opts.pixelRatio;
950 F.coming.height = this.height / F.opts.pixelRatio;
951
952 F._afterLoad();
953 };
954
955 img.onerror = function () {
956 this.onload = this.onerror = null;
957
958 F._error( 'image' );
959 };
960
961 img.src = F.coming.href;
962
963 if (img.complete !== true) {
964 F.showLoading();
965 }
966 },
967
968 _loadAjax: function () {
969 var coming = F.coming;
970
971 F.showLoading();
972
973 F.ajaxLoad = $.ajax($.extend({}, coming.ajax, {
974 url: coming.href,
975 error: function (jqXHR, textStatus) {
976 if (F.coming && textStatus !== 'abort') {
977 F._error( 'ajax', jqXHR );
978
979 } else {
980 F.hideLoading();
981 }
982 },
983 success: function (data, textStatus) {
984 if (textStatus === 'success') {
985 coming.content = data;
986
987 F._afterLoad();
988 }
989 }
990 }));
991 },
992
993 _loadIframe: function() {
994 var coming = F.coming,
995 iframe = $(coming.tpl.iframe.replace(/\{rnd\}/g, new Date().getTime()))
996 .attr('scrolling', isTouch ? 'auto' : coming.iframe.scrolling)
997 .attr('src', coming.href);
998
999 // This helps IE
1000 $(coming.wrap).bind('onReset', function () {
1001 try {
1002 $(this).find('iframe').hide().attr('src', '//about:blank').end().empty();
1003 } catch (e) {}
1004 });
1005
1006 if (coming.iframe.preload) {
1007 F.showLoading();
1008
1009 iframe.one('load', function() {
1010 $(this).data('ready', 1);
1011
1012 // iOS will lose scrolling if we resize
1013 if (!isTouch) {
1014 $(this).bind('load.fb', F.update);
1015 }
1016
1017 // Without this trick:
1018 // - iframe won't scroll on iOS devices
1019 // - IE7 sometimes displays empty iframe
1020 $(this).parents('.fancybox-wrap').width('100%').removeClass('fancybox-tmp').show();
1021
1022 F._afterLoad();
1023 });
1024 }
1025
1026 coming.content = iframe.appendTo( coming.inner );
1027
1028 if (!coming.iframe.preload) {
1029 F._afterLoad();
1030 }
1031 },
1032
1033 _preloadImages: function() {
1034 var group = F.group,
1035 current = F.current,
1036 len = group.length,
1037 cnt = current.preload ? Math.min(current.preload, len - 1) : 0,
1038 item,
1039 i;
1040
1041 for (i = 1; i <= cnt; i += 1) {
1042 item = group[ (current.index + i ) % len ];
1043
1044 if (item.type === 'image' && item.href) {
1045 new Image().src = item.href;
1046 }
1047 }
1048 },
1049
1050 _afterLoad: function () {
1051 var coming = F.coming,
1052 previous = F.current,
1053 placeholder = 'fancybox-placeholder',
1054 current,
1055 content,
1056 type,
1057 scrolling,
1058 href,
1059 embed;
1060
1061 F.hideLoading();
1062
1063 if (!coming || F.isActive === false) {
1064 return;
1065 }
1066
1067 if (false === F.trigger('afterLoad', coming, previous)) {
1068 coming.wrap.stop(true).trigger('onReset').remove();
1069
1070 F.coming = null;
1071
1072 return;
1073 }
1074
1075 if (previous) {
1076 F.trigger('beforeChange', previous);
1077
1078 previous.wrap.stop(true).removeClass('fancybox-opened')
1079 .find('.fancybox-item, .fancybox-nav')
1080 .remove();
1081 }
1082
1083 F.unbindEvents();
1084
1085 current = coming;
1086 content = coming.content;
1087 type = coming.type;
1088 scrolling = coming.scrolling;
1089
1090 $.extend(F, {
1091 wrap : current.wrap,
1092 skin : current.skin,
1093 outer : current.outer,
1094 inner : current.inner,
1095 current : current,
1096 previous : previous
1097 });
1098
1099 href = current.href;
1100
1101 switch (type) {
1102 case 'inline':
1103 case 'ajax':
1104 case 'html':
1105 if (current.selector) {
1106 content = $('<div>').html(content).find(current.selector);
1107
1108 } else if (isQuery(content)) {
1109 if (!content.data(placeholder)) {
1110 content.data(placeholder, $('<div class="' + placeholder + '"></div>').insertAfter( content ).hide() );
1111 }
1112
1113 content = content.show().detach();
1114
1115 current.wrap.bind('onReset', function () {
1116 if ($(this).find(content).length) {
1117 content.hide().replaceAll( content.data(placeholder) ).data(placeholder, false);
1118 }
1119 });
1120 }
1121 break;
1122
1123 case 'image':
1124 content = current.tpl.image.replace(/\{href\}/g, href);
1125 break;
1126
1127 case 'swf':
1128 content = '<object id="fancybox-swf" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="100%" height="100%"><param name="movie" value="' + href + '"></param>';
1129 embed = '';
1130
1131 $.each(current.swf, function(name, val) {
1132 content += '<param name="' + name + '" value="' + val + '"></param>';
1133 embed += ' ' + name + '="' + val + '"';
1134 });
1135
1136 content += '<embed src="' + href + '" type="application/x-shockwave-flash" width="100%" height="100%"' + embed + '></embed></object>';
1137 break;
1138 }
1139
1140 if (!(isQuery(content) && content.parent().is(current.inner))) {
1141 current.inner.append( content );
1142 }
1143
1144 // Give a chance for helpers or callbacks to update elements
1145 F.trigger('beforeShow');
1146
1147 // Set scrolling before calculating dimensions
1148 current.inner.css('overflow', scrolling === 'yes' ? 'scroll' : (scrolling === 'no' ? 'hidden' : scrolling));
1149
1150 // Set initial dimensions and start position
1151 F._setDimension();
1152
1153 F.reposition();
1154
1155 F.isOpen = false;
1156 F.coming = null;
1157
1158 F.bindEvents();
1159
1160 if (!F.isOpened) {
1161 $('.fancybox-wrap').not( current.wrap ).stop(true).trigger('onReset').remove();
1162
1163 } else if (previous.prevMethod) {
1164 F.transitions[ previous.prevMethod ]();
1165 }
1166
1167 F.transitions[ F.isOpened ? current.nextMethod : current.openMethod ]();
1168
1169 F._preloadImages();
1170 },
1171
1172 _setDimension: function () {
1173 var viewport = F.getViewport(),
1174 steps = 0,
1175 canShrink = false,
1176 canExpand = false,
1177 wrap = F.wrap,
1178 skin = F.skin,
1179 inner = F.inner,
1180 current = F.current,
1181 width = current.width,
1182 height = current.height,
1183 minWidth = current.minWidth,
1184 minHeight = current.minHeight,
1185 maxWidth = current.maxWidth,
1186 maxHeight = current.maxHeight,
1187 scrolling = current.scrolling,
1188 scrollOut = current.scrollOutside ? current.scrollbarWidth : 0,
1189 margin = current.margin,
1190 wMargin = getScalar(margin[1] + margin[3]),
1191 hMargin = getScalar(margin[0] + margin[2]),
1192 wPadding,
1193 hPadding,
1194 wSpace,
1195 hSpace,
1196 origWidth,
1197 origHeight,
1198 origMaxWidth,
1199 origMaxHeight,
1200 ratio,
1201 width_,
1202 height_,
1203 maxWidth_,
1204 maxHeight_,
1205 iframe,
1206 body;
1207
1208 // Reset dimensions so we could re-check actual size
1209 wrap.add(skin).add(inner).width('auto').height('auto').removeClass('fancybox-tmp');
1210
1211 wPadding = getScalar(skin.outerWidth(true) - skin.width());
1212 hPadding = getScalar(skin.outerHeight(true) - skin.height());
1213
1214 // Any space between content and viewport (margin, padding, border, title)
1215 wSpace = wMargin + wPadding;
1216 hSpace = hMargin + hPadding;
1217
1218 origWidth = isPercentage(width) ? (viewport.w - wSpace) * getScalar(width) / 100 : width;
1219 origHeight = isPercentage(height) ? (viewport.h - hSpace) * getScalar(height) / 100 : height;
1220
1221 if (current.type === 'iframe') {
1222 iframe = current.content;
1223
1224 if (current.autoHeight && iframe.data('ready') === 1) {
1225 try {
1226 if (iframe[0].contentWindow.document.location) {
1227 inner.width( origWidth ).height(9999);
1228
1229 body = iframe.contents().find('body');
1230
1231 if (scrollOut) {
1232 body.css('overflow-x', 'hidden');
1233 }
1234
1235 origHeight = body.outerHeight(true);
1236 }
1237
1238 } catch (e) {}
1239 }
1240
1241 } else if (current.autoWidth || current.autoHeight) {
1242 inner.addClass( 'fancybox-tmp' );
1243
1244 // Set width or height in case we need to calculate only one dimension
1245 if (!current.autoWidth) {
1246 inner.width( origWidth );
1247 }
1248
1249 if (!current.autoHeight) {
1250 inner.height( origHeight );
1251 }
1252
1253 if (current.autoWidth) {
1254 origWidth = inner.width();
1255 }
1256
1257 if (current.autoHeight) {
1258 origHeight = inner.height();
1259 }
1260
1261 inner.removeClass( 'fancybox-tmp' );
1262 }
1263
1264 width = getScalar( origWidth );
1265 height = getScalar( origHeight );
1266
1267 ratio = origWidth / origHeight;
1268
1269 // Calculations for the content
1270 minWidth = getScalar(isPercentage(minWidth) ? getScalar(minWidth, 'w') - wSpace : minWidth);
1271 maxWidth = getScalar(isPercentage(maxWidth) ? getScalar(maxWidth, 'w') - wSpace : maxWidth);
1272
1273 minHeight = getScalar(isPercentage(minHeight) ? getScalar(minHeight, 'h') - hSpace : minHeight);
1274 maxHeight = getScalar(isPercentage(maxHeight) ? getScalar(maxHeight, 'h') - hSpace : maxHeight);
1275
1276 // These will be used to determine if wrap can fit in the viewport
1277 origMaxWidth = maxWidth;
1278 origMaxHeight = maxHeight;
1279
1280 if (current.fitToView) {
1281 maxWidth = Math.min(viewport.w - wSpace, maxWidth);
1282 maxHeight = Math.min(viewport.h - hSpace, maxHeight);
1283 }
1284
1285 maxWidth_ = viewport.w - wMargin;
1286 maxHeight_ = viewport.h - hMargin;
1287
1288 if (current.aspectRatio) {
1289 if (width > maxWidth) {
1290 width = maxWidth;
1291 height = getScalar(width / ratio);
1292 }
1293
1294 if (height > maxHeight) {
1295 height = maxHeight;
1296 width = getScalar(height * ratio);
1297 }
1298
1299 if (width < minWidth) {
1300 width = minWidth;
1301 height = getScalar(width / ratio);
1302 }
1303
1304 if (height < minHeight) {
1305 height = minHeight;
1306 width = getScalar(height * ratio);
1307 }
1308
1309 } else {
1310 width = Math.max(minWidth, Math.min(width, maxWidth));
1311
1312 if (current.autoHeight && current.type !== 'iframe') {
1313 inner.width( width );
1314
1315 height = inner.height();
1316 }
1317
1318 height = Math.max(minHeight, Math.min(height, maxHeight));
1319 }
1320
1321 // Try to fit inside viewport (including the title)
1322 if (current.fitToView) {
1323 inner.width( width ).height( height );
1324
1325 wrap.width( width + wPadding );
1326
1327 // Real wrap dimensions
1328 width_ = wrap.width();
1329 height_ = wrap.height();
1330
1331 if (current.aspectRatio) {
1332 while ((width_ > maxWidth_ || height_ > maxHeight_) && width > minWidth && height > minHeight) {
1333 if (steps++ > 19) {
1334 break;
1335 }
1336
1337 height = Math.max(minHeight, Math.min(maxHeight, height - 10));
1338 width = getScalar(height * ratio);
1339
1340 if (width < minWidth) {
1341 width = minWidth;
1342 height = getScalar(width / ratio);
1343 }
1344
1345 if (width > maxWidth) {
1346 width = maxWidth;
1347 height = getScalar(width / ratio);
1348 }
1349
1350 inner.width( width ).height( height );
1351
1352 wrap.width( width + wPadding );
1353
1354 width_ = wrap.width();
1355 height_ = wrap.height();
1356 }
1357
1358 } else {
1359 width = Math.max(minWidth, Math.min(width, width - (width_ - maxWidth_)));
1360 height = Math.max(minHeight, Math.min(height, height - (height_ - maxHeight_)));
1361 }
1362 }
1363
1364 if (scrollOut && scrolling === 'auto' && height < origHeight && (width + wPadding + scrollOut) < maxWidth_) {
1365 width += scrollOut;
1366 }
1367
1368 inner.width( width ).height( height );
1369
1370 wrap.width( width + wPadding );
1371
1372 width_ = wrap.width();
1373 height_ = wrap.height();
1374
1375 canShrink = (width_ > maxWidth_ || height_ > maxHeight_) && width > minWidth && height > minHeight;
1376 canExpand = current.aspectRatio ? (width < origMaxWidth && height < origMaxHeight && width < origWidth && height < origHeight) : ((width < origMaxWidth || height < origMaxHeight) && (width < origWidth || height < origHeight));
1377
1378 $.extend(current, {
1379 dim : {
1380 width : getValue( width_ ),
1381 height : getValue( height_ )
1382 },
1383 origWidth : origWidth,
1384 origHeight : origHeight,
1385 canShrink : canShrink,
1386 canExpand : canExpand,
1387 wPadding : wPadding,
1388 hPadding : hPadding,
1389 wrapSpace : height_ - skin.outerHeight(true),
1390 skinSpace : skin.height() - height
1391 });
1392
1393 if (!iframe && current.autoHeight && height > minHeight && height < maxHeight && !canExpand) {
1394 inner.height('auto');
1395 }
1396 },
1397
1398 _getPosition: function (onlyAbsolute) {
1399 var current = F.current,
1400 viewport = F.getViewport(),
1401 margin = current.margin,
1402 width = F.wrap.width() + margin[1] + margin[3],
1403 height = F.wrap.height() + margin[0] + margin[2],
1404 rez = {
1405 position: 'absolute',
1406 top : margin[0],
1407 left : margin[3]
1408 };
1409
1410 if (current.autoCenter && current.fixed && !onlyAbsolute && height <= viewport.h && width <= viewport.w) {
1411 rez.position = 'fixed';
1412
1413 } else if (!current.locked) {
1414 rez.top += viewport.y;
1415 rez.left += viewport.x;
1416 }
1417
1418 rez.top = getValue(Math.max(rez.top, rez.top + ((viewport.h - height) * current.topRatio)));
1419 rez.left = getValue(Math.max(rez.left, rez.left + ((viewport.w - width) * current.leftRatio)));
1420
1421 return rez;
1422 },
1423
1424 _afterZoomIn: function () {
1425 var current = F.current;
1426
1427 if (!current) {
1428 return;
1429 }
1430
1431 F.isOpen = F.isOpened = true;
1432
1433 F.wrap.css('overflow', 'visible').addClass('fancybox-opened').hide().show(0);
1434
1435 F.update();
1436
1437 // Assign a click event
1438 if ( current.closeClick || (current.nextClick && F.group.length > 1) ) {
1439 F.inner.css('cursor', 'pointer').bind('click.fb', function(e) {
1440 if (!$(e.target).is('a') && !$(e.target).parent().is('a')) {
1441 e.preventDefault();
1442
1443 F[ current.closeClick ? 'close' : 'next' ]();
1444 }
1445 });
1446 }
1447
1448 // Create a close button
1449 if (current.closeBtn) {
1450 $(current.tpl.closeBtn).appendTo(F.skin).bind('click.fb', function(e) {
1451 e.preventDefault();
1452
1453 F.close();
1454 });
1455 }
1456
1457 // Create navigation arrows
1458 if (current.arrows && F.group.length > 1) {
1459 if (current.loop || current.index > 0) {
1460 $(current.tpl.prev).appendTo(F.outer).bind('click.fb', F.prev);
1461 }
1462
1463 if (current.loop || current.index < F.group.length - 1) {
1464 $(current.tpl.next).appendTo(F.outer).bind('click.fb', F.next);
1465 }
1466 }
1467
1468 F.trigger('afterShow');
1469
1470 // Stop the slideshow if this is the last item
1471 if (!current.loop && current.index === current.group.length - 1) {
1472
1473 F.play( false );
1474
1475 } else if (F.opts.autoPlay && !F.player.isActive) {
1476 F.opts.autoPlay = false;
1477
1478 F.play(true);
1479 }
1480 },
1481
1482 _afterZoomOut: function ( obj ) {
1483 obj = obj || F.current;
1484
1485 $('.fancybox-wrap').trigger('onReset').remove();
1486
1487 $.extend(F, {
1488 group : {},
1489 opts : {},
1490 router : false,
1491 current : null,
1492 isActive : false,
1493 isOpened : false,
1494 isOpen : false,
1495 isClosing : false,
1496 wrap : null,
1497 skin : null,
1498 outer : null,
1499 inner : null
1500 });
1501
1502 F.trigger('afterClose', obj);
1503 }
1504 });
1505
1506 /*
1507 * Default transitions
1508 */
1509
1510 F.transitions = {
1511 getOrigPosition: function () {
1512 var current = F.current,
1513 element = current.element,
1514 orig = current.orig,
1515 pos = {},
1516 width = 50,
1517 height = 50,
1518 hPadding = current.hPadding,
1519 wPadding = current.wPadding,
1520 viewport = F.getViewport();
1521
1522 if (!orig && current.isDom && element.is(':visible')) {
1523 orig = element.find('img:first');
1524
1525 if (!orig.length) {
1526 orig = element;
1527 }
1528 }
1529
1530 if (isQuery(orig)) {
1531 pos = orig.offset();
1532
1533 if (orig.is('img')) {
1534 width = orig.outerWidth();
1535 height = orig.outerHeight();
1536 }
1537
1538 } else {
1539 pos.top = viewport.y + (viewport.h - height) * current.topRatio;
1540 pos.left = viewport.x + (viewport.w - width) * current.leftRatio;
1541 }
1542
1543 if (F.wrap.css('position') === 'fixed' || current.locked) {
1544 pos.top -= viewport.y;
1545 pos.left -= viewport.x;
1546 }
1547
1548 pos = {
1549 top : getValue(pos.top - hPadding * current.topRatio),
1550 left : getValue(pos.left - wPadding * current.leftRatio),
1551 width : getValue(width + wPadding),
1552 height : getValue(height + hPadding)
1553 };
1554
1555 return pos;
1556 },
1557
1558 step: function (now, fx) {
1559 var ratio,
1560 padding,
1561 value,
1562 prop = fx.prop,
1563 current = F.current,
1564 wrapSpace = current.wrapSpace,
1565 skinSpace = current.skinSpace;
1566
1567 if (prop === 'width' || prop === 'height') {
1568 ratio = fx.end === fx.start ? 1 : (now - fx.start) / (fx.end - fx.start);
1569
1570 if (F.isClosing) {
1571 ratio = 1 - ratio;
1572 }
1573
1574 padding = prop === 'width' ? current.wPadding : current.hPadding;
1575 value = now - padding;
1576
1577 F.skin[ prop ]( getScalar( prop === 'width' ? value : value - (wrapSpace * ratio) ) );
1578 F.inner[ prop ]( getScalar( prop === 'width' ? value : value - (wrapSpace * ratio) - (skinSpace * ratio) ) );
1579 }
1580 },
1581
1582 zoomIn: function () {
1583 var current = F.current,
1584 startPos = current.pos,
1585 effect = current.openEffect,
1586 elastic = effect === 'elastic',
1587 endPos = $.extend({opacity : 1}, startPos);
1588
1589 // Remove "position" property that breaks older IE
1590 delete endPos.position;
1591
1592 if (elastic) {
1593 startPos = this.getOrigPosition();
1594
1595 if (current.openOpacity) {
1596 startPos.opacity = 0.1;
1597 }
1598
1599 } else if (effect === 'fade') {
1600 startPos.opacity = 0.1;
1601 }
1602
1603 F.wrap.css(startPos).animate(endPos, {
1604 duration : effect === 'none' ? 0 : current.openSpeed,
1605 easing : current.openEasing,
1606 step : elastic ? this.step : null,
1607 complete : F._afterZoomIn
1608 });
1609 },
1610
1611 zoomOut: function () {
1612 var current = F.current,
1613 effect = current.closeEffect,
1614 elastic = effect === 'elastic',
1615 endPos = {opacity : 0.1};
1616
1617 if (elastic) {
1618 endPos = this.getOrigPosition();
1619
1620 if (current.closeOpacity) {
1621 endPos.opacity = 0.1;
1622 }
1623 }
1624
1625 F.wrap.animate(endPos, {
1626 duration : effect === 'none' ? 0 : current.closeSpeed,
1627 easing : current.closeEasing,
1628 step : elastic ? this.step : null,
1629 complete : F._afterZoomOut
1630 });
1631 },
1632
1633 changeIn: function () {
1634 var current = F.current,
1635 effect = current.nextEffect,
1636 startPos = current.pos,
1637 endPos = { opacity : 1 },
1638 direction = F.direction,
1639 distance = 200,
1640 field;
1641
1642 startPos.opacity = 0.1;
1643
1644 if (effect === 'elastic') {
1645 field = direction === 'down' || direction === 'up' ? 'top' : 'left';
1646
1647 if (direction === 'down' || direction === 'right') {
1648 startPos[ field ] = getValue(getScalar(startPos[ field ]) - distance);
1649 endPos[ field ] = '+=' + distance + 'px';
1650
1651 } else {
1652 startPos[ field ] = getValue(getScalar(startPos[ field ]) + distance);
1653 endPos[ field ] = '-=' + distance + 'px';
1654 }
1655 }
1656
1657 // Workaround for http://bugs.jquery.com/ticket/12273
1658 if (effect === 'none') {
1659 F._afterZoomIn();
1660
1661 } else {
1662 F.wrap.css(startPos).animate(endPos, {
1663 duration : current.nextSpeed,
1664 easing : current.nextEasing,
1665 complete : F._afterZoomIn
1666 });
1667 }
1668 },
1669
1670 changeOut: function () {
1671 var previous = F.previous,
1672 effect = previous.prevEffect,
1673 endPos = { opacity : 0.1 },
1674 direction = F.direction,
1675 distance = 200;
1676
1677 if (effect === 'elastic') {
1678 endPos[ direction === 'down' || direction === 'up' ? 'top' : 'left' ] = ( direction === 'up' || direction === 'left' ? '-' : '+' ) + '=' + distance + 'px';
1679 }
1680
1681 previous.wrap.animate(endPos, {
1682 duration : effect === 'none' ? 0 : previous.prevSpeed,
1683 easing : previous.prevEasing,
1684 complete : function () {
1685 $(this).trigger('onReset').remove();
1686 }
1687 });
1688 }
1689 };
1690
1691 /*
1692 * Overlay helper
1693 */
1694
1695 F.helpers.overlay = {
1696 defaults : {
1697 closeClick : true, // if true, fancyBox will be closed when user clicks on the overlay
1698 speedOut : 200, // duration of fadeOut animation
1699 showEarly : true, // indicates if should be opened immediately or wait until the content is ready
1700 css : {}, // custom CSS properties
1701 locked : !isTouch, // if true, the content will be locked into overlay
1702 fixed : true // if false, the overlay CSS position property will not be set to "fixed"
1703 },
1704
1705 overlay : null, // current handle
1706 fixed : false, // indicates if the overlay has position "fixed"
1707 el : $('html'), // element that contains "the lock"
1708
1709 // Public methods
1710 create : function(opts) {
1711 var parent;
1712
1713 opts = $.extend({}, this.defaults, opts);
1714
1715 if (this.overlay) {
1716 this.close();
1717 }
1718
1719 parent = F.coming ? F.coming.parent : opts.parent;
1720
1721 this.overlay = $('<div class="fancybox-overlay"></div>').appendTo( parent && parent.lenth ? parent : 'body' );
1722 this.fixed = false;
1723
1724 if (opts.fixed && F.defaults.fixed) {
1725 this.overlay.addClass('fancybox-overlay-fixed');
1726
1727 this.fixed = true;
1728 }
1729 },
1730
1731 open : function(opts) {
1732 var that = this;
1733
1734 opts = $.extend({}, this.defaults, opts);
1735
1736 if (this.overlay) {
1737 this.overlay.unbind('.overlay').width('auto').height('auto');
1738
1739 } else {
1740 this.create(opts);
1741 }
1742
1743 if (!this.fixed) {
1744 W.bind('resize.overlay', $.proxy( this.update, this) );
1745
1746 this.update();
1747 }
1748
1749 if (opts.closeClick) {
1750 this.overlay.bind('click.overlay', function(e) {
1751 if ($(e.target).hasClass('fancybox-overlay')) {
1752 if (F.isActive) {
1753 F.close();
1754 } else {
1755 that.close();
1756 }
1757
1758 return false;
1759 }
1760 });
1761 }
1762
1763 this.overlay.css( opts.css ).show();
1764 },
1765
1766 close : function() {
1767 W.unbind('resize.overlay');
1768
1769 if (this.el.hasClass('fancybox-lock')) {
1770 $('.fancybox-margin').removeClass('fancybox-margin');
1771
1772 this.el.removeClass('fancybox-lock');
1773
1774 W.scrollTop( this.scrollV ).scrollLeft( this.scrollH );
1775 }
1776
1777 $('.fancybox-overlay').remove().hide();
1778
1779 $.extend(this, {
1780 overlay : null,
1781 fixed : false
1782 });
1783 },
1784
1785 // Private, callbacks
1786
1787 update : function () {
1788 var width = '100%', offsetWidth;
1789
1790 // Reset width/height so it will not mess
1791 this.overlay.width(width).height('100%');
1792
1793 // jQuery does not return reliable result for IE
1794 if (IE) {
1795 offsetWidth = Math.max(document.documentElement.offsetWidth, document.body.offsetWidth);
1796
1797 if (D.width() > offsetWidth) {
1798 width = D.width();
1799 }
1800
1801 } else if (D.width() > W.width()) {
1802 width = D.width();
1803 }
1804
1805 this.overlay.width(width).height(D.height());
1806 },
1807
1808 // This is where we can manipulate DOM, because later it would cause iframes to reload
1809 onReady : function (opts, obj) {
1810 var overlay = this.overlay;
1811
1812 $('.fancybox-overlay').stop(true, true);
1813
1814 if (!overlay) {
1815 this.create(opts);
1816 }
1817
1818 if (opts.locked && this.fixed && obj.fixed) {
1819 obj.locked = this.overlay.append( obj.wrap );
1820 obj.fixed = false;
1821 }
1822
1823 if (opts.showEarly === true) {
1824 this.beforeShow.apply(this, arguments);
1825 }
1826 },
1827
1828 beforeShow : function(opts, obj) {
1829 if (obj.locked && !this.el.hasClass('fancybox-lock')) {
1830 if (this.fixPosition !== false) {
1831 $('*').filter(function(){
1832 return ($(this).css('position') === 'fixed' && !$(this).hasClass("fancybox-overlay") && !$(this).hasClass("fancybox-wrap") );
1833 }).addClass('fancybox-margin');
1834 }
1835
1836 this.el.addClass('fancybox-margin');
1837
1838 this.scrollV = W.scrollTop();
1839 this.scrollH = W.scrollLeft();
1840
1841 this.el.addClass('fancybox-lock');
1842
1843 W.scrollTop( this.scrollV ).scrollLeft( this.scrollH );
1844 }
1845
1846 this.open(opts);
1847 },
1848
1849 onUpdate : function() {
1850 if (!this.fixed) {
1851 this.update();
1852 }
1853 },
1854
1855 afterClose: function (opts) {
1856 // Remove overlay if exists and fancyBox is not opening
1857 // (e.g., it is not being open using afterClose callback)
1858 if (this.overlay && !F.coming) {
1859 this.overlay.fadeOut(opts.speedOut, $.proxy( this.close, this ));
1860 }
1861 }
1862 };
1863
1864 /*
1865 * Title helper
1866 */
1867
1868 F.helpers.title = {
1869 defaults : {
1870 type : 'float', // 'float', 'inside', 'outside' or 'over',
1871 position : 'bottom' // 'top' or 'bottom'
1872 },
1873
1874 beforeShow: function (opts) {
1875 var current = F.current,
1876 text = current.title,
1877 type = opts.type,
1878 title,
1879 target;
1880
1881 if ($.isFunction(text)) {
1882 text = text.call(current.element, current);
1883 }
1884
1885 if (!isString(text) || $.trim(text) === '') {
1886 return;
1887 }
1888
1889 title = $('<div class="fancybox-title fancybox-title-' + type + '-wrap">' + text + '</div>');
1890
1891 switch (type) {
1892 case 'inside':
1893 target = F.skin;
1894 break;
1895
1896 case 'outside':
1897 target = F.wrap;
1898 break;
1899
1900 case 'over':
1901 target = F.inner;
1902 break;
1903
1904 default: // 'float'
1905 target = F.skin;
1906
1907 title.appendTo('body');
1908
1909 if (IE) {
1910 title.width( title.width() );
1911 }
1912
1913 title.wrapInner('<span class="child"></span>');
1914
1915 //Increase bottom margin so this title will also fit into viewport
1916 F.current.margin[2] += Math.abs( getScalar(title.css('margin-bottom')) );
1917 break;
1918 }
1919
1920 title[ (opts.position === 'top' ? 'prependTo' : 'appendTo') ](target);
1921 }
1922 };
1923
1924 // jQuery plugin initialization
1925 $.fn.fancybox = function (options) {
1926 var index,
1927 that = $(this),
1928 selector = this.selector || '',
1929 run = function(e) {
1930 var what = $(this).blur(), idx = index, relType, relVal;
1931
1932 if (!(e.ctrlKey || e.altKey || e.shiftKey || e.metaKey) && !what.is('.fancybox-wrap')) {
1933 relType = options.groupAttr || 'data-fancybox-group';
1934 relVal = what.attr(relType);
1935
1936 if (!relVal) {
1937 relType = 'rel';
1938 relVal = what.get(0)[ relType ];
1939 }
1940
1941 if (relVal && relVal !== '' && relVal !== 'nofollow') {
1942 what = selector.length ? $(selector) : that;
1943 what = what.filter('[' + relType + '="' + relVal + '"]');
1944 idx = what.index(this);
1945 }
1946
1947 options.index = idx;
1948
1949 // Stop an event from bubbling if everything is fine
1950 if (F.open(what, options) !== false) {
1951 e.preventDefault();
1952 }
1953 }
1954 };
1955
1956 options = options || {};
1957 index = options.index || 0;
1958
1959 if (!selector || options.live === false) {
1960 that.unbind('click.fb-start').bind('click.fb-start', run);
1961
1962 } else {
1963 D.undelegate(selector, 'click.fb-start').delegate(selector + ":not('.fancybox-item, .fancybox-nav')", 'click.fb-start', run);
1964 }
1965
1966 this.filter('[data-fancybox-start=1]').trigger('click');
1967
1968 return this;
1969 };
1970
1971 // Tests that need a body at doc ready
1972 D.ready(function() {
1973 var w1, w2;
1974
1975 if ( $.scrollbarWidth === undefined ) {
1976 // http://benalman.com/projects/jquery-misc-plugins/#scrollbarwidth
1977 $.scrollbarWidth = function() {
1978 var parent = $('<div style="width:50px;height:50px;overflow:auto"><div/></div>').appendTo('body'),
1979 child = parent.children(),
1980 width = child.innerWidth() - child.height( 99 ).innerWidth();
1981
1982 parent.remove();
1983
1984 return width;
1985 };
1986 }
1987
1988 if ( $.support.fixedPosition === undefined ) {
1989 $.support.fixedPosition = (function() {
1990 var elem = $('<div style="position:fixed;top:20px;"></div>').appendTo('body'),
1991 fixed = ( elem[0].offsetTop === 20 || elem[0].offsetTop === 15 );
1992
1993 elem.remove();
1994
1995 return fixed;
1996 }());
1997 }
1998
1999 $.extend(F.defaults, {
2000 scrollbarWidth : $.scrollbarWidth(),
2001 fixed : $.support.fixedPosition,
2002 parent : $('body')
2003 });
2004
2005 //Get real width of page scroll-bar
2006 w1 = $(window).width();
2007
2008 H.addClass('fancybox-lock-test');
2009
2010 w2 = $(window).width();
2011
2012 H.removeClass('fancybox-lock-test');
2013
2014 $("<style type='text/css'>.fancybox-margin{margin-right:" + (w2 - w1) + "px;}</style>").appendTo("head");
2015 });
2016
2017}(window, document, jQuery));