HEX
Server: Apache/2.4.52 (Ubuntu)
System: Linux spn-python 5.15.0-89-generic #99-Ubuntu SMP Mon Oct 30 20:42:41 UTC 2023 x86_64
User: arjun (1000)
PHP: 8.1.2-1ubuntu2.20
Disabled: NONE
Upload Files
File: //home/arjun/projects/propbase/propbase_website/node_modules/final-form/dist/final-form.es.js
import _extends from '@babel/runtime/helpers/esm/extends';
import _objectWithoutPropertiesLoose from '@babel/runtime/helpers/esm/objectWithoutPropertiesLoose';

//      

var charCodeOfDot = ".".charCodeAt(0);
var reEscapeChar = /\\(\\)?/g;
var rePropName = RegExp(
// Match anything that isn't a dot or bracket.
"[^.[\\]]+" + "|" +
// Or match property names within brackets.
"\\[(?:" +
// Match a non-string expression.
"([^\"'][^[]*)" + "|" +
// Or match strings (supports escaping characters).
"([\"'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2" + ")\\]" + "|" +
// Or match "" as the space between consecutive dots or empty brackets.
"(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))", "g");

/**
 * Converts `string` to a property path array.
 *
 * @private
 * @param {string} string The string to convert.
 * @returns {Array} Returns the property path array.
 */
var stringToPath = function stringToPath(string) {
  var result = [];
  if (string.charCodeAt(0) === charCodeOfDot) {
    result.push("");
  }
  string.replace(rePropName, function (match, expression, quote, subString) {
    var key = match;
    if (quote) {
      key = subString.replace(reEscapeChar, "$1");
    } else if (expression) {
      key = expression.trim();
    }
    result.push(key);
  });
  return result;
};
var keysCache = {};
var keysRegex = /[.[\]]+/;
var toPath = function toPath(key) {
  if (key === null || key === undefined || !key.length) {
    return [];
  }
  if (typeof key !== "string") {
    throw new Error("toPath() expects a string");
  }
  if (keysCache[key] == null) {
    /**
     * The following patch fixes issue 456, introduced since v4.20.3:
     *
     * Before v4.20.3, i.e. in v4.20.2, a `key` like 'choices[]' would map to ['choices']
     * (e.g. an array of choices used where 'choices[]' is name attribute of an input of type checkbox).
     *
     * Since v4.20.3, a `key` like 'choices[]' would map to ['choices', ''] which is wrong and breaks
     * this kind of inputs e.g. in React.
     *
     * v4.20.3 introduced an unwanted breaking change, this patch fixes it, see the issue at the link below.
     *
     * @see https://github.com/final-form/final-form/issues/456
     */
    if (key.endsWith("[]")) {
      // v4.20.2 (a `key` like 'choices[]' should map to ['choices'], which is fine).
      keysCache[key] = key.split(keysRegex).filter(Boolean);
    } else {
      // v4.20.3 (a `key` like 'choices[]' maps to ['choices', ''], which breaks applications relying on inputs like `<input type="checkbox" name="choices[]" />`).
      keysCache[key] = stringToPath(key);
    }
  }
  return keysCache[key];
};

//      
var getIn = function getIn(state, complexKey) {
  // Intentionally using iteration rather than recursion
  var path = toPath(complexKey);
  var current = state;
  for (var i = 0; i < path.length; i++) {
    var key = path[i];
    if (current === undefined || current === null || typeof current !== "object" || Array.isArray(current) && isNaN(key)) {
      return undefined;
    }
    current = current[key];
  }
  return current;
};

function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
var setInRecursor = function setInRecursor(current, index, path, value, destroyArrays) {
  if (index >= path.length) {
    // end of recursion
    return value;
  }
  var key = path[index];

  // determine type of key
  if (isNaN(key)) {
    var _extends2;
    // object set
    if (current === undefined || current === null) {
      var _ref;
      // recurse
      var _result = setInRecursor(undefined, index + 1, path, value, destroyArrays);

      // delete or create an object
      return _result === undefined ? undefined : (_ref = {}, _ref[key] = _result, _ref);
    }
    if (Array.isArray(current)) {
      throw new Error("Cannot set a non-numeric property on an array");
    }
    // current exists, so make a copy of all its values, and add/update the new one
    var _result2 = setInRecursor(current[key], index + 1, path, value, destroyArrays);
    if (_result2 === undefined) {
      var numKeys = Object.keys(current).length;
      if (current[key] === undefined && numKeys === 0) {
        // object was already empty
        return undefined;
      }
      if (current[key] !== undefined && numKeys <= 1) {
        // only key we had was the one we are deleting
        if (!isNaN(path[index - 1]) && !destroyArrays) {
          // we are in an array, so return an empty object
          return {};
        } else {
          return undefined;
        }
      }
      current[key];
        var _final = _objectWithoutPropertiesLoose(current, [key].map(_toPropertyKey));
      return _final;
    }
    // set result in key
    return _extends({}, current, (_extends2 = {}, _extends2[key] = _result2, _extends2));
  }
  // array set
  var numericKey = Number(key);
  if (current === undefined || current === null) {
    // recurse
    var _result3 = setInRecursor(undefined, index + 1, path, value, destroyArrays);

    // if nothing returned, delete it
    if (_result3 === undefined) {
      return undefined;
    }

    // create an array
    var _array = [];
    _array[numericKey] = _result3;
    return _array;
  }
  if (!Array.isArray(current)) {
    throw new Error("Cannot set a numeric property on an object");
  }
  // recurse
  var existingValue = current[numericKey];
  var result = setInRecursor(existingValue, index + 1, path, value, destroyArrays);

  // current exists, so make a copy of all its values, and add/update the new one
  var array = [].concat(current);
  if (destroyArrays && result === undefined) {
    array.splice(numericKey, 1);
    if (array.length === 0) {
      return undefined;
    }
  } else {
    array[numericKey] = result;
  }
  return array;
};
var setIn = function setIn(state, key, value, destroyArrays) {
  if (destroyArrays === void 0) {
    destroyArrays = false;
  }
  if (state === undefined || state === null) {
    throw new Error("Cannot call setIn() with " + String(state) + " state");
  }
  if (key === undefined || key === null) {
    throw new Error("Cannot call setIn() with " + String(key) + " key");
  }
  // Recursive function needs to accept and return State, but public API should
  // only deal with Objects
  return setInRecursor(state, 0, toPath(key), value, destroyArrays);
};

