﻿//if (navigator.userAgent.indexOf('MSIE') != -1)
//    console = false;
//var urchinTracker = function() {};
     
// enums
function $Direction()
{
    this.Left = 1;
    this.Right = 2;
}
var Direction = new $Direction();

function $AnimationTarget()
{
    this.Open = 1;
    this.Closed = 2;
}
var AnimationTarget = new $AnimationTarget();

function $AnimationType()
{
    this.Wipe = 1;
    this.Slide = 2;
}
var AnimationType = new $AnimationType();

function $EasingMethod()
{
    this.None = 0;
    // This easing method is currently not supported and will result in an error.
    this.FasterAtStartAndEnd = 1;
    this.FasterAtStart = 2;
    this.FasterAtEnd = 3;
}
var EasingMethod = new $EasingMethod();
// end enums


/*
    Any modifiable settings that need to be initialized before the script is loaded
    exist in this function.  It's initialized when the script is loaded, and these
    settings can therefore be modified by invoking script through the ServerSettings
    object.  For example, the C# code:
    
    string resultScript = string.Format("ServerSettings.RequestBasePath = '{0}';", Request.ApplicationPath);
    
    would produce a script that could be executed in order to alter the base request
    or application path, so that images set dynamically by the script would point
    to the right part of the directory structure.
    
    These settings must be set *before* the PhotoGallery instance is created; setting
    them after calling:
    
    var gallery = new PhotoGallery();
    
    will result in unpredictable behavior.
*/
function $ServerSettings()
{
    this.RequestBasePath = '';
    this.DownloadUrl = 'DownloadGalleryImages.aspx';
    this.StartWithImage = false;
    this.StretchImages = true;
}

var ServerSettings = new $ServerSettings();

/*
    Most modifiable settings for this script exist here (see the $ServerSettings
    function for additional settings that need to be set at load).  Arguably, as long as
    this function exists somewhere before the PhotoGallery instance is created,
    you're okay.  So for instance, if you were going to use this in multiple
    places but with different settings, you'd remove this function from the 
    script and embed it in your application page.
    
    Then you'd initialize the photo gallery object by calling:
    
    var gallery = new PhotoGallery();
    
    That's it!
    
    Settings:
    
    instance.FramesPerSecond - the number of frames of animation per second.  This 
                                determines the interval between position changes.
    instance.AnimationType - one of the members of the AnimationType enum, which is 
                                currently defined as Wipe or Slide.
    instance.HideEasingMethod - one of the members of the EasingMethod enum, which 
    instance.ShowEasingMethod -  is currently defined as FasterAtEnd, FasterAtStart,
                                 or None.  
    instance.LeftFloatDivs - an Array with the IDs of the left-side divs.  This can 
                                 be null if only the Slide animation will be used, but
                                 necessary if the Wipe animation will be used.
    instance.ContentDivs - an Array with the IDs of the content divs.  This is required
                                 as these divs will be animated.
    instance.MaxNumberOfPages - a Number that represents the maximum number of pages to 
                                 display before cropping the number of pages.  For instance,
                                 if this is set to 10, but there are 12 pages, the script
                                 will create "1 2 3 4 5 ... 8 9 10 11 12" as links, depending
                                 on where the current page is.  If it's page 7, it would be 
                                 "... 2 3 4 5 6 7 8 9 10 11 ...".  Finally, a 19-page display at 
                                 page 13 would display: "1 ... 10 11 12 13 14 15 16 17 ... 19"
    instance.SlideShowInterval - a Number that represents the number of milliseconds to display 
                                 an image while the slide show is playing.
                                 
    instance.LeftFloatDivDefaultSize - the default size of the left floating div.  This
                                        is necessary in order to collapse the div appropriately.
    instance.ContentDivDefaultSize - the default size of the content div.  This is necessary
                                        in order to collapse the div appropriately.
*/
function $PhotoGallerySettings()
{
    this.FramesPerSecond = 40;
    this.AnimationType = AnimationType.Slide;
    this.HideEasingMethod = EasingMethod.FasterAtEnd;
    this.ShowEasingMethod = EasingMethod.FasterAtEnd;
    this.MaxNumberOfPages = 10;
    this.SlideShowInterval = 2000; 
    this.Width = 640;
    this.PaddingWidth = 44;
    this.AnimationDuration = 0.75;
    
    this.ServerBaseRequestPath = ServerSettings.RequestBasePath;
    this.ServerDownloadImagePath = ServerSettings.DownloadUrl;

    this.LeftFloatDivs = new Array("TopLeftFloat", "BottomLeftFloat");
    this.ContentDivs = new Array("TopPhotoContent", "BottomPhotoContent");
    this.PagerDivID = "GalleryPager";
    
    this.DisplayedImageID = "GalleryImageDisplay";
    this.DisplayedImageNumberID = "largePhotoId";
    
    this.SelectedImageCssClass = 'selectedImage';
    this.DeselectedImageCssClass = 'bdrImg';
    
    this.LoaderContainerID = "loaderContainer";
    
    
    this.BackdropDivID = "GalleryExpansionBackdrop";
    this.ExpansionDivID = "BigImagePlaceholder";
    
    this.PhotoSearchInputID = 'photoSearch';
    
    this.SendToFriendPath = "assets-playtime";
    
    this.LeftFloatDivDefaultSize = 1;
    this.ContentDivDefaultSize = 450;
    
    this.CategoryImageTabIDs = new Array(
                                            "selectAllImages", 
                                            "selectRoamingImages",
                                            "selectSeattleSkylineImages"
                                        );
                                        /*
public enum GalleryBackdrop : short
{
    AllImages = 0
    Roaming = 1,
    SeattleSkyline = 2,
}   */
    this.CategoryIDsToElementMap = {
                                    0 : this.CategoryImageTabIDs[0],
                                    1 : this.CategoryImageTabIDs[1],
                                    2 : this.CategoryImageTabIDs[2]
                                   };
                                            
    this.CategorySelectedImagePaths = new Array(
                                            "common/assets/images/event_gallery/btn_gallery_allPhotos_SEL.gif", 
                                            "common/assets/images/event_gallery/btn_gallery_roaming_SEL.gif",
                                            "common/assets/images/event_gallery/btn_gallery_seattleSkyline_SEL.gif"
                                              );

    this.CategoryDeselectedImagePaths = new Array(
                                            "common/assets/images/event_gallery/btn_gallery_allPhotos.gif", 
                                            "common/assets/images/event_gallery/btn_gallery_roaming.gif",
                                            "common/assets/images/event_gallery/btn_gallery_seattleSkyline.gif"
                                                 );
                                                 
    this.CategoryPreferredImageOrder = new Array(
                                                    1,
                                                    2,
                                                    0
                                                 );
                                                 
    this.ImageCategoryTextID = 'imageCategoryName';
    this.CategoryNames = new Array(
                                            "All Photos", // 0
                                            "Roaming", // 1
                                            "Seattle Skyline" // 2
                                   );

    this.ErrorMessages = 
    {
        "NoPhotosInCategory" : "There aren't any photos in that category.",
        'InvalidAnimationType' : 'An invalid animation type was selected.  Please choose a member of the AnimationType enumeration.',
        'WipeAnimationUnsupported' : 'Wipe animation is currently unsupported.',
        'PhotoGalleryAjaxError' : 'There was a problem displaying the photo gallery.  Please try again later.',
        'InvalidEasingMethod' : 'An invalid easing method was selected.  Please select a different method.',
        'ImageIDNotFound' : 'That image ID was not found.  Maybe you have the wrong event?',
        'NoPhotosYet' : "There aren't any photos of the event just yet.  Check back soon and often for updates!"
    };
    this.LoadingImage = "/common/assets/images/loading_panel.gif";
    
    this.PassEventIDToLog = true;
}


