/*!
 * pulltorefreshjs v0.1.22
 * (c) Rafael Soto
 * Released under the MIT License.
 */
(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  typeof define === 'function' && define.amd ? define(factory) :
  (global = global || self, global.PullToRefresh = factory());
}(this, function () { 'use strict';

  var _shared = {
    pullStartY: null,
    pullMoveY: null,
    handlers: [],
    styleEl: null,
    events: null,
    dist: 0,
    state: 'pending',
    timeout: null,
    distResisted: 0,
    supportsPassive: false,
    supportsPointerEvents: typeof window !== 'undefined' && !!window.PointerEvent
  };

  try {
    window.addEventListener('test', null, {
      get passive() {
        // eslint-disable-line getter-return
        _shared.supportsPassive = true;
      }

    });
  } catch (e) {// do nothing
  }

  function setupDOM(handler) {
    if (!handler.ptrElement) {
      var ptr = document.createElement('div');

      if (handler.mainElement !== document.body) {
        handler.mainElement.parentNode.insertBefore(ptr, handler.mainElement);
      } else {
        document.body.insertBefore(ptr, document.body.firstChild);
      }

      ptr.classList.add(((handler.classPrefix) + "ptr"));
      ptr.innerHTML = handler.getMarkup().replace(/__PREFIX__/g, handler.classPrefix);
      handler.ptrElement = ptr;

      if (typeof handler.onInit === 'function') {
        handler.onInit(handler);
      } // Add the css styles to the style node, and then
      // insert it into the dom


      if (!_shared.styleEl) {
        _shared.styleEl = document.createElement('style');

        _shared.styleEl.setAttribute('id', 'pull-to-refresh-js-style');

        document.head.appendChild(_shared.styleEl);
      }

      _shared.styleEl.textContent = handler.getStyles().replace(/__PREFIX__/g, handler.classPrefix).replace(/\s+/g, ' ');
    }

    return handler;
  }

  function onReset(handler) {
    if (!handler.ptrElement) { return; }
    handler.ptrElement.classList.remove(((handler.classPrefix) + "refresh"));
    handler.ptrElement.style[handler.cssProp] = '0px';
    setTimeout(function () {
      // remove previous ptr-element from DOM
      if (handler.ptrElement && handler.ptrElement.parentNode) {
        handler.ptrElement.parentNode.removeChild(handler.ptrElement);
        handler.ptrElement = null;
      } // reset state


      _shared.state = 'pending';
    }, handler.refreshTimeout);
  }

  function update(handler) {
    var iconEl = handler.ptrElement.querySelector(("." + (handler.classPrefix) + "icon"));
    var textEl = handler.ptrElement.querySelector(("." + (handler.classPrefix) + "text"));

    if (iconEl) {
      if (_shared.state === 'refreshing') {
        iconEl.innerHTML = handler.iconRefreshing;
      } else {
        iconEl.innerHTML = handler.iconArrow;
      }
    }

    if (textEl) {
      if (_shared.state === 'releasing') {
        textEl.innerHTML = handler.instructionsReleaseToRefresh;
      }

      if (_shared.state === 'pulling' || _shared.state === 'pending') {
        textEl.innerHTML = handler.instructionsPullToRefresh;
      }

      if (_shared.state === 'refreshing') {
        textEl.innerHTML = handler.instructionsRefreshing;
      }
    }
  }

  var _ptr = {
    setupDOM: setupDOM,
    onReset: onReset,
    update: update
  };

  var _timeout;

  var screenY = function screenY(event) {
    if (_shared.pointerEventsEnabled && _shared.supportsPointerEvents) {
      return event.screenY;
    }

    return event.touches[0].screenY;
  };

  var _setupEvents = (function () {
    var _el;

    function _onTouchStart(e) {
      // here, we must pick a handler first, and then append their html/css on the DOM
      var target = _shared.handlers.filter(function (h) { return h.contains(e.target); })[0];

      _shared.enable = !!target;

      if (target && _shared.state === 'pending') {
        _el = _ptr.setupDOM(target);

        if (target.shouldPullToRefresh()) {
          _shared.pullStartY = screenY(e);
        }

        clearTimeout(_shared.timeout);

        _ptr.update(target);
      }
    }

    function _onTouchMove(e) {
      if (!(_el && _el.ptrElement && _shared.enable)) {
        return;
      }

      if (!_shared.pullStartY) {
        if (_el.shouldPullToRefresh()) {
          _shared.pullStartY = screenY(e);
        }
      } else {
        _shared.pullMoveY = screenY(e);
      }

      if (_shared.state === 'refreshing') {
        if (e.cancelable && _el.shouldPullToRefresh() && _shared.pullStartY < _shared.pullMoveY) {
          e.preventDefault();
        }

        return;
      }

      if (_shared.state === 'pending') {
        _el.ptrElement.classList.add(((_el.classPrefix) + "pull"));

        _shared.state = 'pulling';

        _ptr.update(_el);
      }

      if (_shared.pullStartY && _shared.pullMoveY) {
        _shared.dist = _shared.pullMoveY - _shared.pullStartY;
      }

      _shared.distExtra = _shared.dist - _el.distIgnore;

      if (_shared.distExtra > 0) {
        if (e.cancelable) {
          e.preventDefault();
        }

        _el.ptrElement.style[_el.cssProp] = (_shared.distResisted) + "px";
        _shared.distResisted = _el.resistanceFunction(_shared.distExtra / _el.distThreshold) * Math.min(_el.distMax, _shared.distExtra);

        if (_shared.state === 'pulling' && _shared.distResisted > _el.distThreshold) {
          _el.ptrElement.classList.add(((_el.classPrefix) + "release"));

          _shared.state = 'releasing';

          _ptr.update(_el);
        }

        if (_shared.state === 'releasing' && _shared.distResisted < _el.distThreshold) {
          _el.ptrElement.classList.remove(((_el.classPrefix) + "release"));

          _shared.state = 'pulling';

          _ptr.update(_el);
        }
      }
    }

    function _onTouchEnd() {
      if (!(_el && _el.ptrElement && _shared.enable)) {
        return;
      } // wait 1/2 sec before unmounting...


      clearTimeout(_timeout);
      _timeout = setTimeout(function () {
        if (_el && _el.ptrElement && _shared.state === 'pending') {
          _ptr.onReset(_el);
        }
      }, 500);

      if (_shared.state === 'releasing' && _shared.distResisted > _el.distThreshold) {
        _shared.state = 'refreshing';
        _el.ptrElement.style[_el.cssProp] = (_el.distReload) + "px";

        _el.ptrElement.classList.add(((_el.classPrefix) + "refresh"));

        _shared.timeout = setTimeout(function () {
          var retval = _el.onRefresh(function () { return _ptr.onReset(_el); });

          if (retval && typeof retval.then === 'function') {
            retval.then(function () { return _ptr.onReset(_el); });
          }

          if (!retval && !_el.onRefresh.length) {
            _ptr.onReset(_el);
          }
        }, _el.refreshTimeout);
      } else {
        if (_shared.state === 'refreshing') {
          return;
        }

        _el.ptrElement.style[_el.cssProp] = '0px';
        _shared.state = 'pending';
      }

      _ptr.update(_el);

      _el.ptrElement.classList.remove(((_el.classPrefix) + "release"));

      _el.ptrElement.classList.remove(((_el.classPrefix) + "pull"));

      _shared.pullStartY = _shared.pullMoveY = null;
      _shared.dist = _shared.distResisted = 0;
    }

    function _onScroll() {
      if (_el) {
        _el.mainElement.classList.toggle(((_el.classPrefix) + "top"), _el.shouldPullToRefresh());
      }
    }

    var _passiveSettings = _shared.supportsPassive ? {
      passive: _shared.passive || false
    } : undefined;

    if (_shared.pointerEventsEnabled && _shared.supportsPointerEvents) {
      window.addEventListener('pointerup', _onTouchEnd);
      window.addEventListener('pointerdown', _onTouchStart);
      window.addEventListener('pointermove', _onTouchMove, _passiveSettings);
    } else {
      window.addEventListener('touchend', _onTouchEnd);
      window.addEventListener('touchstart', _onTouchStart);
      window.addEventListener('touchmove', _onTouchMove, _passiveSettings);
    }

    window.addEventListener('scroll', _onScroll);
    return {
      onTouchEnd: _onTouchEnd,
      onTouchStart: _onTouchStart,
      onTouchMove: _onTouchMove,
      onScroll: _onScroll,

      destroy: function destroy() {
        if (_shared.pointerEventsEnabled && _shared.supportsPointerEvents) {
          window.removeEventListener('pointerdown', _onTouchStart);
          window.removeEventListener('pointerup', _onTouchEnd);
          window.removeEventListener('pointermove', _onTouchMove, _passiveSettings);
        } else {
          window.removeEventListener('touchstart', _onTouchStart);
          window.removeEventListener('touchend', _onTouchEnd);
          window.removeEventListener('touchmove', _onTouchMove, _passiveSettings);
        }

        window.removeEventListener('scroll', _onScroll);
      }

    };
  });

  var _ptrMarkup = "\n<div class=\"__PREFIX__box\">\n  <div class=\"__PREFIX__content\">\n    <div class=\"__PREFIX__icon\"></div>\n    <div class=\"__PREFIX__text\"></div>\n  </div>\n</div>\n";

  var _ptrStyles = "\n.__PREFIX__ptr {\n  box-shadow: inset 0 -3px 5px rgba(0, 0, 0, 0.12);\n  pointer-events: none;\n  font-size: 0.85em;\n  font-weight: bold;\n  top: 0;\n  height: 0;\n  transition: height 0.3s, min-height 0.3s;\n  text-align: center;\n  width: 100%;\n  overflow: hidden;\n  display: flex;\n  align-items: flex-end;\n  align-content: stretch;\n}\n\n.__PREFIX__box {\n  padding: 10px;\n  flex-basis: 100%;\n}\n\n.__PREFIX__pull {\n  transition: none;\n}\n\n.__PREFIX__text {\n  margin-top: .33em;\n  color: rgba(0, 0, 0, 0.3);\n}\n\n.__PREFIX__icon {\n  color: rgba(0, 0, 0, 0.3);\n  transition: transform .3s;\n}\n\n/*\nWhen at the top of the page, disable vertical overscroll so passive touch\nlisteners can take over.\n*/\n.__PREFIX__top {\n  touch-action: pan-x pan-down pinch-zoom;\n}\n\n.__PREFIX__release .__PREFIX__icon {\n  transform: rotate(180deg);\n}\n";

  var _defaults = {
    distThreshold: 60,
    distMax: 80,
    distReload: 50,
    distIgnore: 0,
    mainElement: 'body',
    triggerElement: 'body',
    ptrElement: '.ptr',
    classPrefix: 'ptr--',
    cssProp: 'min-height',
    iconArrow: '&#8675;',
    iconRefreshing: '&hellip;',
    instructionsPullToRefresh: 'Pull down to refresh',
    instructionsReleaseToRefresh: 'Release to refresh',
    instructionsRefreshing: 'Refreshing',
    refreshTimeout: 500,
    getMarkup: function () { return _ptrMarkup; },
    getStyles: function () { return _ptrStyles; },
    onInit: function () {},
    onRefresh: function () { return location.reload(); },
    resistanceFunction: function (t) { return Math.min(1, t / 2.5); },
    shouldPullToRefresh: function () { return !window.scrollY; }
  };

  var _methods = ['mainElement', 'ptrElement', 'triggerElement'];
  var _setupHandler = (function (options) {
    var _handler = {}; // merge options with defaults

    Object.keys(_defaults).forEach(function (key) {
      _handler[key] = options[key] || _defaults[key];
    }); // normalize timeout value, even if it is zero

    _handler.refreshTimeout = typeof options.refreshTimeout === 'number' ? options.refreshTimeout : _defaults.refreshTimeout; // normalize elements

    _methods.forEach(function (method) {
      if (typeof _handler[method] === 'string') {
        _handler[method] = document.querySelector(_handler[method]);
      }
    }); // attach events lazily


    if (!_shared.events) {
      _shared.events = _setupEvents();
    }

    _handler.contains = function (target) {
      return _handler.triggerElement.contains(target);
    };

    _handler.destroy = function () {
      // stop pending any pending callbacks
      clearTimeout(_shared.timeout); // remove handler from shared state

      var offset = _shared.handlers.indexOf(_handler);

      _shared.handlers.splice(offset, 1);
    };

    return _handler;
  });

  var index = {
    setPassiveMode: function setPassiveMode(isPassive) {
      _shared.passive = isPassive;
    },

    setPointerEventsMode: function setPointerEventsMode(isEnabled) {
      _shared.pointerEventsEnabled = isEnabled;
    },

    destroyAll: function destroyAll() {
      if (_shared.events) {
        _shared.events.destroy();

        _shared.events = null;
      }

      _shared.handlers.forEach(function (h) {
        h.destroy();
      });
    },

    init: function init(options) {
      if ( options === void 0 ) options = {};

      var handler = _setupHandler(options);

      _shared.handlers.push(handler);

      return handler;
    },

    // export utils for testing
    _: {
      setupHandler: _setupHandler,
      setupEvents: _setupEvents,
      setupDOM: _ptr.setupDOM,
      onReset: _ptr.onReset,
      update: _ptr.update
    }
  };

  return index;

}));
