/*!
 * smooth-scroll v12.1.5: Animate scrolling to anchor links
 * (c) 2017 Chris Ferdinandi
 * MIT License
 * http://github.com/cferdinandi/smooth-scroll
 */

/**
 * closest() polyfill
 * @link https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill
 */
if (window.Element && !Element.prototype.closest) {
  Element.prototype.closest = function (s) {
    var matches = (this.document || this.ownerDocument).querySelectorAll(s),
      i,
      el = this;
    do {
      i = matches.length;
      while (--i >= 0 && matches.item(i) !== el) {}
    } while (i < 0 && (el = el.parentElement));
    return el;
  };
}

/**
 * requestAnimationFrame() polyfill
 * By Erik Möller. Fixes from Paul Irish and Tino Zijdel.
 * @link http://paulirish.com/2011/requestanimationframe-for-smart-animating/
 * @link http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
 * @license MIT
 */
(function () {
  var lastTime = 0;
  var vendors = ["ms", "moz", "webkit", "o"];
  for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
    window.requestAnimationFrame = window[vendors[x] + "RequestAnimationFrame"];
    window.cancelAnimationFrame =
      window[vendors[x] + "CancelAnimationFrame"] ||
      window[vendors[x] + "CancelRequestAnimationFrame"];
  }

  if (!window.requestAnimationFrame) {
    window.requestAnimationFrame = function (callback, element) {
      var currTime = new Date().getTime();
      var timeToCall = Math.max(0, 16 - (currTime - lastTime));
      var id = window.setTimeout(function () {
        callback(currTime + timeToCall);
      }, timeToCall);
      lastTime = currTime + timeToCall;
      return id;
    };
  }

  if (!window.cancelAnimationFrame) {
    window.cancelAnimationFrame = function (id) {
      clearTimeout(id);
    };
  }
})();

