/*!

* jQuery Smooth Scroll - v2.2.0 - 2017-05-05
* https://github.com/kswedberg/jquery-smooth-scroll
* Copyright (c) 2017 Karl Swedberg
* Licensed MIT
*/

(function(factory) {

if (typeof define === 'function' && define.amd) {
  // AMD. Register as an anonymous module.
  define(['jquery'], factory);
} else if (typeof module === 'object' && module.exports) {
  // CommonJS
  factory(require('jquery'));
} else {
  // Browser globals
  factory(jQuery);
}

}(function($) {

var version = '2.2.0';
var optionOverrides = {};
var defaults = {
  exclude: [],
  excludeWithin: [],
  offset: 0,

  // one of 'top' or 'left'
  direction: 'top',

  // if set, bind click events through delegation
  //  supported since jQuery 1.4.2
  delegateSelector: null,

  // jQuery set of elements you wish to scroll (for $.smoothScroll).
  //  if null (default), $('html, body').firstScrollable() is used.
  scrollElement: null,

  // only use if you want to override default behavior
  scrollTarget: null,

  // automatically focus the target element after scrolling to it
  autoFocus: false,

  // fn(opts) function to be called before scrolling occurs.
  // `this` is the element(s) being scrolled
  beforeScroll: function() {},

  // fn(opts) function to be called after scrolling occurs.
  // `this` is the triggering element
  afterScroll: function() {},

  // easing name. jQuery comes with "swing" and "linear." For others, you'll need an easing plugin
  // from jQuery UI or elsewhere
  easing: 'swing',

  // speed can be a number or 'auto'
  // if 'auto', the speed will be calculated based on the formula:
  // (current scroll position - target scroll position) / autoCoeffic
  speed: 400,

  // coefficient for "auto" speed
  autoCoefficient: 2,

  // $.fn.smoothScroll only: whether to prevent the default click action
  preventDefault: true
};

var getScrollable = function(opts) {
  var scrollable = [];
  var scrolled = false;
  var dir = opts.dir && opts.dir === 'left' ? 'scrollLeft' : 'scrollTop';

  this.each(function() {
    var el = $(this);

    if (this === document || this === window) {
      return;
    }

    if (document.scrollingElement && (this === document.documentElement || this === document.body)) {
      scrollable.push(document.scrollingElement);

      return false;
    }

    if (el[dir]() > 0) {
      scrollable.push(this);
    } else {
      // if scroll(Top|Left) === 0, nudge the element 1px and see if it moves
      el[dir](1);
      scrolled = el[dir]() > 0;

      if (scrolled) {
        scrollable.push(this);
      }
      // then put it back, of course
      el[dir](0);
    }
  });

  if (!scrollable.length) {
    this.each(function() {
      // If no scrollable elements and <html> has scroll-behavior:smooth because
      // "When this property is specified on the root element, it applies to the viewport instead."
      // and "The scroll-behavior property of the … body element is *not* propagated to the viewport."
      // → https://drafts.csswg.org/cssom-view/#propdef-scroll-behavior
      if (this === document.documentElement && $(this).css('scrollBehavior') === 'smooth') {
        scrollable = [this];
      }

      // If still no scrollable elements, fall back to <body>,
      // if it's in the jQuery collection
      // (doing this because Safari sets scrollTop async,
      // so can't set it to 1 and immediately get the value.)
      if (!scrollable.length && this.nodeName === 'BODY') {
        scrollable = [this];
      }
    });
  }

  // Use the first scrollable element if we're calling firstScrollable()
  if (opts.el === 'first' && scrollable.length > 1) {
    scrollable = [scrollable[0]];
  }

  return scrollable;
};

var rRelative = /^([\-\+]=)(\d+)/;

$.fn.extend({
  scrollable: function(dir) {
    var scrl = getScrollable.call(this, {dir: dir});

    return this.pushStack(scrl);
  },
  firstScrollable: function(dir) {
    var scrl = getScrollable.call(this, {el: 'first', dir: dir});

    return this.pushStack(scrl);
  },

  smoothScroll: function(options, extra) {
    options = options || {};

    if (options === 'options') {
      if (!extra) {
        return this.first().data('ssOpts');
      }

      return this.each(function() {
        var $this = $(this);
        var opts = $.extend($this.data('ssOpts') || {}, extra);

        $(this).data('ssOpts', opts);
      });
    }

    var opts = $.extend({}, $.fn.smoothScroll.defaults, options);

    var clickHandler = function(event) {
      var escapeSelector = function(str) {
        return str.replace(/(:|\.|\/)/g, '\\$1');
      };

      var link = this;
      var $link = $(this);
      var thisOpts = $.extend({}, opts, $link.data('ssOpts') || {});
      var exclude = opts.exclude;
      var excludeWithin = thisOpts.excludeWithin;
      var elCounter = 0;
      var ewlCounter = 0;
      var include = true;
      var clickOpts = {};
      var locationPath = $.smoothScroll.filterPath(location.pathname);
      var linkPath = $.smoothScroll.filterPath(link.pathname);
      var hostMatch = location.hostname === link.hostname || !link.hostname;
      var pathMatch = thisOpts.scrollTarget || (linkPath === locationPath);
      var thisHash = escapeSelector(link.hash);

      if (thisHash && !$(thisHash).length) {
        include = false;
      }

      if (!thisOpts.scrollTarget && (!hostMatch || !pathMatch || !thisHash)) {
        include = false;
      } else {
        while (include && elCounter < exclude.length) {
          if ($link.is(escapeSelector(exclude[elCounter++]))) {
            include = false;
          }
        }

        while (include && ewlCounter < excludeWithin.length) {
          if ($link.closest(excludeWithin[ewlCounter++]).length) {
            include = false;
          }
        }
      }

      if (include) {
        if (thisOpts.preventDefault) {
          event.preventDefault();
        }

        $.extend(clickOpts, thisOpts, {
          scrollTarget: thisOpts.scrollTarget || thisHash,
          link: link
        });

        $.smoothScroll(clickOpts);
      }
    };

    if (options.delegateSelector !== null) {
      this
      .off('click.smoothscroll', options.delegateSelector)
      .on('click.smoothscroll', options.delegateSelector, clickHandler);
    } else {
      this
      .off('click.smoothscroll')
      .on('click.smoothscroll', clickHandler);
    }

    return this;
  }
});

var getExplicitOffset = function(val) {
  var explicit = {relative: ''};
  var parts = typeof val === 'string' && rRelative.exec(val);

  if (typeof val === 'number') {
    explicit.px = val;
  } else if (parts) {
    explicit.relative = parts[1];
    explicit.px = parseFloat(parts[2]) || 0;
  }

  return explicit;
};

var onAfterScroll = function(opts) {
  var $tgt = $(opts.scrollTarget);

  if (opts.autoFocus && $tgt.length) {
    $tgt[0].focus();

    if (!$tgt.is(document.activeElement)) {
      $tgt.prop({tabIndex: -1});
      $tgt[0].focus();
    }
  }

  opts.afterScroll.call(opts.link, opts);
};

$.smoothScroll = function(options, px) {
  if (options === 'options' && typeof px === 'object') {
    return $.extend(optionOverrides, px);
  }
  var opts, $scroller, speed, delta;
  var explicitOffset = getExplicitOffset(options);
  var scrollTargetOffset = {};
  var scrollerOffset = 0;
  var offPos = 'offset';
  var scrollDir = 'scrollTop';
  var aniProps = {};
  var aniOpts = {};

  if (explicitOffset.px) {
    opts = $.extend({link: null}, $.fn.smoothScroll.defaults, optionOverrides);
  } else {
    opts = $.extend({link: null}, $.fn.smoothScroll.defaults, options || {}, optionOverrides);

    if (opts.scrollElement) {
      offPos = 'position';

      if (opts.scrollElement.css('position') === 'static') {
        opts.scrollElement.css('position', 'relative');
      }
    }

    if (px) {
      explicitOffset = getExplicitOffset(px);
    }
  }

  scrollDir = opts.direction === 'left' ? 'scrollLeft' : scrollDir;

  if (opts.scrollElement) {
    $scroller = opts.scrollElement;

    if (!explicitOffset.px && !(/^(?:HTML|BODY)$/).test($scroller[0].nodeName)) {
      scrollerOffset = $scroller[scrollDir]();
    }
  } else {
    $scroller = $('html, body').firstScrollable(opts.direction);
  }

  // beforeScroll callback function must fire before calculating offset
  opts.beforeScroll.call($scroller, opts);

  scrollTargetOffset = explicitOffset.px ? explicitOffset : {
    relative: '',
    px: ($(opts.scrollTarget)[offPos]() && $(opts.scrollTarget)[offPos]()[opts.direction]) || 0
  };

  aniProps[scrollDir] = scrollTargetOffset.relative + (scrollTargetOffset.px + scrollerOffset + opts.offset);

  speed = opts.speed;

  // automatically calculate the speed of the scroll based on distance / coefficient
  if (speed === 'auto') {

    // $scroller[scrollDir]() is position before scroll, aniProps[scrollDir] is position after
    // When delta is greater, speed will be greater.
    delta = Math.abs(aniProps[scrollDir] - $scroller[scrollDir]());

    // Divide the delta by the coefficient
    speed = delta / opts.autoCoefficient;
  }

  aniOpts = {
    duration: speed,
    easing: opts.easing,
    complete: function() {
      onAfterScroll(opts);
    }
  };

  if (opts.step) {
    aniOpts.step = opts.step;
  }

  if ($scroller.length) {
    $scroller.stop().animate(aniProps, aniOpts);
  } else {
    onAfterScroll(opts);
  }
};

$.smoothScroll.version = version;
$.smoothScroll.filterPath = function(string) {
  string = string || '';

  return string
    .replace(/^\//, '')
    .replace(/(?:index|default).[a-zA-Z]{3,4}$/, '')
    .replace(/\/$/, '');
};

// default options
$.fn.smoothScroll.defaults = defaults;

}));