/*
    PhotoGallery constructor, singleton, and initialization functions.
*/
function PhotoGallery(eventID)
{
    if (PhotoGallery.__instance)
        return false;
        
    this.__imageElements = [];
    this.__textElements = [];
    this.__currentPage = 0;
    this.__isInSlideshow = false;
    this.__slideshowTimeoutID = -1;
    this.__currentImageElementIndex = 0;    
    this.EventID = eventID;
    
    this.__hasLoadedCompletely = false;
    
    this.Settings = new $PhotoGallerySettings();
    this.__animationTimeouts = [];
    for (var i = 0; i < this.Settings.ContentDivs.length; i++)
    {
        this.__animationTimeouts[this.Settings.ContentDivs[i]] = -1;
    }
    
    this.__preloadedImages = [];
    
    this.isOK = function() { return true; }
    
    PhotoGallery.__instance = this;
    
    this.initialize();
    
    this.__selectCategoryTab(0);
}

PhotoGallery.getInstance = function()
{
    return PhotoGallery.__instance;
}

/*
    This function initializes the photo gallery's images 
*/
PhotoGallery.prototype.initialize = function()
{
    /*
        This function gets the first direct child with the specified tag name.
        For instance, in the following HTML:
        <div><a><img />Some text</a></div>
        
        Given that "div" references the <div> and "a" references the <a>, the
        following calls will:
        
        getFirstChildNodeOfType(div, "a") returns a.
        getFirstChildNodeOfType(div, "img") returns null.
        getFirstChildNodeOfType(a, "img") returns the <img> node.
        getFirstChildNodeOfType(a, "#text") returns the text node.
        
        This function performs its search without respect to casing.
    */
    var getFirstChildNodeOfType = function(parentNode, nodeTypeName)
    {
        nodeTypeName = nodeTypeName.toLowerCase();
        var childNodes = parentNode.childNodes;
        var targetNode = null;
        for (var i = 0; i < childNodes.length; i++)
        {
            var childNode = childNodes[i];
            if (!childNode) continue;
            var childNodeName = childNode.nodeName;
            if (!childNodeName) continue;
            
            childNodeName = childNodeName.toLowerCase();
            if (childNodeName == nodeTypeName)
            {
                targetNode = childNode;
                break;
            }
        }
        return targetNode;
    }
    
    
    for (var j = 0; j < this.Settings.ContentDivs.length; j++)
    {
        var divName = this.Settings.ContentDivs[j];
        if (!divName) continue;
        
        var divNode = document.getElementById(divName);
        if (!divNode) continue;
        
        var childNodes = divNode.childNodes;
        for (var i = 0; i < childNodes.length; i++)
        {
            var currentChild = childNodes[i];
            if (!currentChild) continue;
            var childNodeName = currentChild.nodeName;
            if (!childNodeName || childNodeName.toLowerCase() != "div") continue;
            
            // at this point we know that the current child is a div.  Each div has:
            // <a><img /></a> ... <div>#text</div>
            var a = getFirstChildNodeOfType(currentChild, 'a');
            if (!a) continue;
            
            var img = getFirstChildNodeOfType(a, 'img');
            
            var textDiv = getFirstChildNodeOfType(currentChild, 'div');
            
            if (img)
                this.__imageElements.push(img);
                
            if (textDiv)
                this.__textElements.push(textDiv);
        }
    }
    
    PageMethods.GetImages(this.EventID, PhotoGallery.onRetrievePhotosSucceeded, PhotoGallery.onRetrievePhotosFailed);
}


/*
    PhotoGallery paging functions
*/
PhotoGallery.prototype.incrementPage = function()
{
    this.gotoPage(this.__currentPage + 1);
}

PhotoGallery.prototype.decrementPage = function()
{
    this.gotoPage(this.__currentPage - 1);
}

PhotoGallery.prototype.gotoPage = function(pageNumber)
{
    var animationDirection = Direction.Right;
        // TO DO
    // Re-add this when it's working right
    /*
    if (pageNumber > this.__currentPage)
        animationDirection = Direction.Left;
    else
        animationDirection = Direction.Right;
        */
    if (this.Settings.LoadingImage)
        this.__setLoadingDisplay();

    this.performAnimation(this.Settings.AnimationType, animationDirection, AnimationTarget.Closed, this.Settings.HideEasingMethod, this.Settings.Width, "PhotoGallery.getInstance().__gotoPageImpl(" + pageNumber.toString() + ");");
}

PhotoGallery.prototype.gotoPageAndImage = function(pageNumber, imageIndex)
{
    var animationDirection = Direction.Right;
        // TO DO
    // Re-add this when it's working right
    /*
    if (pageNumber > this.__currentPage)
        animationDirection = Direction.Left;
    else
        animationDirection = Direction.Right;
        */

    if (this.Settings.LoadingImage)
        this.__setLoadingDisplay();
    this.performAnimation(this.Settings.AnimationType, animationDirection, AnimationTarget.Closed, this.Settings.HideEasingMethod, this.Settings.Width, "PhotoGallery.getInstance().__gotoPageAndImageImpl(" + pageNumber.toString() + ", " + imageIndex.toString() + ");");
}

