// This library contains effects: operations that take time
// An effect should be chainable: the last argument of an effect call may be another function, that is to be executed when
// the effect has finished.

var stepsPerSecond = 20;
var stepDelay = 1 / stepsPerSecond;

// this function moves from start to end, starting off fast and slow in the end, 
// in 'duration' seconds; it updates some value 'stepsPerSecond' times, 
// by calling 'setter()'; when it is done, it calls 'nextEffect()'
function gfChangeParabolically(start, end, duration, setter, nextEffect)
{
    var startTime = gfGetTimeStamp();
    gfHelper();

    function gfHelper()
    {
        // calculate the time passed (as a number in [0..1])
        var alpha = gfGetDurationAlpha(startTime, duration);
        // calculate the position shift as a function of time passed (alpha); offset should be in [0..1]
        var offset = 1 - (1 - alpha)*(1 - alpha);
        var pos = start + offset * (end - start);
        
        pos = gfConstrain(pos, start, end);
        setter(pos);
            
        // iterate again?
        if (alpha < 1) gfSchedule(gfHelper, stepDelay);
        // no: perform next effect
        else if (nextEffect) nextEffect();
    }
}
// this function moves from start to end, linearly, 
// in 'duration' seconds; it updates some value 'stepsPerSecond' times, 
// by calling 'setter()'; when it is done, it calls 'nextEffect()'
function gfChangeLinearly(start, end, duration, setter, nextEffect)
{
    var startTime = gfGetTimeStamp();
    gfHelper();

    function gfHelper()
    {
        // calculate the time passed (as a number in [0..1])
        var alpha = gfGetDurationAlpha(startTime, duration);
        var pos = start + alpha * (end - start);
        setter(pos);
        // iterate again?
        if (alpha < 1) gfSchedule(gfHelper, stepDelay);
        // no: perform next effect
        else if (nextEffect) nextEffect();
    }
}

// duration: in seconds
function gfFade(element, start, end, duration, nextEffect)
{
    gfChangeLinearly(start, end, duration, function(pos){ gfSetOpacity(element, pos); }, nextEffect)
}

// duration: in seconds
function gfScrollHorizontalParabolically(element, start, end, duration, nextEffect)
{
    gfChangeParabolically(start, end, duration, function(pos){ gfSetBorderPos(element, pos, 0); }, nextEffect)
}
function gfScrollVerticalParabolically(element, start, end, duration, nextEffect)
{
    gfChangeParabolically(start, end, duration, function(pos){ gfSetBorderPos(element, 0, pos); }, nextEffect)
}

// hides element if it was shown; shows it when it was hidden
// currently only for blocks
function gfFlipDisplay(element)
{
    if (element.style.display == "none") element.style.display = "block";
    else element.style.display = "none";
}
function gfFlipText(element, firstText, secondText)
{
    if (element.nodeValue == secondText) element.nodeValue = firstText; 
    else element.nodeValue = secondText;
}
// creates an animated border
/*function gfAddAnimatedBorder(parentElement, childElement, borderWidth, image, animation)
{
    var borderElement = gfCreateAndAdd(parentElement, "div");
    borderElement.style.position = "relative";
    borderElement.style.backgroundRepeat = "repeat";
    borderElement.style.backgroundColor = "red";
    
    var imageElement = gfCreateAndAdd(borderElement, "div");
    imageElement.style.backgroundImage = "url("+ image +")";
    imageElement.position = "absolute";
    
    gfAddChild(borderElement, childElement);
    childElement.style.margin = borderWidth;
    //childElement.style.backgroundColor = "white";
    
    animation(imageElement);
}*/