var gFrameDelay = 80;

function FadeOut ()
{
    var xOpacity = parseFloat(gBrowser.getElementOpacity(gPrevImage));
    if ( xOpacity > 0.01 )
    {
        gBrowser.setElementOpacity(gPrevImage, xOpacity - 0.08);
        setTimeout("FadeOut()", 10);
    }
    else
    {
        gPrevImage.parentNode.removeChild(gPrevImage);
        if ( ! gImageLoaded )
        {
            //  Next image hasn't finished loading yet.
            //  Set the "Loading..." background gif on the photo display area.
            document.getElementById('photo').style.background = 'url("http://associatedtechs.com/demo/mckay/loading.gif") no-repeat center center';
        }
        if ( ! document.all ) { delete gPrevImage; }
        gPrevImage = null;
    }
}

function FadeIn ()
{
    if ( ! gImageLoaded )
    {
        gImageLoaded = true;
        //  Make the new image invisible.
        gBrowser.setElementOpacity(gLoadingImage, 0);
        //  Set the top of the image.
        gLoadingImage.top = 0;
        //  Remove the "Loading..." background image on the photo display area.
        document.getElementById('photo').style.background = 'url("none") no-repeat center center';
        //  Safari incorrectly sets the "this" value for an onload handler,
        //  so the image has to be referenced via its global variable.
        document.getElementById('photo').appendChild(gLoadingImage);
        //  Safari AGAIN. Have to center the image after attaching it to the photo div,
        //  otherwise the image has a 0 width.
        gLoadingImage.style.left = ((parseInt(gBrowser.getElementStyle(document.getElementById('photo'), 'width')) / 2) - (parseInt(gLoadingImage.width) / 2)) + 'px';
        //  Finally, continue fading the new image in.
        gNextImage = gLoadingImage;
        FadeIn();        
    }
    else
    {
        var xOpacity = parseFloat(gBrowser.getElementOpacity(gNextImage));
        if ( xOpacity < 1 )
        {
            gBrowser.setElementOpacity(gNextImage, 0.04 + xOpacity);
            setTimeout("FadeIn()", 13);
        }
        else
        {
            gCurrentImage = gNextImage;
        }
    }
}

function Slide_From ( pElement, pCoordinates, pCallback )
//  Slides an element from its current position to pCoordinates (new top-left corner for the element).
//  This function slides an element progressively faster until it reaches its location, and is
//  ideal for moving elements "off" the page.
{
    if ( pElement && pCoordinates )
    {
        var xTask = new TimeTask(Slide_From);
        xTask.SetVar('element', pElement);
        //  Opera apparently has a bug where it doesn't correctly handle saving the 'destination' var as an object.
        //  The best thing about the bug is that it decides to return the same value for destination.x as element.style.left;
        //  I have no clue why. That behavior doesn't make any sense at all. I really love spending all this time
        //  coming up with workarounds for bad browser behavior.
        //  So, anyway, split the pCoordinates parameter into two separate variables here.
        xTask.SetVar('destination-x', pCoordinates.x);
        xTask.SetVar('destination-y', pCoordinates.y);
        xTask.SetVar('iteration', 0);
        if ( pCallback ) { xTask.SetVar('completion', pCallback); }
        xTask.SetCleanup(Slide_From);
        xTask.Run();
        return xTask.id;
    }
    else if ( this.vars )
    {
        if ( this.dying )
        {
            if ( ! isNaN(this.vars['destination-x']) ) { this.vars['element'].style.left = this.vars['destination-x'] + 'px'; }
            if ( ! isNaN(this.vars['destination-y']) ) { this.vars['element'].style.top = this.vars['destination-y'] + 'px'; }
            if ( this.vars['completion'] ) { this.vars['completion'](this.vars['element']); }
        }
        else
        {
            if ( this.vars['iteration'] == 0 )
            {
                //  Calculate the number of iterations of Math.log() required to get the element
                //  from a to b. Then, count down from that number, using the same formula as
                //  the old Slip_ToCenter function.
                var xDeltaFromX = parseInt(gBrowser.getElementStyle(this.vars['element'], 'left')) - this.vars['destination-x'];
                this.vars['direction'] = xDeltaFromX < 0 ? 1 : -1;
                xDeltaFromX *= this.vars['direction'] * -1;
                this.vars['iteration'] = 2;
                while ( xDeltaFromX > 2 )
                {
                    this.vars['iteration']++;
                    xDeltaFromX -= xDeltaFromX + (this.vars['direction'] * (xDeltaFromX / Math.log(this.vars['iteration'])));
                }
                this.vars['lastX'] = xDeltaFromX;
                return 10;
            }
            else
            {
                //  This function is not quite right because it won't perfectly return an element
                //  to the position according to the calculations in the loop above.
                //  But it's good enough for now.
                this.vars['lastX'] = this.vars['lastX'] * Math.log(this.vars['iteration'] + 1);
                this.vars['element'].style.left = parseInt(this.vars['element'].style.left) + (this.vars['direction'] * this.vars['lastX']) + 'px';
                this.vars['iteration']--;
                if ( this.vars['iteration'] > 2 ) { return 50; }
                return 0;
            }
        }
    }
}

function Slide_To ( pElement, pCoordinates, pCallback )
//  Slides an element from its current position to pCoordinates (new top-left corner for the element).
//  This function slides an element progressively slower until it reaches its location, and is
//  ideal for moving elements "onto" the page.
{
    if ( pElement && pCoordinates )
    {
        var xTask = new TimeTask(Slide_To);
        xTask.SetVar('element', pElement);
        //  See notes above in Slide_From() regarding destination-x & destination-y.
        xTask.SetVar('destination-x', pCoordinates.x);
        xTask.SetVar('destination-y', pCoordinates.y);
        xTask.SetVar('iteration', 0);
        if ( pCallback ) { xTask.SetVar('completion', pCallback); }
        xTask.SetCleanup(Slide_To);
        xTask.Run();
        return xTask.id;
    }
    else if ( this.vars )
    {
        if ( this.dying )
        {
            if ( ! isNaN(this.vars['destination-x']) ) { this.vars['element'].style.left = this.vars['destination-x'] + 'px'; }
            if ( ! isNaN(this.vars['destination-y']) ) { this.vars['element'].style.top = this.vars['destination-y'] + 'px'; }
            if ( this.vars['completion'] ) { this.vars['completion'](this.vars['element']); }
        }
        else
        {
            var xDeltaFromX = parseInt(gBrowser.getElementStyle(this.vars['element'], 'left')) - this.vars['destination-x'];
            if ( this.vars['iteration'] == 0 )
            {
                this.vars['direction'] = xDeltaFromX < 0 ? 1 : -1;
            }
            xDeltaFromX *= this.vars['direction'] * -1;
            if ( xDeltaFromX > 2 )
            {
                this.vars['iteration']++;
                this.vars['element'].style.left = parseInt(this.vars['element'].style.left) + (this.vars['direction'] * (xDeltaFromX / Math.log(this.vars['iteration'] + 5))) + 'px';
                return 50;
            }
            return 0;
        }
    }
}
