﻿/// <reference path="global.js" />
/// <reference path="validation.js" />

$effect =
 {
     Info: new Array(), // Массив текущих анимаций
     GetInfo: function (obj, formal) // Получает текущую анимацию по объекту, к которому она применятеся
     {
         var result = null;
         for (var i = 0; i < this.Info.length; i++) {
             if (obj == this.Info[i].obj && (this.Info[i].formal != null && this.Info[i].formal == formal)) {
                 result = this.Info[i];
                 break;
             }
         }
         return result;
     },
     ClearInfo: function (obj, formal) // Удаляет анимацию привязаную в заданому объекту
     {
         var arr = new Array();
         for (var i = 0; i < this.Info.length; i++)
             if (obj != this.Info[i].obj || !(this.Info[i].formal != null && this.Info[i].formal == formal))
                 arr.push(this.Info[i]);
         this.Info = arr;
     },
     Timer: null, // -- проверить необходимость - не вижу
     AnimationTime: 1500, // Длительность анимации (в милисекундах)
     AnimationInterval: 10, // Интервал в милисекундах между итерациями анимационного процесса
     AnimationType: "sin", // Тип изменения скорости анимации. Возможные значения: "sin", "cos"
     Default: function () // Сброс параметров в значения по умолчанию
     {
         this.Timer = null;
         this.AnimationTime = 1500;
         this.AnimationInterval = 10;
         this.AnimationType = "sin";
     },
     Time: function (_time) // Задает время Анимации и возвращает управляющий объект (для конструкций типа $effect.Time(1000).FadeIn(this))
     {
         this.AnimationTime = _time;
         return this;
     },
     Interval: function (_interval) // Задает интервал Анимации и возвращает управляющий объект (аналогично .Time().)
     {
         this.AnimationInterval = _interval;
         return this;
     },
     Type: function (_type) // Задает Тип Анимации и возвращает управляющий объект (аналогично .Time().)
     {
         this.AnimationType = _type;
         return this;
     },
     FadeIn: function (obj, end, formal) // Постепенное проявление объекта (из прозрачности)
     {
         if (obj) {
             this.AnimateOpacity(obj, 100, 0, end, formal);
         }
     },
     FadeOut: function (obj, end, formal) // Постепенное исчезновение объекта (прозрачность)
     {
         if (obj) {
             this.AnimateOpacity(obj, -100, 100, end, formal);
         }
     },
     //     Scroll: function(obj, disp, bord, onEnd, onRun, formal)
     //     {
     //         var border = bord == null ? null : bord;
     //         var x0 = 0;
     //         var a = x0 + disp;
     //         var displace = border != null ? (disp > 0 ? a < border : a > border) ? disp : (x0 - border) * (-1) : disp;
     //         var time = Math.abs(Math.round(displace * $effect.AnimationTime / disp));
     //         var info = this.GetInfo(obj, formal);
     //         if (info != null)
     //         {
     //             info.Stop();
     //         }
     //         var run = function(f)
     //         {
     //             obj.scrollBy(0, Math.round(f * displace - x0));
     //             var x0 = Math.round(f * displace);
     //             if (onRun)
     //                 onRun();
     //         }
     //         var end = function()
     //         {
     //             obj.scrollBy(0, Math.round(displace - x0));
     //             $effect.Default();
     //             if (onEnd)
     //                 onEnd(obj);
     //         }
     //         this.Animate(obj, time, null, run, end, formal);
     //     },
     MorphColors: function (obj, start, end, style, onEnd, onRun, formal) {
         if (obj) {
             var _up = 16777215;
             var _down = 0;
             var start = start ? start : $G.Browser.Detect.gecko ? eval($G.GetStyle(obj, style)) : $G.GetStyle(obj, style);
             var _c0 = $G.Number.toDecRGB(start ? start : $G.GetStyle(obj, style));
             var _c1 = $G.Number.toDecRGB(end);
             var _cd = $G.Color.Minus(_c1, _c0);
             var time = $effect.AnimationTime;
             var info = this.GetInfo(obj, formal);
             if (info != null)
                 info.Stop();
             var run = function (f) {
                 var _n = $G.Color.Plus($G.Color.Mult(_cd, f), _c0);
                 $G.SetStyle($global.NormCssStr(style), $G.Color.fromRGB(_n), obj);
                 if (onRun)
                     onRun();
             }
             var end = function () {
                 var _n = $G.Color.Plus(_cd, _c0);
                 $G.SetStyle($global.NormCssStr(style), $G.Color.fromRGB(_n), obj);
                 $effect.Default();
                 if (onEnd)
                     onEnd(obj);
             }
             this.Animate(obj, time, null, run, end, formal);
         }
     },
     AnimateOpacity: function (obj, disp, start, onEnd, formal) // Анимирует изменение непрозрачности
     {
         var border = disp < 0 ? 0 : 100;
         var x0 = start;
         var a = x0 + disp;
         var displace = border != null ? (disp > 0 ? a < border : a > border) ? disp : (x0 - border) * (-1) : disp;
         var time = Math.abs(Math.round(displace * $effect.AnimationTime / disp));
         var info = this.GetInfo(obj, formal);
         if (info != null)
             info.Stop();

         var run = function (f) {
             $global.SetOpacity(obj, Math.round(f * displace + x0) / 100);
         }
         var end = function () {
             $global.SetOpacity(obj, Math.round(displace + x0) / 100);
             $effect.Default();
             if (onEnd)
                 onEnd(obj);
         }
         this.Animate(obj, time, null, run, end, formal);
     },
     Page:
    {
        InitFader: function (color, op, z) {
            var fader = $G.Get.ById("A2-Page-Fader");
            var cm = $G.GET("div:0");
            if (!fader) {
                fader = $G.Append(document.body, $G.Tag("div", { id: "A2-Page-Fader" }), document.body.firstChild, true);
                if (op != null)
                    $G.Opacity.Set(fader, op);
                fader.style.top = "0px";
                fader.style.left = "0px";
                fader.style.zIndex = (z || 1).toString();
            }
            fader.style.position = "absolute";
            var winX = $G.Window.Width();
            var winY = $G.Window.Height();
            if (document.body.offsetHeight > cm.offsetHeight) {
                cm = document.body;
            }
            fader.style.width = (document.body.offsetWidth > (winX - 17) ? document.body.offsetWidth : winX-($G.Browser.Detect.gecko ? 17 : 0)) + "px"; // -17 потому что скрол в фф
            fader.style.height = (cm.offsetHeight > winY ? cm.offsetHeight : winY) + "px";
            if (color)
                fader.style.backgroundColor = color;
            return fader;
        },
        FadeIn: function (color, max, onEnd, z) {
            var fader = $effect.Page.InitFader(color, 0.0, z);
            var cOpacity = $G.Opacity.Get(fader);
            $effect.AnimateOpacity(fader, (max - cOpacity), cOpacity, onEnd, "A2-Page-Fading");
            return $effect;
        },
        FadeOut: function (color, onEnd) {
            var fader = $effect.Page.InitFader(color)
            $effect.AnimateOpacity(fader, -100, $G.Opacity.Get(fader), function (obj) { if (onEnd) { onEnd(obj); }; $FX.Page.CleanUp(); }, "A2-Page-Fading");
            return $effect;
        },
        CleanUp: function () {
            document.body.removeChild($G.Get.ById("A2-Page-Fader"));
        }
    },
     AnimateStyle: function (obj, style, disp, bord, onEnd, onRun, formal) // Анимирует изменение параметра стиля заданого в style
     {
         var border = bord == null ? null : bord;
         var x0 = $global.GetStyle(obj, style, true);
         var a = x0 + disp;
         var displace = border != null ? (disp > 0 ? a < border : a > border) ? disp : (x0 - border) * (-1) : disp;
         var time = Math.abs(Math.round(displace * $effect.AnimationTime / disp));
         var info = this.GetInfo(obj, formal);
         if (info != null) {
             info.Stop();
         }
         var run = function (f) {
             $global.SetStyle($global.NormCssStr(style), Math.round(f * displace + x0) + "px", obj);
             if (onRun)
                 onRun(Math.round(f * displace + x0));
         }
         var end = function () {
             $global.SetStyle($global.NormCssStr(style), (x0 + displace) + "px", obj);
             $effect.Default();
             if (onEnd)
                 onEnd(obj);
         }
         this.Animate(obj, time, null, run, end, formal);
     },
     SlideH: function (obj, disp, bord, onEnd, onRun, formal) // Перемещение объекта по горизонтали
     {
         this.AnimateStyle(obj, "left", disp, bord, onEnd, onRun, formal);
     },
     SlideV: function (obj, disp, bord, onEnd, onRun, formal) // Перемещение объекта по вертикали
     {
         this.AnimateStyle(obj, "top", disp, bord, onEnd);
     },
     SlideToPoint: function (obj, x, y, formal) {
         var info = this.GetInfo(obj, formal);
         if (info != null)
             info.Stop();

         var pos = $global.FindPosition(obj);
         var x0 = Number(pos[0]);
         var y0 = Number(pos[1]);
         var dispX = x - x0;
         var dispY = y - y0;
         var start = function () {
         }
         var run = function (f) {
             obj.style.top = Math.round(f * dispY + y0) + "px";
             obj.style.left = Math.round(f * dispX + x0) + "px";
         }
         var end = function () {
             obj.style.top = y + "px";
             obj.style.left = x + "px";
             $effect.Default();
         }
         this.Animate(obj, start, run, end);
     },
     // Необходим для изменения ускорения согласно выбранному типу
     // type - варианты "sin" : sin(value), "cos" : cos(value)
     TypedChange: function (value, type) {
         if (type == "sin")
             return Math.sin(value);
         else if (type == "cos")
             return Math.cos(value);
         else
             return value;
     },
     // Функция - движок, изменяет параметры в течение времени (плавно)
     //           obj - объект анимации
     //    uTotalTime - общее время анимации
     //     fnOnStart - функция выполняющаяся до начала анимации
     //       fnOnRun - функция выполняющаяся на каждом шаге анимации func(f), f - ускорение?!
     //       fnOnEnd - функция выполняющаяся после завершения анимации
     Animate: function (obj, uTotalTime, fnOnStart, fnOnRun, fnOnEnd, formal) {
         if (fnOnStart) { fnOnStart(); }
         var freq = Math.PI / (2 * uTotalTime); // частота
         var startTime = new Date().getTime();
         var info = null;
         var type = this.AnimationType;
         var loop = function () {
             var elapsedTime = new Date().getTime() - startTime;
             if (elapsedTime < uTotalTime) {
                 var f = Math.abs($effect.TypedChange(elapsedTime * freq, type));
                 if (fnOnRun) { fnOnRun(f); }
             }
             else {
                 if (fnOnEnd) { info.Stop(); fnOnEnd(); }
             }
         }
         var tmr = setInterval(loop, $effect.AnimationInterval);
         info = new AnimInfo(tmr, obj, formal);
         $effect.Info.push(info);
     },
     Reflection:
    {
        Add: function (obj, height, opacity) {
            if (obj) {
                var refHeight = Math.floor(obj.offsetHeight * height);
                var refWidth = obj.offsetWidth;
                var divHeight = Math.floor(obj.offsetHeight * (1 + height));
                var wrap = document.createElement("div");

                wrap.className = obj.className;
                wrap.style.cssText = obj.style.cssText;

                $global.SetStyle("width", refWidth + "px", wrap);
                $global.SetStyle("height", divHeight + "px", wrap);
                $global.SetStyle($global.NormCssStr("vertical-align"), "bottom", wrap);
                $global.SetStyle($global.NormCssStr("overflow"), "hidden", wrap);

                var reflect = null;

                if (document.all && !window.opera) {
                    reflect = document.createElement("img");
                    reflect.src = obj.src;
                    $global.SetStyle("width", refWidth + "px", reflect);
                    $global.SetStyle($global.NormCssStr("margin-bottom"), "-" + (obj.offsetHeight - refHeight) + "px", reflect);
                    reflect.style.filter = 'flipv Alpha(opacity=' + (opacity * 100) + ', style=1, finishOpacity=0, startx=0, starty=0, finishx=0, finishy=' + (height * 100) + ')';
                }
                else {
                    reflect = document.createElement("canvas");
                    var context = reflect.getContext("2d");

                    $global.SetStyle("height", refHeight + "px", reflect);
                    $global.SetStyle("width", refWidth + "px", reflect);

                    reflect.width = refWidth;
                    reflect.height = refHeight;

                    // append ?
                    context.save();

                    context.translate(0, obj.offsetHeight - 1);
                    context.scale(1, -1);

                    context.drawImage(obj, 0, 0, refWidth, obj.offsetHeight);

                    context.restore();

                    context.globalCompositeOperation = "destination-out";
                    var gradient = context.createLinearGradient(0, 0, 0, refHeight);

                    gradient.addColorStop(1, "rgba(255, 255, 255, 1.0)");
                    gradient.addColorStop(0, "rgba(255, 255, 255, " + (1 - opacity) + ")");

                    context.fillStyle = gradient;
                    if (navigator.appVersion.indexOf('WebKit') != -1) {
                        context.fill();
                    } else {
                        context.fillRect(0, 0, refWidth, refHeight * 2);
                    }
                }

                obj.className = "reflected";
                obj.style.cssText = "";

                obj.parentNode.replaceChild(wrap, obj);
                wrap.appendChild(obj);
                wrap.appendChild(reflect);
            }
        },
        Remove: function (obj) // Несовершенен...
        {
            if (obj.className == "reflected") {
                obj.className = obj.parentNode.className;
                obj.parentNode.style.height = obj.style.height;
                obj.parentNode.style.width = obj.style.width;
                obj.style.cssText = obj.parentNode.style.cssText;
                obj.parentNode.parentNode.replaceChild(obj, obj.parentNode);
            }
        }
    }
 };

$FX = $effect; //Алиас на $effect

function AnimInfo(interval, obj, formal)
{
    this.obj = obj;
    this.Interval = interval;
    this.formal = formal;
    this.Stop = function()
    {
        clearInterval(this.Interval);
        $effect.ClearInfo(this.obj, this.formal);
    }
}

function MultyAnimInfo()
{
    this.obj = null;

}
