Renderer = function () {
}
Renderer.prototype.fade = function (element, dir, fps, stepping, stopOpacity) {
    if (typeof element.rendererFadeInterval != 'undefined')
        window.clearInterval(element.rendererFadeInterval);
    element.fading = true;
    var opacity = cssHandler.getOpacity(element);
    dir = dir || (opacity==0 ? 'in' : 'out');
    fps = fps || this.defaultEffectSettings.fade.fps;
    stepping = stepping || this.defaultEffectSettings.fade.stepping;
    stopOpacity = stopOpacity || this.defaultEffectSettings.fade.stopOpacity[dir];
    element.rendererFadeInterval = window.setInterval(function () {
        opacity = dir=='out' ? opacity-stepping : opacity+stepping;
        if ((dir=='in' && opacity >= stopOpacity)
            || (dir=='out' && opacity <= stopOpacity)) {
            cssHandler.setOpacity(element, opacity);
            window.clearInterval(element.rendererFadeInterval);
            if (typeof element.onfadeready == 'function') {
                element.onfadeready();
            }
            if (typeof element.oneffectready == 'function') {
                element.oneffectready();
            }
            element.fading = undefined;
        } else {
            cssHandler.setOpacity(element, opacity);
        }
    }, Math.round(1000/fps));
}
Renderer.prototype.move = function (element, stopPos, easing, stepping, fps) {
    if (typeof element.rendererMoveInterval != 'undefined')
        window.clearInterval(element.rendererMoveInterval);
    element.moving = true;
    fps = fps || this.defaultEffectSettings.move.fps;
    stepping = stepping || this.defaultEffectSettings.move.stepping;
    easing = easing || this.defaultEffectSettings.move.easing;
    var startpos, currentPos;
    startPos = {
        'x':parseInt(cssHandler.getCSSValue(element,'left')),
        'y':parseInt(cssHandler.getCSSValue(element,'top'))
    };
    currentPos = {
        'x':parseInt(cssHandler.getCSSValue(element,'left')),
        'y':parseInt(cssHandler.getCSSValue(element,'top'))
    };
    var deltaX = Math.max(startPos.x, stopPos.x) - Math.min(startPos.x, stopPos.x);
    var deltaY = Math.max(startPos.y, stopPos.y) - Math.min(startPos.y, stopPos.y);
    var dirX = startPos.x > stopPos.x ? 'left' : 'right';
    var dirY = startPos.y > stopPos.y ? 'up' : 'down';
    
    var vector = deltaX >= deltaY ? 'x' : 'y';
    
    var v = deltaX >= deltaY ? deltaY/deltaX : deltaX/deltaY;
    
    var newPos = startPos;
    var getNewPos = function () {
        var x,y;
        if (deltaX > deltaY) { // move it along the X-Axis
            x = dirX == 'right' ? currentPos.x += stepping : x = currentPos.x -= stepping;
            y = dirY == 'down' ?  currentPos.y += stepping * v : currentPos.y -= stepping * v;
        } else { // move it along the Y-Axis
            y = dirY == 'down' ? currentPos.y += stepping : y = currentPos.y -= stepping;
            x = dirX == 'right' ? currentPos.x += stepping * v : currentPos.x -= stepping * v;
        }
        return {'x':Math.round(x),'y':Math.round(y)};
    }
    var ed = 0;
    var stmp = stepping;
    while (stmp/2 > 0) {
        ed += stmp;
        stmp = Math.floor(stmp/2);
    }
    stmp = stepping;
    var stepIndex = 0;
    element.rendererMoveInterval = window.setInterval (function () {
        var delta = Math.max(Math.abs(newPos[vector]), Math.abs(stopPos[vector])) - Math.min(Math.abs(newPos[vector]), Math.abs(stopPos[vector]));
        
        if (easing == true && delta < ed) {
            var tmp = Math.ceil(stepping / 2);
            if (stepping == stmp) {
                stepping = stepping-(ed-delta) > tmp ? stepping-(ed-delta) : tmp;
            } else {
                tmp = Math.ceil(delta / 2);
                stepping = tmp>=1 ? tmp : 1;
            }
        }
        if (delta <= (easing==true?1:stepping)) {
            window.clearInterval(element.rendererMoveInterval);
            element.style.left = stopPos.x + 'px';
            element.style.top = stopPos.y + 'px';
            if (typeof element.onmoveready == 'function') {
                element.onmoveready();
            }
            if (typeof element.oneffectready == 'function') {
                element.oneffectready();
            }
            element.moving = undefined;
        } else {
            newPos = getNewPos()
            element.style.left = newPos.x + 'px';
            element.style.top = newPos.y + 'px';
            newPos.stepIndex = stepIndex++;
            newPos.stepWidth = stepping;
            newPos.delta = delta;
            if (typeof element.onmove == 'function') {
                element.onmove(newPos);
            }
        }
    }, Math.round(1000/fps));
}

