﻿/// <reference path="jquery-1.4.2.min.js" />

Wheel = function () {
    this.DefaultItemClass = "Item";

    this.MovementNone = "None";
    this.MovementLeft = "Left";
    this.MovementRight = "Right";

    this.ContainerIsAnimating = false;
    this.DefaultItemWidth = 107;
    this.MaxHeightUnitsOfContainer = 4;
    this.MaxHeightUnitsHeight = 360;

    this.ColumnRegister = {};
    this.CounterNumber = 0;
    this.RegionNumber = 0;
    this.ScrollDurationPer100PxInMS = 200;
    this.LastScrollDirection = this.MovementNone;
    this.MovingContainer = null;
    this.LastOutsideCheck = new Date();
    this.OutsideCheckInterval = null;
    this.OutsideCheckIntervalDuration = 4000;
    this.MinOutsideCheckTimeDifference = 500;

    this.ColumnLeftMargin = 10;
    this.RegionLeftPadding = 10;
    this.RegionRightPadding = 10;
    this.ItemTopMargin = 10;

    this.LastMousePosX = 0;
    this.LastMousePosY = 0;
    this.LastMouseXDelta = 0;
    this.LastMouseYDelta = 0;
    this.IsMouseTracking = false;

    this.MinMouseMovementDistanceForThrow = 5;
    this.MouseThrowSpeed = 7;
    this.MouseScrollSpeed = 4;

    this.BreakingErrorOccured = false;

    this.BatchRunning = false;

    this.MethodToCallForFillingItem = null;
    this.MethodToCallForRemainingItems = null;

    this.HasBeenMovedWithLastClick = false;
    this.MousePressedScrolling = null;

    this.TrackRemainingItems = true;
}

