/**
 * Alex Software(c)2008.
 * 
 * Last update: 24.11.2008
 *
 */

/**
 * @param {Object} config - Object, containing configuration parametrs:
 *      'renderTo' - (required) id of parent element,
 *      'padding' - slide padding (px), default 10
 *      'delay' - delay (ms) of the slide when it is stopped automatically, default 2000
 *      'speed' - the delay (ms) between slide positions at the time of moving, default 30
 *      'step' - pixels count between slide steps, default 2
 *      'dir' - moving direction ('left', 'right'), default 'left'
 *      'autoStart' - true if moving starts after object creation, default true
 *      'autoStop' - true if moving stops when mouse moves over scroller, default false
 *      'axel' - acceleration coefficient, sets start speed, when slide begins moving, default 0
 *      'width' - width of slide, default equals to parent's element width divided to count
 *      'count' - number of visible slides, defaults to 1
 *      'slideInt' - interval between slides in pixels, defaults to 5
 *      'cls' - an optional CSS class that will be added to each slide 
 * @param {Array} data - Array of objects: {innerHTML, callback}. Here:
 *      'innerHTML' - display HTML of the slide,
 *      'callback' - function called on slide click
 * @return {Object} this - Scroller object
 */
Scroller = function(config, data){
    var _scroller = this;
    var data = data || [];
    var config = config || {};
	var speed = config.speed || 70;
    var renderer = null;
    var is_rendered = false;
    var padding = (typeof config.padding == "undefined") ? 10:config.padding;
    var slides = Array();
    var slide_pos = 0;
    var cur_slide = 0;
    var fix_slide = false;
    var count = config.count || 1;
    var show_timer = 0, delay_timer = 0;
    var width, height; 
    var d_width = config.slideInt || 5;
    var dir;
    var moving_limit = 0;
    var freeze = false;
    switch (config.dir){
        case "left": dir = -1; break;
        case "right": dir = 1; break;
        default: dir = -1;
    }
	
    if (data.length < count)
        throw "Error! Must be specified at least "+count+" element(s) in data array!";
        
    if (renderer = document.getElementById(config.renderTo)) 
        do_render();

    // private
    function do_resize(){
        width = config.width || ((renderer.clientWidth - padding*(count+1) - d_width*(count-1)) / count);
        height = renderer.clientHeight - padding * 2;
        for (var i=-1; i<=count; i++) {
            var el = slides[i];
            el.style.width = width + 'px';
            el.style.height = height + 'px';
            el.style.left = ((width + padding + d_width) * i + padding) + 'px';
            el.style.top = padding + 'px';
        }
    }
    
    // private 
    function do_render(){
      // if (renderer.clientHeight == 0 || renderer.clientWidth == 0) throw "Error! Renderer has no dimensions (width or height)";
        renderer.style.overflowX = "hidden";
        renderer.style.position = "relative";
        renderer.style.display = "block";
        
        for (var i=-1; i<=count; i++) {
            var el = slides[i] = document.createElement("DIV");
            el.className = config.cls || "";            
            el.style.position = "absolute";
            el.style.overflow = "hidden";
            el.style.display = "block";
            var data_pos = (i<0) ? data.length+i : ( (i>data.length-1) ? i-data.length: i);
            el.innerHTML = data[data_pos].innerHTML;
            renderer.appendChild(el);            
        }
        
        is_rendered = true;
        do_resize();
    
        if (typeof config.autoStart == "undefined" || config.autoStart) {
        	if (config.delay === 0) start_moving();
        	else delay_timer = window.setTimeout(start_moving, config.delay || 2000);
        }
    }
    
    // private
    function swap_slides(dir){
        //swap slide references
        var i = (dir < 0)?-1:count;
        var t_slide = slides[i];
        while (i != ((dir < 0)?count:-1) ){
                slides[i] = slides[i-dir];
                i-=dir;                    
            }
        slides[i] = t_slide;
        
        //global current slide
        if (dir>0) cur_slide--;
        if (dir<0) cur_slide++;
        
        if (cur_slide == data.length) cur_slide = 0;
        if (cur_slide<0) cur_slide = data.length-1;
        for (var i=-1; i<=count; i++) {
            var ii = i + cur_slide;
            var data_pos = (ii<0) ? data.length+ii : ( (ii>data.length-1) ? ii-data.length: ii);
            slides[i].innerHTML = data[data_pos].innerHTML;
        }
    }
    
    // private
    function stop_moving(){
        if (slide_pos != 0 || show_timer == 0) return;
        window.clearInterval(show_timer);
        show_timer = 0;
        
        if (moving_limit != 0) swap_slides(dir);
       if (!fix_slide){
        	if (config.delay === 0) start_moving();
            else delay_timer = window.setTimeout(start_moving, config.delay || 2000);
        }
    }
    
    // private
    function start_moving(){
        if (show_timer != 0) return;
        moving_limit = width + padding + d_width;
        show_timer = window.setInterval(moving, speed);
    }
    
    // private
    function moving(){
        for (var i=-1; i<=count; i++){
            slides[i].style.left = ((width + padding + d_width) * i + padding - dir * slide_pos) + 'px';
        }        
        
        if (slide_pos == -moving_limit){
            slide_pos = 0;
            stop_moving();
            return;
        }
           
        var path = width + padding + d_width;
        var path_curpos = Math.abs(moving_limit - Math.abs(slide_pos));
        var acceleration = (config.axel || 0)*(1 - Math.pow(1.0*(path-path_curpos)/path, 1.75));
        
        slide_pos-= (config.step || 2)*(1+acceleration);
         if (slide_pos < -moving_limit) slide_pos = -moving_limit;
    }
    
    return {
        /**
         * Render scroller to element with id, passed in parameter
         * @param {String} id - id of renderer
         */
        render : function(id){
            if (!renderer){
                if (!(renderer = document.getElementById(id))) throw "Error! Element with passed id doesn't exists";
                do_render();
            }
        },
        
        /**
         * Stops moving, after current slide show
         */
        stop : function(){
            if (!is_rendered) return;
            if (delay_timer != 0){
                window.clearTimeout(delay_timer);
                delay_timer = 0;
            }
            fix_slide = true;
        },
        
        /**
         * Restore moving
         */
        start : function(){
            if (!is_rendered) return;
            if (delay_timer != 0){
                window.clearTimeout(delay_timer);
                delay_timer = 0;
            }
            fix_slide = false;
            freeze = false;
            if (show_timer == 0){
				show_timer = window.setInterval(moving, speed);
            }
        },
        
        /**
         * Add new data to the scroller
         * @param {Array} new_data - {innerHTML,callback} data array
         */
        add : function(new_data){
            data[data.length] = new_data;                
        },
        
        /**
         * Immediately stop
         */
        urgentStop : function(){
            if (!is_rendered) return;
            window.clearInterval(show_timer);
            show_timer = 0;
            window.clearInterval(delay_timer);
            delay_timer = 0;
            freeze = true;
        },
        
        /**
         * Changes direction
         * @param {String} direction - moving direction ['left','right']
         */
        move : function(direction){
            if (!is_rendered) return;
            var new_dir;
            switch (direction){
                case "left": new_dir = -1; break;
                case "right": new_dir = 1; break;
                default: new_dir = -1;
            }
            
            if (dir != new_dir){
                dir = new_dir;
                slide_pos = -slide_pos;
                moving_limit = (moving_limit == 0)?(width + padding + d_width):(0);
            }
             if (show_timer == 0){
                moving_limit = width + padding + d_width;
                this.start();
            }
        },
        
        /**
         * Resize all slides
         */
        resize : function(){
            if (!is_rendered) return;
            do_resize();
            moving_limit = (moving_limit == 0)?(width + padding + d_width):(0);
            slide_pos = 0;                        
        },
        
        /**
         * Delete index's element from data array specified in constructor. 
         * If element with current index doesn�t exists or number of elements 
         * in data array are equal to slides count, function returns false, otherwise � true.
         * @param {Number} index - index of removing element
         */
        remove : function(index){
        	if (typeof data[index] == "undefined") return false;
        	if (count == data.length) return false;
        	data.splice(index, 1);
        	return true;        	
        },
		
		setSpeed : function(val){
			if (val){
				speed = val;
			}
		}
    }    
}