PhotoGallery.prototype.__setLoadingDisplay = function()
{
    for (var i = 0; i < this.__imageElements.length; i++)
    {
        this.__imageElements[i].src = this.Settings.LoadingImage;
    }
}

PhotoGallery.prototype.__gotoPageImpl = function(pageNumber)
{
    this.highlightSelectedImage(-1);
    
    var imagesPerPage = this.__imageElements.length;
    
    var totalPages = Math.ceil(this.__photoGallery.GalleryItems.length / imagesPerPage);
    
    // sanitize the input.
    if (pageNumber >= totalPages) 
    {
        pageNumber = 0;
    }
    else if (pageNumber < 0)
    {
        pageNumber = totalPages - 1;
    }
    
    var firstImageIndex = pageNumber * imagesPerPage;
    for (var i = 0; i < this.__imageElements.length; i++)
    {
        if ((i + firstImageIndex) < this.__photoGallery.GalleryItems.length)
        {
            this.__imageElements[i].parentNode.parentNode.style.visibility = 'visible';
            var url = this.__photoGallery.GalleryItems[i + firstImageIndex].ThumbImageName;
            if (url.toLowerCase().indexOf('http://') == -1)
            {
                // this is not a flickr photo
                url = this.__photoGallery.BasePath + url;
            }
            this.__imageElements[i].src = url;
            this.__imageElements[i].parentNode.href = "javascript:PhotoGallery.getInstance().selectImageByIndex(" + (i + firstImageIndex).toString() + ");";
            var txt = this.__textElements[i];
            if (!txt) continue;
            var str = '#' + this.__photoGallery.GalleryItems[i + firstImageIndex].ID.toString();
            if (navigator.userAgent.indexOf('MSIE') != -1)
            {
                txt.innerText = str;
            }
            else
            {
                txt.textContent = str;
            }
        }
        else
        {
            this.__imageElements[i].parentNode.parentNode.style.visibility = 'hidden';
        }
    }
    
    var oldPage = this.__currentPage;
    var direction = Direction.Left;
    // TO DO
    // Re-add this when it's working right
    /*
    if (oldPage < pageNumber)
        direction = Direction.Right;
        */
        
    this.__currentPage = pageNumber;
    this.setupPager();
    
    this.performAnimation(this.Settings.AnimationType, direction, AnimationTarget.Open, 
                                this.Settings.ShowEasingMethod, this.Settings.Width, 
                                'PhotoGallery.getInstance().selectImageByIndex(' + (pageNumber * this.__imageElements.length).toString() + ');');
                                
    if (this.__preloadedImages.length <= (this.__imageElements.length * 2)) // the initial preload hits the first set of thumbs and full size images
    {
        this.__finishPreloading();
    }
}

PhotoGallery.prototype.__finishPreloading = function()
{
    return;
    // preload
    for (var i = this.__imageElements.length; i < this.__photoGallery.FullGalleryItems.length; i++)
    {
        var img = new Image();
        img.src = (this.__photoGallery.BasePath + this.__photoGallery.FullGalleryItems[i].ThumbImageName);
        this.__preloadedImages.push(img);
        
        if (i % this.__imageElements.length == 0) // first image per page
        {    
            // preload the display image too
            var bigger = new Image();
            bigger.src = (this.__photoGallery.BasePath + this.__photoGallery.FullGalleryItems[i].FullSizeImageName);
            this.__preloadedImages.push(bigger);
        }
    }
}

PhotoGallery.prototype.__gotoPageAndImageImpl = function(pageNumber, imageIndex)
{
    this.highlightSelectedImage(-1);
    
    var imagesPerPage = this.__imageElements.length;
    
    var totalPages = Math.ceil(this.__photoGallery.GalleryItems.length / imagesPerPage);
    
    // sanitize the input.
    if (pageNumber >= totalPages) 
    {
        pageNumber = 0;
    }
    else if (pageNumber < 0)
    {
        pageNumber = totalPages - 1;
    }
    if (imageIndex > this.__photoGallery.GalleryItems.length || imageIndex < 0)
    {
        imageIndex = 0;
    }
    if (imageIndex / imagesPerPage != pageNumber)
        pageNumber = Math.floor(imageIndex / imagesPerPage);
        
    
    var firstImageIndex = pageNumber * imagesPerPage;
    
    for (var i = 0; i < this.__imageElements.length; i++)
    {
        if ((i + firstImageIndex) < this.__photoGallery.GalleryItems.length)
        {
            this.__imageElements[i].parentNode.parentNode.style.visibility = 'visible';
            
            var url = this.__photoGallery.GalleryItems[i + firstImageIndex].ThumbImageName;
            if (url.toLowerCase().indexOf('http://') == -1)
            {
                // this is not a flickr photo
                url = this.__photoGallery.BasePath + url;
            }
            this.__imageElements[i].src = url;
            this.__imageElements[i].parentNode.href = "javascript:PhotoGallery.getInstance().selectImageByIndex(" + (i + firstImageIndex).toString() + ");";
            var txt = this.__textElements[i];
            if (!txt) continue;
            var str = '#' + this.__photoGallery.GalleryItems[i + firstImageIndex].ID.toString();
            if (navigator.userAgent.indexOf('MSIE') != -1)
            {
                txt.innerText = str;
            }
            else
            {
                txt.textContent = str;
            }
        }
        else
        {
            this.__imageElements[i].parentNode.parentNode.style.visibility = 'hidden';
        }
    }
    
    var oldPage = this.__currentPage;
    var direction = Direction.Left;
    // TO DO
    // Re-add this when it's working right
    /*
    if (oldPage < pageNumber)
        direction = Direction.Right;
        */
        
    this.__currentPage = pageNumber;
    this.setupPager();
    
    this.performAnimation(this.Settings.AnimationType, direction, AnimationTarget.Open, 
                                this.Settings.ShowEasingMethod, this.Settings.Width, 
                                'PhotoGallery.getInstance().selectImageByIndex(' + imageIndex.toString() + ');');
}

