/** j hat geändert
* HoverScroll jQuery Plugin
*
* Make an unordered list scrollable by hovering the mouse over it
*
* @author RasCarlito <carl.ogren@gmail.com>
* @version 0.2.4
* @revision 21
*
* 
*
* FREE BEER LICENSE VERSION 1.02
*
* The free beer license is a license to give free software to you and free
* beer (in)to the author(s).
* 
*
* Released: 09-12-2010 11:31pm
*
* Changelog
* ----------------------------------------------------
*
* 0.2.4    - Added "Right to Left" option, only works with horizontal lists
*          - Optimization of arrows opacity control (Thanks to Josef Körner)
*
* 0.2.3    - Added fixed arrows option
*          - Binded startMoving and stopMoving functions to the HoverScrolls HTML object for external access
*
* 0.2.2    Bug fixes
*          - Backward compatibility with jQuery 1.1.x
*          - Added test file to the archive
*          - Bug fix: The right arrow appeared when it wasn't necessary (thanks to <admin at unix dot am>)
*        
* 0.2.1    Bug fixes
*          - Backward compatibility with jQuery 1.2.x (thanks to Andy Mull for compatibility with jQuery >= 1.2.4)
*          - Added information to the debug log
* 
* 0.2.0    Added some new features
*          - Direction indicator arrows
*          - Permanent override of default parameters
* 
* 0.1.1    Minor bug fix
*          - Hover zones did not cover the complete container
*
*          note: The css file has not changed therefore it is still versioned 0.1.0
*
* 0.1.0    First release of the plugin. Supports:
*          - Horizontal and vertical lists
*          - Width and height control
*          - Debug log (doesn't show useful information for the moment)
*/

