Newer
Older
reroad-test / 2020-fuga / aframe-master / src / extras / primitives / primitives.js
@fuga sakurai fuga sakurai on 4 Nov 2020 6 KB a-フレームを追加した
var AEntity = require('../../core/a-entity');
var components = require('../../core/component').components;
var registerElement = require('../../core/a-register-element').registerElement;
var utils = require('../../utils/');

var debug = utils.debug;
var setComponentProperty = utils.entity.setComponentProperty;
var log = debug('extras:primitives:debug');
var warn = debug('extras:primitives:warn');

var primitives = module.exports.primitives = {};

module.exports.registerPrimitive = function registerPrimitive (name, definition) {
  name = name.toLowerCase();
  log('Registering <%s>', name);

  // Deprecation warning for defaultAttributes usage.
  if (definition.defaultAttributes) {
    warn("The 'defaultAttributes' object is deprecated. Use 'defaultComponents' instead.");
  }

  var primitive = registerElement(name, {
    prototype: Object.create(AEntity.prototype, {
      defaultComponentsFromPrimitive: {
        value: definition.defaultComponents || definition.defaultAttributes || {}
      },
      deprecated: {value: definition.deprecated || null},
      deprecatedMappings: {value: definition.deprecatedMappings || {}},
      mappings: {value: definition.mappings || {}},

      createdCallback: {
        value: function () {
          if (definition.deprecated) { console.warn(definition.deprecated); }
          this.resolveMappingCollisions();
        }
      },

      /**
       * If a mapping collides with a registered component name
       * it renames the mapping to componentname-property
       */
      resolveMappingCollisions: {
        value: function () {
          var mappings = this.mappings;
          var self = this;
          Object.keys(mappings).forEach(function resolveCollision (key) {
            var newAttribute;
            if (key !== key.toLowerCase()) { warn('Mapping keys should be specified in lower case. The mapping key ' + key + ' may not be recognized'); }
            if (components[key]) {
              newAttribute = mappings[key].replace('.', '-');
              mappings[newAttribute] = mappings[key];
              delete mappings[key];
              console.warn('The primitive ' + self.tagName.toLowerCase() + ' has a mapping collision. ' +
                           'The attribute ' + key + ' has the same name as a registered component and' +
                           ' has been renamed to ' + newAttribute);
            }
          });
        }
      },

      getExtraComponents: {
        value: function () {
          var attr;
          var data;
          var i;
          var mapping;
          var mixins;
          var path;
          var self = this;

          // Gather component data from default components.
          data = utils.clone(this.defaultComponentsFromPrimitive);

          // Factor in mixins to overwrite default components.
          mixins = this.getAttribute('mixin');
          if (mixins) {
            mixins = mixins.trim().split(' ');
            mixins.forEach(function applyMixin (mixinId) {
              var mixinComponents = self.sceneEl.querySelector('#' + mixinId).componentCache;
              Object.keys(mixinComponents).forEach(function setComponent (name) {
                data[name] = extend(data[name], mixinComponents[name]);
              });
            });
          }

          // Gather component data from mappings.
          for (i = 0; i < this.attributes.length; i++) {
            attr = this.attributes[i];
            mapping = this.mappings[attr.name];
            if (mapping) {
              path = utils.entity.getComponentPropertyPath(mapping);
              if (path.constructor === Array) {
                data[path[0]] = data[path[0]] || {};
                data[path[0]][path[1]] = attr.value.trim();
              } else {
                data[path] = attr.value.trim();
              }
              continue;
            }
          }

          return data;

          /**
           * For the base to be extensible, both objects must be pure JavaScript objects.
           * The function assumes that base is undefined, or null or a pure object.
           */
          function extend (base, extension) {
            if (isUndefined(base)) {
              return copy(extension);
            }
            if (isUndefined(extension)) {
              return copy(base);
            }
            if (isPureObject(base) && isPureObject(extension)) {
              return utils.extendDeep(base, extension);
            }
            return copy(extension);
          }

          function isUndefined (value) {
            return typeof value === 'undefined';
          }

          function copy (value) {
            if (isPureObject(value)) {
              return utils.extendDeep({}, value);
            }
            return value;
          }

          function isPureObject (value) {
            return value !== null && value.constructor === Object;
          }
        }
      },

      /**
       * Sync to attribute to component property whenever mapped attribute changes.
       * If attribute is mapped to a component property, set the component property using
       * the attribute value.
       */
      attributeChangedCallback: {
        value: function (attr, oldVal, value) {
          var componentName = this.mappings[attr];

          if (attr in this.deprecatedMappings) {
            console.warn(this.deprecatedMappings[attr]);
          }

          if (!attr || !componentName) { return; }

          // Set value.
          setComponentProperty(this, componentName, value);
        }
      }
    })
  });

  // Store.
  primitives[name] = primitive;
  return primitive;
};

/**
 * Add component mappings using schema.
 */
function addComponentMapping (componentName, mappings) {
  var schema = components[componentName].schema;
  Object.keys(schema).map(function (prop) {
    // Hyphenate where there is camelCase.
    var attrName = prop.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
    // If there is a mapping collision, prefix with component name and hyphen.
    if (mappings[attrName] !== undefined) { attrName = componentName + '-' + prop; }
    mappings[attrName] = componentName + '.' + prop;
  });
}

/**
 * Helper to define a primitive, building mappings using a component schema.
 */
function definePrimitive (tagName, defaultComponents, mappings) {
  // If no initial mappings provided, start from empty map.
  mappings = mappings || {};

  // From the default components, add mapping automagically.
  Object.keys(defaultComponents).map(function buildMappings (componentName) {
    addComponentMapping(componentName, mappings);
  });

  // Register the primitive.
  module.exports.registerPrimitive(tagName, utils.extendDeep({}, null, {
    defaultComponents: defaultComponents,
    mappings: mappings
  }));
}
module.exports.definePrimitive = definePrimitive;