/* See $PhotoGallerySettings.MaxNumberOfPages for more information */
PhotoGallery.prototype.setupPager = function()
{
    var imagesPerPage = this.__imageElements.length;
    
    var totalPages = Math.ceil(this.__photoGallery.GalleryItems.length / imagesPerPage);
//    if ((this.__photoGallery.GalleryItems.length % imagesPerPage) == 0)
//        totalPages += 1;
    var lastPage = totalPages - 1;
    
    var maxPages = this.Settings.MaxNumberOfPages;
    
    var pagesToLink = [];
    /*
    instance.MaxNumberOfPages - a Number that represents the maximum number of pages to 
                                 display before cropping the number of pages.  For instance,
                                 if this is set to 10, but there are 12 pages, the script
                                 will create "1 2 3 4 5 6 7 8 9 ... 12" as links, depending
                                 on where the current page is.  Finally, a 19-page display at 
                                 page 13 would display: "1 ... 10 11 12 13 14 15 16 17 ... 19"
    */
    if (totalPages <= maxPages)
    {
        for (var i = 0; i < totalPages; i++)
        {
            pagesToLink.push(i);
        }
    }
    else
    {
        var middlePages = maxPages - 2;
        var lowerBound = -1;
        var upperBound = -1;
        if ((maxPages % 2) == 1) // if odd
        {
            lowerBound = this.__currentPage - ((middlePages - 1) / 2);
            upperBound = this.__currentPage + ((middlePages - 1) / 2);
        } 
        else // if even
        {
            lowerBound = this.__currentPage - ((middlePages - 2) / 2);
            upperBound = this.__currentPage + (middlePages / 2);
        }
        if (lowerBound < 0)
        {
            var difference = lowerBound;
            lowerBound -= difference; 
            lowerBound++;
            upperBound -= difference;
            upperBound++;
        }
        
        if (upperBound > lastPage)
        {
            var difference = (lastPage - upperBound);
            lowerBound += difference;
            lowerBound--;
            upperBound += difference;
            upperBound--;
        }
        
        pagesToLink.push(0);
        if (lowerBound > 1)
            pagesToLink.push('...');
        
        for (var i = lowerBound; i <= upperBound; i++)
        {
            pagesToLink.push(i);
        }
        
        if ((upperBound + 1) < lastPage)
            pagesToLink.push('...');
        
        pagesToLink.push(lastPage);
    }
    
    var targetPagerDiv = document.getElementById(this.Settings.PagerDivID);
    // clear out the existing pager
    while (targetPagerDiv.childNodes.length > 0)
    {
        targetPagerDiv.removeChild(targetPagerDiv.firstChild);
    }
    
    for (var i = 0; i < pagesToLink.length; i++)
    {
        var current = pagesToLink[i];
        var node = null;
        if (current.toString() == "...") 
        {
            node = document.createTextNode(current);
        }
        else
        {
            node = document.createElement("a");
            node.href = "javascript:PhotoGallery.getInstance().gotoPage(" + current + ");";
            node.appendChild(document.createTextNode( (current + 1).toString() ));
            if (current == this.__currentPage)
            {
                node.style.fontWeight = 'bold';
                node.style.textDecoration = 'underline';
            }
        }
        targetPagerDiv.appendChild(node);
        targetPagerDiv.appendChild(document.createTextNode(' '));
    }
}


/* 
    PhotoGallery slideshow functions
*/
PhotoGallery.prototype.beginSlideshow = function()
{
    if (this.__photoGallery.GalleryItems.length == 0) 
    {
        alert(this.Settings.ErrorMessages.NoPhotosYet);
        return;
    }
    if (this.__isInSlideshow) return false;
    
    this.__isInSlideshow = true;
    this.__slideshowTimeoutID = setTimeout('PhotoGallery.getInstance().__slideshowTick()', this.Settings.SlideShowInterval);
}

PhotoGallery.prototype.pauseSlideshow = function()
{
    if (this.__photoGallery.GalleryItems.length == 0) 
    {
        alert(this.Settings.ErrorMessages.NoPhotosYet);
        return;
    }
    if (!this.__isInSlideshow) return false; 
    
    clearTimeout(this.__slideshowTimeoutID);
    this.__slideshowTimeoutID = -1;
}

PhotoGallery.prototype.resumeSlideshow = function()
{
    if (this.__photoGallery.GalleryItems.length == 0) 
    {
        alert(this.Settings.ErrorMessages.NoPhotosYet);
        return;
    }
    if (!this.__isInSlideshow) return this.beginSlideshow();
    
    this.__slideshowTimeoutID = setTimeout('PhotoGallery.getInstance().__slideshowTick()', this.Settings.SlideShowInterval);
}

PhotoGallery.prototype.isSlideshowPaused = function() { return this.__slideshowTimeoutID == -1 && this.__isInSlideshow; };

PhotoGallery.prototype.isSlideshowActive = function() { return this.__slideshowTimeoutID >= 0 && this.__isInSlideshow; };

PhotoGallery.prototype.isSlideshowStopped = function() { return this.__slideshowTimeoutID == -1 && !this.__isInSlideshow; };

PhotoGallery.prototype.__isSlideshowStateInconsistent = function() { return !(this.isSlideshowPaused() || this.isSlideshowActive() || this.isSlideshowStopped()); };

PhotoGallery.prototype.stopSlideshow = function()
{
    if (!this.__isInSlideshow) return false;
    
    this.__isInSlideshow = false;
    clearTimeout(this.__slideshowTimeoutID);
}

PhotoGallery.prototype.__slideshowTick = function()
{
    this.nextImage();
    var timeout = this.Settings.SlideShowInterval;
    // if we're about to animate, then add time to animate in and out.
    if (this.__currentImageElementIndex == (this.__imageElements.length - 1))
        timeout += (2000 * this.Settings.AnimationDuration);
    this.__slideshowTimeoutID = setTimeout('PhotoGallery.getInstance().__slideshowTick()', timeout);
}



/* 
    Image select functions
*/
PhotoGallery.prototype.selectImage = function(imageID)
{
    imageID = imageID.toString().toLowerCase();
    if (this.__hasLoadedCompletely)
    {
        var foundImage = false;
        for (var i = 0; i < this.__photoGallery.GalleryItems.length; i++)
        {
            var img = this.__photoGallery.GalleryItems[i];
            if (img.ID.toLowerCase() == imageID)
            {
                this.selectImageByIndex(i);
                foundImage = true;
                break;
            }
        }
        
        if (!foundImage)
        {
            for (var i = 0; i < this.__photoGallery.FullGalleryItems.length; i++)
            {
                var img = this.__photoGallery.FullGalleryItems[i];
                if (img.ID.toLowerCase() == imageID)
                {
                    this.selectCategoryAndImage(-1, i);
                    foundImage = true;
                    break;
                }
            }
        }
        
        return foundImage;
    }
    else
    {
        this.__hasLoadedCompletely = true;
        return false;
    }
}

