var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

(function (global, factory) {
  (typeof exports === 'undefined' ? 'undefined' : _typeof(exports)) === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : global.AsyncComputed = factory();
})(this, function () {
  'use strict';

  function isComputedLazy(item) {
    return item.hasOwnProperty('lazy') && item.lazy;
  }

  function isLazyActive(vm, key) {
    return vm[lazyActivePrefix + key];
  }

  var lazyActivePrefix = 'async_computed$lazy_active$';
  var lazyDataPrefix = 'async_computed$lazy_data$';

  function initLazy(data, key) {
    data[lazyActivePrefix + key] = false;
    data[lazyDataPrefix + key] = null;
  }

  function makeLazyComputed(key) {
    return {
      get: function get() {
        this[lazyActivePrefix + key] = true;
        return this[lazyDataPrefix + key];
      },
      set: function set(value) {
        this[lazyDataPrefix + key] = value;
      }
    };
  }

  function silentSetLazy(vm, key, value) {
    vm[lazyDataPrefix + key] = value;
  }
  function silentGetLazy(vm, key) {
    return vm[lazyDataPrefix + key];
  }

  var prefix = '_async_computed$';

  var AsyncComputed = {
    install: function install(Vue, pluginOptions) {
      pluginOptions = pluginOptions || {};

      Vue.config.optionMergeStrategies.asyncComputed = Vue.config.optionMergeStrategies.computed;

      Vue.mixin({
        beforeCreate: function beforeCreate() {
          var optionData = this.$options.data;

          if (!this.$options.computed) this.$options.computed = {};

          for (var key in this.$options.asyncComputed || {}) {
            this.$options.computed[prefix + key] = getterFn(key, this.$options.asyncComputed[key]);
          }

          this.$options.data = function vueAsyncComputedInjectedDataFn() {
            var data = (typeof optionData === 'function' ? optionData.call(this) : optionData) || {};
            for (var _key in this.$options.asyncComputed || {}) {
              var item = this.$options.asyncComputed[_key];
              if (isComputedLazy(item)) {
                initLazy(data, _key);
                this.$options.computed[_key] = makeLazyComputed(_key);
              } else {
                data[_key] = null;
              }
            }
            return data;
          };
        },
        created: function created() {
          var _this = this;

          for (var key in this.$options.asyncComputed || {}) {
            var item = this.$options.asyncComputed[key],
                value = generateDefault.call(this, item, pluginOptions);
            if (isComputedLazy(item)) {
              silentSetLazy(this, key, value);
            } else {
              this[key] = value;
            }
          }

          var _loop = function _loop(_key2) {
            var promiseId = 0;
            _this.$watch(prefix + _key2, function (newPromise) {
              var thisPromise = ++promiseId;

              if (!newPromise || !newPromise.then) {
                newPromise = Promise.resolve(newPromise);
              }

              newPromise.then(function (value) {
                if (thisPromise !== promiseId) return;
                _this[_key2] = value;
              }).catch(function (err) {
                if (thisPromise !== promiseId) return;

                if (pluginOptions.errorHandler === false) return;

                var handler = pluginOptions.errorHandler === undefined ? console.error.bind(console, 'Error evaluating async computed property:') : pluginOptions.errorHandler;

                if (pluginOptions.useRawError) {
                  handler(err);
                } else {
                  handler(err.stack);
                }
              });
            }, { immediate: true });
          };

          for (var _key2 in this.$options.asyncComputed || {}) {
            _loop(_key2);
          }
        }
      });
    }
  };

  function getterFn(key, fn) {
    if (typeof fn === 'function') return fn;

    var getter = fn.get;

    if (fn.hasOwnProperty('watch')) {
      getter = function getter() {
        fn.watch.call(this);
        return fn.get.call(this);
      };
    }
    if (isComputedLazy(fn)) {
      var nonLazy = getter;
      getter = function lazyGetter() {
        if (isLazyActive(this, key)) {
          return nonLazy.call(this);
        } else {
          return silentGetLazy(this, key);
        }
      };
    }
    return getter;
  }

  function generateDefault(fn, pluginOptions) {
    var defaultValue = null;

    if ('default' in fn) {
      defaultValue = fn.default;
    } else if ('default' in pluginOptions) {
      defaultValue = pluginOptions.default;
    }

    if (typeof defaultValue === 'function') {
      return defaultValue.call(this);
    } else {
      return defaultValue;
    }
  }

  /* istanbul ignore if */
  if (typeof window !== 'undefined' && window.Vue) {
    // Auto install in dist mode
    window.Vue.use(AsyncComputed);
  }

  return AsyncComputed;
});
