/* jshint browser: true, jquery: true, devel: true */
/* global window, jQuery */

(function($, window, undefined) {
  'use strict';

  function throttle(func, delay) {
      var timer = null;

      return function() {
          var context = this,
              args = arguments;

          if ( timer === null ) {
              timer = setTimeout(function() {
                  func.apply(context, args);
                  timer = null;
              }, delay);
          }
      };
  }

  // Check for browser CSS support and cache the result for subsequent calls.
  var checkStyleSupport = (function() {
      var support = {};
      return function(prop) {
          if ( support[prop] !== undefined ) { return support[prop]; }

          var div = document.createElement('div'),
              style = div.style,
              ucProp = prop.charAt(0).toUpperCase() + prop.slice(1),
              prefixes = ["webkit", "moz", "ms", "o"],
              props = (prop + ' ' + (prefixes).join(ucProp + ' ') + ucProp).split(' ');

          for (var i in props) {
              if ( props[i] in style ) { return support[prop] = props[i]; }
          }

          return support[prop] = false;
      };
  }());

  var svgNS = 'http://www.w3.org/2000/svg',
      svgSupport = (function() {
          var support;
          return function() {
              if ( support !== undefined ) { return support; }
              var div = document.createElement('div');
              div.innerHTML = '<svg/>';
              support = ( div.firstChild && div.firstChild.namespaceURI === svgNS );
              return support;
          };
      }());

  var $window = $(window),

      transformSupport = checkStyleSupport('transform'),

      defaults = {
          itemContainer: 'ul',
          // [string|object]
          // Selector for the container of the flippin' items.

          itemSelector: 'li',
          // [string|object]
          // Selector for children of `itemContainer` to flip

          start: 'center',
          // ['center'|number]
          // Zero based index of the starting item, or use 'center' to start in the middle

          fadeIn: 400,
          // [milliseconds]
          // Speed of the fade in animation after items have been setup

          loop: false,
          // [true|false|number]
          // Loop around when the start or end is reached
          // If number, this is the number of items that will be shown when the beginning or end is reached

          autoplay: false,
          // [false|milliseconds]
          // If a positive number, Flipster will automatically advance to next item after that number of milliseconds

          pauseOnHover: true,
          // [true|false]
          // If true, autoplay advancement will pause when Flipster is hovered

          style: 'coverflow',
          // [coverflow|carousel|flat|...]
          // Adds a class (e.g. flipster--coverflow) to the flipster element to switch between display styles
          // Create your own theme in CSS and use this setting to have Flipster add the custom class

          spacing: -0.6,
          // [number]
          // Space between items relative to each item's width. 0 for no spacing, negative values to overlap

          click: true,
          // [true|false]
          // Clicking an item switches to that item

          keyboard: true,
          // [true|false]
          // Enable left/right arrow navigation

          scrollwheel: true,
          // [true|false]
          // Enable mousewheel/trackpad navigation; up/left = previous, down/right = next

          touch: true,
          // [true|false]
          // Enable swipe navigation for touch devices

          nav: false,
          // [true|false|'before'|'after']
          // If not false, Flipster will build an unordered list of the items
          // Values true or 'before' will insert the navigation before the items, 'after' will append the navigation after the items

          buttons: false,
          // [true|false|'custom']
          // If true, Flipster will insert Previous / Next buttons with SVG arrows
          // If 'custom', Flipster will not insert the arrows and will instead use the values of `buttonPrev` and `buttonNext`

          buttonPrev: 'Previous',
          // [text|html]
          // Changes the text for the Previous button

          buttonNext: 'Next',
          // [text|html]
          // Changes the text for the Next button

          onItemSwitch: false
          // [function]
          // Callback function when items are switched
          // Arguments received: [currentItem, previousItem]
      },

      classes = {
          main: 'flipster',
          active: 'flipster--active',
          container: 'flipster__container',

          nav: 'flipster__nav',
          navChild: 'flipster__nav__child',
          navItem: 'flipster__nav__item',
          navLink: 'flipster__nav__link',
          navCurrent: 'flipster__nav__item--current',
          navCategory: 'flipster__nav__item--category',
          navCategoryLink: 'flipster__nav__link--category',

          button: 'flipster__button',
          buttonPrev: 'flipster__button--prev',
          buttonNext: 'flipster__button--next',

          item: 'flipster__item',
          itemCurrent: 'flipster__item--current',
          itemPast: 'flipster__item--past',
          itemFuture: 'flipster__item--future',
          itemContent: 'flipster__item__content'
      },

      classRemover = new RegExp('\\b(' + classes.itemCurrent + '|' + classes.itemPast + '|' + classes.itemFuture + ')(.*?)(\\s|$)', 'g'),
      whiteSpaceRemover = new RegExp('\\s\\s+', 'g');

  $.fn.flipster = function(options) {
      var isMethodCall = (typeof options === 'string' ? true : false);

      if ( isMethodCall ) {
          var args = Array.prototype.slice.call(arguments, 1);
          return this.each(function() {
              var methods = $(this).data('methods');
              if ( methods[options] ) {
                  return methods[options].apply(this, args);
              } else {
                  return this;
              }
          });
      }

      var settings = $.extend({}, defaults, options);

      return this.each(function() {

          var self = $(this),
              methods,

              _container,
              _containerWidth,

              _items,
              _itemOffsets = [],
              _currentItem,
              _currentIndex = 0,

              _nav,
              _navItems,
              _navLinks,

              _playing = false,
              _startDrag = false,
              _isItemClicked = false;

          function buildButtonContent(dir) {
              var text = ( dir === 'next' ? settings.buttonNext : settings.buttonPrev );

              if ( settings.buttons === 'custom' || !svgSupport ) { return text; }

              return '<svg viewBox="0 0 13 20" xmlns="' + svgNS + '" aria-labelledby="title"><title>' + text + '</title><polyline points="10,3 3,10 10,17"' + (dir === 'next' ? ' transform="rotate(180 6.5,10)"' : '') + '/></svg>';
          }

          function buildButton(dir) {
              dir = dir || 'next';

              return $('<button class="' + classes.button + ' ' + ( dir === 'next' ? classes.buttonNext : classes.buttonPrev ) + '" role="button" />')
                  .html(buildButtonContent(dir))
                  .on('click', function(e) {
                      jump(dir);
                      e.preventDefault();
                  });

          }

          function buildButtons() {
              if ( settings.buttons && _items.length > 1 ) {
                  self.find('.' + classes.button).remove();
                  self.append(buildButton('prev'), buildButton('next'));
              }
          }

          function buildNav() {
              var navCategories = {};

              if ( !settings.nav || _items.length <= 1 ) { return; }

              if ( _nav ) { _nav.remove(); }

              _nav = $('<ul class="' + classes.nav + '" role="navigation" />');
              _navLinks = $('');

              _items.each(function(i) {
                  var item = $(this),
                      category = item.data('flip-category'),
                      itemTitle = item.data('flip-title') || item.attr('title') || i,
                      navLink = $('<a href="#" class="' + classes.navLink + '">' + itemTitle + '</a>')
                      .data('index', i);

                  _navLinks = _navLinks.add(navLink);

                  if ( category ) {

                      if ( !navCategories[category] ) {

                          var categoryItem = $('<li class="' + classes.navItem + ' ' + classes.navCategory + '">');
                          var categoryLink = $('<a href="#" class="' + classes.navLink + ' ' + classes.navCategoryLink + '" data-flip-category="' + category + '">' + category + '</a>')
                                  .data('category', category)
                                  .data('index', i);

                          navCategories[category] = $('<ul class="' + classes.navChild + '" />');

                          _navLinks = _navLinks.add(categoryLink);

                          categoryItem
                              .append(categoryLink, navCategories[category])
                              .appendTo(_nav);
                      }

                      navCategories[category].append(navLink);
                  } else {
                      _nav.append(navLink);
                  }

                  navLink.wrap('<li class="' + classes.navItem + '">');

              });

              _nav.on('click', 'a', function(e) {
                  var index = $(this).data('index');
                  if ( index >= 0 ) {
                      jump(index);
                      e.preventDefault();
                  }
              });

              if ( settings.nav === 'after' ) { self.append(_nav); }
              else { self.prepend(_nav); }

              _navItems = _nav.find('.' + classes.navItem);
          }

          function updateNav() {
              if ( settings.nav ) {

                  var category = _currentItem.data('flip-category');

                  _navItems.removeClass(classes.navCurrent);

                  _navLinks
                      .filter(function() {
                          return ($(this).data('index') === _currentIndex || (category && $(this).data('category') === category));
                      })
                      .parent()
                      .addClass(classes.navCurrent);

              }
          }

          function noTransition() {
              self.css('transition', 'none');
              _container.css('transition', 'none');
              _items.css('transition', 'none');
          }

          function resetTransition() {
              self.css('transition', '');
              _container.css('transition', '');
              _items.css('transition', '');
          }

          function calculateBiggestItemHeight() {
              var biggestHeight = 0,
                  itemHeight;

              _items.each(function() {
                  itemHeight = $(this).height();
                  if ( itemHeight > biggestHeight ) { biggestHeight = itemHeight; }
              });
              return biggestHeight;
          }

          function resize(skipTransition) {

              //check for hidden parents
              
              if(hasHiddenParent() == true)
              return(false);

              if ( skipTransition ) { noTransition(); }

              _containerWidth = _container.width();
             
              _container.height(calculateBiggestItemHeight());

              _items.each(function(i) {
                  var item = $(this),
                      width,
                      left;

                  item.attr('class', function(i, c) {
                      return c && c.replace(classRemover, '').replace(whiteSpaceRemover, ' ');
                  });

                  width = item.outerWidth();

                  if ( settings.spacing !== 0 ) {
                      item.css('margin-right', ( width * settings.spacing ) + 'px');
                  }

                  left = item.position().left;
                  _itemOffsets[i] = -1 * ((left + (width / 2)) - (_containerWidth / 2));

                  if ( i === _items.length - 1 ) {
                      center();
                      if ( skipTransition ) { setTimeout(resetTransition, 1); }
                  }
              });
          }

          function center() {
              var total = _items.length,
                  loopCount = ( settings.loop !== true && settings.loop > 0 ? settings.loop : false ),
                  item, newClass, zIndex, past, offset;

              if ( _currentIndex >= 0 ) {

                  _items.each(function(i) {
                      item = $(this);
                      newClass = ' ';

                      if ( i === _currentIndex ) {
                          newClass += classes.itemCurrent;
                          zIndex = (total + 2);
                      } else {
                          past = ( i < _currentIndex ? true : false );
                          offset = ( past ? _currentIndex - i : i - _currentIndex );

                          if ( loopCount ) {
                              if ( _currentIndex <= loopCount && i > _currentIndex + loopCount ) {
                                  past = true;
                                  offset = (total + _currentIndex) - i;
                              } else if ( _currentIndex >= total - loopCount && i < _currentIndex - loopCount ) {
                                  past = false;
                                  offset = (total - _currentIndex) + i;
                              }
                          }

                          newClass += (past ?
                              classes.itemPast + ' ' + classes.itemPast + '-' + offset :
                              classes.itemFuture + ' ' + classes.itemFuture + '-' + offset
                          );

                          zIndex = total - offset;
                      }

                      item
                          .css('z-index', zIndex * 2)
                          .attr('class', function(i, c) {
                              return c && c.replace(classRemover, '').replace(whiteSpaceRemover, ' ') + newClass;
                          });
                  });

                  if ( !_containerWidth || _itemOffsets[_currentIndex] === undefined ) { resize(true); }

                  if ( transformSupport ) {
                      _container.css('transform', 'translateX(' + _itemOffsets[_currentIndex] + 'px)');
                  } else {
                      _container.css('left', _itemOffsets[_currentIndex] + 'px' );
                  }
              }

              updateNav();
          }

          function jump(to) {
              var _previous = _currentIndex;

              if ( _items.length <= 1 ) { return; }

              if ( to === 'prev' ) {
                  if ( _currentIndex > 0 ) { _currentIndex--; }
                  else if ( settings.loop ) { _currentIndex = _items.length - 1; }
              } else if ( to === 'next' ) {
                  if ( _currentIndex < _items.length - 1 ) { _currentIndex++; }
                  else if ( settings.loop ) { _currentIndex = 0; }
              } else if ( typeof to === 'number' ) { _currentIndex = to;
              } else if ( to !== undefined ) {
                  // if object is sent, get its index
                  _currentIndex = _items.index(to);
              }

              _currentItem = _items.eq(_currentIndex);

              if ( _currentIndex !== _previous && settings.onItemSwitch ) {
                  settings.onItemSwitch.call(self, _items[_currentIndex], _items[_previous]);
              }

              center();

              return self;
          }

          function play(interval) {
              settings.autoplay = interval || settings.autoplay;

              clearInterval(_playing);

              _playing = setInterval(function() {
                  var prev = _currentIndex;
                  jump('next');
                  if ( prev === _currentIndex && !settings.loop ) { clearInterval(_playing); }
              }, settings.autoplay);

              return self;
          }

          function pause() {
              clearInterval(_playing);
              if ( settings.autoplay ) { _playing = -1; }

              return self;
          }

          function show() {
              resize(true);
              self.hide()
                  .css('visibility', '')
                  .addClass(classes.active)
                  .fadeIn(settings.fadeIn);
          }

          function index() {

              _container = self.find(settings.itemContainer).addClass(classes.container);

              _items = _container.find(settings.itemSelector);

              if ( _items.length <= 1 ) { return; }

              _items
                  .addClass(classes.item)
                  // Wrap inner content
                  .each(function() {
                      var item = $(this);
                      if ( !item.children('.' + classes.itemContent ).length) {
                          item.wrapInner('<div class="' + classes.itemContent + '" />');
                      }
                  });

              // Navigate directly to an item by clicking
              if ( settings.click ) {
                  _items.on('click.flipster touchend.flipster', function(e) {
                      
                      if ( !_startDrag ) {
                          if ( !$(this).hasClass(classes.itemCurrent) ) { e.preventDefault(); }
                          jump(this);
                      }
                  });
              }

              // Insert navigation if enabled.
              buildButtons();
              buildNav();

              if ( _currentIndex >= 0 ) { jump(_currentIndex); }

              return self;
          }

          function keyboardEvents(elem) {
              if ( settings.keyboard ) {
                  elem[0].tabIndex = 0;
                  elem.on('keydown.flipster', throttle(function(e) {
                      var code = e.which;
                      if ( code === 37 || code === 39 ) {
                          jump( code === 37 ? 'prev' : 'next' );
                          e.preventDefault();
                      }
                  }, 250, true));
              }
          }

          function wheelEvents(elem) {
              if ( settings.scrollwheel ) {
                  var _wheelInside = false,
                      _actionThrottle = 0,
                      _throttleTimeout = 0,
                      _delta = 0,
                      _dir, _lastDir;

                  elem
                      .on('mousewheel.flipster wheel.flipster', function() { _wheelInside = true; })
                      .on('mousewheel.flipster wheel.flipster', throttle(function(e) {

                          // Reset after a period without scrolling.
                          clearTimeout(_throttleTimeout);
                          _throttleTimeout = setTimeout(function() {
                              _actionThrottle = 0;
                              _delta = 0;
                          }, 300);

                          e = e.originalEvent;

                          // Add to delta (+=) so that continuous small events can still get past the speed limit, and quick direction reversals get cancelled out
                          _delta += (e.wheelDelta || (e.deltaY + e.deltaX) * -1); // Invert numbers for Firefox

                          // Don't trigger unless the scroll is decent speed.
                          if ( Math.abs(_delta) < 25 ) { return; }

                          _actionThrottle++;

                          _dir = (_delta > 0 ? 'prev' : 'next');

                          // Reset throttle if direction changed.
                          if ( _lastDir !== _dir ) { _actionThrottle = 0; }
                          _lastDir = _dir;

                          // Regular scroll wheels trigger less events, so they don't need to be throttled. Trackpads trigger many events (inertia), so only trigger jump every three times to slow things down.
                          if ( _actionThrottle < 6 || _actionThrottle % 3 === 0 ) { jump(_dir); }

                          _delta = 0;

                      }, 50));

                  // Disable mousewheel on window if event began in elem.
                  elem.on('mousewheel.flipster wheel.flipster', function(e) {
                      if ( _wheelInside ) {
                          e.preventDefault();
                          _wheelInside = false;
                      }
                  });
              }
          }

          function touchEvents(elem) {
              
              if ( settings.touch ) {
                  
                  var _startDragY, _startDragX = false,
                  _touchJump = throttle(jump, 300),
                  x, y, offsetY, offsetX;
                  
                  var lastPosition = _itemOffsets[_currentIndex];   
                  var isMousePressed = false;                     

                  elem.on({

                      'touchstart.flipster': function(e) {
                      
                          e = e.originalEvent;
                          _startDragX = (e.touches ? e.touches[0].clientX : e.clientX);
                          _startDragY = (e.touches ? e.touches[0].clientY : e.clientY);
                          //e.preventDefault();
                          lastPosition = _itemOffsets[_currentIndex];
                          _isItemClicked = true;
                      },

                      'touchmove.flipster': function(e) {
                  
                          if ( _startDragX !== false ) {
                              e = e.originalEvent;

                              x = (e.touches ? e.touches[0].clientX : e.clientX);
                              y = (e.touches ? e.touches[0].clientY : e.clientY);
                              offsetY = y - _startDragY;
                              offsetX = x - _startDragX;

                              if(settings.style != 'flat'){

                                  if ( Math.abs(offsetY) < 100 && Math.abs(offsetX) >= 30 ) {
                                      _touchJump((offsetX < 0 ? 'next' : 'prev'));
                                      _startDragX = x;
                                      // e.preventDefault();
                                  }
                                  
                              }

                              //swipe settings
                              if(settings.style == 'flat'){

                                  if ( Math.abs(offsetY) < 100 && Math.abs(offsetX) >= 30) {
                                      
                                      // e.preventDefault();              
                                      
                                      _container.css('transform', 'translateX(' + (lastPosition + offsetX) + 'px)');    
                                      _container.css('transition', '0s');  
                                      _isItemClicked = false;
                                          
                                  }

                              }

                          }
                      }, 

                      'touchend.flipster touchcancel.flipster ': function() {
                    
                          //swipe settings
                          if(settings.style == 'flat'){

                              if(Math.abs(offsetX) >= 30){
                                  
                                  lastPosition += offsetX;
                                  
                                  var closestItemCoord = _itemOffsets.reduce((a, b) => {
                                      return Math.abs(b - lastPosition) < Math.abs(a - lastPosition) ? b : a;
                                  });           
                                                                                                           
                                  var selectedIndex = _itemOffsets.indexOf(closestItemCoord)
                                                                  
                                  if(_isItemClicked == false)
                                  jump(selectedIndex);         
                                  
                                  _container.css('transition', ''); 
                                  lastPosition = closestItemCoord;

                              }

                          }

                          _startDragX = false; 
                          
                      },

                    

                  });

                  elem.parents('.'+classes.main).on({

                      'mousedown.flipster': function(e) {

                          var objTarget = jQuery(e.target);
                          var isButtonClicked = objTarget.hasClass(classes.button) || objTarget.parents().hasClass(classes.button);

                          if(isButtonClicked == false){

                              _isItemClicked = true;

                              e = e.originalEvent;
                              _startDragX = e.clientX;
                              _startDragY = e.clientY;
                                
                              isMousePressed = true;
                              lastPosition = _itemOffsets[_currentIndex]; 
  
                              e.preventDefault();
                              
                              _items.css('cursor', 'grab');

                          }
                       
                      },

                      'mousemove.flipster': function(e) {

                          var objTarget = jQuery(e.target);
                          var isButtonClicked = objTarget.hasClass(classes.button) || objTarget.parents().hasClass(classes.button);
                          
                          if(isButtonClicked == false){

                              if ( _startDragX !== false ) {
                                  e = e.originalEvent;
  
                                  x = e.clientX;
                                  y = e.clientY;
                                  offsetY = y - _startDragY;
                                  offsetX = x - _startDragX;
  
                                  //drag settings
                                  if(settings.style == 'flat'){
  
                                      if ( Math.abs(offsetY) < 100 && Math.abs(offsetX) >= 30) {
                                          
                                          e.preventDefault();           
                                             
                                          _container.css('transform', 'translateX(' + (lastPosition + offsetX) + 'px)');   
                                          
                                          _container.css('transition', '0s');    
                                        
                                          _isItemClicked = false;
                                              
                                      }
  
                                  }
  
                              }

                          }
                      },

                      'mouseup.flipster': function(e) {

                          var objTarget = jQuery(e.target);
                          var isButtonClicked = objTarget.hasClass(classes.button) || objTarget.parents().hasClass(classes.button);

                          //swipe settings
                          if(settings.style == 'flat' && isButtonClicked == false){
                              
                              if(isMousePressed == true && Math.abs(offsetX) >= 30){
                               
                                  lastPosition += offsetX;
                                  
                                  var closestItemCoord = _itemOffsets.reduce((a, b) => {
                                      return Math.abs(b - lastPosition) < Math.abs(a - lastPosition) ? b : a;
                                  });
                                                                                                                      
                                  var selectedIndex = _itemOffsets.indexOf(closestItemCoord);

                                  _items.css('cursor', '');  
                                  _container.css('transition', '');  
                               
                                  if(_isItemClicked == false)
                                  jump(selectedIndex);
                                  
                                  lastPosition = closestItemCoord;
                                  
                                  isMousePressed = false;
                                  
                              }                                
                              
                              _startDragX = false; 
                          }

                          
                      },
                      
                      'mouseleave.flipster': function() {
  
                          //swipe settings
                          if(settings.style == 'flat'){
  
                              if(isMousePressed == true && Math.abs(offsetX) >= 30){
                                  
                                  _container.css('transform', 'translateX(' + lastPosition + 'px)');  

                                  _items.css('cursor', '');  
                                  _container.css('transition', '');  
                                  
                                  isMousePressed = false;      
  
                              }
                              
                              _startDragX = false; 
                          }
  
                          
                      },

                  });
              }
          }

          /*
          * potection from hidden parent
          */
          function hasHiddenParent(){

            var objParents = self.parents();
            var hasHiddenParent = false;
            
            objParents.each(function(){

              var objParent = jQuery(this);
              var isParentVisible = objParent.is(":visible");

              if(isParentVisible == false)
              hasHiddenParent = true;

            });

            return(hasHiddenParent);
            
          }

          function init() {

              var style;

              self.css('visibility', 'hidden');

              index();

              if ( _items.length <= 1 ) {
                  self.css('visibility', '');
                  return;
              }

              style = (settings.style ? 'flipster--' + settings.style.split(' ').join(' flipster--') : false);

              self.addClass([
                  classes.main,
                  (transformSupport ? 'flipster--transform' : ' flipster--no-transform'),
                  style, // 'flipster--'+settings.style : '' ),
                  (settings.click ? 'flipster--click' : '')
              ].join(' '));

              // Set the starting item
              if ( settings.start ) {
                  // Find the middle item if start = center
                  _currentIndex = ( settings.start === 'center' ? Math.floor(_items.length / 2) : settings.start );
              }

              jump(_currentIndex);

              var images = self.find('img');

              if ( images.length ) {
                  var imagesLoaded = 0;

                  // Resize after all images have loaded.
                  images.on('load', function() {
                      imagesLoaded++;
                      if ( imagesLoaded >= images.length ) { show(); }
                  });

                  // Fallback to show Flipster while images load in case it takes a while.
                  setTimeout(show, 750);
              } else {
                  show();
              }

              // Attach event bindings.
              $window.on('resize.flipster', throttle(resize, 400));

              if ( settings.autoplay ) { play(); }

              if ( settings.pauseOnHover ) {
                  _container
                      .on('mouseenter.flipster', pause)
                      .on('mouseleave.flipster', function() {
                          if ( _playing === -1 ) { play(); }
                      });
              }

              keyboardEvents(self);
              wheelEvents(_container);
              touchEvents(_container);
          }

          // public methods
          methods = {
              jump: jump,
              next: function() { return jump('next'); },
              prev: function() { return jump('prev'); },
              play: play,
              pause: pause,
              index: index
          };
          self.data('methods', methods);

          // Initialize if flipster is not already active.
          if ( !self.hasClass(classes.active) ) { init(); }

      });
  };
})(jQuery, window);