Renderer.prototype.grow = function (element, steps, fps) {
    // get dimensions
    /*
    var width = element.getCSSValue('width', 'int')+element.getCSSValue('paddingLeft', 'int')+element.getCSSValue('paddingRight', 'int');
    var height = element.getCSSValue('height', 'int')+element.getCSSValue('paddingTop', 'int')+element.getCSSValue('paddingBottom', 'int'), steppingA, steppingB;
    */
    var width = element.offsetWidth+element.getCSSValue('paddingLeft', 'int')+element.getCSSValue('paddingRight', 'int');
    var height = element.offsetHeight+element.getCSSValue('paddingTop', 'int')+element.getCSSValue('paddingBottom', 'int'), steppingA, steppingB;
    
    // set clipping to max Values, render element invisible
    var clipTop = Math.floor(height/2);
    var clipRight = Math.floor(width/2);
    var clipBottom = Math.ceil(height/2);
    var clipLeft = Math.ceil(width/2);
    //console.log(element.getCSSValue('width', 'int'));
    element.style.clip = 'rect('+clipTop+'px, '+clipRight+'px, '+clipBottom+'px, '+clipLeft+'px)';
    
    // calculate stepping
    steppingA = (width > height ? width : height) / steps;
    steppingB = Math.ceil((steppingA * (width > height ? height : width) / (width > height ? width : height)) / 2);
    steppingA = Math.ceil(steppingA/2);
    
    // animate
    element.rendererGrowInterval = window.setInterval(function () {
        if (clipTop - (width > height ? steppingB : steppingA) > 0) clipTop -= (width > height ? steppingB : steppingA);
        else clipTop = 0;
        if (clipRight + (width > height ? steppingA : steppingB) < width) clipRight += (width > height ? steppingA : steppingB);
        else clipRight = width;
        if (clipBottom + (width > height ? steppingB : steppingA) < height) clipBottom += (width > height ? steppingB : steppingA);
        else clipBottom = height;
        if (clipLeft - (width > height ? steppingA : steppingB) > 0) clipLeft -= (width > height ? steppingA : steppingB);
        else clipLeft = 0;
        element.style.clip = 'rect('+clipTop+'px '+clipRight+'px '+clipBottom+'px '+clipLeft+'px)';
        if (clipLeft == 0) {
            // @TODO: clear clip on 0
            element.unclip();
            window.clearInterval(element.rendererGrowInterval)
            if (typeof element.ongrowready == 'function') {
                element.ongrowready();
            }
            if (typeof element.oneffectready == 'function') {
                element.oneffectready();
            }
        }
    }.bindTo(this), Math.round(1000/fps))
}

Renderer.prototype.animate = function (element, property, unit, steps, from, to, easing) {
    var value;
    unit = unit||'';
    if (from > to) {
        var dir = '-';
        var stepping = (from - to) / steps;
    } else {
        var dir = '+';
        var stepping = (to - from) / steps;
    }
    element[property] = from+unit;
    console.log(from+unit);
    element.rendererAnimateInterval = window.setInterval(function () {
        value = eval('parseInt(element[property])'+dir+'stepping');
        element[property] = value+unit;
    console.log(element[property]);
        if (function () { if (dir == '-') { return value <= to ? true : false } else { return value >= to ? true : false }}()) {
            window.clearInterval(element.rendererAnimateInterval);
            element[property] = to+unit;
        }
    }.bindTo(this), 40)
}

