3
*
4
* This source code is licensed under the MIT license found in the
5
* LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
*/
9
10
import {
9
- ID_ATTRIBUTE_NAME,
10
- ROOT_ATTRIBUTE_NAME,
11
getPropertyInfo,
12
- shouldSetAttribute,
12
+ shouldSkipAttribute,
13
+ shouldTreatAttributeValueAsNull,
14
isAttributeNameSafe,
15
} from '../shared/DOMProperty';
16
16
-// shouldIgnoreValue() is currently duplicated in DOMMarkupOperations.
17
-// TODO: Find a better place for this.
18
-function shouldIgnoreValue(propertyInfo, value) {
19
- return (
20
- value == null ||
21
- (propertyInfo.hasBooleanValue && !value) ||
22
- (propertyInfo.hasNumericValue && isNaN(value)) ||
23
- (propertyInfo.hasPositiveNumericValue && value < 1) ||
24
- (propertyInfo.hasOverloadedBooleanValue && value === false)
25
- );
26
-}
27
-
28
-/**
29
- * Operations for dealing with DOM properties.
30
- */
31
-
32
-export function setAttributeForID(node, id) {
33
- node.setAttribute(ID_ATTRIBUTE_NAME, id);
34
-}
35
-
36
-export function setAttributeForRoot(node) {
37
- node.setAttribute(ROOT_ATTRIBUTE_NAME, '');
38
-}
39
-
17
/**
18
* Get the value for a property on a node. Only used in DEV for SSR validation.
19
* The "expected" argument is used as a hint of what the expected value is.
20
* Some properties have multiple equivalent values.
21
*/
45
-export function getValueForProperty(node, name, expected) {
22
+export function getValueForProperty(
23
+ node: Element,
24
+ name: string,
25
+ expected: mixed,
26
+): mixed {
27
if (__DEV__) {
28
const propertyInfo = getPropertyInfo(name);
29
if (propertyInfo) {
30
if (propertyInfo.mustUseProperty) {
50
- return node[propertyInfo.propertyName];
31
+ const {propertyName} = propertyInfo;
32
+ return (node: any)[propertyName];
33
} else {
34
const attributeName = propertyInfo.attributeName;
35
41
if (value === '') {
42
return true;
43
}
62
- if (shouldIgnoreValue(propertyInfo, expected)) {
44
+ if (shouldTreatAttributeValueAsNull(name, expected, false)) {
45
return value;
46
}
65
- if (value === '' + expected) {
47
+ if (value === '' + (expected: any)) {
48
return expected;
49
}
50
return value;
51
}
52
} else if (node.hasAttribute(attributeName)) {
71
- if (shouldIgnoreValue(propertyInfo, expected)) {
53
+ if (shouldTreatAttributeValueAsNull(name, expected, false)) {
54
// We had an attribute but shouldn't have had one, so read it
55
// for the error message.
56
return node.getAttribute(attributeName);
67
stringValue = node.getAttribute(attributeName);
68
}
69
88
- if (shouldIgnoreValue(propertyInfo, expected)) {
70
+ if (shouldTreatAttributeValueAsNull(name, expected, false)) {
71
return stringValue === null ? expected : stringValue;
90
- } else if (stringValue === '' + expected) {
72
+ } else if (stringValue === '' + (expected: any)) {
73
return expected;
74
} else {
75
return stringValue;
84
* The third argument is used as a hint of what the expected value is. Some
85
* attributes have multiple equivalent values.
86
*/
105
-export function getValueForAttribute(node, name, expected) {
87
+export function getValueForAttribute(
88
+ node: Element,
89
+ name: string,
90
+ expected: mixed,
91
+): mixed {
92
if (__DEV__) {
93
if (!isAttributeNameSafe(name)) {
94
return;
97
return expected === undefined ? undefined : null;
98
}
99
const value = node.getAttribute(name);
114
- if (value === '' + expected) {
100
+ if (value === '' + (expected: any)) {
101
return expected;
102
}
103
return value;
111
* @param {string} name
112
* @param {*} value
113
*/
128
-export function setValueForProperty(node, name, value) {
129
- const propertyInfo = getPropertyInfo(name);
130
-
131
- if (propertyInfo && shouldSetAttribute(name, value)) {
132
- if (shouldIgnoreValue(propertyInfo, value)) {
133
- deleteValueForProperty(node, name);
134
- return;
135
- } else if (propertyInfo.mustUseProperty) {
136
- // Contrary to `setAttribute`, object properties are properly
137
- // `toString`ed by IE8/9.
138
- node[propertyInfo.propertyName] = value;
139
- } else {
140
- const attributeName = propertyInfo.attributeName;
141
- const namespace = propertyInfo.attributeNamespace;
142
- // `setAttribute` with objects becomes only `[object]` in IE8/9,
143
- // ('' + value) makes it output the correct toString()-value.
144
- if (namespace) {
145
- node.setAttributeNS(namespace, attributeName, '' + value);
146
- } else if (
147
- propertyInfo.hasBooleanValue ||
148
- (propertyInfo.hasOverloadedBooleanValue && value === true)
149
- ) {
150
- node.setAttribute(attributeName, '');
114
+export function setValueForProperty(
115
+ node: Element,
116
+ name: string,
117
+ value: mixed,
118
+ isCustomComponentTag: boolean,
119
+) {
120
+ if (shouldSkipAttribute(name, isCustomComponentTag)) {
121
+ return;
122
+ }
123
+ const propertyInfo = isCustomComponentTag ? null : getPropertyInfo(name);
124
+ if (shouldTreatAttributeValueAsNull(name, value, isCustomComponentTag)) {
125
+ value = null;
126
+ }
127
+ // If the prop isn't in the special list, treat it as a simple attribute.
128
+ if (!propertyInfo) {
129
+ if (isAttributeNameSafe(name)) {
130
+ const attributeName = name;
131
+ if (value == null) {
132
+ node.removeAttribute(attributeName);
133
} else {
152
- node.setAttribute(attributeName, '' + value);
134
+ node.setAttribute(attributeName, '' + (value: any));
135
}
136
}
155
- } else {
156
- setValueForAttribute(
157
- node,
158
- name,
159
- shouldSetAttribute(name, value) ? value : null,
160
- );
137
return;
138
}
163
-}
164
-
165
-export function setValueForAttribute(node, name, value) {
166
- if (!isAttributeNameSafe(name)) {
139
+ const {
140
+ hasBooleanValue,
141
+ hasOverloadedBooleanValue,
142
+ mustUseProperty,
143
+ } = propertyInfo;
144
+ if (mustUseProperty) {
145
+ const {propertyName} = propertyInfo;
146
+ if (value === null) {
147
+ (node: any)[propertyName] = hasBooleanValue ? false : '';
148
+ } else {
149
+ // Contrary to `setAttribute`, object properties are properly
150
+ // `toString`ed by IE8/9.
151
+ (node: any)[propertyName] = value;
152
+ }
153
return;
154
}
169
- if (value == null) {
170
- node.removeAttribute(name);
155
+ // The rest are treated as attributes with special cases.
156
+ const {attributeName, attributeNamespace} = propertyInfo;
157
+ if (value === null) {
158
+ node.removeAttribute(attributeName);
159
} else {
172
- node.setAttribute(name, '' + value);
173
- }
174
-}
175
-
176
-/**
177
- * Deletes an attributes from a node.
178
- *
179
- * @param {DOMElement} node
180
- * @param {string} name
181
- */
182
-export function deleteValueForAttribute(node, name) {
183
- node.removeAttribute(name);
184
-}
185
-
186
-/**
187
- * Deletes the value for a property on a node.
188
- *
189
- * @param {DOMElement} node
190
- * @param {string} name
191
- */
192
-export function deleteValueForProperty(node, name) {
193
- const propertyInfo = getPropertyInfo(name);
194
- if (propertyInfo) {
195
- if (propertyInfo.mustUseProperty) {
196
- const propName = propertyInfo.propertyName;
197
- if (propertyInfo.hasBooleanValue) {
198
- node[propName] = false;
199
- } else {
200
- node[propName] = '';
201
- }
160
+ let attributeValue;
161
+ if (hasBooleanValue || (hasOverloadedBooleanValue && value === true)) {
162
+ attributeValue = '';
163
+ } else {
164
+ // `setAttribute` with objects becomes only `[object]` in IE8/9,
165
+ // ('' + value) makes it output the correct toString()-value.
166
+ attributeValue = '' + (value: any);
167
+ }
168
+ if (attributeNamespace) {
169
+ node.setAttributeNS(attributeNamespace, attributeName, attributeValue);
170
} else {
203
- node.removeAttribute(propertyInfo.attributeName);
171
+ node.setAttribute(attributeName, attributeValue);
172
}
205
- } else {
206
- node.removeAttribute(name);
173
}
174
}