/* $Id: df-scroll.js 1579 2010-02-24 11:24:10Z jamesf $ */
/*
 * ** Copyright and license information                      **
 * **                                                        **
 * -- THIS TEXT MUST REMAIN VISIBLE AND IN TACT IN ALL CASES --
 *
 * DF Scroll - Javascript for custom scroll bars
 *
 * Copyright (c) 2010 Dalton Firth Limited, some rights reserved
 *
 * http://www.daltonfirth.co.uk/opensource
 *
 * This software is licensed as open source for free reuse according to the
 * terms of the Gnu Affero General Public License (AGPL)
 *
 * For the full text of the license, see http://www.gnu.org/licenses/agpl.txt
 *
 * You are free to modify and redistribute this software within the terms 
 * described in the above specified license.  Send any modifications, updates
 * and derivatives of this software via email to:
 *
 *     opensource-notify@daltonfirth.co.uk
 *
 * Under the terms of the AGPL you are obliged to make available the source,
 * including this notice,e to all users of this software, even when the user
 * accesses services over a network.
 *
 * This software comes with ABSOLUTELY NO WARRANTY WHATSOEVER.
 *
 * ^^ THE ABOVE TEXT MUST REMAIN UNMODIFIED IN ALL CASES     ^^ 
 */

/*
 * General advice: instead of hacking the classes, add new functionality
 *                 that preserves default behaviour and existing functionality.
 *                 
 * Two classes are defined. An instance of DFScroll is created for each HTML
 * className that needs handling as a custom scroll bar. The DFScroll instance
 * then creates an instance of DFScrollBar for each <div> element for the given
 * className.  This gives greatest flexibility, allowing many identical custom 
 * scrollable divs to be created, or N instances of differeing types.
 */

