function TouchCatsCarouselManager(opts) {
    var options = opts;
    var scarousel = {};
    var manager = {
        loadInitItems: function(carousel) {
            //alert("abc");
            var offset = 0;
            var max = options.pageSize;
            scarousel = carousel;
            manager.makeRequest(options.fetchUrl, max, offset);
            manager.makeRequest(options.fetchUrl, max, offset + max);
        },
        /* Overridable functions */
        composeQueryString: function(max, offset) {
            return "max=" + max + "&offset=" + offset;
        },
        validateData: function(data) {
            return $.trim(data).length > 0 && $(data).children("div.Item").size() > 0;
        },

        /* Private function */
        handleSuccess: function(data, max, offset) {
            if (manager.validateData(data)) { // only add if the server return real data
                scarousel.addOrUpdate(offset/max, data);
            }
        },
        makeRequest: function(url, max, offset) {
            $.ajax({
                async: options.async,
                url: url,
                type: "POST",
                dataType: "html",
                data: manager.composeQueryString(max, offset),
                success: function(data, textStatus, request) {
                    manager.handleSuccess(data, max, offset);
                },
                error: function(request, textStatus, errorThrown) {
                    //alert("textStatus" + textStatus + " errorThrown:" + errorThrown);
                }
            });
        },
        animationEndCallback: function(direction, isJump) {
            if (isJump) {
                scarousel.resetJump();
                return;
            }
            
            if (direction == "next") {
                manager.moveIndicatorForward();
                var listSize = scarousel.size();
                var preload = scarousel.last + options.numPreload - listSize;
                var i = 0;
                while (preload > 0) {
                    manager.makeRequest(options.fetchUrl, options.pageSize, (listSize + i) * options.pageSize);
                    preload --;
                    i++;
                }
            } else if (direction == "prev") {
                manager.moveIndicatorBackward();
            }
        },
        moveIndicatorForward: function(){
            var $currentPageItem = $("#" + options.indicatorId + " a." + options.indicatorCurrentPageClass);
            var $nextItem = $currentPageItem.next();
            if ($nextItem.size() > 0) {
                $currentPageItem.removeClass(options.indicatorCurrentPageClass);
                $nextItem.addClass(options.indicatorCurrentPageClass);
            }
        },
        moveIndicatorBackward: function() {
            var $currentPageItem = $("#" + options.indicatorId + " a." + options.indicatorCurrentPageClass);
            var $nextItem = $currentPageItem.prev();
            if ($nextItem.size() > 0) {
                $currentPageItem.removeClass(options.indicatorCurrentPageClass);
                $nextItem.addClass(options.indicatorCurrentPageClass);
            }
        },
        init: function() {
            carouselInstance = $("#" + options.elementId).scarousel({
                scrollInc: 1,
                numVisible: 1,
                initCallback: manager.loadInitItems,
                loadNextHandler: null,
                loadPrevHandler: null,
                animation: {
                    animationStartCallback: manager.animationStartCallback,
                    animationEndCallback: manager.animationEndCallback,
                    css3: {
                        //animationClass: "slide",
                        animation: "slide",
                        duration: "0.3s",
                        easing: "ease-in-out"
                    },
                    jquery: {
                        animation: "normal",
                        easing: "swing"
                    }
                }
            });
            
            $("#" + options.indicatorId + " a").click(function() {
                var $currentPageItem = $("#" + options.indicatorId + " a." + options.indicatorCurrentPageClass);
                var currentIndex = parseInt($currentPageItem.attr("rel"));
                var index = parseInt(jQuery(this).attr("rel"));
                var times = index - currentIndex;
                if (times == 0) return;
                
                if (times > 0) {
                    var listSize = scarousel.size();
                    var preload = scarousel.last + options.numPreload - listSize + (times - 1);
                    var i = 0;
                    while (preload > 0) {
                        manager.makeRequest(options.fetchUrl, options.pageSize, (listSize + i) * options.pageSize);
                        preload --;
                        i++;
                    }
                    $("#" + options.elementId).scarousel("goNext", times);
                } else {
                    $("#" + options.elementId).scarousel("goPrev", Math.abs(times));
                }
                
                jQuery(this).addClass("Current");
                $currentPageItem.removeClass("Current");
            });
            
            $("#" + options.elementId + " .carousel-clip-region").touchswipe({
                wipeLeft: function() {
                    $("#" + options.elementId).scarousel("next");
                },
                wipeRight: function() {
                    $("#" + options.elementId).scarousel("prev");
                },
                vertical: false,
                gestureMinDelta: 20,
                preventDefaultEvents: true
            });
        }
    };
    return manager;
};

