// FreeStyler Library // Copyright 2009 Ian Porter

function fsMorph(parent,newStyle,timePeriod,swooshSpeed)
{
    this.index = this.all.length;
    this.all[this.index] = this;
    this.parent = parent;
    this.newStyle = newStyle;
    this.timePeriod = timePeriod ? timePeriod : 1000;
    this.swooshSpeed = swooshSpeed ? swooshSpeed : 1;
}

fsMorph.prototype.all = new Array();

fsMorph.prototype.isSequenceBusy = function(direction)
{
    if(this.loopId)
        return true;
    if(this.nextInSequence && direction=="fwd" && this.nextInSequence.isSequenceBusy("fwd"))
        return true;
    if(this.previousInSequence && direction=="bwd" && this.previousInSequence.isSequenceBusy("bwd"))
        return true;
    return false;
}

fsMorph.prototype.sequenceAfter = function(preceedingMorph)
{
    this.previousInSequence = preceedingMorph;
    preceedingMorph.nextInSequence = this;
}

fsMorph.prototype.init = function()
{
    if(this.style)
        return true;

    // parent can be css rule name or element id
    this.style = fsGetStyle(this.parent);
    if(!this.style)
        this.style = document.getElementById(this.parent).style;
    if(!this.style)
        return false;   // unable to find parent

    // new style can be css rule name or css text
    var newStyleRule = fsGetStyle(this.newStyle);
    var cssText = newStyleRule ? newStyleRule.cssText : this.newStyle;    

    var styles = cssText.split(";");
    this.styleVars = new Array();
    for(var i in styles)
    {
        var bits = styles[i].split(":");
        if(bits.length==2)
        {
            this.styleVars[i] = new fsStyleVar(this.style,bits[0],bits[1]);
            this.styleVars[i].init();  // set start values
            // note : if new style is css text and parent is element then styles should be declared inline on the element
        }
    }

    this.linear = 0;
    
    return true;
}

fsMorph.prototype.go = function(doSequence, forceInterrupt)
{
    if(!this.init())
        return;

    // stop any interrupted sequences
    //if(this.isSequenceBusy() && forceInterrupt)
    //   clearTimeout(this.loopId);// on all in sequence!

    // start the sequence if not busy or if interrupting 
    if( (!this.isSequenceBusy() && this.linear<1.0) || forceInterrupt )
    {
        this.isMorphBusy = true;
        this.doSequence = doSequence;        
       
        this.startTime = new Date().getTime();
        this.endTime = this.startTime + this.timePeriod;
        
        this.step();
    }
   
    // if parent busy then line up to happen afterwards if so desired
    //else
    //{
    //   this.oneOffFollowOn = 
    //}
}

fsMorph.prototype.goBack = function(doSequence,forceInterrupt)
{
    if(!this.init())
        return;

    if( (!this.isSequenceBusy() && this.linear>0.0) || forceInterrupt )
    {
        this.isMorphBusy = true;
        this.doSequence = doSequence;
       
        this.startTime = new Date().getTime();
        this.endTime = this.startTime + this.timePeriod;
        
        this.stepBack();
    }
}

fsMorph.prototype.step = function()
{
    // calculate where we are in the loop
    var currentTime = new Date().getTime();
    this.linear = this.timePeriod<=0 ? 1 : Math.min( 1, (currentTime-this.startTime)/this.timePeriod );
    var swoosh = Math.pow( ( 1 - Math.cos(Math.PI*this.linear) ) / 2, this.swooshSpeed);

    // apply new values
    if(this.styleVars!=null)
        for (var i in this.styleVars)
            this.styleVars[i].step(swoosh);
    
    // loop
    if(this.linear < 1.0)
    {    
        this.loopId = setTimeout( "fsMorph.prototype.all["+this.index+"].step();" , 10 ); //100Hz
        return;
    }

    // finished loop 
    clearTimeout(this.loopId);
    this.loopId = null;
    this.linear = 1.0;

    //continue sequence
    if(this.nextInSequence && this.doSequence)
        this.nextInSequence.go(this.doSequence);
}

fsMorph.prototype.stepBack = function()
{
    // calculate where we are in the loop
    var currentTime = new Date().getTime();
    this.linear = this.timePeriod<=0 ? 0 : Math.max( 0, (this.endTime-currentTime)/this.timePeriod );
    var swoosh = Math.pow( ( 1 - Math.cos(Math.PI*this.linear) ) / 2, this.swooshSpeed);

    // apply new values
    if(this.styleVars!=null)
        for (var i in this.styleVars)
            this.styleVars[i].step(swoosh);
    
    // loop
    if(this.linear > 0.0)
    {    
        this.loopId = setTimeout( "fsMorph.prototype.all["+this.index+"].stepBack();" , 10 ); //100Hz
        return;
    }

    // finished loop 
    clearTimeout(this.loopId);
    this.loopId = null;
    this.linear = 0.0;

    // continue sequence
    if(this.previousInSequence && this.doSequence)
        this.previousInSequence.goBack(this.doSequence);
}

function fsGetStyle(ruleName)
{
    for (var i=0; i<document.styleSheets.length; i++)
    {
        var sheet = document.styleSheets[i];
        var rules = sheet.cssRules ? sheet.cssRules : sheet.rules;
        for (var j=0; j<rules.length; j++)
            if(rules[j].selectorText && rules[j].selectorText==ruleName)
                return rules[j].style;      // returns first one found
    }
    return null;
}