UNPKG

d2-ui

Version:
225 lines (203 loc) 7.88 kB
/** * Copyright 2013-2015, 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. * * @providesModule DOMPropertyOperations * @typechecks static-only */ 'use strict'; var DOMProperty = require('./DOMProperty'); var ReactPerf = require('./ReactPerf'); var quoteAttributeValueForBrowser = require('./quoteAttributeValueForBrowser'); var warning = require('fbjs/lib/warning'); // Simplified subset var VALID_ATTRIBUTE_NAME_REGEX = /^[a-zA-Z_][\w\.\-]*$/; var illegalAttributeNameCache = {}; var validatedAttributeNameCache = {}; 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) : undefined; return false; } function shouldIgnoreValue(propertyInfo, value) { return value == null || propertyInfo.hasBooleanValue && !value || propertyInfo.hasNumericValue && isNaN(value) || propertyInfo.hasPositiveNumericValue && value < 1 || propertyInfo.hasOverloadedBooleanValue && value === false; } if (process.env.NODE_ENV !== 'production') { var reactProps = { children: true, dangerouslySetInnerHTML: true, key: true, ref: true }; var warnedProperties = {}; var warnUnknownProperty = function (name) { if (reactProps.hasOwnProperty(name) && reactProps[name] || warnedProperties.hasOwnProperty(name) && warnedProperties[name]) { return; } warnedProperties[name] = true; var lowerCasedName = name.toLowerCase(); // data-* attributes should be lowercase; suggest the lowercase version var standardName = DOMProperty.isCustomAttribute(lowerCasedName) ? lowerCasedName : DOMProperty.getPossibleStandardName.hasOwnProperty(lowerCasedName) ? DOMProperty.getPossibleStandardName[lowerCasedName] : null; // For now, only warn when we have a suggested correction. This prevents // logging too much when using transferPropsTo. process.env.NODE_ENV !== 'production' ? warning(standardName == null, 'Unknown DOM property %s. Did you mean %s?', name, standardName) : undefined; }; } /** * Operations for dealing with DOM properties. */ var DOMPropertyOperations = { /** * Creates markup for the ID property. * * @param {string} id Unescaped ID. * @return {string} Markup string. */ createMarkupForID: function (id) { return DOMProperty.ID_ATTRIBUTE_NAME + '=' + quoteAttributeValueForBrowser(id); }, setAttributeForID: function (node, id) { node.setAttribute(DOMProperty.ID_ATTRIBUTE_NAME, id); }, /** * Creates markup for a property. * * @param {string} name * @param {*} value * @return {?string} Markup string, or null if the property was invalid. */ createMarkupForProperty: function (name, value) { var propertyInfo = DOMProperty.properties.hasOwnProperty(name) ? DOMProperty.properties[name] : null; if (propertyInfo) { 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); } else if (process.env.NODE_ENV !== 'production') { warnUnknownProperty(name); } return null; }, /** * Creates markup for a custom property. * * @param {string} name * @param {*} value * @return {string} Markup string, or empty string if the property was invalid. */ createMarkupForCustomAttribute: function (name, value) { if (!isAttributeNameSafe(name) || value == null) { return ''; } return name + '=' + quoteAttributeValueForBrowser(value); }, /** * Sets the value for a property on a node. * * @param {DOMElement} node * @param {string} name * @param {*} value */ setValueForProperty: function (node, name, value) { var propertyInfo = DOMProperty.properties.hasOwnProperty(name) ? DOMProperty.properties[name] : null; if (propertyInfo) { var mutationMethod = propertyInfo.mutationMethod; if (mutationMethod) { mutationMethod(node, value); } else if (shouldIgnoreValue(propertyInfo, value)) { this.deleteValueForProperty(node, name); } else if (propertyInfo.mustUseAttribute) { var attributeName = propertyInfo.attributeName; var namespace = propertyInfo.attributeNamespace; // `setAttribute` with objects becomes only `[object]` in IE8/9, // ('' + value) makes it output the correct toString()-value. if (namespace) { node.setAttributeNS(namespace, attributeName, '' + value); } else if (propertyInfo.hasBooleanValue || propertyInfo.hasOverloadedBooleanValue && value === true) { node.setAttribute(attributeName, ''); } else { node.setAttribute(attributeName, '' + value); } } else { var propName = propertyInfo.propertyName; // Must explicitly cast values for HAS_SIDE_EFFECTS-properties to the // property type before comparing; only `value` does and is string. if (!propertyInfo.hasSideEffects || '' + node[propName] !== '' + value) { // Contrary to `setAttribute`, object properties are properly // `toString`ed by IE8/9. node[propName] = value; } } } else if (DOMProperty.isCustomAttribute(name)) { DOMPropertyOperations.setValueForAttribute(node, name, value); } else if (process.env.NODE_ENV !== 'production') { warnUnknownProperty(name); } }, setValueForAttribute: function (node, name, value) { if (!isAttributeNameSafe(name)) { return; } if (value == null) { node.removeAttribute(name); } else { node.setAttribute(name, '' + value); } }, /** * Deletes the value for a property on a node. * * @param {DOMElement} node * @param {string} name */ 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.mustUseAttribute) { node.removeAttribute(propertyInfo.attributeName); } else { var propName = propertyInfo.propertyName; var defaultValue = DOMProperty.getDefaultValueForProperty(node.nodeName, propName); if (!propertyInfo.hasSideEffects || '' + node[propName] !== defaultValue) { node[propName] = defaultValue; } } } else if (DOMProperty.isCustomAttribute(name)) { node.removeAttribute(name); } else if (process.env.NODE_ENV !== 'production') { warnUnknownProperty(name); } } }; ReactPerf.measureMethods(DOMPropertyOperations, 'DOMPropertyOperations', { setValueForProperty: 'setValueForProperty', setValueForAttribute: 'setValueForAttribute', deleteValueForProperty: 'deleteValueForProperty' }); module.exports = DOMPropertyOperations;