208 lines
8.0 KiB
JavaScript
208 lines
8.0 KiB
JavaScript
|
/**
|
||
|
* Copyright 2013-present, Facebook, Inc.
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* This source code is licensed under the BSD-style license found in the
|
||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
'use strict';
|
||
|
|
||
|
var _prodInvariant = require('./reactProdInvariant');
|
||
|
|
||
|
var invariant = require('fbjs/lib/invariant');
|
||
|
|
||
|
function checkMask(value, bitmask) {
|
||
|
return (value & bitmask) === bitmask;
|
||
|
}
|
||
|
|
||
|
var DOMPropertyInjection = {
|
||
|
/**
|
||
|
* Mapping from normalized, camelcased property names to a configuration that
|
||
|
* specifies how the associated DOM property should be accessed or rendered.
|
||
|
*/
|
||
|
MUST_USE_PROPERTY: 0x1,
|
||
|
HAS_BOOLEAN_VALUE: 0x4,
|
||
|
HAS_NUMERIC_VALUE: 0x8,
|
||
|
HAS_POSITIVE_NUMERIC_VALUE: 0x10 | 0x8,
|
||
|
HAS_OVERLOADED_BOOLEAN_VALUE: 0x20,
|
||
|
|
||
|
/**
|
||
|
* Inject some specialized knowledge about the DOM. This takes a config object
|
||
|
* with the following properties:
|
||
|
*
|
||
|
* isCustomAttribute: function that given an attribute name will return true
|
||
|
* if it can be inserted into the DOM verbatim. Useful for data-* or aria-*
|
||
|
* attributes where it's impossible to enumerate all of the possible
|
||
|
* attribute names,
|
||
|
*
|
||
|
* Properties: object mapping DOM property name to one of the
|
||
|
* DOMPropertyInjection constants or null. If your attribute isn't in here,
|
||
|
* it won't get written to the DOM.
|
||
|
*
|
||
|
* DOMAttributeNames: object mapping React attribute name to the DOM
|
||
|
* attribute name. Attribute names not specified use the **lowercase**
|
||
|
* normalized name.
|
||
|
*
|
||
|
* DOMAttributeNamespaces: object mapping React attribute name to the DOM
|
||
|
* attribute namespace URL. (Attribute names not specified use no namespace.)
|
||
|
*
|
||
|
* DOMPropertyNames: similar to DOMAttributeNames but for DOM properties.
|
||
|
* Property names not specified use the normalized name.
|
||
|
*
|
||
|
* DOMMutationMethods: Properties that require special mutation methods. If
|
||
|
* `value` is undefined, the mutation method should unset the property.
|
||
|
*
|
||
|
* @param {object} domPropertyConfig the config as described above.
|
||
|
*/
|
||
|
injectDOMPropertyConfig: function (domPropertyConfig) {
|
||
|
var Injection = DOMPropertyInjection;
|
||
|
var Properties = domPropertyConfig.Properties || {};
|
||
|
var DOMAttributeNamespaces = domPropertyConfig.DOMAttributeNamespaces || {};
|
||
|
var DOMAttributeNames = domPropertyConfig.DOMAttributeNames || {};
|
||
|
var DOMPropertyNames = domPropertyConfig.DOMPropertyNames || {};
|
||
|
var DOMMutationMethods = domPropertyConfig.DOMMutationMethods || {};
|
||
|
|
||
|
if (domPropertyConfig.isCustomAttribute) {
|
||
|
DOMProperty._isCustomAttributeFunctions.push(domPropertyConfig.isCustomAttribute);
|
||
|
}
|
||
|
|
||
|
for (var propName in Properties) {
|
||
|
!!DOMProperty.properties.hasOwnProperty(propName) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'injectDOMPropertyConfig(...): You\'re trying to inject DOM property \'%s\' which has already been injected. You may be accidentally injecting the same DOM property config twice, or you may be injecting two configs that have conflicting property names.', propName) : _prodInvariant('48', propName) : void 0;
|
||
|
|
||
|
var lowerCased = propName.toLowerCase();
|
||
|
var propConfig = Properties[propName];
|
||
|
|
||
|
var propertyInfo = {
|
||
|
attributeName: lowerCased,
|
||
|
attributeNamespace: null,
|
||
|
propertyName: propName,
|
||
|
mutationMethod: null,
|
||
|
|
||
|
mustUseProperty: checkMask(propConfig, Injection.MUST_USE_PROPERTY),
|
||
|
hasBooleanValue: checkMask(propConfig, Injection.HAS_BOOLEAN_VALUE),
|
||
|
hasNumericValue: checkMask(propConfig, Injection.HAS_NUMERIC_VALUE),
|
||
|
hasPositiveNumericValue: checkMask(propConfig, Injection.HAS_POSITIVE_NUMERIC_VALUE),
|
||
|
hasOverloadedBooleanValue: checkMask(propConfig, Injection.HAS_OVERLOADED_BOOLEAN_VALUE)
|
||
|
};
|
||
|
!(propertyInfo.hasBooleanValue + propertyInfo.hasNumericValue + propertyInfo.hasOverloadedBooleanValue <= 1) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'DOMProperty: Value can be one of boolean, overloaded boolean, or numeric value, but not a combination: %s', propName) : _prodInvariant('50', propName) : void 0;
|
||
|
|
||
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
DOMProperty.getPossibleStandardName[lowerCased] = propName;
|
||
|
}
|
||
|
|
||
|
if (DOMAttributeNames.hasOwnProperty(propName)) {
|
||
|
var attributeName = DOMAttributeNames[propName];
|
||
|
propertyInfo.attributeName = attributeName;
|
||
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
DOMProperty.getPossibleStandardName[attributeName] = propName;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (DOMAttributeNamespaces.hasOwnProperty(propName)) {
|
||
|
propertyInfo.attributeNamespace = DOMAttributeNamespaces[propName];
|
||
|
}
|
||
|
|
||
|
if (DOMPropertyNames.hasOwnProperty(propName)) {
|
||
|
propertyInfo.propertyName = DOMPropertyNames[propName];
|
||
|
}
|
||
|
|
||
|
if (DOMMutationMethods.hasOwnProperty(propName)) {
|
||
|
propertyInfo.mutationMethod = DOMMutationMethods[propName];
|
||
|
}
|
||
|
|
||
|
DOMProperty.properties[propName] = propertyInfo;
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/* eslint-disable max-len */
|
||
|
var ATTRIBUTE_NAME_START_CHAR = ':A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD';
|
||
|
/* eslint-enable max-len */
|
||
|
|
||
|
/**
|
||
|
* DOMProperty exports lookup objects that can be used like functions:
|
||
|
*
|
||
|
* > DOMProperty.isValid['id']
|
||
|
* true
|
||
|
* > DOMProperty.isValid['foobar']
|
||
|
* undefined
|
||
|
*
|
||
|
* Although this may be confusing, it performs better in general.
|
||
|
*
|
||
|
* @see http://jsperf.com/key-exists
|
||
|
* @see http://jsperf.com/key-missing
|
||
|
*/
|
||
|
var DOMProperty = {
|
||
|
ID_ATTRIBUTE_NAME: 'data-reactid',
|
||
|
ROOT_ATTRIBUTE_NAME: 'data-reactroot',
|
||
|
|
||
|
ATTRIBUTE_NAME_START_CHAR: ATTRIBUTE_NAME_START_CHAR,
|
||
|
ATTRIBUTE_NAME_CHAR: ATTRIBUTE_NAME_START_CHAR + '\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040',
|
||
|
|
||
|
/**
|
||
|
* Map from property "standard name" to an object with info about how to set
|
||
|
* the property in the DOM. Each object contains:
|
||
|
*
|
||
|
* attributeName:
|
||
|
* Used when rendering markup or with `*Attribute()`.
|
||
|
* attributeNamespace
|
||
|
* propertyName:
|
||
|
* Used on DOM node instances. (This includes properties that mutate due to
|
||
|
* external factors.)
|
||
|
* mutationMethod:
|
||
|
* If non-null, used instead of the property or `setAttribute()` after
|
||
|
* initial render.
|
||
|
* mustUseProperty:
|
||
|
* Whether the property must be accessed and mutated as an object property.
|
||
|
* hasBooleanValue:
|
||
|
* Whether the property should be removed when set to a falsey value.
|
||
|
* hasNumericValue:
|
||
|
* Whether the property must be numeric or parse as a numeric and should be
|
||
|
* removed when set to a falsey value.
|
||
|
* hasPositiveNumericValue:
|
||
|
* Whether the property must be positive numeric or parse as a positive
|
||
|
* numeric and should be removed when set to a falsey value.
|
||
|
* hasOverloadedBooleanValue:
|
||
|
* Whether the property can be used as a flag as well as with a value.
|
||
|
* Removed when strictly equal to false; present without a value when
|
||
|
* strictly equal to true; present with a value otherwise.
|
||
|
*/
|
||
|
properties: {},
|
||
|
|
||
|
/**
|
||
|
* Mapping from lowercase property names to the properly cased version, used
|
||
|
* to warn in the case of missing properties. Available only in __DEV__.
|
||
|
*
|
||
|
* autofocus is predefined, because adding it to the property whitelist
|
||
|
* causes unintended side effects.
|
||
|
*
|
||
|
* @type {Object}
|
||
|
*/
|
||
|
getPossibleStandardName: process.env.NODE_ENV !== 'production' ? { autofocus: 'autoFocus' } : null,
|
||
|
|
||
|
/**
|
||
|
* All of the isCustomAttribute() functions that have been injected.
|
||
|
*/
|
||
|
_isCustomAttributeFunctions: [],
|
||
|
|
||
|
/**
|
||
|
* Checks whether a property name is a custom attribute.
|
||
|
* @method
|
||
|
*/
|
||
|
isCustomAttribute: function (attributeName) {
|
||
|
for (var i = 0; i < DOMProperty._isCustomAttributeFunctions.length; i++) {
|
||
|
var isCustomAttributeFn = DOMProperty._isCustomAttributeFunctions[i];
|
||
|
if (isCustomAttributeFn(attributeName)) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
},
|
||
|
|
||
|
injection: DOMPropertyInjection
|
||
|
};
|
||
|
|
||
|
module.exports = DOMProperty;
|