function _Animation() {
	this.list_anim = new Object;
	this.count_item = 0;
	this.period = 40;
	this.timer = null;
    var style_color = ['color', 'backgroundColor', 'borderColor'];
    
	this.add = function(elem, params) {
        if (typeof(elem) == 'string') elem = $(elem, true);
        if (elem==null) return false;
        var elem_id = Utils.getId(elem);
        if ( typeof(params) != 'object' ) return this;
        var time = (arguments.length > 2) ? arguments[2] : 0;
        if (time < 0) time = 1;
        var method = (arguments.length > 3) ? arguments[3] : '';
        var callback = (arguments.length > 4) ? arguments[4] : null;
        if (typeof(callback) != 'function') callback = null;
        
        if (this.list_anim[elem_id] == null) {
            this.list_anim[elem_id] = {
                'elem':  elem,
                'keyframes': new Array()
            }
        }
        
        var keyframe = {
            'key':    0,
            'keys':   parseInt(time / this.period),
            'params': params,
            'm':      method,
            'func':   callback
        }
		this.list_anim[elem_id]['keyframes'].push( keyframe );
        
        this.start();
        
        return this;
    }
	
    this.start = function() {
        if (this.timer == null) {
            this.timer = window.setInterval('Animation.anim()', this.period);
        }
    }
    
	this.anim = function() {
		var current  = 0;
        var count = 0;
        
        for(var elem_id in this.list_anim) {
            var elem     = this.list_anim[elem_id]['elem'];
            var key      = this.list_anim[elem_id]['keyframes'][current]['key'];
            
            if (key == 0) {
                // подготавливаем раскадровку
                var keyframe = this.list_anim[elem_id]['keyframes'][current];
                for(var style in keyframe['params']) {
                    var value = keyframe['params'][style];
                    
                    // if colored style
                    if (style == 'opacity') {
                        var v1 = this.getParameter( elem,  style, 'opacity' );
                        var v2 = this.getParameter( value, 'opacity' );
                        this.list_anim[elem_id]['keyframes'][current]['params'][style] = { v1: v1, v2: v2, 'ms': 'opacity' };
                    } else if (this.in_array(style_color, style)) {
                        var v1 = this.getParameter( elem,  style, 'color' );
                        var v2 = this.getParameter( value, 'color' );
                        this.list_anim[elem_id]['keyframes'][current]['params'][style] = { v1: v1, v2: v2, 'ms': 'color' };
                    } else {
                        if (value.match(/(%)/)) {
                            var v1 = this.getParameter( elem, style, '%' );
                            var v2 = this.getParameter( value, '%' );
                            this.list_anim[elem_id]['keyframes'][current]['params'][style] = { v1: v1, v2: v2, 'ms': '%' };
                        } else {
                            var v1 = this.getParameter( elem, style, '' );
                            var v2 = this.getParameter( value, '' );
                            this.list_anim[elem_id]['keyframes'][current]['params'][style] = { v1: v1, v2: v2, 'ms': '' };
                        }
                    }
                }
            }
            
            var keyframe = this.list_anim[elem_id]['keyframes'][current];
            this.list_anim[elem_id]['keyframes'][current]['key']++;
    		
            if ( this.interpolation(elem, keyframe) ) {
                this._stop( elem_id );
    		}
            count++;
        }
        if (count == 0) {
            window.clearInterval(this.timer);
            this.timer = null;
        }
	}
	
    this.flush = function(elem) {
        if (typeof(elem) == 'string') elem = $(elem, true);
        if (elem==null) return false;
        var elem_id = Utils.getId(elem);
        
        if (this.list_anim[elem_id] != null) {
            var keys = this.list_anim[elem_id]['keyframes'][0]['keys'];
            this.list_anim[elem_id]['keyframes'][0]['key'] = keys;
        }
    }
    
    this.stop = function(elem) {
        if (typeof(elem) == 'string') elem = $(elem, true);
        if (elem==null) return false;
        var elem_id = Utils.getId(elem);
        this._stop( elem_id );
    }
    
    this._stop = function(elem_id) {
        if (this.list_anim[elem_id] != null) {
            if (this.list_anim[elem_id]['keyframes'][0]['func'] != null) {
                this.list_anim[elem_id]['keyframes'][0]['func']();
            }
            this.list_anim[elem_id]['keyframes'].shift();
            if (this.list_anim[elem_id]['keyframes'].length == 0) {
                delete this.list_anim[elem_id];
            }
        }
    }
    
	this.interpolation = function(elem, keyframe) {
        var k = keyframe['key'] / keyframe['keys'];
        if (k > 1) k = 1;
        
        var m = keyframe['m'];
        switch(m) {
            case 'slow':
                k = Math.sin( k * Math.PI / 2 );
            break;
            case 'fast':
                k = 1 + Math.sin( (k * Math.PI / 2) - (Math.PI / 2) );
            break;
        }
        
        for(var style in keyframe['params']) {
            var v1 = keyframe['params'][style]['v1'];
            var v2 = keyframe['params'][style]['v2'];
            var ms = keyframe['params'][style]['ms'];
            
            if (ms == 'opacity') {
                var opac = parseInt((parseInt(v1) + ((v2-v1) * k)));
                Utils.setOpacity( elem, opac );
            } else if (ms == 'color') {
    			var color = [
                    parseInt( parseInt(v1[0]) + ((v2[0]-v1[0]) * k) ),
    				parseInt( parseInt(v1[1]) + ((v2[1]-v1[1]) * k) ),
    				parseInt( parseInt(v1[2]) + ((v2[2]-v1[2]) * k) )
                ];
    			color = 'rgb('+color[0]+', '+color[1]+', '+color[2]+')';
    			elem.style[style] = color;
    		} else if (ms == '%') {
    			elem.style[style] = (parseFloat(v1) + ((v2-v1) * k)) + '%';
    		} else {
                elem.style[style] = parseInt((parseInt(v1) + ((v2-v1) * k))) + 'px';
            }
        }
        
        return (k == 1);
	}
	
    this.getParameter = function() {
        var value = '';
        var ms = '';
        
        if (typeof(arguments[0]) == 'object') {
            var elem = arguments[0];
            var style = arguments[1];
            ms = arguments[2];
            
            if (ms == 'opacity') {
                value = Utils.getOpacity( elem );
            } else {
                value = elem.style[style];
            }
        } else {
            value = arguments[0];
            ms = arguments[1];
        }
        
        if (ms == 'opacity') {
            if ((value < 0) || (value > 100)) value = 0;
            return value
        } else if (ms == 'color') {
            var m = null;
            if (m = value.match(/#([\da-h]{2})([\da-h]{2})([\da-h]{2})/i)) {
                return [parseInt(m[1], 16), parseInt(m[2], 16), parseInt(m[3], 16)];
            } else if (m = value.match(/rgb\((\d+),[ ]*(\d+),[ ]*(\d+)\)/i)) {
                return [m[1], m[2], m[3]];
            } else if (value == 'transparent') {
                return ['255', '255', '255'];
            } else {
                return ['0', '0', '0'];
            }
        } else if (ms == '%') {
            var m = null;
            if (m = value.match(/^(\d+)%$/)) {
                return m[1];
            }
            return 0;
        } else {
            var m = null;
            if (m = value.match(/^(\d+)%$/)) {
                return 0;
            } else if (m = value.match(/^(\d+)/)) {
                return m[1];
            } else if (m = value.match(/^-(\d+)/)) {
                return -m[1];
            } else {
                return 0;
            }
        }
    }
    
    this.in_array = function(arr, needle) {
        for (var i = 0; i < arr.length; i++) {
            if (arr[i] == needle) {
                return true;
            }
        }
        return false;
    }
    
    return this;
}
var Animation = new _Animation();