react 0.10.0 → 0.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/README.md +3 -0
  2. package/dist/JSXTransformer.js +20344 -0
  3. package/dist/react-with-addons.js +20276 -0
  4. package/dist/react-with-addons.min.js +22 -0
  5. package/dist/react.js +18484 -0
  6. package/dist/react.min.js +21 -0
  7. package/lib/BeforeInputEventPlugin.js +222 -0
  8. package/lib/CSSPropertyOperations.js +3 -3
  9. package/lib/{ReactMountReady.js → CallbackQueue.js} +32 -24
  10. package/lib/ChangeEventPlugin.js +1 -1
  11. package/lib/CompositionEventPlugin.js +5 -1
  12. package/lib/DOMChildrenOperations.js +21 -14
  13. package/lib/DOMProperty.js +45 -17
  14. package/lib/DOMPropertyOperations.js +22 -10
  15. package/lib/DefaultEventPluginOrder.js +1 -0
  16. package/lib/EventConstants.js +1 -0
  17. package/lib/EventListener.js +5 -2
  18. package/lib/EventPluginHub.js +0 -5
  19. package/lib/EventPluginRegistry.js +6 -4
  20. package/lib/EventPluginUtils.js +11 -1
  21. package/lib/ExecutionEnvironment.js +8 -2
  22. package/lib/{DefaultDOMPropertyConfig.js → HTMLDOMPropertyConfig.js} +42 -49
  23. package/lib/LinkedValueUtils.js +21 -22
  24. package/lib/LocalEventTrapMixin.js +52 -0
  25. package/lib/React.js +57 -3
  26. package/lib/ReactBrowserComponentMixin.js +4 -0
  27. package/lib/{ReactEventEmitter.js → ReactBrowserEventEmitter.js} +115 -94
  28. package/lib/ReactCSSTransitionGroup.js +2 -0
  29. package/lib/ReactCSSTransitionGroupChild.js +2 -5
  30. package/lib/ReactChildren.js +31 -10
  31. package/lib/ReactComponent.js +88 -237
  32. package/lib/ReactComponentBrowserEnvironment.js +3 -2
  33. package/lib/ReactComponentWithPureRenderMixin.js +54 -0
  34. package/lib/ReactCompositeComponent.js +222 -384
  35. package/lib/ReactDOM.js +22 -18
  36. package/lib/ReactDOMComponent.js +26 -24
  37. package/lib/ReactDOMForm.js +5 -13
  38. package/lib/ReactDOMIDOperations.js +2 -31
  39. package/lib/ReactDOMImg.js +5 -14
  40. package/lib/ReactDOMSelect.js +16 -15
  41. package/lib/ReactDOMSelection.js +35 -10
  42. package/lib/ReactDOMTextarea.js +2 -4
  43. package/lib/ReactDefaultBatchingStrategy.js +3 -3
  44. package/lib/ReactDefaultInjection.js +18 -15
  45. package/lib/ReactDefaultPerf.js +28 -11
  46. package/lib/ReactDefaultPerfAnalysis.js +4 -0
  47. package/lib/ReactDescriptor.js +251 -0
  48. package/lib/ReactDescriptorValidator.js +283 -0
  49. package/lib/ReactEmptyComponent.js +78 -0
  50. package/lib/ReactEventEmitterMixin.js +1 -3
  51. package/lib/ReactEventListener.js +189 -0
  52. package/lib/ReactInjection.js +4 -2
  53. package/lib/ReactLink.js +24 -0
  54. package/lib/ReactMount.js +51 -19
  55. package/lib/ReactMultiChild.js +9 -11
  56. package/lib/ReactPropTransferer.js +44 -29
  57. package/lib/ReactPropTypes.js +226 -242
  58. package/lib/ReactPutListenerQueue.js +2 -2
  59. package/lib/ReactReconcileTransaction.js +14 -14
  60. package/lib/ReactServerRendering.js +5 -5
  61. package/lib/ReactServerRenderingTransaction.js +4 -5
  62. package/lib/ReactTestUtils.js +39 -21
  63. package/lib/ReactTextComponent.js +8 -22
  64. package/lib/ReactTransitionChildMapping.js +2 -2
  65. package/lib/ReactTransitionEvents.js +19 -0
  66. package/lib/ReactTransitionGroup.js +9 -6
  67. package/lib/ReactUpdates.js +139 -22
  68. package/lib/ReactWithAddons.js +5 -2
  69. package/lib/SVGDOMPropertyConfig.js +97 -0
  70. package/lib/SimpleEventPlugin.js +7 -1
  71. package/lib/SyntheticInputEvent.js +52 -0
  72. package/lib/SyntheticKeyboardEvent.js +33 -4
  73. package/lib/SyntheticMouseEvent.js +3 -0
  74. package/lib/SyntheticTouchEvent.js +4 -1
  75. package/lib/SyntheticUIEvent.js +24 -2
  76. package/lib/Transaction.js +0 -32
  77. package/lib/cloneWithProps.js +3 -1
  78. package/lib/createFullPageComponent.js +1 -1
  79. package/lib/dangerousStyleValue.js +11 -5
  80. package/lib/escapeTextForBrowser.js +2 -3
  81. package/lib/flattenChildren.js +9 -7
  82. package/lib/getEventKey.js +35 -5
  83. package/lib/getEventModifierState.js +52 -0
  84. package/lib/getMarkupWrap.js +2 -0
  85. package/lib/getTextContentAccessor.js +1 -1
  86. package/lib/hyphenate.js +3 -0
  87. package/lib/hyphenateStyleName.js +46 -0
  88. package/lib/instantiateReactComponent.js +13 -21
  89. package/lib/invariant.js +17 -19
  90. package/lib/{objMap.js → mapObject.js} +8 -3
  91. package/lib/mergeHelpers.js +11 -0
  92. package/lib/mergeInto.js +3 -2
  93. package/lib/onlyChild.js +3 -3
  94. package/lib/performance.js +33 -0
  95. package/lib/performanceNow.js +5 -14
  96. package/lib/setInnerHTML.js +85 -0
  97. package/lib/shouldUpdateReactComponent.js +12 -29
  98. package/lib/toArray.js +1 -1
  99. package/lib/traverseAllChildren.js +7 -4
  100. package/lib/update.js +57 -45
  101. package/package.json +4 -3
  102. package/lib/ReactEventTopLevelCallback.js +0 -149
  103. package/lib/createObjectFrom.js +0 -61
  104. package/lib/objMapKeyVal.js +0 -47