var FORM_ERROR = "FINAL_FORM/form-error";
var ARRAY_ERROR = "FINAL_FORM/array-error";

//      


/**
 * Converts internal field state to published field state
 */
function publishFieldState(formState, field) {
  var errors = formState.errors,
    initialValues = formState.initialValues,
    lastSubmittedValues = formState.lastSubmittedValues,
    submitErrors = formState.submitErrors,
    submitFailed = formState.submitFailed,
    submitSucceeded = formState.submitSucceeded,
    submitting = formState.submitting,
    values = formState.values;
  var active = field.active,
    blur = field.blur,
    change = field.change,
    data = field.data,
    focus = field.focus,
    modified = field.modified,
    modifiedSinceLastSubmit = field.modifiedSinceLastSubmit,
    name = field.name,
    touched = field.touched,
    validating = field.validating,
    visited = field.visited;
  var value = getIn(values, name);
  var error = getIn(errors, name);
  if (error && error[ARRAY_ERROR]) {
    error = error[ARRAY_ERROR];
  }
  var submitError = submitErrors && getIn(submitErrors, name);
  var initial = initialValues && getIn(initialValues, name);
  var pristine = field.isEqual(initial, value);
  var dirtySinceLastSubmit = !!(lastSubmittedValues && !field.isEqual(getIn(lastSubmittedValues, name), value));
  var valid = !error && !submitError;
  return {
    active: active,
    blur: blur,
    change: change,
    data: data,
    dirty: !pristine,
    dirtySinceLastSubmit: dirtySinceLastSubmit,
    error: error,
    focus: focus,
    initial: initial,
    invalid: !valid,
    length: Array.isArray(value) ? value.length : undefined,
    modified: modified,
    modifiedSinceLastSubmit: modifiedSinceLastSubmit,
    name: name,
    pristine: pristine,
    submitError: submitError,
    submitFailed: submitFailed,
    submitSucceeded: submitSucceeded,
    submitting: submitting,
    touched: touched,
    valid: valid,
    value: value,
    visited: visited,
    validating: validating
  };
}

//      
var fieldSubscriptionItems = ["active", "data", "dirty", "dirtySinceLastSubmit", "error", "initial", "invalid", "length", "modified", "modifiedSinceLastSubmit", "pristine", "submitError", "submitFailed", "submitSucceeded", "submitting", "touched", "valid", "value", "visited", "validating"];

//      

var shallowEqual = function shallowEqual(a, b) {
  if (a === b) {
    return true;
  }
  if (typeof a !== "object" || !a || typeof b !== "object" || !b) {
    return false;
  }
  var keysA = Object.keys(a);
  var keysB = Object.keys(b);
  if (keysA.length !== keysB.length) {
    return false;
  }
  var bHasOwnProperty = Object.prototype.hasOwnProperty.bind(b);
  for (var idx = 0; idx < keysA.length; idx++) {
    var key = keysA[idx];
    if (!bHasOwnProperty(key) || a[key] !== b[key]) {
      return false;
    }
  }
  return true;
};

//      
function subscriptionFilter (dest, src, previous, subscription, keys, shallowEqualKeys) {
  var different = false;
  keys.forEach(function (key) {
    if (subscription[key]) {
      dest[key] = src[key];
      if (!previous || (~shallowEqualKeys.indexOf(key) ? !shallowEqual(src[key], previous[key]) : src[key] !== previous[key])) {
        different = true;
      }
    }
  });
  return different;
}

//      
var shallowEqualKeys$1 = ["data"];

/**
 * Filters items in a FieldState based on a FieldSubscription
 */
var filterFieldState = function filterFieldState(state, previousState, subscription, force) {
  var result = {
    blur: state.blur,
    change: state.change,
    focus: state.focus,
    name: state.name
  };
  var different = subscriptionFilter(result, state, previousState, subscription, fieldSubscriptionItems, shallowEqualKeys$1) || !previousState;
  return different || force ? result : undefined;
};

//      
var formSubscriptionItems = ["active", "dirty", "dirtyFields", "dirtyFieldsSinceLastSubmit", "dirtySinceLastSubmit", "error", "errors", "hasSubmitErrors", "hasValidationErrors", "initialValues", "invalid", "modified", "modifiedSinceLastSubmit", "pristine", "submitting", "submitError", "submitErrors", "submitFailed", "submitSucceeded", "touched", "valid", "validating", "values", "visited"];

//      
var shallowEqualKeys = ["touched", "visited"];

/**
 * Filters items in a FormState based on a FormSubscription
 */
function filterFormState(state, previousState, subscription, force) {
  var result = {};
  var different = subscriptionFilter(result, state, previousState, subscription, formSubscriptionItems, shallowEqualKeys) || !previousState;
  return different || force ? result : undefined;
}

