Source: widgets/guide/verticalcarouselaligner.js

define('application/widgets/guide/verticalcarouselaligner', [
  'rofl/widgets/carousel/aligners/natural',
  'antie/widgets/carousel/aligners/aligner',
  'rofl/lib/utils',
  'application/utils'
], function (
  NauturalAligner,
  Aligner,
  Utils,
  AppUtils
) {
  '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);

          this._direction = direction;

          if (this._isWrap(startIndex, targetIndex, direction)) {
              throw new Error('Wrapping is not supported in the NaturalAligner');
          }

          if (Utils.isNumber(targetIndex)) {
              this._doAlign(targetIndex, startIndex, options);
          }
      },

      /**
       * Executes the align function.
       *
       * @param {number} targetIndex - The target index.
       * @param {number} startIndex - The start index.
       * @param {Object} options - The animation options.
       * @private
       */
      _doAlign: function (targetIndex, startIndex, options) {
          var itemsOnScreenToIndex = this._itemsOnScreen - 1,
              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 = this._mask.getWidgetStrip().lengthOfWidgetAtIndex(targetIndex);

          // When backwards.
          if (targetIndex < startIndex) {

              if (startIndex > itemsOnScreenToIndex) {

                  // Set the align point to the max-right position
                  if (!AppUtils.isInt(itemsOnScreenToIndex) && targetIndex === 0) {
                      itemsOnScreenToIndex = Math.floor(itemsOnScreenToIndex);
                      this._mask.setAlignPoint(itemsOnScreenToIndex * widgetLength);
                      this._moveNormally(targetIndex, options);
                  }

                  this._mask.setAlignPoint(itemsOnScreenToIndex * widgetLength);
                  this._moveNormally(targetIndex, options);
              } else if (startIndex < 0) {

                  // Start moving back so align on the left
                  this._mask.setAlignPoint(0);
                  this._moveNormally(targetIndex, options);
              }
              if (startIndex <= itemsOnScreenToIndex && targetIndex >= 0) {

                  // Align only when a wrapped event is not going to carry out this
                  this._mask.setAlignPoint(targetIndex * widgetLength);
                  this._informMaskAfterAlign(targetIndex);
              }
          } else {
              if (targetIndex > itemsOnScreenToIndex) {

                  // Set the align point to the max-right position
                  this._mask.setAlignPoint(itemsOnScreenToIndex * widgetLength);
                  this._moveNormally(targetIndex, options);
              } else if (targetIndex < 0) {

                  // Start moving back so align on the left
                  this._mask.setAlignPoint(0);
                  this._moveNormally(targetIndex, options);
              }

              if (targetIndex <= itemsOnScreenToIndex && targetIndex >= 0) {

                  // Align only when a wrapped event is not going to carry out this
                  this._mask.setAlignPoint(targetIndex * widgetLength);
                  this._informMaskAfterAlign(targetIndex);
              }
          }
      },

      /**
       * Get the direction.
       *
       * @returns {number} Direction - The direction Aligner.directions.FORWARD or Aligner.directions.BACKWARD.
       */
      getDirection: function () {
          return this._direction;
      },

      /**
       * Aligns to index.
       *
       * @param {number} index - The index.
       * @param {Object} options - The options.
       */
      alignToIndex: function (index, options) {
          var startIndex = this.indexOfLastAlignRequest();

          this._direction = index > startIndex ? 0 : 1;

          options = options || {skipAnim: false,
              duration: 300,
              easing: 'easeInOut',
              fps: 60
          };

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