/**1* Copyright 2013-2015, Facebook, Inc.2* All rights reserved.3*4* This source code is licensed under the BSD-style license found in the5* LICENSE file in the root directory of this source tree. An additional grant6* of patent rights can be found in the PATENTS file in the same directory.7*8* @providesModule DOMProperty9* @typechecks static-only10*/1112/*jslint bitwise: true */1314'use strict';1516var invariant = require("./invariant");1718function checkMask(value, bitmask) {19return (value & bitmask) === bitmask;20}2122var DOMPropertyInjection = {23/**24* Mapping from normalized, camelcased property names to a configuration that25* specifies how the associated DOM property should be accessed or rendered.26*/27MUST_USE_ATTRIBUTE: 0x1,28MUST_USE_PROPERTY: 0x2,29HAS_SIDE_EFFECTS: 0x4,30HAS_BOOLEAN_VALUE: 0x8,31HAS_NUMERIC_VALUE: 0x10,32HAS_POSITIVE_NUMERIC_VALUE: 0x20 | 0x10,33HAS_OVERLOADED_BOOLEAN_VALUE: 0x40,3435/**36* Inject some specialized knowledge about the DOM. This takes a config object37* with the following properties:38*39* isCustomAttribute: function that given an attribute name will return true40* if it can be inserted into the DOM verbatim. Useful for data-* or aria-*41* attributes where it's impossible to enumerate all of the possible42* attribute names,43*44* Properties: object mapping DOM property name to one of the45* DOMPropertyInjection constants or null. If your attribute isn't in here,46* it won't get written to the DOM.47*48* DOMAttributeNames: object mapping React attribute name to the DOM49* attribute name. Attribute names not specified use the **lowercase**50* normalized name.51*52* DOMPropertyNames: similar to DOMAttributeNames but for DOM properties.53* Property names not specified use the normalized name.54*55* DOMMutationMethods: Properties that require special mutation methods. If56* `value` is undefined, the mutation method should unset the property.57*58* @param {object} domPropertyConfig the config as described above.59*/60injectDOMPropertyConfig: function(domPropertyConfig) {61var Properties = domPropertyConfig.Properties || {};62var DOMAttributeNames = domPropertyConfig.DOMAttributeNames || {};63var DOMPropertyNames = domPropertyConfig.DOMPropertyNames || {};64var DOMMutationMethods = domPropertyConfig.DOMMutationMethods || {};6566if (domPropertyConfig.isCustomAttribute) {67DOMProperty._isCustomAttributeFunctions.push(68domPropertyConfig.isCustomAttribute69);70}7172for (var propName in Properties) {73("production" !== process.env.NODE_ENV ? invariant(74!DOMProperty.isStandardName.hasOwnProperty(propName),75'injectDOMPropertyConfig(...): You\'re trying to inject DOM property ' +76'\'%s\' which has already been injected. You may be accidentally ' +77'injecting the same DOM property config twice, or you may be ' +78'injecting two configs that have conflicting property names.',79propName80) : invariant(!DOMProperty.isStandardName.hasOwnProperty(propName)));8182DOMProperty.isStandardName[propName] = true;8384var lowerCased = propName.toLowerCase();85DOMProperty.getPossibleStandardName[lowerCased] = propName;8687if (DOMAttributeNames.hasOwnProperty(propName)) {88var attributeName = DOMAttributeNames[propName];89DOMProperty.getPossibleStandardName[attributeName] = propName;90DOMProperty.getAttributeName[propName] = attributeName;91} else {92DOMProperty.getAttributeName[propName] = lowerCased;93}9495DOMProperty.getPropertyName[propName] =96DOMPropertyNames.hasOwnProperty(propName) ?97DOMPropertyNames[propName] :98propName;99100if (DOMMutationMethods.hasOwnProperty(propName)) {101DOMProperty.getMutationMethod[propName] = DOMMutationMethods[propName];102} else {103DOMProperty.getMutationMethod[propName] = null;104}105106var propConfig = Properties[propName];107DOMProperty.mustUseAttribute[propName] =108checkMask(propConfig, DOMPropertyInjection.MUST_USE_ATTRIBUTE);109DOMProperty.mustUseProperty[propName] =110checkMask(propConfig, DOMPropertyInjection.MUST_USE_PROPERTY);111DOMProperty.hasSideEffects[propName] =112checkMask(propConfig, DOMPropertyInjection.HAS_SIDE_EFFECTS);113DOMProperty.hasBooleanValue[propName] =114checkMask(propConfig, DOMPropertyInjection.HAS_BOOLEAN_VALUE);115DOMProperty.hasNumericValue[propName] =116checkMask(propConfig, DOMPropertyInjection.HAS_NUMERIC_VALUE);117DOMProperty.hasPositiveNumericValue[propName] =118checkMask(propConfig, DOMPropertyInjection.HAS_POSITIVE_NUMERIC_VALUE);119DOMProperty.hasOverloadedBooleanValue[propName] =120checkMask(propConfig, DOMPropertyInjection.HAS_OVERLOADED_BOOLEAN_VALUE);121122("production" !== process.env.NODE_ENV ? invariant(123!DOMProperty.mustUseAttribute[propName] ||124!DOMProperty.mustUseProperty[propName],125'DOMProperty: Cannot require using both attribute and property: %s',126propName127) : invariant(!DOMProperty.mustUseAttribute[propName] ||128!DOMProperty.mustUseProperty[propName]));129("production" !== process.env.NODE_ENV ? invariant(130DOMProperty.mustUseProperty[propName] ||131!DOMProperty.hasSideEffects[propName],132'DOMProperty: Properties that have side effects must use property: %s',133propName134) : invariant(DOMProperty.mustUseProperty[propName] ||135!DOMProperty.hasSideEffects[propName]));136("production" !== process.env.NODE_ENV ? invariant(137!!DOMProperty.hasBooleanValue[propName] +138!!DOMProperty.hasNumericValue[propName] +139!!DOMProperty.hasOverloadedBooleanValue[propName] <= 1,140'DOMProperty: Value can be one of boolean, overloaded boolean, or ' +141'numeric value, but not a combination: %s',142propName143) : invariant(!!DOMProperty.hasBooleanValue[propName] +144!!DOMProperty.hasNumericValue[propName] +145!!DOMProperty.hasOverloadedBooleanValue[propName] <= 1));146}147}148};149var defaultValueCache = {};150151/**152* DOMProperty exports lookup objects that can be used like functions:153*154* > DOMProperty.isValid['id']155* true156* > DOMProperty.isValid['foobar']157* undefined158*159* Although this may be confusing, it performs better in general.160*161* @see http://jsperf.com/key-exists162* @see http://jsperf.com/key-missing163*/164var DOMProperty = {165166ID_ATTRIBUTE_NAME: 'data-reactid',167168/**169* Checks whether a property name is a standard property.170* @type {Object}171*/172isStandardName: {},173174/**175* Mapping from lowercase property names to the properly cased version, used176* to warn in the case of missing properties.177* @type {Object}178*/179getPossibleStandardName: {},180181/**182* Mapping from normalized names to attribute names that differ. Attribute183* names are used when rendering markup or with `*Attribute()`.184* @type {Object}185*/186getAttributeName: {},187188/**189* Mapping from normalized names to properties on DOM node instances.190* (This includes properties that mutate due to external factors.)191* @type {Object}192*/193getPropertyName: {},194195/**196* Mapping from normalized names to mutation methods. This will only exist if197* mutation cannot be set simply by the property or `setAttribute()`.198* @type {Object}199*/200getMutationMethod: {},201202/**203* Whether the property must be accessed and mutated as an object property.204* @type {Object}205*/206mustUseAttribute: {},207208/**209* Whether the property must be accessed and mutated using `*Attribute()`.210* (This includes anything that fails `<propName> in <element>`.)211* @type {Object}212*/213mustUseProperty: {},214215/**216* Whether or not setting a value causes side effects such as triggering217* resources to be loaded or text selection changes. We must ensure that218* the value is only set if it has changed.219* @type {Object}220*/221hasSideEffects: {},222223/**224* Whether the property should be removed when set to a falsey value.225* @type {Object}226*/227hasBooleanValue: {},228229/**230* Whether the property must be numeric or parse as a231* numeric and should be removed when set to a falsey value.232* @type {Object}233*/234hasNumericValue: {},235236/**237* Whether the property must be positive numeric or parse as a positive238* numeric and should be removed when set to a falsey value.239* @type {Object}240*/241hasPositiveNumericValue: {},242243/**244* Whether the property can be used as a flag as well as with a value. Removed245* when strictly equal to false; present without a value when strictly equal246* to true; present with a value otherwise.247* @type {Object}248*/249hasOverloadedBooleanValue: {},250251/**252* All of the isCustomAttribute() functions that have been injected.253*/254_isCustomAttributeFunctions: [],255256/**257* Checks whether a property name is a custom attribute.258* @method259*/260isCustomAttribute: function(attributeName) {261for (var i = 0; i < DOMProperty._isCustomAttributeFunctions.length; i++) {262var isCustomAttributeFn = DOMProperty._isCustomAttributeFunctions[i];263if (isCustomAttributeFn(attributeName)) {264return true;265}266}267return false;268},269270/**271* Returns the default property value for a DOM property (i.e., not an272* attribute). Most default values are '' or false, but not all. Worse yet,273* some (in particular, `type`) vary depending on the type of element.274*275* TODO: Is it better to grab all the possible properties when creating an276* element to avoid having to create the same element twice?277*/278getDefaultValueForProperty: function(nodeName, prop) {279var nodeDefaults = defaultValueCache[nodeName];280var testElement;281if (!nodeDefaults) {282defaultValueCache[nodeName] = nodeDefaults = {};283}284if (!(prop in nodeDefaults)) {285testElement = document.createElement(nodeName);286nodeDefaults[prop] = testElement[prop];287}288return nodeDefaults[prop];289},290291injection: DOMPropertyInjection292};293294module.exports = DOMProperty;295296297