//      
var memoize = function memoize(fn) {
  var lastArgs;
  var lastResult;
  return function () {
    for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
    }
    if (!lastArgs || args.length !== lastArgs.length || args.some(function (arg, index) {
      return !shallowEqual(lastArgs[index], arg);
    })) {
      lastArgs = args;
      lastResult = fn.apply(void 0, args);
    }
    return lastResult;
  };
};

var isPromise = (function (obj) {
  return !!obj && (typeof obj === "object" || typeof obj === "function") && typeof obj.then === "function";
});

var version = "4.20.10";

var configOptions = ["debug", "initialValues", "keepDirtyOnReinitialize", "mutators", "onSubmit", "validate", "validateOnBlur"];
var tripleEquals = function tripleEquals(a, b) {
  return a === b;
};
var hasAnyError = function hasAnyError(errors) {
  return Object.keys(errors).some(function (key) {
    var value = errors[key];
    if (value && typeof value === "object" && !(value instanceof Error)) {
      return hasAnyError(value);
    }
    return typeof value !== "undefined";
  });
};
function convertToExternalFormState(_ref) {
  var active = _ref.active,
    dirtySinceLastSubmit = _ref.dirtySinceLastSubmit,
    modifiedSinceLastSubmit = _ref.modifiedSinceLastSubmit,
    error = _ref.error,
    errors = _ref.errors,
    initialValues = _ref.initialValues,
    pristine = _ref.pristine,
    submitting = _ref.submitting,
    submitFailed = _ref.submitFailed,
    submitSucceeded = _ref.submitSucceeded,
    submitError = _ref.submitError,
    submitErrors = _ref.submitErrors,
    valid = _ref.valid,
    validating = _ref.validating,
    values = _ref.values;
  return {
    active: active,
    dirty: !pristine,
    dirtySinceLastSubmit: dirtySinceLastSubmit,
    modifiedSinceLastSubmit: modifiedSinceLastSubmit,
    error: error,
    errors: errors,
    hasSubmitErrors: !!(submitError || submitErrors && hasAnyError(submitErrors)),
    hasValidationErrors: !!(error || hasAnyError(errors)),
    invalid: !valid,
    initialValues: initialValues,
    pristine: pristine,
    submitting: submitting,
    submitFailed: submitFailed,
    submitSucceeded: submitSucceeded,
    submitError: submitError,
    submitErrors: submitErrors,
    valid: valid,
    validating: validating > 0,
    values: values
  };
}
function notifySubscriber(subscriber, subscription, state, lastState, filter, force) {
  var notification = filter(state, lastState, subscription, force);
  if (notification) {
    subscriber(notification);
    return true;
  }
  return false;
}
function notify(_ref2, state, lastState, filter, force) {
  var entries = _ref2.entries;
  Object.keys(entries).forEach(function (key) {
    var entry = entries[Number(key)];
    // istanbul ignore next
    if (entry) {
      var subscription = entry.subscription,
        subscriber = entry.subscriber,
        notified = entry.notified;
      if (notifySubscriber(subscriber, subscription, state, lastState, filter, force || !notified)) {
        entry.notified = true;
      }
    }
  });
}
function createForm(config) {
  if (!config) {
    throw new Error("No config specified");
  }
  var debug = config.debug,
    destroyOnUnregister = config.destroyOnUnregister,
    keepDirtyOnReinitialize = config.keepDirtyOnReinitialize,
    initialValues = config.initialValues,
    mutators = config.mutators,
    onSubmit = config.onSubmit,
    validate = config.validate,
    validateOnBlur = config.validateOnBlur;
  if (!onSubmit) {
    throw new Error("No onSubmit function specified");
  }
  var state = {
    subscribers: {
      index: 0,
      entries: {}
    },
    fieldSubscribers: {},
    fields: {},
    formState: {
      asyncErrors: {},
      dirtySinceLastSubmit: false,
      modifiedSinceLastSubmit: false,
      errors: {},
      initialValues: initialValues && _extends({}, initialValues),
      invalid: false,
      pristine: true,
      submitting: false,
      submitFailed: false,
      submitSucceeded: false,
      resetWhileSubmitting: false,
      valid: true,
      validating: 0,
      values: initialValues ? _extends({}, initialValues) : {}
    },
    lastFormState: undefined
  };
  var inBatch = 0;
  var validationPaused = false;
  var validationBlocked = false;
  var preventNotificationWhileValidationPaused = false;
  var nextAsyncValidationKey = 0;
  var asyncValidationPromises = {};
  var clearAsyncValidationPromise = function clearAsyncValidationPromise(key) {
    return function (result) {
      delete asyncValidationPromises[key];
      return result;
    };
  };
  var changeValue = function changeValue(state, name, mutate) {
    var before = getIn(state.formState.values, name);
    var after = mutate(before);
    state.formState.values = setIn(state.formState.values, name, after) || {};
  };
  var renameField = function renameField(state, from, to) {
    if (state.fields[from]) {
      var _extends2, _extends3;
      state.fields = _extends({}, state.fields, (_extends2 = {}, _extends2[to] = _extends({}, state.fields[from], {
        name: to,
        // rebind event handlers
        blur: function blur() {
          return api.blur(to);
        },
        change: function change(value) {
          return api.change(to, value);
        },
        focus: function focus() {
          return api.focus(to);
        },
        lastFieldState: undefined
      }), _extends2));
      delete state.fields[from];
      state.fieldSubscribers = _extends({}, state.fieldSubscribers, (_extends3 = {}, _extends3[to] = state.fieldSubscribers[from], _extends3));
      delete state.fieldSubscribers[from];
      var value = getIn(state.formState.values, from);
      state.formState.values = setIn(state.formState.values, from, undefined) || {};
      state.formState.values = setIn(state.formState.values, to, value);
      delete state.lastFormState;
    }
  };

  // bind state to mutators
  var getMutatorApi = function getMutatorApi(key) {
    return function () {
      // istanbul ignore next
      if (mutators) {
        // ^^ causes branch coverage warning, but needed to appease the Flow gods
        var mutatableState = {
          formState: state.formState,
          fields: state.fields,
          fieldSubscribers: state.fieldSubscribers,
          lastFormState: state.lastFormState
        };
        for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
          args[_key] = arguments[_key];
        }
        var returnValue = mutators[key](args, mutatableState, {
          changeValue: changeValue,
          getIn: getIn,
          renameField: renameField,
          resetFieldState: api.resetFieldState,
          setIn: setIn,
          shallowEqual: shallowEqual
        });
        state.formState = mutatableState.formState;
        state.fields = mutatableState.fields;
        state.fieldSubscribers = mutatableState.fieldSubscribers;
        state.lastFormState = mutatableState.lastFormState;
        runValidation(undefined, function () {
          notifyFieldListeners();
          notifyFormListeners();
        });
        return returnValue;
      }
    };
  };
  var mutatorsApi = mutators ? Object.keys(mutators).reduce(function (result, key) {
    result[key] = getMutatorApi(key);
    return result;
  }, {}) : {};
  var runRecordLevelValidation = function runRecordLevelValidation(setErrors) {
    var promises = [];
    if (validate) {
      var errorsOrPromise = validate(_extends({}, state.formState.values)); // clone to avoid writing
      if (isPromise(errorsOrPromise)) {
        promises.push(errorsOrPromise.then(function (errors) {
          return setErrors(errors, true);
        }));
      } else {
        setErrors(errorsOrPromise, false);
      }
    }
    return promises;
  };
  var getValidators = function getValidators(field) {
    return Object.keys(field.validators).reduce(function (result, index) {
      var validator = field.validators[Number(index)]();
      if (validator) {
        result.push(validator);
      }
      return result;
    }, []);
  };
  var runFieldLevelValidation = function runFieldLevelValidation(field, setError) {
    var promises = [];
    var validators = getValidators(field);
    if (validators.length) {
      var error;
      validators.forEach(function (validator) {
        var errorOrPromise = validator(getIn(state.formState.values, field.name), state.formState.values, validator.length === 0 || validator.length === 3 ? publishFieldState(state.formState, state.fields[field.name]) : undefined);
        if (errorOrPromise && isPromise(errorOrPromise)) {
          field.validating = true;
          var promise = errorOrPromise.then(function (error) {
            if (state.fields[field.name]) {
              state.fields[field.name].validating = false;
              setError(error);
            }
          }); // errors must be resolved, not rejected
          promises.push(promise);
        } else if (!error) {
          // first registered validator wins
          error = errorOrPromise;
        }
      });
      setError(error);
    }
    return promises;
  };
  var runValidation = function runValidation(fieldChanged, callback) {
    if (validationPaused) {
      validationBlocked = true;
      callback();
      return;
    }
    var fields = state.fields,
      formState = state.formState;
    var safeFields = _extends({}, fields);
    var fieldKeys = Object.keys(safeFields);
    if (!validate && !fieldKeys.some(function (key) {
      return getValidators(safeFields[key]).length;
    })) {
      callback();
      return; // no validation rules
    }

    // pare down field keys to actually validate
    var limitedFieldLevelValidation = false;
    if (fieldChanged) {
      var changedField = safeFields[fieldChanged];
      if (changedField) {
        var validateFields = changedField.validateFields;
        if (validateFields) {
          limitedFieldLevelValidation = true;
          fieldKeys = validateFields.length ? validateFields.concat(fieldChanged) : [fieldChanged];
        }
      }
    }
    var recordLevelErrors = {};
    var asyncRecordLevelErrors = {};
    var fieldLevelErrors = {};
    var promises = [].concat(runRecordLevelValidation(function (errors, wasAsync) {
      if (wasAsync) {
        asyncRecordLevelErrors = errors || {};
      } else {
        recordLevelErrors = errors || {};
      }
    }), fieldKeys.reduce(function (result, name) {
      return result.concat(runFieldLevelValidation(fields[name], function (error) {
        fieldLevelErrors[name] = error;
      }));
    }, []));
    var hasAsyncValidations = promises.length > 0;
    var asyncValidationPromiseKey = ++nextAsyncValidationKey;
    var promise = Promise.all(promises).then(clearAsyncValidationPromise(asyncValidationPromiseKey));

    // backwards-compat: add promise to submit-blocking promises iff there are any promises to await
    if (hasAsyncValidations) {
      asyncValidationPromises[asyncValidationPromiseKey] = promise;
    }
    var processErrors = function processErrors(afterAsync) {
      var merged = _extends({}, limitedFieldLevelValidation ? formState.errors : {}, recordLevelErrors, afterAsync ? asyncRecordLevelErrors // new async errors
      : formState.asyncErrors);
      var forEachError = function forEachError(fn) {
        fieldKeys.forEach(function (name) {
          if (fields[name]) {
            // make sure field is still registered
            // field-level errors take precedent over record-level errors
            var recordLevelError = getIn(recordLevelErrors, name);
            var errorFromParent = getIn(merged, name);
            var hasFieldLevelValidation = getValidators(safeFields[name]).length;
            var fieldLevelError = fieldLevelErrors[name];
            fn(name, hasFieldLevelValidation && fieldLevelError || validate && recordLevelError || (!recordLevelError && !limitedFieldLevelValidation ? errorFromParent : undefined));
          }
        });
      };
      forEachError(function (name, error) {
        merged = setIn(merged, name, error) || {};
      });
      forEachError(function (name, error) {
        if (error && error[ARRAY_ERROR]) {
          var existing = getIn(merged, name);
          var copy = [].concat(existing);
          copy[ARRAY_ERROR] = error[ARRAY_ERROR];
          merged = setIn(merged, name, copy);
        }
      });
      if (!shallowEqual(formState.errors, merged)) {
        formState.errors = merged;
      }
      if (afterAsync) {
        formState.asyncErrors = asyncRecordLevelErrors;
      }
      formState.error = recordLevelErrors[FORM_ERROR];
    };
    if (hasAsyncValidations) {
      // async validations are running, ensure validating is true before notifying
      state.formState.validating++;
      callback();
    }

    // process sync errors
    processErrors(false);
    // sync errors have been set. notify listeners while we wait for others
    callback();
    if (hasAsyncValidations) {
      var afterPromise = function afterPromise() {
        state.formState.validating--;
        callback();
        // field async validation may affect formState validating
        // so force notifyFormListeners if validating is still 0 after callback finished
        // and lastFormState validating is true
        if (state.formState.validating === 0 && state.lastFormState.validating) {
          notifyFormListeners();
        }
      };
      promise.then(function () {
        if (nextAsyncValidationKey > asyncValidationPromiseKey) {
          // if this async validator has been superseded by another, ignore its results
          return;
        }
        processErrors(true);
      }).then(afterPromise, afterPromise);
    }
  };
  var notifyFieldListeners = function notifyFieldListeners(name) {
    if (inBatch) {
      return;
    }
    var fields = state.fields,
      fieldSubscribers = state.fieldSubscribers,
      formState = state.formState;
    var safeFields = _extends({}, fields);
    var notifyField = function notifyField(name) {
      var field = safeFields[name];
      var fieldState = publishFieldState(formState, field);
      var lastFieldState = field.lastFieldState;
      field.lastFieldState = fieldState;
      var fieldSubscriber = fieldSubscribers[name];
      if (fieldSubscriber) {
        notify(fieldSubscriber, fieldState, lastFieldState, filterFieldState, lastFieldState === undefined);
      }
    };
    if (name) {
      notifyField(name);
    } else {
      Object.keys(safeFields).forEach(notifyField);
    }
  };
  var markAllFieldsTouched = function markAllFieldsTouched() {
    Object.keys(state.fields).forEach(function (key) {
      state.fields[key].touched = true;
    });
  };
  var hasSyncErrors = function hasSyncErrors() {
    return !!(state.formState.error || hasAnyError(state.formState.errors));
  };
  var calculateNextFormState = function calculateNextFormState() {
    var fields = state.fields,
      formState = state.formState,
      lastFormState = state.lastFormState;
    var safeFields = _extends({}, fields);
    var safeFieldKeys = Object.keys(safeFields);

    // calculate dirty/pristine
    var foundDirty = false;
    var dirtyFields = safeFieldKeys.reduce(function (result, key) {
      var dirty = !safeFields[key].isEqual(getIn(formState.values, key), getIn(formState.initialValues || {}, key));
      if (dirty) {
        foundDirty = true;
        result[key] = true;
      }
      return result;
    }, {});
    var dirtyFieldsSinceLastSubmit = safeFieldKeys.reduce(function (result, key) {
      // istanbul ignore next
      var nonNullLastSubmittedValues = formState.lastSubmittedValues || {}; // || {} is for flow, but causes branch coverage complaint
      if (!safeFields[key].isEqual(getIn(formState.values, key), getIn(nonNullLastSubmittedValues, key))) {
        result[key] = true;
      }
      return result;
    }, {});
    formState.pristine = !foundDirty;
    formState.dirtySinceLastSubmit = !!(formState.lastSubmittedValues && Object.values(dirtyFieldsSinceLastSubmit).some(function (value) {
      return value;
    }));
    formState.modifiedSinceLastSubmit = !!(formState.lastSubmittedValues &&
    // Object.values would treat values as mixed (facebook/flow#2221)
    Object.keys(safeFields).some(function (value) {
      return safeFields[value].modifiedSinceLastSubmit;
    }));
    formState.valid = !formState.error && !formState.submitError && !hasAnyError(formState.errors) && !(formState.submitErrors && hasAnyError(formState.submitErrors));
    var nextFormState = convertToExternalFormState(formState);
    var _safeFieldKeys$reduce = safeFieldKeys.reduce(function (result, key) {
        result.modified[key] = safeFields[key].modified;
        result.touched[key] = safeFields[key].touched;
        result.visited[key] = safeFields[key].visited;
        return result;
      }, {
        modified: {},
        touched: {},
        visited: {}
      }),
      modified = _safeFieldKeys$reduce.modified,
      touched = _safeFieldKeys$reduce.touched,
      visited = _safeFieldKeys$reduce.visited;
    nextFormState.dirtyFields = lastFormState && shallowEqual(lastFormState.dirtyFields, dirtyFields) ? lastFormState.dirtyFields : dirtyFields;
    nextFormState.dirtyFieldsSinceLastSubmit = lastFormState && shallowEqual(lastFormState.dirtyFieldsSinceLastSubmit, dirtyFieldsSinceLastSubmit) ? lastFormState.dirtyFieldsSinceLastSubmit : dirtyFieldsSinceLastSubmit;
    nextFormState.modified = lastFormState && shallowEqual(lastFormState.modified, modified) ? lastFormState.modified : modified;
    nextFormState.touched = lastFormState && shallowEqual(lastFormState.touched, touched) ? lastFormState.touched : touched;
    nextFormState.visited = lastFormState && shallowEqual(lastFormState.visited, visited) ? lastFormState.visited : visited;
    return lastFormState && shallowEqual(lastFormState, nextFormState) ? lastFormState : nextFormState;
  };
  var callDebug = function callDebug() {
    return debug && "development" !== "production" && debug(calculateNextFormState(), Object.keys(state.fields).reduce(function (result, key) {
      result[key] = state.fields[key];
      return result;
    }, {}));
  };
  var notifying = false;
  var scheduleNotification = false;
  var notifyFormListeners = function notifyFormListeners() {
    if (notifying) {
      scheduleNotification = true;
    } else {
      notifying = true;
      callDebug();
      if (!inBatch && !(validationPaused && preventNotificationWhileValidationPaused)) {
        var lastFormState = state.lastFormState;
        var nextFormState = calculateNextFormState();
        if (nextFormState !== lastFormState) {
          state.lastFormState = nextFormState;
          notify(state.subscribers, nextFormState, lastFormState, filterFormState);
        }
      }
      notifying = false;
      if (scheduleNotification) {
        scheduleNotification = false;
        notifyFormListeners();
      }
    }
  };
  var beforeSubmit = function beforeSubmit() {
    return Object.keys(state.fields).some(function (name) {
      return state.fields[name].beforeSubmit && state.fields[name].beforeSubmit() === false;
    });
  };
  var afterSubmit = function afterSubmit() {
    return Object.keys(state.fields).forEach(function (name) {
      return state.fields[name].afterSubmit && state.fields[name].afterSubmit();
    });
  };
  var resetModifiedAfterSubmit = function resetModifiedAfterSubmit() {
    return Object.keys(state.fields).forEach(function (key) {
      return state.fields[key].modifiedSinceLastSubmit = false;
    });
  };

  // generate initial errors
  runValidation(undefined, function () {
    notifyFormListeners();
  });
  var api = {
    batch: function batch(fn) {
      inBatch++;
      fn();
      inBatch--;
      notifyFieldListeners();
      notifyFormListeners();
    },
    blur: function blur(name) {
      var fields = state.fields,
        formState = state.formState;
      var previous = fields[name];
      if (previous) {
        // can only blur registered fields
        delete formState.active;
        fields[name] = _extends({}, previous, {
          active: false,
          touched: true
        });
        if (validateOnBlur) {
          runValidation(name, function () {
            notifyFieldListeners();
            notifyFormListeners();
          });
        } else {
          notifyFieldListeners();
          notifyFormListeners();
        }
      }
    },
    change: function change(name, value) {
      var fields = state.fields,
        formState = state.formState;
      if (getIn(formState.values, name) !== value) {
        changeValue(state, name, function () {
          return value;
        });
        var previous = fields[name];
        if (previous) {
          // only track modified for registered fields
          fields[name] = _extends({}, previous, {
            modified: true,
            modifiedSinceLastSubmit: !!formState.lastSubmittedValues
          });
        }
        if (validateOnBlur) {
          notifyFieldListeners();
          notifyFormListeners();
        } else {
          runValidation(name, function () {
            notifyFieldListeners();
            notifyFormListeners();
          });
        }
      }
    },
    get destroyOnUnregister() {
      return !!destroyOnUnregister;
    },
    set destroyOnUnregister(value) {
      destroyOnUnregister = value;
    },
    focus: function focus(name) {
      var field = state.fields[name];
      if (field && !field.active) {
        state.formState.active = name;
        field.active = true;
        field.visited = true;
        notifyFieldListeners();
        notifyFormListeners();
      }
    },
    mutators: mutatorsApi,
    getFieldState: function getFieldState(name) {
      var field = state.fields[name];
      return field && field.lastFieldState;
    },
    getRegisteredFields: function getRegisteredFields() {
      return Object.keys(state.fields);
    },
    getState: function getState() {
      return calculateNextFormState();
    },
    initialize: function initialize(data) {
      var fields = state.fields,
        formState = state.formState;
      var safeFields = _extends({}, fields);
      var values = typeof data === "function" ? data(formState.values) : data;
      if (!keepDirtyOnReinitialize) {
        formState.values = values;
      }
      /**
       * Hello, inquisitive code reader! Thanks for taking the time to dig in!
       *
       * The following code is the way it is to allow for non-registered deep
       * field values to be set via initialize()
       */

      // save dirty values
      var savedDirtyValues = keepDirtyOnReinitialize ? Object.keys(safeFields).reduce(function (result, key) {
        var field = safeFields[key];
        var pristine = field.isEqual(getIn(formState.values, key), getIn(formState.initialValues || {}, key));
        if (!pristine) {
          result[key] = getIn(formState.values, key);
        }
        return result;
      }, {}) : {};
      // update initalValues and values
      formState.initialValues = values;
      formState.values = values;
      // restore the dirty values
      Object.keys(savedDirtyValues).forEach(function (key) {
        formState.values = setIn(formState.values, key, savedDirtyValues[key]) || {};
      });
      runValidation(undefined, function () {
        notifyFieldListeners();
        notifyFormListeners();
      });
    },
    isValidationPaused: function isValidationPaused() {
      return validationPaused;
    },
    pauseValidation: function pauseValidation(preventNotification) {
      if (preventNotification === void 0) {
        preventNotification = true;
      }
      validationPaused = true;
      preventNotificationWhileValidationPaused = preventNotification;
    },
    registerField: function registerField(name, subscriber, subscription, fieldConfig) {
      if (subscription === void 0) {
        subscription = {};
      }
      if (!state.fieldSubscribers[name]) {
        state.fieldSubscribers[name] = {
          index: 0,
          entries: {}
        };
      }
      var index = state.fieldSubscribers[name].index++;

      // save field subscriber callback
      state.fieldSubscribers[name].entries[index] = {
        subscriber: memoize(subscriber),
        subscription: subscription,
        notified: false
      };

      // create initial field state if not exists
      var field = state.fields[name] || {
        active: false,
        afterSubmit: fieldConfig && fieldConfig.afterSubmit,
        beforeSubmit: fieldConfig && fieldConfig.beforeSubmit,
        data: fieldConfig && fieldConfig.data || {},
        isEqual: fieldConfig && fieldConfig.isEqual || tripleEquals,
        lastFieldState: undefined,
        modified: false,
        modifiedSinceLastSubmit: false,
        name: name,
        touched: false,
        valid: true,
        validateFields: fieldConfig && fieldConfig.validateFields,
        validators: {},
        validating: false,
        visited: false
      };
      // Mutators can create a field in order to keep the field states
      // We must update this field when registerField is called afterwards
      field.blur = field.blur || function () {
        return api.blur(name);
      };
      field.change = field.change || function (value) {
        return api.change(name, value);
      };
      field.focus = field.focus || function () {
        return api.focus(name);
      };
      state.fields[name] = field;
      var haveValidator = false;
      var silent = fieldConfig && fieldConfig.silent;
      var notify = function notify() {
        if (silent && state.fields[name]) {
          notifyFieldListeners(name);
        } else {
          notifyFormListeners();
          notifyFieldListeners();
        }
      };
      if (fieldConfig) {
        haveValidator = !!(fieldConfig.getValidator && fieldConfig.getValidator());
        if (fieldConfig.getValidator) {
          state.fields[name].validators[index] = fieldConfig.getValidator;
        }
        var noValueInFormState = getIn(state.formState.values, name) === undefined;
        if (fieldConfig.initialValue !== undefined && (noValueInFormState || getIn(state.formState.values, name) === getIn(state.formState.initialValues, name))
        // only initialize if we don't yet have any value for this field
        ) {
          state.formState.initialValues = setIn(state.formState.initialValues || {}, name, fieldConfig.initialValue);
          state.formState.values = setIn(state.formState.values, name, fieldConfig.initialValue);
          runValidation(undefined, notify);
        }

        // only use defaultValue if we don't yet have any value for this field
        if (fieldConfig.defaultValue !== undefined && fieldConfig.initialValue === undefined && getIn(state.formState.initialValues, name) === undefined && noValueInFormState) {
          state.formState.values = setIn(state.formState.values, name, fieldConfig.defaultValue);
        }
      }
      if (haveValidator) {
        runValidation(undefined, notify);
      } else {
        notify();
      }
      return function () {
        var validatorRemoved = false;
        // istanbul ignore next
        if (state.fields[name]) {
          // state.fields[name] may have been removed by a mutator
          validatorRemoved = !!(state.fields[name].validators[index] && state.fields[name].validators[index]());
          delete state.fields[name].validators[index];
        }
        var hasFieldSubscribers = !!state.fieldSubscribers[name];
        if (hasFieldSubscribers) {
          // state.fieldSubscribers[name] may have been removed by a mutator
          delete state.fieldSubscribers[name].entries[index];
        }
        var lastOne = hasFieldSubscribers && !Object.keys(state.fieldSubscribers[name].entries).length;
        if (lastOne) {
          delete state.fieldSubscribers[name];
          delete state.fields[name];
          if (validatorRemoved) {
            state.formState.errors = setIn(state.formState.errors, name, undefined) || {};
          }
          if (destroyOnUnregister) {
            state.formState.values = setIn(state.formState.values, name, undefined, true) || {};
          }
        }
        if (!silent) {
          if (validatorRemoved) {
            runValidation(undefined, function () {
              notifyFormListeners();
              notifyFieldListeners();
            });
          } else if (lastOne) {
            // values or errors may have changed
            notifyFormListeners();
          }
        }
      };
    },
    reset: function reset(initialValues) {
      if (initialValues === void 0) {
        initialValues = state.formState.initialValues;
      }
      if (state.formState.submitting) {
        state.formState.resetWhileSubmitting = true;
      }
      state.formState.submitFailed = false;
      state.formState.submitSucceeded = false;
      delete state.formState.submitError;
      delete state.formState.submitErrors;
      delete state.formState.lastSubmittedValues;
      api.initialize(initialValues || {});
    },
    /**
     * Resets all field flags (e.g. touched, visited, etc.) to their initial state
     */
    resetFieldState: function resetFieldState(name) {
      state.fields[name] = _extends({}, state.fields[name], {
        active: false,
        lastFieldState: undefined,
        modified: false,
        touched: false,
        valid: true,
        validating: false,
        visited: false
      });
      runValidation(undefined, function () {
        notifyFieldListeners();
        notifyFormListeners();
      });
    },
    /**
     * Returns the form to a clean slate; that is:
     * - Clear all values
     * - Resets all fields to their initial state
     */
    restart: function restart(initialValues) {
      if (initialValues === void 0) {
        initialValues = state.formState.initialValues;
      }
      api.batch(function () {
        for (var name in state.fields) {
          api.resetFieldState(name);
          state.fields[name] = _extends({}, state.fields[name], {
            active: false,
            lastFieldState: undefined,
            modified: false,
            modifiedSinceLastSubmit: false,
            touched: false,
            valid: true,
            validating: false,
            visited: false
          });
        }
        api.reset(initialValues);
      });
    },
    resumeValidation: function resumeValidation() {
      validationPaused = false;
      preventNotificationWhileValidationPaused = false;
      if (validationBlocked) {
        // validation was attempted while it was paused, so run it now
        runValidation(undefined, function () {
          notifyFieldListeners();
          notifyFormListeners();
        });
      }
      validationBlocked = false;
    },
    setConfig: function setConfig(name, value) {
      switch (name) {
        case "debug":
          debug = value;
          break;
        case "destroyOnUnregister":
          destroyOnUnregister = value;
          break;
        case "initialValues":
          api.initialize(value);
          break;
        case "keepDirtyOnReinitialize":
          keepDirtyOnReinitialize = value;
          break;
        case "mutators":
          mutators = value;
          if (value) {
            Object.keys(mutatorsApi).forEach(function (key) {
              if (!(key in value)) {
                delete mutatorsApi[key];
              }
            });
            Object.keys(value).forEach(function (key) {
              mutatorsApi[key] = getMutatorApi(key);
            });
          } else {
            Object.keys(mutatorsApi).forEach(function (key) {
              delete mutatorsApi[key];
            });
          }
          break;
        case "onSubmit":
          onSubmit = value;
          break;
        case "validate":
          validate = value;
          runValidation(undefined, function () {
            notifyFieldListeners();
            notifyFormListeners();
          });
          break;
        case "validateOnBlur":
          validateOnBlur = value;
          break;
        default:
          throw new Error("Unrecognised option " + name);
      }
    },
    submit: function submit() {
      var formState = state.formState;
      if (formState.submitting) {
        return;
      }
      delete formState.submitErrors;
      delete formState.submitError;
      formState.lastSubmittedValues = _extends({}, formState.values);
      if (hasSyncErrors()) {
        markAllFieldsTouched();
        resetModifiedAfterSubmit();
        state.formState.submitFailed = true;
        notifyFormListeners();
        notifyFieldListeners();
        return; // no submit for you!!
      }

      var asyncValidationPromisesKeys = Object.keys(asyncValidationPromises);
      if (asyncValidationPromisesKeys.length) {
        // still waiting on async validation to complete...
        Promise.all(asyncValidationPromisesKeys.map(function (key) {
          return asyncValidationPromises[Number(key)];
        })).then(api.submit, console.error);
        return;
      }
      var submitIsBlocked = beforeSubmit();
      if (submitIsBlocked) {
        return;
      }
      var resolvePromise;
      var completeCalled = false;
      var complete = function complete(errors) {
        formState.submitting = false;
        var resetWhileSubmitting = formState.resetWhileSubmitting;
        if (resetWhileSubmitting) {
          formState.resetWhileSubmitting = false;
        }
        if (errors && hasAnyError(errors)) {
          formState.submitFailed = true;
          formState.submitSucceeded = false;
          formState.submitErrors = errors;
          formState.submitError = errors[FORM_ERROR];
          markAllFieldsTouched();
        } else {
          if (!resetWhileSubmitting) {
            formState.submitFailed = false;
            formState.submitSucceeded = true;
          }
          afterSubmit();
        }
        notifyFormListeners();
        notifyFieldListeners();
        completeCalled = true;
        if (resolvePromise) {
          resolvePromise(errors);
        }
        return errors;
      };
      formState.submitting = true;
      formState.submitFailed = false;
      formState.submitSucceeded = false;
      formState.lastSubmittedValues = _extends({}, formState.values);
      resetModifiedAfterSubmit();

      // onSubmit is either sync, callback or async with a Promise
      var result = onSubmit(formState.values, api, complete);
      if (!completeCalled) {
        if (result && isPromise(result)) {
          // onSubmit is async with a Promise
          notifyFormListeners(); // let everyone know we are submitting
          notifyFieldListeners(); // notify fields also
          return result.then(complete, function (error) {
            complete();
            throw error;
          });
        } else if (onSubmit.length >= 3) {
          // must be async, so we should return a Promise
          notifyFormListeners(); // let everyone know we are submitting
          notifyFieldListeners(); // notify fields also
          return new Promise(function (resolve) {
            resolvePromise = resolve;
          });
        } else {
          // onSubmit is sync
          complete(result);
        }
      }
    },
    subscribe: function subscribe(subscriber, subscription) {
      if (!subscriber) {
        throw new Error("No callback given.");
      }
      if (!subscription) {
        throw new Error("No subscription provided. What values do you want to listen to?");
      }
      var memoized = memoize(subscriber);
      var subscribers = state.subscribers;
      var index = subscribers.index++;
      subscribers.entries[index] = {
        subscriber: memoized,
        subscription: subscription,
        notified: false
      };
      var nextFormState = calculateNextFormState();
      notifySubscriber(memoized, subscription, nextFormState, nextFormState, filterFormState, true);
      return function () {
        delete subscribers.entries[index];
      };
    }
  };
  return api;
}

export { ARRAY_ERROR, FORM_ERROR, configOptions, createForm, fieldSubscriptionItems, formSubscriptionItems, getIn, setIn, version };