(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
    typeof define === 'function' && define.amd ? define(factory) :
    (global.infiniteScroll = factory());
  }(this, function () { 'use strict';
  
    var ctx = '@@InfiniteScroll';
  
    var throttle = function throttle(fn, delay) {
      var now, lastExec, timer, context, args; //eslint-disable-line
  
      var execute = function execute() {
        fn.apply(context, args);
        lastExec = now;
      };
  
      return function () {
        context = this;
        args = arguments;
  
        now = Date.now();
  
        if (timer) {
          clearTimeout(timer);
          timer = null;
        }
  
        if (lastExec) {
          var diff = delay - (now - lastExec);
          if (diff < 0) {
            execute();
          } else {
            timer = setTimeout(function () {
              execute();
            }, diff);
          }
        } else {
          execute();
        }
      };
    };
  
    var getScrollTop = function getScrollTop(element) {
      if (element === window) {
        return Math.max(window.pageYOffset || 0, document.documentElement.scrollTop);
      }
  
      return element.scrollTop;
    };
  
    var getComputedStyle = document.defaultView.getComputedStyle;
  
    var getScrollEventTarget = function getScrollEventTarget(element) {
      var currentNode = element;
      // bugfix, see http://w3help.org/zh-cn/causes/SD9013 and http://stackoverflow.com/questions/17016740/onscroll-function-is-not-working-for-chrome
      while (currentNode && currentNode.tagName !== 'HTML' && currentNode.tagName !== 'BODY' && currentNode.nodeType === 1) {
        var overflowY = getComputedStyle(currentNode).overflowY;
        if (overflowY === 'scroll' || overflowY === 'auto') {
          return currentNode;
        }
        currentNode = currentNode.parentNode;
      }
      return window;
    };
  
    var getVisibleHeight = function getVisibleHeight(element) {
      if (element === window) {
        return document.documentElement.clientHeight;
      }
  
      return element.clientHeight;
    };
  
    var getElementTop = function getElementTop(element) {
      if (element === window) {
        return getScrollTop(window);
      }
      return element.getBoundingClientRect().top + getScrollTop(window);
    };
  
    var isAttached = function isAttached(element) {
      var currentNode = element.parentNode;
      while (currentNode) {
        if (currentNode.tagName === 'HTML') {
          return true;
        }
        if (currentNode.nodeType === 11) {
          return false;
        }
        currentNode = currentNode.parentNode;
      }
      return false;
    };
  
    var doBind = function doBind() {
      if (this.binded) return; // eslint-disable-line
      this.binded = true;
  
      var directive = this;
      var element = directive.el;
  
      var throttleDelayExpr = element.getAttribute('infinite-scroll-throttle-delay');
      var throttleDelay = 200;
      if (throttleDelayExpr) {
        throttleDelay = Number(directive.vm[throttleDelayExpr] || throttleDelayExpr);
        if (isNaN(throttleDelay) || throttleDelay < 0) {
          throttleDelay = 200;
        }
      }
      directive.throttleDelay = throttleDelay;
  
      directive.scrollEventTarget = getScrollEventTarget(element);
      directive.scrollListener = throttle(doCheck.bind(directive), directive.throttleDelay);
      directive.scrollEventTarget.addEventListener('scroll', directive.scrollListener);
  
      this.vm.$on('hook:beforeDestroy', function () {
        directive.scrollEventTarget.removeEventListener('scroll', directive.scrollListener);
      });
  
      var disabledExpr = element.getAttribute('infinite-scroll-disabled');
      var disabled = false;
  
      if (disabledExpr) {
        this.vm.$watch(disabledExpr, function (value) {
          directive.disabled = value;
          if (!value && directive.immediateCheck) {
            doCheck.call(directive);
          }
        });
        disabled = Boolean(directive.vm[disabledExpr]);
      }
      directive.disabled = disabled;
  
      var distanceExpr = element.getAttribute('infinite-scroll-distance');
      var distance = 0;
      if (distanceExpr) {
        distance = Number(directive.vm[distanceExpr] || distanceExpr);
        if (isNaN(distance)) {
          distance = 0;
        }
      }
      directive.distance = distance;
  
      var immediateCheckExpr = element.getAttribute('infinite-scroll-immediate-check');
      var immediateCheck = true;
      if (immediateCheckExpr) {
        immediateCheck = Boolean(directive.vm[immediateCheckExpr]);
      }
      directive.immediateCheck = immediateCheck;
  
      if (immediateCheck) {
        doCheck.call(directive);
      }
  
      var eventName = element.getAttribute('infinite-scroll-listen-for-event');
      if (eventName) {
        directive.vm.$on(eventName, function () {
          doCheck.call(directive);
        });
      }
    };
  
    var doCheck = function doCheck(force) {
      var scrollEventTarget = this.scrollEventTarget;
      var element = this.el;
      var distance = this.distance;
  
      if (force !== true && this.disabled) return; //eslint-disable-line
      var viewportScrollTop = getScrollTop(scrollEventTarget);
      var viewportBottom = viewportScrollTop + getVisibleHeight(scrollEventTarget);
  
      var shouldTrigger = false;
  
      if (scrollEventTarget === element) {
        shouldTrigger = scrollEventTarget.scrollHeight - viewportBottom <= distance;
      } else {
        var elementBottom = getElementTop(element) - getElementTop(scrollEventTarget) + element.offsetHeight + viewportScrollTop;
  
        shouldTrigger = viewportBottom + distance >= elementBottom;
      }
  
      if (shouldTrigger && this.expression) {
        this.expression();
      }
    };
  
    var InfiniteScroll = {
      bind: function bind(el, binding, vnode) {
        el[ctx] = {
          el: el,
          vm: vnode.context,
          expression: binding.value
        };
        var args = arguments;
        el[ctx].vm.$on('hook:mounted', function () {
          el[ctx].vm.$nextTick(function () {
            if (isAttached(el)) {
              doBind.call(el[ctx], args);
            }
  
            el[ctx].bindTryCount = 0;
  
            var tryBind = function tryBind() {
              if (el[ctx].bindTryCount > 10) return; //eslint-disable-line
              el[ctx].bindTryCount++;
              if (isAttached(el)) {
                doBind.call(el[ctx], args);
              } else {
                setTimeout(tryBind, 50);
              }
            };
  
            tryBind();
          });
        });
      },
      unbind: function unbind(el) {
        if (el && el[ctx] && el[ctx].scrollEventTarget) el[ctx].scrollEventTarget.removeEventListener('scroll', el[ctx].scrollListener);
      }
    };
  
    var install = function install(Vue) {
      Vue.directive('InfiniteScroll', InfiniteScroll);
    };
  
    if (window.Vue) {
      window.infiniteScroll = InfiniteScroll;
      Vue.use(install); // eslint-disable-line
    }
  
    InfiniteScroll.install = install;
  
    return InfiniteScroll;
  
  }));