PhotoGallery.prototype.selectImageByIndex = function(imageIndex)
{
    if (imageIndex >= this.__photoGallery.GalleryItems.length)
        return false;
    
    var currentPage = Math.floor(imageIndex / this.__imageElements.length);
    if (this.__currentPage == currentPage)
    {
        // just select the appropriate image.
        this.__selectImageImpl(imageIndex);
    }
    else
    {
        var animationDirection = 0;
        if (currentPage > this.__currentPage)
            animationDirection = Direction.Left;
        else
            animationDirection = Direction.Right;
            
        
        this.gotoPageAndImage(currentPage, imageIndex);
    }
}

PhotoGallery.prototype.__selectImageImpl = function(imageIndex)
{
    var pageRelativeImageIndex = imageIndex % this.__imageElements.length;
    this.__currentImageElementIndex = pageRelativeImageIndex;
    this.__currentImageIndex = imageIndex;
    this.highlightSelectedImage(pageRelativeImageIndex);
    
    var categoryName = this.Settings.CategoryNames[0];
    if (this.__photoGallery.GalleryItems[imageIndex].ClassificationID < this.Settings.CategoryNames.length)
    {
        categoryName = this.Settings.CategoryNames[this.__photoGallery.GalleryItems[imageIndex].ClassificationID];
    }
    
    var url = this.__photoGallery.GalleryItems[imageIndex].FullSizeImageName;
    if (url.toLowerCase().indexOf('http://') == -1)
        url = this.__photoGallery.BasePath + url;
    var elem = document.getElementById(this.Settings.DisplayedImageID);
    elem.style.width = '';
    elem.style.height = '';
    
    var newImgElem = null;
    
    newImgElem = new Asset.images([ url ], { onComplete : function() 
    {
        newImgElem.id = PhotoGallery.getInstance().Settings.DisplayedImageID;
        elem.parentNode.replaceChild(newImgElem, elem);
        elem = newImgElem;
        
        var mooElem = newImgElem;
        var theSize = mooElem.getSize();
        var fullWidth = 466.0;
        var fullHeight = 318.0;
        var targetRatio = fullWidth / fullHeight;
        var imgWidth = parseFloat(theSize.size.x.toString());
        var imgHeight = parseFloat(theSize.size.y.toString());
        var imgRatio = imgWidth / imgHeight;
        
        if (ServerSettings.StretchImages)
        {
            if (imgRatio > targetRatio)
            {
                // img is wider than the target, so set width style
                elem.style.width = '466px';
                elem.width = '466';
                elem.style.height = (466.0 / imgRatio).toString() + 'px';
                elem.height = (466.0 / imgRatio).toString();
            }
            else
            {
                // img is taller than the target, so set height style
                elem.style.height = '318px';
                elem.height = '318';
                elem.style.width = (318.0 * imgRatio).toString() + 'px';
                elem.width = (318.0 * imgRatio).toString();
            }
        }
    }
    })[0];

    if (!Element)
    {
        if (navigator.userAgent.indexOf('MSIE') != -1)
        {
            document.getElementById(this.Settings.DisplayedImageNumberID).innerText = this.__photoGallery.GalleryItems[imageIndex].ID.toString();
            document.getElementById(this.Settings.ImageCategoryTextID).innerText = categoryName;
        }
        else
        {
            document.getElementById(this.Settings.DisplayedImageNumberID).textContent = this.__photoGallery.GalleryItems[imageIndex].ID.toString();
            document.getElementById(this.Settings.ImageCategoryTextID).textContent = categoryName;
        }
    }
    else
    {
        $(this.Settings.DisplayedImageNumberID).setText(this.__photoGallery.GalleryItems[imageIndex].ID.toString());
        $(this.Settings.ImageCategoryTextID).setText(categoryName);
    }
}

PhotoGallery.prototype.highlightSelectedImage = function(imageElementIndex)
{
    for (var i = 0; i < this.__imageElements.length; i++)
    {
        if (i == imageElementIndex)
        {
            this.__imageElements[i].className = this.Settings.SelectedImageCssClass;
        }
        else
        {
            this.__imageElements[i].className = this.Settings.DeselectedImageCssClass;
        }
    }
}

PhotoGallery.prototype.nextImage = function()
{
    if (this.__photoGallery.GalleryItems.length == 0) 
    {
        alert(this.Settings.ErrorMessages.NoPhotosYet);
        return;
    }
        
    var currentImageIndex = this.__currentImageIndex;
    currentImageIndex += 1;
    if (currentImageIndex >= this.__photoGallery.GalleryItems.length)
        currentImageIndex = 0;
    
    this.selectImageByIndex(currentImageIndex);
}

PhotoGallery.prototype.previousImage = function()
{
    if (this.__photoGallery.GalleryItems.length == 0) 
    {
        alert(this.Settings.ErrorMessages.NoPhotosYet);
        return;
    }
        
    var currentImageIndex = this.__currentImageIndex;
    currentImageIndex -= 1;
    if (currentImageIndex < 0)
        currentImageIndex = this.__photoGallery.GalleryItems.length - 1;
    
    this.selectImageByIndex(currentImageIndex);
}


PhotoGallery.prototype.logSearch = function(search)
{
    if (PageMethods.LogSearch)
    {
        if (this.Settings.PassEventIDToLog)
            PageMethods.LogSearch(search, PhotoGallery.getInstance().__photoGallery.EventID, null, null);
        else
            PageMethods.LogSearch(search, null, null);
    }
}

