/*============================- Animation queue -==========================*/	
AnimationQueue.prototype.settings={	
  frameDelay: 24	
};	
/**	
* Анимационный поток, представляет собой цепочку анимационных функций,	
* запускаеммх с заданной частотой. Можно представить как процессор и тактовый	
* генератор =)	
*/	
function AnimationQueue() {	
  this.anims=[];	
  this.currentAnim=0;	
  this.timer=null;	
  this.waittime=null;	
  this.fromTime=null;	
  this.loop=true;	
}	
/**	
* Установить повторение анимации после завершения.	
* По теории этого метода не должно быть. Выбираеться дефолтовое действие:	
* повторять(у нас так) либо не повторять. Затем если нужно выполнить действие	
* не равное дефолтовому, то добавляеться обьект "анимация" в ряд, что и выполнит	
* необходимое действие.	
* НО! на практике чем меньше телодвижений, тем лучше =)	
* @param boolean loop true - поставить повтор, false - отменить повтор	
*/	
AnimationQueue.prototype.setLoop=function(loop) {	
  this.loop=Boolean(loop);	
}	
/**	
* Установить паузу для следующего "кадра".	
* Метод необходим анимациям, работающих гораздо медленней чем "анимационный	
* поток". Время задаёться в миллисекундах, но меньше чем шаг аниматора быть не	
* может. Чем меньше время, тем мнее точней будет результирующая пауза.	
* @param integer ms пауза в миллисекундах	
*/	
AnimationQueue.prototype.wait=function(ms) {	
  if(typeof(ms)!='number') throw "AnimationQueue.wait: wait time '"+ms+"' must be an integer representing time in milliseconds";	
  this.waittime=ms;	
  this.fromTime=(new Date()).getTime();	
}	
/**	
* Следующая анимация.	
* Методы вызываеться анимациями, когда текущая полностью завершиться, что бы	
* отдать управлениие следующей анимации.	
*/	
AnimationQueue.prototype.next=function() {	
  if(!this.loop && (++this.currentAnim)>=this.anims.length) this.stop(); 	
  else if(this.loop) this.currentAnim=(++this.currentAnim)>=this.anims.length? 0: this.currentAnim;	
  this.waittime=null;	
}	
/**	
* Добавить анимацию в "анимационный поток".	
* Метод добавляет анимации в конец цепочки. Можно добавлять новые анимации после	
* вызова start, но это может порождать труднонаходимые глюки =)	
* Поэтому набиваем поток анимациями заранее, затем вызываем start() и забываем.	
* @param function animation функция аниматор, что будет вызываться каждый проход	
*   аниматора. Параметром получает ссылку на обьект аниматора.	
*/	
AnimationQueue.prototype.add=function(animation) {	
  if(!(animation instanceof Function)) throw "AnimationQueue.add: given animation object '"+animation+"' is not a function!";	
  this.anims[this.anims.length]=animation;	
}	
/**	
* Запустить анимационный поток.	
* Выполнение продолжаеться с момента остановки. Параметром можно указать с какой	
* анимации начинать выполнение, отсчёт от 0	
* @param integer порядковый номер стартовой анимации с 0	
*/	
AnimationQueue.prototype.start=function(initAnim) {	
  if(this.timer!=null||this.anims.length<1) return;	
  if(typeof(initAnim)=='number') this.currentAnim=initAnim;	
  this.currentAnim=this.currentAnim<0? 0: this.currentAnim>=this.anims.length? this.anims.length-1: this.currentAnim;	
  var animator=this;	
  this.timer=window.setInterval(function() {	
     if(animator.waittime!=null) {	
       if(((new Date()).getTime()-animator.fromTime)<animator.waittime) return;	
       animator.waittime=null;	
     }	
     animator.anims[animator.currentAnim](animator);	
  }, this.settings.frameDelay);	
}	
/**	
* Остановить анимационный поток.	
* Все анимации сохраняют своё состояние! Проще говоря останавливаеться главный	
* таймер, функции-анимации просто перестают регулярно вызываться. При следующем	
* вызове start() выполнение продолжиться со состояния на момент stop().	
*/	
AnimationQueue.prototype.stop=function() {	
  window.clearInterval(this.timer);	
}	
/*===============================- Animation Delay -=======================*/	
/**	
* Анимация - пауза.	
* Этот обьект-обёртка понадобился для работы с паузами в пакетах	
* анимаций(см. ниже). Представляеться как обычная анимационная функция.	
* Передаёт управление по прошествии определённого интервала времени.	
* Пауза срабатывает только один раз, для повторяющихся пауз нужно задать третьим	
* параметром true.	
* Вызов setDelay сбрасывает текущую паузу и устанавливает новую.	
* @param function anim анимационная функция	
* @param interger msc задержка в миллисекундах	
* @param boolean continous true - разрешить постоянную паузу, false(default) - отменить постоянную паузу	
* @return function результирующая анимационная функция	
*/	
function AnimationDelay(anim, msc, continous) {	
   var a=function(animator) {	
     if(arguments.callee.waittime!=null) {	
       if(((new Date()).getTime()-arguments.callee.fromTime)<arguments.callee.waittime) return;	
       if(continous) arguments.callee.setDelay(msc);	
       else arguments.callee.waittime=null;	
     }	
     anim(animator);	
   }	
   a.setDelay=function(ms) {	
     if(typeof(ms)!='number') throw "AnimationDelay.setDelay: wait time '"+ms+"' must be an integer representing time in milliseconds";	
     this.waittime=ms;	
     this.fromTime=(new Date()).getTime();   	
   }	
   a.setDelay(msc);	
}	
/*==============================- Animation Set -==========================*/	
/**	
* Обьединение анимаций по времени.	
* Иногда(часто =)) требуеться запустить несколько анимаций одновременно. Обьект	
* обьединяет анимации в одну. Выход из обьединения произойдёт после того как все	
* анимации вызовут либо next(), либо stop(). Если анимация вызвала	
* next()/stop(), то она удаляеться из списка анимаций и при следующем проходе	
* вызванна не будет.	
* @param array масив с функциями-анимациями, что будут выполняться одновременно	
* @return function результирующая анимационная функция	
*/	
function AnimationSet(set) {	
  return function(anim) {	
     var animator={}, hasn=false;	
     for(var i=0; i<set.length; i++) {	
        if(!set[i]) {	
          if(i<set.length-1||hasn) continue;	
          else {anim.next(); return;}	
        }	
        hasn=true;	
        animator.next=animator.stop=function() { set[i]=null; }	
        animator.wait=function(ms) {	
          if(!set[i].setDelay) set[i]=AnimationDelay(set[i], ms);	
          else set[i].setDelay(ms);	
        }	
        set[i](animator);	
     }	
  }	
}	
/*=========================- Анимационные функции -=======================*/	
/**	
* Простая анимация создающая задержку в миллисекундах.	
* @param integer ms пауза в миллисекундах	
*/	
function waitAnimation(ms) {	
  var w=false;	
  return function(animator) {	
    if(w) {animator.next(); w=false;}	
    animator.wait(ms);	
    w=true;	
  }	
}	
/**	
* Анимация изменяющая размер обьекта.	
* Изначальный размер обьекта вычисляеться при первом "кадре". Далее эти размеры	
* подгоняються под заданные.	
* Параметрами задаёться целевой обьект, размеры и время, за которое анимация	
* должна выполниться.	
* Внимание: время всегда используеться чуть больше чем заданно, т.к. анимация	
* на сложных обьектах браузера всё таки жутко не эффективная работа =)	
* @param AnimationQueue thread анимационный поток в котором будем исполняться	
* @param integer time длительность анимации	
* @param HTMLElement obj целевой обьект	
* @param integer width требуемая ширина обьекта	
* @param integer height требуемая высота обьекта	
*/	
function resizeAnimation(thread, time, obj, width, height) {	
  var steps=Math.ceil(time/thread.settings.frameDelay);	
  var currstep=1;	
  var owidth, oheight, hstep, wstep;	
  return function(animator) {	
    if(typeof(owidth)!='number') {	
       owidth=obj.offsetWidth;	
       oheight=obj.offsetHeight;	
       wstep=(width-owidth)/steps;	
       hstep=(height-oheight)/steps;	
    }	
    if(currstep>steps) {	
      obj.style.width=width+'px';	
      obj.style.height=height+'px';	
      animator.next();	
      return;	
    }	
    obj.style.width=Math.round(owidth+wstep*currstep)+'px';	
    obj.style.height=Math.round(oheight+hstep*currstep)+'px';	
    currstep++;	
  }	
}	
/**	
* Простая анимация "печатаемого текста".	
* Заданная строка "печатаеться" через innerHTML в целевом обьекте.	
*/	
function typeAnimation(obj, text, timedelay) {	
   var len=0;	
   return function(animator) {	
      animator.wait(timedelay);	
      if(++len>text.length) {len=0; animator.next();}	
      else obj.innerHTML=text.substring(0, len);	
   }	
}	