function DFScrollBar()
{
    this.origContentWidth  = 0;
    this.sbarWidth = 60;
    this.sbarPaddingRight = 10;
    this.pageOverlap = 20;
    this.contentOffsetLeft = 0;
    this.sliderWrapperTop = 0;
    this.contentDiv       = null;
    this.sliderWrapperDiv = null;
    this.sliderDiv        = null;
    this.sliderYOffset    = 0;
    this.sliderYPadT      = 5; // Configurable
    this.sliderYPadB      = 5; // Configurable
    this.sliderElem       = null;
    this.wrapperDiv       = null;
    this.scrollPosY       = 0;
    this.inputModeActive  = false;

    this.mouseWheelSpeed = 6;
    this.sliderElemType  = 'div';
    this.sliderElemDefaultHeight = '60';
    this.sliderElemSrc   = false;
    this.enableState     = false;
    this.initialised     = false;
    this.sliderMouseDown = false;
    this.mouseTamer      = 0;
    this.sliderButtonLoc = 'tb'; // tt: top, top - both buttons above slider
                                 // tb: top, bottom - one button either side of slider
                                 // bb: bottom, bottom - both buttons below slider
    this.sliderTopSrc    = false;
    this.sliderBottomSrc = false;
    this.sliderTopElem    = null;
    this.sliderBottomElem = null;
    this.makeFocus        = false;

    // Public methods
    this.setSliderImgSrc = function(src)
        {
            this.sliderElemType = 'img';
            this.sliderElemSrc  = src;
        };
    

    this.init = function()
    {
        var i;
        var iobj;

        // Need to assign all divs before calling init()
        if (this.contentDiv && this.wrapperDiv)
        {
            // Set-up content div
            this.origContentWidth  = this.contentDiv.offsetWidth;

            // alert(this.contentDiv.offsetHeight + ' : ' + this.contentDiv.clientHeight + ' : ' + this.contentDiv.scrollHeight);

            this.contentDiv.style.position='relative';
            this.contentDiv.style.outline='#000 none 0';

            this.contentDiv.style.overflow='hidden';
            this.contentDiv.hideFocus = true;
            this.contentDiv.style.width = (this.contentDiv.offsetWidth - (this.sbarWidth + this.sbarPaddingRight)) + 'px';
                
            // Make the content "focussable" so can receive keystrokes
            this.contentDiv.setAttribute('tabIndex', '0');
                
            // Hack to get event handlers to work
            var cb_obj = this;

            df_attach_event(this.contentDiv, 'keydown',        function(e){cb_obj.handleKeypress(e)});
            df_attach_event(this.contentDiv, 'mousewheel',     function(e){cb_obj.handleMousewheel(e)});
            df_attach_event(this.contentDiv, 'DOMMouseScroll', function(e){cb_obj.handleMousewheel(e)});


            df_attach_event(this.contentDiv, 'resize', function(e){cb_obj.recalc(e)});
            df_attach_event(this.contentDiv, 'change', function(e){cb_obj.recalc(e)});


            // stop input being stolen from child text areas and 
            // other input methods active within the scrollable div.
            // - possibly better method is to see if tabIndex attribute is set?
            for (i=0; iobj=this.contentDiv.getElementsByTagName('textarea')[i]; i++)
            {
                df_attach_event(iobj, 'focus', function(e){cb_obj.handleFocusBlur(e)});
                df_attach_event(iobj, 'blur', function(e){cb_obj.handleFocusBlur(e)});
            }

            for (i=0; iobj=this.contentDiv.getElementsByTagName('input')[i]; i++)
            {
                df_attach_event(iobj, 'focus', function(e){cb_obj.handleFocusBlur(e)});
                df_attach_event(iobj, 'blur', function(e){cb_obj.handleFocusBlur(e)});
            }

            for (i=0; iobj=this.contentDiv.getElementsByTagName('select')[i]; i++)
            {
                df_attach_event(iobj, 'focus', function(e){cb_obj.handleFocusBlur(e)});
                df_attach_event(iobj, 'blur', function(e){cb_obj.handleFocusBlur(e)});
            }

            // Set-up wrapper
            this.wrapperDiv.style.overflow = 'hidden';               
                
            var obj = this.wrapperDiv;
            if (obj.offsetParent)
            {
                do
                {
                    // add all other inline (static) parent elements, but not block
                    if (df_get_current_style(obj, 'position') == 'static')
                    {
                        this.contentOffsetLeft += obj.offsetLeft;
                        this.sliderWrapperTop += obj.offsetTop;
                    }
                    // Whilst screen pixels don't care about 'static' elements
                    this.sliderYOffset += obj.offsetTop;
                }
                while (obj = obj.offsetParent)
            }
                
            // Don't let system enable itself through events until ready,
            // else can be drawn before offset calculations done
            this.initialised=true;
            // this will kick-start the timer and enable if/when needed
            this.handleTimeout();

            // Some sanity checks
            if (this.contentDiv.clientHeight <= this.pageOverlap)
            {
                // Page Up/Page Down key has overlap to give reader
                // "confidence", except when div too small
                this.pageOverlap = 0;
            }

            if (this.makeFocus)
            {
                this.contentDiv.focus();
            }

            return true;
        }
        else
        {
            return false;
        }

    };

    this.handleMousewheel = function(event)
    {
        var delta=0;
        
        if (this.enableState)
        {
            if (!event)
            {
                event = window.event;
            }
            if (event.wheelDelta) 
            { 
                // IE and Opera case, seems to be massive 
                // values for delta. "Normalise"
                delta = event.wheelDelta/30;
            
                // In Opera, mouse-wheel is negative
                // - strangely not anymore. May need further investigation
                //   for full compatibility with all releases of opera...
                //: mystery explained. Opera has both event.detail and event.wheelDelta
                if (window.opera)
                {
                    // delta = -delta;
                }
            } 
            else if (event.detail) 
            { 
                // Mozilla etc, again negative, like Opera
                delta = -event.detail;
            }
            
            if (delta)
            {
                this.recalc();
                this.scrollPosY -= (delta*this.mouseWheelSpeed);
                this.redraw();
            }

            if (event.preventDefault)
            {
                event.preventDefault();
            }
            
            // Prevent parent frames receiving this event
            event.returnValue = false;
        }
    };

    this.handleSelect = function(event)
    {  
        if (this.enableState)
        {
            if (!event)
            {
                event = window.event;
            }
        

            if (this.sliderMouseDown)
            {
                // prevent select operations interacting
                if (event.preventDefault)
                {
                    event.preventDefault();
                }
            
                // Prevent parent frames receiving this event
                event.returnValue = false;
            }
        }
    }

    this.handleMouse = function(event)
    { 
        var is_handled   = true;
 
        if (this.enableState)
        {
            if (!event)
            {
                event = window.event;
            }
        
            switch(event.type)
            {
                case 'mousedown':
                    this.mouseTamer=0;
                    this.sliderMouseDown = true;
                    break;

                case 'mouseup':
                case 'blur':
                    this.mouseTamer=0;
                this.sliderMouseDown = false;
                break;

                case 'mousemove':
                    if ((this.mouseTamer%4)!=0)
                    {
                        // Be nice to processor.
                        // act on only one in every 4 events
                        is_handled=0;
                    }
                    this.mouseTamer++;
                    break;
            }

            if (this.sliderMouseDown && is_handled)
            {
                var mouse_x  = 0;
                var mouse_y  = 0;
                var slider_y = 0;
            
                if (event.clientX && event.clientY)
                {
                    mouse_y = event.clientY;
                }
                else
                {
                    mouse_y = event.clientY;
                }

                (mouse_y+document.documentElement.scrollTop-this.sliderYOffset)

                    slider_y = (mouse_y+document.documentElement.scrollTop-this.sliderYOffset);

                if (slider_y > this.sliderDiv.clientHeight)
                {
                    slider_y = this.sliderDiv.clientHeight;              
                }
            
                // Remove top offset padding
                slider_y -= this.sliderYPadT;

                // Remove half-height of slider itself to get to centre-point
                slider_y -= this.sliderElem.clientHeight/2;
            
                if (slider_y < 0)
                {
                    slider_y = 0;
                }

                this.scrollPosY = ((this.contentDiv.scrollHeight-this.contentDiv.clientHeight)*slider_y)/(this.sliderDiv.clientHeight-this.sliderYPadT-(this.sliderElem.clientHeight)-this.sliderYPadB);
                this.redraw();
            }

            if (this.sliderMouseDown)
            {
                if (event.preventDefault)
                {
                    event.preventDefault();
                }
            
                // Prevent parent frames receiving this event
                event.returnValue = false;
            }
        }
    }
        
    this.handleKeypress = function(event)
    { 
        var keychar;
        var is_handled = true;

        if (this.enableState)
        {
            // Exception - do not steel keystrokes if a text area
            // or input is active
            if (this.inputModeActive)
            {
                return;
            }

            if (!event)
            {
                event = window.event;
            }
        
            keychar = (event.which) ? event.which : event.keyCode;

            switch(event.type)
            {
                case 'keydown':
                    this.recalc();
                    switch(keychar)
                    {
                        case 33:
                            // PG UP
                            this.scrollPosY -= (this.contentDiv.clientHeight-this.pageOverlap);
                            break;

                        case 34:
                            // PG Down
                            this.scrollPosY += (this.contentDiv.clientHeight-this.pageOverlap);
                            break;

                        case 35:
                            // END
                            this.scrollPosY = this.contentDiv.scrollHeight;
                            break;


                        case 36:
                            // HOME
                            this.scrollPosY = 0;
                            break;


                        case 38:
                            // Up-arrow
                            this.scrollPosY -= 10;
                            break;

                        case 40:
                            // Down-arrow
                            this.scrollPosY += 10;
                            break;

                        default:
                            is_handled = false;
                    }
                    break;
            }

            // Prevent parent frames receiving this event
            // - but only if the event has been handled
            if (is_handled)
            {
                this.redraw();
                
                if (event.preventDefault)
                {
                    event.preventDefault();
                }
                event.returnValue = false;
            }
        }        
    };


    this.handleTimeout = function()
    {
        var cb_obj = this;

        cb_obj.recalc();
        
        setTimeout(function(e){cb_obj.handleTimeout();}, 100);        
    }
    

    this.recalc = function()
    {
        // Slider position calculation is pretty simple, 
        // but need to take into account hieght of slider itself
        if (this.contentDiv.scrollHeight <= this.contentDiv.clientHeight)
        {
            // Disable Scroll Bar
            this.disable();
        }
        else
        {
            // Enable slider
            this.enable();
        }

        if (this.enableState)
        {
            if (this.scrollPosY != this.contentDiv.scrollTop)
            {
                this.scrollPosY = this.contentDiv.scrollTop;
            }
        }
        
        
        return true;
    };

    this.disable = function()       
    {
        if (this.enableState == true)
        {
            this.removeSlider();
            // Do disabling action
        }

        this.enableState = false;
    };

    this.enable = function()        
    {
        // Don't let system enable itself through events until ready,
        // else can be drawn before offset calculations done
        if (this.initialised)
        {
            if (this.enableState == false)
            {
                this.createSlider();
                // Do enabling action
            }
            
            this.enableState = true;
        }
    };
    

    this.handleFocusBlur = function(event)
    {
        if (!event)
        {
            event = window.event;
        }
            
        switch (event.type)
        {
            case 'focus':
            this.inputModeActive = true;
            break;
        
            case 'blur':
            this.inputModeActive = false;
            break;                
        }
    };
    
    this.redraw = function()
    {
        this.contentDiv.scrollTop = this.scrollPosY;
         
        if (this.enableState)
        {
            // Slider position calculation is pretty simple, 
            // but need to take into account hieght of slider itself
            if (this.contentDiv.scrollHeight <= this.contentDiv.clientHeight)
            {
                this.sliderElem.style.marginTop = 0; 
            }
            else
            {            
                this.sliderElem.style.marginTop =  (this.sliderYPadT+(((this.sliderDiv.clientHeight-(this.sliderElem.offsetHeight+this.sliderYPadT+this.sliderYPadB))*this.contentDiv.scrollTop)/(this.contentDiv.scrollHeight-this.contentDiv.clientHeight))) + 'px';
            }
        }
    };

    this.clickUp = function(clicks)
    {
        if (this.enableState)
        {
            this.recalc();
            this.scrollPosY -= (this.contentDiv.clientHeight-this.pageOverlap)*clicks/4;
            this.redraw()
        }
    }


    this.clickDown = function(clicks)
    {
        if (this.enableState)
        {
            this.recalc();
            this.scrollPosY += (this.contentDiv.clientHeight-this.pageOverlap)*clicks/4;
            this.redraw()
        }
    }


    this.createSlider = function()
    {
        var cb_obj = this;
        

        // Create ScrollBar division
        if (this.sliderWrapperDiv == null)
        {
            this.sliderWrapperDiv = document.createElement('div');
            this.wrapperDiv.appendChild(this.sliderWrapperDiv);

            this.sliderWrapperDiv.style.top=this.sliderWrapperTop+'px';
            
            this.sliderWrapperDiv.style.position='absolute';

            this.sliderWrapperDiv.style.backgroundColor='';
            this.sliderWrapperDiv.style.width=this.sbarWidth+'px';

            this.sliderWrapperDiv.style.textAlign = 'center';
            
            this.sliderWrapperDiv.style.height=this.contentDiv.clientHeight + 'px';
            this.sliderWrapperDiv.style.left=(this.contentOffsetLeft + this.origContentWidth - this.sbarWidth) + 'px';
        }
        // Set-up scroll bar

        
        // These next few sections are order-critical.
        // Create elements first in oder to access image heights/sizes
        // but inserting them into DOM tree later
        var insert_sti = false;
        var insert_sbi = false;
        var insert_div = false;
        
        if (this.sliderTopSrc && (this.sliderTopElem == null))
        {
            insert_sti = true;
            
            this.sliderTopElem           = document.createElement('img');
            this.sliderTopElem.src       = this.sliderTopSrc;

        }


        if (this.sliderBottomSrc && (this.sliderBottomElem == null))
        {
            insert_sbi = true;

            this.sliderBottomElem           = document.createElement('img');
            this.sliderBottomElem.src       = this.sliderBottomSrc;

        }

        // Create ScrollBar division
        if (this.sliderDiv == null)
        {
            insert_div = true;
            
            this.sliderDiv                       = document.createElement('div');                
            this.sliderDiv.style.position        = 'relative';
            this.sliderDiv.style.backgroundColor = '';
            this.sliderDiv.style.width           = this.sbarWidth+'px';
            this.sliderDiv.style.textAlign       = 'center';
            this.sliderDiv.style.left            = '0';
            
        }

        switch (this.sliderButtonLoc)
        {
            case 'tt': // top, top: both buttons above slider
                if (insert_sti)
                {
                    this.sliderWrapperDiv.appendChild(this.sliderTopElem);

                    // Fix MS bug, can only set class names AFTER inserting
                    this.sliderTopElem.className    = 'DFScrollSliderTopImg';
                    
                    // Can only calc client height AFTER inserting
                    this.sliderYOffset += this.sliderTopElem.clientHeight;

                }
                if (insert_sbi)
                {
                    this.sliderWrapperDiv.appendChild(this.sliderBottomElem);

                    // Fix MS bug, can only set class names AFTER inserting
                    this.sliderBottomElem.className = 'DFScrollSliderBottomImg';

                    // Can only calc client height AFTER inserting
                    this.sliderYOffset += this.sliderBottomElem.clientHeight;
                } 
                if (insert_div)
                {
                    this.sliderWrapperDiv.appendChild(this.sliderDiv);
                }
                break;
            case 'tb': // top, bottom: one button either side of slider
                if (insert_sti)
                {
                    this.sliderWrapperDiv.appendChild(this.sliderTopElem);

                    // Fix MS bug, can only set class names AFTER inserting
                    this.sliderTopElem.className    = 'DFScrollSliderTopImg';

                    // Can only calc client height AFTER inserting
                    this.sliderYOffset += this.sliderBottomElem.clientHeight;
                }
                if (insert_div)
                {
                    this.sliderWrapperDiv.appendChild(this.sliderDiv);
                }
                if (insert_sbi)
                {
                    this.sliderWrapperDiv.appendChild(this.sliderBottomElem);

                    // Fix MS bug, can only set class names AFTER inserting
                    this.sliderBottomElem.className = 'DFScrollSliderBottomImg';
                }   
                break;
            case 'bb': // bottom, bottom: both buttons below slider
                if (insert_div)
                {
                    this.sliderWrapperDiv.appendChild(this.sliderDiv);
                }
                if (insert_sti)
                {
                    this.sliderWrapperDiv.appendChild(this.sliderTopElem);
                    // Fix MS bug, can only set class names AFTER inserting
                    this.sliderTopElem.className    = 'DFScrollSliderTopImg';
                }
                if (insert_sbi)
                {
                    this.sliderWrapperDiv.appendChild(this.sliderBottomElem);
                    // Fix MS bug, can only set class names AFTER inserting
                    this.sliderBottomElem.className = 'DFScrollSliderBottomImg';
                }   
                break;
        }

        this.sliderElem = document.createElement(this.sliderElemType);


        switch (this.sliderElemType)
        {
            case 'img':
                this.sliderElem.src=this.sliderElemSrc;
                break;

            case 'div':
                this.sliderElem.style.height = this.sliderElemDefaultHeight+'px';
                this.sliderElem.style.width  = '80%';
                this.sliderElem.style.left   = '10%';
                this.sliderElem.style.backgroundColor='#000';
                break;
        }


        if (insert_div)
        {
            var slider_height = this.contentDiv.clientHeight;
            
            if (this.sliderTopElem)
            {
                slider_height -= this.sliderTopElem.clientHeight;
            }

            if (this.sliderBottomElem)
            {
                slider_height -= this.sliderBottomElem.clientHeight;
            }

            this.sliderDiv.style.height= slider_height + 'px';
        }

        // this.sliderElem.style.position = 'absolute';
        this.sliderElem.style.textAlign = 'center';


        this.sliderElem.style.marginTop = this.sliderYPadT+'px';

        this.sliderDiv.appendChild(this.sliderElem);

	// DO this before attaching events but after insertion 
	// into DOM tree.  In theory should be more efficient to 
	// do before, except for IE bug needs the img to be rendered
	// ordinarily, therefore inserted as IMG, else it might not load.
	this.sliderElem = this.renderFixMsPng(this.sliderElem);
        if (insert_sti)
        {
	    this.sliderTopElem = this.renderFixMsPng(this.sliderTopElem);
            df_attach_event(this.sliderTopElem, 'click', function(e){cb_obj.clickUp(1)});
            df_attach_event(this.sliderTopElem, 'doubleclick', function(e){cb_obj.clickUp(2)});
        }
        if (insert_sbi)
        {
	    this.sliderBottomElem = this.renderFixMsPng(this.sliderBottomElem);
            df_attach_event(this.sliderBottomElem, 'click', function(e){cb_obj.clickDown(1)});
            df_attach_event(this.sliderBottomElem, 'doubleclick', function(e){cb_obj.clickDown(2)});
        }   



        
        df_attach_event(document,       'selectstart',  function(e){cb_obj.handleSelect(e)});
        df_attach_event(this.sliderDiv, 'click',        function(e){cb_obj.handleMouse(e)});
        df_attach_event(this.sliderDiv, 'mousedown',    function(e){cb_obj.handleMouse(e)});
        df_attach_event(document,       'mousemove',    function(e){cb_obj.handleMouse(e)});
        df_attach_event(this.sliderDiv, 'blur',         function(e){cb_obj.handleMouse(e)});
        df_attach_event(document,       'blur',         function(e){cb_obj.handleMouse(e)});
        df_attach_event(document,       'mouseup',      function(e){cb_obj.handleMouse(e)});
        //df_attach_event(this.sliderDiv, 'mouseout',     function(e){cb_obj.handleMouse(e)});
        df_attach_event(this.sliderElem, 'selectstart', function(e){cb_obj.handleMouse(e)});
        df_attach_event(this.sliderElem, 'click',       function(e){cb_obj.handleMouse(e)});
        df_attach_event(this.sliderElem, 'mousedown',   function(e){cb_obj.handleMouse(e)});
        df_attach_event(this.sliderElem, 'mousemove',   function(e){cb_obj.handleMouse(e)});

        df_attach_event(this.sliderDiv, 'keydown',        function(e){cb_obj.handleKeypress(e)});        
        df_attach_event(this.sliderDiv, 'mousewheel',     function(e){cb_obj.handleMousewheel(e)});
        df_attach_event(this.sliderDiv, 'DOMMouseScroll', function(e){cb_obj.handleMousewheel(e)});



  };

    this.removeSlider = function()
    {
	if (this.sliderElem != null)
        {	
            this.sliderDiv.removeChild(this.sliderElem);
            this.sliderElem = null;
        }

        if (this.sliderDiv != null)
        {
            this.sliderWrapperDiv.removeChild(this.sliderDiv);
            this.sliderDiv = null;
        }

        if (this.sliderTopElem != null)
        {
            this.sliderWrapperDiv.removeChild(this.sliderTopElem);
            this.sliderTopElem = null;
        }


        if (this.sliderBottomElem != null)
        {
            this.sliderWrapperDiv.removeChild(this.sliderBottomElem);
            this.sliderBottomElem = null;
        }

        if (this.sliderWrapperDiv != null)
        {
            this.wrapperDiv.removeChild(this.sliderWrapperDiv);
            this.sliderWrapperDiv = null;
        }
      
    };


    this.renderFixMsPng = function(img_elem)
    {
        if  ((img_elem.src.substring(img_elem.src.length-4) == ".png") &&
             (navigator.appName == "Microsoft Internet Explorer"))
        {
            // IE can't display transparent PNGs and IE7 is shocking
            // when rendering them. So we use AlphaImageLoader filter, much better
            // (but not for IE8, which works fine for PNGs and has problems on 
            //  first load for AlphaImageLoader)
            var ie_version = parseFloat(navigator.appVersion.split("MSIE")[1]);
            if ((ie_version >= 5.5) && (ie_version < 8) && (document.body.filters)) 
            {
                var ie_div = document.createElement("div");
	        var str;
	
		for(var prop in img_elem)
            	{
                    str = "ie_div." + prop + " = img_elem." + prop + ";";
		    
		    try
		    {
                        eval(str);
                    }
                    catch(e)
                    {
                        // do nothing
                    }
    		}
		
                ie_div.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + img_elem.src + "')";
                ie_div.style.width  = img_elem.width  + "px";
                ie_div.style.height = img_elem.height + "px";
                ie_div.style.visibility = "visible";

                ie_div.style.top  = img_elem.offsetTop + "px";
                ie_div.style.left = img_elem.offsetLeft + "px";

	
		img_elem.replaceNode(ie_div);
                img_elem = ie_div;
            }
        }
        return img_elem;
    }

}