/* 
    AJAX Callbacks
*/
PhotoGallery.onRetrievePhotosSucceeded = function(returnValue)
{
    var pg = PhotoGallery.getInstance();
    
    pg.__photoGallery = returnValue;
    
    if (pg.Settings.CategoryPreferredImageOrder)
    {
        var sortedList = [];
        for (var i = 0; i < pg.Settings.CategoryPreferredImageOrder.length; i++)
        {
            for (var j = 0; j < pg.__photoGallery.GalleryItems.length; j++)
            {
                if (pg.__photoGallery.GalleryItems[j].ClassificationID == pg.Settings.CategoryPreferredImageOrder[i])
                {
                    sortedList.push(pg.__photoGallery.GalleryItems[j]);
                }
            }
        }
        pg.__photoGallery.GalleryItems = sortedList;
    }    
    
    pg.__photoGallery.FullGalleryItems = pg.__photoGallery.GalleryItems;
    
    if (returnValue.CategoryIDs) 
    {
        for (var i = 0; i < returnValue.CategoryIDs.length; i++)
        {
            document.getElementById(pg.Settings.CategoryIDsToElementMap[returnValue.CategoryIDs[i]]).style.display = '';
        }
    }
    
    // preload
    for (var i = 0; i < pg.__imageElements.length && i < pg.__photoGallery.FullGalleryItems.length; i++)
    {
        if (i < pg.__imageElements.length)
        {
            var img = new Image();
            img.src = (pg.__photoGallery.BasePath + pg.__photoGallery.FullGalleryItems[i].ThumbImageName);
            pg.__preloadedImages.push(img);
            
            // preload the display image too
            var bigger = new Image();
            bigger.src = (pg.__photoGallery.BasePath + pg.__photoGallery.FullGalleryItems[i].FullSizeImageName);
            pg.__preloadedImages.push(bigger);
        }
    }
    
    pg.__hasLoadedCompletely = true;
    
    if (returnValue.GalleryItems.length == 0)
    {
        alert(pg.Settings.ErrorMessages.NoPhotosYet);
    }
    
    if (ServerSettings.StartWithImage)
    {
        pg.selectImage(ServerSettings.StartWithImage);
    }
    else
    {
        pg.gotoPage(0);
    }
}

PhotoGallery.onRetrievePhotosFailed = function()
{
    alert(PhotoGallery.getInstance().Settings.ErrorMessages.PhotoGalleryAjaxError);
}


/*
    Animation events
*/
PhotoGallery.prototype.onAllAnimationsEnd = function()
{

}

PhotoGallery.prototype.onHideAnimationEnd = function()
{

}

PhotoGallery.prototype.onShowAnimationEnd = function()
{

}

PhotoGallery.prototype.performAnimation = function(animationType, animationDirection, animationTarget, easingMethod, width, functionToRunAtEnd)
{       
    var msInterval = 1000 / this.Settings.FramesPerSecond;
    var msDuration = 1000 * this.Settings.AnimationDuration;
    var totalFrames = msDuration / msInterval;
    
    if (animationType == AnimationType.Slide)
    {
        var startingPos = 0;
        var targetPos = 0;
        if (animationTarget == AnimationTarget.Closed)
        {
            startingPos = 0;
            targetPos = width;
            if (animationDirection == Direction.Left)
                targetPos = -targetPos;
        }
        else if (animationTarget == AnimationTarget.Open)
        {
            startingPos = width;
            targetPos = 0;
            if (animationDirection == Direction.Right)
                startingPos = -startingPos;
        }
        
        for (var i = 0; i < this.Settings.ContentDivs.length; i++)
        {
            // if the animation timeout isn't -1, then it's already in animation
            // so don't re-animate or we'll get a funky jumpy thing.
            if (this.__animationTimeouts[this.Settings.ContentDivs[i]] != -1) { continue; }
            
            var div = document.getElementById(this.Settings.ContentDivs[i]);
            if (!div) { continue; }
            
            var terminalFunctionCall = '';
            if (i == (this.Settings.ContentDivs.length - 1)) 
            {
                terminalFunctionCall = functionToRunAtEnd;
            }
            
            var functionToExecute = 'PhotoGallery.getInstance().__animationStep(' + animationType + ', ' + animationDirection + ', ' + animationTarget + ', ' + easingMethod + ', 0, '
                        + totalFrames + ', ' + startingPos + ', ' + targetPos + ', "' + div.id + '", "' + terminalFunctionCall + '", ' + msInterval + ');'

            var to = setTimeout(functionToExecute, msInterval);
            this.__animationTimeouts[div.id] = to;
        } 
    }
}


/*
    Enlarge functions
*/
PhotoGallery.prototype.enlarge = function(photoUrl)
{
    var win = window.open(photoUrl, "enlargedImage", "toolbar=0,directories=no,location=no,menubar=no,resizable=yes,width=1200,height=790,statusbar=no,status=no,scrollbars=yes", true);
    win.focus();
}

PhotoGallery.prototype.enlargeCurrent = function()
{
    if (this.__photoGallery.GalleryItems.length == 0) 
    {
        alert(this.Settings.ErrorMessages.NoPhotosYet);
        return;
    }
        
    var index = this.__currentImageIndex;
    var photo = this.__photoGallery.GalleryItems[index];
    var url = photo.PrintImageName;
    if (url.toLowerCase().indexOf('http://') == -1)
        url = this.__photoGallery.BasePath + url;
    this.enlarge(url);
};

PhotoGallery.prototype.downloadCurrent = function()
{
    if (this.__photoGallery.GalleryItems.length == 0) 
    {
        alert(this.Settings.ErrorMessages.NoPhotosYet);
        return;
    }
        
    var index = this.__currentImageIndex;
    var photo = this.__photoGallery.GalleryItems[index];
    if (photo.PrintImageName.toLowerCase().indexOf('http://') != -1) 
    {
        this.enlargeCurrent();
    }
    else
    {
    var url = this.Settings.ServerDownloadImagePath + '?id=' + this.EventID + '&file=' + photo.PrintImageName;
    window.location = url;
    }
};


/* 
    Text box search functions
*/
PhotoGallery.prototype.tryToParseTypedIndex = function()
{
    var element = document.getElementById(this.Settings.PhotoSearchInputID);
    if (!element) return false;
    
    var text = element.value;
    
    if (!this.selectImage(text))
    {
        alert(this.Settings.ErrorMessages.ImageIDNotFound);
        return false;
    } 
    else
    {
        this.logSearch(text);
    }
    
    return true;
}

PhotoGallery.prototype.onTextKeyDown = function(sender, e)
{
    var key;
    if (window.event)
        key = window.event.keyCode;
    else
        key = e.which;
    
    if (key == 13)
    {
        if (!this.tryToParseTypedIndex())
        {
            sender.focus();
        }
        return false;
    }
    return true;
}