Renderer.prototype.defaultEffectSettings = {
    fade:{
        startOpacity:{
                'in':0,
                'out':1
            },
        stopOpacity:{
                'in':1,
                'out':0
            },
        fps:25,
        stepping:.1
    },
    move:{
        fps:25,
        stepping:25,
        easing:false,
        stopPos:{
            'x':0,
            'y':0
        }
    },
    grow:{
        fps:25,
        steps:5
    },
    slideIn:{
        fps:25,
        stepping:25,
        easing:true,
        stopPos:{
            'x':0,
            'y':0
        }
    },
    slideOut:{
        fps:25,
        stepping:25,
        easing:true
    }
}
Renderer.prototype.getEffectSettings = function (effect, effectParams) {
    effect = libObject.isMember(effect, this.defaultEffectSettings) ? effect : null;
    if (effect != null) {
        effectParams = effectParams || this.defaultEffectSettings[effect];
    }
    for (var i in this.defaultEffectSettings[effect]) {
        if (typeof effectParams[i] == 'undefined') {
            effectParams[i] = this.defaultEffectSettings[effect][i];
        }
    }
    return effectParams;
}

Renderer.prototype.elementMethods = {
    renderIn : function (renderParent, effect, effectParams) {
        if (effect) {
            effectParams = this.renderer.getEffectSettings(effect, effectParams);
        }
        switch (effect) {
            case 'fade':
                cssHandler.setOpacity(this, effectParams.startOpacity['in']);
                renderParent.appendChild(this);
                this.renderer.fade(this, effectParams.dir, effectParams.fps, effectParams.stepping, effectParams.stopOpacity['in']);
                break;
            case 'slideIn':
                this.style.position = 'absolute';
                renderParent.appendChild(this);
                var settings = {
                    'style':{
                        'left':-this.offsetWidth + 'px',
                        'top':-this.offsetHeight + 'px'
                    }
                };
                var stopPos = {'x':0,'y':0};
                if (typeof effectParams.startPos != 'undefined') {
                    switch (typeof effectParams.startPos) {
                        case 'string':
                            if (effectParams.startPos == 'rand') {
                                if (typeof this.lastSlideInStartPos == 'undefined') this.lastSlideInStartPos = effectParams.startPos;
                                var r = ['top','topRight','right','bottomRight','bottom','bottomLeft','left','topLeft'];
                                do {
                                    effectParams.startPos = r[Math.round(Math.random()*(r.length-1))];
                                } while (this.lastSlideInStartPos == effectParams.startPos);
                                this.lastSlideInStartPos = effectParams.startPos;
                            }
                            switch (effectParams.startPos) {
                                case 'top':
                                    settings.style.left = Math.floor((renderParent.clientWidth)/2) - Math.floor(this.offsetWidth/2) + 'px';
                                    settings.style.top = -this.offsetHeight + 'px';
                                    stopPos.x = Math.floor((renderParent.clientWidth)/2) - Math.floor(this.offsetWidth/2);
                                    stopPos.y = 0;
                                    break;
                                case 'topRight':
                                    settings.style.left = renderParent.clientWidth + 'px';
                                    settings.style.top = -this.offsetHeight + 'px';
                                    stopPos.x = renderParent.clientWidth - this.offsetWidth;
                                    stopPos.y = '0';
                                    break;
                                case 'right':
                                    settings.style.left = renderParent.clientWidth + 'px';
                                    settings.style.top = Math.floor(renderParent.clientHeight/2 - this.offsetHeight/2) + 'px';
                                    stopPos.x = renderParent.clientWidth - this.offsetWidth;
                                    stopPos.y = Math.floor(renderParent.clientHeight/2 - this.offsetHeight/2);
                                    break;
                                case 'bottomRight':
                                    settings.style.left = renderParent.clientWidth + 'px';
                                    settings.style.top = renderParent.clientHeight + 'px';
                                    stopPos.x = renderParent.clientWidth - this.offsetWidth;
                                    stopPos.y = renderParent.clientHeight - this.offsetHeight;
                                    break;
                                case 'bottom':
                                    settings.style.left = Math.floor(renderParent.clientWidth/2 - this.offsetWidth/2) + 'px';
                                    settings.style.top = renderParent.clientHeight + 'px';
                                    stopPos.x = parseInt(Math.floor(renderParent.clientWidth/2 - this.offsetWidth/2));
                                    stopPos.y = renderParent.clientHeight - this.offsetHeight;
                                    break;
                                case 'bottomLeft':
                                    settings.style.left = -this.offsetWidth + 'px';
                                    settings.style.top = renderParent.clientHeight + 'px';
                                    stopPos.x = '0';
                                    stopPos.y = renderParent.clientHeight - this.offsetHeight;
                                    break;
                                case 'left':
                                    settings.style.left = -this.offsetWidth + 'px';
                                    settings.style.top = Math.floor(renderParent.clientHeight/2 - this.offsetHeight/2) + 'px';
                                    stopPos.x = '0';
                                    stopPos.y = Math.floor(renderParent.clientHeight/2 - this.offsetHeight/2);
                                    break;
                                case 'topLeft':
                                default:
                                break;
                            }
                            break;
                        default:
                        break;
                    }
                }
                this.setProperties(settings);
                this.renderer.move(this, stopPos, effectParams.easing, effectParams.stepping, effectParams.fps);
                break;
            case 'grow':
                renderParent.appendChild(this);
                this.renderer.grow(this, effectParams.steps, effectParams.fps);
                break;
            default:
            renderParent.appendChild(this);
        }
        if (typeof this.onrender == 'function') {
            this.onrender();
        }
    },
    remove : function (effect, effectParams) {
        if (effect) {
            effectParams = this.renderer.getEffectSettings(effect, effectParams);
        }
        switch (effect) {
            case 'fade':
                eventHandler.addEvent(this, 'fadeready', this.fadeready = function () {
                    var a = function () {
                        eventHandler.removeEvent(this, 'fadeready', this.fadeready);
                    }.call(this);
                    if(this.parentNode)
                        this.parentNode.removeChild(this);
                }.bindAsEventListener(this));
                this.renderer.fade(this, effectParams.dir, effectParams.fps, effectParams.stepping, effectParams.stopOpacity['out']);
                break;
            case 'slideOut':
                this.style.position = 'absolute';
                var stopPos = {
                    'x':-this.offsetWidth,
                    'y':-this.offsetHeight
                };
                if (typeof effectParams.stopPos != 'undefined') {
                    switch (typeof effectParams.stopPos) {
                        case 'string':
                            switch (effectParams.stopPos) {
                                case 'top':
                                    stopPos.x = this.offsetLeft + parseInt(cssHandler.getCSSValue(this.parentNode,'borderLeftWidth'));
                                    stopPos.y = -this.offsetHeight;
                                    break;
                                case 'topRight':
                                    stopPos.x = this.parentNode.clientWidth;
                                    stopPos.y = -this.offsetHeight;
                                    break;
                                case 'right':
                                    stopPos.x = this.parentNode.clientWidth;
                                    stopPos.y = Math.floor(this.parentNode.clientHeight/2 - this.offsetHeight/2);
                                    break;
                                case 'bottomRight':
                                    stopPos.x = this.parentNode.clientWidth;
                                    stopPos.y = this.parentNode.clientHeight;
                                    break;
                                case 'bottom':
                                    stopPos.x = parseInt(Math.floor(this.parentNode.clientWidth/2 - this.offsetWidth/2));
                                    stopPos.y = this.parentNode.clientHeight;
                                    break;
                                case 'bottomLeft':
                                    stopPos.x = -this.offsetWidth;
                                    stopPos.y = this.parentNode.clientHeight;
                                    break;
                                case 'left':
                                    stopPos.x = -this.offsetWidth;
                                    stopPos.y = Math.floor(this.parentNode.clientHeight/2 - this.offsetHeight/2);
                                    break;
                                case 'topLeft':
                                default:
                                break;
                            }
                            break;
                        default:
                        break;
                    }
                }
                eventHandler.addEvent(this, 'moveready', this.moveready = function () {
                    eventHandler.removeEvent(this, 'moveready', this.moveready);
                    if(this.parentNode)
                        this.parentNode.removeChild(this);
                }.bindAsEventListener(this));
                this.renderer.move(this, stopPos, effectParams.easing, effectParams.stepping, effectParams.fps);
                break;
            default:
            if(this.parentNode)
                this.parentNode.removeChild(this);
        }
        if (typeof this.onremove == 'function') {
            this.onremove();
        }
    },
    move : function (params) {
        params = this.renderer.getEffectSettings('move', params);
        this.renderer.move(this, params.stopPos, params.easing, params.stepping, params.fps);
    },
    fade : function (params) { //element, dir, fps, stepping, stopOpacity
        params = this.renderer.getEffectSettings('fade', params);
        this.renderer.fade(this, params.dir, params.fps, params.stepping, params.stopOpacity);
    },
    setOpacity : function (opacity) {
        cssHandler.setOpacity(this, opacity);
    },
    getCSSValue : function (property, type) {
        return cssHandler.getCSSValue(this, property, type);
    },
    unclip : function () {
        cssHandler.unclip(this);
    },
    getScreenOffset : function () {
        return this.renderer.getScreenOffset(this);
    },
    getOffsetSize : function () {
        return {Width:this.offsetWidth,Height:this.offsetHeight};
    },
    getClientSize : function () {
        return {Width:this.clientWidth,Height:this.clientHeight};
    },
    setProperties : function (properties,subject) {
        subject=subject||this;
        this.renderer.setProperties(properties,subject);
    },
    empty : function () {
        this.renderer.empty(this);
    },
    appendImage : function (args) {
        this.renderer.appendImage(args, this);
    },
    getScreenOffset : function () {
        return this.renderer.getScreenOffset(this);
    },
    addClassName : function (string) {
        this.className += this.className != '' ? ' '+string : string;
    },
    removeClassName : function (string) {
        var reg = new RegExp('(^'+string+' ?| '+string+')');
        this.className = this.className.replace(reg, '');
        reg = undefined;
    }
}
Renderer.prototype.getScreenOffset = function (element) {
    var sco = libScreen.getScrollOffset();
    var x = element.offsetLeft;
    var y = element.offsetTop;
    var parent = element.offsetParent;
    do {
        x += parent.offsetLeft;
        y += parent.offsetTop;
        parent = parent.offsetParent;
    } while (parent != null);
    return {x:x-sco.x,y:y-sco.y};
}
Renderer.prototype.appendImage = function (args, element) {
    var image = this.createElement('IMG');
    if (typeof args == 'object') {
        this.setProperties(image, args);
    }
    image.renderIn(element);
}
Renderer.prototype.empty = function (element) {
    while(element.hasChildNodes()) {
        element.removeChild(element.firstChild);
    }
};
Renderer.prototype.setProperties = function (properties,subject) {
    for (var n in properties) {
        if (typeof properties[n] == 'object' && typeof subject[n] != 'undefined') {
            this.setProperties(properties[n],subject[n]);
        } else {
            subject[n] = properties[n];
        }
    }
};
Renderer.prototype.createElement = function (elementName, attributes) {
    var element = elementName.toLowerCase() == 'img' ? new Image : document.createElement(elementName);
    element.renderer = this;

    for (var i in this.elementMethods) {
        element[i] = this.elementMethods[i];
    }
    if (attributes) {
        element.setProperties(attributes);
    }
    if (typeof element.oncreate == 'function') {
        element.oncreate();
    }
    return element;
}
Renderer.prototype.getWindowSize = function () {
    var win = document.getElementsByTagName('HEAD')[0].parentNode;
    return {"width":win.clientWidth,"height":win.clientHeight}
}
Renderer.prototype.assimilate = function (element, attributes) {
    element.renderer = this;

    for (var i in this.elementMethods) {
        element[i] = this.elementMethods[i];
    }
    if (attributes) {
        element.setProperties(attributes);
    }
    return element;
}

window.renderer = new Renderer;