Source: widgets/carousel/horizontalcarouselaligner.js

define('application/widgets/carousel/horizontalcarouselaligner', [
    'rofl/widgets/carousel/aligners/natural',
    'antie/widgets/carousel/aligners/aligner',
    'rofl/lib/utils'
], function (
    NauturalAligner,
    Aligner,
    Utils
) {
    'use strict';

    return NauturalAligner.extend({

        /**
         * Aligns the Mask and WidgetStrip to their correct position.
         *
         * @param {Navigator} navigator - The carousel navigator object.
         * @param {number} direction - The direction.
         *                 Aligner.directions.FORWARD or Aligner.directions.BACKWARD.
         * @param {Object} options - The animation options.
         * @private
         */
        _align: function (navigator, direction, options) {
            var startIndex = this._mask.getWidgetStrip().getActiveChildWidgetIndex(),
                targetIndex = (direction === Aligner.directions.FORWARD)
                    ? navigator.indexAfter(startIndex)
                    : navigator.indexBefore(startIndex);

            if (Utils.isNumber(targetIndex)) {

                if (this._isWrap(startIndex, targetIndex, direction)) {
                    return;
                }

                this._doAlign(startIndex, targetIndex, options);
            }
        },

        /**
         * Executes the alignment.
         *
         * @param {number} startIndex - The start index.
         * @param {number} targetIndex - The target index.
         * @param {Object} [options] - The alignment options.
         * @private
         */
        _doAlign: function (startIndex, targetIndex, options) {
            var mask = this._mask,
                widgetStrip = mask.getWidgetStrip(),
                targetDistance,
                newAlignPoint,
                distanceFromMask,
                widgetLength;

            this._informMaskBeforeAlign(targetIndex);

            /*
             * Calculate the virtual column position for our target based
             * on the widget size and the current scroll position of the
             * WidgetStrip
             */
            widgetLength = widgetStrip.lengthOfWidgetAtIndex(targetIndex);
            distanceFromMask = widgetStrip.getLengthToIndex(startIndex);
            targetDistance = widgetStrip.getLengthToIndex(targetIndex);

            if (!this._lastAlignerPoint) {
                this._lastAlignerPoint = distanceFromMask;
            }

            if (targetIndex === 0) {

                // Always align to the start position when the target is the first target.
                this._scrolled = false;
                newAlignPoint = 0;
                mask.setAlignPoint(newAlignPoint);

            } else if (targetIndex < startIndex) { // Navigating left

                newAlignPoint = targetDistance;

                // When we've scrolled and navigating backwards, move to the left position.
                if (this._scrolled) {
                    newAlignPoint = newAlignPoint - widgetLength;
                }

                if (newAlignPoint < 0) {
                    newAlignPoint = 0;
                }

                if (this._lastAlignerPoint - newAlignPoint > widgetLength) {
                    newAlignPoint = this._lastAlignerPoint - widgetLength;
                }

                mask.setAlignPoint(newAlignPoint);

            } else { // Navigating right

                newAlignPoint = targetDistance;

                // If we reached the end of the row, keep the same alignpoint.
                if (newAlignPoint > (widgetLength * (this._itemsOnScreen - 1))) {

                    this._scrolled = true;
                    newAlignPoint = (widgetLength * (this._itemsOnScreen - 1));
                }

                if (newAlignPoint - this._lastAlignerPoint > widgetLength) {
                    newAlignPoint = this._lastAlignerPoint + widgetLength;
                }

                mask.setAlignPoint(newAlignPoint);
            }

            this._lastAlignerPoint = newAlignPoint;
            this._moveNormally(targetIndex, options);

            if (distanceFromMask !== newAlignPoint && newAlignPoint !== 0) {
                this._informMaskAfterAlign(targetIndex);
            }
        },

        /**
         * Aligns the carousel to a specific index.
         *
         * @param {number} index - The index.
         * @param {Object} [options] - The alignment options.
         */
        alignToIndex: function alignToIndex (index, options) {
            var mask = this._mask,
                widgetStrip = mask.getWidgetStrip(),
                widgetCount = widgetStrip.getChildWidgetCount();

            if (index > widgetCount - this._itemsOnScreen) {

                index = widgetCount - this._itemsOnScreen;
            }

            this._scrolled = true;

            if (index < 0) {
                index = 0;
            }

            this._mask.setAlignPoint(0);
            this._lastAlignerPoint = 0;
            alignToIndex.base.call(this, index, options);
        },

        /**
         * Resets the aligner.
         */
        reset: function () {
            this._lastAlignIndex = null;
        }
    });
});