package/lib/ReactDOM.js CHANGED
@@ -19,10 +19,12 @@
19
19
 
20
20
  "use strict";
21
21
 
22
+ var ReactDescriptor = require("./ReactDescriptor");
23
+ var ReactDescriptorValidator = require("./ReactDescriptorValidator");
22
24
  var ReactDOMComponent = require("./ReactDOMComponent");
23
25
 
24
26
  var mergeInto = require("./mergeInto");
25
- var objMapKeyVal = require("./objMapKeyVal");
27
+ var mapObject = require("./mapObject");
26
28
 
27
29
  /**
28
30
  * Creates a new React class that is idempotent and capable of containing other
@@ -35,30 +37,26 @@ var objMapKeyVal = require("./objMapKeyVal");
35
37
  * The `style` property functions differently from the DOM API. It accepts an
36
38
  * object mapping of style properties to values.
37
39
  *
38
- * @param {string} tag Tag name (e.g. `div`).
39
40
  * @param {boolean} omitClose True if the close tag should be omitted.
41
+ * @param {string} tag Tag name (e.g. `div`).
40
42
  * @private
41
43
  */
42
- function createDOMComponentClass(tag, omitClose) {
43
- var Constructor = function() {};
44
+ function createDOMComponentClass(omitClose, tag) {
45
+ var Constructor = function(descriptor) {
46
+ this.construct(descriptor);
47
+ };
44
48
  Constructor.prototype = new ReactDOMComponent(tag, omitClose);
45
49
  Constructor.prototype.constructor = Constructor;
46
50
  Constructor.displayName = tag;
47
51
 
48
- var ConvenienceConstructor = function(props, children) {
49
- var instance = new Constructor();
50
- instance.construct.apply(instance, arguments);
51
- return instance;
52
- };
52
+ var ConvenienceConstructor = ReactDescriptor.createFactory(Constructor);
53
53
 
54
- // Expose the constructor on the ConvenienceConstructor and prototype so that
55
- // it can be easily easily accessed on descriptors.
56
- // E.g. <div />.type === div.type
57
- ConvenienceConstructor.type = Constructor;
58
- Constructor.prototype.type = Constructor;
54
+ if ("production" !== process.env.NODE_ENV) {
55
+ return ReactDescriptorValidator.createFactory(
56
+ ConvenienceConstructor
57
+ );
58
+ }
59
59
 
60
- Constructor.ConvenienceConstructor = ConvenienceConstructor;
61
- ConvenienceConstructor.componentConstructor = Constructor;
62
60
  return ConvenienceConstructor;
63
61
  }
64
62
 
@@ -68,7 +66,7 @@ function createDOMComponentClass(tag, omitClose) {
68
66
  *
69
67
  * @public
70
68
  */
71
- var ReactDOM = objMapKeyVal({
69
+ var ReactDOM = mapObject({
72
70
  a: false,
73
71
  abbr: false,
74
72
  address: false,
@@ -97,6 +95,7 @@ var ReactDOM = objMapKeyVal({
97
95
  del: false,
98
96
  details: false,
99
97
  dfn: false,
98
+ dialog: false,
100
99
  div: false,
101
100
  dl: false,
102
101
  dt: false,
@@ -144,6 +143,7 @@ var ReactDOM = objMapKeyVal({
144
143
  output: false,
145
144
  p: false,
146
145
  param: true,
146
+ picture: false,
147
147
  pre: false,
148
148
  progress: false,
149
149
  q: false,
@@ -183,17 +183,21 @@ var ReactDOM = objMapKeyVal({
183
183
  // SVG
184
184
  circle: false,
185
185
  defs: false,
186
+ ellipse: false,
186
187
  g: false,
187
188
  line: false,
188
189
  linearGradient: false,
190
+ mask: false,
189
191
  path: false,
192
+ pattern: false,
190
193
  polygon: false,
191
194
  polyline: false,
192
195
  radialGradient: false,
193
196
  rect: false,
194
197
  stop: false,
195
198
  svg: false,
196
- text: false
199
+ text: false,
200
+ tspan: false
197
201
  }, createDOMComponentClass);
198
202
 
199
203
  var injection = {
@@ -24,7 +24,7 @@ var DOMProperty = require("./DOMProperty");
24
24
  var DOMPropertyOperations = require("./DOMPropertyOperations");
25
25
  var ReactBrowserComponentMixin = require("./ReactBrowserComponentMixin");
26
26
  var ReactComponent = require("./ReactComponent");
27
- var ReactEventEmitter = require("./ReactEventEmitter");
27
+ var ReactBrowserEventEmitter = require("./ReactBrowserEventEmitter");
28
28
  var ReactMount = require("./ReactMount");
29
29
  var ReactMultiChild = require("./ReactMultiChild");
30
30
  var ReactPerf = require("./ReactPerf");
@@ -35,9 +35,9 @@ var keyOf = require("./keyOf");
35
35
  var merge = require("./merge");
36
36
  var mixInto = require("./mixInto");
37
37
 
38
- var deleteListener = ReactEventEmitter.deleteListener;
39
- var listenTo = ReactEventEmitter.listenTo;
40
- var registrationNameModules = ReactEventEmitter.registrationNameModules;
38
+ var deleteListener = ReactBrowserEventEmitter.deleteListener;
39
+ var listenTo = ReactBrowserEventEmitter.listenTo;
40
+ var registrationNameModules = ReactBrowserEventEmitter.registrationNameModules;
41
41
 
42
42
  // For quickly matching children type, to test if can be treated as content.
43
43
  var CONTENT_TYPES = {'string': true, 'number': true};
@@ -147,7 +147,7 @@ ReactDOMComponent.Mixin = {
147
147
  if (propValue == null) {
148
148
  continue;
149
149
  }
150
- if (registrationNameModules[propKey]) {
150
+ if (registrationNameModules.hasOwnProperty(propKey)) {
151
151
  putListener(this._rootNodeID, propKey, propValue, transaction);
152
152
  } else {
153
153
  if (propKey === STYLE) {
@@ -205,20 +205,22 @@ ReactDOMComponent.Mixin = {
205
205
  return '';
206
206
  },
207
207
 
208
- receiveComponent: function(nextComponent, transaction) {
209
- if (nextComponent === this) {
210
- // Since props and context are immutable after the component is
211
- // mounted, we can do a cheap identity compare here to determine
212
- // if this is a superfluous reconcile.
213
-
214
- // TODO: compare the descriptor
208
+ receiveComponent: function(nextDescriptor, transaction) {
209
+ if (nextDescriptor === this._descriptor &&
210
+ nextDescriptor._owner != null) {
211
+ // Since descriptors are immutable after the owner is rendered,
212
+ // we can do a cheap identity compare here to determine if this is a
213
+ // superfluous reconcile. It's possible for state to be mutable but such
214
+ // change should trigger an update of the owner which would recreate
215
+ // the descriptor. We explicitly check for the existence of an owner since
216
+ // it's possible for a descriptor created outside a composite to be
217
+ // deeply mutated and reused.
215
218
  return;
216
219
  }
217
220
 
218
- assertValidProps(nextComponent.props);
219
221
  ReactComponent.Mixin.receiveComponent.call(
220
222
  this,
221
- nextComponent,
223
+ nextDescriptor,
222
224
  transaction
223
225
  );
224
226
  },
@@ -228,22 +230,22 @@ ReactDOMComponent.Mixin = {
228
230
  * attached to the DOM. Reconciles the root DOM node, then recurses.
229
231
  *
230
232
  * @param {ReactReconcileTransaction} transaction
231
- * @param {object} prevProps
233
+ * @param {ReactDescriptor} prevDescriptor
232
234
  * @internal
233
235
  * @overridable
234
236
  */
235
237
  updateComponent: ReactPerf.measure(
236
238
  'ReactDOMComponent',
237
239
  'updateComponent',
238
- function(transaction, prevProps, prevOwner) {
240
+ function(transaction, prevDescriptor) {
241
+ assertValidProps(this._descriptor.props);
239
242
  ReactComponent.Mixin.updateComponent.call(
240
243
  this,
241
244
  transaction,
242
- prevProps,
243
- prevOwner
245
+ prevDescriptor
244
246
  );
245
- this._updateDOMProperties(prevProps, transaction);
246
- this._updateDOMChildren(prevProps, transaction);
247
+ this._updateDOMProperties(prevDescriptor.props, transaction);
248
+ this._updateDOMChildren(prevDescriptor.props, transaction);
247
249
  }
248
250
  ),
249
251
 
@@ -280,7 +282,7 @@ ReactDOMComponent.Mixin = {
280
282
  styleUpdates[styleName] = '';
281
283
  }
282
284
  }
283
- } else if (registrationNameModules[propKey]) {
285
+ } else if (registrationNameModules.hasOwnProperty(propKey)) {
284
286
  deleteListener(this._rootNodeID, propKey);
285
287
  } else if (
286
288
  DOMProperty.isStandardName[propKey] ||
@@ -305,7 +307,7 @@ ReactDOMComponent.Mixin = {
305
307
  // Unset styles on `lastProp` but not on `nextProp`.
306
308
  for (styleName in lastProp) {
307
309
  if (lastProp.hasOwnProperty(styleName) &&
308
- !nextProp.hasOwnProperty(styleName)) {
310
+ (!nextProp || !nextProp.hasOwnProperty(styleName))) {
309
311
  styleUpdates = styleUpdates || {};
310
312
  styleUpdates[styleName] = '';
311
313
  }
@@ -322,7 +324,7 @@ ReactDOMComponent.Mixin = {
322
324
  // Relies on `updateStylesByID` not mutating `styleUpdates`.
323
325
  styleUpdates = nextProp;
324
326
  }
325
- } else if (registrationNameModules[propKey]) {
327
+ } else if (registrationNameModules.hasOwnProperty(propKey)) {
326
328
  putListener(this._rootNodeID, propKey, nextProp, transaction);
327
329
  } else if (
328
330
  DOMProperty.isStandardName[propKey] ||
@@ -402,7 +404,7 @@ ReactDOMComponent.Mixin = {
402
404
  */
403
405
  unmountComponent: function() {
404
406
  this.unmountChildren();
405
- ReactEventEmitter.deleteAllListeners(this._rootNodeID);
407
+ ReactBrowserEventEmitter.deleteAllListeners(this._rootNodeID);
406
408
  ReactComponent.Mixin.unmountComponent.call(this);
407
409
  }
408
410
 
@@ -18,11 +18,11 @@
18
18
 
19
19
  "use strict";
20
20
 
21
+ var EventConstants = require("./EventConstants");
22
+ var LocalEventTrapMixin = require("./LocalEventTrapMixin");
21
23
  var ReactBrowserComponentMixin = require("./ReactBrowserComponentMixin");
22
24
  var ReactCompositeComponent = require("./ReactCompositeComponent");
23
25
  var ReactDOM = require("./ReactDOM");
24
- var ReactEventEmitter = require("./ReactEventEmitter");
25
- var EventConstants = require("./EventConstants");
26
26
 
27
27
  // Store a reference to the <form> `ReactDOMComponent`.
28
28
  var form = ReactDOM.form;
@@ -36,7 +36,7 @@ var form = ReactDOM.form;
36
36
  var ReactDOMForm = ReactCompositeComponent.createClass({
37
37
  displayName: 'ReactDOMForm',
38
38
 
39
- mixins: [ReactBrowserComponentMixin],
39
+ mixins: [ReactBrowserComponentMixin, LocalEventTrapMixin],
40
40
 
41
41
  render: function() {
42
42
  // TODO: Instead of using `ReactDOM` directly, we should use JSX. However,
@@ -46,16 +46,8 @@ var ReactDOMForm = ReactCompositeComponent.createClass({
46
46
  },
47
47
 
48
48
  componentDidMount: function() {
49
- ReactEventEmitter.trapBubbledEvent(
50
- EventConstants.topLevelTypes.topReset,
51
- 'reset',
52
- this.getDOMNode()
53
- );
54
- ReactEventEmitter.trapBubbledEvent(
55
- EventConstants.topLevelTypes.topSubmit,
56
- 'submit',
57
- this.getDOMNode()
58
- );
49
+ this.trapBubbledEvent(EventConstants.topLevelTypes.topReset, 'reset');
50
+ this.trapBubbledEvent(EventConstants.topLevelTypes.topSubmit, 'submit');
59
51
  }
60
52
  });
61
53
 
@@ -28,6 +28,7 @@ var ReactMount = require("./ReactMount");
28
28
  var ReactPerf = require("./ReactPerf");
29
29
 
30
30
  var invariant = require("./invariant");
31
+ var setInnerHTML = require("./setInnerHTML");
31
32
 
32
33
  /**
33
34
  * Errors for properties that should not be updated with `updatePropertyById()`.
@@ -41,8 +42,6 @@ var INVALID_PROPERTY_ERRORS = {
41
42
  style: '`style` must be set using `updateStylesByID()`.'
42
43
  };
43
44
 
44
- var useWhitespaceWorkaround;
45
-
46
45
  /**
47
46
  * Operations used to process updates to DOM nodes. This is made injectable via
48
47
  * `ReactComponent.BackendIDOperations`.
@@ -131,35 +130,7 @@ var ReactDOMIDOperations = {
131
130
  'updateInnerHTMLByID',
132
131
  function(id, html) {
133
132
  var node = ReactMount.getNode(id);
134
-
135
- // IE8: When updating a just created node with innerHTML only leading
136
- // whitespace is removed. When updating an existing node with innerHTML
137
- // whitespace in root TextNodes is also collapsed.
138
- // @see quirksmode.org/bugreports/archives/2004/11/innerhtml_and_t.html
139
-
140
- if (useWhitespaceWorkaround === undefined) {
141
- // Feature detection; only IE8 is known to behave improperly like this.
142
- var temp = document.createElement('div');
143
- temp.innerHTML = ' ';
144
- useWhitespaceWorkaround = temp.innerHTML === '';
145
- }
146
-
147
- if (useWhitespaceWorkaround) {
148
- // Magic theory: IE8 supposedly differentiates between added and updated
149
- // nodes when processing innerHTML, innerHTML on updated nodes suffers
150
- // from worse whitespace behavior. Re-adding a node like this triggers
151
- // the initial and more favorable whitespace behavior.
152
- node.parentNode.replaceChild(node, node);
153
- }
154
-
155
- if (useWhitespaceWorkaround && html.match(/^[ \r\n\t\f]/)) {
156
- // Recover leading whitespace by temporarily prepending any character.
157
- // \uFEFF has the potential advantage of being zero-width/invisible.
158
- node.innerHTML = '\uFEFF' + html;
159
- node.firstChild.deleteData(0, 1);
160
- } else {
161
- node.innerHTML = html;
162
- }
133
+ setInnerHTML(node, html);
163
134
  }
164
135
  ),
165
136
 
@@ -18,11 +18,11 @@
18
18
 
19
19
  "use strict";
20
20
 
21
+ var EventConstants = require("./EventConstants");
22
+ var LocalEventTrapMixin = require("./LocalEventTrapMixin");
21
23
  var ReactBrowserComponentMixin = require("./ReactBrowserComponentMixin");
22
24
  var ReactCompositeComponent = require("./ReactCompositeComponent");
23
25
  var ReactDOM = require("./ReactDOM");
24
- var ReactEventEmitter = require("./ReactEventEmitter");
25
- var EventConstants = require("./EventConstants");
26
26
 
27
27
  // Store a reference to the <img> `ReactDOMComponent`.
28
28
  var img = ReactDOM.img;
@@ -37,24 +37,15 @@ var ReactDOMImg = ReactCompositeComponent.createClass({
37
37
  displayName: 'ReactDOMImg',
38
38
  tagName: 'IMG',
39
39
 
40
- mixins: [ReactBrowserComponentMixin],
40
+ mixins: [ReactBrowserComponentMixin, LocalEventTrapMixin],
41
41
 
42
42
  render: function() {
43
43
  return img(this.props);
44
44
  },
45
45
 
46
46
  componentDidMount: function() {
47
- var node = this.getDOMNode();
48
- ReactEventEmitter.trapBubbledEvent(
49
- EventConstants.topLevelTypes.topLoad,
50
- 'load',
51
- node
52
- );
53
- ReactEventEmitter.trapBubbledEvent(
54
- EventConstants.topLevelTypes.topError,
55
- 'error',
56
- node
57
- );
47
+ this.trapBubbledEvent(EventConstants.topLevelTypes.topLoad, 'load');
48
+ this.trapBubbledEvent(EventConstants.topLevelTypes.topError, 'error');
58
49
  }
59
50
  });
60
51
 
@@ -24,7 +24,6 @@ var ReactBrowserComponentMixin = require("./ReactBrowserComponentMixin");
24
24
  var ReactCompositeComponent = require("./ReactCompositeComponent");
25
25
  var ReactDOM = require("./ReactDOM");
26
26
 
27
- var invariant = require("./invariant");
28
27
  var merge = require("./merge");
29
28
 
30
29
  // Store a reference to the <select> `ReactDOMComponent`.
@@ -39,19 +38,19 @@ function selectValueType(props, propName, componentName) {
39
38
  return;
40
39
  }
41
40
  if (props.multiple) {
42
- ("production" !== process.env.NODE_ENV ? invariant(
43
- Array.isArray(props[propName]),
44
- 'The `%s` prop supplied to <select> must be an array if `multiple` is ' +
45
- 'true.',
46
- propName
47
- ) : invariant(Array.isArray(props[propName])));
41
+ if (!Array.isArray(props[propName])) {
42
+ return new Error(
43
+ ("The `" + propName + "` prop supplied to <select> must be an array if ") +
44
+ ("`multiple` is true.")
45
+ );
46
+ }
48
47
  } else {
49
- ("production" !== process.env.NODE_ENV ? invariant(
50
- !Array.isArray(props[propName]),
51
- 'The `%s` prop supplied to <select> must be a scalar value if ' +
52
- '`multiple` is false.',
53
- propName
54
- ) : invariant(!Array.isArray(props[propName])));
48
+ if (Array.isArray(props[propName])) {
49
+ return new Error(
50
+ ("The `" + propName + "` prop supplied to <select> must be a scalar ") +
51
+ ("value if `multiple` is false.")
52
+ );
53
+ }
55
54
  }
56
55
  }
57
56
 
@@ -142,9 +141,11 @@ var ReactDOMSelect = ReactCompositeComponent.createClass({
142
141
  updateOptions(this, LinkedValueUtils.getValue(this));
143
142
  },
144
143
 
145
- componentDidUpdate: function() {
144
+ componentDidUpdate: function(prevProps) {
146
145
  var value = LinkedValueUtils.getValue(this);
147
- if (value != null) {
146
+ var prevMultiple = !!prevProps.multiple;
147
+ var multiple = !!this.props.multiple;
148
+ if (value != null || prevMultiple !== multiple) {
148
149
  updateOptions(this, value);
149
150
  }
150
151
  },
@@ -18,9 +18,20 @@
18
18
 
19
19
  "use strict";
20
20
 
21
+ var ExecutionEnvironment = require("./ExecutionEnvironment");
22
+
21
23
  var getNodeForCharacterOffset = require("./getNodeForCharacterOffset");
22
24
  var getTextContentAccessor = require("./getTextContentAccessor");
23
25
 
26
+ /**
27
+ * While `isCollapsed` is available on the Selection object and `collapsed`
28
+ * is available on the Range object, IE11 sometimes gets them wrong.
29
+ * If the anchor/focus nodes and offsets are the same, the range is collapsed.
30
+ */
31
+ function isCollapsed(anchorNode, anchorOffset, focusNode, focusOffset) {
32
+ return anchorNode === focusNode && anchorOffset === focusOffset;
33
+ }
34
+
24
35
  /**
25
36
  * Get the appropriate anchor and focus node/offset pairs for IE.
26
37
  *
@@ -71,13 +82,31 @@ function getModernOffsets(node) {
71
82
  var focusOffset = selection.focusOffset;
72
83
 
73
84
  var currentRange = selection.getRangeAt(0);
74
- var rangeLength = currentRange.toString().length;
85
+
86
+ // If the node and offset values are the same, the selection is collapsed.
87
+ // `Selection.isCollapsed` is available natively, but IE sometimes gets
88
+ // this value wrong.
89
+ var isSelectionCollapsed = isCollapsed(
90
+ selection.anchorNode,
91
+ selection.anchorOffset,
92
+ selection.focusNode,
93
+ selection.focusOffset
94
+ );
95
+
96
+ var rangeLength = isSelectionCollapsed ? 0 : currentRange.toString().length;
75
97
 
76
98
  var tempRange = currentRange.cloneRange();
77
99
  tempRange.selectNodeContents(node);
78
100
  tempRange.setEnd(currentRange.startContainer, currentRange.startOffset);
79
101
 
80
- var start = tempRange.toString().length;
102
+ var isTempRangeCollapsed = isCollapsed(
103
+ tempRange.startContainer,
104
+ tempRange.startOffset,
105
+ tempRange.endContainer,
106
+ tempRange.endOffset
107
+ );
108
+
109
+ var start = isTempRangeCollapsed ? 0 : tempRange.toString().length;
81
110
  var end = start + rangeLength;
82
111
 
83
112
  // Detect whether the selection is backward.
@@ -167,23 +196,19 @@ function setModernOffsets(node, offsets) {
167
196
  }
168
197
  }
169
198
 
199
+ var useIEOffsets = ExecutionEnvironment.canUseDOM && document.selection;
200
+
170
201
  var ReactDOMSelection = {
171
202
  /**
172
203
  * @param {DOMElement} node
173
204
  */
174
- getOffsets: function(node) {
175
- var getOffsets = document.selection ? getIEOffsets : getModernOffsets;
176
- return getOffsets(node);
177
- },
205
+ getOffsets: useIEOffsets ? getIEOffsets : getModernOffsets,
178
206
 
179
207
  /**
180
208
  * @param {DOMElement|DOMTextNode} node
181
209
  * @param {object} offsets
182
210
  */
183
- setOffsets: function(node, offsets) {
184
- var setOffsets = document.selection ? setIEOffsets : setModernOffsets;
185
- setOffsets(node, offsets);
186
- }
211
+ setOffsets: useIEOffsets ? setIEOffsets : setModernOffsets
187
212
  };
188
213
 
189
214
  module.exports = ReactDOMSelection;