<commit_before>/*
	AOL Photo Gallery Module
	@author <NAME>
	@since 1/21/2011
	
	To Do:
	
	* Set the gallery container width the width the user specifies.
	* Autodetect the width of the column, and adjust the image size.
	* Make default thumbnail view with toggle.
	* Portrait galleries: http://www.stylelist.com/2011/01/24/look-of-the-day-emily-blunt/
	* Keep track of dimensions of the gallery as a whole.
	* Create a function that handles inside/outside and before/after based on settings.
	
*/
(function($, location){

var defaultOptions = {
		
	//	customClass: "aol-photo-gallery-portrait",
		customClass: "aol-photo-gallery-carousel",
		carousel: 1,
		carouselSiblings: 2,
		
		speed: 250,
		
		photoWidth: 450, //"auto",
		photoHeight: 325, //"auto",
		
		thumbnailWidth: 74,
		thumbnailHeight: 74,
		
		thumbnailCount: 999,
		thumbnailAfter: 1,
		
		activeIndex: 1,
		
		refreshAd: 0,
		refreshDivId: "",
		refreshCount: 3,
		
		showThumbnails: 0,
		showName: 1,
		showDescription: 1,
		showControls: 1,
		controlsAfter: 1,
		controlsInside: 1,
		showStatus: 1,
		
		toggleThumbnails: 1,
		toggleThumbnailsAfter: 1,
		
		showCaptions: 1,
		captionsAfter: 1,
	
		ui: {
			slides: "> .photos > li",
			galleryName: "> h2",
			galleryDescription: "> .description",
			anchors: "> .photos > li > a",
			thumbnails: "> .thumbnails"
		},
	
		data: {
			galleryPhotos: []
		},
		
		namespace: "aol-photo-gallery"

	},

	// Internal private variable, used to ensure we 
	// stay between a 1:2 and 1:9 refresh rate.
	refreshMinimum = 2,
	refreshLimit = 9,
	
	// Standard naming convention for deep linked photos.
	deepLinkHashName = "photo";

$.aolPhotoGallery = function( customOptions, elem ){
	// Initialize the gallery.
	if ( elem ) {
		
		var options = $.extend( true, {}, defaultOptions, customOptions ),
		
			$aolPhotoGallery = $(elem),
			$aolPhotoGalleryClone = $aolPhotoGallery.clone(), // Offline copy.
			
			ui = options.ui,
			data = options.data,
			photos = data.galleryPhotos,
			namespace = options.namespace,
			
			// Subtract 1 to convert from user-friendly to an index.
			activeIndex = options.activeIndex - 1, 
			totalPhotos,
			
			speed = options.speed,
			
			photoWidth = options.photoWidth,
			photoHeight = options.photoHeight,
			thumbnailWidth =  options.thumbnailWidth,
			thumbnailHeight = options.thumbnailHeight,
			
			statusRateLimit = 0,
			
			isCarousel = options.carousel,
			// Used to understand how many slides to be sure to load up front.
			carouselSiblings = options.carouselSiblings, 
			carouselPosition,
			
			// Object we use to store if photo has been cached.
			photoCached = {},
			
			$anchors,
			$slides,
			$slideContainer,
			$captions,
			$captionContainer,
			$gallery,
			$status,
			$controls,
			$thumbnailContainer,
			$thumbnails,
			$showThumbnails,
			$sponsor,
			
			core = {
				
				// For a given index and length, return the true index.
				getIndex: function( index, length ){
					length = totalPhotos;
					var trueIndex = index < length ? index : index % length;
					if (trueIndex < 0 ) { 
						trueIndex = length + trueIndex; 
					}
					return trueIndex;
				},
				
				init: function(){
					
					var $galleryName = ui.$galleryName = $aolPhotoGalleryClone.find( ui.galleryName ),
						$galleryDescription = ui.$galleryDescription = $aolPhotoGalleryClone.find( ui.galleryDescription );

					data.galleryName = $galleryName.text();
					data.galleryDescription = $galleryDescription.html();
					
					$anchors = ui.$anchors = $aolPhotoGalleryClone.find( ui.anchors );
					$slides = ui.$slides = $aolPhotoGalleryClone.find( ui.slides );
					$slideContainer = ui.$slideContainer = $slides.parent();
					
					// Just a temporary assurance of fixed width. 
					// Think we will want this to be dynamic at some point.
	//				$aolPhotoGalleryClone.css({
	//					width: photoWidth + "px"
	//				});
					
					if ( isCarousel ) {
							
						$slideContainer.css({
							height: photoHeight + "px"
						});
						
					} else {
						
						$slideContainer.css({
							height: photoHeight + "px",
							width: photoWidth + "px"
						});
						
						$slides.css({
							width: photoWidth + "px",
							height: photoHeight + "px",
							lineHeight: photoHeight + "px"
						});
						
					}
					
					totalPhotos = $anchors.length;
					
					if ( activeIndex >= totalPhotos ) {
						activeIndex = totalPhotos - 1;
					}
					
					$anchors.each(function(i){
						
						var anchorElem = this,
							$anchorElem = $(anchorElem),

							// Save the details of this photo for later.	
							photoName = $anchorElem.text(),
							photoDescription = $anchorElem.attr("title"),
							photoSrc = $anchorElem.attr("data-photo-src");
						
						photos.push({
							photoName: photoName,
							photoDescription: photoDescription,
							photoSrc: photoSrc
						});
						
					});
					
					if ( options.customClass ) {
						$aolPhotoGalleryClone.addClass( options.customClass );
					}
					
					core.buildGallery();
					
					if ( ! options.showName ) {
						$galleryName.hide();
					}
					
					if ( ! options.showDescription ) {
						$galleryDescription.hide();
					}
					
					if ( options.showCaptions ) {
						core.buildCaptions();
					}
					
					core.buildStatus();
					
					if ( options.showControls ) {
						core.buildControls();
					}
					
					if ( options.showThumbnails || options.toggleThumbnails ) {
						core.buildThumbnails();
					}
					
					if ( options.sponsorImage ) {
						core.buildSponsor();
					}
					
					$aolPhotoGallery.replaceWith( $aolPhotoGalleryClone );
				},
				
				preloadPhoto: function( index ){
					
					var photo = photos[ index ],
						photoSrc = photo.photoSrc,
						dynamicPhotoSrc = $.getDynamicImageSrc( photoSrc, photoWidth, photoHeight ),
						$slide = $slides.eq( index ),
						image;
					
					if ( ! $slide.data("image-loaded." + namespace) ) {

						// Preload this image and be sure its siblings are loaded.
						if ( isCarousel ) {
							
							// Images are tricky because we need to know the width
							// to pull off a carousel.  We must first download the 
							// image, figure out the width, and set its parent.
							image = new Image();
							image.src = dynamicPhotoSrc;
							image.onload = function(){
								
								var slideContainerWidth = $slideContainer.width();

								$slide.css({
									backgroundImage: "url(" + dynamicPhotoSrc + ")",
									width: image.width + "px",
				//					height: image.height + "px", // We want images to center, so we keep this full height.
									height: photoHeight + "px",
									visibility: "visible"
								});
								
								// Update the slide container's width.
								$slideContainer.width( slideContainerWidth + $slide.outerWidth() );

								// Update the position of the active index if needed.
								core.updateCarouselPosition();
								
							};


							
						} else {
							// Set up background images on slides. This is nice because we don't have to 
							// mess with <img>, which downloads automatically where as CSS will only
							// download when visible.
							$slide.css("backgroundImage", "url(" + dynamicPhotoSrc + ")");
	
						}
						
						$slide.data("image-loaded." + namespace, 1);
					}
					
				},
				
				preloadCarouselPhotos: function( index ) {
					
					var currentIndex = index - carouselSiblings,
						lastIndex = index + carouselSiblings;
						
					// For the active index and its siblings, we'll need
					// to load the image as well.
					while ( currentIndex <= lastIndex ) {
						core.preloadPhoto( getIndex( currentIndex ) );
						currentIndex++;
					} 
				},
				
				updateCarousel: function( oldIndex ){

					var	$nodeBack,
						$nodeNext;
						
					core.preloadCarouselPhotos( activeIndex );
					
					// Based on the position of the active index, ensure 
					// siblings exist in the DOM to the left and to 
					// the right of the active.
	
					$nodeBack = $nodeNext = $slides.eq( activeIndex );
					for ( var i = 0; i < carouselSiblings; i++ ) {
						
						$nodeBack = $nodeBack.prev();
						$nodeNext = $nodeNext.next();
						
						// If we don't have the previous one, we need to grab
						// the last node and put it in the beginning.
						if ( ! $nodeBack.length ) {
							$slideContainer.prepend( $slides.eq( getIndex( activeIndex - i - 1 ) ) );
						}
						// If we don't have the next one, we need to grab
						// the first node and put it at the end.
						if ( ! $nodeNext.length ) {
							$slideContainer.append( $slides.eq( getIndex( activeIndex + i + 1 ) ) );
						}
					}
				
					core.updateCarouselPosition( oldIndex );
				},
				
				// Think about moving this back into preloadImage.
				updateCarouselPosition: function( oldIndex ){
					
						// Look into caching some of these widths.
					var $slide = $slides.eq( activeIndex ),
						galleryWidth = $gallery.width(),
						activePosition = ( galleryWidth - $slide.width() )/2 - $slide.position().left,
						$oldSlide,
						oldPosition;
					
					// If we are passed an old index, we want to first set the position 
					// to the old one, and animate to the new one.
					if ( oldIndex >= 0 ) {

						$oldSlide = $slides.eq( oldIndex );
						oldPosition = ( galleryWidth - $oldSlide.width() )/2 - $oldSlide.position().left;
						$slideContainer.queue(function( next ){ $slideContainer.css( "left", oldPosition + "px" ); next(); });
						$slideContainer.animate({ "left": activePosition + "px" }, speed );
					
					} else {
					
						if ( activePosition !== carouselPosition ) {
							$slideContainer.css( "left", activePosition + "px" );
							carouselPosition = activePosition;
						}
					}

															
				},

				buildGallery: function(){
					
					// Remove the anchor links, we no longer need them.
					// On second thought, leave them in for screen readers.
					// $anchors.remove();
					
					// Wrap the photos for design hooks.
					$slideContainer.wrap( "<div class=\"gallery\"></div>" );
					
					$gallery = ui.$gallery = $slideContainer.parent();
					
					if ( isCarousel ) {
					
						setTimeout(function(){
							core.updateCarousel();
						}, 0);

					// Preload the active index.					
					} else {
						
						$slides.css({
							opacity: 0
						});
						
						// Set up the active photo.
						$slides.eq( activeIndex ).css({
							zIndex: 1,
							opacity: 1,
							visibility: "visible"
						});
						
						core.preloadPhoto( activeIndex );
					}
					
					// In the carousel, the gallery height is fixed.
					if ( options.carousel ) {
						$gallery.height( photoHeight );
					}
									
					// If we need to show thumbnails by default and 
					// hide the gallery, do so now. 
					// Note: We hide from the DOM so the Thumbnails
					// look casual.
					if ( options.showThumbnails && options.toggleThumbnails ) {
						setTimeout(function(){
							$slideContainer.css({
								position: "absolute",
								left: -$gallery.width()
							});
						}, 0);
					}
					
					core.bindGallery();
					
				},
				
				bindGallery: function(){
					
					$aolPhotoGalleryClone.delegate(".photos > li", "click." + namespace, function(){
						$(this).trigger("next-click." + namespace);
					});
					
					$aolPhotoGalleryClone.delegate(".photos > li", "mouseover." + namespace, function(){
						$(this).trigger("next-mouseover." + namespace);
					});
	
					// Listen for thumbnail mouseovers and morph the image if needed.
					$aolPhotoGalleryClone.bind("thumbnail-mouseover." + namespace, function( event, data ){
						var index = data.index;
						core.preloadPhoto( index );
					});

					$aolPhotoGalleryClone.bind("back-mouseover." + namespace, function( event, data ){
						var backIndex = activeIndex === 0 ? totalPhotos - 1 : activeIndex - 1;
						if ( isCarousel ) {
							core.preloadCarouselPhotos( backIndex);
						} else {
							core.preloadPhoto( backIndex );
						}
					});
					
					$aolPhotoGalleryClone.bind("next-mouseover." + namespace, function( event, data ){
						var nextIndex = activeIndex === totalPhotos - 1 ? 0 : activeIndex + 1;
						if ( isCarousel ) {
							core.preloadCarouselPhotos( nextIndex );
						} else {
							core.preloadPhoto( nextIndex );
						}
					});
					
					$aolPhotoGalleryClone.bind("status-update." + namespace, function(event, data){

						var oldIndex = data.oldIndex,
							activeIndex = data.activeIndex,

							$oldSlide = $slides.eq( oldIndex ),
							$activeSlide = $slides.eq( activeIndex ),
							
							backIndex = activeIndex === 0 ? totalPhotos - 1 : activeIndex - 1,
							nextIndex = activeIndex === totalPhotos - 1 ? 0 : activeIndex + 1;			

						if ( isCarousel ) {
							
							// Handle carousel transition.
							core.updateCarousel( oldIndex );
							
						} else {
							// Handle fade transition.
							$oldSlide.css({
								zIndex: 0 
							}).animate({
								opacity: 0
							}, speed);
							
							$activeSlide.css({ 
								visibility: "visible",
								zIndex: 1 
							}).animate({
								opacity: 1
							}, speed);	
							
													
							// Preload the previous image if needed.	
							core.preloadPhoto( backIndex );
							
							// Preload the next image if needed.
							core.preloadPhoto( nextIndex );

						}

					});
					
					// If the toggle feature is present, add those bindings.
					if ( options.toggleThumbnails ) {
						
						$aolPhotoGalleryClone.bind("show-thumbnails." + namespace, function(){
							$slideContainer
								.css("position", "relative")
								.animate({
									"left": -$gallery.width()
								}, speed);
						});
						
						$aolPhotoGalleryClone.bind("thumbnail-click." + namespace, function(event, data){
							
							$slideContainer
								.css({
									"position": "relative" // Artz: Why do we need this? (re: visibility: visible)
								}) 
								.animate({
									"left": 0
								}, speed);

							// Prempt the fade in by setting our opacity 
							// to full, for the slide in effect.
							$slides.eq( activeIndex ).css({
								opacity: 0
							});
							$slides.eq( data.index ).css({
								opacity: 1
							});
							
						});	
					}
					
				},
				
				buildCaptions: function(){
					
					var i = 0, 
						l = ui.$anchors.length,
						captionHTML = ["<ul class=\"captions\">"],
						photo;
								
					// Build thumbnail HTML.
					for (; i < l; i++) {
						photo = photos[i];
						captionHTML.push("<li data-index=\"" + i + "\"><h3>" + photo.photoName + "</h3>" + photo.photoDescription + "</li>");
					}
					
					captionHTML.push("</ul>");
					
					$captions = $( captionHTML.join("") );
					
					// Append captions.
					if ( options.captionsAfter ) {
						$gallery.after( $captions );
					} else {
						$gallery.prepend( $captions );
					}
					
					$captions = ui.$captions = $captions.find("li");
					
					$captions.eq( activeIndex ).css({
						zIndex: 1,
						visibility: "visible"
					});
	
					$captionContainer = ui.$captionContainer = $captions.parent();
									
					// Need to make this tweak in the next UI thread.
					setTimeout(function(){
						$captionContainer.height( $captions.eq( activeIndex ).height() );
					}, 0);
					
					core.bindCaptions();
					
					// If we need to show thumbnails by default and 
					// hide the captions, do so now.
					if ( options.showThumbnails && options.toggleThumbnails ) {
//						$captions.parent().css("visibility", "hidden");
						$captionContainer.hide();
					}
				},
				
				bindCaptions: function(){
					$aolPhotoGalleryClone.bind("status-update." + namespace, function(event, data){
						
						var oldIndex = data.oldIndex,
							activeIndex = data.activeIndex,
														
							$oldCaption = $captions.eq( oldIndex ),
							$activeCaption = $captions.eq( activeIndex );

						$activeCaption.css({
							visibility: "visible",
							opacity: 0,
							zIndex: 1
						}).animate({
							opacity: 1
						}, speed);	

						$oldCaption.css({
							zIndex: 0
						}).animate({
							opacity: 0
						}, speed); // Fade out didn't work when its parent container was not displayed.
						
						$captionContainer.animate({
							height: $activeCaption.height()
						}, speed);

					});
					
					// If the toggle feature is present, add those bindings.
					if ( options.toggleThumbnails ) {
						$aolPhotoGalleryClone.bind("show-thumbnails." + namespace, function(){
							$captionContainer.css("opacity", 0);
						});
						$aolPhotoGalleryClone.bind("thumbnail-click." + namespace, function(){
							$captions.eq( activeIndex ).css("opacity", 0);
							$captionContainer.css({
								"display": "block",
								"visibility": "visible",
								"opacity": 1
							});
						});
					}
					
				},
				
				buildStatus: function(){
					
					var statusHTML = "<div class=\"status\">" + ( activeIndex + 1 ) + " of " + totalPhotos + "</div>";
						
					$status = ui.$status = $( statusHTML );
					
					$gallery.after( $status );
					
					// We still need Status to live in the DOM.
					if ( ! options.showStatus ) {
						
						$status.hide();
										
					// If we need to show thumbnails by default and 
					// hide the captions, do so now.
					} else if ( options.showThumbnails && options.toggleThumbnails ) {
						// $status.css("visibility", "hidden");
						$status.hide();
					}
					
					core.bindStatus();
				},
				
				bindStatus: function(){
									
					$aolPhotoGalleryClone.bind("thumbnail-click." + namespace, function( event, data ){
						
						var oldIndex = activeIndex;
						
						activeIndex = data.index;
						
						if ( oldIndex !== activeIndex ) {
							$status.html( ( activeIndex + 1 ) + " of " + totalPhotos )
								.trigger("status-update." + namespace, [{ oldIndex: oldIndex, activeIndex: activeIndex }]);
						}
					});
					
					$aolPhotoGalleryClone.bind("back-click." + namespace, function( event, data ){
						if ( ! statusRateLimit ) {
							statusRateLimit = 1;
							var oldIndex = activeIndex;
								
							activeIndex = activeIndex === 0 ? totalPhotos - 1 : activeIndex - 1;
							
							$status.html( ( activeIndex + 1 ) + " of " + totalPhotos )
								.trigger("status-update." + namespace, [{ oldIndex: oldIndex, activeIndex: activeIndex }]);
						
							setTimeout(function(){
								statusRateLimit = 0;
							}, speed + 50 );
						}
					});
					
					$aolPhotoGalleryClone.bind("next-click." + namespace, function(){
						if ( ! statusRateLimit ) {
							statusRateLimit = 1;
								
							var oldIndex = activeIndex;
								
							activeIndex = activeIndex === totalPhotos - 1 ? 0 : activeIndex + 1;
							
							$status.html( ( activeIndex + 1 ) + " of " + totalPhotos )
								.trigger("status-update." + namespace, [{ oldIndex: oldIndex, activeIndex: activeIndex }]);
						
							setTimeout(function(){
								statusRateLimit = 0;
							}, speed );
						}	
					});
					
					// If the toggle feature is present, add those bindings.
					if ( options.toggleThumbnails ) {
						<commit_msg>Final animation tweaks to carousel.<commit_after>/*
	AOL Photo Gallery Module
	@author <NAME>
	@since 1/21/2011
	
	To Do:
	
	* Set the gallery container width the width the user specifies.
	* Autodetect the width of the column, and adjust the image size.
	* Make default thumbnail view with toggle.
	* Portrait galleries: http://www.stylelist.com/2011/01/24/look-of-the-day-emily-blunt/
	* Keep track of dimensions of the gallery as a whole.
	* Create a function that handles inside/outside and before/after based on settings.
	
*/
(function($, location){

var defaultOptions = {
		
	//	customClass: "aol-photo-gallery-portrait",
		customClass: "aol-photo-gallery-carousel",
		carousel: 1,
		carouselSiblings: 2,
		
		speed: 250,
		
		photoWidth: 450, //"auto",
		photoHeight: 325, //"auto",
		
		thumbnailWidth: 74,
		thumbnailHeight: 74,
		
		thumbnailCount: 999,
		thumbnailAfter: 1,
		
		activeIndex: 1,
		
		refreshAd: 0,
		refreshDivId: "",
		refreshCount: 3,
		
		showThumbnails: 0,
		showName: 1,
		showDescription: 1,
		showControls: 1,
		controlsAfter: 1,
		controlsInside: 1,
		showStatus: 1,
		
		toggleThumbnails: 1,
		toggleThumbnailsAfter: 1,
		
		showCaptions: 1,
		captionsAfter: 1,
	
		ui: {
			slides: "> .photos > li",
			galleryName: "> h2",
			galleryDescription: "> .description",
			anchors: "> .photos > li > a",
			thumbnails: "> .thumbnails"
		},
	
		data: {
			galleryPhotos: []
		},
		
		namespace: "aol-photo-gallery"

	},

	// Internal private variable, used to ensure we 
	// stay between a 1:2 and 1:9 refresh rate.
	refreshMinimum = 2,
	refreshLimit = 9,
	
	// Standard naming convention for deep linked photos.
	deepLinkHashName = "photo";

$.aolPhotoGallery = function( customOptions, elem ){
	// Initialize the gallery.
	if ( elem ) {
		
		var options = $.extend( true, {}, defaultOptions, customOptions ),
		
			$aolPhotoGallery = $(elem),
			$aolPhotoGalleryClone = $aolPhotoGallery.clone(), // Offline copy.
			
			ui = options.ui,
			data = options.data,
			photos = data.galleryPhotos,
			namespace = options.namespace,
			
			// Subtract 1 to convert from user-friendly to an index.
			activeIndex = options.activeIndex - 1, 
			totalPhotos,
			
			speed = options.speed,
			
			photoWidth = options.photoWidth,
			photoHeight = options.photoHeight,
			thumbnailWidth =  options.thumbnailWidth,
			thumbnailHeight = options.thumbnailHeight,
			
			statusRateLimit = 0,
			
			isCarousel = options.carousel,
			// Used to understand how many slides to be sure to load up front.
			carouselSiblings = options.carouselSiblings, 
			carouselPosition,
			
			// Object we use to store if photo has been cached.
			photoCached = {},
			
			$anchors,
			$slides,
			$slideContainer,
			$captions,
			$captionContainer,
			$gallery,
			$status,
			$controls,
			$thumbnailContainer,
			$thumbnails,
			$showThumbnails,
			$sponsor,
			
			core = {
				
				// For a given index and length, return the true index.
				getIndex: function( index, length ){
					length = totalPhotos;
					var trueIndex = index < length ? index : index % length;
					if (trueIndex < 0 ) { 
						trueIndex = length + trueIndex; 
					}
					return trueIndex;
				},
				
				init: function(){
					
					var $galleryName = ui.$galleryName = $aolPhotoGalleryClone.find( ui.galleryName ),
						$galleryDescription = ui.$galleryDescription = $aolPhotoGalleryClone.find( ui.galleryDescription );

					data.galleryName = $galleryName.text();
					data.galleryDescription = $galleryDescription.html();
					
					$anchors = ui.$anchors = $aolPhotoGalleryClone.find( ui.anchors );
					$slides = ui.$slides = $aolPhotoGalleryClone.find( ui.slides );
					$slideContainer = ui.$slideContainer = $slides.parent();
					
					// Just a temporary assurance of fixed width. 
					// Think we will want this to be dynamic at some point.
	//				$aolPhotoGalleryClone.css({
	//					width: photoWidth + "px"
	//				});
					
					if ( isCarousel ) {
							
						$slideContainer.css({
							height: photoHeight + "px"
						});
						
					} else {
						
						$slideContainer.css({
							height: photoHeight + "px",
							width: photoWidth + "px"
						});
						
						$slides.css({
							width: photoWidth + "px",
							height: photoHeight + "px",
							lineHeight: photoHeight + "px"
						});
						
					}
					
					totalPhotos = $anchors.length;
					
					if ( activeIndex >= totalPhotos ) {
						activeIndex = totalPhotos - 1;
					}
					
					$anchors.each(function(i){
						
						var anchorElem = this,
							$anchorElem = $(anchorElem),

							// Save the details of this photo for later.	
							photoName = $anchorElem.text(),
							photoDescription = $anchorElem.attr("title"),
							photoSrc = $anchorElem.attr("data-photo-src");
						
						photos.push({
							photoName: photoName,
							photoDescription: photoDescription,
							photoSrc: photoSrc
						});
						
					});
					
					if ( options.customClass ) {
						$aolPhotoGalleryClone.addClass( options.customClass );
					}
					
					core.buildGallery();
					
					if ( ! options.showName ) {
						$galleryName.hide();
					}
					
					if ( ! options.showDescription ) {
						$galleryDescription.hide();
					}
					
					if ( options.showCaptions ) {
						core.buildCaptions();
					}
					
					core.buildStatus();
					
					if ( options.showControls ) {
						core.buildControls();
					}
					
					if ( options.showThumbnails || options.toggleThumbnails ) {
						core.buildThumbnails();
					}
					
					if ( options.sponsorImage ) {
						core.buildSponsor();
					}
					
					$aolPhotoGallery.replaceWith( $aolPhotoGalleryClone );
				},
				
				preloadPhoto: function( index ){
					
					var photo = photos[ index ],
						photoSrc = photo.photoSrc,
						dynamicPhotoSrc = $.getDynamicImageSrc( photoSrc, photoWidth, photoHeight ),
						$slide = $slides.eq( index ),
						image;
					
					if ( ! $slide.data("image-loaded." + namespace) ) {

						// Preload this image and be sure its siblings are loaded.
						if ( isCarousel ) {
							
							// Images are tricky because we need to know the width
							// to pull off a carousel.  We must first download the 
							// image, figure out the width, and set its parent.
							image = new Image();
							image.src = dynamicPhotoSrc;
							image.onload = function(){
								
								var slideContainerWidth = $slideContainer.width();

								$slide.css({
									backgroundImage: "url(" + dynamicPhotoSrc + ")",
									width: image.width + "px",
				//					height: image.height + "px", // We want images to center, so we keep this full height.
									height: photoHeight + "px",
									visibility: "visible"
								});
								
								// Update the slide container's width.
								$slideContainer.width( slideContainerWidth + $slide.outerWidth() );

								// Update the position of the active index if needed.
								core.updateCarouselPosition();
								
							};


							
						} else {
							// Set up background images on slides. This is nice because we don't have to 
							// mess with <img>, which downloads automatically where as CSS will only
							// download when visible.
							$slide.css("backgroundImage", "url(" + dynamicPhotoSrc + ")");
	
						}
						
						$slide.data("image-loaded." + namespace, 1);
					}
					
				},
				
				preloadCarouselPhotos: function( index ) {
					
					var currentIndex = index - carouselSiblings,
						lastIndex = index + carouselSiblings;
						
					// For the active index and its siblings, we'll need
					// to load the image as well.
					while ( currentIndex <= lastIndex ) {
						core.preloadPhoto( getIndex( currentIndex ) );
						currentIndex++;
					} 
				},
				
				updateCarousel: function( oldIndex ){

					var	$nodeBack,
						$nodeNext;
					
					// Before we do anything, we should animate to ensure it's smooth.
					core.updateCarouselPosition( oldIndex );
					
					// Next we should start fetching any new photos.
					core.preloadCarouselPhotos( activeIndex );
					
					// After all animations are done, and before
					// any others start, we do this.
					$slideContainer.queue(function( next ){
						console.log("Updating order and stuff.");
						// Based on the position of the active index, ensure 
						// siblings exist in the DOM to the left and to 
						// the right of the active.
						$nodeBack = $nodeNext = $slides.eq( activeIndex );
						for ( var i = 0; i < carouselSiblings; i++ ) {
							
							$nodeBack = $nodeBack.prev();
							$nodeNext = $nodeNext.next();
							
							// If we don't have the previous one, we need to grab
							// the last node and put it in the beginning.
							if ( ! $nodeBack.length ) {
								$slideContainer.prepend( $slides.eq( getIndex( activeIndex - i - 1 ) ) );
							}
							// If we don't have the next one, we need to grab
							// the first node and put it at the end.
							if ( ! $nodeNext.length ) {
								$slideContainer.append( $slides.eq( getIndex( activeIndex + i + 1 ) ) );
							}
						}
						core.updateCarouselPosition();
						next();
					});
					
				},
				
				// Think about moving this back into preloadImage.
				updateCarouselPosition: function( oldIndex ){

						// Look into caching some of these widths.
					var $slide = $slides.eq( activeIndex ),
						galleryWidth = $gallery.width(),
						activePosition = ( galleryWidth - $slide.width() )/2 - $slide.position().left,
						$oldSlide,
						oldPosition;
					
					// If we are passed an old index, we want to first set the position 
					// to the old one, and animate to the new one.
					if ( oldIndex >= 0 ) {

						$oldSlide = $slides.eq( oldIndex );
						oldPosition = ( galleryWidth - $oldSlide.width() )/2 - $oldSlide.position().left;
						$slideContainer.queue(function( next ){ $slideContainer.css( "left", oldPosition + "px" ); next(); });
						$slideContainer.animate({ "left": activePosition + "px" }, speed );
					
					} else {
					
						if ( activePosition !== carouselPosition ) {
							$slideContainer.css( "left", activePosition + "px" );
							carouselPosition = activePosition;
						}
					}

															
				},

				buildGallery: function(){
					
					// Remove the anchor links, we no longer need them.
					// On second thought, leave them in for screen readers.
					// $anchors.remove();
					
					// Wrap the photos for design hooks.
					$slideContainer.wrap( "<div class=\"gallery\"></div>" );
					
					$gallery = ui.$gallery = $slideContainer.parent();
					
					if ( isCarousel ) {
					
						setTimeout(function(){
							core.updateCarousel();
						}, 0);

					// Preload the active index.					
					} else {
						
						$slides.css({
							opacity: 0
						});
						
						// Set up the active photo.
						$slides.eq( activeIndex ).css({
							zIndex: 1,
							opacity: 1,
							visibility: "visible"
						});
						
						core.preloadPhoto( activeIndex );
					}
					
					// In the carousel, the gallery height is fixed.
					if ( options.carousel ) {
						$gallery.height( photoHeight );
					}
									
					// If we need to show thumbnails by default and 
					// hide the gallery, do so now. 
					// Note: We hide from the DOM so the Thumbnails
					// look casual.
					if ( options.showThumbnails && options.toggleThumbnails ) {
						setTimeout(function(){
							$slideContainer.css({
								position: "absolute",
								left: -$gallery.width()
							});
						}, 0);
					}
					
					core.bindGallery();
					
				},
				
				bindGallery: function(){
					
					$aolPhotoGalleryClone.delegate(".photos > li", "click." + namespace, function(){
						$(this).trigger("next-click." + namespace);
					});
					
					$aolPhotoGalleryClone.delegate(".photos > li", "mouseover." + namespace, function(){
						$(this).trigger("next-mouseover." + namespace);
					});
	
					// Listen for thumbnail mouseovers and morph the image if needed.
					$aolPhotoGalleryClone.bind("thumbnail-mouseover." + namespace, function( event, data ){
						var index = data.index;
						core.preloadPhoto( index );
					});

					$aolPhotoGalleryClone.bind("back-mouseover." + namespace, function( event, data ){
						var backIndex = activeIndex === 0 ? totalPhotos - 1 : activeIndex - 1;
						if ( isCarousel ) {
							core.preloadCarouselPhotos( backIndex);
						} else {
							core.preloadPhoto( backIndex );
						}
					});
					
					$aolPhotoGalleryClone.bind("next-mouseover." + namespace, function( event, data ){
						var nextIndex = activeIndex === totalPhotos - 1 ? 0 : activeIndex + 1;
						if ( isCarousel ) {
							core.preloadCarouselPhotos( nextIndex );
						} else {
							core.preloadPhoto( nextIndex );
						}
					});
					
					$aolPhotoGalleryClone.bind("status-update." + namespace, function(event, data){

						var oldIndex = data.oldIndex,
							activeIndex = data.activeIndex,

							$oldSlide = $slides.eq( oldIndex ),
							$activeSlide = $slides.eq( activeIndex ),
							
							backIndex = activeIndex === 0 ? totalPhotos - 1 : activeIndex - 1,
							nextIndex = activeIndex === totalPhotos - 1 ? 0 : activeIndex + 1;			

						if ( isCarousel ) {
							
							// Handle carousel transition.
							core.updateCarousel( oldIndex );
							
						} else {
							// Handle fade transition.
							$oldSlide.css({
								zIndex: 0 
							}).animate({
								opacity: 0
							}, speed);
							
							$activeSlide.css({ 
								visibility: "visible",
								zIndex: 1 
							}).animate({
								opacity: 1
							}, speed);	
							
													
							// Preload the previous image if needed.	
							core.preloadPhoto( backIndex );
							
							// Preload the next image if needed.
							core.preloadPhoto( nextIndex );

						}

					});
					
					// If the toggle feature is present, add those bindings.
					if ( options.toggleThumbnails ) {
						
						$aolPhotoGalleryClone.bind("show-thumbnails." + namespace, function(){
							$slideContainer
								.css("position", "relative")
								.animate({
									"left": -$gallery.width()
								}, speed);
						});
						
						$aolPhotoGalleryClone.bind("thumbnail-click." + namespace, function(event, data){
							
							$slideContainer
								.css({
									"position": "relative" // Artz: Why do we need this? (re: visibility: visible)
								}) 
								.animate({
									"left": 0
								}, speed);

							// Prempt the fade in by setting our opacity 
							// to full, for the slide in effect.
							$slides.eq( activeIndex ).css({
								opacity: 0
							});
							$slides.eq( data.index ).css({
								opacity: 1
							});
							
						});	
					}
					
				},
				
				buildCaptions: function(){
					
					var i = 0, 
						l = ui.$anchors.length,
						captionHTML = ["<ul class=\"captions\">"],
						photo;
								
					// Build thumbnail HTML.
					for (; i < l; i++) {
						photo = photos[i];
						captionHTML.push("<li data-index=\"" + i + "\"><h3>" + photo.photoName + "</h3>" + photo.photoDescription + "</li>");
					}
					
					captionHTML.push("</ul>");
					
					$captions = $( captionHTML.join("") );
					
					// Append captions.
					if ( options.captionsAfter ) {
						$gallery.after( $captions );
					} else {
						$gallery.prepend( $captions );
					}
					
					$captions = ui.$captions = $captions.find("li");
					
					$captions.eq( activeIndex ).css({
						zIndex: 1,
						visibility: "visible"
					});
	
					$captionContainer = ui.$captionContainer = $captions.parent();
									
					// Need to make this tweak in the next UI thread.
					setTimeout(function(){
						$captionContainer.height( $captions.eq( activeIndex ).height() );
					}, 0);
					
					core.bindCaptions();
					
					// If we need to show thumbnails by default and 
					// hide the captions, do so now.
					if ( options.showThumbnails && options.toggleThumbnails ) {
//						$captions.parent().css("visibility", "hidden");
						$captionContainer.hide();
					}
				},
				
				bindCaptions: function(){
					$aolPhotoGalleryClone.bind("status-update." + namespace, function(event, data){
						
						var oldIndex = data.oldIndex,
							activeIndex = data.activeIndex,
														
							$oldCaption = $captions.eq( oldIndex ),
							$activeCaption = $captions.eq( activeIndex );

						$activeCaption.css({
							visibility: "visible",
							opacity: 0,
							zIndex: 1
						}).animate({
							opacity: 1
						}, speed);	

						$oldCaption.css({
							zIndex: 0
						}).animate({
							opacity: 0
						}, speed); // Fade out didn't work when its parent container was not displayed.
						
						$captionContainer.animate({
							height: $activeCaption.height()
						}, speed);

					});
					
					// If the toggle feature is present, add those bindings.
					if ( options.toggleThumbnails ) {
						$aolPhotoGalleryClone.bind("show-thumbnails." + namespace, function(){
							$captionContainer.css("opacity", 0);
						});
						$aolPhotoGalleryClone.bind("thumbnail-click." + namespace, function(){
							$captions.eq( activeIndex ).css("opacity", 0);
							$captionContainer.css({
								"display": "block",
								"visibility": "visible",
								"opacity": 1
							});
						});
					}
					
				},
				
				buildStatus: function(){
					
					var statusHTML = "<div class=\"status\">" + ( activeIndex + 1 ) + " of " + totalPhotos + "</div>";
						
					$status = ui.$status = $( statusHTML );
					
					$gallery.after( $status );
					
					// We still need Status to live in the DOM.
					if ( ! options.showStatus ) {
						
						$status.hide();
										
					// If we need to show thumbnails by default and 
					// hide the captions, do so now.
					} else if ( options.showThumbnails && options.toggleThumbnails ) {
						// $status.css("visibility", "hidden");
						$status.hide();
					}
					
					core.bindStatus();
				},
				
				bindStatus: function(){
									
					$aolPhotoGalleryClone.bind("thumbnail-click." + namespace, function( event, data ){
						
						var oldIndex = activeIndex;
						
						activeIndex = data.index;
						
						if ( oldIndex !== activeIndex ) {
							$status.html( ( activeIndex + 1 ) + " of " + totalPhotos )
								.trigger("status-update." + namespace, [{ oldIndex: oldIndex, activeIndex: activeIndex }]);
						}
					});
					
					$aolPhotoGalleryClone.bind("back-click." + namespace, function( event, data ){
						if ( ! statusRateLimit ) {
							statusRateLimit = 1;
							var oldIndex = activeIndex;
								
							activeIndex = activeIndex === 0 ? totalPhotos - 1 : activeIndex - 1;
							
							$status.html( ( activeIndex + 1 ) + " of " + totalPhotos )
								.trigger("status-update." + namespace, [{ oldIndex: oldIndex, activeIndex: activeIndex }]);
						
							setTimeout(function(){
								statusRateLimit = 0;
							}, speed );
						}
					});
					
					$aolPhotoGalleryClone.bind("next-click." + namespace, function(){
						if ( ! statusRateLimit ) {
							statusRateLimit = 1;
								
							var oldIndex = activeIndex;
								
							activeIndex = activeIndex === totalPhotos - 1 ? 0 : activeIndex + 1;
							
							$status.html( ( activeIndex + 1 ) + " of " + totalPhotos )
								.trigger("status-update." + namespace, [{ oldIndex: oldIndex, activeIndex: activeIndex }]);
						
							setTimeout(function(){
								statusRateLimit = 0;
							}, speed );
						}	
					});
					
					// If the toggle feature is present, add those bindings.
					if ( options.toggleThumbnails ) {
						
{'fraction_non_alphanumeric': 0.1248871113404722, 'fraction_numerical': 0.006141143078312475, 'mean_word_length': 2.024504448259716, 'pattern_counts': {'":': 14, '<': 37, '<?xml version=': 0, '>': 51, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3716946', 'n_tokens_mistral': 17706, 'n_tokens_neox': 11215, 'n_words': 2949}