hjg
2023-11-18 bb48edb3d9faaaeab0088151c86fc24137acdb08
提交 | 用户 | 时间
58d006 1 /*!
A 2     Colorbox 1.6.3
3     license: MIT
4     http://www.jacklmoore.com/colorbox
5 */
6 (function ($, document, window) {
7     var
8     // Default settings object.
9     // See http://jacklmoore.com/colorbox for details.
10     defaults = {
11         // data sources
12         html: false,
13         photo: false,
14         iframe: false,
15         inline: false,
16
17         // behavior and appearance
18         transition: "elastic",
19         speed: 300,
20         fadeOut: 300,
21         width: false,
22         initialWidth: "600",
23         innerWidth: false,
24         maxWidth: false,
25         height: false,
26         initialHeight: "450",
27         innerHeight: false,
28         maxHeight: false,
29         scalePhotos: true,
30         scrolling: true,
31         opacity: 0.9,
32         preloading: true,
33         className: false,
34         overlayClose: true,
35         escKey: true,
36         arrowKey: true,
37         top: false,
38         bottom: false,
39         left: false,
40         right: false,
41         fixed: false,
42         data: undefined,
43         closeButton: true,
44         fastIframe: true,
45         open: false,
46         reposition: true,
47         loop: true,
48         slideshow: false,
49         slideshowAuto: true,
50         slideshowSpeed: 2500,
51         slideshowStart: "start slideshow",
52         slideshowStop: "stop slideshow",
53         photoRegex: /\.(gif|png|jp(e|g|eg)|bmp|ico|webp|jxr|svg)((#|\?).*)?$/i,
54
55         // alternate image paths for high-res displays
56         retinaImage: false,
57         retinaUrl: false,
58         retinaSuffix: '@2x.$1',
59
60         // internationalization
61         current: "image {current} of {total}",
62         previous: "previous",
63         next: "next",
64         close: "close",
65         xhrError: "This content failed to load.",
66         imgError: "This image failed to load.",
67
68         // accessbility
69         returnFocus: true,
70         trapFocus: true,
71
72         // callbacks
73         onOpen: false,
74         onLoad: false,
75         onComplete: false,
76         onCleanup: false,
77         onClosed: false,
78
79         rel: function() {
80             return this.rel;
81         },
82         href: function() {
83             // using this.href would give the absolute url, when the href may have been inteded as a selector (e.g. '#container')
84             return $(this).attr('href');
85         },
86         title: function() {
87             return this.title;
88         },
89         createImg: function() {
90             var img = new Image();
91             var attrs = $(this).data('cbox-img-attrs');
92
93             if (typeof attrs === 'object') {
94                 $.each(attrs, function(key, val){
95                     img[key] = val;
96                 });
97             }
98
99             return img;
100         },
101         createIframe: function() {
102             var iframe = document.createElement('iframe');
103             var attrs = $(this).data('cbox-iframe-attrs');
104
105             if (typeof attrs === 'object') {
106                 $.each(attrs, function(key, val){
107                     iframe[key] = val;
108                 });
109             }
110
111             if ('frameBorder' in iframe) {
112                 iframe.frameBorder = 0;
113             }
114             if ('allowTransparency' in iframe) {
115                 iframe.allowTransparency = "true";
116             }
117             iframe.name = (new Date()).getTime(); // give the iframe a unique name to prevent caching
118             iframe.allowFullscreen = true;
119
120             return iframe;
121         }
122     },
123
124     // Abstracting the HTML and event identifiers for easy rebranding
125     colorbox = 'colorbox',
126     prefix = 'cbox',
127     boxElement = prefix + 'Element',
128
129     // Events
130     event_open = prefix + '_open',
131     event_load = prefix + '_load',
132     event_complete = prefix + '_complete',
133     event_cleanup = prefix + '_cleanup',
134     event_closed = prefix + '_closed',
135     event_purge = prefix + '_purge',
136
137     // Cached jQuery Object Variables
138     $overlay,
139     $box,
140     $wrap,
141     $content,
142     $topBorder,
143     $leftBorder,
144     $rightBorder,
145     $bottomBorder,
146     $related,
147     $window,
148     $loaded,
149     $loadingBay,
150     $loadingOverlay,
151     $title,
152     $current,
153     $slideshow,
154     $next,
155     $prev,
156     $close,
157     $groupControls,
158     $events = $('<a/>'), // $({}) would be prefered, but there is an issue with jQuery 1.4.2
159
160     // Variables for cached values or use across multiple functions
161     settings,
162     interfaceHeight,
163     interfaceWidth,
164     loadedHeight,
165     loadedWidth,
166     index,
167     photo,
168     open,
169     active,
170     closing,
171     loadingTimer,
172     publicMethod,
173     div = "div",
174     requests = 0,
175     previousCSS = {},
176     init;
177
178     // ****************
179     // HELPER FUNCTIONS
180     // ****************
181
182     // Convenience function for creating new jQuery objects
183     function $tag(tag, id, css) {
184         var element = document.createElement(tag);
185
186         if (id) {
187             element.id = prefix + id;
188         }
189
190         if (css) {
191             element.style.cssText = css;
192         }
193
194         return $(element);
195     }
196
197     // Get the window height using innerHeight when available to avoid an issue with iOS
198     // http://bugs.jquery.com/ticket/6724
199     function winheight() {
200         return window.innerHeight ? window.innerHeight : $(window).height();
201     }
202
203     function Settings(element, options) {
204         if (options !== Object(options)) {
205             options = {};
206         }
207
208         this.cache = {};
209         this.el = element;
210
211         this.value = function(key) {
212             var dataAttr;
213
214             if (this.cache[key] === undefined) {
215                 dataAttr = $(this.el).attr('data-cbox-'+key);
216
217                 if (dataAttr !== undefined) {
218                     this.cache[key] = dataAttr;
219                 } else if (options[key] !== undefined) {
220                     this.cache[key] = options[key];
221                 } else if (defaults[key] !== undefined) {
222                     this.cache[key] = defaults[key];
223                 }
224             }
225
226             return this.cache[key];
227         };
228
229         this.get = function(key) {
230             var value = this.value(key);
231             return $.isFunction(value) ? value.call(this.el, this) : value;
232         };
233     }
234
235     // Determine the next and previous members in a group.
236     function getIndex(increment) {
237         var
238         max = $related.length,
239         newIndex = (index + increment) % max;
240
241         return (newIndex < 0) ? max + newIndex : newIndex;
242     }
243
244     // Convert '%' and 'px' values to integers
245     function setSize(size, dimension) {
246         return Math.round((/%/.test(size) ? ((dimension === 'x' ? $window.width() : winheight()) / 100) : 1) * parseInt(size, 10));
247     }
248
249     // Checks an href to see if it is a photo.
250     // There is a force photo option (photo: true) for hrefs that cannot be matched by the regex.
251     function isImage(settings, url) {
252         return settings.get('photo') || settings.get('photoRegex').test(url);
253     }
254
255     function retinaUrl(settings, url) {
256         return settings.get('retinaUrl') && window.devicePixelRatio > 1 ? url.replace(settings.get('photoRegex'), settings.get('retinaSuffix')) : url;
257     }
258
259     function trapFocus(e) {
260         if ('contains' in $box[0] && !$box[0].contains(e.target) && e.target !== $overlay[0]) {
261             e.stopPropagation();
262             $box.focus();
263         }
264     }
265
266     function setClass(str) {
267         if (setClass.str !== str) {
268             $box.add($overlay).removeClass(setClass.str).addClass(str);
269             setClass.str = str;
270         }
271     }
272
273     function getRelated(rel) {
274         index = 0;
275
276         if (rel && rel !== false && rel !== 'nofollow') {
277             $related = $('.' + boxElement).filter(function () {
278                 var options = $.data(this, colorbox);
279                 var settings = new Settings(this, options);
280                 return (settings.get('rel') === rel);
281             });
282             index = $related.index(settings.el);
283
284             // Check direct calls to Colorbox.
285             if (index === -1) {
286                 $related = $related.add(settings.el);
287                 index = $related.length - 1;
288             }
289         } else {
290             $related = $(settings.el);
291         }
292     }
293
294     function trigger(event) {
295         // for external use
296         $(document).trigger(event);
297         // for internal use
298         $events.triggerHandler(event);
299     }
300
301     var slideshow = (function(){
302         var active,
303             className = prefix + "Slideshow_",
304             click = "click." + prefix,
305             timeOut;
306
307         function clear () {
308             clearTimeout(timeOut);
309         }
310
311         function set() {
312             if (settings.get('loop') || $related[index + 1]) {
313                 clear();
314                 timeOut = setTimeout(publicMethod.next, settings.get('slideshowSpeed'));
315             }
316         }
317
318         function start() {
319             $slideshow
320                 .html(settings.get('slideshowStop'))
321                 .unbind(click)
322                 .one(click, stop);
323
324             $events
325                 .bind(event_complete, set)
326                 .bind(event_load, clear);
327
328             $box.removeClass(className + "off").addClass(className + "on");
329         }
330
331         function stop() {
332             clear();
333
334             $events
335                 .unbind(event_complete, set)
336                 .unbind(event_load, clear);
337
338             $slideshow
339                 .html(settings.get('slideshowStart'))
340                 .unbind(click)
341                 .one(click, function () {
342                     publicMethod.next();
343                     start();
344                 });
345
346             $box.removeClass(className + "on").addClass(className + "off");
347         }
348
349         function reset() {
350             active = false;
351             $slideshow.hide();
352             clear();
353             $events
354                 .unbind(event_complete, set)
355                 .unbind(event_load, clear);
356             $box.removeClass(className + "off " + className + "on");
357         }
358
359         return function(){
360             if (active) {
361                 if (!settings.get('slideshow')) {
362                     $events.unbind(event_cleanup, reset);
363                     reset();
364                 }
365             } else {
366                 if (settings.get('slideshow') && $related[1]) {
367                     active = true;
368                     $events.one(event_cleanup, reset);
369                     if (settings.get('slideshowAuto')) {
370                         start();
371                     } else {
372                         stop();
373                     }
374                     $slideshow.show();
375                 }
376             }
377         };
378
379     }());
380
381
382     function launch(element) {
383         var options;
384
385         if (!closing) {
386
387             options = $(element).data(colorbox);
388
389             settings = new Settings(element, options);
390
391             getRelated(settings.get('rel'));
392
393             if (!open) {
394                 open = active = true; // Prevents the page-change action from queuing up if the visitor holds down the left or right keys.
395
396                 setClass(settings.get('className'));
397
398                 // Show colorbox so the sizes can be calculated in older versions of jQuery
399                 $box.css({visibility:'hidden', display:'block', opacity:''});
400
401                 $loaded = $tag(div, 'LoadedContent', 'width:0; height:0; overflow:hidden; visibility:hidden');
402                 $content.css({width:'', height:''}).append($loaded);
403
404                 // Cache values needed for size calculations
405                 interfaceHeight = $topBorder.height() + $bottomBorder.height() + $content.outerHeight(true) - $content.height();
406                 interfaceWidth = $leftBorder.width() + $rightBorder.width() + $content.outerWidth(true) - $content.width();
407                 loadedHeight = $loaded.outerHeight(true);
408                 loadedWidth = $loaded.outerWidth(true);
409
410                 // Opens inital empty Colorbox prior to content being loaded.
411                 var initialWidth = setSize(settings.get('initialWidth'), 'x');
412                 var initialHeight = setSize(settings.get('initialHeight'), 'y');
413                 var maxWidth = settings.get('maxWidth');
414                 var maxHeight = settings.get('maxHeight');
415
416                 settings.w = Math.max((maxWidth !== false ? Math.min(initialWidth, setSize(maxWidth, 'x')) : initialWidth) - loadedWidth - interfaceWidth, 0);
417                 settings.h = Math.max((maxHeight !== false ? Math.min(initialHeight, setSize(maxHeight, 'y')) : initialHeight) - loadedHeight - interfaceHeight, 0);
418
419                 $loaded.css({width:'', height:settings.h});
420                 publicMethod.position();
421
422                 trigger(event_open);
423                 settings.get('onOpen');
424
425                 $groupControls.add($title).hide();
426
427                 $box.focus();
428
429                 if (settings.get('trapFocus')) {
430                     // Confine focus to the modal
431                     // Uses event capturing that is not supported in IE8-
432                     if (document.addEventListener) {
433
434                         document.addEventListener('focus', trapFocus, true);
435
436                         $events.one(event_closed, function () {
437                             document.removeEventListener('focus', trapFocus, true);
438                         });
439                     }
440                 }
441
442                 // Return focus on closing
443                 if (settings.get('returnFocus')) {
444                     $events.one(event_closed, function () {
445                         $(settings.el).focus();
446                     });
447                 }
448             }
449
450             var opacity = parseFloat(settings.get('opacity'));
451             $overlay.css({
452                 opacity: opacity === opacity ? opacity : '',
453                 cursor: settings.get('overlayClose') ? 'pointer' : '',
454                 visibility: 'visible'
455             }).show();
456
457             if (settings.get('closeButton')) {
458                 $close.html(settings.get('close')).appendTo($content);
459             } else {
460                 $close.appendTo('<div/>'); // replace with .detach() when dropping jQuery < 1.4
461             }
462
463             load();
464         }
465     }
466
467     // Colorbox's markup needs to be added to the DOM prior to being called
468     // so that the browser will go ahead and load the CSS background images.
469     function appendHTML() {
470         if (!$box) {
471             init = false;
472             $window = $(window);
473             $box = $tag(div).attr({
474                 id: colorbox,
475                 'class': $.support.opacity === false ? prefix + 'IE' : '', // class for optional IE8 & lower targeted CSS.
476                 role: 'dialog',
477                 tabindex: '-1'
478             }).hide();
479             $overlay = $tag(div, "Overlay").hide();
480             $loadingOverlay = $([$tag(div, "LoadingOverlay")[0],$tag(div, "LoadingGraphic")[0]]);
481             $wrap = $tag(div, "Wrapper");
482             $content = $tag(div, "Content").append(
483                 $title = $tag(div, "Title"),
484                 $current = $tag(div, "Current"),
485                 $prev = $('<button type="button"/>').attr({id:prefix+'Previous'}),
486                 $next = $('<button type="button"/>').attr({id:prefix+'Next'}),
487                 $slideshow = $tag('button', "Slideshow"),
488                 $loadingOverlay
489             );
490
491             $close = $('<button type="button"/>').attr({id:prefix+'Close'});
492
493             $wrap.append( // The 3x3 Grid that makes up Colorbox
494                 $tag(div).append(
495                     $tag(div, "TopLeft"),
496                     $topBorder = $tag(div, "TopCenter"),
497                     $tag(div, "TopRight")
498                 ),
499                 $tag(div, false, 'clear:left').append(
500                     $leftBorder = $tag(div, "MiddleLeft"),
501                     $content,
502                     $rightBorder = $tag(div, "MiddleRight")
503                 ),
504                 $tag(div, false, 'clear:left').append(
505                     $tag(div, "BottomLeft"),
506                     $bottomBorder = $tag(div, "BottomCenter"),
507                     $tag(div, "BottomRight")
508                 )
509             ).find('div div').css({'float': 'left'});
510
511             $loadingBay = $tag(div, false, 'position:absolute; width:9999px; visibility:hidden; display:none; max-width:none;');
512
513             $groupControls = $next.add($prev).add($current).add($slideshow);
514         }
515         if (document.body && !$box.parent().length) {
516             $(document.body).append($overlay, $box.append($wrap, $loadingBay));
517         }
518     }
519
520     // Add Colorbox's event bindings
521     function addBindings() {
522         function clickHandler(e) {
523             // ignore non-left-mouse-clicks and clicks modified with ctrl / command, shift, or alt.
524             // See: http://jacklmoore.com/notes/click-events/
525             if (!(e.which > 1 || e.shiftKey || e.altKey || e.metaKey || e.ctrlKey)) {
526                 e.preventDefault();
527                 launch(this);
528             }
529         }
530
531         if ($box) {
532             if (!init) {
533                 init = true;
534
535                 // Anonymous functions here keep the public method from being cached, thereby allowing them to be redefined on the fly.
536                 $next.click(function () {
537                     publicMethod.next();
538                 });
539                 $prev.click(function () {
540                     publicMethod.prev();
541                 });
542                 $close.click(function () {
543                     publicMethod.close();
544                 });
545                 $overlay.click(function () {
546                     if (settings.get('overlayClose')) {
547                         publicMethod.close();
548                     }
549                 });
550
551                 // Key Bindings
552                 $(document).bind('keydown.' + prefix, function (e) {
553                     var key = e.keyCode;
554                     if (open && settings.get('escKey') && key === 27) {
555                         e.preventDefault();
556                         publicMethod.close();
557                     }
558                     if (open && settings.get('arrowKey') && $related[1] && !e.altKey) {
559                         if (key === 37) {
560                             e.preventDefault();
561                             $prev.click();
562                         } else if (key === 39) {
563                             e.preventDefault();
564                             $next.click();
565                         }
566                     }
567                 });
568
569                 if ($.isFunction($.fn.on)) {
570                     // For jQuery 1.7+
571                     $(document).on('click.'+prefix, '.'+boxElement, clickHandler);
572                 } else {
573                     // For jQuery 1.3.x -> 1.6.x
574                     // This code is never reached in jQuery 1.9, so do not contact me about 'live' being removed.
575                     // This is not here for jQuery 1.9, it's here for legacy users.
576                     $('.'+boxElement).live('click.'+prefix, clickHandler);
577                 }
578             }
579             return true;
580         }
581         return false;
582     }
583
584     // Don't do anything if Colorbox already exists.
585     if ($[colorbox]) {
586         return;
587     }
588
589     // Append the HTML when the DOM loads
590     $(appendHTML);
591
592
593     // ****************
594     // PUBLIC FUNCTIONS
595     // Usage format: $.colorbox.close();
596     // Usage from within an iframe: parent.jQuery.colorbox.close();
597     // ****************
598
599     publicMethod = $.fn[colorbox] = $[colorbox] = function (options, callback) {
600         var settings;
601         var $obj = this;
602
603         options = options || {};
604
605         if ($.isFunction($obj)) { // assume a call to $.colorbox
606             $obj = $('<a/>');
607             options.open = true;
608         }
609
610         if (!$obj[0]) { // colorbox being applied to empty collection
611             return $obj;
612         }
613
614         appendHTML();
615
616         if (addBindings()) {
617
618             if (callback) {
619                 options.onComplete = callback;
620             }
621
622             $obj.each(function () {
623                 var old = $.data(this, colorbox) || {};
624                 $.data(this, colorbox, $.extend(old, options));
625             }).addClass(boxElement);
626
627             settings = new Settings($obj[0], options);
628
629             if (settings.get('open')) {
630                 launch($obj[0]);
631             }
632         }
633
634         return $obj;
635     };
636
637     publicMethod.position = function (speed, loadedCallback) {
638         var
639         css,
640         top = 0,
641         left = 0,
642         offset = $box.offset(),
643         scrollTop,
644         scrollLeft;
645
646         $window.unbind('resize.' + prefix);
647
648         // remove the modal so that it doesn't influence the document width/height
649         $box.css({top: -9e4, left: -9e4});
650
651         scrollTop = $window.scrollTop();
652         scrollLeft = $window.scrollLeft();
653
654         if (settings.get('fixed')) {
655             offset.top -= scrollTop;
656             offset.left -= scrollLeft;
657             $box.css({position: 'fixed'});
658         } else {
659             top = scrollTop;
660             left = scrollLeft;
661             $box.css({position: 'absolute'});
662         }
663
664         // keeps the top and left positions within the browser's viewport.
665         if (settings.get('right') !== false) {
666             left += Math.max($window.width() - settings.w - loadedWidth - interfaceWidth - setSize(settings.get('right'), 'x'), 0);
667         } else if (settings.get('left') !== false) {
668             left += setSize(settings.get('left'), 'x');
669         } else {
670             left += Math.round(Math.max($window.width() - settings.w - loadedWidth - interfaceWidth, 0) / 2);
671         }
672
673         if (settings.get('bottom') !== false) {
674             top += Math.max(winheight() - settings.h - loadedHeight - interfaceHeight - setSize(settings.get('bottom'), 'y'), 0);
675         } else if (settings.get('top') !== false) {
676             top += setSize(settings.get('top'), 'y');
677         } else {
678             top += Math.round(Math.max(winheight() - settings.h - loadedHeight - interfaceHeight, 0) / 2);
679         }
680
681         $box.css({top: offset.top, left: offset.left, visibility:'visible'});
682
683         // this gives the wrapper plenty of breathing room so it's floated contents can move around smoothly,
684         // but it has to be shrank down around the size of div#colorbox when it's done.  If not,
685         // it can invoke an obscure IE bug when using iframes.
686         $wrap[0].style.width = $wrap[0].style.height = "9999px";
687
688         function modalDimensions() {
689             $topBorder[0].style.width = $bottomBorder[0].style.width = $content[0].style.width = (parseInt($box[0].style.width,10) - interfaceWidth)+'px';
690             $content[0].style.height = $leftBorder[0].style.height = $rightBorder[0].style.height = (parseInt($box[0].style.height,10) - interfaceHeight)+'px';
691         }
692
693         css = {width: settings.w + loadedWidth + interfaceWidth, height: settings.h + loadedHeight + interfaceHeight, top: top, left: left};
694
695         // setting the speed to 0 if the content hasn't changed size or position
696         if (speed) {
697             var tempSpeed = 0;
698             $.each(css, function(i){
699                 if (css[i] !== previousCSS[i]) {
700                     tempSpeed = speed;
701                     return;
702                 }
703             });
704             speed = tempSpeed;
705         }
706
707         previousCSS = css;
708
709         if (!speed) {
710             $box.css(css);
711         }
712
713         $box.dequeue().animate(css, {
714             duration: speed || 0,
715             complete: function () {
716                 modalDimensions();
717
718                 active = false;
719
720                 // shrink the wrapper down to exactly the size of colorbox to avoid a bug in IE's iframe implementation.
721                 $wrap[0].style.width = (settings.w + loadedWidth + interfaceWidth) + "px";
722                 $wrap[0].style.height = (settings.h + loadedHeight + interfaceHeight) + "px";
723
724                 if (settings.get('reposition')) {
725                     setTimeout(function () {  // small delay before binding onresize due to an IE8 bug.
726                         $window.bind('resize.' + prefix, publicMethod.position);
727                     }, 1);
728                 }
729
730                 if ($.isFunction(loadedCallback)) {
731                     loadedCallback();
732                 }
733             },
734             step: modalDimensions
735         });
736     };
737
738     publicMethod.resize = function (options) {
739         var scrolltop;
740
741         if (open) {
742             options = options || {};
743
744             if (options.width) {
745                 settings.w = setSize(options.width, 'x') - loadedWidth - interfaceWidth;
746             }
747
748             if (options.innerWidth) {
749                 settings.w = setSize(options.innerWidth, 'x');
750             }
751
752             $loaded.css({width: settings.w});
753
754             if (options.height) {
755                 settings.h = setSize(options.height, 'y') - loadedHeight - interfaceHeight;
756             }
757
758             if (options.innerHeight) {
759                 settings.h = setSize(options.innerHeight, 'y');
760             }
761
762             if (!options.innerHeight && !options.height) {
763                 scrolltop = $loaded.scrollTop();
764                 $loaded.css({height: "auto"});
765                 settings.h = $loaded.height();
766             }
767
768             $loaded.css({height: settings.h});
769
770             if(scrolltop) {
771                 $loaded.scrollTop(scrolltop);
772             }
773
774             publicMethod.position(settings.get('transition') === "none" ? 0 : settings.get('speed'));
775         }
776     };
777
778     publicMethod.prep = function (object) {
779         if (!open) {
780             return;
781         }
782
783         var callback, speed = settings.get('transition') === "none" ? 0 : settings.get('speed');
784
785         $loaded.remove();
786
787         $loaded = $tag(div, 'LoadedContent').append(object);
788
789         function getWidth() {
790             settings.w = settings.w || $loaded.width();
791             settings.w = settings.mw && settings.mw < settings.w ? settings.mw : settings.w;
792             return settings.w;
793         }
794         function getHeight() {
795             settings.h = settings.h || $loaded.height();
796             settings.h = settings.mh && settings.mh < settings.h ? settings.mh : settings.h;
797             return settings.h;
798         }
799
800         $loaded.hide()
801         .appendTo($loadingBay.show())// content has to be appended to the DOM for accurate size calculations.
802         .css({width: getWidth(), overflow: settings.get('scrolling') ? 'auto' : 'hidden'})
803         .css({height: getHeight()})// sets the height independently from the width in case the new width influences the value of height.
804         .prependTo($content);
805
806         $loadingBay.hide();
807
808         // floating the IMG removes the bottom line-height and fixed a problem where IE miscalculates the width of the parent element as 100% of the document width.
809
810         $(photo).css({'float': 'none'});
811
812         setClass(settings.get('className'));
813
814         callback = function () {
815             var total = $related.length,
816                 iframe,
817                 complete;
818
819             if (!open) {
820                 return;
821             }
822
823             function removeFilter() { // Needed for IE8 in versions of jQuery prior to 1.7.2
824                 if ($.support.opacity === false) {
825                     $box[0].style.removeAttribute('filter');
826                 }
827             }
828
829             complete = function () {
830                 clearTimeout(loadingTimer);
831                 $loadingOverlay.hide();
832                 trigger(event_complete);
833                 settings.get('onComplete');
834             };
835
836
837             $title.html(settings.get('title')).show();
838             $loaded.show();
839
840             if (total > 1) { // handle grouping
841                 if (typeof settings.get('current') === "string") {
842                     $current.html(settings.get('current').replace('{current}', index + 1).replace('{total}', total)).show();
843                 }
844
845                 $next[(settings.get('loop') || index < total - 1) ? "show" : "hide"]().html(settings.get('next'));
846                 $prev[(settings.get('loop') || index) ? "show" : "hide"]().html(settings.get('previous'));
847
848                 slideshow();
849
850                 // Preloads images within a rel group
851                 if (settings.get('preloading')) {
852                     $.each([getIndex(-1), getIndex(1)], function(){
853                         var img,
854                             i = $related[this],
855                             settings = new Settings(i, $.data(i, colorbox)),
856                             src = settings.get('href');
857
858                         if (src && isImage(settings, src)) {
859                             src = retinaUrl(settings, src);
860                             img = document.createElement('img');
861                             img.src = src;
862                         }
863                     });
864                 }
865             } else {
866                 $groupControls.hide();
867             }
868
869             if (settings.get('iframe')) {
870
871                 iframe = settings.get('createIframe');
872
873                 if (!settings.get('scrolling')) {
874                     iframe.scrolling = "no";
875                 }
876
877                 $(iframe)
878                     .attr({
879                         src: settings.get('href'),
880                         'class': prefix + 'Iframe'
881                     })
882                     .one('load', complete)
883                     .appendTo($loaded);
884
885                 $events.one(event_purge, function () {
886                     iframe.src = "//about:blank";
887                 });
888
889                 if (settings.get('fastIframe')) {
890                     $(iframe).trigger('load');
891                 }
892             } else {
893                 complete();
894             }
895
896             if (settings.get('transition') === 'fade') {
897                 $box.fadeTo(speed, 1, removeFilter);
898             } else {
899                 removeFilter();
900             }
901         };
902
903         if (settings.get('transition') === 'fade') {
904             $box.fadeTo(speed, 0, function () {
905                 publicMethod.position(0, callback);
906             });
907         } else {
908             publicMethod.position(speed, callback);
909         }
910     };
911
912     function load () {
913         var href, setResize, prep = publicMethod.prep, $inline, request = ++requests;
914
915         active = true;
916
917         photo = false;
918
919         trigger(event_purge);
920         trigger(event_load);
921         settings.get('onLoad');
922
923         settings.h = settings.get('height') ?
924                 setSize(settings.get('height'), 'y') - loadedHeight - interfaceHeight :
925                 settings.get('innerHeight') && setSize(settings.get('innerHeight'), 'y');
926
927         settings.w = settings.get('width') ?
928                 setSize(settings.get('width'), 'x') - loadedWidth - interfaceWidth :
929                 settings.get('innerWidth') && setSize(settings.get('innerWidth'), 'x');
930
931         // Sets the minimum dimensions for use in image scaling
932         settings.mw = settings.w;
933         settings.mh = settings.h;
934
935         // Re-evaluate the minimum width and height based on maxWidth and maxHeight values.
936         // If the width or height exceed the maxWidth or maxHeight, use the maximum values instead.
937         if (settings.get('maxWidth')) {
938             settings.mw = setSize(settings.get('maxWidth'), 'x') - loadedWidth - interfaceWidth;
939             settings.mw = settings.w && settings.w < settings.mw ? settings.w : settings.mw;
940         }
941         if (settings.get('maxHeight')) {
942             settings.mh = setSize(settings.get('maxHeight'), 'y') - loadedHeight - interfaceHeight;
943             settings.mh = settings.h && settings.h < settings.mh ? settings.h : settings.mh;
944         }
945
946         href = settings.get('href');
947
948         loadingTimer = setTimeout(function () {
949             $loadingOverlay.show();
950         }, 100);
951
952         if (settings.get('inline')) {
953             var $target = $(href);
954             // Inserts an empty placeholder where inline content is being pulled from.
955             // An event is bound to put inline content back when Colorbox closes or loads new content.
956             $inline = $('<div>').hide().insertBefore($target);
957
958             $events.one(event_purge, function () {
959                 $inline.replaceWith($target);
960             });
961
962             prep($target);
963         } else if (settings.get('iframe')) {
964             // IFrame element won't be added to the DOM until it is ready to be displayed,
965             // to avoid problems with DOM-ready JS that might be trying to run in that iframe.
966             prep(" ");
967         } else if (settings.get('html')) {
968             prep(settings.get('html'));
969         } else if (isImage(settings, href)) {
970
971             href = retinaUrl(settings, href);
972
973             photo = settings.get('createImg');
974
975             $(photo)
976             .addClass(prefix + 'Photo')
977             .bind('error.'+prefix,function () {
978                 prep($tag(div, 'Error').html(settings.get('imgError')));
979             })
980             .one('load', function () {
981                 if (request !== requests) {
982                     return;
983                 }
984
985                 // A small pause because some browsers will occassionaly report a
986                 // img.width and img.height of zero immediately after the img.onload fires
987                 setTimeout(function(){
988                     var percent;
989
990                     if (settings.get('retinaImage') && window.devicePixelRatio > 1) {
991                         photo.height = photo.height / window.devicePixelRatio;
992                         photo.width = photo.width / window.devicePixelRatio;
993                     }
994
995                     if (settings.get('scalePhotos')) {
996                         setResize = function () {
997                             photo.height -= photo.height * percent;
998                             photo.width -= photo.width * percent;
999                         };
1000                         if (settings.mw && photo.width > settings.mw) {
1001                             percent = (photo.width - settings.mw) / photo.width;
1002                             setResize();
1003                         }
1004                         if (settings.mh && photo.height > settings.mh) {
1005                             percent = (photo.height - settings.mh) / photo.height;
1006                             setResize();
1007                         }
1008                     }
1009
1010                     if (settings.h) {
1011                         photo.style.marginTop = Math.max(settings.mh - photo.height, 0) / 2 + 'px';
1012                     }
1013
1014                     if ($related[1] && (settings.get('loop') || $related[index + 1])) {
1015                         photo.style.cursor = 'pointer';
1016
1017                         $(photo).bind('click.'+prefix, function () {
1018                             publicMethod.next();
1019                         });
1020                     }
1021
1022                     photo.style.width = photo.width + 'px';
1023                     photo.style.height = photo.height + 'px';
1024                     prep(photo);
1025                 }, 1);
1026             });
1027
1028             photo.src = href;
1029
1030         } else if (href) {
1031             $loadingBay.load(href, settings.get('data'), function (data, status) {
1032                 if (request === requests) {
1033                     prep(status === 'error' ? $tag(div, 'Error').html(settings.get('xhrError')) : $(this).contents());
1034                 }
1035             });
1036         }
1037     }
1038
1039     // Navigates to the next page/image in a set.
1040     publicMethod.next = function () {
1041         if (!active && $related[1] && (settings.get('loop') || $related[index + 1])) {
1042             index = getIndex(1);
1043             launch($related[index]);
1044         }
1045     };
1046
1047     publicMethod.prev = function () {
1048         if (!active && $related[1] && (settings.get('loop') || index)) {
1049             index = getIndex(-1);
1050             launch($related[index]);
1051         }
1052     };
1053
1054     // Note: to use this within an iframe use the following format: parent.jQuery.colorbox.close();
1055     publicMethod.close = function () {
1056         if (open && !closing) {
1057
1058             closing = true;
1059             open = false;
1060             trigger(event_cleanup);
1061             settings.get('onCleanup');
1062             $window.unbind('.' + prefix);
1063             $overlay.fadeTo(settings.get('fadeOut') || 0, 0);
1064
1065             $box.stop().fadeTo(settings.get('fadeOut') || 0, 0, function () {
1066                 $box.hide();
1067                 $overlay.hide();
1068                 trigger(event_purge);
1069                 $loaded.remove();
1070
1071                 setTimeout(function () {
1072                     closing = false;
1073                     trigger(event_closed);
1074                     settings.get('onClosed');
1075                 }, 1);
1076             });
1077         }
1078     };
1079
1080     // Removes changes Colorbox made to the document, but does not remove the plugin.
1081     publicMethod.remove = function () {
1082         if (!$box) { return; }
1083
1084         $box.stop();
1085         $[colorbox].close();
1086         $box.stop(false, true).remove();
1087         $overlay.remove();
1088         closing = false;
1089         $box = null;
1090         $('.' + boxElement)
1091             .removeData(colorbox)
1092             .removeClass(boxElement);
1093
1094         $(document).unbind('click.'+prefix).unbind('keydown.'+prefix);
1095     };
1096
1097     // A method for fetching the current element Colorbox is referencing.
1098     // returns a jQuery object.
1099     publicMethod.element = function () {
1100         return $(settings.el);
1101     };
1102
1103     publicMethod.settings = defaults;
1104
1105 }(jQuery, document, window));