﻿(function($) {
    // global jquery function
    jQuery.fn.carousel = function(options) {
        var api = this.eq(typeof options == 'number' ? options : 0).data('carousel');
        if (api) {
            return api;
        }

        var defaultOptions = {
            // classes
            activeClass: 'active',
            disabledClass: 'disabled',
            hoverClass: 'hover',

            // selectors
            contentSel: '.content',
            pagerSel: '.pager',
            pagerTagSel: 'a', // anchor
            reverseSel: '.reverse',
            forwardSel: '.forward',
            reversePageSel: '.reversePage',
            forwardPageSel: '.forwardPage',

            // misc
            debug: true,
            contentCount: 5,
            canClick: true,
            speed: 450,
            easingType: 'swing'
        };

        $.extend(defaultOptions, options);
        this.each(function() {
            var element = new carousel($(this), defaultOptions);
            $(this).data('carousel', element);
        });

        return this;
    };

    function carousel(base, options) {
        var self = this;
        var wrapper = $(options.contentSel, base);
        var currIndex = 0;

        // nav selectors
        var pagerSel = base.siblings(options.pagerSel).eq(0);
        var reversePageSel = base.siblings(options.reversePageSel).eq(0);
        var forwardPageSel = base.siblings(options.forwardPageSel).eq(0);
        var reverseSel = base.siblings(options.reverseSel).eq(0);
        var forwardSel = base.siblings(options.forwardSel).eq(0);

        // extend new members
        $.extend(self, {

            // get/set - start

            getContent: function() {
                return wrapper.children();
            },

            getCount: function() {
                return self.getContent().size();
            },

            getPagerIndex: function() {
                return Math.ceil(currIndex / options.contentCount);
            },

            getPagerCount: function() {
                return Math.ceil(this.getCount() / options.contentCount);
            },

            hidePager: function() {
                pagerSel.children().addClass(options.disabledClass);
                reverseSel.addClass(options.disabledClass);
                forwardSel.addClass(options.disabledClass);
            },

            getCurrIndex: function() {
                return currIndex;
            },

            setCurrIndex: function(idx, time, fn) {
                time = time || options.speed;

                // fn?
                if ($.isFunction(time)) {
                    fn = time;
                    time = options.speed;
                }

                if (idx < 0) {
                    idx = 0;
                }
                if (idx > self.getCount() - options.contentCount) {
                    return self;
                }

                var item = self.getContent().eq(idx);
                if (!item.length) {
                    return self;
                }

                var left = -(item.outerWidth(true) * idx);
                wrapper.animate(
                    { left: left },
                    time,
                    options.easingType,
                    fn ? function() {
                        fn.call(self);
                    } : null);

                // pager
                if (pagerSel.length) {
                    var active = options.activeClass;
                    var page = Math.ceil(idx / options.contentCount);
                    page = Math.min(page, pagerSel.children().length - 1);
                    pagerSel.children().removeClass(active).eq(page).addClass(active);
                }

                // reverse button
                if (0 === idx) {
                    reverseSel.add(reversePageSel).addClass(options.disabledClass);
                } else {
                    reverseSel.add(reversePageSel).removeClass(options.disabledClass);
                }

                // forward button
                if (idx >= self.getCount() - options.contentCount) {
                    forwardSel.add(forwardPageSel).addClass(options.disabledClass);
                } else {
                    forwardSel.add(forwardPageSel).removeClass(options.disabledClass);
                }

                currIndex = idx; // current index

                return self;
            },

            // get/set - start

            // movement - start

            start: function(time, fn) {
                return this.setCurrIndex(0, time, fn);
            },

            end: function(time, fn) {
                return this.setCurrIndex(this.getCount() - options.contentCount, time, fn);
            },

            seek: function(offset, time, fn) {
                var location = currIndex + offset;
                return this.setCurrIndex(location, time, fn);
            },

            forward: function(time, fn) {
                return this.seek(1, time, fn);
            },

            reverse: function(time, fn) {
                return this.seek(-1, time, fn);
            },

            // movement - end

            // paging - start

            movePage: function(offset, time, fn) {
                return this.seek(options.contentCount * offset, time, fn);
            },

            setPage: function(page, time, fn) {
                var count = parseInt(options.contentCount);
                var index = count * parseInt(page);
                var isLast = (index + count) >= this.getCount();
                if (isLast) {
                    index = this.getCount() - options.contentCount;
                }
                return this.setCurrIndex(index, time, fn);
            },

            reversePage: function(time, fn) {
                return this.setPage(this.getPagerIndex() - 1, time, fn);
            },

            forwardPage: function(time, fn) {
                return this.setPage(this.getPagerIndex() + 1, time, fn);
            },

            // paging - end

            // actions - start

            click: function(currIndex, time, fn) {
                var item = self.getContent().eq(currIndex);
                var active = options.activeClass;

                if (!item.hasClass(active) && ((currIndex >= 0) || (currIndex < this.getCount()))) {
                    self.getContent().removeClass(active);
                    item.addClass(active);
                    var delta = Math.floor(options.contentCount / 2);
                    var location = currIndex - delta;

                    // next to last item must work
                    if (location > self.getCount() - options.contentCount) {
                        --location;
                    }

                    if (location !== currIndex) {
                        return this.setCurrIndex(location, time, fn);
                    }
                }

                return self;
            }

            // actions - end
        });

        // reverse button		
        reverseSel.addClass(options.disabledClass).click(function() {
            self.reverse();
        });

        // forward button
        forwardSel.click(function() {
            self.forward();
        });

        // reverse page button
        forwardPageSel.click(function() {
            self.forwardPage();
        });

        // forward page button
        reversePageSel.addClass(options.disabledClass).click(function() {
            self.reversePage();
        });

        // pager
        function load() {
            pagerSel.each(function() {
                var pager = $(this);
                if ((pager.children().length == 0) || (pager.data('self') == self)) {
                    pager.empty();
                    pager.data('self', self);

                    for (var i = 0; i < self.getPagerCount(); ++i) {
                        var kid = $('<' + options.pagerTagSel + '/>').attr('href', i).click(function(e) {
                            var element = $(this);
                            self.setPage(element.attr('href'));
                            return e.preventDefault();
                        });

                        if (0 === i) {
                            kid.addClass(options.activeClass);
                        }
                        pager.append(kid);
                    }
                }
                else {
                    pager.children().each(function(i) {
                        var kid = $(this);
                        kid.attr('href', i);
                        if (0 === i) {
                            kid.addClass(options.activeClass);
                        }

                        kid.click(function() {
                            pager.find('.' + options.activeClass).removeClass(options.activeClass);
                            kid.addClass(options.activeClass);
                            self.setPage(kid.attr('href'));
                        });
                    });
                }
            });

            // hover stuff
            if (options.hoverClass) {
                self.getContent().hover(function() {
                    $(this).addClass(options.hoverClass);
                }, function() {
                    $(this).removeClass(options.hoverClass);
                });
            }

            // content click (if specified)
            if (options.canClick) {
                self.getContent().each(function(currIndex, arg) {
                    var element = $(this);
                    if (!element.data('click')) {
                        element.bind('click.carousel', function() { // use namespace
                            self.click(currIndex);
                        });
                        element.data('click', true);
                    }
                });
            }

            return self;
        }

        load();
        
        // now that loaded, hide paging if required
        if (self.getCount() <= options.contentCount) {
            self.hidePager();
        }
    }
})(jQuery);