/*
    Send to Friend functions
*/
PhotoGallery.prototype.sendToFriend = function(photoFilename, photoID)
{
    window.open(this.Settings.ServerBaseRequestPath + 'SendToFriend.aspx?event=' + this.EventID + '&image=' + photoFilename + 
        '&path=' + this.Settings.SendToFriendPath, null, 'toolbar=0,width=360,height=455,statusbar=no,scrollbars=0'); 
}

PhotoGallery.prototype.sendCurrentToFriend = function()
{
    if (this.__photoGallery.GalleryItems.length == 0) 
    {
        alert(this.Settings.ErrorMessages.NoPhotosYet);
        return;
    }
        
    var index = this.__currentImageIndex;
    var photo = this.__photoGallery.GalleryItems[index];
    this.sendToFriend(photo.FullSizeImageName, photo.ID);
}


/*
    Category functions
*/

PhotoGallery.prototype.selectCategoryAndImage = function(categoryID, imageIndex, actualCategoryIndex)
{
    if (!actualCategoryIndex) actualCategoryIndex = categoryID;
    
    if (this.Settings.LoadingImage)
        this.__setLoadingDisplay();
        
    this.performAnimation(this.Settings.AnimationType, Direction.Right, AnimationTarget.Closed,
                            this.Settings.HideEasingMethod, this.Settings.Width,
                            "PhotoGallery.getInstance().__selectCategoryWithImageImpl(" + categoryID.toString() + ", " + imageIndex.toString() + ", " + actualCategoryIndex.toString() + ");");
};

PhotoGallery.prototype.__selectCategoryWithImageImpl = function(categoryID, imageIndex, actualCategoryIndex)
{
    if (!this.__photoGallery.FullGalleryItems)
    {
        this.__photoGallery.FullGalleryItems = this.__photoGallery.GalleryItems;
    }
    
    var galleryItems = [];
    
    /* Event-specific Customization:
            FLUGTAG TEXAS (event id 46) 
            COMBINE special display photos (ClassificationID = 0) with Roaming Photo (ClassificationID = 6) */
    if (categoryID == 6)
    {
        for (var i = 0; i < this.__photoGallery.FullGalleryItems.length; i++)
        {
            if (this.__photoGallery.FullGalleryItems[i].ClassificationID == 0)
                galleryItems.push(this.__photoGallery.FullGalleryItems[i]);
        }
    }
    
    for (var i = 0; i < this.__photoGallery.FullGalleryItems.length; i++)
    {
        if (categoryID == -1 || categoryID == this.__photoGallery.FullGalleryItems[i].ClassificationID)
            galleryItems.push(this.__photoGallery.FullGalleryItems[i]);
    }
    if (categoryID == -1 || categoryID > this.Settings.CategoryImageTabIDs.length) categoryID = 0;
    
    if (galleryItems.length == 0 && categoryID != -1)
    {
        alert(this.Settings.ErrorMessages.NoPhotosInCategory);
        return this.__selectCategoryWithImageImpl(-1, imageIndex);
    }
    else if (categoryID == -1)
    {
        alert(this.Settings.ErrorMessages.NoPhotosInCategory);
        return false;
    }
    else
    {
        this.__photoGallery.GalleryItems = galleryItems;
    }
    
    this.__selectCategoryTab(actualCategoryIndex);

    // calculate the page of the image based on its index
    var imagesPerPage = this.__imageElements.length;
    var pageIndex = Math.floor(imageIndex / imagesPerPage);
    
    this.__gotoPageAndImageImpl(pageIndex, imageIndex);
};

PhotoGallery.prototype.selectCategory = function(categoryID, actualIndex)
{
    if (!actualIndex) actualIndex = categoryID;
    
    if (this.Settings.LoadingImage)
        this.__setLoadingDisplay();
        
    this.performAnimation(this.Settings.AnimationType, Direction.Right, AnimationTarget.Closed, 
                            this.Settings.HideEasingMethod, this.Settings.Width, 
                            "PhotoGallery.getInstance().__selectCategoryImpl(" + categoryID.toString() + ", " + actualIndex.toString() + ");");
}

PhotoGallery.prototype.__selectCategoryImpl = function(categoryID, actualIndex)
{
    if (!this.__photoGallery.FullGalleryItems)
    {
        this.__photoGallery.FullGalleryItems = this.__photoGallery.GalleryItems;
    }
    
    var galleryItems = [];

    /* Event-specific Customization:
            FLUGTAG TEXAS (event id 46) 
            COMBINE special display photos (ClassificationID = 0) with Roaming Photo (ClassificationID = 6) */
    if (categoryID == 6)
    {
        for (var i = 0; i < this.__photoGallery.FullGalleryItems.length; i++)
        {
            if (this.__photoGallery.FullGalleryItems[i].ClassificationID == 0)
                galleryItems.push(this.__photoGallery.FullGalleryItems[i]);
        }
    }
    
    for (var i = 0; i < this.__photoGallery.FullGalleryItems.length; i++)
    {
        if (categoryID == -1 || categoryID == this.__photoGallery.FullGalleryItems[i].ClassificationID)
            galleryItems.push(this.__photoGallery.FullGalleryItems[i]);
    }
    if (categoryID == -1 || categoryID > this.Settings.CategoryImageTabIDs.length) categoryID = 0;
    
    if (galleryItems.length == 0)
    {
        alert(this.Settings.ErrorMessages.NoPhotosInCategory);
        return this.__selectCategoryImpl(-1);
    }
    else
    {
        this.__photoGallery.GalleryItems = galleryItems;
    }
    
    this.__selectCategoryTab(actualIndex);
    this.__gotoPageImpl(0);
}

PhotoGallery.prototype.__selectCategoryTab = function(categoryID)
{
    if (categoryID < 0) categoryID = 0;
    
    for (var i = 0; i < this.Settings.CategoryImageTabIDs.length; i++)
    {
        if (i == categoryID)
        {
            var url = this.Settings.ServerBaseRequestPath + this.Settings.CategorySelectedImagePaths[i];
            document.getElementById(this.Settings.CategoryImageTabIDs[i]).src = url;
        }
        else
        {
            var url = this.Settings.ServerBaseRequestPath + this.Settings.CategoryDeselectedImagePaths[i];
            document.getElementById(this.Settings.CategoryImageTabIDs[i]).src = url;
        }
    }
}