(function (root, factory) {
  if (typeof define === "function" && define.amd) {
    define([], function () {
      return factory(root);
    });
  } else if (typeof exports === "object") {
    module.exports = factory(root);
  } else {
    root.SmoothScroll = factory(root);
  }
})(
  typeof global !== "undefined"
    ? global
    : typeof window !== "undefined"
    ? window
    : this,
  function (window) {
    "use strict";

    //
    // Feature Test
    //

    var supports =
      "querySelector" in document &&
      "addEventListener" in window &&
      "requestAnimationFrame" in window &&
      "closest" in window.Element.prototype;

    //
    // Default settings
    //

    var defaults = {
      // Selectors
      ignore: "[data-scroll-ignore]",
      header: null,

      // Speed & Easing
      speed: 600,
      offset: 50,
      easing: "easeInOutQuart",
      customEasing: null,

      // Callback API
      before: function () {},
      after: function () {},
    };

    //
    // Utility Methods
    //

    /**
     * Merge two or more objects. Returns a new object.
     * @param {Object}   objects  The objects to merge together
     * @returns {Object}          Merged values of defaults and options
     */
    var extend = function () {
      // Variables
      var extended = {};
      var deep = false;
      var i = 0;
      var length = arguments.length;

      // Merge the object into the extended object
      var merge = function (obj) {
        for (var prop in obj) {
          if (obj.hasOwnProperty(prop)) {
            extended[prop] = obj[prop];
          }
        }
      };

      // Loop through each object and conduct a merge
      for (; i < length; i++) {
        var obj = arguments[i];
        merge(obj);
      }

      return extended;
    };

    /**
     * Get the height of an element.
     * @param  {Node} elem The element to get the height of
     * @return {Number}    The element's height in pixels
     */
    var getHeight = function (elem) {
      return parseInt(window.getComputedStyle(elem).height, 10);
    };

    /**
     * Escape special characters for use with querySelector
     * @param {String} id The anchor ID to escape
     * @author Mathias Bynens
     * @link https://github.com/mathiasbynens/CSS.escape
     */
    var escapeCharacters = function (id) {
      // Remove leading hash
      if (id.charAt(0) === "#") {
        id = id.substr(1);
      }

      var string = String(id);
      var length = string.length;
      var index = -1;
      var codeUnit;
      var result = "";
      var firstCodeUnit = string.charCodeAt(0);
      while (++index < length) {
        codeUnit = string.charCodeAt(index);
        // Note: there’s no need to special-case astral symbols, surrogate
        // pairs, or lone surrogates.

        // If the character is NULL (U+0000), then throw an
        // `InvalidCharacterError` exception and terminate these steps.
        if (codeUnit === 0x0000) {
          throw new InvalidCharacterError(
            "Invalid character: the input contains U+0000."
          );
        }

        if (
          // If the character is in the range [\1-\1F] (U+0001 to U+001F) or is
          // U+007F, […]
          (codeUnit >= 0x0001 && codeUnit <= 0x001f) ||
          codeUnit == 0x007f ||
          // If the character is the first character and is in the range [0-9]
          // (U+0030 to U+0039), […]
          (index === 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) ||
          // If the character is the second character and is in the range [0-9]
          // (U+0030 to U+0039) and the first character is a `-` (U+002D), […]
          (index === 1 &&
            codeUnit >= 0x0030 &&
            codeUnit <= 0x0039 &&
            firstCodeUnit === 0x002d)
        ) {
          // http://dev.w3.org/csswg/cssom/#escape-a-character-as-code-point
          result += "\\" + codeUnit.toString(16) + " ";
          continue;
        }

        // If the character is not handled by one of the above rules and is
        // greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or
        // is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to
        // U+005A), or [a-z] (U+0061 to U+007A), […]
        if (
          codeUnit >= 0x0080 ||
          codeUnit === 0x002d ||
          codeUnit === 0x005f ||
          (codeUnit >= 0x0030 && codeUnit <= 0x0039) ||
          (codeUnit >= 0x0041 && codeUnit <= 0x005a) ||
          (codeUnit >= 0x0061 && codeUnit <= 0x007a)
        ) {
          // the character itself
          result += string.charAt(index);
          continue;
        }

        // Otherwise, the escaped character.
        // http://dev.w3.org/csswg/cssom/#escape-a-character
        result += "\\" + string.charAt(index);
      }

      return "#" + result;
    };

    /**
     * Calculate the easing pattern
     * @link https://gist.github.com/gre/1650294
     * @param {String} type Easing pattern
     * @param {Number} time Time animation should take to complete
     * @returns {Number}
     */
    var easingPattern = function (settings, time) {
      var pattern;

      // Default Easing Patterns
      if (settings.easing === "easeInQuad") pattern = time * time; // accelerating from zero velocity
      if (settings.easing === "easeOutQuad") pattern = time * (2 - time); // decelerating to zero velocity
      if (settings.easing === "easeInOutQuad")
        pattern = time < 0.5 ? 2 * time * time : -1 + (4 - 2 * time) * time; // acceleration until halfway, then deceleration
      if (settings.easing === "easeInCubic") pattern = time * time * time; // accelerating from zero velocity
      if (settings.easing === "easeOutCubic")
        pattern = --time * time * time + 1; // decelerating to zero velocity
      if (settings.easing === "easeInOutCubic")
        pattern =
          time < 0.5
            ? 4 * time * time * time
            : (time - 1) * (2 * time - 2) * (2 * time - 2) + 1; // acceleration until halfway, then deceleration
      if (settings.easing === "easeInQuart")
        pattern = time * time * time * time; // accelerating from zero velocity
      if (settings.easing === "easeOutQuart")
        pattern = 1 - --time * time * time * time; // decelerating to zero velocity
      if (settings.easing === "easeInOutQuart")
        pattern =
          time < 0.5
            ? 8 * time * time * time * time
            : 1 - 8 * --time * time * time * time; // acceleration until halfway, then deceleration
      if (settings.easing === "easeInQuint")
        pattern = time * time * time * time * time; // accelerating from zero velocity
      if (settings.easing === "easeOutQuint")
        pattern = 1 + --time * time * time * time * time; // decelerating to zero velocity
      if (settings.easing === "easeInOutQuint")
        pattern =
          time < 0.5
            ? 16 * time * time * time * time * time
            : 1 + 16 * --time * time * time * time * time; // acceleration until halfway, then deceleration

      // Custom Easing Patterns
      if (!!settings.customEasing) pattern = settings.customEasing(time);

      return pattern || time; // no easing, no acceleration
    };

    /**
     * Determine the document's height
     * @returns {Number}
     */
    var getDocumentHeight = function () {
      return Math.max(
        document.body.scrollHeight,
        document.documentElement.scrollHeight,
        document.body.offsetHeight,
        document.documentElement.offsetHeight,
        document.body.clientHeight,
        document.documentElement.clientHeight
      );
    };

    /**
     * Calculate how far to scroll
     * @param {Element} anchor The anchor element to scroll to
     * @param {Number} headerHeight Height of a fixed header, if any
     * @param {Number} offset Number of pixels by which to offset scroll
     * @returns {Number}
     */
    var getEndLocation = function (anchor, headerHeight, offset) {
      var location = 0;
      if (anchor.offsetParent) {
        do {
          location += anchor.offsetTop;
          anchor = anchor.offsetParent;
        } while (anchor);
      }
      location = Math.max(location - headerHeight - offset, 0);
      return location;
    };

    /**
     * Get the height of the fixed header
     * @param  {Node}   header The header
     * @return {Number}        The height of the header
     */
    var getHeaderHeight = function (header) {
      return !header ? 0 : getHeight(header) + header.offsetTop;
    };

    /**
     * Bring the anchored element into focus
     * @param {Node}     anchor      The anchor element
     * @param {Number}   endLocation The end location to scroll to
     * @param {Boolean}  isNum       If true, scroll is to a position rather than an element
     */
    var adjustFocus = function (anchor, endLocation, isNum) {
      // Don't run if scrolling to a number on the page
      if (isNum) return;

      // Otherwise, bring anchor element into focus
      anchor.focus();
      if (document.activeElement.id !== anchor.id) {
        anchor.setAttribute("tabindex", "-1");
        anchor.focus();
        anchor.style.outline = "none";
      }
      window.scrollTo(0, endLocation);
    };

    /**
     * Check to see if user prefers reduced motion
     * @param  {Object} settings Script settings
     */
    var reduceMotion = function (settings) {
      if (
        "matchMedia" in window &&
        window.matchMedia("(prefers-reduced-motion)").matches
      ) {
        return true;
      }
      return false;
    };

    //
    // SmoothScroll Constructor
    //

    var SmoothScroll = function (selector, options) {
      //
      // Variables
      //

      var smoothScroll = {}; // Object for public APIs
      var settings,
        anchor,
        toggle,
        fixedHeader,
        headerHeight,
        eventTimeout,
        animationInterval;

      //
      // Methods
      //

      /**
       * Cancel a scroll-in-progress
       */
      smoothScroll.cancelScroll = function () {
        // clearInterval(animationInterval);
        cancelAnimationFrame(animationInterval);
      };

      /**
       * Start/stop the scrolling animation
       * @param {Node|Number} anchor  The element or position to scroll to
       * @param {Element}     toggle  The element that toggled the scroll event
       * @param {Object}      options
       */
      smoothScroll.animateScroll = function (anchor, toggle, options) {
        // Local settings
        var animateSettings = extend(settings || defaults, options || {}); // Merge user options with defaults

        // Selectors and variables
        var isNum =
          Object.prototype.toString.call(anchor) === "[object Number]"
            ? true
            : false;
        var anchorElem = isNum || !anchor.tagName ? null : anchor;
        if (!isNum && !anchorElem) return;
        var startLocation = window.pageYOffset; // Current location on the page
        if (animateSettings.header && !fixedHeader) {
          // Get the fixed header if not already set
          fixedHeader = document.querySelector(animateSettings.header);
        }
        if (!headerHeight) {
          // Get the height of a fixed header if one exists and not already set
          headerHeight = getHeaderHeight(fixedHeader);
        }
        var endLocation = isNum
          ? anchor
          : getEndLocation(
              anchorElem,
              headerHeight,
              parseInt(
                typeof animateSettings.offset === "function"
                  ? animateSettings.offset()
                  : animateSettings.offset,
                10
              )
            ); // Location to scroll to
        var distance = endLocation - startLocation; // distance to travel
        var documentHeight = getDocumentHeight();
        var timeLapsed = 0;
        var start, percentage, position;

        /**
         * Stop the scroll animation when it reaches its target (or the bottom/top of page)
         * @param {Number} position Current position on the page
         * @param {Number} endLocation Scroll to location
         * @param {Number} animationInterval How much to scroll on this loop
         */
        var stopAnimateScroll = function (position, endLocation) {
          // Get the current location
          var currentLocation = window.pageYOffset;

          // Check if the end location has been reached yet (or we've hit the end of the document)
          if (
            position == endLocation ||
            currentLocation == endLocation ||
            (startLocation < endLocation &&
              window.innerHeight + currentLocation) >= documentHeight
          ) {
            // Clear the animation timer
            smoothScroll.cancelScroll();

            // Bring the anchored element into focus
            adjustFocus(anchor, endLocation, isNum);

            // Run callback after animation complete
            animateSettings.after(anchor, toggle);

            // Reset start
            start = null;

            return true;
          }
        };

        /**
         * Loop scrolling animation
         */
        var loopAnimateScroll = function (timestamp) {
          if (!start) {
            start = timestamp;
          }
          timeLapsed += timestamp - start;
          percentage = timeLapsed / parseInt(animateSettings.speed, 10);
          percentage = percentage > 1 ? 1 : percentage;
          position =
            startLocation +
            distance * easingPattern(animateSettings, percentage);
          window.scrollTo(0, Math.floor(position));
          if (!stopAnimateScroll(position, endLocation)) {
            window.requestAnimationFrame(loopAnimateScroll);
            start = timestamp;
          }
        };

        /**
         * Reset position to fix weird iOS bug
         * @link https://github.com/cferdinandi/smooth-scroll/issues/45
         */
        if (window.pageYOffset === 0) {
          window.scrollTo(0, 0);
        }

        // Run callback before animation starts
        animateSettings.before(anchor, toggle);

        // Start scrolling animation
        smoothScroll.cancelScroll();
        window.requestAnimationFrame(loopAnimateScroll);
      };

      /**
       * Handle has change event
       */
      var hashChangeHandler = function (event) {
        // Only run if there's an anchor element to scroll to
        if (!anchor) return;

        // Reset the anchor element's ID
        anchor.id = anchor.getAttribute("data-scroll-id");

        // Scroll to the anchored content
        smoothScroll.animateScroll(anchor, toggle);

        // Reset anchor and toggle
        anchor = null;
        toggle = null;
      };

      /**
       * If smooth scroll element clicked, animate scroll
       */
      var clickHandler = function (event) {
        // Don't run if the user prefers reduced motion
        if (reduceMotion(settings)) return;

        // Don't run if right-click or command/control + click
        if (event.button !== 0 || event.metaKey || event.ctrlKey) return;

        // Check if a smooth scroll link was clicked
        toggle = event.target.closest(selector);
        if (
          !toggle ||
          toggle.tagName.toLowerCase() !== "a" ||
          event.target.closest(settings.ignore)
        )
          return;

        // Only run if link is an anchor and points to the current page
        if (
          toggle.hostname !== window.location.hostname ||
          toggle.pathname !== window.location.pathname ||
          !/#/.test(toggle.href)
        )
          return;

        // Get the sanitized hash
        var hash;
        try {
          hash = escapeCharacters(decodeURIComponent(toggle.hash));
        } catch (e) {
          hash = escapeCharacters(toggle.hash);
        }

        // If the hash is empty, scroll to the top of the page
        if (hash === "#") {
          // Prevent default link behavior
          event.preventDefault();

          // Set the anchored element
          anchor = document.body;

          // Save or create the ID as a data attribute and remove it (prevents scroll jump)
          var id = anchor.id ? anchor.id : "smooth-scroll-top";
          anchor.setAttribute("data-scroll-id", id);
          anchor.id = "";

          // If no hash change event will happen, fire manually
          // Otherwise, update the hash
          if (window.location.hash.substring(1) === id) {
            hashChangeHandler();
          } else {
            window.location.hash = id;
          }

          return;
        }

        // Get the anchored element
        anchor = document.querySelector(hash);

        // If anchored element exists, save the ID as a data attribute and remove it (prevents scroll jump)
        if (!anchor) return;
        anchor.setAttribute("data-scroll-id", anchor.id);
        anchor.id = "";

        // If no hash change event will happen, fire manually
        if (toggle.hash === window.location.hash) {
          event.preventDefault();
          hashChangeHandler();
        }
      };

      /**
       * On window scroll and resize, only run events at a rate of 15fps for better performance
       */
      var resizeThrottler = function (event) {
        if (!eventTimeout) {
          eventTimeout = setTimeout(function () {
            eventTimeout = null; // Reset timeout
            headerHeight = getHeaderHeight(fixedHeader); // Get the height of a fixed header if one exists
          }, 66);
        }
      };

      /**
       * Destroy the current initialization.
       */
      smoothScroll.destroy = function () {
        // If plugin isn't already initialized, stop
        if (!settings) return;

        // Remove event listeners
        document.removeEventListener("click", clickHandler, false);
        window.removeEventListener("resize", resizeThrottler, false);

        // Cancel any scrolls-in-progress
        smoothScroll.cancelScroll();

        // Reset variables
        settings = null;
        anchor = null;
        toggle = null;
        fixedHeader = null;
        headerHeight = null;
        eventTimeout = null;
        animationInterval = null;
      };

      /**
       * Initialize Smooth Scroll
       * @param {Object} options User settings
       */
      smoothScroll.init = function (options) {
        // feature test
        if (!supports) return;

        // Destroy any existing initializations
        smoothScroll.destroy();

        // Selectors and variables
        settings = extend(defaults, options || {}); // Merge user options with defaults
        fixedHeader = settings.header
          ? document.querySelector(settings.header)
          : null; // Get the fixed header
        headerHeight = getHeaderHeight(fixedHeader);

        // When a toggle is clicked, run the click handler
        document.addEventListener("click", clickHandler, false);

        // Listen for hash changes
        window.addEventListener("hashchange", hashChangeHandler, false);

        // If window is resized and there's a fixed header, recalculate its size
        if (fixedHeader) {
          window.addEventListener("resize", resizeThrottler, false);
        }
      };
      //
      // Initialize plugin
      //
      smoothScroll.init(options);
      //
      // Public APIs
      //

      return smoothScroll;
    };

    return SmoothScroll;
  }
);
var scroll = new SmoothScroll('a[href*="#"]');
