function slideShow (yui, picsObj)
{
    this.mState = 0;
    this.mNewImage = null; //Node class
    this.mFirstThumbnailIndex = 0;
    this.mSlotList = 0; //NodeList class
    this.mYui = yui; //YUI class
    
    this.show = function (tagRef, picIndex)
    {
        var img = this.mYui.one ("#idImg");
        var l = picsObj.mPicArray.length;
        var url = picsObj.mPicsPath + picsObj.mPicArray [picIndex].ur;
        
        function onFadeOutComplete ()
        {
            this.pleaseWait (true);
            this.mState++;
            this.displayNewImage ();
            this.printCaption (picsObj.mPicArray [picIndex].tx);
        }                    
        
        if ((url != null) && (img != null))
        {
            var anim = new this.mYui.Anim (
            {
                node: "#idImg",
                to: {opacity: 0},
                duration: 0.25
            });
            anim.on ("end", onFadeOutComplete, this);
            
            this.mState = 0;
            this.mNewImage = null;
            this.loadNewImage (url); //begin async-loading new image
            anim.run (); //begin fade-out of current image
            pageTracker._trackEvent (picsObj.mPicsPath, "showPhoto", picsObj.mPicArray [picIndex].ur); //Google Analytics tracking
        }
    } //show
    
    this.loadNewImage = function (url) //asynchronously load a new image
    {
        function onLoadComplete ()
        {
            this.mState++;
            this.displayNewImage ();
        }
        
        this.mNewImage = this.mYui.Node.create ('<img id="idImg" src="" alt="">');
        this.mNewImage.on ("load", onLoadComplete, this);
        this.mNewImage.set("src", url); //begin async loading
    } //loadNewImage
    
    this.displayNewImage = function ()  //add new image to DOM
    {
        if (this.mState == 2)
        {
            var attrib = {opacity: { to: 1 }};
            var anim = null;
            var imgDiv = this.mYui.one ("#idImageDiv"); 
            
            this.mNewImage.setStyle ("opacity", 0); //set to total transparent
            imgDiv.removeChild (this.mYui.one ("#idImg"));
            imgDiv.appendChild (this.mNewImage);
            //???? anim = new YAHOO.util.Anim ("idImg", attrib, 0.25);
            anim = new this.mYui.Anim (
            {
                node: "#idImg",
                to: {opacity: 1},
                duration: 0.25
            });
            this.pleaseWait (false);
            anim.run (); //begin fade-in of new image
        }
    } //displayNewImage
    
    this.pleaseWait = function (wait)
    {
        var waitDiv = document.getElementById ("idWait");
        if (wait)
        {
            if (waitDiv.firstChild)
                waitDiv.removeChild (waitDiv.firstChild);
            waitDiv.appendChild (document.createTextNode ("loading photo"));
        }
        else
        {
            if (waitDiv.firstChild)
                waitDiv.removeChild (waitDiv.firstChild);
        }
    }//pleaseWait
    
    this.printCaption = function (text)
    {
        var cap = document.getElementById ("idCaption");
        if (cap)
        {
            if (cap.firstChild)
                cap.removeChild (cap.firstChild);
            cap.appendChild (document.createTextNode (text));
        }
    }//printCaption

    this.initThumbnail = function ()
    {
        //mouse has clicked on a nav arrow
        function clickNavEvent (event)
        {
            //clear all marked thumbnail
            this.mSlotList.setStyle ("borderColor", "black");
            if (event.target.get("id") == "idPrevPage")
            {
                var newFirstIndex = this.mFirstThumbnailIndex - this.mSlotList.size();
                //check if new index is within bound
                if (newFirstIndex >= 0)
                    this.displayThumbnail (newFirstIndex);
            }
            else if (event.target.get("id") == "idNextPage")
            {
                var newFirstIndex = this.mFirstThumbnailIndex + this.mSlotList.size();
                //check if new index is within bound
                if (newFirstIndex < picsObj.mPicArray.length)
                    this.displayThumbnail (newFirstIndex);
            }
        }

        //get a list of elements that are slots for display thumbnails
        this.mSlotList = this.mYui.all ("#idTinyPics .sqrPics");
        
        //add click-event handlers to two navigation icons
        this.mYui.one ("#idPrevPage").on ("click", clickNavEvent, this);
        this.mYui.one ("#idNextPage").on ("click", clickNavEvent, this);
    } //initThumbnail
    
    this.displayThumbnail = function (firstPicIndex)
    {
        var picIndex = firstPicIndex; //index of photo in slot #1

        //mouse has clicked on a thumbnail
        function clickSlotEvent (event)
        {
            //get the index number of the photo
            var picIndex = event.target.getAttribute("picIndex");
            //clear all marked thumbnail
            this.mSlotList.setStyle ("borderColor", "black");
            //mark clicked thumbnail
            event.target.setStyle ("borderColor", "aqua");
            this.show (null, picIndex);
        }
        
        function load (elem)
        {
            if (picIndex < picsObj.mPicArray.length)
            {
                //calculate thumbnail's url
                var url = picsObj.mPicsPath + picsObj.mPicArray [picIndex].ur;
                var lastSlash = url.lastIndexOf ("/");
                var url2 = url.slice (0, lastSlash) + "/tiny/" + url.slice(lastSlash);
                elem.setAttribute ("src", url2);
                elem.setStyle ("display", "inline");
                
                //set the index number of the photo
                elem.setAttribute ("picIndex", picIndex);
                elem.purge (); //remove all subscriptions for all events
                elem.on ("click", clickSlotEvent, this); //subscribe to click event
            }
            else
            {
                elem.setStyle ("display", "none");
                elem.setAttribute ("src", ""); //do not display anything
                elem.purge (); //remove all subscriptions for all events
            }
            picIndex++;
        }

        this.displayPageNumber = function (slotList)
        {
            var prev = this.mYui.one ("#idPrevPage");
            var next = this.mYui.one ("#idNextPage");
            var elem = this.mYui.one ("#idPageNumber");
            var text = Math.ceil(firstPicIndex/15)+ 1 + " / " + Math.ceil(picsObj.mPicArray.length/15);

            elem.setContent (text);
            prev.setStyle ("visibility", (firstPicIndex>0) ? "visible" : "hidden");
            next.setStyle (next, "visibility",
                ((firstPicIndex+slotList.length) < picsObj.mPicArray.length) ? "visible" : "hidden");
        }

        this.mFirstThumbnailIndex = firstPicIndex;
        //run load() on all slots to display thumbnails
        this.mSlotList.each (load, this);

        this.displayPageNumber (this.mSlotList);
    } //displayThumbnail
} //slideShow

