diff --git a/docs/docs/ref-03-component-specs.md b/docs/docs/ref-03-component-specs.md index 0a4a63c825b..393faea3d67 100644 --- a/docs/docs/ref-03-component-specs.md +++ b/docs/docs/ref-03-component-specs.md @@ -31,7 +31,7 @@ The `render()` function should be *pure*, meaning that it does not modify compon object getInitialState() ``` -Invoked once when the component is mounted. The return value will be used as the initial value of `this.state`. +Invoked once before the component is mounted. The return value will be used as the initial value of `this.state`. ### getDefaultProps diff --git a/src/core/__tests__/ReactRenderDocument-test.js b/src/core/__tests__/ReactRenderDocument-test.js index dd5250811ca..2409e74ab7e 100644 --- a/src/core/__tests__/ReactRenderDocument-test.js +++ b/src/core/__tests__/ReactRenderDocument-test.js @@ -62,7 +62,7 @@ describe('rendering React components at document', function() { ReactMount.allowFullPageRender = true; var component = React.renderComponent(, testDocument); - expect(testDocument.body.innerHTML).toBe(' Hello world '); + expect(testDocument.body.innerHTML).toBe('Hello world'); var componentID = ReactMount.getReactRootID(testDocument); expect(componentID).toBe(component._rootNodeID); @@ -91,7 +91,7 @@ describe('rendering React components at document', function() { ReactMount.allowFullPageRender = true; React.renderComponent(, testDocument); - expect(testDocument.body.innerHTML).toBe(' Hello world '); + expect(testDocument.body.innerHTML).toBe('Hello world'); var unmounted = React.unmountComponentAtNode(testDocument); expect(unmounted).toBe(true); @@ -152,12 +152,12 @@ describe('rendering React components at document', function() { ReactMount.allowFullPageRender = true; var component = React.renderComponent(, testDocument); - expect(testDocument.body.innerHTML).toBe(' Hello world '); + expect(testDocument.body.innerHTML).toBe('Hello world'); // Reactive update via state transition component.toggle(); - expect(testDocument.body.innerHTML).toBe(' Goodbye world '); + expect(testDocument.body.innerHTML).toBe('Goodbye world'); }); @@ -200,12 +200,12 @@ describe('rendering React components at document', function() { ReactMount.allowFullPageRender = true; React.renderComponent(, testDocument); - expect(testDocument.body.innerHTML).toBe(' Hello world '); + expect(testDocument.body.innerHTML).toBe('Hello world'); // Reactive update React.renderComponent(, testDocument); - expect(testDocument.body.innerHTML).toBe(' Goodbye world '); + expect(testDocument.body.innerHTML).toBe('Goodbye world'); }); diff --git a/src/vendor/core/dom/getDocumentScrollElement.js b/src/vendor/core/dom/getDocumentScrollElement.js deleted file mode 100644 index ec289c3ba6d..00000000000 --- a/src/vendor/core/dom/getDocumentScrollElement.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @providesModule getDocumentScrollElement - * @typechecks - */ - -"use strict"; - -// TODO: Replace this with a UserAgent module. -var isWebkit = navigator.userAgent.indexOf('AppleWebKit') > -1; - -/** - * Gets the element with the document scroll properties such as `scrollLeft` and - * `scrollHeight`. This may differ across different browsers. - * - * NOTE: The return value can be null if the DOM is not yet ready. - * - * @param {?DOMDocument} doc Defaults to current document. - * @return {?DOMElement} - */ -function getDocumentScrollElement(doc) { - doc = doc || document; - return !isWebkit && doc.compatMode === 'CSS1Compat' ? - doc.documentElement : - doc.body; -} - -module.exports = getDocumentScrollElement; diff --git a/src/vendor/core/dom/getUnboundedScrollPosition.js b/src/vendor/core/dom/getUnboundedScrollPosition.js index b0e32bd53a0..872465bbc15 100644 --- a/src/vendor/core/dom/getUnboundedScrollPosition.js +++ b/src/vendor/core/dom/getUnboundedScrollPosition.js @@ -5,8 +5,6 @@ "use strict"; -var getDocumentScrollElement = require('getDocumentScrollElement'); - /** * Gets the scroll position of the supplied element or window. * @@ -19,7 +17,10 @@ var getDocumentScrollElement = require('getDocumentScrollElement'); */ function getUnboundedScrollPosition(scrollable) { if (scrollable === window) { - return getUnboundedScrollPosition(getDocumentScrollElement()); + return { + x: document.documentElement.scrollLeft || document.body.scrollLeft, + y: document.documentElement.scrollTop || document.body.scrollTop + }; } return { x: scrollable.scrollLeft, diff --git a/vendor/fbtransform/transforms/react.js b/vendor/fbtransform/transforms/react.js index 094632ac244..7bef3e097c8 100644 --- a/vendor/fbtransform/transforms/react.js +++ b/vendor/fbtransform/transforms/react.js @@ -74,7 +74,8 @@ function visitReactTag(traverse, object, path, state) { move(object.name.range[1], state); var childrenToRender = object.children.filter(function(child) { - return !(child.type === Syntax.Literal && !child.value.match(/\S/)); + return !(child.type === Syntax.Literal && + child.value.match(/^[ \t]*[\r\n][ \t\r\n]*$/)); }); // if we don't have any attributes, pass in null @@ -140,9 +141,6 @@ function visitReactTag(traverse, object, path, state) { append(', ', state); object.children.forEach(function(child) { - if (child.type === Syntax.Literal && !child.value.match(/\S/)) { - return; - } catchup(child.range[0], state); var isLast = child === childrenToRender[childrenToRender.length - 1]; diff --git a/vendor/fbtransform/transforms/xjs.js b/vendor/fbtransform/transforms/xjs.js index b04c4e50b17..8d420612524 100644 --- a/vendor/fbtransform/transforms/xjs.js +++ b/vendor/fbtransform/transforms/xjs.js @@ -15,10 +15,8 @@ */ /*global exports:true*/ "use strict"; -var append = require('jstransform/src/utils').append; -var catchup = require('jstransform/src/utils').catchup; -var move = require('jstransform/src/utils').move; var Syntax = require('esprima-fb').Syntax; +var utils = require('jstransform/src/utils'); var knownTags = { a: true, @@ -147,134 +145,94 @@ var knownTags = { wbr: true }; -function safeTrim(string) { - return string.replace(/^[ \t]+/, '').replace(/[ \t]+$/, ''); -} - -// Replace all trailing whitespace characters with a single space character -function trimWithSingleSpace(string) { - return string.replace(/^[ \t\xA0]{2,}/, ' '). - replace(/[ \t\xA0]{2,}$/, ' ').replace(/^\s+$/, ''); -} - /** - * Special handling for multiline string literals - * print lines: - * - * line - * line - * - * as: + * #1 Any whitespace sequence which CONTAIN A NEWLINE and TOUCHES + * the start or end of the string is removed completely * - * "line "+ - * "line" + * #2 Any whitespace sequence which CONTAIN A NEWLINE but DOES NOT TOUCH + * the start or end of the string is replaced with a single newline */ -function renderXJSLiteral(object, isLast, state, start, end) { - /** Added blank check filtering and triming*/ - var trimmedChildValue = safeTrim(object.value); - var hasFinalNewLine = false; - - if (trimmedChildValue) { - // head whitespace - append(object.value.match(/^[\t ]*/)[0], state); - if (start) { - append(start, state); - } - var trimmedChildValueWithSpace = trimWithSingleSpace(object.value); - - /** - */ - var initialLines = trimmedChildValue.split(/\r\n|\n|\r/); +function removeWhitespace(str) { + return str.replace(/[ \t]+/g, ''); +}; + +function renderXJSLiteral(object, isLast, state, start, end) { + var trimmedChildValue = + object.value.replace(/^[ \t]*[\r\n][ \t\r\n]*/, removeWhitespace). // #1 + replace(/[ \t]*[\r\n][ \t\r\n]*$/, removeWhitespace). // #1 + replace(/([^ \t\r\n])([ \t]*[\r\n][ \t\r\n]*)(?=[^ \t\r\n])/g, // #2 + function(_, leading, match) { + return leading + ' ' + removeWhitespace(match); + }); + + //utils.append(object.value.match(/^[ \t]*/)[0], state); + if (start) { + utils.append(start, state); + } - var lines = initialLines.filter(function(line) { - return safeTrim(line).length > 0; - }); + var trimmedLines = trimmedChildValue.split(/\r\n|\n|\r/); + var lines = object.value.split(/\r\n|\n|\r/); - var hasInitialNewLine = initialLines[0] !== lines[0]; - hasFinalNewLine = - initialLines[initialLines.length - 1] !== lines[lines.length - 1]; + var lastNonEmptyLine = -1; + trimmedLines.forEach(function (line, ii) { + if (line) lastNonEmptyLine = ii; + }); - var numLines = lines.length; - lines.forEach(function (line, ii) { - var lastLine = ii === numLines - 1; - var trimmedLine = safeTrim(line); - if (trimmedLine === '' && !lastLine) { - append(line, state); - } else { - var preString = ''; - var postString = ''; - var leading = line.match(/^[ \t]*/)[0]; + var numLines = trimmedLines.length; + trimmedLines.forEach(function (line, ii) { + var isLastLine = (ii === numLines - 1); - if (ii === 0) { - if (hasInitialNewLine) { - preString = ' '; - leading = '\n' + leading; - } - if (trimmedChildValueWithSpace.substring(0, 1) === ' ') { - // If this is the first line, and the original content starts with - // whitespace, place a single space at the beginning. - preString = ' '; - } - } - if (!lastLine || trimmedChildValueWithSpace.substr( - trimmedChildValueWithSpace.length - 1, 1) === ' ' || - hasFinalNewLine - ) { - // If either not on the last line, or the original content ends with - // whitespace, place a single character at the end. - postString = ' '; - } + var isEmptyLine = !!lines[ii].match(/^[ \t\xA0]*$/); - append( - leading + - JSON.stringify( - preString + trimmedLine + postString - ) + - (lastLine ? '' : '+') + - line.match(/[ \t]*$/)[0], - state); + if (line) { + if (!isEmptyLine) { + utils.append(lines[ii].match(/^[ \t\xA0]*/)[0], state); } - if (!lastLine) { - append('\n', state); + if (!isLastLine) { + // we temporarily appended a space for #2, replace with a newline + line = line.replace(/[ \t]$/g, '\n'); } - }); - } else { - if (start) { - append(start, state); - } - append('""', state); - } - if (end) { - append(end, state); - } - - // add comma before trailing whitespace - if (!isLast) { - append(',', state); - } - // tail whitespace - if (hasFinalNewLine) { - append('\n', state); - } - append(object.value.match(/[ \t]*$/)[0], state); - move(object.range[1], state); + utils.append( + JSON.stringify(line) + + (ii >= lastNonEmptyLine ? '' : '+'), + state); + } + + // insert just after the last literal + if (!isLast && ii === lastNonEmptyLine) { + if (end) { + utils.append(end, state); + } + utils.append(',', state); + } + + //if (!isEmptyLine || ii > 0) { + utils.append(lines[ii].match(/[ \t\xA0]*$/)[0], state); + //} + + if (!isLastLine) { + utils.append('\n', state); + } + }); + + utils.move(object.range[1], state); } function renderXJSExpressionContainer(traverse, object, isLast, path, state) { // Plus 1 to skip `{`. - move(object.range[0] + 1, state); + utils.move(object.range[0] + 1, state); traverse(object.expression, path, state); if (!isLast && object.expression.type !== Syntax.XJSEmptyExpression) { // If we need to append a comma, make sure to do so after the expression. - catchup(object.expression.range[1], state); - append(',', state); + utils.catchup(object.expression.range[1], state); + utils.append(',', state); } // Minus 1 to skip `}`. - catchup(object.range[1] - 1, state); - move(object.range[1], state); + utils.catchup(object.range[1] - 1, state); + utils.move(object.range[1], state); return false; }