/*
 * File: jquery.flexisel.js
 * Version: 1.0.2
 * Description: Responsive carousel jQuery plugin
 * Author: 9bit Studios
 * Copyright 2012, 9bit Studios
 * http://www.9bitstudios.com
 * Free to use and abuse under the MIT license.
 * http://www.opensource.org/licenses/mit-license.php
 */
(function ($) {
    $.fn.flexisel = function(options) {
	
        var defaults = $.extend({
            visibleItems : 4,
            animationSpeed : 200,
            autoPlay : false,
            autoPlaySpeed : 3000,
            pauseOnHover : true,
            setMaxWidthAndHeight : false,
            enableResponsiveBreakpoints : true,
            flipPage: false,
            clone : true,
            responsiveBreakpoints : {
                portrait: { 
                    changePoint:480,
                    visibleItems: 1
                }, 
                landscape: { 
                    changePoint:640,
                    visibleItems: 2
                },
                tablet: { 
                    changePoint:768,
                    visibleItems: 3
                }
            }
        }, options);
        
        /******************************
        Private Variables
         *******************************/
         
        var object = $(this);
        var settings = $.extend(defaults, options);
        var itemsWidth; // Declare the global width of each item in carousel
        var canNavigate = true;
        var itemsVisible = settings.visibleItems; // Get visible items
        var totalItems = object.children().length; // Get number of elements
        var responsivePoints = [];
        
        /******************************
        Public Methods
        *******************************/
        var methods = {
            init : function() {
                return this.each(function() {
                    methods.appendHTML();
                    methods.setEventHandlers();
                    methods.initializeItems();
                });
            },
		    
            /******************************
            Initialize Items
            Fully initialize everything. Plugin is loaded and ready after finishing execution
	    *******************************/
            initializeItems : function() {

                var listParent = object.parent();
                var innerHeight = listParent.height();
                var childSet = object.children();
                methods.sortResponsiveObject(settings.responsiveBreakpoints);
                
                var innerWidth = listParent.width(); // Set widths
                itemsWidth = (innerWidth) / itemsVisible;
                childSet.width(itemsWidth);        
                if (settings.clone) {
                    childSet.last().insertBefore(childSet.first());
                    childSet.last().insertBefore(childSet.first());
                    object.css({
                        'left' : -itemsWidth
                    });
                }

                object.fadeIn();
                $(window).trigger("resize"); // needed to position arrows correctly

            },
            
	    /******************************
            Append HTML
            Add additional markup needed by plugin to the DOM
	    *******************************/
            appendHTML : function() {
                object.addClass("nbs-flexisel-ul");
                object.wrap("<div class='nbs-flexisel-container'><div class='nbs-flexisel-inner'></div></div>");
                object.find("li").addClass("nbs-flexisel-item");

                var flexiselInner = object.parent(); // flexisel-inner

                if (settings.setMaxWidthAndHeight) {
                    var baseWidth = $(".nbs-flexisel-item img").width();
                    var baseHeight = $(".nbs-flexisel-item img").height();
                    $(".nbs-flexisel-item img").css("max-width", baseWidth);
                    $(".nbs-flexisel-item img").css("max-height", baseHeight);
                }
                $("<div class='nbs-flexisel-nav-left'></div><div class='nbs-flexisel-nav-right'></div>").insertAfter(flexiselInner);
                if (settings.clone) {
                    var cloneContent = object.children().clone();
                    object.append(cloneContent);
                }
            },
            /******************************
            Set Event Handlers
	    Set events: click, resize, etc
            *******************************/
            setEventHandlers : function() {

                var listParent = object.parent();
                var flexiselInner = listParent.parent();
                var childSet = object.children();
                var leftArrow = flexiselInner.find(".nbs-flexisel-nav-left");
                var rightArrow = flexiselInner.find(".nbs-flexisel-nav-right");

                $(window).on("resize", function(event) {

                    methods.setResponsiveEvents();

                    var innerWidth = $(listParent).width();
                    var innerHeight = $(listParent).height();

                    itemsWidth = (innerWidth) / itemsVisible;

                    childSet.width(itemsWidth);
                    if (settings.clone) {
                        object.css({
                            'left' : -itemsWidth                            
                        });
                    }else {
                        object.css({
                            'left' : 0
                        });
                    }

                    // Hide the arrows if the elements are the same of the visible
                    if (!settings.clone && totalItems <= itemsVisible) {
                      leftArrow.add(rightArrow).css('visibility', 'hidden');
                    }
                    else {
                      leftArrow.add(rightArrow).css('visibility', 'visible');

                      var halfArrowHeight = (leftArrow.height()) / 2;
                      var arrowMargin = (innerHeight / 2) - halfArrowHeight;
                      leftArrow.css("top", arrowMargin + "px");
                      rightArrow.css("top", arrowMargin + "px");
                    }

                });
                $(leftArrow).on("click", function(event) {
                    methods.scrollLeft();
                });
                $(rightArrow).on("click", function(event) {
                    methods.scrollRight();
                });
                if (settings.pauseOnHover === true) {
                    $(".nbs-flexisel-item").on({
                        mouseenter : function() {
                            canNavigate = false;
                        },
                        mouseleave : function() {
                            canNavigate = true;
                        }
                    });
                }
                if (settings.autoPlay === true) {

                    setInterval(function() {
                        if (canNavigate === true)
                            methods.scrollRight();
                    }, settings.autoPlaySpeed);
                }
                
                object[0].addEventListener('touchstart', methods.touchHandler.handleTouchStart, false);        
                object[0].addEventListener('touchmove', methods.touchHandler.handleTouchMove, false);

            },
            /******************************
            Set Responsive Events
            Set breakpoints depending on responsiveBreakpoints
            *******************************/            
            
            setResponsiveEvents: function() {
                var contentWidth = $('html').width();
                
                if(settings.enableResponsiveBreakpoints) {
                    
                    var largestCustom = responsivePoints[responsivePoints.length-1].changePoint; // sorted array 
                    
                    for(var i in responsivePoints) {
                        
                        if(contentWidth >= largestCustom) { // set to default if width greater than largest custom responsiveBreakpoint 
                            itemsVisible = settings.visibleItems;
                            break;
                        }
                        else { // determine custom responsiveBreakpoint to use
                        
                            if(contentWidth < responsivePoints[i].changePoint) {
                                itemsVisible = responsivePoints[i].visibleItems;
                                break;
                            }
                            else
                                continue;
                        }
                    }
                }
            },

            /******************************
            Sort Responsive Object
            Gets all the settings in resposiveBreakpoints and sorts them into an array
            *******************************/            
            
            sortResponsiveObject: function(obj) {
                
                var responsiveObjects = [];
                
                for(var i in obj) {
                    responsiveObjects.push(obj[i]);
                }
                
                responsiveObjects.sort(function(a, b) {
                    return a.changePoint - b.changePoint;
                });
            
                responsivePoints = responsiveObjects;
            },
            
            /******************************
            Scroll Left
            *******************************/
            scrollLeft : function() {
                if (object.position().left < 0) {
                    if (canNavigate === true) {
                        canNavigate = false;

                        var listParent = object.parent();
                        var innerWidth = listParent.width();

                        itemsWidth = (innerWidth) / itemsVisible;

                        var childSet = object.children();
			var increment = (settings.flipPage)? innerWidth: itemsWidth;
			
                        object.animate({
                            'left' : "+=" + increment
                        }, {
                            queue : false,
                            duration : settings.animationSpeed,
                            easing : "linear",
                            complete : function() {
                                if (settings.clone) {
                                    childSet.last().insertBefore(childSet.first()); // Get the first list item and put it after the last list item (that's how the infinite effects is made)                                   
                                }
                                methods.adjustScroll();
                                canNavigate = true;
                            }
                        });
                    }
                }
            },
            /******************************
            Scroll Right
            *******************************/            
            scrollRight : function() {
                var listParent = object.parent();
                var innerWidth = listParent.width();

                itemsWidth = (innerWidth) / itemsVisible;

                var difObject = (itemsWidth - innerWidth);
                var objPosition = (object.position().left + ((totalItems-itemsVisible)*itemsWidth)-innerWidth);    
                
                var increment = (settings.flipPage)? innerWidth: itemsWidth;
                
                if((difObject <= Math.ceil(objPosition)) && (!settings.clone)){
                    if (canNavigate === true) {
                        canNavigate = false;                    
    
                        object.animate({
                            'left' : "-=" + increment
                        }, {
                            queue : false,
                            duration : settings.animationSpeed,
                            easing : "linear",
                            complete : function() {                                
                                methods.adjustScroll();
                                canNavigate = true;
                            }
                        });
                    }
                } else if(settings.clone){
                    if (canNavigate === true) {
                        canNavigate = false;
    
                        var childSet = object.children();
    
                        object.animate({
                            'left' : "-=" + increment
                        }, {
                            queue : false,
                            duration : settings.animationSpeed,
                            easing : "linear",
                            complete : function() {                                
                                childSet.first().insertAfter(childSet.last()); // Get the first list item and put it after the last list item (that's how the infinite effects is made)                                
                                methods.adjustScroll();
                                canNavigate = true;
                            }
                        });
                    }
                };                
            },
            /******************************
            Adjust Scroll 
             *******************************/
            adjustScroll : function() {
                var listParent = object.parent();
                var childSet = object.children();

                var innerWidth = listParent.width();
                itemsWidth = (innerWidth) / itemsVisible;
                childSet.width(itemsWidth);
                
                var increment = (settings.flipPage)? innerWidth: itemsWidth;
                
                if (settings.clone) {
                    object.css({
                        'left' : -increment
                    });
                }
            },
            touchHandler: {

                xDown: null,
                yDown: null,
                handleTouchStart: function(evt) {                                         
                    this.xDown = evt.touches[0].clientX;                                      
                    this.yDown = evt.touches[0].clientY;
                }, 
                handleTouchMove: function (evt) {
                    if (!this.xDown || !this.yDown) {
                        return;
                    }

                    var xUp = evt.touches[0].clientX;                                    
                    var yUp = evt.touches[0].clientY;

                    var xDiff = this.xDown - xUp;
                    var yDiff = this.yDown - yUp;
                    
                    // only comparing xDiff
                    // compare which is greater against yDiff to determine whether left/right or up/down  e.g. if (Math.abs( xDiff ) > Math.abs( yDiff ))
                    if (Math.abs( xDiff ) > 0) {
                        if ( xDiff > 0 ) {
                            // swipe left
                            methods.scrollRight();
                        } else {
                            //swipe right
                            methods.scrollLeft();
                        }                       
                    }
                    
                    /* reset values */
                    this.xDown = null;
                    this.yDown = null;
                    canNavigate = true;
                }
            }            
        };
        if (methods[options]) { // $("#element").pluginName('methodName', 'arg1', 'arg2');
            return methods[options].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof options === 'object' || !options) { // $("#element").pluginName({ option: 1, option:2 });
            return methods.init.apply(this);
        } else {
            $.error('Method "' + method + '" does not exist in flexisel plugin!');
        }
    };
})(jQuery);
