﻿/*
Example Usage:
$('#homecarousel').infiniteCarousel({showArrows: false, showNumeric: true, auto:true, speed:500, pause: 5000});
	
Example External Calls
<a href="#" class="next" target="homecarousel">Next</a>
<a href="#" class="prev" target="homecarousel">Previous</a>
<a href="1" class="goto" target="carousel1">Skip to Slide 1</a>
*/

(function($) {
    var PROP_NAME = 'infiniteCarousel';

    function InfiniteCarousel() {
        this._defaults = {
            width: "100%",
            viewportWidth: "100%",
            slidesPerPage: 1,
            speed: 800,
            auto: true,
            pause: 2000,
            showArrows: true,
            showNumeric: true,
            scaleImages: true,
            advanceBy: "page"
        }
    }

    $.extend(InfiniteCarousel.prototype, {
        markerClassName: 'infinite-carousel',

        _attachInfiniteCarousel: function(target, settings) {
            target = $(target);
            if (target.hasClass(this.markerClassName)) {
                return;
            }

            var inst = { settings: $.extend({}, this._defaults, settings) };

            target.css({ width: inst.settings.width });
            inst.wrapper = $('> div', target);
            inst.slider = inst.wrapper.find('> ul');
            inst.items = inst.slider.find('> li');
            inst.numitems = inst.items.length;

            //first thing we're trying to do is adjust the height of the slider to the height of the tallest slide
            //first we have to determine the proper widths of the slides based on the number of slides per page
            inst.wrapper.width(inst.settings.viewportWidth);
            inst.itemWidth = inst.wrapper.innerWidth() / inst.settings.slidesPerPage;
            inst.items.width(inst.itemWidth);

            var newHeight = this._getMaxHeight(inst.items) + 20;
            var newWidth = target.innerWidth();

            //var newHeight = Math.ceil(firstImage.outerHeight() * (newWidth / firstImage.outerWidth()));
            //target.find("img").css({ width: newWidth, height: newHeight });

            target.css({ height: newHeight });

            inst.wrapper.css({ overflow: 'hidden', height: newHeight });
            inst.single = inst.items.filter(':first');
            inst.singleWidth = inst.single.outerWidth();
            inst.visible = Math.ceil(inst.wrapper.innerWidth() / inst.singleWidth);


            //Padding with Empty Items
            if ((inst.settings.advanceBy != "item") && (inst.items.length % inst.visible) != 0) {
                inst.slider.append(this._repeat('<li class="empty" />', inst.visible - (inst.items.length % inst.visible)));
                inst.items = inst.slider.find('> li'); // reselect
            }
            inst.firstItem = inst.items.filter(':first');
            inst.lastItem = inst.items.filter(':last');


            inst.pages = Math.ceil(inst.items.length / inst.visible);

            //Generating Clones
            var firstpage, lastpage;
            firstpage = inst.items.slice(0, inst.visible).clone().addClass('cloned');
            lastpage = inst.items.slice(-inst.visible).clone().addClass('cloned');
            inst.firstItem.before(lastpage);
            inst.lastItem.after(firstpage);
            inst.items = inst.slider.find('> li'); // reselect
            inst.items.width(inst.itemWidth);

            inst.slider.width(inst.visible * inst.singleWidth * (inst.pages + 2)).css({ height: newHeight });
            inst.currentPage = 1;


            // Reset Scroll Left
            inst.wrapper.scrollLeft(inst.singleWidth * inst.visible);

            // add the numeric navigation
            if (inst.settings.showNumeric) {
                var temphtml = '<ol class="numeric pagination"></ol>';
                target.append(temphtml);

                var numitems = (inst.settings.advanceBy == "item") ? inst.numitems : inst.pages;
                for (var i = 0; i < numitems; i++) {
                    $(document.createElement("li"))
						.addClass('pagination' + (i + 1))
						.html('<a rel=' + i + ' href=""><span>' + (i + 1) + '</span></a>')
						.appendTo($(".pagination", target))
						.click(function(event) {
						    event.preventDefault();
						    event.stopPropagation();
						    target.infiniteCarousel("gotoPage", parseInt($("a", $(this)).attr('rel')) + 1);
						    return false;
						});
                };

                if (numitems == 1) {
                    $(".pagination", target).hide();
                }

                $(".pagination", target).prepend('<li class="back"><a href="#" class="back">&lt;</a></li>');
                $(".pagination", target).append('<li class="forward"><a href="#" class="forward">&gt;</a></li>');
                $('.pagination1', target).addClass('current');
            }




            // Navigation buttons
            if (inst.settings.showArrows && inst.items.length > 1) {
                inst.wrapper.after('<a href="#" class="arrow back"></a><a href="#" class="arrow forward"></a>');
            }

            // AUTOMATIC Scrolling
            if (inst.settings.auto && inst.numitems > 1) {
                var autoscrolling = true;
                setInterval(function() {
                    if (autoscrolling) {
                        target.trigger('next');
                    }
                }, inst.settings.pause);
            }


            $('a.back', target).bind("click.InfiniteCarousel", function(evt) {
                evt.preventDefault();
                return target.infiniteCarousel("gotoPage", inst.currentPage - 1);
            });

            $('a.forward', target).bind("click.InfiniteCarousel", function(evt) {
                evt.preventDefault();
                return target.infiniteCarousel("gotoPage", inst.currentPage + 1);
            });

            // this currently refers to the element the plugin was bound to
            target.bind('goto.InfiniteCarousel', function(event, page) {
                target._gotoPage(page);
            });

            target.bind('next.InfiniteCarousel', function(evt) {
                target.infiniteCarousel("gotoPage", inst.currentPage + 1);
            });

            target.bind('prev.InfiniteCarousel', function(evt) {
                target.infiniteCarousel("gotoPage", inst.currentPage - 1);
            });

            target.addClass(this.markerClassName).
            bind("goto.InfiniteCarousel", this._doGoto);
            $.data(target[0], PROP_NAME, inst);
        },

        /* Retrieve the instance data for the target control.
        @param  target  element - the target input field or division or span
        @return  object - the associated instance data
        @throws  error if a jQuery problem getting data */
        _getInst: function(target) {
            try {
                return $.data(target, PROP_NAME);
            }
            catch (err) {
                throw 'Missing instance data for this infiniteCarousel';
            }
        },

        _settingsInfiniteCarousel: function(target) {
            var inst = $.data(target, PROP_NAME);
            return inst.settings;
        },

        _repeat: function(str, n) {
            return new Array(n + 1).join(str);
        },

        _getMaxHeight: function(items) {
            var newHeight = 0;
            for (i = 0; i < items.length; i++) {
                var height = $(items[i]).outerHeight();
                newHeight = (height > newHeight) ? height : newHeight;
            }
            return newHeight;
        },

        _doGoto: function(event, page) {
            var target = $(event.target);
            $.infiniteCarousel._gotoPageInfiniteCarousel.apply($.infiniteCarousel, [target, page]);
        },

        _gotoPageInfiniteCarousel: function(target, page) {
            var target = $(target);
            var inst = this._getInst(target[0]);

            $('.current', target).removeClass('current');

            var numitems = (inst.settings.advanceBy == "item") ? inst.numitems : inst.pages;
            if (page > numitems) {
                $('.pagination1', target).addClass('current');
            } else if (page < 1) {
                $('.pagination' + numitems, target).addClass('current');
            } else {
                $('.pagination' + page, target).addClass('current');
            }



            var dir = page < inst.currentPage ? -1 : 1;
            var n = Math.abs(inst.currentPage - page);
            var left = inst.singleWidth * dir * inst.visible * n;

            if (inst.settings.advanceBy == "item") {
                left = inst.singleWidth * dir * n;
            }
            else {
                left = inst.singleWidth * dir * inst.visible * n;
            }

            inst.wrapper.filter(':not(:animated)').animate(
					{ scrollLeft: '+=' + left }, // what we are animating
					{
					duration: inst.settings.speed, // how fast we are animating
					easing: 'swing', // the type of easing
					complete: function() { // the callback
					    // if page == last page - then reset position
					    var currentPosition = inst.wrapper.scrollLeft();
					    var endPosition = inst.lastItem.position().left + inst.singleWidth;


					    if (currentPosition >= endPosition) {
					        var xvalue = inst.singleWidth * inst.visible;
					        inst.wrapper.scrollLeft(xvalue);
					        page = 1;
					    } else if (currentPosition <= 0) {
					        page = numitems;
					        inst.wrapper.scrollLeft(inst.singleWidth * (numitems));
					    }
					    inst.currentPage = page;
					}
	}
	        );
        }
    });


    // The list of commands that return values and don't permit chaining
    var getters = ['settings'];

    $.fn.infiniteCarousel = function(options) {
        var otherArgs = Array.prototype.slice.call(arguments, 1);
        if ($.inArray(options, getters) > -1) {
            return $.infiniteCarousel['_' + options + 'InfiniteCarousel'].
			apply($.infiniteCarousel, [this[0]].concat(otherArgs));
        }
        return this.each(function() {
            if (typeof options == 'string') {
                $.infiniteCarousel['_' + options + 'InfiniteCarousel'].
				apply($.infiniteCarousel, [this].concat(otherArgs));
            }
            else {
                $.infiniteCarousel._attachInfiniteCarousel(this, options || {});
            }
        });
    };

    $.infiniteCarousel = new InfiniteCarousel(); // singleton instance
})(jQuery);
