Source: CircularCarousel/CircularCarousel.js

// Copyright 2011 Tart. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview tart.CircularCarousel is an event driven Carousel/Image Slider class which
 * handles next and previous events and gets visible items on viewport.
 *
 * Example usage:
 *
 *     var items = [
 *         {name : 'one'},
 *         {name : 'two'},
 *         {name : 'three'},
 *         {name : 'four'},
 *         {name : 'five'},
 *         {name : 'six'},
 *         {name : 'seven'}
 *     ]; //seven items
 *
 *     var carousel = new tart.CircularCarousel(items);
 *
 *     carousel.setItemPerViewport(2); //only 2 items is visibile
 *
 *     goog.events.listen(carousel, tart.Carousel.EventTypes.NEXT, function (e) {
 *         console.info('items moved next');
 *         console.log (e.itemsToBeRemoved);
 *         console.log (e.itemsToBeInserted);
 *         console.info(carousel.getVisibleItems());
 *     });
 *
 *     goog.events.listen(carousel, tart.Carousel.EventTypes.PREV, function (e) {
 *         console.info('items moved prev');
 *         console.log (e.itemsToBeRemoved);
 *         console.log (e.itemsToBeInserted);
 *         console.info(carousel.getVisibleItems());
 *     });
 *
 *     carousel.prev(3);
 *     carousel.next(1);
 */

goog.provide('tart.CircularCarousel');

goog.require('goog.events.EventTarget');
goog.require('tart.Carousel');



/**
 * Pagination class to handle all paging events
 *
 * @param {Array.<*>=} items array of items.
 * @extends {tart.Carousel}
 * @constructor
 */
tart.CircularCarousel = function(items) {
    goog.base(this, items);
};
goog.inherits(tart.CircularCarousel, tart.Carousel);


/**
 * Find which items to be removed and inserted after move
 *
 * @param {number} moveCount item move count.
 * @return {Object} object literal which has itemsToBeInserted and itemsToBeRemoved nodes.
 */
tart.CircularCarousel.prototype.getItemsToBeInsertedAndRemoved = function(moveCount) {
    var i,
        previousItemsIndex = [],
        nextItemsIndex = [],
        start = this.firstVisible + moveCount;


    for (i = 0; i < this.lastVisible; i++) {
        previousItemsIndex.push(i);
    }

    for (i = start; i < start + this.itemPerViewport; i++) {
        nextItemsIndex.push(i);
    }

    var moveDiff = this.getMoveDiff(previousItemsIndex, nextItemsIndex, moveCount);

    return moveDiff;
};


/**
 * low level move which handles next and prev methods
 *
 * @param {string} direction 'next' or 'prev' direction of movement.
 * @param {*} moveCount item move count.
 * @override
 * @protected
 */
tart.CircularCarousel.prototype.move = function(direction, moveCount) {
    moveCount = moveCount || 1;
    moveCount = Math.abs(moveCount);
    moveCount = moveCount % this.itemCount;

    var tmpCursor = 0;

    //default event dispatched
    var eventToDispatch = tart.Carousel.EventTypes.NEXT;

    if (direction == 'prev') {
        moveCount = moveCount * -1;
        tmpCursor = this.itemCount;
        eventToDispatch = tart.Carousel.EventTypes.PREV;
    }

    var tmp = [].concat(this.items).concat(this.items);

    var moveDiff = this.getItemsToBeInsertedAndRemoved(moveCount);

    this.firstVisible = this.firstVisible + tmpCursor + moveCount;
    this.lastVisible = this.lastVisible + tmpCursor + moveCount;

    this.items = tmp.slice(this.firstVisible, this.firstVisible + this.itemCount);

    this.firstVisible = 0;
    this.lastVisible = this.itemPerViewport;

    var eventObj = {type: eventToDispatch,
        itemsToBeRemoved: moveDiff.itemsToBeRemoved,
        itemsToBeInserted: moveDiff.itemsToBeInserted};

    this.dispatchEvent(eventObj);
};