,
+ container
+ );
expect(setInnerHTML.callCount).toBe(callCountOnMount + 1);
});
});
diff --git a/src/dom/Danger.js b/src/dom/Danger.js
index 2c18695a2b..02615ea0e3 100644
--- a/src/dom/Danger.js
+++ b/src/dom/Danger.js
@@ -23,65 +23,13 @@
var ExecutionEnvironment = require('ExecutionEnvironment');
+var createNodesFromMarkup = require('createNodesFromMarkup');
+var emptyFunction = require('emptyFunction');
+var getMarkupWrap = require('getMarkupWrap');
var invariant = require('invariant');
/**
- * Dummy container used to render all markup.
- */
-var dummyNode = ExecutionEnvironment.canUseDOM ?
- document.createElement('div') :
- null;
-
-/**
- * Some browsers cannot use `innerHTML` to render certain elements standalone,
- * so we wrap them, render the wrapped nodes, then extract the desired node.
- */
-var markupWrap = {
- 'option': [1, ''],
- 'legend': [1, ''],
- 'area': [1, ''],
- 'param': [1, ''],
- 'thead': [1, '
', '
'],
- 'tr': [2, '
', '
'],
- 'col': [2, '
', '
'],
- 'td': [3, '
', '
']
-};
-markupWrap['optgroup'] = markupWrap['option'];
-markupWrap['tbody'] = markupWrap['thead'];
-markupWrap['tfoot'] = markupWrap['thead'];
-markupWrap['colgroup'] = markupWrap['thead'];
-markupWrap['caption'] = markupWrap['thead'];
-markupWrap['th'] = markupWrap['td'];
-
-/**
- * In IE8, certain elements cannot render alone, so wrap all elements.
- */
-var defaultWrap = [1, '?
', '
'];
-
-/**
- * Feature detection, remove wraps that are unnecessary for the current browser.
- */
-if (dummyNode) {
- for (var nodeName in markupWrap) {
- if (!markupWrap.hasOwnProperty(nodeName)) {
- continue;
- }
- dummyNode.innerHTML = '<' + nodeName + '>' + nodeName + '>';
- if (dummyNode.firstChild) {
- markupWrap[nodeName] = null;
- }
- }
- dummyNode.innerHTML = '';
- if (dummyNode.firstChild) {
- defaultWrap = null;
- }
-}
-
-/**
- * Extracts the `nodeName` from a string of markup. This does not require a
- * regular expression match because we make assumptions about React-generated
- * markup (i.e. there are no spaces surrounding the opening tag and there is at
- * least an ID attribute).
+ * Extracts the `nodeName` from a string of markup.
*
* NOTE: Extracting the `nodeName` does not require a regular expression match
* because we make assumptions about React-generated markup (i.e. there are no
@@ -95,31 +43,6 @@ function getNodeName(markup) {
return markup.substring(1, markup.indexOf(' '));
}
-/**
- * Renders markup into nodes. The returned HTMLCollection is live and should be
- * used immediately (or at least before the next invocation to `renderMarkup`).
- *
- * @param {string} markup Markup for one or more nodes with the same `nodeName`.
- * @param {?string} nodeName Optional, the lowercase node name of the markup.
- * @return {*} An HTMLCollection.
- */
-function renderMarkup(markup, nodeName) {
- nodeName = nodeName || getNodeName(markup);
- var node = dummyNode;
- var wrap = markupWrap[nodeName] || defaultWrap;
- if (wrap) {
- node.innerHTML = wrap[1] + markup + wrap[2];
-
- var wrapDepth = wrap[0];
- while (wrapDepth--) {
- node = node.lastChild;
- }
- } else {
- node.innerHTML = markup;
- }
- return node.childNodes;
-}
-
var Danger = {
/**
@@ -147,31 +70,39 @@ var Danger = {
'dangerouslyRenderMarkup(...): Missing markup.'
);
nodeName = getNodeName(markupList[i]);
- nodeName = markupWrap[nodeName] ? nodeName : '*';
+ nodeName = getMarkupWrap(nodeName) ? nodeName : '*';
markupByNodeName[nodeName] = markupByNodeName[nodeName] || [];
markupByNodeName[nodeName][i] = markupList[i];
}
- var renderedMarkup = [];
+ var resultList = [];
for (nodeName in markupByNodeName) {
if (!markupByNodeName.hasOwnProperty(nodeName)) {
continue;
}
var markupListByNodeName = markupByNodeName[nodeName];
var markup = markupListByNodeName.join('');
- // Render each group of markup.
- var childNode = renderMarkup(markup, nodeName)[0];
+ // Render each group of markup with similar `nodeName`.
+ var renderNodes = createNodesFromMarkup(markup, emptyFunction);
// Restore the initial ordering.
- for (var j = 0; j < markupListByNodeName.length; j++) {
+ var renderIndex = 0;
+ var resultIndex = 0;
+ while (resultIndex < markupListByNodeName.length) {
// `markupListByNodeName` may be a sparse array.
- if (markupListByNodeName[j]) {
- invariant(childNode, 'dangerouslyRenderMarkup(...): Missing node.');
- renderedMarkup[j] = childNode;
- childNode = childNode.nextSibling;
+ if (markupListByNodeName[resultIndex]) {
+ invariant(
+ resultList[resultIndex] = renderNodes[renderIndex],
+ 'dangerouslyRenderMarkup(...): Missing node.'
+ );
+ renderIndex++;
}
+ resultIndex++;
}
- invariant(!childNode, 'dangerouslyRenderMarkupO(...): Unexpected nodes.');
+ invariant(
+ renderIndex === renderNodes.length,
+ 'dangerouslyRenderMarkupO(...): Unexpected nodes.'
+ );
}
- return renderedMarkup;
+ return resultList;
},
/**
@@ -190,7 +121,7 @@ var Danger = {
'immediately.'
);
invariant(markup, 'dangerouslyReplaceNodeWithMarkup(...): Missing markup.');
- var newChild = renderMarkup(markup)[0];
+ var newChild = createNodesFromMarkup(markup, emptyFunction)[0];
oldChild.parentNode.replaceChild(newChild, oldChild);
}
diff --git a/src/vendor/core/createArrayFrom.js b/src/vendor/core/createArrayFrom.js
new file mode 100644
index 0000000000..1924c0c241
--- /dev/null
+++ b/src/vendor/core/createArrayFrom.js
@@ -0,0 +1,56 @@
+/**
+ * Copyright 2013 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @providesModule createArrayFrom
+ * @typechecks
+ */
+
+var hasArrayNature = require('hasArrayNature');
+
+/**
+ * Ensure that the argument is an array by wrapping it in an array if it is not.
+ * Creates a copy of the argument if it is already an array.
+ *
+ * This is mostly useful idiomatically:
+ *
+ * var createArrayFrom = require('createArrayFrom');
+ *
+ * function takesOneOrMoreThings(things) {
+ * things = createArrayFrom(things);
+ * ...
+ * }
+ *
+ * This allows you to treat `things' as an array, but accept scalars in the API.
+ *
+ * This is also good for converting certain pseudo-arrays, like `arguments` or
+ * HTMLCollections, into arrays.
+ *
+ * @param {*} obj
+ * @return {array}
+ */
+function createArrayFrom(obj) {
+ if (!hasArrayNature(obj)) {
+ return [obj];
+ }
+ if (obj.item) {
+ // IE does not support Array#slice on HTMLCollections
+ var l = obj.length, ret = new Array(l);
+ while (l--) { ret[l] = obj[l]; }
+ return ret;
+ }
+ return Array.prototype.slice.call(obj);
+}
+
+module.exports = createArrayFrom;
diff --git a/src/vendor/core/createNodesFromMarkup.js b/src/vendor/core/createNodesFromMarkup.js
new file mode 100644
index 0000000000..6ef538fa40
--- /dev/null
+++ b/src/vendor/core/createNodesFromMarkup.js
@@ -0,0 +1,89 @@
+/**
+ * Copyright 2013 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @providesModule createNodesFromMarkup
+ * @typechecks
+ */
+
+/*jslint evil: true, sub: true */
+
+var createArrayFrom = require('createArrayFrom');
+var getMarkupWrap = require('getMarkupWrap');
+var invariant = require('invariant');
+
+/**
+ * Dummy container used to render all markup.
+ */
+var dummyNode = document.createElement('div');
+
+/**
+ * Pattern used by `getNodeName`.
+ */
+var nodeNamePattern = /^\s*<(\w+)/;
+
+/**
+ * Extracts the `nodeName` of the first element in a string of markup.
+ *
+ * @param {string} markup String of markup.
+ * @return {?string} Node name of the supplied markup.
+ */
+function getNodeName(markup) {
+ var nodeNameMatch = markup.match(nodeNamePattern);
+ return nodeNameMatch && nodeNameMatch[1].toLowerCase();
+}
+
+/**
+ * Creates an array containing the nodes rendered from the supplied markup. The
+ * optionally supplied `handleScript` function will be invoked once for each
+ *