var Runners = [];

var WhyEffects = {
  Runners: [],
  
  RunEffect: function(el, effect, obj){
    if(!Effects[effect] && typeof(effect) !== "object") return;
    if(typeof(el) !== "object"){
      if(el[0] == "#"){
        if(!(el = document.getElementById(el.substring(1,el.length)))) return;
      }else return;
    }
    if(typeof(obj) !== "object") obj = {};
    if(typeof(effect) !== "object") effect = new Effects[effect]();
    
    el.effect = effect;
    el.effect.el = el;
    this.LoadConf(el.effect,obj,'speed',1000);
    this.LoadConf(el.effect,obj,'f',"square");
    this.LoadConf(el.effect,obj,'callback',null);
    if(typeof(el.effect.conf) === 'object')
      for(var i in el.effect.conf) this.LoadConf(el.effect,obj,i,el.effect.conf[i]);
    el.effect.f = this.DetermineF(el.effect.f);

    if(typeof(el.effect.Pre) === "function") el.effect.Pre();

    el.effect.last = Number(new Date());
    el.effect.start = Number(new Date());
    this.AddRunner(el);
  },
  
  AddRunner: function(el){
    for(var i=0; i<this.Runners.length; i++){
      if(this.Runners[i] === el) this.RemoveRunner(el);
    }
    this.Runners.push(el);
    if(this.Runners.length == 1) requestAnimFrame(this.RunCallback);
  },
  
  RemoveRunner: function(el){
    for(var i=0; i<this.Runners.length; i++){
      if(this.Runners[i] === el){
        this.Runners.splice(i,1);
        break;
      }
    }
  },

  RunCallback: function(){
    WhyEffects.RunEffects();
  },
  
  RunEffects: function(){
    var el;
    var time = Number(new Date()), t, tt;
    for(var i=0;i<this.Runners.length; i++){
      el = this.Runners[i];
      t = time-el.effect.start;
      tt = time-el.effect.last;
      el.effect.ratio = t<el.effect.speed ? Math.min(el.effect.f(t/el.effect.speed), 1) : 1;
      el.effect.Run(t, tt);
      el.effect.last = time;
      this.StopEffect(el, false);
    }
    if(this.Runners.length) requestAnimFrame(this.RunCallback);
  },

  StopEffect: function(el){
    if(el.effect.ratio >= 1){
      var callback;
      this.RemoveRunner(el);
      if(typeof(el.effect.Post) === "function") el.effect.Post();
      if(el.effect.callback) callback = el.effect.callback;
      el.effect = undefined;
      if(typeof(callback) == 'function') callback(el);
    }
  },

  LoadConf: function(el, conf, key, val){
    if(typeof(conf[key]) !== 'undefined') el[key] = conf[key];
    else el[key] = val;
  },
  
  DetermineF: function(f){
    if(typeof(f) == "function") return f;
    
    switch(f){
      case "linear":
        return function(t){ return t };
        break;
      case "smooth":
        return function(r){return 0.5-Math.cos(Math.PI*r)/2;};
        break;
      case "square":
      default:
        return function(t){ return t*t; };
        break;
    }
  }
}

function RunEffect(el, effect, obj){
  WhyEffects.RunEffect(el, effect, obj);
}

function changeOpac(obj,opacity){
  if(!obj || !obj.style) return;
  obj.style.opacity=(opacity/100);
  obj.style.MozOpacity=(opacity/100);
  obj.style.KhtmlOpacity=(opacity/100);
  obj.style.filter="alpha(opacity="+opacity+");";
}

window.requestAnimFrame = (function() {
  return window.requestAnimationFrame ||
         window.webkitRequestAnimationFrame ||
         window.mozRequestAnimationFrame ||
         window.oRequestAnimationFrame ||
         window.msRequestAnimationFrame ||
         function(callback, element) {
           window.setTimeout(callback, 1000/60);
         };
})();

