react-addons
Version:
Simple packaging of react addons to avoid fiddly 'react/addons' npm module.
188 lines (163 loc) • 7.28 kB
JavaScript
/**
* Copyright 2013-2014 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 Danger
* @typechecks static-only
*/
/*jslint evil: true, sub: true */
;
var ExecutionEnvironment = require("./ExecutionEnvironment");
var createNodesFromMarkup = require("./createNodesFromMarkup");
var emptyFunction = require("./emptyFunction");
var getMarkupWrap = require("./getMarkupWrap");
var invariant = require("./invariant");
var OPEN_TAG_NAME_EXP = /^(<[^ \/>]+)/;
var RESULT_INDEX_ATTR = 'data-danger-index';
/**
* 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
* spaces surrounding the opening tag and there is at least one attribute).
*
* @param {string} markup String of markup.
* @return {string} Node name of the supplied markup.
* @see http://jsperf.com/extract-nodename
*/
function getNodeName(markup) {
return markup.substring(1, markup.indexOf(' '));
}
var Danger = {
/**
* Renders markup into an array of nodes. The markup is expected to render
* into a list of root nodes. Also, the length of `resultList` and
* `markupList` should be the same.
*
* @param {array<string>} markupList List of markup strings to render.
* @return {array<DOMElement>} List of rendered nodes.
* @internal
*/
dangerouslyRenderMarkup: function(markupList) {
("production" !== process.env.NODE_ENV ? invariant(
ExecutionEnvironment.canUseDOM,
'dangerouslyRenderMarkup(...): Cannot render markup in a Worker ' +
'thread. This is likely a bug in the framework. Please report ' +
'immediately.'
) : invariant(ExecutionEnvironment.canUseDOM));
var nodeName;
var markupByNodeName = {};
// Group markup by `nodeName` if a wrap is necessary, else by '*'.
for (var i = 0; i < markupList.length; i++) {
("production" !== process.env.NODE_ENV ? invariant(
markupList[i],
'dangerouslyRenderMarkup(...): Missing markup.'
) : invariant(markupList[i]));
nodeName = getNodeName(markupList[i]);
nodeName = getMarkupWrap(nodeName) ? nodeName : '*';
markupByNodeName[nodeName] = markupByNodeName[nodeName] || [];
markupByNodeName[nodeName][i] = markupList[i];
}
var resultList = [];
var resultListAssignmentCount = 0;
for (nodeName in markupByNodeName) {
if (!markupByNodeName.hasOwnProperty(nodeName)) {
continue;
}
var markupListByNodeName = markupByNodeName[nodeName];
// This for-in loop skips the holes of the sparse array. The order of
// iteration should follow the order of assignment, which happens to match
// numerical index order, but we don't rely on that.
for (var resultIndex in markupListByNodeName) {
if (markupListByNodeName.hasOwnProperty(resultIndex)) {
var markup = markupListByNodeName[resultIndex];
// Push the requested markup with an additional RESULT_INDEX_ATTR
// attribute. If the markup does not start with a < character, it
// will be discarded below (with an appropriate console.error).
markupListByNodeName[resultIndex] = markup.replace(
OPEN_TAG_NAME_EXP,
// This index will be parsed back out below.
'$1 ' + RESULT_INDEX_ATTR + '="' + resultIndex + '" '
);
}
}
// Render each group of markup with similar wrapping `nodeName`.
var renderNodes = createNodesFromMarkup(
markupListByNodeName.join(''),
emptyFunction // Do nothing special with <script> tags.
);
for (i = 0; i < renderNodes.length; ++i) {
var renderNode = renderNodes[i];
if (renderNode.hasAttribute &&
renderNode.hasAttribute(RESULT_INDEX_ATTR)) {
resultIndex = +renderNode.getAttribute(RESULT_INDEX_ATTR);
renderNode.removeAttribute(RESULT_INDEX_ATTR);
("production" !== process.env.NODE_ENV ? invariant(
!resultList.hasOwnProperty(resultIndex),
'Danger: Assigning to an already-occupied result index.'
) : invariant(!resultList.hasOwnProperty(resultIndex)));
resultList[resultIndex] = renderNode;
// This should match resultList.length and markupList.length when
// we're done.
resultListAssignmentCount += 1;
} else if ("production" !== process.env.NODE_ENV) {
console.error(
"Danger: Discarding unexpected node:",
renderNode
);
}
}
}
// Although resultList was populated out of order, it should now be a dense
// array.
("production" !== process.env.NODE_ENV ? invariant(
resultListAssignmentCount === resultList.length,
'Danger: Did not assign to every index of resultList.'
) : invariant(resultListAssignmentCount === resultList.length));
("production" !== process.env.NODE_ENV ? invariant(
resultList.length === markupList.length,
'Danger: Expected markup to render %s nodes, but rendered %s.',
markupList.length,
resultList.length
) : invariant(resultList.length === markupList.length));
return resultList;
},
/**
* Replaces a node with a string of markup at its current position within its
* parent. The markup must render into a single root node.
*
* @param {DOMElement} oldChild Child node to replace.
* @param {string} markup Markup to render in place of the child node.
* @internal
*/
dangerouslyReplaceNodeWithMarkup: function(oldChild, markup) {
("production" !== process.env.NODE_ENV ? invariant(
ExecutionEnvironment.canUseDOM,
'dangerouslyReplaceNodeWithMarkup(...): Cannot render markup in a ' +
'worker thread. This is likely a bug in the framework. Please report ' +
'immediately.'
) : invariant(ExecutionEnvironment.canUseDOM));
("production" !== process.env.NODE_ENV ? invariant(markup, 'dangerouslyReplaceNodeWithMarkup(...): Missing markup.') : invariant(markup));
("production" !== process.env.NODE_ENV ? invariant(
oldChild.tagName.toLowerCase() !== 'html',
'dangerouslyReplaceNodeWithMarkup(...): Cannot replace markup of the ' +
'<html> node. This is because browser quirks make this unreliable ' +
'and/or slow. If you want to render to the root you must use ' +
'server rendering. See renderComponentToString().'
) : invariant(oldChild.tagName.toLowerCase() !== 'html'));
var newChild = createNodesFromMarkup(markup, emptyFunction)[0];
oldChild.parentNode.replaceChild(newChild, oldChild);
}
};
module.exports = Danger;