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;
}