function DFScroll()
{
    this.scrollClassName = "DFScroll";
    this.sliderSrc   = false;
    this.sliderTopSrc    = false;
    this.sliderButtonLoc = 'tb'; // tt: top, top - both buttons above slider
                                 // tb: top, bottom - one button either side of slider
                                 // bb: bottom, bottom - both buttons below slider
    this.sliderBottomSrc = false;
    this.barWidth  = 20;
    this.sbObjs = new Array();

    this.makeFocus = false;

    this.init = function()
        {
            var all_divs = document.getElementsByTagName('div');
            var i;
            var elem;
            var sb_obj = null;

            for (i=0; elem=all_divs[i]; i++)
            {  
                if (elem.className==this.scrollClassName+'Wrapper')
                {
                    // Always have to get to wrapper before content
                    if (sb_obj != null)
                    {
                        // Save the last one
                        this.sbObjs.push(sb_obj);
                        sb_obj = null;
                    }

                    sb_obj = new DFScrollBar();
                    sb_obj.wrapperDiv = elem;
                }
                else if (elem.className==this.scrollClassName+'Content')
                {
                    sb_obj.contentDiv = elem;
                }
            }

            if (sb_obj != null)
            {
                this.sbObjs.push(sb_obj);
            }

            for (i=0; sb_obj=this.sbObjs[i]; i++)
            {
                // Set parameters
                if (this.sliderSrc != false)
                {
                    sb_obj.setSliderImgSrc(this.sliderSrc);
                }
                sb_obj.sliderTopSrc    = this.sliderTopSrc;
                sb_obj.sliderBottomSrc = this.sliderBottomSrc;

                sb_obj.sliderButtonLoc = this.sliderButtonLoc;

                sb_obj.sbarWidth = parseInt(this.barWidth);

                // If needed, focus the first instance
                if ((i==0) && this.makeFocus)
                {
                    sb_obj.makeFocus = true;
                }

                sb_obj.init();
            }
        };
}



