UNPKG

react-addons

Version:

Simple packaging of react addons to avoid fiddly 'react/addons' npm module.

188 lines (163 loc) 7.28 kB
/** * 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 */ "use strict"; 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;