Wheel.prototype = {
    // Shows a single error message, which causes the wheel to break.
    // [MB]
    ShowBreakingError: function (breakingErrorMsg) {
        if (!wheel.BreakingErrorOccured) {
            wheel.BreakingErrorOccured = true;

            alert(breakingErrorMsg);
        }
    },

    // Initializes the mouse events and the cache-checker.
    // [MB]
    Initialize: function () {
        wheel.MovingContainer = $j("#MovingContainer");
        wheel.MovingContainer.css("cursor", "pointer");

        if (wheel.MovingContainer.length == 0) {
            //wheel.ShowBreakingError("Couldn't find the MovingContainer.");
        }
        else {
            wheel.MovingContainer.mousewheel(wheel.HandleMouseScroll);
            wheel.MovingContainer.mousedown(wheel.HandleMouseDown);
            wheel.MovingContainer.mousemove(wheel.HandleMouseMove);
            wheel.MovingContainer.mouseup(wheel.HandleMouseUp);
            wheel.MovingContainer.keydown(wheel.HandleKeyDown);

            wheel.MovingContainer.bind("touchmove", function (e) {
                e.clientX = e.originalEvent.targetTouches[0].pageX;
                e.clientY = e.originalEvent.targetTouches[0].pageY;

                wheel.HandleMouseMove(e);
            });

            wheel.OutsideCheckInterval = window.setInterval(wheel.CheckForRemainingItems, wheel.OutsideCheckIntervalDuration);

            wheel.InitializeProperties();
        }
    },

    CleanupWheel: function () {
        wheel.InitializeProperties();

        $j(".Region").remove();
    },

    InitializeProperties: function () {
        wheel.ContainerIsAnimating = false;

        wheel.ColumnRegister = {};
        wheel.CounterNumber = 0;
        wheel.RegionNumber = 0;
        wheel.LastScrollDirection = this.MovementNone;
        wheel.LastOutsideCheck = new Date();

        wheel.LastMousePosX = 0;
        wheel.LastMousePosY = 0;
        wheel.LastMouseXDelta = 0;
        wheel.LastMouseYDelta = 0;
        wheel.IsMouseTracking = false;

        wheel.HasBeenMovedWithLastClick = false;
    },

    // Support for scrolling with arrow keys.
    // [MB]
    HandleKeyDown: function (e) {
        var key = 0;

        if (e.keyCode)
            key = e.keyCode;

        if (e.charCode)
            key = e.charCode;

        var unitsToMove = 0;
        if (key == 39) {
            unitsToMove = 2;
        }
        else if (key == 37) {
            unitsToMove = -2;
        }

        if (unitsToMove) {
            if (window.AbortInteractionHintInterval)
                AbortInteractionHintInterval();

            if (window.GlobalConfig)
                GlobalConfig.LastInteractedElement = "Wheel";

            wheel.MoveByColumns(unitsToMove);
            e.preventDefault();
            e.stopPropagation();
        }
    },

    // Checks the direction of the scrolling event and calls the animate method.
    // [MB]
    HandleMouseScroll: function (event, delta) {
        if (window.AbortInteractionHintInterval)
            AbortInteractionHintInterval();

        var deltaKind = 0;
        if (delta < 0) {
            deltaKind = -1;
        }
        else if (delta > 0) {
            deltaKind = 1;
        }

        // Ensure that the page doesn't scroll.
        // [MB]
        event.preventDefault();

        if (window.GlobalConfig)
            GlobalConfig.LastInteractedElement = "Wheel";

        wheel.MoveByUnits(deltaKind * wheel.MouseScrollSpeed);
    },

    // Initialized mouse movement.
    // [MB]
    HandleMouseDown: function (e) {
        if (window.AbortInteractionHintInterval)
            AbortInteractionHintInterval();

        wheel.HasBeenMovedWithLastClick = false;
        wheel.LastMousePosX = e.clientX;
        wheel.LastMousePosY = e.clientY;
        wheel.LastMouseXDelta = 0;
        wheel.LastMouseYDelta = 0;
        wheel.IsMouseTracking = true;
        wheel.MovingContainer.stop();

        if (window.GlobalConfig && window.GlobalConfig.LastInteractedElement)
            GlobalConfig.LastInteractedElement = "Wheel";

        if (e.preventDefault)
            e.preventDefault();
        if (e.stopPropagation)
            e.stopPropagation();
    },

    // Moves the main container related to the mouse position.
    // [MB]
    HandleMouseMove: function (e) {
        if ((e.type == "touchmove") && !wheel.IsMouseTracking) {
            wheel.HandleMouseDown(e);
        }

        if (wheel.IsMouseTracking) {
            wheel.HasBeenMovedWithLastClick = true;
            wheel.LastMouseXDelta = e.clientX - wheel.LastMousePosX;
            wheel.LastMouseYDelta = e.clientY - wheel.LastMousePosY;

            wheel.SetMovingContainerLeft(parseInt(wheel.MovingContainer.css("left")) + wheel.LastMouseXDelta);

            wheel.LastMousePosX = e.clientX;
            wheel.LastMousePosY = e.clientY;
        }

        if (e.preventDefault)
            e.preventDefault();
        if (e.stopPropagation)
            e.stopPropagation();
    },

    SetMovingContainerLeft: function (newLeft) {
        wheel.MovingContainer.css("left", newLeft);
    },

    // Clears mouse handling and animates some kind of throw, if the mouse has moved fast enough.
    // [MB]
    HandleMouseUp: function (e) {
        if (wheel.IsMouseTracking) {
            if (Math.abs(wheel.LastMouseXDelta) > wheel.MinMouseMovementDistanceForThrow) {
                wheel.AnimateBy(wheel.LastMouseXDelta * wheel.MouseThrowSpeed, "easeOutQuad");
            }
            else {
                wheel.SnapMovingContainerToEnd();
                wheel.CheckForRemainingItems();
            }

            wheel.IsMouseTracking = false;
        }

        e.preventDefault();
        e.stopPropagation();
    },

    // Checks both ends if they're away from the browser borders and snaps them back.
    // [MB]
    SnapMovingContainerToEnd: function () {
        var containerLeft = wheel.MovingContainer.position().left;

        if (containerLeft == 0) {
            // Do nothing.
            // [MB]
        }
        else if ((containerLeft > 0) || wheel.MovingContainer.width() < $j("#MovingContainerCarrier").width()) {
            wheel.AnimateTo(0, "easeOutBounce");
        }
        else {
            var containerWidth = wheel.MovingContainer.width();

            var regionSummary = 0;
            $j(".Region", wheel.MovingContainer).each(function (idx, obj) {
                regionSummary += $j(obj).width();
            });

            var containerRight = containerLeft + regionSummary;
            var documentWidth = $j("#MovingContainerCarrier").width();

            if (documentWidth > containerRight) {
                wheel.AnimateTo(-(regionSummary - documentWidth), "easeOutBounce");
            }
        }
    },

    // Creates a new div with the region class and adds it to the end.
    // [MB]
    CreateRegion: function (labelText, direction) {
        if ((direction != -1) && (direction != 1))
            direction = 1;

        var regionDiv = document.createElement("div");
        $j(regionDiv).attr("class", "Region")
                     .attr("id", "Region_" + wheel.RegionNumber)
                     .css("height", "380px");

        wheel.RegionNumber++;

        if (direction == 1) {
            $j("#ClearingEndOfContainer").before(regionDiv);
        }
        else {
            wheel.MovingContainer.prepend(regionDiv);
        }

        if (labelText) {
            var regionLabelDiv = document.createElement("div");
            regionDiv.appendChild(regionLabelDiv);

            $j(regionLabelDiv).addClass("Label")
                              .html(labelText);
        }

        return regionDiv;
    },

    GetAllRegionLabels: function () {
        var result = [];
        $j(".Region .Label", wheel.MovingContainer).each(function (idx, e) {
            result[result.length] = $j(this).html().trim();
        });

        return result;
    },

    // Validates a width or height unit by various criterias.
    // [MB]
    ValidateUnit: function (unit) {
        if (unit == 1)
            return true;

        if (unit <= 0)
            return false;

        //        if ((unit % 2) != 0)
        //            return false;

        if (unit > wheel.MaxHeightUnitsOfContainer)
            return false;

        return true;
    },

    // Simply multiplies number of units by assigned default values.
    // [MB]
    GetItemSize: function (item) {
        var widthUnit = parseInt(item.attr("WidthUnit"));
        var heightUnit = parseInt(item.attr("HeightUnit"));

        if (!wheel.ValidateUnit(widthUnit)) {
            wheel.ShowBreakingError("Found item with invalid width unit: " + widthUnit);
        }
        else if (!wheel.ValidateUnit(heightUnit)) {
            wheel.ShowBreakingError("Found item with invalid height unit: " + heightUnit);
        }
        else {
            var maxItemsOfThisTypeInColumn = wheel.MaxHeightUnitsOfContainer / heightUnit;
            var requiredSpacerWithThisHeightUnit = maxItemsOfThisTypeInColumn - 1;
            var reservedSpace = requiredSpacerWithThisHeightUnit * wheel.ItemTopMargin;
            var leftSpaceForItemsOfThisHeightUnit = wheel.MaxHeightUnitsHeight - reservedSpace;

            var width = widthUnit * wheel.DefaultItemWidth;
            var height = leftSpaceForItemsOfThisHeightUnit / maxItemsOfThisTypeInColumn;

            return { Width: width, Height: height, WidthUnit: widthUnit, HeightUnit: heightUnit };
        }

        return null;
    },

    BeginBatch: function () {
        wheel.BatchRunning = true;
    },

    CommitBatch: function () {
        wheel.BatchRunning = false;

        $j(".Region", wheel.MovingContainer).each(function (regionIdx, regionObj) {
            wheel.AlignItemsInRegion(regionObj);
        });

        wheel.UpdateMovingContainerWidth();
    },

    // Add new item div.
    // [MB]
    AddItem: function (region, itemInfo, type) {
        wheel.CounterNumber++;
        var itemId = "item_" + wheel.CounterNumber;

        var item = document.createElement("div");
        var jItem = $j(item);

        jItem.addClass(wheel.DefaultItemClass)
             .css("position", "absolute")
        //.css("border", "1px gray dashed")
        //.html(itemInfo.Content)
             .attr("WidthUnit", itemInfo.Width)
             .attr("HeightUnit", itemInfo.Height)
             .attr("id", itemId);

        if (type == "before") {
            $j(region).prepend(item);
        }
        else {
            $j(region).append(item);
        }

        // Realigning the moving-container-div.
        // [MB]
        if (!wheel.BatchRunning) {
            wheel.AlignItemsInRegion(region);
            wheel.UpdateMovingContainerWidth();
            wheel.UpdateMovingContainerLeft(itemId);
        }

        if (wheel.MethodToCallForFillingItem)
            wheel.MethodToCallForFillingItem({ Region: region, Item: item, jItem: jItem, ItemDimensions: wheel.GetItemSize(jItem), ItemInfo: itemInfo });

        return item;
    },

    // Calculate how many items are left outside of the screen.
    // [MB]
    GetOutsideItems: function () {
        var leftOutside = 0;
        var rightOutside = 0;
        var bodyWidth = $j(document.body).width();

        $j(".Region", wheel.MovingContainer).each(function (regionIdx, regionObj) {
            $j("." + wheel.DefaultItemClass, regionObj).each(function (itemIdx, itemObj) {
                var itemBorders = wheel.GetItemBorders($j(itemObj));

                if (itemBorders.AbsoluteItemRight < itemBorders.ItemWidth) {
                    leftOutside++;
                }
                else if (itemBorders.AbsoluteItemLeft > (bodyWidth - itemBorders.ItemWidth)) {
                    rightOutside++;
                }
            });
        });

        return { LeftOutside: leftOutside, RightOutside: rightOutside };
    },

    // Calculates the border values of a single item.
    // [MB]
    GetItemBorders: function (item) {
        var itemLeftInRegion = parseInt(item.css("left"));
        var regionLeft = item.parent().position().left;
        var mConLeft = parseInt(wheel.MovingContainer.css("left"));
        var absoluteItemLeft = itemLeftInRegion + regionLeft + mConLeft;
        var itemWidth = item.width();

        return {
            MovingContainerLeft: mConLeft,
            ItemLeftInRegion: itemLeftInRegion,
            AbsoluteItemLeft: absoluteItemLeft,
            ItemWidth: itemWidth,
            AbsoluteItemRight: absoluteItemLeft + itemWidth
        };
    },

    // Calculate wheter the new item has been created at the left outside of the screen and move the container regarding.
    // [MB]
    UpdateMovingContainerLeft: function (itemId) {
        var item = $j("#" + itemId);
        var itemBorders = wheel.GetItemBorders(item);

        if (itemBorders.AbsoluteItemLeft < 0) {
            wheel.MovingContainer.css("left", (itemBorders.MovingContainerLeft - itemBorders.ItemWidth));
        }
    },

    // Calculate the used width and append it to item, region and container.
    // [MB]
    AlignItemsInRegion: function (region) {
        var regionWidth = wheel.RegionLeftPadding + wheel.RegionRightPadding; // including the initial padding.
        var currentTopUnit = 0;
        var currentLeftUnit = 0;
        var currentMaxWidthUnit = 1;
        var currentMaxWidthPx = 0;
        var columnNumber = 0;
        var itemInColumn = 0;
        var previousItemHeights = 0;
        var lastItemLeftPx = 0;

        wheel.ColumnRegister[region.id] = [];

        var itemsInRegion = $j("." + wheel.DefaultItemClass, region);
        var lastItem = itemsInRegion.last();

        itemsInRegion.each(function (itemIdx, itemObj) {
            var jItemObj = $j(itemObj);
            var itemSize = wheel.GetItemSize(jItemObj);

            itemInColumn++;

            if ((currentTopUnit + itemSize.HeightUnit) > wheel.MaxHeightUnitsOfContainer) {
                wheel.ColumnRegister[region.id][columnNumber] = {
                    ColumnMaxWidthPx: currentMaxWidthPx,
                    ColumnLeft: lastItemLeftPx,
                    ColumnRight: lastItemLeftPx + currentMaxWidthPx
                }

                columnNumber++;
                regionWidth += currentMaxWidthPx;
                regionWidth += wheel.ColumnLeftMargin; // XXpx margin-left of each column.
                currentLeftUnit += currentMaxWidthUnit;

                previousItemHeights = 0;
                itemInColumn = 1;
                currentTopUnit = 0;
                currentMaxWidthPx = 0;
                currentMaxWidthUnit = 1;
            }

            var top = previousItemHeights + ((itemInColumn - 1) * wheel.ItemTopMargin);
            if ((currentTopUnit + itemSize.HeightUnit) == 2 && wheel.GetItemSize($j(itemsInRegion[itemIdx + 1])).HeightUnit == 4) {
                top = 100;
            }

            //lastItem
            if ((lastItem.attr("id") == itemObj.id) && (top == 0) && (itemSize.HeightUnit == 2)) {
                top = 100;
            }

            lastItemLeftPx = (currentLeftUnit * wheel.DefaultItemWidth) + wheel.RegionLeftPadding + (wheel.ColumnLeftMargin * columnNumber);
            jItemObj.css("width", itemSize.Width)
                    .css("height", itemSize.Height)
                    .css("top", top)
                    .css("left", lastItemLeftPx); // XXpx of (region margin-left) | + XXpx for each new column

            // Store the widest item of the current column.
            // [MB]
            if (currentMaxWidthUnit < itemSize.WidthUnit) {
                currentMaxWidthUnit = itemSize.WidthUnit;
            }

            if (currentMaxWidthPx < itemSize.Width) {
                currentMaxWidthPx = itemSize.Width
            }

            // Add the last items height to the counted height units.
            // [MB]
            currentTopUnit += itemSize.HeightUnit;
            previousItemHeights += itemSize.Height;
        });

        // Storing the last column.
        // [MB]
        wheel.ColumnRegister[region.id][columnNumber] = {
            ColumnMaxWidthPx: currentMaxWidthPx,
            ColumnLeft: lastItemLeftPx,
            ColumnRight: lastItemLeftPx + currentMaxWidthPx
        }

        $j(region).css("width", regionWidth + currentMaxWidthPx);
    },

    // Update the moving container width by summing up the widths of all regions.
    // [MB]
    UpdateMovingContainerWidth: function () {
        var movingContainerWidth = 0;

        var regionMargin = wheel.RegionLeftPadding + wheel.RegionRightPadding + 2; // 2 border px

        $j(".Region", wheel.MovingContainer).each(function (regionIdx, regionObj) {
            movingContainerWidth += $j(regionObj).width() + regionMargin;
        });

        wheel.MovingContainer.css("width", movingContainerWidth);
    },

    // Moving the main-container by a given number of units.
    // [MB]
    MoveByUnits: function (unitsToMove) {
        if (!wheel.ContainerIsAnimating) {
            wheel.ContainerIsAnimating = true;
            unitsToMove *= -1;

            var scrollDifference = (unitsToMove * wheel.DefaultItemWidth);

            if (unitsToMove == 0) {
                wheel.LastScrollDirection = wheel.MovementNone;
            }
            else {
                wheel.LastScrollDirection = (unitsToMove > 0) ? wheel.MovementLeft : wheel.MovementRight;
            }

            wheel.AnimateBy(scrollDifference);
        }
    },

    StartScrolling: function (columnsToMove) {
        wheel.MoveByColumns(columnsToMove);

        wheel.MousePressedScrolling = window.setInterval("wheel.MoveByColumns(" + columnsToMove + ")", 1000);
    },

    StopScrolling: function () {
        if (wheel.MousePressedScrolling)
            window.clearInterval(wheel.MousePressedScrolling);
    },

    MoveByColumns: function (columnsToMove) {
        if (columnsToMove) {
            var positionToScrollTo = null;

            var movingContainerLeft = parseInt(wheel.MovingContainer.css("left"));
            var movingContainerLeftAbs = Math.abs(movingContainerLeft);
            var movingContainerPosIsNegative = movingContainerLeft <= 0;

            var passedWidth = 0;
            var allRegions = $j(".Region", wheel.MovingContainer);

            var regionAtPositionInfo = wheel.GetRegionInfoOnDistance(movingContainerLeftAbs, allRegions);
            var columnsOfRegion = wheel.ColumnRegister[regionAtPositionInfo.Region.get(0).id];

            var regionLeft = regionAtPositionInfo.Left;

            if (columnsToMove > 0) {
                var lastColumnLeft = columnsOfRegion[columnsOfRegion.length - 1].ColumnLeft;

                // Check if we're currently hitting the last column of the matched region.
                // ACTION: Scroll to next region.
                // [MB]
                if ((regionLeft + lastColumnLeft - wheel.ColumnLeftMargin) <= movingContainerLeftAbs) {
                    positionToScrollTo = $j(allRegions.get(regionAtPositionInfo.Index + 1)).position().left;
                }
                else {
                    // If we got here, we're not matching the last column, so let's search for the matching column and scroll to the next.
                    // ACTION: Scrolls to the next column in the current region.
                    // [MB]
                    var lastCurColRight = 0;
                    for (var colNum = 0; colNum < columnsOfRegion.length; colNum++) {
                        var curColLeft = columnsOfRegion[colNum].ColumnLeft + regionLeft - wheel.ColumnLeftMargin;
                        var curColRight = columnsOfRegion[colNum].ColumnRight + regionLeft - wheel.ColumnLeftMargin;

                        if (lastCurColRight === 0) {
                            lastCurColRight = curColLeft;
                        }

                        if ((lastCurColRight <= movingContainerLeftAbs) && (curColRight >= movingContainerLeftAbs)) {
                            if (columnsOfRegion[colNum + 1]) {
                                positionToScrollTo = columnsOfRegion[colNum + 1].ColumnLeft + regionLeft - wheel.ColumnLeftMargin;
                            }
                            break;
                        }

                        lastCurColRight = curColRight;
                    }
                }
            }
            else {
                var firstColumnWidth = columnsOfRegion[0].ColumnMaxWidthPx;

                // If we're currently hitting the first column, go to previous region.
                // ACTION: Scroll to last column of previous region.
                // [MB]
                if ((regionLeft + firstColumnWidth) > movingContainerLeftAbs) {
                    var previousRegion = allRegions.get(regionAtPositionInfo.Index - 1);

                    if (previousRegion) {
                        var columnsOfPreviousRegion = wheel.ColumnRegister[previousRegion.id];
                        var previousRegionLeft = $j(previousRegion).position().left;
                        var lastColumnOfPreviousRegionLeft = columnsOfPreviousRegion[columnsOfPreviousRegion.length - 1].ColumnLeft;

                        positionToScrollTo = -240;
                    }
                }
                else {
                    // If we got here, we're not matching the first column, so let's search for the matching column and scroll to the previous.
                    // ACTION: Scrolls to the previous column in the current region.
                    // [MB]
                    var lastCurColRight = 0;
                    for (var colNum = 0; colNum < columnsOfRegion.length; colNum++) {
                        var curColLeft = columnsOfRegion[colNum].ColumnLeft + regionLeft - wheel.ColumnLeftMargin;
                        var curColRight = columnsOfRegion[colNum].ColumnRight + regionLeft - wheel.ColumnLeftMargin;

                        if (lastCurColRight === 0) {
                            lastCurColRight = curColLeft;
                        }

                        if ((lastCurColRight <= movingContainerLeftAbs) && (curColRight >= movingContainerLeftAbs)) {
                            positionToScrollTo = columnsOfRegion[colNum - 1].ColumnLeft + regionLeft - wheel.ColumnLeftMargin;
                            break;
                        }

                        lastCurColRight = curColRight;
                    }
                }
            }

            if (positionToScrollTo !== null) {
                if (columnsToMove < 0) {
                    columnsToMove++;
                } else if (columnsToMove > 0) {
                    columnsToMove--;
                }

                wheel.AnimateTo(-positionToScrollTo, null, (columnsToMove) ? "wheel.MoveByColumns(" + columnsToMove + ")" : null);
            }
        }
    },

    GetCurrentlyFirstVisibleRegion: function () {
        var movingContainerLeftAbs = Math.abs(parseInt(wheel.MovingContainer.css("left"))) + 50;
        var regionAtPositionInfo = wheel.GetRegionInfoOnDistance(movingContainerLeftAbs, $j(".Region", wheel.MovingContainer));

        return regionAtPositionInfo;
    },

    GetLabelOfFirstVisibleRegion: function () {
        var regionAtPositionInfo = wheel.GetCurrentlyFirstVisibleRegion();

        if (regionAtPositionInfo && regionAtPositionInfo.Region && (regionAtPositionInfo.Region.length > 0)) {
            return $j(".Label", regionAtPositionInfo.Region[0]).html();
        }

        return null;
    },

    GetRegionInfoOnDistance: function (distance, regions) {
        if (!regions)
            regions = $j(".Region", wheel.MovingContainer);

        var selectedRegion = null;
        var selectedRegionIdx = -1;
        var selectedRegionLeft = 0;
        var selectedRegionWidth = 0;
        var selectedRegionRight = 0;

        regions.each(function (regionIdx, regionObj) {
            var jRegionObj = $j(regionObj)
            var currentRegionLeft = jRegionObj.position().left;
            var currentRegionWidth = jRegionObj.width();
            var currentRegionRight = currentRegionLeft + currentRegionWidth;

            if ((currentRegionLeft <= distance) && (currentRegionRight >= distance)) {
                selectedRegion = jRegionObj;
                selectedRegionIdx = regionIdx;
                selectedRegionLeft = currentRegionLeft;
                selectedRegionRight = currentRegionRight;
                selectedRegionWidth = currentRegionWidth;

                return false;
            }
        });

        if (selectedRegion) {
            return {
                Region: selectedRegion,
                Index: selectedRegionIdx,
                Left: selectedRegionLeft,
                Width: selectedRegionWidth,
                Right: selectedRegionRight
            }
        }
        var firstRegion = $j(regions[0]);
        return {
            Region: firstRegion,
            Index: 0,
            Left: 0,
            Width: firstRegion.width(),
            Right: firstRegion.width()
        }
    },

    // Calculates the scroll difference in relation to the current position.
    // [MB]
    AnimateTo: function (scrollTarget, scrollType, callback) {
        var oldMConLeft = parseInt(wheel.MovingContainer.css("left"));
        wheel.AnimateBy(scrollTarget - oldMConLeft, scrollType, callback);
    },

    // Animates the movable container by a given distance.
    // [MB]
    AnimateBy: function (scrollDifference, scrollType, callback) {
        if (!scrollType)
            scrollType = "linear";

        var oldMConLeft = parseInt(wheel.MovingContainer.css("left"));
        var scrollDuration = wheel.ScrollDurationPer100PxInMS * (Math.abs(scrollDifference) / 100);

        wheel.MovingContainer.stop().animate({ left: scrollDifference + oldMConLeft }, scrollDuration, scrollType, function () {
            wheel.ContainerIsAnimating = false;

            if (!callback) {
                window.setTimeout(wheel.SnapMovingContainerToEnd, 1);

                wheel.CheckForRemainingItems();
            }
            else {
                window.setTimeout(callback, 0);
            }
        });
    },

    // Evaluates the item borders and is able to check how many items are left outside of the browser window.
    // [MB]
    CheckForRemainingItems: function () {
        if (wheel.MethodToCallForRemainingItems && wheel.TrackRemainingItems) {
            var now = new Date();

            if ((now - wheel.LastOutsideCheck) > wheel.MinOutsideCheckTimeDifference) {
                wheel.LastOutsideCheck = now;

                wheel.MethodToCallForRemainingItems(wheel.GetOutsideItems());
            }
        }
    }
}

var wheel = new Wheel();

$j(document).ready(function () {
    wheel.Initialize();
});