(function ($) {

    /**
    * @method hoverscroll
    * @param	params {Object}  Parameter list
    * 	params = {
    * 		vertical {Boolean},	// Vertical list or not ?
    * 		width {Integer},	// Width of list container
    * 		height {Integer},	// Height of list container
    *  	arrows {Boolean},	// Show direction indicators or not
    *  	arrowsOpacity {Float},	// Arrows maximum opacity
    *  	fixedArrows {Boolean},  // Display arrows permenantly, this overrides arrowsOpacity option
    * 		debug {Boolean}		// Debug output in firebug console
    * 	};
    */
    $.fn.hoverscroll = function (params) {
        if (!params) { params = {}; }

        // Extend default parameters
        // note: empty object to prevent params object from overriding default params object
        params = $.extend({}, $.fn.hoverscroll.params, params);

        // Loop through all the elements
        this.each(function () {
            var $this = $(this);

            if (params.debug) {
                $.log('[HoverScroll] Trying to create hoverscroll on element ' + this.tagName + '#' + this.id);
            }

            // wrap ul list with a div.listcontainer
            if (params.fixedArrows) {
                $this.wrap('<div class="fixed-listcontainer"></div>')
            }
            else {
                $this.wrap('<div class="listcontainer"></div>');
            }

            $this.addClass('list');
            //.addClass('ui-helper-clearfix');

            // store handle to listcontainer
            var listctnr = $this.parent();

            // wrap listcontainer with a div.hoverscroll
            listctnr.wrap('<div class="ui-widget-content hoverscroll' +
			(params.rtl && !params.vertical ? " rtl" : "") + '"></div>');
            //listctnr.wrap('<div class="hoverscroll"></div>');

            // store hoverscroll container
            var ctnr = listctnr.parent();

            var leftArrow, rightArrow, topArrow, bottomArrow;

            // Add arrow containers
            if (params.arrows) {
                if (!params.vertical) {
                    if (params.fixedArrows) {
                        leftArrow = '<div class="fixed-arrow left"></div>';
                        rightArrow = '<div class="fixed-arrow right"></div>';

                        listctnr.before(leftArrow).after(rightArrow);
                    }
                    else {
                        leftArrow = '<div class="arrow left"></div>';
                        rightArrow = '<div class="arrow right"></div>';

                        listctnr.append(leftArrow).append(rightArrow);
                    }
                }
                else {
                    if (params.fixedArrows) {
                        topArrow = '<div class="fixed-arrow top"></div>';
                        bottomArrow = '<div class="fixed-arrow bottom"></div>';

                        listctnr.before(topArrow).after(bottomArrow);
                    }
                    else {
                        topArrow = '<div class="arrow top"></div>';
                        bottomArrow = '<div class="arrow bottom"></div>';

                        listctnr.append(topArrow).append(bottomArrow);
                    }
                }
            }

            // Apply parameters width and height
            ctnr.width(params.width).height(params.height);

            if (params.arrows && params.fixedArrows) {
                if (params.vertical) {
                    topArrow = listctnr.prev();
                    bottomArrow = listctnr.next();

                    listctnr.width(params.width)
                    .height(params.height - (topArrow.height() + bottomArrow.height()));
                }
                else {
                    leftArrow = listctnr.prev();
                    rightArrow = listctnr.next();

                    listctnr.height(params.height)
                    .width(params.width - (leftArrow.width() + rightArrow.width()));
                }
            }
            else {
                listctnr.width(params.width).height(params.height);
            }

            var size = 0;

            if (!params.vertical) {
                ctnr.addClass('horizontal');

                // Determine content width
                $this.children().each(function () {
                    $(this).addClass('item');

                    if ($(this).outerWidth) {
                        size += $(this).outerWidth(true);
                    }
                    else {
                        // jQuery < 1.2.x backward compatibility patch
                        size += $(this).width() + parseInt($(this).css('padding-left')) + parseInt($(this).css('padding-right'))
						+ parseInt($(this).css('margin-left')) + parseInt($(this).css('margin-right'));
                    }
                });
                // Apply computed width to listcontainer
                $this.width(size);

                if (params.debug) {
                    $.log('[HoverScroll] Computed content width : ' + size + 'px');
                }

                // Retrieve container width instead of using the given params.width to include padding
                if (ctnr.outerWidth) {
                    size = ctnr.outerWidth();
                }
                else {
                    // jQuery < 1.2.x backward compatibility patch
                    size = ctnr.width() + parseInt(ctnr.css('padding-left')) + parseInt(ctnr.css('padding-right'))
					+ parseInt(ctnr.css('margin-left')) + parseInt(ctnr.css('margin-right'));
                }

                if (params.debug) {
                    $.log('[HoverScroll] Computed container width : ' + size + 'px');
                }
            }
            else {
                ctnr.addClass('vertical');

                // Determine content height
                $this.children().each(function () {
                    $(this).addClass('item')

                    if ($(this).outerHeight) {
                        size += $(this).outerHeight(true);
                    }
                    else {
                        // jQuery < 1.2.x backward compatibility patch
                        size += $(this).height() + parseInt($(this).css('padding-top')) + parseInt($(this).css('padding-bottom'))
						+ parseInt($(this).css('margin-bottom')) + parseInt($(this).css('margin-bottom'));
                    }
                });
                // Apply computed height to listcontainer
                $this.height(size);

                if (params.debug) {
                    $.log('[HoverScroll] Computed content height : ' + size + 'px');
                }

                // Retrieve container height instead of using the given params.height to include padding
                if (ctnr.outerHeight) {
                    size = ctnr.outerHeight();
                }
                else {
                    // jQuery < 1.2.x backward compatibility patch
                    size = ctnr.height() + parseInt(ctnr.css('padding-top')) + parseInt(ctnr.css('padding-bottom'))
					+ parseInt(ctnr.css('margin-top')) + parseInt(ctnr.css('margin-bottom'));
                }

                if (params.debug) {
                    $.log('[HoverScroll] Computed container height : ' + size + 'px');
                }
            }

            // Define hover zones on container
            /*var zone = {
            1: {action: 'move', from: 0, to: 0.06 * size, direction: -1 , speed: 16},
            2: {action: 'move', from: 0.06 * size, to: 0.15 * size, direction: -1 , speed: 8},
            3: {action: 'move', from: 0.15 * size, to: 0.25 * size, direction: -1 , speed: 4},
            4: {action: 'move', from: 0.25 * size, to: 0.4 * size, direction: -1 , speed: 2},
            5: {action: 'stop', from: 0.4 * size, to: 0.6 * size},
            6: {action: 'move', from: 0.6 * size, to: 0.75 * size, direction: 1 , speed: 2},
            7: {action: 'move', from: 0.75 * size, to: 0.85 * size, direction: 1 , speed: 4},
            8: {action: 'move', from: 0.85 * size, to: 0.94 * size, direction: 1 , speed: 8},
            9: {action: 'move', from: 0.94 * size, to: size, direction: 1 , speed: 16}
            }*/
            var zone = {
                1: { action: 'move', from: 0, to: 0.04 * size, direction: -1, speed: 16 },
                2: { action: 'move', from: 0.04 * size, to: 0.06 * size, direction: -1, speed: 8 },
                3: { action: 'move', from: 0.06 * size, to: 0.08 * size, direction: -1, speed: 4 },
                4: { action: 'move', from: 0.08 * size, to: 0.1 * size, direction: -1, speed: 2 },
                5: { action: 'stop', from: 0.1 * size, to: 0.9 * size },
                6: { action: 'move', from: 0.9 * size, to: 0.92 * size, direction: 1, speed: 2 },
                7: { action: 'move', from: 0.92 * size, to: 0.94 * size, direction: 1, speed: 4 },
                8: { action: 'move', from: 0.94 * size, to: 0.96 * size, direction: 1, speed: 8 },
                9: { action: 'move', from: 0.96 * size, to: size, direction: 1, speed: 16 }
            }

            // Store default state values in container
            ctnr[0].isChanging = false;
            ctnr[0].direction = 0;
            ctnr[0].speed = 1;


            /**
            * Check mouse position relative to hoverscroll container
            * and trigger actions according to the zone table
            *
            * @param x {Integer} Mouse X event position
            * @param y {Integer} Mouse Y event position
            */
            function checkMouse(x, y) {
                x = x - ctnr.offset().left;
                y = y - ctnr.offset().top;

                var pos;
                if (!params.vertical) { pos = x; }
                else { pos = y; }

                for (i in zone) {
                    if (pos >= zone[i].from && pos < zone[i].to) {
                        if (zone[i].action == 'move') { startMoving(zone[i].direction, zone[i].speed); }
                        else { stopMoving(); }
                    }
                }
            }


            /**
            * Sets the opacity of the left|top and right|bottom
            * arrows according to the scroll position.
            */
            function setArrowOpacity() {
                if (!params.arrows || params.fixedArrows) { return; }

                var maxScroll;
                var scroll;

                if (!params.vertical) {
                    maxScroll = listctnr[0].scrollWidth - listctnr.width();
                    scroll = listctnr[0].scrollLeft;
                }
                else {
                    maxScroll = listctnr[0].scrollHeight - listctnr.height();
                    scroll = listctnr[0].scrollTop;
                }

                var limit = params.arrowsOpacity;

                var opacityTop = (10 * limit * scroll) / maxScroll;

                if (scroll < 0)
                    var opacityTop = 0;
                if (scroll > 0.1 * maxScroll)
                    var opacityTop = limit;


                var opacityBottom = (-10 * limit * scroll) / maxScroll + 10 * limit;

                if (scroll < 0.9 * maxScroll)
                    var opacityBottom = limit;
                if (scroll > maxScroll)
                    var opacityBottom = 0;

                $('div.arrow.left, div.arrow.top', ctnr).show().css('opacity', opacityTop);
                $('div.arrow.right, div.arrow.bottom', ctnr).show().css('opacity', opacityBottom);
            }


            /**
            * Start scrolling the list with a given speed and direction
            *
            * @param direction {Integer}	Direction of the displacement, either -1|1
            * @param speed {Integer}		Speed of the displacement (20 being very fast)
            */
            function startMoving(direction, speed) {
                if (ctnr[0].direction != direction) {
                    if (params.debug) {
                        $.log('[HoverScroll] Starting to move. direction: ' + direction + ', speed: ' + speed);
                    }

                    stopMoving();
                    ctnr[0].direction = direction;
                    ctnr[0].isChanging = true;
                    move();
                }
                if (ctnr[0].speed != speed) {
                    if (params.debug) {
                        $.log('[HoverScroll] Changed speed: ' + speed);
                    }

                    ctnr[0].speed = speed;
                }
            }

            /**
            * Stop scrolling the list
            */
            function stopMoving() {
                if (ctnr[0].isChanging) {
                    if (params.debug) {
                        $.log('[HoverScroll] Stoped moving');
                    }

                    ctnr[0].isChanging = false;
                    ctnr[0].direction = 0;
                    ctnr[0].speed = 1;
                    clearTimeout(ctnr[0].timer);
                }
            }

            /**
            * Move the list one step in the given direction and speed
            */
            function move() {
                if (ctnr[0].isChanging == false) { return; }

                setArrowOpacity();

                var scrollSide;
                if (!params.vertical) { scrollSide = 'scrollLeft'; }
                else { scrollSide = 'scrollTop'; }

                listctnr[0][scrollSide] += ctnr[0].direction * ctnr[0].speed;
                ctnr[0].timer = setTimeout(function () { move(); }, 50);
            }

            // Initialize "right to left" option if specified
            if (params.rtl && !params.vertical) {
                listctnr[0].scrollLeft = listctnr[0].scrollWidth - listctnr.width();
            }

            // Bind actions to the hoverscroll container
            ctnr
            // Bind checkMouse to the mousemove
		.mousemove(function (e) { checkMouse(e.pageX, e.pageY); })
            // Bind stopMoving to the mouseleave
            // jQuery 1.2.x backward compatibility, thanks to Andy Mull!
            // replaced .mouseleave(...) with .bind('mouseleave', ...)
		.bind('mouseleave', function () { stopMoving(); });

            // Bind the startMoving and stopMoving functions
            // to the HTML object for external access
            this.startMoving = startMoving;
            this.stopMoving = stopMoving;

            if (params.arrows && !params.fixedArrows) {
                // Initialise arrow opacity
                setArrowOpacity();
            }
            else {
                // Hide arrows
                //	$('.arrowleft, .arrowright, .arrowtop, .arrowbottom', ctnr).hide();
                $('.arrowleft, .arrowright', ctnr).hide();

            }
        });

        return this;
    };


    // Backward compatibility with jQuery 1.1.x
    if (!$.fn.offset) {
        $.fn.offset = function () {
            this.left = this.top = 0;

            if (this[0] && this[0].offsetParent) {
                var obj = this[0];
                do {
                    this.left += obj.offsetLeft;
                    this.top += obj.offsetTop;
                } while (obj = obj.offsetParent);
            }

            return this;
        }
    }



    /**
    * HoverScroll default parameters
    */
    $.fn.hoverscroll.params = {
        vertical: false,      // Display the list vertically or not
        width: 400,        // Width of the list
        height: 50,         // Height of the list
        arrows: true,       // Display arrows to the left and top or the top and bottom
        arrowsOpacity: 1,    // Maximum opacity of the arrows if fixedArrows
        fixedArrows: false,     // Fix the displayed arrows to the side of the list
        rtl: false, 	// Set display mode to "Right to Left"
        debug: false       // Display some debugging information in firebug console
    };



    /**
    * Log errors to consoles (firebug, opera) if exist, else uses alert()
    */
    $.log = function () {
        try { console.log.apply(console, arguments); }
        catch (e) {
            try { opera.postError.apply(opera, arguments); }
            catch (e) {
                //            alert(Array.prototype.join.call(arguments, " "));
            }
        }
    };


})(jQuery);

