Newer
Older
reroad-test / 2020-fuga / aframe-master / src / core / schema.js
@fuga sakurai fuga sakurai on 4 Nov 2020 6 KB a-フレームを追加した
var utils = require('../utils/');
var PropertyTypes = require('./propertyTypes');

var debug = utils.debug;
var isValidDefaultValue = PropertyTypes.isValidDefaultValue;
var propertyTypes = PropertyTypes.propertyTypes;

var warn = debug('core:schema:warn');

/**
 * A schema is classified as a schema for a single property if:
 * - `type` is defined on the schema as a string.
 * - OR `default` is defined on the schema, as a reserved keyword.
 * - OR schema is empty.
 */
function isSingleProperty (schema) {
  if ('type' in schema) {
    return typeof schema.type === 'string';
  }
  return 'default' in schema;
}
module.exports.isSingleProperty = isSingleProperty;

/**
 * Build step to schema to use `type` to inject default value, parser, and stringifier.
 *
 * @param {object} schema
 * @param {string} componentName
 * @returns {object} Schema.
 */
module.exports.process = function (schema, componentName) {
  var propName;

  // For single property schema, run processPropDefinition over the whole schema.
  if (isSingleProperty(schema)) {
    return processPropertyDefinition(schema, componentName);
  }

  // For multi-property schema, run processPropDefinition over each property definition.
  for (propName in schema) {
    schema[propName] = processPropertyDefinition(schema[propName], componentName);
  }
  return schema;
};

/**
 * Inject default value, parser, stringifier for single property.
 *
 * @param {object} propDefinition
 * @param {string} componentName
 */
function processPropertyDefinition (propDefinition, componentName) {
  var defaultVal = propDefinition.default;
  var isCustomType;
  var propType;
  var typeName = propDefinition.type;

  // Type inference.
  if (!propDefinition.type) {
    if (defaultVal !== undefined &&
        (typeof defaultVal === 'boolean' || typeof defaultVal === 'number')) {
      // Type inference.
      typeName = typeof defaultVal;
    } else if (Array.isArray(defaultVal)) {
      typeName = 'array';
    } else {
      // Fall back to string.
      typeName = 'string';
    }
  } else if (propDefinition.type === 'bool') {
    typeName = 'boolean';
  } else if (propDefinition.type === 'float') {
    typeName = 'number';
  }

  propType = propertyTypes[typeName];
  if (!propType) {
    warn('Unknown property type for component `' + componentName + '`: ' + typeName);
  }

  // Fill in parse and stringify using property types.
  isCustomType = !!propDefinition.parse;
  propDefinition.parse = propDefinition.parse || propType.parse;
  propDefinition.stringify = propDefinition.stringify || propType.stringify;

  // Fill in type name.
  propDefinition.type = typeName;

  // Check that default value exists.
  if ('default' in propDefinition) {
    // Check that default values are valid.
    if (!isCustomType && !isValidDefaultValue(typeName, defaultVal)) {
      warn('Default value `' + defaultVal + '` does not match type `' + typeName +
           '` in component `' + componentName + '`');
    }
  } else {
    // Fill in default value.
    propDefinition.default = propType.default;
  }

  return propDefinition;
}
module.exports.processPropertyDefinition = processPropertyDefinition;

/**
 * Parse propData using schema. Use default values if not existing in propData.
 *
 * @param {object} propData - Unparsed properties.
 * @param {object} schema - Property types definition.
 * @param {boolean} getPartialData - Whether to return full component data or just the data
 *        with keys in `propData`.
 * @param {string } componentName - Name of the component, used for the property warning.
 * @param {boolean} silent - Suppress warning messages.
 */
module.exports.parseProperties = (function () {
  var propNames = [];

  return function (propData, schema, getPartialData, componentName, silent) {
    var i;
    var propName;
    var propDefinition;
    var propValue;

    propNames.length = 0;
    for (propName in (getPartialData ? propData : schema)) {
      if (getPartialData && propData[propName] === undefined) { continue; }
      propNames.push(propName);
    }

    if (propData === null || typeof propData !== 'object') { return propData; }

    // Validation errors.
    for (propName in propData) {
      if (propData[propName] !== undefined && !schema[propName] && !silent) {
        warn('Unknown property `' + propName +
             '` for component/system `' + componentName + '`.');
      }
    }

    for (i = 0; i < propNames.length; i++) {
      propName = propNames[i];
      propDefinition = schema[propName];
      propValue = propData[propName];
      if (!(schema[propName])) { return; }
      propData[propName] = parseProperty(propValue, propDefinition);
    }

    return propData;
  };
})();

/**
 * Deserialize a single property.
 */
function parseProperty (value, propDefinition) {
  // Use default value if value is falsy.
  if (value === undefined || value === null || value === '') {
    value = propDefinition.default;
    if (Array.isArray(value)) { value = value.slice(); }
  }
  // Invoke property type parser.
  return propDefinition.parse(value, propDefinition.default);
}
module.exports.parseProperty = parseProperty;

/**
 * Serialize a group of properties.
 */
module.exports.stringifyProperties = function (propData, schema) {
  var propName;
  var propDefinition;
  var propValue;
  var stringifiedData = {};
  var value;

  for (propName in propData) {
    propDefinition = schema[propName];
    propValue = propData[propName];
    value = propValue;
    if (typeof value === 'object') {
      value = stringifyProperty(propValue, propDefinition);
      if (!propDefinition) { warn('Unknown component property: ' + propName); }
    }
    stringifiedData[propName] = value;
  }
  return stringifiedData;
};

/**
 * Serialize a single property.
 */
function stringifyProperty (value, propDefinition) {
  // This function stringifies but it's used in a context where
  // there's always second stringification pass. By returning the original
  // value when it's not an object we save one unnecessary call
  // to JSON.stringify.
  if (typeof value !== 'object') { return value; }
  // if there's no schema for the property we use standar JSON stringify
  if (!propDefinition || value === null) { return JSON.stringify(value); }
  return propDefinition.stringify(value);
}
module.exports.stringifyProperty = stringifyProperty;