var scrollingTypes = ['horizontal', 'vertical'];
function ScrollableArea(areaIdentifier, scrollingType, objectToAssociate) {
    
    /* Define elements that are the structure
    of a scrollable area. */
    var elements = {
        container: null,
        content: null,
        scrollbar: null,
        scroller: null,
        overlay: null,
        scrollbarArrows: { first: null, second: null }
    };
    
    
/* Beginning of getting elements. */
    
    (function () {
        
        elements.container = document.getElementById(areaIdentifier);
        elements.content = elements.container.getElementsByTagName('ul')[0];    // always the first unordered list in area's child elements
        
        var containerNestedDivisions = elements.container.getElementsByTagName('div');
        elements.scrollbar = containerNestedDivisions[containerNestedDivisions.length - 3];
        elements.scroller = containerNestedDivisions[containerNestedDivisions.length - 2];
        
        var containerNestedButtons = elements.container.getElementsByTagName('button');
        for (var i = 0; i < containerNestedButtons.length; i++) {
            if (containerNestedButtons[i].className.search('scroller-overlay') != -1) {
                elements.overlay = containerNestedButtons[i];
                break;
            }
        }
        
        var containerNestedAnchors = elements.container.getElementsByTagName('a');
        for (var i = 0; i < containerNestedAnchors.length; i++) {
            var anchor = containerNestedAnchors[i];
            if (anchor.className.search('scrollbar-arrow') != -1) {
                if (anchor.className.search('left') != -1 || anchor.className.search('top') != -1) {
                    elements.scrollbarArrows.first = anchor;
                }
                else elements.scrollbarArrows.second = anchor;
            }
        }
        
    })();
    
/* End of getting elements. */
    
    
/* Beginning of calculating container and its content widths/heights. */

    var containerLength = null;
    var contentLength = null;
    (function () {

        switch (scrollingType) {
            case 'horizontal':
                containerLength = elements.container.offsetWidth;
                contentLength = elements.content.offsetWidth;
                break;
            case 'vertical':
                containerLength = elements.container.offsetHeight;
                contentLength = elements.content.offsetHeight;
                break;
            default:
                return false;
        }
        
    })();
    
/* End of calculating container and its content widths/heights. */


    // Do nothing if the content area doesn't overflows
    // its container client area.
    if (contentLength <= containerLength)
    {
        elements.scroller.style.display = 'none';
        
        return false;
    }
        
        
    if (scrollingType == 'vertical')
    {
        (function () {
            var scrollerOffset = parseFloat(getElementComputedStyle(elements.scroller, 'top'));
            if (scrollerOffset > 0)
            {
                elements.scroller.style.top = '0';
                elements.content.style.bottom = '0';
            }
        })();
    }
    
    
/* Beginning of calculating and setting up scroller width/height. */

    var contentSizeRatio = null;
    (function () {
        
        var contentLengthRatio = containerLength / contentLength;
        var scrollerLength = null;
        switch (scrollingType) {
            case 'horizontal':
                scrollerLength = contentLengthRatio * elements.scrollbar.offsetWidth;
                elements.scroller.style.width = scrollerLength + 'px';
                contentSizeRatio = contentLength / elements.scrollbar.offsetWidth;
                break;
            case 'vertical':
                scrollerLength = contentLengthRatio * elements.scrollbar.offsetHeight;    
                elements.scroller.style.height = scrollerLength + 'px';
                contentSizeRatio = contentLength / elements.scrollbar.offsetHeight;
                break;
        }
        
    })();
    
/* End of calculating and setting up scroller width/height. */


    // Display the scroller.
    elements.scroller.style.display = 'block';
    
    
    var scrollerPosition = 0;
    var contentPosition = 0;
    function doScrolling(delta, returningValues) {
        
        // Delta X must be a numeric value
        // greater than or less than zero.
        if (isNaN(new Number(delta)) || delta == 0)
            return false;
        
        if (delta > 0) { // right scrolling
            
            // Do nothing if scroller is fully scrolled to the right.
            var maximalScrollerPosition = null;
            switch (scrollingType) {
                case 'horizontal':
                    maximalScrollerPosition = elements.scrollbar.offsetWidth - elements.scroller.offsetWidth;
                    break;
                case 'vertical':
                    maximalScrollerPosition = elements.scrollbar.offsetHeight - elements.scroller.offsetHeight;
                    break;
            }
            if (scrollerPosition == maximalScrollerPosition)
                return false;
                
            
            // Current scroller offset X plus its width and the specified delta X.
            var aggregatedscrollerPosition = scrollerPosition + delta;
            
            if (aggregatedscrollerPosition > maximalScrollerPosition) {   
            // When a new scroller position raises its maximal right position.
                
                // Content will be fully scrolled to the right.
                switch (scrollingType) {
                    case 'horizontal':
                        scrollerPosition = elements.scrollbar.offsetWidth - elements.scroller.offsetWidth;
                        contentPosition = elements.content.offsetWidth - elements.container.offsetWidth;
                        break;
                    case 'vertical':
                        scrollerPosition = elements.scrollbar.offsetHeight - elements.scroller.offsetHeight;
                        contentPosition = elements.content.offsetHeight - elements.container.offsetHeight;
                        break;
                }
                
                // Calculate the excess.
                if (returningValues instanceof Array)
                    returningValues['excess'] = delta - (aggregatedscrollerPosition - maximalScrollerPosition);
            }
            else {
            // Common case.
                
                // Calculate new positions of scroller and scrolling content.
                scrollerPosition += delta;
                contentPosition += delta * contentSizeRatio;
                
                // It's not an excess in a common case.
                if (returningValues instanceof Array)
                    returningValues['excess'] = 0;
            }
        }
        else if (delta < 0) {  // left scrolling
            
            // Do nothing if scroller is fully scrolled to the left.
            if (scrollerPosition == 0)
                return false;
            
            
            if ((scrollerPosition + delta) < 0) {
            // When a new scroller position raises its maximal left position.
                
                // Calculate the excess.
                if (returningValues instanceof Array)
                    returningValues['excess'] = scrollerPosition + delta;
            
                // Scroller position is setting to the maximal left position.
                scrollerPosition = 0;
                
                // Content will be fully scrolled to the right.
                contentPosition = 0;
            }
            else {
            // Common case.
            
                // Calculate new positions of scroller and scrolling content.
                scrollerPosition += delta;
                
                // Set content position to the initial state
                // instead calculating a new state
                // if scroller is fully scrolled to the left.
                if (scrollerPosition != 0) {
                    contentPosition += delta * contentSizeRatio;
                }
                else contentPosition = 0;
                
                // It's not an excess in a common case.
                if (returningValues instanceof Array)
                    returningValues['excess'] = 0;
            }
        }
        
        
        // Do scrolling by setting a new calculated positions
        // of scroller and scrolling content.
        switch (scrollingType) {
            case 'horizontal':
                elements.scroller.style.left = scrollerPosition + 'px';
                elements.content.style.right = contentPosition + 'px';
                break;
            case 'vertical':
                elements.scroller.style.top = scrollerPosition + 'px';
                elements.content.style.bottom = contentPosition + 'px';
                break;
        }
        
        // Scrolling was successful.
        return true;
        
    }   // end of function doScrolling(delta, returningValues)
    
    
    elements.overlay.onmousedown = function (event) {
        
        // Get current event object.
        if (event == undefined) event = window.event;

        var getScrollOffset = null;
        switch (scrollingType) {
            case 'horizontal':
                getScrollOffset = function getScrollOffset() {
                    var htmlElement = document.getElementsByTagName('html')[0];
                    return htmlElement.scrollLeft;
                };
                break;
            case 'vertical':
                getScrollOffset = function getScrollOffset() {
                    var htmlElement = document.getElementsByTagName('html')[0];
                    return htmlElement.scrollTop;
                };
                break;
        }
        
        function isInternetExplorer() { return (document.all != undefined); }
        
        // Calculate client X cursor position considering document scrolling state.
        var cursorPosition = null;
        switch (scrollingType) {
            case 'horizontal':
                if (isInternetExplorer()) cursorPosition = event.clientX + getScrollOffset();
                else cursorPosition = event.pageX;
                break;
            case 'vertical':
                if (isInternetExplorer()) cursorPosition = event.clientY + getScrollOffset();
                else cursorPosition = event.pageY;
                break;
        }
        
        
        // Define is the cursor over the scroller. Do nothing if it's not.
        var minimalPosition = null;
        var maximalPosition = null;
        switch (scrollingType) {
            case 'horizontal':
                minimalPosition = elements.scroller.offsetLeft;
                maximalPosition = minimalPosition + elements.scroller.offsetWidth;
                if (navigator.appVersion.search("MSIE 7") != -1)
                {
                    minimalPosition += elements.container.offsetLeft;
                    maximalPosition += elements.container.offsetLeft;
                }
                break;
            case 'vertical':
                minimalPosition = elements.scroller.offsetTop;
                maximalPosition = minimalPosition + elements.scroller.offsetHeight;
                if (navigator.appVersion.search("MSIE 7") != -1)
                {
                    minimalPosition += elements.container.offsetTop;
                    maximalPosition += elements.container.offsetTop;
                }
                break;
        }
        if (cursorPosition < minimalPosition || cursorPosition > maximalPosition)
            return;
        
        var initialCursorPosition = cursorPosition;
        
        
        document.onmouseup = function () {
            
            document.onmousemove = null;
            document.onmouseup = null;
        };
    
        
        document.onmousemove = function (event) {
            
            // Get current event object.
            if (event == undefined) event = window.event;
            
            // Calculate client X cursor position considering document scrolling state.
            var cursorPosition = null;
            switch (scrollingType) {
                case 'horizontal':
                    if (isInternetExplorer()) cursorPosition = event.clientX + getScrollOffset();
                    else cursorPosition = event.pageX;
                    break;
                case 'vertical':
                    if (isInternetExplorer()) cursorPosition = event.clientY + getScrollOffset();
                    else cursorPosition = event.pageY;
                    break;
            }
            
            var delta = cursorPosition - initialCursorPosition;
            var returningValues = new Array();
            if (doScrolling(delta, returningValues) == true) {
                
                if ((returningValues['excess'] != undefined) && (returningValues['excess'] != 0)) {
                    if (returningValues['excess'] > 0)
                        initialCursorPosition += delta - (delta - returningValues['excess']);
                    else if (returningValues['excess'] < 0)
                        initialCursorPosition += delta - returningValues['excess'];
                }
                else initialCursorPosition = cursorPosition;
            }
        };
        
    };   // end of elements.overlay.onmousedown = function (event)
    
    
    var scrollingLength = 20;
    
    
    function isContentOwerflowsContainer()
    {
        if (scrollingType != 'vertical')
        {
            return true;
        }
         
        var containerLength = elements.container.offsetHeight;
        var contentLength = elements.content.offsetHeight;
        
        if (contentLength > containerLength)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    
    
/* Beginning binding scrolling actions with mouse wheel events. */

    (function () {
        
        function handler(event) {
            
            if ( ! isContentOwerflowsContainer())
            {
                return;
            }
            
            // Get the mouse wheel event parameter that defines
            // scrolling direction (up or down).
            var delta = 0;
            if (!event) event = window.event;
            if (event.wheelDelta) delta = event.wheelDelta; // Internet-Explorer, Opera, Safari, Chrome
            else if (event.detail) delta = -event.detail;   // Mozilla Firefox
            
            // If the delta is positive (greater than zero)
            // the user scrolled the mouse wheel up; otherwise,
            // when the delta is negative, the user scrolled
            // the mouse wheel down.
            if (delta > 0) (function() { doScrolling(-scrollingLength); })();
            else if (delta < 0) (function() { doScrolling(scrollingLength); })();
            
            // Disable the default action (window scrolling).
            if (event.preventDefault) event.preventDefault();
            event.returnValue = false;  // for Internet-Explorer
                
        }
        
        if (elements.container.addEventListener != undefined)    // Mozilla Firefox, Safari, Chrome
            elements.container.addEventListener('DOMMouseScroll', handler, false);
        // Internet-Explorer, Opera
        elements.container.onmousewheel = handler;
        
    })();
    
/* End of binding scrolling actions with mouse wheel events. */


/* Beginning of setting scrolling actions for scrollbar arrows. */

    (function initializeScrollbarArrows() {
        
        // Do nothing if scrollbar arrows are not set
        // for the current container.
        if (elements.scrollbarArrows.first == null ||
            elements.scrollbarArrows.second == null ) { return; }
        
        
    /* Beginning of parameters and functions to organize
    scrollling loop if the user holds mouse button pressed. */
        
        var scrollingLoopStartTimeoutId = null;
        var scrollingLoopStartDelay = 500;
        
        var scrollingLoopIntervalId = null;
        var scrollingDelay = 50;
        
        function beginScrollingLoop(oneScrollingDelta) {
            
            scrollingLoopStartTimeoutId = setTimeout(
                
                function () {
                    
                    scrollingLoopStartTimeoutId = null;
                    
                    scrollingLoopIntervalId = setInterval(
                        function () { doScrolling(oneScrollingDelta); },
                        scrollingDelay
                    );
                    
                },
                scrollingLoopStartDelay
                  
            );
            
        }   // end of function beginScrollingLoop(oneScrollingDelta)
        
        function endScrollingLoop() {
            
            if (scrollingLoopStartTimeoutId != null) {
                clearTimeout(scrollingLoopStartTimeoutId);
                scrollingLoopStartTimeoutId = null;
            }
            else {
                clearInterval(scrollingLoopIntervalId);
                scrollingLoopIntervalId = null;
            }
            
        }   // end of function endScrollingLoop()
        
    /* End of parameters and functions to organize
    scrollling loop if the user holds mouse button pressed. */
        
        
        // Set scrolling actions for the first arrow.
        
        elements.scrollbarArrows.first.onmousedown = function () {
            
            if ( ! isContentOwerflowsContainer())
            {
                return;
            }
            
            var delta = -scrollingLength;
            
            doScrolling(delta);
            beginScrollingLoop(delta);
            
        }   // end of elements.scrollbarArrows.first.onmousedown = function ()
        
        elements.scrollbarArrows.first.onmouseup = function () { endScrollingLoop(); }
        elements.scrollbarArrows.first.onmouseout = function () { endScrollingLoop(); }
        
        
        // Set scrolling actions for the second arrow.
        
        elements.scrollbarArrows.second.onmousedown = function () {
            
            if ( ! isContentOwerflowsContainer())
            {
                return;
            }
            
            var delta = scrollingLength;
            
            doScrolling(delta);
            beginScrollingLoop(delta);
            
        }   // end of elements.scrollbarArrows.first.onmousedown = function ()
        
        elements.scrollbarArrows.second.onmouseup = function () { endScrollingLoop(); }
        elements.scrollbarArrows.second.onmouseout = function () { endScrollingLoop(); }
        
    })();   // end of function initializeScrollbarArrows()

/* End of setting scrolling actions for scrollbar arrows. */


    if (objectToAssociate != null)
    {
        function doAbsoluteScrolling(offset) {
            var delta = offset / contentSizeRatio;
            return doScrolling(delta);
        }
        
        objectToAssociate.slideLeft = function slideLeft(offset) {
            return doAbsoluteScrolling(-offset);
        };
        
        objectToAssociate.slideRight = function slideRight(offset) {
            return doAbsoluteScrolling(offset);
        };
    }

    return true;    // a scrollable area was succussfully created
    
}   // end of function ScrollableArea(areaIdentifier, scrollingType)
