Source: form-validate/modules/validation-property.js

/**
 * @summary validation-property
 * @version 3.0.0
 * @since 1.0.0
 * @author Arian Khosravi <arian.khosravi@aofl.com>
 */
import {ValidationFunction} from './validation-function';

/**
 * ValidationProperty implementation
 *
 * @memberof module:@aofl/form-validate
 */
class ValidationProperty {
  /**
   * Creates an instance of ValidationProperty.
   *
   * @param {Object} target
   * @param {Object} validators
   * @param {String} _propName
   */
  constructor(target, validators, _propName = '') {
    Object.defineProperty(this, 'resolve', {
      writable: true
    });
    Object.defineProperty(this, 'validateCompleteResolved', {
      writable: true
    });
    Object.defineProperty(this, 'validateCompletePromise', {
      writable: true
    });

    for (const key in validators) {
      /* istanbul ignore next */
      if (!Object.prototype.hasOwnProperty.call(validators, key)) continue;
      /* istanbul ignore next */
      if (Object.prototype.hasOwnProperty.call(this, key)) {
        throw new Error(`${key} is a reserved keyword and cannot be used as a validator name.`);
      }

      let propName = _propName; // @todo: move to object-utils
      if (propName.length === 0) {
        propName = key;
      } else {
        propName += '.' + key;
      }

      if (typeof validators[key] === 'function') {
        this[key] = new ValidationFunction(target, validators[key], propName);
      } else {
        this[key] = new ValidationProperty(target, validators[key], propName);
      }
    }

    this.reset();
  }

  /**
   *
   */
  reset() {
    this.validateCompletePromise = new Promise((resolve) => {
      this.resolve = resolve;
    });
    this.validateCompleteResolved = false;

    for (const key in this) {
      /* istanbul ignore next */
      if (!Object.prototype.hasOwnProperty.call(this, key) ||
      !(this[key] instanceof ValidationProperty || this[key] instanceof ValidationFunction)) continue;

      this[key].reset();
    }
  }

  /**
   *
   * @readonly
   * @type {Boolean}
   */
  get valid() {
    for (const key in this) {
      /* istanbul ignore next */
      if (!Object.prototype.hasOwnProperty.call(this, key) ||
      !(this[key] instanceof ValidationProperty || this[key] instanceof ValidationFunction)) continue;

      if (this[key].valid === false) {
        return false;
      }
    }
    return true;
  }

  /**
   *
   * @readonly
   * @type {Boolean}
   */
  get pending() {
    for (const key in this) {
      /* istanbul ignore next */
      if (!Object.prototype.hasOwnProperty.call(this, key) ||
      !(this[key] instanceof ValidationProperty || this[key] instanceof ValidationFunction)) continue;

      if (this[key].pending === true) {
        return true;
      }
    }
    return false;
  }

  /**
   *
   * @readonly
   * @type {Boolean}
   */
  get observed() {
    for (const key in this) {
      /* istanbul ignore next */
      if (!Object.prototype.hasOwnProperty.call(this, key) ||
      !(this[key] instanceof ValidationProperty || this[key] instanceof ValidationFunction)) continue;

      if (this[key].observed === true) {
        return true;
      }
    }
    return false;
  }

  /**
   *
   *
   */
  validate() {
    for (const key in this) {
      /* istanbul ignore next */
      if (!Object.prototype.hasOwnProperty.call(this, key) ||
      !(this[key] instanceof ValidationProperty || this[key] instanceof ValidationFunction)) continue;

      this[key].validate();
    }
  }

  /**
   *
   * @readonly
   * @type {Promise}
   */
  get validateComplete() {
    if (this.pending && this.validateCompleteResolved) {
      this.validateCompleteResolved = false;
      this.validateCompletePromise = new Promise((resolve) => {
        this.resolve = resolve;
      });
    }

    const checkPendingPromises = () => {
      const promises = Object.keys(this).map((item) => this[item].validateComplete);
      Promise.all(promises)
        .then(() => {
          if (this.pending) {
            return checkPendingPromises();
          }

          this.resolve();
          this.validateCompleteResolved = true;
        });
    };

    checkPendingPromises();
    return this.validateCompletePromise;
  }

  /* istanbul ignore next */
  /**
   *
   * @return {Array}
   */
  getKeys() {
    const keys = ['valid', 'pending', 'observed'];
    for (const key in this) {
      /* istanbul ignore next */
      if (!Object.prototype.hasOwnProperty.call(this, key)) continue;
      keys.push(key);
      keys.push(...this[key].getKeys());
    }
    return keys;
  }

  /* istanbul ignore next */
  /**
   * @return {String}
   */
  toString() {
    const keys = this.getKeys();
    return JSON.stringify(this, keys, 2);
  }
}

export {
  ValidationProperty
};