var Effects = {
  "fadeIn": function(){
    this.conf = {"min": 0, "max": 100, "f": "linear"},
    this.Pre = function(){
      if(!this.el.style.opacity) changeOpac(this.el,0);
    },
    this.Run = function(t,tt){
      var opac = Math.round(Math.min(this.el.style.opacity*100+tt*(this.max-this.min)/this.speed,this.max));
      changeOpac(this.el, opac);
    };
  },
  "fadeOut": function(){
    this.conf = {"min": 0, "max": 100, "f": "linear"},
    this.Pre = function(){
      if(!this.el.style.opacity) changeOpac(this.el,100);
    },
    this.Run = function(t,tt){
      var opac = Math.round(Math.max(this.el.style.opacity*100-tt*(this.max-this.min)/this.speed,this.min));
      changeOpac(this.el, opac);
    };
  },
  "moveOut": function(){
    this.conf = {"side": "down"},
    this.Pre = function(){
      this.width = this.el.offsetWidth;
      this.height = this.el.offsetHeight;
      this.el.style.position = 'relative';
      this.el.style.width = this.width+'px';
      this.el.style.height = this.height+'px';
      this.el.style.overflow = 'hidden';
      this.to = document.createElement('div');
      this.to.style.width = this.width+'px';
      this.to.style.height = this.height+'px';
      this.to.style.position = 'absolute';
      this.to.style.top = '0px';
      this.to.style.left = '0px';
      var el;
      while(this.el.firstChild && (el = this.el.removeChild(this.el.firstChild))) this.to.appendChild(el);
      this.el.appendChild(this.to);
    },
    this.Run = function(t,tt){
      var top = 0, left = 0;
      switch(this.side){
        case "down":
          top = this.height*this.ratio;
          break;
        case "up":
          top = -this.height*this.ratio;
          break;
        case "left":
          left = -this.width*this.ratio;
          break;
        case "right":
          left = this.width*this.ratio;
          break;
      }
      this.to.style.top = top+'px';
      this.to.style.left = left+'px';
    },
    this.Post = function(){
      this.el.removeChild(this.to);
    };
  },
  "moveIn": function(){
    this.conf = {"side": "up","html": '', "dom": null},
    this.Pre = function(){
      this.width = this.el.offsetWidth;
      this.height = this.el.offsetHeight;
      this.el.style.position = 'relative';
      this.el.style.width = this.width+'px';
      this.el.style.height = this.height+'px';
      this.el.style.overflow = 'hidden';
      if(this.dom){
        this.to = this.dom;
      }else{
        this.to = document.createElement('div');
        this.to.innerHTML = this.html;
      }
      this.to.style.visibility = '';
      this.to.style.width = this.width+'px';
      this.to.style.height = this.height+'px';
      this.to.style.position = 'absolute';
      var top = 0, left = 0;
      switch(this.side){
        case "down":
          top = this.height;
          break;
        case "up":
          top = -this.height;
          break;
        case "left":
          left = -this.width;
          break;
        case "right":
          left = this.width;
          break;
      }
      this.to.style.top = top+'px';
      this.to.style.left = left+'px';
      while(this.el.firstChild) this.el.removeChild(this.el.firstChild);
      this.el.appendChild(this.to);
    },
    this.Run = function(t,tt){
      var top = 0, left = 0;
      switch(this.side){
        case "down":
          top = this.height*(1-this.ratio);
          break;
        case "up":
          top = -this.height*(1-this.ratio);
          break;
        case "left":
          left = -this.width*(1-this.ratio);
          break;
        case "right":
          left = this.width*(1-this.ratio);
          break;
      }
      this.to.style.top = top+'px';
      this.to.style.left = left+'px';
    },
    this.Post = function(){
      var el;
      while(this.to.firstChild && (el = this.to.removeChild(this.to.firstChild))) this.el.appendChild(el);
      this.el.removeChild(this.to);
    };
  },
  "roll": function(){
    this.conf = {"side": "top", "from": 0, "to": 240, "padding": null},
    this.Pre = function(){
      var result = this.from;
      switch(this.side){
        case "top":
        case "bottom":
          this.el.style.overflowY = 'hidden';
          if(this.padding instanceof Array){
            if(result <= this.padding[0]){
              this.el.style.paddingTop = result+'px';
              this.el.style.height = '0px';
              this.el.style.paddingBottom = '0px';
            }else if((this.to > this.from && result <= this.to-this.padding[2]) || (this.to < this.from && result >= this.padding[0])){
              this.el.style.paddingTop = this.padding[0]+'px';
              this.el.style.height = (result-this.padding[0])+'px';
              this.el.style.paddingBottom = '0px';
            }else{
              this.el.style.paddingTop = this.padding[0]+'px';
              this.el.style.height = (this.to-this.padding[0]-this.padding[2])+'px';
              this.el.style.paddingBottom = (this.padding[2] - Math.min(Math.abs(this.to-result),Math.abs(this.from-result)))+'px';
            }
          }else{
            this.el.style.height = result+'px';
          }
          break;
        case "left":
        case "right":
          this.el.style.overflowX = 'hidden';
          if(this.padding instanceof Array){
            if(result <= this.padding[4]){
              this.el.style.paddingLeft = result+'px';
              this.el.style.width = '0px';
              this.el.style.paddingRight = '0px';
            }else if((this.to > this.from && result <= this.to-this.padding[1]) || (this.to < this.from && result >= this.padding[4])){
              this.el.style.paddingLeft = this.padding[4]+'px';
              this.el.style.width = (result-this.padding[4])+'px';
              this.el.style.paddingRight = '0px';
            }else{
              this.el.style.paddingLeft = this.padding[4]+'px';
              this.el.style.width = (this.to-this.padding[4]-this.padding[1])+'px';
              this.el.style.paddingright = (result - this.to + this.padding[1])+'px';
            }
          }else{
            this.el.style.width = result+'px';
          }
          break;
      }
      if(this.el.style.display == 'none') this.el.style.display = '';
    },
    this.Run = function(t,tt){
      var top = 0, left = 0;
      var result = Math.round(this.from+(this.to-this.from)*this.ratio);
      switch(this.side){
        case "top":
        case "bottom":
          if(this.padding instanceof Array){
            if(result <= this.padding[0]){
              this.el.style.paddingTop = result+'px';
            }else if((this.to > this.from && result <= this.to-this.padding[2]) || (this.to < this.from && result >= this.padding[0])){
              this.el.style.paddingTop = this.padding[0]+'px';
              this.el.style.height = (result-this.padding[0])+'px';
            }else{
              this.el.style.paddingTop = this.padding[0]+'px';
              this.el.style.height = (this.to-this.padding[0]-this.padding[2])+'px';
              this.el.style.paddingBottom = (this.padding[2] - Math.min(Math.abs(this.to-result),Math.abs(this.from-result)))+'px';
            }
          }else{
            this.el.style.height = result+'px';
          }
          break;
        case "left":
        case "right":
          if(this.padding instanceof Array){
            if(result <= this.padding[4]){
              this.el.style.paddingLeft = result+'px';
            }else if((this.to > this.from && result <= this.to-this.padding[1]) || (this.to < this.from && result >= this.padding[4])){
              this.el.style.paddingLeft = this.padding[4]+'px';
              this.el.style.width = (result-this.padding[4])+'px';
            }else{
              this.el.style.paddingLeft = this.padding[4]+'px';
              this.el.style.width = (this.to-this.padding[4]-this.padding[1])+'px';
              this.el.style.paddingright = (this.padding[1] - Math.min(Math.abs(this.to-result),Math.abs(this.from-result)))+'px';
            }
          }else{
            this.el.style.width = result+'px';
          }
          break;
      }
    },
    this.Post = function(){
      this.el.style.padding = '';
      this.el.style.width = '';
      this.el.style.height = '';
      this.el.style.overflow = '';
    };
  },
  "scroll": function(){
    this.conf = {"f": "smooth", "to": 'formdiv'},
    this.Pre = function(){
      if(typeof( window.innerHeight ) == 'number'){
        this.winHeight = window.innerHeight;
      }else if(document.documentElement && document.documentElement.clientHeight){
        this.winHeight = document.documentElement.clientHeight;
      }
      var el;
      if(typeof(this.to) != 'object')
        el = document.getElementById(this.to);
      else
        el = this.to;
      var hei = el.offsetHeight;
      var fromtop = 0;
      do{
        fromtop += el.offsetTop;
      }while(el = el.offsetParent);
      fromtop -= Math.max(0,(this.winHeight-hei)/2);
      this.scrollHeight = fromtop > document.body.scrollHeight ? document.body.scrollHeight : fromtop;
      if(typeof(window.pageYOffset) == 'number'){
        this.scrollY = window.pageYOffset;
        this.scrollX = window.pageXOffset;
      }else if(document.body && document.body.scrollTop){
        this.scrollY = document.body.scrollTop;
        this.scrollX = document.body.scrollLeft;
      }else if(document.documentElement && document.documentElement.scrollTop){
        this.scrollY = document.documentElement.scrollTop;
        this.scrollX = document.documentElement.scrollLeft;
      }
      this.nowY = this.scrollY;
    },
    this.Run = function(t,tt){
      this.nowY = Math.round(this.scrollY+(this.scrollHeight-this.scrollY)*this.ratio);
      window.scrollTo(this.scrollX,this.nowY);
    };
  }
};