/* 
    Static call to calculate position based on animation settings.
*/
PhotoGallery.calculatePosition = function(animationStartPos, animationEndPos, currentFrame, totalFrames, framesPerSec, easingMethod)
{
    var nextPosition;
    var interval = 1000.0 / framesPerSec;               // this is the number of millis between frames
    var nextTime = (currentFrame + 1.0) * interval;     // this is the time of the next frame
    var timeForAnimMsecs = interval * totalFrames;      // this is the total number of millis for the animation
    
    if (easingMethod == EasingMethod.FasterAtStart)
    {
        // this is modeled with an x(t) = sqrt(t) relationship.
        // specifically, given the following variables:
        /*
            p'(t) is the current position given time t
            t is current time
            tF is final time (initial time is always assumed to be 0)
            pI is initial position
            pF is final position.
            
            p'(t) = sqrt(t / tF) * (pF - pI)
        */
        nextPosition = Math.sqrt(nextTime / timeForAnimMsecs) * (animationEndPos - animationStartPos);
    } 
    else if (easingMethod == EasingMethod.FasterAtEnd)
    {
        // this is modeled with an x(t) = x^2 relationship:
        /*
            p'(t) is the current position given time t
            t is current time
            tF is final time (initial time is always assumed to be 0)
            pI is initial position
            pF is final position
            
            p'(t) = (pF - pI) * (t / tF)^2 
        */
        nextPosition = Math.pow((nextTime / timeForAnimMsecs), 2) * (animationEndPos - animationStartPos);
        if (nextPosition < 0 && animationEndPos == 0)
            nextPosition = (Math.abs(animationEndPos - animationStartPos) + nextPosition);
    }
    else if (easingMethod == EasingMethod.None)
    {
        // this is modeled with an x(t) = t relationship:
        /*
            p'(t) is the current position given time t
            t is the current time
            tF is the final time (initial time is always assumed to be 0)
            pI is the initial position
            pF is the final position
            
            p'(t) = ((pF - pI) / tF) * t
        */
        nextPosition = ((animationEndPos - animationStartPos) / timeForAnimMsecs) * nextTime;
    }
    else
    {
        alert(this.Settings.ErrorMessages.InvalidEasingMethod);
        return;
    }
    
    // correct for over-dragging
    if (animationEndPos > animationStartPos)
    {
        // going right
        if (nextPosition > animationEndPos)
            nextPosition = animationEndPos;
    }
    else
    {
        // going left
        if (nextPosition < animationEndPos)
            nextPosition = animationEndPos;
    }
    return nextPosition;
}





// this function is the general-purpose animation stepwise function.  It animates a frame step 
// and then sets the timeout for the next frame step.
//
// it is self-recursing and calls itself with all the data it needs to complete the animation.
// it uses setTimeout() to call itself.
//
// @animationType - one of AnimationType.Slide or AnimationType.Wipe.  The Slide is when the content moves out 
//                  to the left or right; the Swipe is when the content stays in place and something moves over it.
// @animationDirection - one of ANIMATION_DIRECTION_LEFT or ANIMATION_DIRECTION_RIGHT.  This is self-documenting.
PhotoGallery.prototype.__animationStep = function(animationType, animationDirection, animationTarget, easingMethod, currentFrame, 
                                                    totalFrames, startingPos, targetPos, idOfDivToMove, functionToExecuteAtEnd, interval)
{
    var nextPos = PhotoGallery.calculatePosition(startingPos, targetPos, currentFrame, totalFrames, this.Settings.FramesPerSecond, easingMethod);
    var div = document.getElementById(idOfDivToMove);
    
//    if (console)
//        console.log('animation step on ' + idOfDivToMove + ':       frame ' + currentFrame + ' of ' + totalFrames + ' total every ' + interval + 'ms.  Waiting to execute: ' + functionToExecuteAtEnd);
    
    /*
        Animation is complete
    */
    if (currentFrame >= totalFrames)
    {
        this.onAllAnimationsEnd();
        if (animationTarget == AnimationTarget.Closed)
        {
            this.onHideAnimationEnd();
        }
        else
        {
            this.onShowAnimationEnd();
        }
        this.__animationTimeouts[idOfDivToMove] = -1;
        eval(functionToExecuteAtEnd);
    }
    else
    {
        
        var nextFrame = currentFrame + 1;
        /* 
            Slide animation is selected
        */
        if (animationType == AnimationType.Slide)
        {
            var startingPos = 0;
            var targetPos = 0;
            if (animationTarget == AnimationTarget.Closed)
            {
                startingPos = 0;
                targetPos = this.Settings.Width;
                if (animationDirection == Direction.Left)
                    targetPos = -targetPos;
            }
            else if (animationTarget == AnimationTarget.Open)
            {
                startingPos = this.Settings.Width;
                targetPos = 0;
                if (animationDirection == Direction.Right)
                    startingPos = -startingPos;
            }
            /*
            animationStartPos, animationEndPos, currentFrame, totalFrames, framesPerSec, easingMethod
            */
            var nextPos = PhotoGallery.calculatePosition(startingPos, targetPos, nextFrame, totalFrames, this.Settings.FramesPerSecond, easingMethod);            
            
            div.style.left = nextPos.toString() + 'px';
            //div.style.width = (this.Settings.Width - nextPos).toString() + 'px';
            
                /* 
                function(animationType, animationDirection, animationTarget, easingMethod, currentFrame, 
                                                    totalFrames, startingPos, targetPos, idOfDivToMove, 
                                                    functionToExecuteAtEnd, interval)
                                                    */
            var functionCallToMake = 'PhotoGallery.getInstance().__animationStep(' +
                    animationType + ', ' + animationDirection + ', ' + 
                    animationTarget + ', ' + easingMethod + ', ' + nextFrame + ', ' +
                    totalFrames + ', ' + startingPos + ', ' + targetPos + ', "' + 
                    div.id + '", "' + functionToExecuteAtEnd + '", ' + interval + ');';
                    
            setTimeout(functionCallToMake, interval);
        }
        else if (animationType == AnimationType.Wipe)
        {
            alert(this.Settings.ErrorMessages.WipeUnsupported);
        }
        else
        {
            alert(this.Settings.ErrorMessages.InvalidAnimationType);
        }
    }
}