DOMPropertyOperations Node property operations to add or remove node properties.
'use strict'; var DOMProperty = require ('./ DOMProperty'); var ReactDOMComponentTree = require('./ReactDOMComponentTree'); var ReactInstrumentation = require('./ReactInstrumentation'); // Boolean, numeric type is converted into string, the string is transcoded by html, and processed ["'&<>]; wrapped in quotation marks var quoteAttributeValueForBrowser = require('./quoteAttributeValueForBrowser'); var warning = require('fbjs/lib/warning'); // Used to verify attribute names, English letters a-zA-Z, ":", "-", numbers, some Unicode characters var VALID_ATTRIBUTE_NAME_REGEX = new RegExp('^[' + DOMProperty.ATTRIBUTE_NAME_START_CHAR + '][' + DOMProperty.ATTRIBUTE_NAME_CHAR + ']*$'); var illegalAttributeNameCache = {};// Cache the attribute name that failed to verify var validatedAttributeNameCache = {};// Cache the attribute name that has been validated successfully // Check attribute name, English letters a-zA-Z, ":", "-", numbers, some Unicode characters function isAttributeNameSafe(attributeName) { if (validatedAttributeNameCache.hasOwnProperty(attributeName)) { return true; } if (illegalAttributeNameCache.hasOwnProperty(attributeName)) { return false; } if (VALID_ATTRIBUTE_NAME_REGEX.test(attributeName)) { validatedAttributeNameCache[attributeName] = true; return true; } illegalAttributeNameCache[attributeName] = true; process.env.NODE_ENV !== 'production' ? warning(false, 'Invalid attribute name: `%s`', attributeName) : void 0; return false; } // Boolean truth check (no value is ignored), numeric check, positive number check for attribute value conventions function shouldIgnoreValue(propertyInfo, value) { return value == null || propertyInfo.hasBooleanValue && !value || propertyInfo.hasNumericValue && isNaN(value) || propertyInfo.hasPositiveNumericValue && value < 1 || propertyInfo.hasOverloadedBooleanValue && value === false; } var DOMPropertyOperations = { // Set the 'data-reactid' attribute and value of the node as a string; use when creating a node by splicing strings createMarkupForID: function (id) { return DOMProperty.ID_ATTRIBUTE_NAME + '=' + quoteAttributeValueForBrowser(id); }, // Set the 'data-reactid' property of the node; used when creating a node in the document.createElement method setAttributeForID: function (node, id) { node.setAttribute(DOMProperty.ID_ATTRIBUTE_NAME, id); }, // Set the 'data-reactroot' attribute and value of the node as a string; use when creating a node by splicing strings createMarkupForRoot: function () { return DOMProperty.ROOT_ATTRIBUTE_NAME + '=""'; }, // Set the 'data-reactroot' property of the node; use document.createElement to create a node setAttributeForRoot: function (node) { node.setAttribute(DOMProperty.ROOT_ATTRIBUTE_NAME, ''); }, // Extract specific attributes and their values, concatenate them into strings // DOMProperty.properties, DOMProperty.isCustomAttribute convention can set node properties // DOMProperty.properties is used to extract or ignore property values, such as properties.hasBooleanValue will ignore configuration items whose property value is no value // DOMProperty.isCustomAttribute verifies customizable properties, such as data- or aria-starting properties createMarkupForProperty: function (name, value) { var propertyInfo = DOMProperty.properties.hasOwnProperty(name) ? DOMProperty.properties[name] : null; if (propertyInfo) { // Some property values are ignored with the property value type agreed by propertyInfo. For example, the property.hasBooleanValue setting will ignore the value of the property value of no value. if (shouldIgnoreValue(propertyInfo, value)) { return ''; } var attributeName = propertyInfo.attributeName; if (propertyInfo.hasBooleanValue || propertyInfo.hasOverloadedBooleanValue && value === true) { return attributeName + '=""'; } return attributeName + '=' + quoteAttributeValueForBrowser(value); } else if (DOMProperty.isCustomAttribute(name)) { if (value == null) { return ''; } return name + '=' + quoteAttributeValueForBrowser(value); } return null; }, // Set custom properties in string form createMarkupForCustomAttribute: function (name, value) { if (!isAttributeNameSafe(name) || value == null) { return ''; } return name + '=' + quoteAttributeValueForBrowser(value); }, // Set node attributes, most often use the setAttribute or setAttributeNS method // Especially when propertyInfo.mutationMethod exists, use this method to set node properties // or when propertyInfo.mustUseProperty is true, use node[propertyInfo.propertyName]=value to set node properties // or through the attributes verified by DOMProperty.isCustomAttribute, remove or add node attributes according to the value value setValueForProperty: function (node, name, value) { var propertyInfo = DOMProperty.properties.hasOwnProperty(name) ? DOMProperty.properties[name] : null; if (propertyInfo) { // Add node attributes with the DOMMutationMethods configuration method of the node attribute plugin var mutationMethod = propertyInfo.mutationMethod; if (mutationMethod) { mutationMethod(node, value); // Some property values are ignored with the property value type agreed by propertyInfo. For example, the property.hasBooleanValue setting will ignore the value of the property value of no value. } else if (shouldIgnoreValue(propertyInfo, value)) { this.deleteValueForProperty(node, name); return; // When propertyInfo.mustUseProperty is true, do not use setAttribute method to set node attributes // Handle the compatibility problem of ie8, 9setAttribute method converting attribute value to string `[object]` } else if (propertyInfo.mustUseProperty) { node[propertyInfo.propertyName] = value; } else { var attributeName = propertyInfo.attributeName; var namespace = propertyInfo.attributeNamespace; // node.setAttributeNS(namespace,attr,value) adds attributes of a specific namespace if (namespace) { node.setAttributeNS(namespace, attributeName, '' + value); // Boolean type can be injected into the node with the property name } else if (propertyInfo.hasBooleanValue || propertyInfo.hasOverloadedBooleanValue && value === true) { node.setAttribute(attributeName, ''); } else { node.setAttribute(attributeName, '' + value); } } // For properties verified by DOMProperty.isCustomAttribute, remove or add properties according to the value } else if (DOMProperty.isCustomAttribute(name)) { DOMPropertyOperations.setValueForAttribute(node, name, value); return; } if (process.env.NODE_ENV !== 'production') { var payload = {}; payload[name] = value; ReactInstrumentation.debugTool.onHostOperation({ instanceID: ReactDOMComponentTree.getInstanceFromNode(node)._debugID, type: 'update attribute', payload: payload }); } }, // add or remove attributes based on value setValueForAttribute: function (node, name, value) { if (!isAttributeNameSafe(name)) { return; } if (value == null) { node.removeAttribute(name); } else { node.setAttribute(name, '' + value); } if (process.env.NODE_ENV !== 'production') { var payload = {}; payload[name] = value; ReactInstrumentation.debugTool.onHostOperation({ instanceID: ReactDOMComponentTree.getInstanceFromNode(node)._debugID, type: 'update attribute', payload: payload }); } }, // Remove the attribute name of the node node deleteValueForAttribute: function (node, name) { node.removeAttribute(name); if (process.env.NODE_ENV !== 'production') { ReactInstrumentation.debugTool.onHostOperation({ instanceID: ReactDOMComponentTree.getInstanceFromNode(node)._debugID, type: 'remove attribute', payload: name }); } }, // Remove node attributes, most often use the removeAttribute method // Especially when propertyInfo.mutationMethod exists, use this method to remove node properties // or when propertyInfo.mustUseProperty is true, use node[propertyInfo.propertyName] to remove node properties deleteValueForProperty: function (node, name) { var propertyInfo = DOMProperty.properties.hasOwnProperty(name) ? DOMProperty.properties[name] : null; if (propertyInfo) { var mutationMethod = propertyInfo.mutationMethod; if (mutationMethod) { mutationMethod(node, undefined); } else if (propertyInfo.mustUseProperty) { var propName = propertyInfo.propertyName; if (propertyInfo.hasBooleanValue) { node[propName] = false; } else { node[propName] = ''; } } else { node.removeAttribute(propertyInfo.attributeName); } } else if (DOMProperty.isCustomAttribute(name)) { node.removeAttribute(name); } if (process.env.NODE_ENV !== 'production') { ReactInstrumentation.debugTool.onHostOperation({ instanceID: ReactDOMComponentTree.getInstanceFromNode(node)._debugID, type: 'remove attribute', payload: name }); } } }; module.exports = DOMPropertyOperations;