function SliderModel(){

    /**
     * Anzeige fläche
     */
    var _ground   = null;

    /**
     * der bereich der gescrollt wird
     */
    var _inner    = null;

    var _elements = [];

    var _modi     = 1;
    
    /* Beschleunigung vom Slideeffekt
     */
    var velocity = 0.08;

    /* die aktuelle Geschwindigkeit
     */
    var speed = 0;

    /* um welche Weite es sich bewegen soll
     */
    var moveToWidth = 0;

    var slideInProcess = false;

    var start = 0;

    var _obs = [];

    function getDimension(htmlobj){
        // Dimension berechnen
        var w = htmlobj.offsetWidth;
        var h = htmlobj.offsetHeight;

        var l = 0;
        var t = 0;
    
        return {
            width:w,
            height:h,
            left:l,
            top:t
        }
    };

    /**
     * css Wert eines Knotens berechnen
     * @access private
     * @param  node HTML Knoten
     * @param  attr String Attribute
     * @return style String das CSS Attribute
     */
    function getCSSValue(node,attr) {
        var style;

        try {
            style = window.getComputedStyle(node,"").getPropertyValue(attr);
        } catch (e) {
            style = node.currentStyle[attr];
        }

        return style;
    };

    /**
     * das Margin direkt für den IE berechnen da er mit margin-left
     * und margin-top gar nicht klar kam sondern direkt das attribute
     * margin abfragte wie es im CSS angegeben ist
     * @access private
     * @param node // HTML Node
     * @return margin integer Value
     */
    function calcIEMargin(node){
        
        var margin = 0;

        if( (margin = node.currentStyle['margin']) != 'undefined' ) {
            margin = margin.split(" ");

            switch (margin.length) {
                case 1:
                    margin = 0;
                    break;
                case 2: case 3:
                    margin = parseInt(margin[1])*2;
                    break;
                case 4:
                    margin = parseInt(margin[1])+parseInt(margin[3]);
                    break;
            }
        }

        return margin;
    };

    function notifyObserver(val){
        for(var i = 0 ; i < _obs.length;i++) {
            _obs[i].updateSlide.call(_inner,val);
        }
    }

    function calcSlide(){
        
        var left  = parseInt(_inner.style.left) || 0;

        speed += velocity;

        var current_width = moveToWidth*Math.sin(speed);

        if(speed > Math.PI/2) {

            start += moveToWidth*_modi;

            notifyObserver(start);
            speed = 0;
            slideInProcess = false;
            return;
            
        } else {
            left = start + ( current_width *_modi);
            notifyObserver(left);
            window.setTimeout(calcSlide,40);
        }
    };

    function getMoveWidth () {
        
        var le = parseInt( _inner.style.left) || 0;
        var iw = parseInt( _inner.style.width );
        var gw = parseInt( _ground.width );

        if(gw > iw) return 0;

        if(_modi == -1) { // nach rechts

            if(iw-( Math.abs(le)+gw ) > gw+1 )
                return gw+1;
            else
                return iw-( Math.abs(le)+gw );


        } else {
            if( Math.abs(le)-(gw+1) > 0 )
                return gw+1;
            else
                return Math.abs(le);
        }

    };

    function startSlide(width){

        moveToWidth = width || getMoveWidth();
        if(moveToWidth == 0) return

        calcSlide();
        slideInProcess = true;
        
    }

    /**
     * Berechnet die weite die gescrollt werden muss
     * bis ein Knoten sich im sichtbaren Bereich befindet
     * @access private
     * @param _node int
     * @param innerleft int Abstands des inneren Breiches nach links
     */
    function moveNodeVisible(_node,innerleft){
        var left = _elements[_node].node.offsetLeft;

        // es sollte so berechnet werden das nur margin links genommen wird
        // ich zieh pauschal mal 1px ab da ich momentan weiß das es 1px margin
        // rechts hat
        var node_range    = [left,left+_elements[_node].width];

        var display_range = [innerleft,_ground.width+innerleft];

        var w = 0;
        var end = false;

        if(node_range[1] <= display_range[0]) {

            _modi = 1;
            
            do {
                w += _ground.width+1; // +1 weil margin rechts 1px

                if(display_range[0]-w < 0) {
                    w = innerleft;
                    break;
                }
                
            } while( node_range[1] <= display_range[0]-w );

            startSlide(w);

        }

        if(node_range[0] >= display_range[1]) {
            _modi = -1;

            do {
                w += _ground.width+1;

                if( display_range[1]+w > parseInt(_inner.style.width) ) {
                    w =  parseInt( _inner.style.width )-display_range[1];
                    break;
                }

            } while( node_range[1] >= display_range[1]+w );

            startSlide(w);
        }
        
    };

    this.setGround = function( ground ) {
        _ground      = getDimension(ground);
        _ground.node = ground;
    };

    this.getGround = function (){
        return _ground;
    };

    this.setSlideInner = function(slideInner){

        var childs = slideInner.childNodes;
        var len    = childs.length;

        var full_width = 0;
        var count = 0;

        for(var i = 0 ; i < len;i++) {
            if(childs[i].nodeName == "#text") continue;
            
            _elements[count]         = getDimension(childs[i]);
            _elements[count]['node'] = childs[i];

            /**
             * das margin der Element noch hinzurechnen für jedes
             * einzelne Kind Element, daraus ergibt sich die tatsächliche Weite
             * für jedes Kind und die gesammt weite für die gesammte Anzeige Fläche
             */
            if(!navigator.userAgent.match(/msie/i)) {
                _elements[count].width += parseInt(getCSSValue(childs[i], 'margin-left'  ));
                _elements[count].width += parseInt(getCSSValue(childs[i], 'margin-right' ));
            } else {
                _elements[count].width += calcIEMargin(_elements[count].node);
            }
            
            full_width += _elements[count].width;

            count++;
        }

        _inner                = slideInner;
        _inner.style.width    = full_width+"px";
        _inner.style.position = 'relative';

        _ground.node.style.position = 'relative';
    };

    /**
     * prüft ab ob der Knoten im Anzeigebereich ist
     * und bewegt dann den Slider dorthin falls notwendig
     * @access public
     * @param key int ID des Knotens
     */
    this.moveNodeInDisplay = function(key){

        if(slideInProcess || _ground.width > parseInt(_inner.style.width) )
            return;
        
        var innerLeft = Math.abs( parseInt(_inner.style.left) ) || 0;

        if(key == _elements.length) {
            // zum start bewegen
            _modi = 1;
            startSlide(innerLeft);

        } else if(key == -1) {
            // zum ende bewegen
            _modi = -1;
            startSlide(parseInt(_inner.style.width)-(_ground.width+innerLeft));

        } else 
            moveNodeVisible(key,innerLeft);
    };

    this.slideToLeft = function(){
        if(slideInProcess) return;
        _modi = 1;
        startSlide();
    };
    
    this.slideToRight = function(){
        if(slideInProcess) return;
        _modi = -1;
        startSlide();
    };
    
    this.addObserver = function(obs){
        _obs.push(obs);
    };
}
