/*
    Behaviours for scrolling panels, with up and down buttons to scroll.
*/

Site.Widgets.Scroller = Class.create
(
    Site.Base,
    {            
        initialize: function($super, element, options)
        {
            $super();
            
            this.element = element;
            
            this.setOptions
            (
                options,
                {
                    interval: 1,
                    increment: 4,
                    hideButtonsAtBoundary: false,
                    direction: "vertical",
                    method: "hold", /* can be "hold" which scrolls as long as the user holds the mouse. or "click" which scrolls once per click */ 
                    scrollDuration: 0, /* only applicable to a scroller using the "click" method . if greater than zero, Effect.Morph is used to scroll the panelElement smoothly */
                    buttonLimitClassName: "bt-limit"
                }
            );

            this.btRetreat = this.options.btRetreat || this.element.select("a.bt-retreat").first();
            this.btAdvance = this.options.btAdvance || this.element.select("a.bt-advance").first();
            this.windowElement = this.options.window || this.element.select(".scroller-window").first();
            this.panelElement = this.options.panelElement || this.element.select(".scroller-panel").first();

            if (this.panelElement)
            {
                if (this.options.direction == "horizontal")
                {
                    this.panelElement.setStyle( { left: "0px" } );
                }
                else
                {
                    this.panelElement.setStyle( { top: "0px" } );
                }
            }
            
            this.addObservers("btRetreatOnMouseDown", "btRetreatOnMouseUp", "btAdvanceOnMouseUp", "btAdvanceOnMouseDown", "ignoreClick", "btAdvanceOnClick", "btRetreatOnClick");
            this.addBlocks("scrollUp", "scrollDown", "scrollLeft", "scrollRight");
            
            if ( this.options.direction == "horizontal" && !(this.options.panelWidth && this.windowElement && this.panelElement) )
            {
                throw("Horizontal scrollers require the width of the panel element to be specified, and a window and panel element");
            }
            else if (this.options.direction == "vertical" && !(this.options.panelHeight && this.windowElement && this.panelElement) )
            {
                throw("Vertical scrollers require the height of the panel element to be specified, and a window and panel element");
            }
                 
                
            // setup event observers

            if (this.options.method == "hold")
            {
                addEvent(this.btRetreat, 'mousedown', this.observers.btRetreatOnMouseDown);
                addEvent(this.btRetreat, 'mouseup', this.observers.btRetreatOnMouseUp);
                addEvent(this.btRetreat, 'mouseout', this.observers.btRetreatOnMouseUp);

                addEvent(this.btAdvance, 'mousedown', this.observers.btAdvanceOnMouseDown);
                addEvent(this.btAdvance, 'mouseup', this.observers.btAdvanceOnMouseUp);
                addEvent(this.btAdvance, 'mouseout', this.observers.btAdvanceOnMouseUp);

                addEvent(this.btAdvance, 'click', this.observers.ignoreClick);
                addEvent(this.btRetreat, 'click', this.observers.ignoreClick);
            }
            else
            {
                // move once for each click (a substantial "options.increment" value would be required here to ensure it doesn't take hundreds of clicks to scroll)
                addEvent(this.btAdvance, 'click', this.observers.btAdvanceOnClick);
                addEvent(this.btRetreat, 'click', this.observers.btRetreatOnClick);
            }
            
            
            this.displayButtons();
        },
        
        scrollToOrigin: function(animate)
        {
            this._setPanelPosition(0, animate);
        },
        
        setPanelHeight: function(height)
        {
            this.options.panelHeight = height;
        },
        
        setPanelWidth: function(width)
        {
            this.options.panelWidth = width;
        },
        
        scrollToElementId: function(id, animate)
        {
            if (element = $(id))
            {
                if (this.options.direction == "vertical")
                    this._setPanelPosition(Math.max(-element.offsetTop), animate);
                else if (this.options.direction == "horizontal")
                    this._setPanelPosition(Math.max(-element.offsetLeft), animate);
            }
        },
        
        ignoreClick: function(event)
        {
            Event.stop(event);
        },
        
        btRetreatOnMouseDown: function(event)
        {
            if (this.options.direction == "vertical")
                this.scrollInterval = window.setInterval(this.blocks.scrollUp, this.options.interval);
            else
                this.scrollInterval = window.setInterval(this.blocks.scrollLeft, this.options.interval);
                
            Event.stop(event);
        },
            
        btRetreatOnMouseUp: function(event)
        {
            this.scrollStop();
            Event.stop(event);
        },
    
        btAdvanceOnMouseDown: function(event)
        {
            if (this.options.direction == "vertical")
                this.scrollInterval = window.setInterval(this.blocks.scrollDown, this.options.interval);
            else
                this.scrollInterval = window.setInterval(this.blocks.scrollRight, this.options.interval);

            Event.stop(event);
        },
    
        btAdvanceOnMouseUp: function(event)
        {
            this.scrollStop();
            
            Event.stop(event);
        },
    
        btAdvanceOnClick: function(event)
        {
            var element = Event.element(event);
            
            if (this.options.direction == "vertical")
                this.scrollDown();
            else
                this.scrollRight();
            
            this.displayButtons();
                
            if (element.blur)
                element.blur();
                
            Event.stop(event);
        },
        
        btRetreatOnClick: function(event)
        {            
            var element = Event.element(event);

            if (this.options.direction == "vertical")
                this.scrollUp();
            else
                this.scrollLeft();

            this.displayButtons();

            if (element.blur)
                element.blur();
                
            Event.stop(event);            
        },
        
        scrollUp: function()
        {
            var top = parseInt(this.panelElement.style.top);

            if (top < 0)
            {
                // the Math.max ensures that the panel is not scrolled up beyond zero
                top = Math.min(0, top + this.options.increment);
                this._setPanelPosition(top);
            }
            else
                this.scrollStop();        
        },
    
        scrollDown: function()
        {
            var top = parseInt(this.panelElement.style.top);
            
            if (Math.abs(top) <= this.options.panelHeight - this.windowElement.offsetHeight)
            {
                // the Math.min ensures that at the end, the panel is only scrolled by as much space as there is available
                top = Math.max(top - this.options.increment, - (this.options.panelHeight - this.windowElement.offsetHeight) );
                this._setPanelPosition(top);
            }
            else
            {
                this.scrollStop();
            }
        },
    
        scrollRight: function()
        {
            // horizontal scrollers work by scrolling a panel element within a window element (using simply the style element, and relying on relative/absolute positioning)

            var left = parseInt(this.panelElement.style.left);
            
            if (Math.abs(left) <= this.options.panelWidth - this.windowElement.offsetWidth)
            {   
                // the Math.min ensures that at the end, the panel is only scrolled by as much space as there is available
                left = Math.max(left - this.options.increment, - (this.options.panelWidth - this.windowElement.offsetWidth) );
                this._setPanelPosition(left);
            }
            else
            {
                this.scrollStop();
            }
        },
    
        scrollLeft: function()
        {
            var left = parseInt(this.panelElement.style.left);

            if (left < 0)
            {
                // the Math.max ensures that the panel is not scrolled up beyond zero
                left = Math.min(0, left + this.options.increment);
                this._setPanelPosition(left);
            }
            else
                this.scrollStop();
        },
        
        _setPanelPosition: function(position, animate)
        {

            if (animate || ( this.options.scrollDuration > 0 && this.options.method == "click" ))
            {
                var style = (this.options.direction == "horizontal" ? { left: position + "px" } : { top: position + "px" });

                if (!this.animating)
                {
                    this.animating = true;
                    new Effect.Morph(this.panelElement, { duration: this.options.scrollDuration, style: style, afterFinish: function() { this.animating = false; this.displayButtons(); }.bind(this) });
                }
            }
            else
            {
                if (this.options.direction == "horizontal")
                    this.panelElement.style.left = position + "px";
                else
                    this.panelElement.style.top = position + "px";
            }
        },
        
        scrollStop: function()
        {
            this.displayButtons();
            
            if (this.scrollInterval)
                window.clearInterval(this.scrollInterval);
        },
        
        displayButtons: function()
        {
            if (this.options.hideButtonsAtBoundary)
            {
                if (this.options.direction == "vertical")
                {
                    this.btRetreat.style.visibility = (this.element.scrollTop == 0) ? 'hidden' : 'visible'; 
                    this.btAdvance.style.visibility = (this.element.scrollTop == this.element.scrollHeight - this.element.offsetHeight) ? 'hidden' : 'visible'; 
                }
            }
            
            // mark the buttons with a "endpoint" class if at the lmit

            this.btAdvance.removeClassName(this.options.buttonLimitClassName);
            this.btRetreat.removeClassName(this.options.buttonLimitClassName);
            
            if (this.options.direction == "horizontal")
            {
                var left = parseInt(this.panelElement.style.left);
                
                if (left == 0)
                {
                    this.btRetreat.addClassName(this.options.buttonLimitClassName);
                }
                
                if (parseInt(this.panelElement.style.left) <= - (this.options.panelWidth - this.windowElement.offsetWidth))
                {
                    this.btAdvance.addClassName(this.options.buttonLimitClassName);
                }
            }
            else
            {
                var top = parseInt(this.panelElement.style.top);
            
                if (top == 0)
                {
                    this.btRetreat.addClassName(this.options.buttonLimitClassName);
                }
            
                if (parseInt(this.panelElement.style.top) <= - (this.options.panelHeight - this.windowElement.offsetHeight))
                {
                    this.btAdvance.addClassName(this.options.buttonLimitClassName);
                }
            }
        },
        
        destroy: function($super)
        {
            removeEvent(this.btRetreat, 'mousedown', this.observers.btRetreatOnMouseDown);
            removeEvent(this.btRetreat, 'mouseup', this.observers.btRetreatOnMouseUp);
            removeEvent(this.btRetreat, 'mouseout', this.observers.btRetreatOnMouseUp);

            removeEvent(this.btAdvance, 'mousedown', this.observers.btAdvanceOnMouseDown);
            removeEvent(this.btAdvance, 'mouseup', this.observers.btAdvanceOnMouseUp);
            removeEvent(this.btAdvance, 'mouseout', this.observers.btAdvanceOnMouseUp);

            removeEvent(this.btAdvance, 'click', this.observers.ignoreClick);
            removeEvent(this.btRetreat, 'click', this.observers.ignoreClick);

            removeEvent(this.btAdvance, 'click', this.observers.btAdvanceOnClick);
            removeEvent(this.btRetreat, 'click', this.observers.btRetreatOnClick);

        }
        
    },
    {
        
    }
);