reagent/bower_components/react/react-with-addons.js

18137 lines
563 KiB
JavaScript

/**
* React (with addons) v0.9.0
*/
!function(e){if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.React=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
/**
* 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 AutoFocusMixin
* @typechecks static-only
*/
"use strict";
var AutoFocusMixin = {
componentDidMount: function() {
if (this.props.autoFocus) {
this.getDOMNode().focus();
}
}
};
module.exports = AutoFocusMixin;
},{}],2:[function(require,module,exports){
/**
* 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 CSSCore
* @typechecks
*/
var invariant = require("./invariant");
/**
* The CSSCore module specifies the API (and implements most of the methods)
* that should be used when dealing with the display of elements (via their
* CSS classes and visibility on screen. It is an API focused on mutating the
* display and not reading it as no logical state should be encoded in the
* display of elements.
*/
var CSSCore = {
/**
* Adds the class passed in to the element if it doesn't already have it.
*
* @param {DOMElement} element the element to set the class on
* @param {string} className the CSS className
* @return {DOMElement} the element passed in
*/
addClass: function(element, className) {
("production" !== "development" ? invariant(
!/\s/.test(className),
'CSSCore.addClass takes only a single class name. "%s" contains ' +
'multiple classes.', className
) : invariant(!/\s/.test(className)));
if (className) {
if (element.classList) {
element.classList.add(className);
} else if (!CSSCore.hasClass(element, className)) {
element.className = element.className + ' ' + className;
}
}
return element;
},
/**
* Removes the class passed in from the element
*
* @param {DOMElement} element the element to set the class on
* @param {string} className the CSS className
* @return {DOMElement} the element passed in
*/
removeClass: function(element, className) {
("production" !== "development" ? invariant(
!/\s/.test(className),
'CSSCore.removeClass takes only a single class name. "%s" contains ' +
'multiple classes.', className
) : invariant(!/\s/.test(className)));
if (className) {
if (element.classList) {
element.classList.remove(className);
} else if (CSSCore.hasClass(element, className)) {
element.className = element.className
.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)', 'g'), '$1')
.replace(/\s+/g, ' ') // multiple spaces to one
.replace(/^\s*|\s*$/g, ''); // trim the ends
}
}
return element;
},
/**
* Helper to add or remove a class from an element based on a condition.
*
* @param {DOMElement} element the element to set the class on
* @param {string} className the CSS className
* @param {*} bool condition to whether to add or remove the class
* @return {DOMElement} the element passed in
*/
conditionClass: function(element, className, bool) {
return (bool ? CSSCore.addClass : CSSCore.removeClass)(element, className);
},
/**
* Tests whether the element has the class specified.
*
* @param {DOMNode|DOMWindow} element the element to set the class on
* @param {string} className the CSS className
* @returns {boolean} true if the element has the class, false if not
*/
hasClass: function(element, className) {
("production" !== "development" ? invariant(
!/\s/.test(className),
'CSS.hasClass takes only a single class name.'
) : invariant(!/\s/.test(className)));
if (element.classList) {
return !!className && element.classList.contains(className);
}
return (' ' + element.className + ' ').indexOf(' ' + className + ' ') > -1;
}
};
module.exports = CSSCore;
},{"./invariant":121}],3:[function(require,module,exports){
/**
* 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 CSSProperty
*/
"use strict";
/**
* CSS properties which accept numbers but are not in units of "px".
*/
var isUnitlessNumber = {
columnCount: true,
fillOpacity: true,
flex: true,
flexGrow: true,
flexShrink: true,
fontWeight: true,
lineClamp: true,
lineHeight: true,
opacity: true,
order: true,
orphans: true,
widows: true,
zIndex: true,
zoom: true
};
/**
* @param {string} prefix vendor-specific prefix, eg: Webkit
* @param {string} key style name, eg: transitionDuration
* @return {string} style name prefixed with `prefix`, properly camelCased, eg:
* WebkitTransitionDuration
*/
function prefixKey(prefix, key) {
return prefix + key.charAt(0).toUpperCase() + key.substring(1);
}
/**
* Support style names that may come passed in prefixed by adding permutations
* of vendor prefixes.
*/
var prefixes = ['Webkit', 'ms', 'Moz', 'O'];
// Using Object.keys here, or else the vanilla for-in loop makes IE8 go into an
// infinite loop, because it iterates over the newly added props too.
Object.keys(isUnitlessNumber).forEach(function(prop) {
prefixes.forEach(function(prefix) {
isUnitlessNumber[prefixKey(prefix, prop)] = isUnitlessNumber[prop];
});
});
/**
* Most style properties can be unset by doing .style[prop] = '' but IE8
* doesn't like doing that with shorthand properties so for the properties that
* IE8 breaks on, which are listed here, we instead unset each of the
* individual properties. See http://bugs.jquery.com/ticket/12385.
* The 4-value 'clock' properties like margin, padding, border-width seem to
* behave without any problems. Curiously, list-style works too without any
* special prodding.
*/
var shorthandPropertyExpansions = {
background: {
backgroundImage: true,
backgroundPosition: true,
backgroundRepeat: true,
backgroundColor: true
},
border: {
borderWidth: true,
borderStyle: true,
borderColor: true
},
borderBottom: {
borderBottomWidth: true,
borderBottomStyle: true,
borderBottomColor: true
},
borderLeft: {
borderLeftWidth: true,
borderLeftStyle: true,
borderLeftColor: true
},
borderRight: {
borderRightWidth: true,
borderRightStyle: true,
borderRightColor: true
},
borderTop: {
borderTopWidth: true,
borderTopStyle: true,
borderTopColor: true
},
font: {
fontStyle: true,
fontVariant: true,
fontWeight: true,
fontSize: true,
lineHeight: true,
fontFamily: true
}
};
var CSSProperty = {
isUnitlessNumber: isUnitlessNumber,
shorthandPropertyExpansions: shorthandPropertyExpansions
};
module.exports = CSSProperty;
},{}],4:[function(require,module,exports){
/**
* 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 CSSPropertyOperations
* @typechecks static-only
*/
"use strict";
var CSSProperty = require("./CSSProperty");
var dangerousStyleValue = require("./dangerousStyleValue");
var escapeTextForBrowser = require("./escapeTextForBrowser");
var hyphenate = require("./hyphenate");
var memoizeStringOnly = require("./memoizeStringOnly");
var processStyleName = memoizeStringOnly(function(styleName) {
return escapeTextForBrowser(hyphenate(styleName));
});
/**
* Operations for dealing with CSS properties.
*/
var CSSPropertyOperations = {
/**
* Serializes a mapping of style properties for use as inline styles:
*
* > createMarkupForStyles({width: '200px', height: 0})
* "width:200px;height:0;"
*
* Undefined values are ignored so that declarative programming is easier.
*
* @param {object} styles
* @return {?string}
*/
createMarkupForStyles: function(styles) {
var serialized = '';
for (var styleName in styles) {
if (!styles.hasOwnProperty(styleName)) {
continue;
}
var styleValue = styles[styleName];
if (styleValue != null) {
serialized += processStyleName(styleName) + ':';
serialized += dangerousStyleValue(styleName, styleValue) + ';';
}
}
return serialized || null;
},
/**
* Sets the value for multiple styles on a node. If a value is specified as
* '' (empty string), the corresponding style property will be unset.
*
* @param {DOMElement} node
* @param {object} styles
*/
setValueForStyles: function(node, styles) {
var style = node.style;
for (var styleName in styles) {
if (!styles.hasOwnProperty(styleName)) {
continue;
}
var styleValue = dangerousStyleValue(styleName, styles[styleName]);
if (styleValue) {
style[styleName] = styleValue;
} else {
var expansion = CSSProperty.shorthandPropertyExpansions[styleName];
if (expansion) {
// Shorthand property that IE8 won't like unsetting, so unset each
// component to placate it
for (var individualStyleName in expansion) {
style[individualStyleName] = '';
}
} else {
style[styleName] = '';
}
}
}
}
};
module.exports = CSSPropertyOperations;
},{"./CSSProperty":3,"./dangerousStyleValue":107,"./escapeTextForBrowser":109,"./hyphenate":120,"./memoizeStringOnly":129}],5:[function(require,module,exports){
/**
* 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 ChangeEventPlugin
*/
"use strict";
var EventConstants = require("./EventConstants");
var EventPluginHub = require("./EventPluginHub");
var EventPropagators = require("./EventPropagators");
var ExecutionEnvironment = require("./ExecutionEnvironment");
var ReactUpdates = require("./ReactUpdates");
var SyntheticEvent = require("./SyntheticEvent");
var isEventSupported = require("./isEventSupported");
var isTextInputElement = require("./isTextInputElement");
var keyOf = require("./keyOf");
var topLevelTypes = EventConstants.topLevelTypes;
var eventTypes = {
change: {
phasedRegistrationNames: {
bubbled: keyOf({onChange: null}),
captured: keyOf({onChangeCapture: null})
},
dependencies: [
topLevelTypes.topBlur,
topLevelTypes.topChange,
topLevelTypes.topClick,
topLevelTypes.topFocus,
topLevelTypes.topInput,
topLevelTypes.topKeyDown,
topLevelTypes.topKeyUp,
topLevelTypes.topSelectionChange
]
}
};
/**
* For IE shims
*/
var activeElement = null;
var activeElementID = null;
var activeElementValue = null;
var activeElementValueProp = null;
/**
* SECTION: handle `change` event
*/
function shouldUseChangeEvent(elem) {
return (
elem.nodeName === 'SELECT' ||
(elem.nodeName === 'INPUT' && elem.type === 'file')
);
}
var doesChangeEventBubble = false;
if (ExecutionEnvironment.canUseDOM) {
// See `handleChange` comment below
doesChangeEventBubble = isEventSupported('change') && (
!('documentMode' in document) || document.documentMode > 8
);
}
function manualDispatchChangeEvent(nativeEvent) {
var event = SyntheticEvent.getPooled(
eventTypes.change,
activeElementID,
nativeEvent
);
EventPropagators.accumulateTwoPhaseDispatches(event);
// If change and propertychange bubbled, we'd just bind to it like all the
// other events and have it go through ReactEventTopLevelCallback. Since it
// doesn't, we manually listen for the events and so we have to enqueue and
// process the abstract event manually.
//
// Batching is necessary here in order to ensure that all event handlers run
// before the next rerender (including event handlers attached to ancestor
// elements instead of directly on the input). Without this, controlled
// components don't work properly in conjunction with event bubbling because
// the component is rerendered and the value reverted before all the event
// handlers can run. See https://github.com/facebook/react/issues/708.
ReactUpdates.batchedUpdates(runEventInBatch, event);
}
function runEventInBatch(event) {
EventPluginHub.enqueueEvents(event);
EventPluginHub.processEventQueue();
}
function startWatchingForChangeEventIE8(target, targetID) {
activeElement = target;
activeElementID = targetID;
activeElement.attachEvent('onchange', manualDispatchChangeEvent);
}
function stopWatchingForChangeEventIE8() {
if (!activeElement) {
return;
}
activeElement.detachEvent('onchange', manualDispatchChangeEvent);
activeElement = null;
activeElementID = null;
}
function getTargetIDForChangeEvent(
topLevelType,
topLevelTarget,
topLevelTargetID) {
if (topLevelType === topLevelTypes.topChange) {
return topLevelTargetID;
}
}
function handleEventsForChangeEventIE8(
topLevelType,
topLevelTarget,
topLevelTargetID) {
if (topLevelType === topLevelTypes.topFocus) {
// stopWatching() should be a noop here but we call it just in case we
// missed a blur event somehow.
stopWatchingForChangeEventIE8();
startWatchingForChangeEventIE8(topLevelTarget, topLevelTargetID);
} else if (topLevelType === topLevelTypes.topBlur) {
stopWatchingForChangeEventIE8();
}
}
/**
* SECTION: handle `input` event
*/
var isInputEventSupported = false;
if (ExecutionEnvironment.canUseDOM) {
// IE9 claims to support the input event but fails to trigger it when
// deleting text, so we ignore its input events
isInputEventSupported = isEventSupported('input') && (
!('documentMode' in document) || document.documentMode > 9
);
}
/**
* (For old IE.) Replacement getter/setter for the `value` property that gets
* set on the active element.
*/
var newValueProp = {
get: function() {
return activeElementValueProp.get.call(this);
},
set: function(val) {
// Cast to a string so we can do equality checks.
activeElementValue = '' + val;
activeElementValueProp.set.call(this, val);
}
};
/**
* (For old IE.) Starts tracking propertychange events on the passed-in element
* and override the value property so that we can distinguish user events from
* value changes in JS.
*/
function startWatchingForValueChange(target, targetID) {
activeElement = target;
activeElementID = targetID;
activeElementValue = target.value;
activeElementValueProp = Object.getOwnPropertyDescriptor(
target.constructor.prototype,
'value'
);
Object.defineProperty(activeElement, 'value', newValueProp);
activeElement.attachEvent('onpropertychange', handlePropertyChange);
}
/**
* (For old IE.) Removes the event listeners from the currently-tracked element,
* if any exists.
*/
function stopWatchingForValueChange() {
if (!activeElement) {
return;
}
// delete restores the original property definition
delete activeElement.value;
activeElement.detachEvent('onpropertychange', handlePropertyChange);
activeElement = null;
activeElementID = null;
activeElementValue = null;
activeElementValueProp = null;
}
/**
* (For old IE.) Handles a propertychange event, sending a `change` event if
* the value of the active element has changed.
*/
function handlePropertyChange(nativeEvent) {
if (nativeEvent.propertyName !== 'value') {
return;
}
var value = nativeEvent.srcElement.value;
if (value === activeElementValue) {
return;
}
activeElementValue = value;
manualDispatchChangeEvent(nativeEvent);
}
/**
* If a `change` event should be fired, returns the target's ID.
*/
function getTargetIDForInputEvent(
topLevelType,
topLevelTarget,
topLevelTargetID) {
if (topLevelType === topLevelTypes.topInput) {
// In modern browsers (i.e., not IE8 or IE9), the input event is exactly
// what we want so fall through here and trigger an abstract event
return topLevelTargetID;
}
}
// For IE8 and IE9.
function handleEventsForInputEventIE(
topLevelType,
topLevelTarget,
topLevelTargetID) {
if (topLevelType === topLevelTypes.topFocus) {
// In IE8, we can capture almost all .value changes by adding a
// propertychange handler and looking for events with propertyName
// equal to 'value'
// In IE9, propertychange fires for most input events but is buggy and
// doesn't fire when text is deleted, but conveniently, selectionchange
// appears to fire in all of the remaining cases so we catch those and
// forward the event if the value has changed
// In either case, we don't want to call the event handler if the value
// is changed from JS so we redefine a setter for `.value` that updates
// our activeElementValue variable, allowing us to ignore those changes
//
// stopWatching() should be a noop here but we call it just in case we
// missed a blur event somehow.
stopWatchingForValueChange();
startWatchingForValueChange(topLevelTarget, topLevelTargetID);
} else if (topLevelType === topLevelTypes.topBlur) {
stopWatchingForValueChange();
}
}
// For IE8 and IE9.
function getTargetIDForInputEventIE(
topLevelType,
topLevelTarget,
topLevelTargetID) {
if (topLevelType === topLevelTypes.topSelectionChange ||
topLevelType === topLevelTypes.topKeyUp ||
topLevelType === topLevelTypes.topKeyDown) {
// On the selectionchange event, the target is just document which isn't
// helpful for us so just check activeElement instead.
//
// 99% of the time, keydown and keyup aren't necessary. IE8 fails to fire
// propertychange on the first input event after setting `value` from a
// script and fires only keydown, keypress, keyup. Catching keyup usually
// gets it and catching keydown lets us fire an event for the first
// keystroke if user does a key repeat (it'll be a little delayed: right
// before the second keystroke). Other input methods (e.g., paste) seem to
// fire selectionchange normally.
if (activeElement && activeElement.value !== activeElementValue) {
activeElementValue = activeElement.value;
return activeElementID;
}
}
}
/**
* SECTION: handle `click` event
*/
function shouldUseClickEvent(elem) {
// Use the `click` event to detect changes to checkbox and radio inputs.
// This approach works across all browsers, whereas `change` does not fire
// until `blur` in IE8.
return (
elem.nodeName === 'INPUT' &&
(elem.type === 'checkbox' || elem.type === 'radio')
);
}
function getTargetIDForClickEvent(
topLevelType,
topLevelTarget,
topLevelTargetID) {
if (topLevelType === topLevelTypes.topClick) {
return topLevelTargetID;
}
}
/**
* This plugin creates an `onChange` event that normalizes change events
* across form elements. This event fires at a time when it's possible to
* change the element's value without seeing a flicker.
*
* Supported elements are:
* - input (see `isTextInputElement`)
* - textarea
* - select
*/
var ChangeEventPlugin = {
eventTypes: eventTypes,
/**
* @param {string} topLevelType Record from `EventConstants`.
* @param {DOMEventTarget} topLevelTarget The listening component root node.
* @param {string} topLevelTargetID ID of `topLevelTarget`.
* @param {object} nativeEvent Native browser event.
* @return {*} An accumulation of synthetic events.
* @see {EventPluginHub.extractEvents}
*/
extractEvents: function(
topLevelType,
topLevelTarget,
topLevelTargetID,
nativeEvent) {
var getTargetIDFunc, handleEventFunc;
if (shouldUseChangeEvent(topLevelTarget)) {
if (doesChangeEventBubble) {
getTargetIDFunc = getTargetIDForChangeEvent;
} else {
handleEventFunc = handleEventsForChangeEventIE8;
}
} else if (isTextInputElement(topLevelTarget)) {
if (isInputEventSupported) {
getTargetIDFunc = getTargetIDForInputEvent;
} else {
getTargetIDFunc = getTargetIDForInputEventIE;
handleEventFunc = handleEventsForInputEventIE;
}
} else if (shouldUseClickEvent(topLevelTarget)) {
getTargetIDFunc = getTargetIDForClickEvent;
}
if (getTargetIDFunc) {
var targetID = getTargetIDFunc(
topLevelType,
topLevelTarget,
topLevelTargetID
);
if (targetID) {
var event = SyntheticEvent.getPooled(
eventTypes.change,
targetID,
nativeEvent
);
EventPropagators.accumulateTwoPhaseDispatches(event);
return event;
}
}
if (handleEventFunc) {
handleEventFunc(
topLevelType,
topLevelTarget,
topLevelTargetID
);
}
}
};
module.exports = ChangeEventPlugin;
},{"./EventConstants":15,"./EventPluginHub":17,"./EventPropagators":20,"./ExecutionEnvironment":21,"./ReactUpdates":80,"./SyntheticEvent":88,"./isEventSupported":122,"./isTextInputElement":124,"./keyOf":128}],6:[function(require,module,exports){
/**
* 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 ClientReactRootIndex
* @typechecks
*/
"use strict";
var nextReactRootIndex = 0;
var ClientReactRootIndex = {
createReactRootIndex: function() {
return nextReactRootIndex++;
}
};
module.exports = ClientReactRootIndex;
},{}],7:[function(require,module,exports){
/**
* 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 CompositionEventPlugin
* @typechecks static-only
*/
"use strict";
var EventConstants = require("./EventConstants");
var EventPropagators = require("./EventPropagators");
var ExecutionEnvironment = require("./ExecutionEnvironment");
var ReactInputSelection = require("./ReactInputSelection");
var SyntheticCompositionEvent = require("./SyntheticCompositionEvent");
var getTextContentAccessor = require("./getTextContentAccessor");
var keyOf = require("./keyOf");
var END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space
var START_KEYCODE = 229;
var useCompositionEvent = (
ExecutionEnvironment.canUseDOM &&
'CompositionEvent' in window
);
// In IE9+, we have access to composition events, but the data supplied
// by the native compositionend event may be incorrect. In Korean, for example,
// the compositionend event contains only one character regardless of
// how many characters have been composed since compositionstart.
// We therefore use the fallback data while still using the native
// events as triggers.
var useFallbackData = (
!useCompositionEvent ||
'documentMode' in document && document.documentMode > 8
);
var topLevelTypes = EventConstants.topLevelTypes;
var currentComposition = null;
// Events and their corresponding property names.
var eventTypes = {
compositionEnd: {
phasedRegistrationNames: {
bubbled: keyOf({onCompositionEnd: null}),
captured: keyOf({onCompositionEndCapture: null})
},
dependencies: [
topLevelTypes.topBlur,
topLevelTypes.topCompositionEnd,
topLevelTypes.topKeyDown,
topLevelTypes.topKeyPress,
topLevelTypes.topKeyUp,
topLevelTypes.topMouseDown
]
},
compositionStart: {
phasedRegistrationNames: {
bubbled: keyOf({onCompositionStart: null}),
captured: keyOf({onCompositionStartCapture: null})
},
dependencies: [
topLevelTypes.topBlur,
topLevelTypes.topCompositionStart,
topLevelTypes.topKeyDown,
topLevelTypes.topKeyPress,
topLevelTypes.topKeyUp,
topLevelTypes.topMouseDown
]
},
compositionUpdate: {
phasedRegistrationNames: {
bubbled: keyOf({onCompositionUpdate: null}),
captured: keyOf({onCompositionUpdateCapture: null})
},
dependencies: [
topLevelTypes.topBlur,
topLevelTypes.topCompositionUpdate,
topLevelTypes.topKeyDown,
topLevelTypes.topKeyPress,
topLevelTypes.topKeyUp,
topLevelTypes.topMouseDown
]
}
};
/**
* Translate native top level events into event types.
*
* @param {string} topLevelType
* @return {object}
*/
function getCompositionEventType(topLevelType) {
switch (topLevelType) {
case topLevelTypes.topCompositionStart:
return eventTypes.compositionStart;
case topLevelTypes.topCompositionEnd:
return eventTypes.compositionEnd;
case topLevelTypes.topCompositionUpdate:
return eventTypes.compositionUpdate;
}
}
/**
* Does our fallback best-guess model think this event signifies that
* composition has begun?
*
* @param {string} topLevelType
* @param {object} nativeEvent
* @return {boolean}
*/
function isFallbackStart(topLevelType, nativeEvent) {
return (
topLevelType === topLevelTypes.topKeyDown &&
nativeEvent.keyCode === START_KEYCODE
);
}
/**
* Does our fallback mode think that this event is the end of composition?
*
* @param {string} topLevelType
* @param {object} nativeEvent
* @return {boolean}
*/
function isFallbackEnd(topLevelType, nativeEvent) {
switch (topLevelType) {
case topLevelTypes.topKeyUp:
// Command keys insert or clear IME input.
return (END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1);
case topLevelTypes.topKeyDown:
// Expect IME keyCode on each keydown. If we get any other
// code we must have exited earlier.
return (nativeEvent.keyCode !== START_KEYCODE);
case topLevelTypes.topKeyPress:
case topLevelTypes.topMouseDown:
case topLevelTypes.topBlur:
// Events are not possible without cancelling IME.
return true;
default:
return false;
}
}
/**
* Helper class stores information about selection and document state
* so we can figure out what changed at a later date.
*
* @param {DOMEventTarget} root
*/
function FallbackCompositionState(root) {
this.root = root;
this.startSelection = ReactInputSelection.getSelection(root);
this.startValue = this.getText();
}
/**
* Get current text of input.
*
* @return {string}
*/
FallbackCompositionState.prototype.getText = function() {
return this.root.value || this.root[getTextContentAccessor()];
};
/**
* Text that has changed since the start of composition.
*
* @return {string}
*/
FallbackCompositionState.prototype.getData = function() {
var endValue = this.getText();
var prefixLength = this.startSelection.start;
var suffixLength = this.startValue.length - this.startSelection.end;
return endValue.substr(
prefixLength,
endValue.length - suffixLength - prefixLength
);
};
/**
* This plugin creates `onCompositionStart`, `onCompositionUpdate` and
* `onCompositionEnd` events on inputs, textareas and contentEditable
* nodes.
*/
var CompositionEventPlugin = {
eventTypes: eventTypes,
/**
* @param {string} topLevelType Record from `EventConstants`.
* @param {DOMEventTarget} topLevelTarget The listening component root node.
* @param {string} topLevelTargetID ID of `topLevelTarget`.
* @param {object} nativeEvent Native browser event.
* @return {*} An accumulation of synthetic events.
* @see {EventPluginHub.extractEvents}
*/
extractEvents: function(
topLevelType,
topLevelTarget,
topLevelTargetID,
nativeEvent) {
var eventType;
var data;
if (useCompositionEvent) {
eventType = getCompositionEventType(topLevelType);
} else if (!currentComposition) {
if (isFallbackStart(topLevelType, nativeEvent)) {
eventType = eventTypes.compositionStart;
}
} else if (isFallbackEnd(topLevelType, nativeEvent)) {
eventType = eventTypes.compositionEnd;
}
if (useFallbackData) {
// The current composition is stored statically and must not be
// overwritten while composition continues.
if (!currentComposition && eventType === eventTypes.compositionStart) {
currentComposition = new FallbackCompositionState(topLevelTarget);
} else if (eventType === eventTypes.compositionEnd) {
if (currentComposition) {
data = currentComposition.getData();
currentComposition = null;
}
}
}
if (eventType) {
var event = SyntheticCompositionEvent.getPooled(
eventType,
topLevelTargetID,
nativeEvent
);
if (data) {
// Inject data generated from fallback path into the synthetic event.
// This matches the property of native CompositionEventInterface.
event.data = data;
}
EventPropagators.accumulateTwoPhaseDispatches(event);
return event;
}
}
};
module.exports = CompositionEventPlugin;
},{"./EventConstants":15,"./EventPropagators":20,"./ExecutionEnvironment":21,"./ReactInputSelection":56,"./SyntheticCompositionEvent":86,"./getTextContentAccessor":118,"./keyOf":128}],8:[function(require,module,exports){
/**
* 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 DOMChildrenOperations
* @typechecks static-only
*/
"use strict";
var Danger = require("./Danger");
var ReactMultiChildUpdateTypes = require("./ReactMultiChildUpdateTypes");
var getTextContentAccessor = require("./getTextContentAccessor");
/**
* The DOM property to use when setting text content.
*
* @type {string}
* @private
*/
var textContentAccessor = getTextContentAccessor();
/**
* Inserts `childNode` as a child of `parentNode` at the `index`.
*
* @param {DOMElement} parentNode Parent node in which to insert.
* @param {DOMElement} childNode Child node to insert.
* @param {number} index Index at which to insert the child.
* @internal
*/
function insertChildAt(parentNode, childNode, index) {
var childNodes = parentNode.childNodes;
if (childNodes[index] === childNode) {
return;
}
// If `childNode` is already a child of `parentNode`, remove it so that
// computing `childNodes[index]` takes into account the removal.
if (childNode.parentNode === parentNode) {
parentNode.removeChild(childNode);
}
if (index >= childNodes.length) {
parentNode.appendChild(childNode);
} else {
parentNode.insertBefore(childNode, childNodes[index]);
}
}
/**
* Sets the text content of `node` to `text`.
*
* @param {DOMElement} node Node to change
* @param {string} text New text content
*/
var updateTextContent;
if (textContentAccessor === 'textContent') {
updateTextContent = function(node, text) {
node.textContent = text;
};
} else {
updateTextContent = function(node, text) {
// In order to preserve newlines correctly, we can't use .innerText to set
// the contents (see #1080), so we empty the element then append a text node
while (node.firstChild) {
node.removeChild(node.firstChild);
}
if (text) {
var doc = node.ownerDocument || document;
node.appendChild(doc.createTextNode(text));
}
};
}
/**
* Operations for updating with DOM children.
*/
var DOMChildrenOperations = {
dangerouslyReplaceNodeWithMarkup: Danger.dangerouslyReplaceNodeWithMarkup,
updateTextContent: updateTextContent,
/**
* Updates a component's children by processing a series of updates. The
* update configurations are each expected to have a `parentNode` property.
*
* @param {array<object>} updates List of update configurations.
* @param {array<string>} markupList List of markup strings.
* @internal
*/
processUpdates: function(updates, markupList) {
var update;
// Mapping from parent IDs to initial child orderings.
var initialChildren = null;
// List of children that will be moved or removed.
var updatedChildren = null;
for (var i = 0; update = updates[i]; i++) {
if (update.type === ReactMultiChildUpdateTypes.MOVE_EXISTING ||
update.type === ReactMultiChildUpdateTypes.REMOVE_NODE) {
var updatedIndex = update.fromIndex;
var updatedChild = update.parentNode.childNodes[updatedIndex];
var parentID = update.parentID;
initialChildren = initialChildren || {};
initialChildren[parentID] = initialChildren[parentID] || [];
initialChildren[parentID][updatedIndex] = updatedChild;
updatedChildren = updatedChildren || [];
updatedChildren.push(updatedChild);
}
}
var renderedMarkup = Danger.dangerouslyRenderMarkup(markupList);
// Remove updated children first so that `toIndex` is consistent.
if (updatedChildren) {
for (var j = 0; j < updatedChildren.length; j++) {
updatedChildren[j].parentNode.removeChild(updatedChildren[j]);
}
}
for (var k = 0; update = updates[k]; k++) {
switch (update.type) {
case ReactMultiChildUpdateTypes.INSERT_MARKUP:
insertChildAt(
update.parentNode,
renderedMarkup[update.markupIndex],
update.toIndex
);
break;
case ReactMultiChildUpdateTypes.MOVE_EXISTING:
insertChildAt(
update.parentNode,
initialChildren[update.parentID][update.fromIndex],
update.toIndex
);
break;
case ReactMultiChildUpdateTypes.TEXT_CONTENT:
updateTextContent(
update.parentNode,
update.textContent
);
break;
case ReactMultiChildUpdateTypes.REMOVE_NODE:
// Already removed by the for-loop above.
break;
}
}
}
};
module.exports = DOMChildrenOperations;
},{"./Danger":11,"./ReactMultiChildUpdateTypes":63,"./getTextContentAccessor":118}],9:[function(require,module,exports){
/**
* 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 DOMProperty
* @typechecks static-only
*/
/*jslint bitwise: true */
"use strict";
var invariant = require("./invariant");
var DOMPropertyInjection = {
/**
* Mapping from normalized, camelcased property names to a configuration that
* specifies how the associated DOM property should be accessed or rendered.
*/
MUST_USE_ATTRIBUTE: 0x1,
MUST_USE_PROPERTY: 0x2,
HAS_SIDE_EFFECTS: 0x4,
HAS_BOOLEAN_VALUE: 0x8,
HAS_POSITIVE_NUMERIC_VALUE: 0x10,
/**
* Inject some specialized knowledge about the DOM. This takes a config object
* with the following properties:
*
* isCustomAttribute: function that given an attribute name will return true
* if it can be inserted into the DOM verbatim. Useful for data-* or aria-*
* attributes where it's impossible to enumerate all of the possible
* attribute names,
*
* Properties: object mapping DOM property name to one of the
* DOMPropertyInjection constants or null. If your attribute isn't in here,
* it won't get written to the DOM.
*
* DOMAttributeNames: object mapping React attribute name to the DOM
* attribute name. Attribute names not specified use the **lowercase**
* normalized name.
*
* DOMPropertyNames: similar to DOMAttributeNames but for DOM properties.
* Property names not specified use the normalized name.
*
* DOMMutationMethods: Properties that require special mutation methods. If
* `value` is undefined, the mutation method should unset the property.
*
* @param {object} domPropertyConfig the config as described above.
*/
injectDOMPropertyConfig: function(domPropertyConfig) {
var Properties = domPropertyConfig.Properties || {};
var DOMAttributeNames = domPropertyConfig.DOMAttributeNames || {};
var DOMPropertyNames = domPropertyConfig.DOMPropertyNames || {};
var DOMMutationMethods = domPropertyConfig.DOMMutationMethods || {};
if (domPropertyConfig.isCustomAttribute) {
DOMProperty._isCustomAttributeFunctions.push(
domPropertyConfig.isCustomAttribute
);
}
for (var propName in Properties) {
("production" !== "development" ? invariant(
!DOMProperty.isStandardName[propName],
'injectDOMPropertyConfig(...): You\'re trying to inject DOM property ' +
'\'%s\' which has already been injected. You may be accidentally ' +
'injecting the same DOM property config twice, or you may be ' +
'injecting two configs that have conflicting property names.',
propName
) : invariant(!DOMProperty.isStandardName[propName]));
DOMProperty.isStandardName[propName] = true;
var lowerCased = propName.toLowerCase();
DOMProperty.getPossibleStandardName[lowerCased] = propName;
var attributeName = DOMAttributeNames[propName];
if (attributeName) {
DOMProperty.getPossibleStandardName[attributeName] = propName;
}
DOMProperty.getAttributeName[propName] = attributeName || lowerCased;
DOMProperty.getPropertyName[propName] =
DOMPropertyNames[propName] || propName;
var mutationMethod = DOMMutationMethods[propName];
if (mutationMethod) {
DOMProperty.getMutationMethod[propName] = mutationMethod;
}
var propConfig = Properties[propName];
DOMProperty.mustUseAttribute[propName] =
propConfig & DOMPropertyInjection.MUST_USE_ATTRIBUTE;
DOMProperty.mustUseProperty[propName] =
propConfig & DOMPropertyInjection.MUST_USE_PROPERTY;
DOMProperty.hasSideEffects[propName] =
propConfig & DOMPropertyInjection.HAS_SIDE_EFFECTS;
DOMProperty.hasBooleanValue[propName] =
propConfig & DOMPropertyInjection.HAS_BOOLEAN_VALUE;
DOMProperty.hasPositiveNumericValue[propName] =
propConfig & DOMPropertyInjection.HAS_POSITIVE_NUMERIC_VALUE;
("production" !== "development" ? invariant(
!DOMProperty.mustUseAttribute[propName] ||
!DOMProperty.mustUseProperty[propName],
'DOMProperty: Cannot require using both attribute and property: %s',
propName
) : invariant(!DOMProperty.mustUseAttribute[propName] ||
!DOMProperty.mustUseProperty[propName]));
("production" !== "development" ? invariant(
DOMProperty.mustUseProperty[propName] ||
!DOMProperty.hasSideEffects[propName],
'DOMProperty: Properties that have side effects must use property: %s',
propName
) : invariant(DOMProperty.mustUseProperty[propName] ||
!DOMProperty.hasSideEffects[propName]));
("production" !== "development" ? invariant(
!DOMProperty.hasBooleanValue[propName] ||
!DOMProperty.hasPositiveNumericValue[propName],
'DOMProperty: Cannot have both boolean and positive numeric value: %s',
propName
) : invariant(!DOMProperty.hasBooleanValue[propName] ||
!DOMProperty.hasPositiveNumericValue[propName]));
}
}
};
var defaultValueCache = {};
/**
* DOMProperty exports lookup objects that can be used like functions:
*
* > DOMProperty.isValid['id']
* true
* > DOMProperty.isValid['foobar']
* undefined
*
* Although this may be confusing, it performs better in general.
*
* @see http://jsperf.com/key-exists
* @see http://jsperf.com/key-missing
*/
var DOMProperty = {
ID_ATTRIBUTE_NAME: 'data-reactid',
/**
* Checks whether a property name is a standard property.
* @type {Object}
*/
isStandardName: {},
/**
* Mapping from lowercase property names to the properly cased version, used
* to warn in the case of missing properties.
* @type {Object}
*/
getPossibleStandardName: {},
/**
* Mapping from normalized names to attribute names that differ. Attribute
* names are used when rendering markup or with `*Attribute()`.
* @type {Object}
*/
getAttributeName: {},
/**
* Mapping from normalized names to properties on DOM node instances.
* (This includes properties that mutate due to external factors.)
* @type {Object}
*/
getPropertyName: {},
/**
* Mapping from normalized names to mutation methods. This will only exist if
* mutation cannot be set simply by the property or `setAttribute()`.
* @type {Object}
*/
getMutationMethod: {},
/**
* Whether the property must be accessed and mutated as an object property.
* @type {Object}
*/
mustUseAttribute: {},
/**
* Whether the property must be accessed and mutated using `*Attribute()`.
* (This includes anything that fails `<propName> in <element>`.)
* @type {Object}
*/
mustUseProperty: {},
/**
* Whether or not setting a value causes side effects such as triggering
* resources to be loaded or text selection changes. We must ensure that
* the value is only set if it has changed.
* @type {Object}
*/
hasSideEffects: {},
/**
* Whether the property should be removed when set to a falsey value.
* @type {Object}
*/
hasBooleanValue: {},
/**
* Whether the property must be positive numeric or parse as a positive
* numeric and should be removed when set to a falsey value.
* @type {Object}
*/
hasPositiveNumericValue: {},
/**
* All of the isCustomAttribute() functions that have been injected.
*/
_isCustomAttributeFunctions: [],
/**
* Checks whether a property name is a custom attribute.
* @method
*/
isCustomAttribute: function(attributeName) {
return DOMProperty._isCustomAttributeFunctions.some(
function(isCustomAttributeFn) {
return isCustomAttributeFn.call(null, attributeName);
}
);
},
/**
* Returns the default property value for a DOM property (i.e., not an
* attribute). Most default values are '' or false, but not all. Worse yet,
* some (in particular, `type`) vary depending on the type of element.
*
* TODO: Is it better to grab all the possible properties when creating an
* element to avoid having to create the same element twice?
*/
getDefaultValueForProperty: function(nodeName, prop) {
var nodeDefaults = defaultValueCache[nodeName];
var testElement;
if (!nodeDefaults) {
defaultValueCache[nodeName] = nodeDefaults = {};
}
if (!(prop in nodeDefaults)) {
testElement = document.createElement(nodeName);
nodeDefaults[prop] = testElement[prop];
}
return nodeDefaults[prop];
},
injection: DOMPropertyInjection
};
module.exports = DOMProperty;
},{"./invariant":121}],10:[function(require,module,exports){
/**
* 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 DOMPropertyOperations
* @typechecks static-only
*/
"use strict";
var DOMProperty = require("./DOMProperty");
var escapeTextForBrowser = require("./escapeTextForBrowser");
var memoizeStringOnly = require("./memoizeStringOnly");
function shouldIgnoreValue(name, value) {
return value == null ||
DOMProperty.hasBooleanValue[name] && !value ||
DOMProperty.hasPositiveNumericValue[name] && (isNaN(value) || value < 1);
}
var processAttributeNameAndPrefix = memoizeStringOnly(function(name) {
return escapeTextForBrowser(name) + '="';
});
if ("production" !== "development") {
var reactProps = {
children: true,
dangerouslySetInnerHTML: true,
key: true,
ref: true
};
var warnedProperties = {};
var warnUnknownProperty = function(name) {
if (reactProps[name] || warnedProperties[name]) {
return;
}
warnedProperties[name] = true;
var lowerCasedName = name.toLowerCase();
// data-* attributes should be lowercase; suggest the lowercase version
var standardName = DOMProperty.isCustomAttribute(lowerCasedName) ?
lowerCasedName : DOMProperty.getPossibleStandardName[lowerCasedName];
// For now, only warn when we have a suggested correction. This prevents
// logging too much when using transferPropsTo.
if (standardName != null) {
console.warn(
'Unknown DOM property ' + name + '. Did you mean ' + standardName + '?'
);
}
};
}
/**
* Operations for dealing with DOM properties.
*/
var DOMPropertyOperations = {
/**
* Creates markup for the ID property.
*
* @param {string} id Unescaped ID.
* @return {string} Markup string.
*/
createMarkupForID: function(id) {
return processAttributeNameAndPrefix(DOMProperty.ID_ATTRIBUTE_NAME) +
escapeTextForBrowser(id) + '"';
},
/**
* Creates markup for a property.
*
* @param {string} name
* @param {*} value
* @return {?string} Markup string, or null if the property was invalid.
*/
createMarkupForProperty: function(name, value) {
if (DOMProperty.isStandardName[name]) {
if (shouldIgnoreValue(name, value)) {
return '';
}
var attributeName = DOMProperty.getAttributeName[name];
if (DOMProperty.hasBooleanValue[name]) {
return escapeTextForBrowser(attributeName);
}
return processAttributeNameAndPrefix(attributeName) +
escapeTextForBrowser(value) + '"';
} else if (DOMProperty.isCustomAttribute(name)) {
if (value == null) {
return '';
}
return processAttributeNameAndPrefix(name) +
escapeTextForBrowser(value) + '"';
} else if ("production" !== "development") {
warnUnknownProperty(name);
}
return null;
},
/**
* Sets the value for a property on a node.
*
* @param {DOMElement} node
* @param {string} name
* @param {*} value
*/
setValueForProperty: function(node, name, value) {
if (DOMProperty.isStandardName[name]) {
var mutationMethod = DOMProperty.getMutationMethod[name];
if (mutationMethod) {
mutationMethod(node, value);
} else if (shouldIgnoreValue(name, value)) {
this.deleteValueForProperty(node, name);
} else if (DOMProperty.mustUseAttribute[name]) {
node.setAttribute(DOMProperty.getAttributeName[name], '' + value);
} else {
var propName = DOMProperty.getPropertyName[name];
if (!DOMProperty.hasSideEffects[name] || node[propName] !== value) {
node[propName] = value;
}
}
} else if (DOMProperty.isCustomAttribute(name)) {
if (value == null) {
node.removeAttribute(DOMProperty.getAttributeName[name]);
} else {
node.setAttribute(name, '' + value);
}
} else if ("production" !== "development") {
warnUnknownProperty(name);
}
},
/**
* Deletes the value for a property on a node.
*
* @param {DOMElement} node
* @param {string} name
*/
deleteValueForProperty: function(node, name) {
if (DOMProperty.isStandardName[name]) {
var mutationMethod = DOMProperty.getMutationMethod[name];
if (mutationMethod) {
mutationMethod(node, undefined);
} else if (DOMProperty.mustUseAttribute[name]) {
node.removeAttribute(DOMProperty.getAttributeName[name]);
} else {
var propName = DOMProperty.getPropertyName[name];
var defaultValue = DOMProperty.getDefaultValueForProperty(
node.nodeName,
name
);
if (!DOMProperty.hasSideEffects[name] ||
node[propName] !== defaultValue) {
node[propName] = defaultValue;
}
}
} else if (DOMProperty.isCustomAttribute(name)) {
node.removeAttribute(name);
} else if ("production" !== "development") {
warnUnknownProperty(name);
}
}
};
module.exports = DOMPropertyOperations;
},{"./DOMProperty":9,"./escapeTextForBrowser":109,"./memoizeStringOnly":129}],11:[function(require,module,exports){
/**
* 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" !== "development" ? 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" !== "development" ? 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" !== "development" ? 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" !== "development") {
console.error(
"Danger: Discarding unexpected node:",
renderNode
);
}
}
}
// Although resultList was populated out of order, it should now be a dense
// array.
("production" !== "development" ? invariant(
resultListAssignmentCount === resultList.length,
'Danger: Did not assign to every index of resultList.'
) : invariant(resultListAssignmentCount === resultList.length));
("production" !== "development" ? 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" !== "development" ? 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" !== "development" ? invariant(markup, 'dangerouslyReplaceNodeWithMarkup(...): Missing markup.') : invariant(markup));
("production" !== "development" ? 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;
},{"./ExecutionEnvironment":21,"./createNodesFromMarkup":104,"./emptyFunction":108,"./getMarkupWrap":115,"./invariant":121}],12:[function(require,module,exports){
/**
* 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 DefaultDOMPropertyConfig
*/
/*jslint bitwise: true*/
"use strict";
var DOMProperty = require("./DOMProperty");
var MUST_USE_ATTRIBUTE = DOMProperty.injection.MUST_USE_ATTRIBUTE;
var MUST_USE_PROPERTY = DOMProperty.injection.MUST_USE_PROPERTY;
var HAS_BOOLEAN_VALUE = DOMProperty.injection.HAS_BOOLEAN_VALUE;
var HAS_SIDE_EFFECTS = DOMProperty.injection.HAS_SIDE_EFFECTS;
var HAS_POSITIVE_NUMERIC_VALUE =
DOMProperty.injection.HAS_POSITIVE_NUMERIC_VALUE;
var DefaultDOMPropertyConfig = {
isCustomAttribute: RegExp.prototype.test.bind(
/^(data|aria)-[a-z_][a-z\d_.\-]*$/
),
Properties: {
/**
* Standard Properties
*/
accept: null,
accessKey: null,
action: null,
allowFullScreen: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE,
allowTransparency: MUST_USE_ATTRIBUTE,
alt: null,
async: HAS_BOOLEAN_VALUE,
autoComplete: null,
// autoFocus is polyfilled/normalized by AutoFocusMixin
// autoFocus: HAS_BOOLEAN_VALUE,
autoPlay: HAS_BOOLEAN_VALUE,
cellPadding: null,
cellSpacing: null,
charSet: MUST_USE_ATTRIBUTE,
checked: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
className: MUST_USE_PROPERTY,
cols: MUST_USE_ATTRIBUTE | HAS_POSITIVE_NUMERIC_VALUE,
colSpan: null,
content: null,
contentEditable: null,
contextMenu: MUST_USE_ATTRIBUTE,
controls: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
crossOrigin: null,
data: null, // For `<object />` acts as `src`.
dateTime: MUST_USE_ATTRIBUTE,
defer: HAS_BOOLEAN_VALUE,
dir: null,
disabled: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE,
download: null,
draggable: null,
encType: null,
form: MUST_USE_ATTRIBUTE,
formNoValidate: HAS_BOOLEAN_VALUE,
frameBorder: MUST_USE_ATTRIBUTE,
height: MUST_USE_ATTRIBUTE,
hidden: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE,
href: null,
hrefLang: null,
htmlFor: null,
httpEquiv: null,
icon: null,
id: MUST_USE_PROPERTY,
label: null,
lang: null,
list: null,
loop: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
max: null,
maxLength: MUST_USE_ATTRIBUTE,
mediaGroup: null,
method: null,
min: null,
multiple: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
muted: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
name: null,
noValidate: HAS_BOOLEAN_VALUE,
pattern: null,
placeholder: null,
poster: null,
preload: null,
radioGroup: null,
readOnly: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
rel: null,
required: HAS_BOOLEAN_VALUE,
role: MUST_USE_ATTRIBUTE,
rows: MUST_USE_ATTRIBUTE | HAS_POSITIVE_NUMERIC_VALUE,
rowSpan: null,
sandbox: null,
scope: null,
scrollLeft: MUST_USE_PROPERTY,
scrollTop: MUST_USE_PROPERTY,
seamless: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE,
selected: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
size: MUST_USE_ATTRIBUTE | HAS_POSITIVE_NUMERIC_VALUE,
span: HAS_POSITIVE_NUMERIC_VALUE,
spellCheck: null,
src: null,
srcDoc: MUST_USE_PROPERTY,
step: null,
style: null,
tabIndex: null,
target: null,
title: null,
type: null,
value: MUST_USE_PROPERTY | HAS_SIDE_EFFECTS,
width: MUST_USE_ATTRIBUTE,
wmode: MUST_USE_ATTRIBUTE,
/**
* Non-standard Properties
*/
autoCapitalize: null, // Supported in Mobile Safari for keyboard hints
autoCorrect: null, // Supported in Mobile Safari for keyboard hints
property: null, // Supports OG in meta tags
/**
* SVG Properties
*/
cx: MUST_USE_ATTRIBUTE,
cy: MUST_USE_ATTRIBUTE,
d: MUST_USE_ATTRIBUTE,
fill: MUST_USE_ATTRIBUTE,
fx: MUST_USE_ATTRIBUTE,
fy: MUST_USE_ATTRIBUTE,
gradientTransform: MUST_USE_ATTRIBUTE,
gradientUnits: MUST_USE_ATTRIBUTE,
offset: MUST_USE_ATTRIBUTE,
points: MUST_USE_ATTRIBUTE,
r: MUST_USE_ATTRIBUTE,
rx: MUST_USE_ATTRIBUTE,
ry: MUST_USE_ATTRIBUTE,
spreadMethod: MUST_USE_ATTRIBUTE,
stopColor: MUST_USE_ATTRIBUTE,
stopOpacity: MUST_USE_ATTRIBUTE,
stroke: MUST_USE_ATTRIBUTE,
strokeLinecap: MUST_USE_ATTRIBUTE,
strokeWidth: MUST_USE_ATTRIBUTE,
transform: MUST_USE_ATTRIBUTE,
version: MUST_USE_ATTRIBUTE,
viewBox: MUST_USE_ATTRIBUTE,
x1: MUST_USE_ATTRIBUTE,
x2: MUST_USE_ATTRIBUTE,
x: MUST_USE_ATTRIBUTE,
y1: MUST_USE_ATTRIBUTE,
y2: MUST_USE_ATTRIBUTE,
y: MUST_USE_ATTRIBUTE
},
DOMAttributeNames: {
className: 'class',
gradientTransform: 'gradientTransform',
gradientUnits: 'gradientUnits',
htmlFor: 'for',
spreadMethod: 'spreadMethod',
stopColor: 'stop-color',
stopOpacity: 'stop-opacity',
strokeLinecap: 'stroke-linecap',
strokeWidth: 'stroke-width',
viewBox: 'viewBox'
},
DOMPropertyNames: {
autoCapitalize: 'autocapitalize',
autoComplete: 'autocomplete',
autoCorrect: 'autocorrect',
autoFocus: 'autofocus',
autoPlay: 'autoplay',
encType: 'enctype',
hrefLang: 'hreflang',
radioGroup: 'radiogroup',
spellCheck: 'spellcheck',
srcDoc: 'srcdoc'
},
DOMMutationMethods: {
/**
* Setting `className` to null may cause it to be set to the string "null".
*
* @param {DOMElement} node
* @param {*} value
*/
className: function(node, value) {
node.className = value || '';
}
}
};
module.exports = DefaultDOMPropertyConfig;
},{"./DOMProperty":9}],13:[function(require,module,exports){
/**
* 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 DefaultEventPluginOrder
*/
"use strict";
var keyOf = require("./keyOf");
/**
* Module that is injectable into `EventPluginHub`, that specifies a
* deterministic ordering of `EventPlugin`s. A convenient way to reason about
* plugins, without having to package every one of them. This is better than
* having plugins be ordered in the same order that they are injected because
* that ordering would be influenced by the packaging order.
* `ResponderEventPlugin` must occur before `SimpleEventPlugin` so that
* preventing default on events is convenient in `SimpleEventPlugin` handlers.
*/
var DefaultEventPluginOrder = [
keyOf({ResponderEventPlugin: null}),
keyOf({SimpleEventPlugin: null}),
keyOf({TapEventPlugin: null}),
keyOf({EnterLeaveEventPlugin: null}),
keyOf({ChangeEventPlugin: null}),
keyOf({SelectEventPlugin: null}),
keyOf({CompositionEventPlugin: null}),
keyOf({AnalyticsEventPlugin: null}),
keyOf({MobileSafariClickEventPlugin: null})
];
module.exports = DefaultEventPluginOrder;
},{"./keyOf":128}],14:[function(require,module,exports){
/**
* 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 EnterLeaveEventPlugin
* @typechecks static-only
*/
"use strict";
var EventConstants = require("./EventConstants");
var EventPropagators = require("./EventPropagators");
var SyntheticMouseEvent = require("./SyntheticMouseEvent");
var ReactMount = require("./ReactMount");
var keyOf = require("./keyOf");
var topLevelTypes = EventConstants.topLevelTypes;
var getFirstReactDOM = ReactMount.getFirstReactDOM;
var eventTypes = {
mouseEnter: {
registrationName: keyOf({onMouseEnter: null}),
dependencies: [
topLevelTypes.topMouseOut,
topLevelTypes.topMouseOver
]
},
mouseLeave: {
registrationName: keyOf({onMouseLeave: null}),
dependencies: [
topLevelTypes.topMouseOut,
topLevelTypes.topMouseOver
]
}
};
var extractedEvents = [null, null];
var EnterLeaveEventPlugin = {
eventTypes: eventTypes,
/**
* For almost every interaction we care about, there will be both a top-level
* `mouseover` and `mouseout` event that occurs. Only use `mouseout` so that
* we do not extract duplicate events. However, moving the mouse into the
* browser from outside will not fire a `mouseout` event. In this case, we use
* the `mouseover` top-level event.
*
* @param {string} topLevelType Record from `EventConstants`.
* @param {DOMEventTarget} topLevelTarget The listening component root node.
* @param {string} topLevelTargetID ID of `topLevelTarget`.
* @param {object} nativeEvent Native browser event.
* @return {*} An accumulation of synthetic events.
* @see {EventPluginHub.extractEvents}
*/
extractEvents: function(
topLevelType,
topLevelTarget,
topLevelTargetID,
nativeEvent) {
if (topLevelType === topLevelTypes.topMouseOver &&
(nativeEvent.relatedTarget || nativeEvent.fromElement)) {
return null;
}
if (topLevelType !== topLevelTypes.topMouseOut &&
topLevelType !== topLevelTypes.topMouseOver) {
// Must not be a mouse in or mouse out - ignoring.
return null;
}
var win;
if (topLevelTarget.window === topLevelTarget) {
// `topLevelTarget` is probably a window object.
win = topLevelTarget;
} else {
// TODO: Figure out why `ownerDocument` is sometimes undefined in IE8.
var doc = topLevelTarget.ownerDocument;
if (doc) {
win = doc.defaultView || doc.parentWindow;
} else {
win = window;
}
}
var from, to;
if (topLevelType === topLevelTypes.topMouseOut) {
from = topLevelTarget;
to =
getFirstReactDOM(nativeEvent.relatedTarget || nativeEvent.toElement) ||
win;
} else {
from = win;
to = topLevelTarget;
}
if (from === to) {
// Nothing pertains to our managed components.
return null;
}
var fromID = from ? ReactMount.getID(from) : '';
var toID = to ? ReactMount.getID(to) : '';
var leave = SyntheticMouseEvent.getPooled(
eventTypes.mouseLeave,
fromID,
nativeEvent
);
leave.type = 'mouseleave';
leave.target = from;
leave.relatedTarget = to;
var enter = SyntheticMouseEvent.getPooled(
eventTypes.mouseEnter,
toID,
nativeEvent
);
enter.type = 'mouseenter';
enter.target = to;
enter.relatedTarget = from;
EventPropagators.accumulateEnterLeaveDispatches(leave, enter, fromID, toID);
extractedEvents[0] = leave;
extractedEvents[1] = enter;
return extractedEvents;
}
};
module.exports = EnterLeaveEventPlugin;
},{"./EventConstants":15,"./EventPropagators":20,"./ReactMount":60,"./SyntheticMouseEvent":91,"./keyOf":128}],15:[function(require,module,exports){
/**
* 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 EventConstants
*/
"use strict";
var keyMirror = require("./keyMirror");
var PropagationPhases = keyMirror({bubbled: null, captured: null});
/**
* Types of raw signals from the browser caught at the top level.
*/
var topLevelTypes = keyMirror({
topBlur: null,
topChange: null,
topClick: null,
topCompositionEnd: null,
topCompositionStart: null,
topCompositionUpdate: null,
topContextMenu: null,
topCopy: null,
topCut: null,
topDoubleClick: null,
topDrag: null,
topDragEnd: null,
topDragEnter: null,
topDragExit: null,
topDragLeave: null,
topDragOver: null,
topDragStart: null,
topDrop: null,
topError: null,
topFocus: null,
topInput: null,
topKeyDown: null,
topKeyPress: null,
topKeyUp: null,
topLoad: null,
topMouseDown: null,
topMouseMove: null,
topMouseOut: null,
topMouseOver: null,
topMouseUp: null,
topPaste: null,
topReset: null,
topScroll: null,
topSelectionChange: null,
topSubmit: null,
topTouchCancel: null,
topTouchEnd: null,
topTouchMove: null,
topTouchStart: null,
topWheel: null
});
var EventConstants = {
topLevelTypes: topLevelTypes,
PropagationPhases: PropagationPhases
};
module.exports = EventConstants;
},{"./keyMirror":127}],16:[function(require,module,exports){
/**
* @providesModule EventListener
*/
var emptyFunction = require("./emptyFunction");
/**
* Upstream version of event listener. Does not take into account specific
* nature of platform.
*/
var EventListener = {
/**
* Listen to DOM events during the bubble phase.
*
* @param {DOMEventTarget} target DOM element to register listener on.
* @param {string} eventType Event type, e.g. 'click' or 'mouseover'.
* @param {function} callback Callback function.
* @return {object} Object with a `remove` method.
*/
listen: function(target, eventType, callback) {
if (target.addEventListener) {
target.addEventListener(eventType, callback, false);
return {
remove: function() {
target.removeEventListener(eventType, callback, false);
}
};
} else if (target.attachEvent) {
target.attachEvent('on' + eventType, callback);
return {
remove: function() {
target.detachEvent(eventType, callback);
}
};
}
},
/**
* Listen to DOM events during the capture phase.
*
* @param {DOMEventTarget} target DOM element to register listener on.
* @param {string} eventType Event type, e.g. 'click' or 'mouseover'.
* @param {function} callback Callback function.
* @return {object} Object with a `remove` method.
*/
capture: function(target, eventType, callback) {
if (!target.addEventListener) {
if ("production" !== "development") {
console.error(
'Attempted to listen to events during the capture phase on a ' +
'browser that does not support the capture phase. Your application ' +
'will not receive some events.'
);
}
return {
remove: emptyFunction
};
} else {
target.addEventListener(eventType, callback, true);
return {
remove: function() {
target.removeEventListener(eventType, callback, true);
}
};
}
}
};
module.exports = EventListener;
},{"./emptyFunction":108}],17:[function(require,module,exports){
/**
* 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 EventPluginHub
*/
"use strict";
var EventPluginRegistry = require("./EventPluginRegistry");
var EventPluginUtils = require("./EventPluginUtils");
var ExecutionEnvironment = require("./ExecutionEnvironment");
var accumulate = require("./accumulate");
var forEachAccumulated = require("./forEachAccumulated");
var invariant = require("./invariant");
var isEventSupported = require("./isEventSupported");
/**
* Internal store for event listeners
*/
var listenerBank = {};
/**
* Internal queue of events that have accumulated their dispatches and are
* waiting to have their dispatches executed.
*/
var eventQueue = null;
/**
* Dispatches an event and releases it back into the pool, unless persistent.
*
* @param {?object} event Synthetic event to be dispatched.
* @private
*/
var executeDispatchesAndRelease = function(event) {
if (event) {
var executeDispatch = EventPluginUtils.executeDispatch;
// Plugins can provide custom behavior when dispatching events.
var PluginModule = EventPluginRegistry.getPluginModuleForEvent(event);
if (PluginModule && PluginModule.executeDispatch) {
executeDispatch = PluginModule.executeDispatch;
}
EventPluginUtils.executeDispatchesInOrder(event, executeDispatch);
if (!event.isPersistent()) {
event.constructor.release(event);
}
}
};
/**
* - `InstanceHandle`: [required] Module that performs logical traversals of DOM
* hierarchy given ids of the logical DOM elements involved.
*/
var InstanceHandle = null;
function validateInstanceHandle() {
var invalid = !InstanceHandle||
!InstanceHandle.traverseTwoPhase ||
!InstanceHandle.traverseEnterLeave;
if (invalid) {
throw new Error('InstanceHandle not injected before use!');
}
}
/**
* This is a unified interface for event plugins to be installed and configured.
*
* Event plugins can implement the following properties:
*
* `extractEvents` {function(string, DOMEventTarget, string, object): *}
* Required. When a top-level event is fired, this method is expected to
* extract synthetic events that will in turn be queued and dispatched.
*
* `eventTypes` {object}
* Optional, plugins that fire events must publish a mapping of registration
* names that are used to register listeners. Values of this mapping must
* be objects that contain `registrationName` or `phasedRegistrationNames`.
*
* `executeDispatch` {function(object, function, string)}
* Optional, allows plugins to override how an event gets dispatched. By
* default, the listener is simply invoked.
*
* Each plugin that is injected into `EventsPluginHub` is immediately operable.
*
* @public
*/
var EventPluginHub = {
/**
* Methods for injecting dependencies.
*/
injection: {
/**
* @param {object} InjectedMount
* @public
*/
injectMount: EventPluginUtils.injection.injectMount,
/**
* @param {object} InjectedInstanceHandle
* @public
*/
injectInstanceHandle: function(InjectedInstanceHandle) {
InstanceHandle = InjectedInstanceHandle;
if ("production" !== "development") {
validateInstanceHandle();
}
},
getInstanceHandle: function() {
if ("production" !== "development") {
validateInstanceHandle();
}
return InstanceHandle;
},
/**
* @param {array} InjectedEventPluginOrder
* @public
*/
injectEventPluginOrder: EventPluginRegistry.injectEventPluginOrder,
/**
* @param {object} injectedNamesToPlugins Map from names to plugin modules.
*/
injectEventPluginsByName: EventPluginRegistry.injectEventPluginsByName
},
eventNameDispatchConfigs: EventPluginRegistry.eventNameDispatchConfigs,
registrationNameModules: EventPluginRegistry.registrationNameModules,
/**
* Stores `listener` at `listenerBank[registrationName][id]`. Is idempotent.
*
* @param {string} id ID of the DOM element.
* @param {string} registrationName Name of listener (e.g. `onClick`).
* @param {?function} listener The callback to store.
*/
putListener: function(id, registrationName, listener) {
("production" !== "development" ? invariant(
ExecutionEnvironment.canUseDOM,
'Cannot call putListener() in a non-DOM environment.'
) : invariant(ExecutionEnvironment.canUseDOM));
("production" !== "development" ? invariant(
!listener || typeof listener === 'function',
'Expected %s listener to be a function, instead got type %s',
registrationName, typeof listener
) : invariant(!listener || typeof listener === 'function'));
if ("production" !== "development") {
// IE8 has no API for event capturing and the `onScroll` event doesn't
// bubble.
if (registrationName === 'onScroll' &&
!isEventSupported('scroll', true)) {
console.warn('This browser doesn\'t support the `onScroll` event');
}
}
var bankForRegistrationName =
listenerBank[registrationName] || (listenerBank[registrationName] = {});
bankForRegistrationName[id] = listener;
},
/**
* @param {string} id ID of the DOM element.
* @param {string} registrationName Name of listener (e.g. `onClick`).
* @return {?function} The stored callback.
*/
getListener: function(id, registrationName) {
var bankForRegistrationName = listenerBank[registrationName];
return bankForRegistrationName && bankForRegistrationName[id];
},
/**
* Deletes a listener from the registration bank.
*
* @param {string} id ID of the DOM element.
* @param {string} registrationName Name of listener (e.g. `onClick`).
*/
deleteListener: function(id, registrationName) {
var bankForRegistrationName = listenerBank[registrationName];
if (bankForRegistrationName) {
delete bankForRegistrationName[id];
}
},
/**
* Deletes all listeners for the DOM element with the supplied ID.
*
* @param {string} id ID of the DOM element.
*/
deleteAllListeners: function(id) {
for (var registrationName in listenerBank) {
delete listenerBank[registrationName][id];
}
},
/**
* Allows registered plugins an opportunity to extract events from top-level
* native browser events.
*
* @param {string} topLevelType Record from `EventConstants`.
* @param {DOMEventTarget} topLevelTarget The listening component root node.
* @param {string} topLevelTargetID ID of `topLevelTarget`.
* @param {object} nativeEvent Native browser event.
* @return {*} An accumulation of synthetic events.
* @internal
*/
extractEvents: function(
topLevelType,
topLevelTarget,
topLevelTargetID,
nativeEvent) {
var events;
var plugins = EventPluginRegistry.plugins;
for (var i = 0, l = plugins.length; i < l; i++) {
// Not every plugin in the ordering may be loaded at runtime.
var possiblePlugin = plugins[i];
if (possiblePlugin) {
var extractedEvents = possiblePlugin.extractEvents(
topLevelType,
topLevelTarget,
topLevelTargetID,
nativeEvent
);
if (extractedEvents) {
events = accumulate(events, extractedEvents);
}
}
}
return events;
},
/**
* Enqueues a synthetic event that should be dispatched when
* `processEventQueue` is invoked.
*
* @param {*} events An accumulation of synthetic events.
* @internal
*/
enqueueEvents: function(events) {
if (events) {
eventQueue = accumulate(eventQueue, events);
}
},
/**
* Dispatches all synthetic events on the event queue.
*
* @internal
*/
processEventQueue: function() {
// Set `eventQueue` to null before processing it so that we can tell if more
// events get enqueued while processing.
var processingEventQueue = eventQueue;
eventQueue = null;
forEachAccumulated(processingEventQueue, executeDispatchesAndRelease);
("production" !== "development" ? invariant(
!eventQueue,
'processEventQueue(): Additional events were enqueued while processing ' +
'an event queue. Support for this has not yet been implemented.'
) : invariant(!eventQueue));
},
/**
* These are needed for tests only. Do not use!
*/
__purge: function() {
listenerBank = {};
},
__getListenerBank: function() {
return listenerBank;
}
};
module.exports = EventPluginHub;
},{"./EventPluginRegistry":18,"./EventPluginUtils":19,"./ExecutionEnvironment":21,"./accumulate":97,"./forEachAccumulated":111,"./invariant":121,"./isEventSupported":122}],18:[function(require,module,exports){
/**
* 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 EventPluginRegistry
* @typechecks static-only
*/
"use strict";
var invariant = require("./invariant");
/**
* Injectable ordering of event plugins.
*/
var EventPluginOrder = null;
/**
* Injectable mapping from names to event plugin modules.
*/
var namesToPlugins = {};
/**
* Recomputes the plugin list using the injected plugins and plugin ordering.
*
* @private
*/
function recomputePluginOrdering() {
if (!EventPluginOrder) {
// Wait until an `EventPluginOrder` is injected.
return;
}
for (var pluginName in namesToPlugins) {
var PluginModule = namesToPlugins[pluginName];
var pluginIndex = EventPluginOrder.indexOf(pluginName);
("production" !== "development" ? invariant(
pluginIndex > -1,
'EventPluginRegistry: Cannot inject event plugins that do not exist in ' +
'the plugin ordering, `%s`.',
pluginName
) : invariant(pluginIndex > -1));
if (EventPluginRegistry.plugins[pluginIndex]) {
continue;
}
("production" !== "development" ? invariant(
PluginModule.extractEvents,
'EventPluginRegistry: Event plugins must implement an `extractEvents` ' +
'method, but `%s` does not.',
pluginName
) : invariant(PluginModule.extractEvents));
EventPluginRegistry.plugins[pluginIndex] = PluginModule;
var publishedEvents = PluginModule.eventTypes;
for (var eventName in publishedEvents) {
("production" !== "development" ? invariant(
publishEventForPlugin(
publishedEvents[eventName],
PluginModule,
eventName
),
'EventPluginRegistry: Failed to publish event `%s` for plugin `%s`.',
eventName,
pluginName
) : invariant(publishEventForPlugin(
publishedEvents[eventName],
PluginModule,
eventName
)));
}
}
}
/**
* Publishes an event so that it can be dispatched by the supplied plugin.
*
* @param {object} dispatchConfig Dispatch configuration for the event.
* @param {object} PluginModule Plugin publishing the event.
* @return {boolean} True if the event was successfully published.
* @private
*/
function publishEventForPlugin(dispatchConfig, PluginModule, eventName) {
("production" !== "development" ? invariant(
!EventPluginRegistry.eventNameDispatchConfigs[eventName],
'EventPluginHub: More than one plugin attempted to publish the same ' +
'event name, `%s`.',
eventName
) : invariant(!EventPluginRegistry.eventNameDispatchConfigs[eventName]));
EventPluginRegistry.eventNameDispatchConfigs[eventName] = dispatchConfig;
var phasedRegistrationNames = dispatchConfig.phasedRegistrationNames;
if (phasedRegistrationNames) {
for (var phaseName in phasedRegistrationNames) {
if (phasedRegistrationNames.hasOwnProperty(phaseName)) {
var phasedRegistrationName = phasedRegistrationNames[phaseName];
publishRegistrationName(
phasedRegistrationName,
PluginModule,
eventName
);
}
}
return true;
} else if (dispatchConfig.registrationName) {
publishRegistrationName(
dispatchConfig.registrationName,
PluginModule,
eventName
);
return true;
}
return false;
}
/**
* Publishes a registration name that is used to identify dispatched events and
* can be used with `EventPluginHub.putListener` to register listeners.
*
* @param {string} registrationName Registration name to add.
* @param {object} PluginModule Plugin publishing the event.
* @private
*/
function publishRegistrationName(registrationName, PluginModule, eventName) {
("production" !== "development" ? invariant(
!EventPluginRegistry.registrationNameModules[registrationName],
'EventPluginHub: More than one plugin attempted to publish the same ' +
'registration name, `%s`.',
registrationName
) : invariant(!EventPluginRegistry.registrationNameModules[registrationName]));
EventPluginRegistry.registrationNameModules[registrationName] = PluginModule;
EventPluginRegistry.registrationNameDependencies[registrationName] =
PluginModule.eventTypes[eventName].dependencies;
}
/**
* Registers plugins so that they can extract and dispatch events.
*
* @see {EventPluginHub}
*/
var EventPluginRegistry = {
/**
* Ordered list of injected plugins.
*/
plugins: [],
/**
* Mapping from event name to dispatch config
*/
eventNameDispatchConfigs: {},
/**
* Mapping from registration name to plugin module
*/
registrationNameModules: {},
/**
* Mapping from registration name to event name
*/
registrationNameDependencies: {},
/**
* Injects an ordering of plugins (by plugin name). This allows the ordering
* to be decoupled from injection of the actual plugins so that ordering is
* always deterministic regardless of packaging, on-the-fly injection, etc.
*
* @param {array} InjectedEventPluginOrder
* @internal
* @see {EventPluginHub.injection.injectEventPluginOrder}
*/
injectEventPluginOrder: function(InjectedEventPluginOrder) {
("production" !== "development" ? invariant(
!EventPluginOrder,
'EventPluginRegistry: Cannot inject event plugin ordering more than once.'
) : invariant(!EventPluginOrder));
// Clone the ordering so it cannot be dynamically mutated.
EventPluginOrder = Array.prototype.slice.call(InjectedEventPluginOrder);
recomputePluginOrdering();
},
/**
* Injects plugins to be used by `EventPluginHub`. The plugin names must be
* in the ordering injected by `injectEventPluginOrder`.
*
* Plugins can be injected as part of page initialization or on-the-fly.
*
* @param {object} injectedNamesToPlugins Map from names to plugin modules.
* @internal
* @see {EventPluginHub.injection.injectEventPluginsByName}
*/
injectEventPluginsByName: function(injectedNamesToPlugins) {
var isOrderingDirty = false;
for (var pluginName in injectedNamesToPlugins) {
if (!injectedNamesToPlugins.hasOwnProperty(pluginName)) {
continue;
}
var PluginModule = injectedNamesToPlugins[pluginName];
if (namesToPlugins[pluginName] !== PluginModule) {
("production" !== "development" ? invariant(
!namesToPlugins[pluginName],
'EventPluginRegistry: Cannot inject two different event plugins ' +
'using the same name, `%s`.',
pluginName
) : invariant(!namesToPlugins[pluginName]));
namesToPlugins[pluginName] = PluginModule;
isOrderingDirty = true;
}
}
if (isOrderingDirty) {
recomputePluginOrdering();
}
},
/**
* Looks up the plugin for the supplied event.
*
* @param {object} event A synthetic event.
* @return {?object} The plugin that created the supplied event.
* @internal
*/
getPluginModuleForEvent: function(event) {
var dispatchConfig = event.dispatchConfig;
if (dispatchConfig.registrationName) {
return EventPluginRegistry.registrationNameModules[
dispatchConfig.registrationName
] || null;
}
for (var phase in dispatchConfig.phasedRegistrationNames) {
if (!dispatchConfig.phasedRegistrationNames.hasOwnProperty(phase)) {
continue;
}
var PluginModule = EventPluginRegistry.registrationNameModules[
dispatchConfig.phasedRegistrationNames[phase]
];
if (PluginModule) {
return PluginModule;
}
}
return null;
},
/**
* Exposed for unit testing.
* @private
*/
_resetEventPlugins: function() {
EventPluginOrder = null;
for (var pluginName in namesToPlugins) {
if (namesToPlugins.hasOwnProperty(pluginName)) {
delete namesToPlugins[pluginName];
}
}
EventPluginRegistry.plugins.length = 0;
var eventNameDispatchConfigs = EventPluginRegistry.eventNameDispatchConfigs;
for (var eventName in eventNameDispatchConfigs) {
if (eventNameDispatchConfigs.hasOwnProperty(eventName)) {
delete eventNameDispatchConfigs[eventName];
}
}
var registrationNameModules = EventPluginRegistry.registrationNameModules;
for (var registrationName in registrationNameModules) {
if (registrationNameModules.hasOwnProperty(registrationName)) {
delete registrationNameModules[registrationName];
}
}
}
};
module.exports = EventPluginRegistry;
},{"./invariant":121}],19:[function(require,module,exports){
/**
* 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 EventPluginUtils
*/
"use strict";
var EventConstants = require("./EventConstants");
var invariant = require("./invariant");
/**
* Injected dependencies:
*/
/**
* - `Mount`: [required] Module that can convert between React dom IDs and
* actual node references.
*/
var injection = {
Mount: null,
injectMount: function(InjectedMount) {
injection.Mount = InjectedMount;
if ("production" !== "development") {
("production" !== "development" ? invariant(
InjectedMount && InjectedMount.getNode,
'EventPluginUtils.injection.injectMount(...): Injected Mount module ' +
'is missing getNode.'
) : invariant(InjectedMount && InjectedMount.getNode));
}
}
};
var topLevelTypes = EventConstants.topLevelTypes;
function isEndish(topLevelType) {
return topLevelType === topLevelTypes.topMouseUp ||
topLevelType === topLevelTypes.topTouchEnd ||
topLevelType === topLevelTypes.topTouchCancel;
}
function isMoveish(topLevelType) {
return topLevelType === topLevelTypes.topMouseMove ||
topLevelType === topLevelTypes.topTouchMove;
}
function isStartish(topLevelType) {
return topLevelType === topLevelTypes.topMouseDown ||
topLevelType === topLevelTypes.topTouchStart;
}
var validateEventDispatches;
if ("production" !== "development") {
validateEventDispatches = function(event) {
var dispatchListeners = event._dispatchListeners;
var dispatchIDs = event._dispatchIDs;
var listenersIsArr = Array.isArray(dispatchListeners);
var idsIsArr = Array.isArray(dispatchIDs);
var IDsLen = idsIsArr ? dispatchIDs.length : dispatchIDs ? 1 : 0;
var listenersLen = listenersIsArr ?
dispatchListeners.length :
dispatchListeners ? 1 : 0;
("production" !== "development" ? invariant(
idsIsArr === listenersIsArr && IDsLen === listenersLen,
'EventPluginUtils: Invalid `event`.'
) : invariant(idsIsArr === listenersIsArr && IDsLen === listenersLen));
};
}
/**
* Invokes `cb(event, listener, id)`. Avoids using call if no scope is
* provided. The `(listener,id)` pair effectively forms the "dispatch" but are
* kept separate to conserve memory.
*/
function forEachEventDispatch(event, cb) {
var dispatchListeners = event._dispatchListeners;
var dispatchIDs = event._dispatchIDs;
if ("production" !== "development") {
validateEventDispatches(event);
}
if (Array.isArray(dispatchListeners)) {
for (var i = 0; i < dispatchListeners.length; i++) {
if (event.isPropagationStopped()) {
break;
}
// Listeners and IDs are two parallel arrays that are always in sync.
cb(event, dispatchListeners[i], dispatchIDs[i]);
}
} else if (dispatchListeners) {
cb(event, dispatchListeners, dispatchIDs);
}
}
/**
* Default implementation of PluginModule.executeDispatch().
* @param {SyntheticEvent} SyntheticEvent to handle
* @param {function} Application-level callback
* @param {string} domID DOM id to pass to the callback.
*/
function executeDispatch(event, listener, domID) {
event.currentTarget = injection.Mount.getNode(domID);
var returnValue = listener(event, domID);
event.currentTarget = null;
return returnValue;
}
/**
* Standard/simple iteration through an event's collected dispatches.
*/
function executeDispatchesInOrder(event, executeDispatch) {
forEachEventDispatch(event, executeDispatch);
event._dispatchListeners = null;
event._dispatchIDs = null;
}
/**
* Standard/simple iteration through an event's collected dispatches, but stops
* at the first dispatch execution returning true, and returns that id.
*
* @return id of the first dispatch execution who's listener returns true, or
* null if no listener returned true.
*/
function executeDispatchesInOrderStopAtTrue(event) {
var dispatchListeners = event._dispatchListeners;
var dispatchIDs = event._dispatchIDs;
if ("production" !== "development") {
validateEventDispatches(event);
}
if (Array.isArray(dispatchListeners)) {
for (var i = 0; i < dispatchListeners.length; i++) {
if (event.isPropagationStopped()) {
break;
}
// Listeners and IDs are two parallel arrays that are always in sync.
if (dispatchListeners[i](event, dispatchIDs[i])) {
return dispatchIDs[i];
}
}
} else if (dispatchListeners) {
if (dispatchListeners(event, dispatchIDs)) {
return dispatchIDs;
}
}
return null;
}
/**
* Execution of a "direct" dispatch - there must be at most one dispatch
* accumulated on the event or it is considered an error. It doesn't really make
* sense for an event with multiple dispatches (bubbled) to keep track of the
* return values at each dispatch execution, but it does tend to make sense when
* dealing with "direct" dispatches.
*
* @return The return value of executing the single dispatch.
*/
function executeDirectDispatch(event) {
if ("production" !== "development") {
validateEventDispatches(event);
}
var dispatchListener = event._dispatchListeners;
var dispatchID = event._dispatchIDs;
("production" !== "development" ? invariant(
!Array.isArray(dispatchListener),
'executeDirectDispatch(...): Invalid `event`.'
) : invariant(!Array.isArray(dispatchListener)));
var res = dispatchListener ?
dispatchListener(event, dispatchID) :
null;
event._dispatchListeners = null;
event._dispatchIDs = null;
return res;
}
/**
* @param {SyntheticEvent} event
* @return {bool} True iff number of dispatches accumulated is greater than 0.
*/
function hasDispatches(event) {
return !!event._dispatchListeners;
}
/**
* General utilities that are useful in creating custom Event Plugins.
*/
var EventPluginUtils = {
isEndish: isEndish,
isMoveish: isMoveish,
isStartish: isStartish,
executeDirectDispatch: executeDirectDispatch,
executeDispatch: executeDispatch,
executeDispatchesInOrder: executeDispatchesInOrder,
executeDispatchesInOrderStopAtTrue: executeDispatchesInOrderStopAtTrue,
hasDispatches: hasDispatches,
injection: injection,
useTouchEvents: false
};
module.exports = EventPluginUtils;
},{"./EventConstants":15,"./invariant":121}],20:[function(require,module,exports){
/**
* 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 EventPropagators
*/
"use strict";
var EventConstants = require("./EventConstants");
var EventPluginHub = require("./EventPluginHub");
var accumulate = require("./accumulate");
var forEachAccumulated = require("./forEachAccumulated");
var PropagationPhases = EventConstants.PropagationPhases;
var getListener = EventPluginHub.getListener;
/**
* Some event types have a notion of different registration names for different
* "phases" of propagation. This finds listeners by a given phase.
*/
function listenerAtPhase(id, event, propagationPhase) {
var registrationName =
event.dispatchConfig.phasedRegistrationNames[propagationPhase];
return getListener(id, registrationName);
}
/**
* Tags a `SyntheticEvent` with dispatched listeners. Creating this function
* here, allows us to not have to bind or create functions for each event.
* Mutating the event's members allows us to not have to create a wrapping
* "dispatch" object that pairs the event with the listener.
*/
function accumulateDirectionalDispatches(domID, upwards, event) {
if ("production" !== "development") {
if (!domID) {
throw new Error('Dispatching id must not be null');
}
}
var phase = upwards ? PropagationPhases.bubbled : PropagationPhases.captured;
var listener = listenerAtPhase(domID, event, phase);
if (listener) {
event._dispatchListeners = accumulate(event._dispatchListeners, listener);
event._dispatchIDs = accumulate(event._dispatchIDs, domID);
}
}
/**
* Collect dispatches (must be entirely collected before dispatching - see unit
* tests). Lazily allocate the array to conserve memory. We must loop through
* each event and perform the traversal for each one. We can not perform a
* single traversal for the entire collection of events because each event may
* have a different target.
*/
function accumulateTwoPhaseDispatchesSingle(event) {
if (event && event.dispatchConfig.phasedRegistrationNames) {
EventPluginHub.injection.getInstanceHandle().traverseTwoPhase(
event.dispatchMarker,
accumulateDirectionalDispatches,
event
);
}
}
/**
* Accumulates without regard to direction, does not look for phased
* registration names. Same as `accumulateDirectDispatchesSingle` but without
* requiring that the `dispatchMarker` be the same as the dispatched ID.
*/
function accumulateDispatches(id, ignoredDirection, event) {
if (event && event.dispatchConfig.registrationName) {
var registrationName = event.dispatchConfig.registrationName;
var listener = getListener(id, registrationName);
if (listener) {
event._dispatchListeners = accumulate(event._dispatchListeners, listener);
event._dispatchIDs = accumulate(event._dispatchIDs, id);
}
}
}
/**
* Accumulates dispatches on an `SyntheticEvent`, but only for the
* `dispatchMarker`.
* @param {SyntheticEvent} event
*/
function accumulateDirectDispatchesSingle(event) {
if (event && event.dispatchConfig.registrationName) {
accumulateDispatches(event.dispatchMarker, null, event);
}
}
function accumulateTwoPhaseDispatches(events) {
forEachAccumulated(events, accumulateTwoPhaseDispatchesSingle);
}
function accumulateEnterLeaveDispatches(leave, enter, fromID, toID) {
EventPluginHub.injection.getInstanceHandle().traverseEnterLeave(
fromID,
toID,
accumulateDispatches,
leave,
enter
);
}
function accumulateDirectDispatches(events) {
forEachAccumulated(events, accumulateDirectDispatchesSingle);
}
/**
* A small set of propagation patterns, each of which will accept a small amount
* of information, and generate a set of "dispatch ready event objects" - which
* are sets of events that have already been annotated with a set of dispatched
* listener functions/ids. The API is designed this way to discourage these
* propagation strategies from actually executing the dispatches, since we
* always want to collect the entire set of dispatches before executing event a
* single one.
*
* @constructor EventPropagators
*/
var EventPropagators = {
accumulateTwoPhaseDispatches: accumulateTwoPhaseDispatches,
accumulateDirectDispatches: accumulateDirectDispatches,
accumulateEnterLeaveDispatches: accumulateEnterLeaveDispatches
};
module.exports = EventPropagators;
},{"./EventConstants":15,"./EventPluginHub":17,"./accumulate":97,"./forEachAccumulated":111}],21:[function(require,module,exports){
/**
* 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 ExecutionEnvironment
*/
/*jslint evil: true */
"use strict";
var canUseDOM = typeof window !== 'undefined';
/**
* Simple, lightweight module assisting with the detection and context of
* Worker. Helps avoid circular dependencies and allows code to reason about
* whether or not they are in a Worker, even if they never include the main
* `ReactWorker` dependency.
*/
var ExecutionEnvironment = {
canUseDOM: canUseDOM,
canUseWorkers: typeof Worker !== 'undefined',
canUseEventListeners:
canUseDOM && (window.addEventListener || window.attachEvent),
isInWorker: !canUseDOM // For now, this is true - might change in the future.
};
module.exports = ExecutionEnvironment;
},{}],22:[function(require,module,exports){
/**
* 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 LinkedStateMixin
* @typechecks static-only
*/
"use strict";
var ReactLink = require("./ReactLink");
var ReactStateSetters = require("./ReactStateSetters");
/**
* A simple mixin around ReactLink.forState().
*/
var LinkedStateMixin = {
/**
* Create a ReactLink that's linked to part of this component's state. The
* ReactLink will have the current value of this.state[key] and will call
* setState() when a change is requested.
*
* @param {string} key state key to update. Note: you may want to use keyOf()
* if you're using Google Closure Compiler advanced mode.
* @return {ReactLink} ReactLink instance linking to the state.
*/
linkState: function(key) {
return new ReactLink(
this.state[key],
ReactStateSetters.createStateKeySetter(this, key)
);
}
};
module.exports = LinkedStateMixin;
},{"./ReactLink":58,"./ReactStateSetters":74}],23:[function(require,module,exports){
/**
* 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 LinkedValueUtils
* @typechecks static-only
*/
"use strict";
var ReactPropTypes = require("./ReactPropTypes");
var invariant = require("./invariant");
var hasReadOnlyValue = {
'button': true,
'checkbox': true,
'image': true,
'hidden': true,
'radio': true,
'reset': true,
'submit': true
};
function _assertSingleLink(input) {
("production" !== "development" ? invariant(
input.props.checkedLink == null || input.props.valueLink == null,
'Cannot provide a checkedLink and a valueLink. If you want to use ' +
'checkedLink, you probably don\'t want to use valueLink and vice versa.'
) : invariant(input.props.checkedLink == null || input.props.valueLink == null));
}
function _assertValueLink(input) {
_assertSingleLink(input);
("production" !== "development" ? invariant(
input.props.value == null && input.props.onChange == null,
'Cannot provide a valueLink and a value or onChange event. If you want ' +
'to use value or onChange, you probably don\'t want to use valueLink.'
) : invariant(input.props.value == null && input.props.onChange == null));
}
function _assertCheckedLink(input) {
_assertSingleLink(input);
("production" !== "development" ? invariant(
input.props.checked == null && input.props.onChange == null,
'Cannot provide a checkedLink and a checked property or onChange event. ' +
'If you want to use checked or onChange, you probably don\'t want to ' +
'use checkedLink'
) : invariant(input.props.checked == null && input.props.onChange == null));
}
/**
* @param {SyntheticEvent} e change event to handle
*/
function _handleLinkedValueChange(e) {
/*jshint validthis:true */
this.props.valueLink.requestChange(e.target.value);
}
/**
* @param {SyntheticEvent} e change event to handle
*/
function _handleLinkedCheckChange(e) {
/*jshint validthis:true */
this.props.checkedLink.requestChange(e.target.checked);
}
/**
* Provide a linked `value` attribute for controlled forms. You should not use
* this outside of the ReactDOM controlled form components.
*/
var LinkedValueUtils = {
Mixin: {
propTypes: {
value: function(props, propName, componentName) {
if ("production" !== "development") {
if (props[propName] &&
!hasReadOnlyValue[props.type] &&
!props.onChange &&
!props.readOnly &&
!props.disabled) {
console.warn(
'You provided a `value` prop to a form field without an ' +
'`onChange` handler. This will render a read-only field. If ' +
'the field should be mutable use `defaultValue`. Otherwise, ' +
'set either `onChange` or `readOnly`.'
);
}
}
},
checked: function(props, propName, componentName) {
if ("production" !== "development") {
if (props[propName] &&
!props.onChange &&
!props.readOnly &&
!props.disabled) {
console.warn(
'You provided a `checked` prop to a form field without an ' +
'`onChange` handler. This will render a read-only field. If ' +
'the field should be mutable use `defaultChecked`. Otherwise, ' +
'set either `onChange` or `readOnly`.'
);
}
}
},
onChange: ReactPropTypes.func
}
},
/**
* @param {ReactComponent} input Form component
* @return {*} current value of the input either from value prop or link.
*/
getValue: function(input) {
if (input.props.valueLink) {
_assertValueLink(input);
return input.props.valueLink.value;
}
return input.props.value;
},
/**
* @param {ReactComponent} input Form component
* @return {*} current checked status of the input either from checked prop
* or link.
*/
getChecked: function(input) {
if (input.props.checkedLink) {
_assertCheckedLink(input);
return input.props.checkedLink.value;
}
return input.props.checked;
},
/**
* @param {ReactComponent} input Form component
* @return {function} change callback either from onChange prop or link.
*/
getOnChange: function(input) {
if (input.props.valueLink) {
_assertValueLink(input);
return _handleLinkedValueChange;
} else if (input.props.checkedLink) {
_assertCheckedLink(input);
return _handleLinkedCheckChange;
}
return input.props.onChange;
}
};
module.exports = LinkedValueUtils;
},{"./ReactPropTypes":69,"./invariant":121}],24:[function(require,module,exports){
/**
* 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 MobileSafariClickEventPlugin
* @typechecks static-only
*/
"use strict";
var EventConstants = require("./EventConstants");
var emptyFunction = require("./emptyFunction");
var topLevelTypes = EventConstants.topLevelTypes;
/**
* Mobile Safari does not fire properly bubble click events on non-interactive
* elements, which means delegated click listeners do not fire. The workaround
* for this bug involves attaching an empty click listener on the target node.
*
* This particular plugin works around the bug by attaching an empty click
* listener on `touchstart` (which does fire on every element).
*/
var MobileSafariClickEventPlugin = {
eventTypes: null,
/**
* @param {string} topLevelType Record from `EventConstants`.
* @param {DOMEventTarget} topLevelTarget The listening component root node.
* @param {string} topLevelTargetID ID of `topLevelTarget`.
* @param {object} nativeEvent Native browser event.
* @return {*} An accumulation of synthetic events.
* @see {EventPluginHub.extractEvents}
*/
extractEvents: function(
topLevelType,
topLevelTarget,
topLevelTargetID,
nativeEvent) {
if (topLevelType === topLevelTypes.topTouchStart) {
var target = nativeEvent.target;
if (target && !target.onclick) {
target.onclick = emptyFunction;
}
}
}
};
module.exports = MobileSafariClickEventPlugin;
},{"./EventConstants":15,"./emptyFunction":108}],25:[function(require,module,exports){
/**
* 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 PooledClass
*/
"use strict";
var invariant = require("./invariant");
/**
* Static poolers. Several custom versions for each potential number of
* arguments. A completely generic pooler is easy to implement, but would
* require accessing the `arguments` object. In each of these, `this` refers to
* the Class itself, not an instance. If any others are needed, simply add them
* here, or in their own files.
*/
var oneArgumentPooler = function(copyFieldsFrom) {
var Klass = this;
if (Klass.instancePool.length) {
var instance = Klass.instancePool.pop();
Klass.call(instance, copyFieldsFrom);
return instance;
} else {
return new Klass(copyFieldsFrom);
}
};
var twoArgumentPooler = function(a1, a2) {
var Klass = this;
if (Klass.instancePool.length) {
var instance = Klass.instancePool.pop();
Klass.call(instance, a1, a2);
return instance;
} else {
return new Klass(a1, a2);
}
};
var threeArgumentPooler = function(a1, a2, a3) {
var Klass = this;
if (Klass.instancePool.length) {
var instance = Klass.instancePool.pop();
Klass.call(instance, a1, a2, a3);
return instance;
} else {
return new Klass(a1, a2, a3);
}
};
var fiveArgumentPooler = function(a1, a2, a3, a4, a5) {
var Klass = this;
if (Klass.instancePool.length) {
var instance = Klass.instancePool.pop();
Klass.call(instance, a1, a2, a3, a4, a5);
return instance;
} else {
return new Klass(a1, a2, a3, a4, a5);
}
};
var standardReleaser = function(instance) {
var Klass = this;
("production" !== "development" ? invariant(
instance instanceof Klass,
'Trying to release an instance into a pool of a different type.'
) : invariant(instance instanceof Klass));
if (instance.destructor) {
instance.destructor();
}
if (Klass.instancePool.length < Klass.poolSize) {
Klass.instancePool.push(instance);
}
};
var DEFAULT_POOL_SIZE = 10;
var DEFAULT_POOLER = oneArgumentPooler;
/**
* Augments `CopyConstructor` to be a poolable class, augmenting only the class
* itself (statically) not adding any prototypical fields. Any CopyConstructor
* you give this may have a `poolSize` property, and will look for a
* prototypical `destructor` on instances (optional).
*
* @param {Function} CopyConstructor Constructor that can be used to reset.
* @param {Function} pooler Customizable pooler.
*/
var addPoolingTo = function(CopyConstructor, pooler) {
var NewKlass = CopyConstructor;
NewKlass.instancePool = [];
NewKlass.getPooled = pooler || DEFAULT_POOLER;
if (!NewKlass.poolSize) {
NewKlass.poolSize = DEFAULT_POOL_SIZE;
}
NewKlass.release = standardReleaser;
return NewKlass;
};
var PooledClass = {
addPoolingTo: addPoolingTo,
oneArgumentPooler: oneArgumentPooler,
twoArgumentPooler: twoArgumentPooler,
threeArgumentPooler: threeArgumentPooler,
fiveArgumentPooler: fiveArgumentPooler
};
module.exports = PooledClass;
},{"./invariant":121}],26:[function(require,module,exports){
/**
* 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 React
*/
"use strict";
var DOMPropertyOperations = require("./DOMPropertyOperations");
var EventPluginUtils = require("./EventPluginUtils");
var ReactChildren = require("./ReactChildren");
var ReactComponent = require("./ReactComponent");
var ReactCompositeComponent = require("./ReactCompositeComponent");
var ReactContext = require("./ReactContext");
var ReactCurrentOwner = require("./ReactCurrentOwner");
var ReactDOM = require("./ReactDOM");
var ReactDOMComponent = require("./ReactDOMComponent");
var ReactDefaultInjection = require("./ReactDefaultInjection");
var ReactInstanceHandles = require("./ReactInstanceHandles");
var ReactMount = require("./ReactMount");
var ReactMultiChild = require("./ReactMultiChild");
var ReactPerf = require("./ReactPerf");
var ReactPropTypes = require("./ReactPropTypes");
var ReactServerRendering = require("./ReactServerRendering");
var ReactTextComponent = require("./ReactTextComponent");
var onlyChild = require("./onlyChild");
ReactDefaultInjection.inject();
var React = {
Children: {
map: ReactChildren.map,
forEach: ReactChildren.forEach,
only: onlyChild
},
DOM: ReactDOM,
PropTypes: ReactPropTypes,
initializeTouchEvents: function(shouldUseTouch) {
EventPluginUtils.useTouchEvents = shouldUseTouch;
},
createClass: ReactCompositeComponent.createClass,
constructAndRenderComponent: ReactMount.constructAndRenderComponent,
constructAndRenderComponentByID: ReactMount.constructAndRenderComponentByID,
renderComponent: ReactPerf.measure(
'React',
'renderComponent',
ReactMount.renderComponent
),
renderComponentToString: ReactServerRendering.renderComponentToString,
unmountComponentAtNode: ReactMount.unmountComponentAtNode,
isValidClass: ReactCompositeComponent.isValidClass,
isValidComponent: ReactComponent.isValidComponent,
withContext: ReactContext.withContext,
__internals: {
Component: ReactComponent,
CurrentOwner: ReactCurrentOwner,
DOMComponent: ReactDOMComponent,
DOMPropertyOperations: DOMPropertyOperations,
InstanceHandles: ReactInstanceHandles,
Mount: ReactMount,
MultiChild: ReactMultiChild,
TextComponent: ReactTextComponent
}
};
if ("production" !== "development") {
var ExecutionEnvironment = require("./ExecutionEnvironment");
if (ExecutionEnvironment.canUseDOM &&
window.top === window.self &&
navigator.userAgent.indexOf('Chrome') > -1) {
console.debug(
'Download the React DevTools for a better development experience: ' +
'http://fb.me/react-devtools'
);
}
}
// Version exists only in the open-source version of React, not in Facebook's
// internal version.
React.version = '0.9.0';
module.exports = React;
},{"./DOMPropertyOperations":10,"./EventPluginUtils":19,"./ExecutionEnvironment":21,"./ReactChildren":29,"./ReactComponent":30,"./ReactCompositeComponent":33,"./ReactContext":34,"./ReactCurrentOwner":35,"./ReactDOM":36,"./ReactDOMComponent":38,"./ReactDefaultInjection":48,"./ReactInstanceHandles":57,"./ReactMount":60,"./ReactMultiChild":62,"./ReactPerf":65,"./ReactPropTypes":69,"./ReactServerRendering":73,"./ReactTextComponent":76,"./onlyChild":136}],27:[function(require,module,exports){
/**
* 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.
*
* @typechecks
* @providesModule ReactCSSTransitionGroup
* @jsx React.DOM
*/
"use strict";
var React = require("./React");
var ReactTransitionGroup = require("./ReactTransitionGroup");
var ReactCSSTransitionGroupChild = require("./ReactCSSTransitionGroupChild");
var ReactCSSTransitionGroup = React.createClass({displayName: 'ReactCSSTransitionGroup',
propTypes: {
transitionName: React.PropTypes.string.isRequired,
transitionEnter: React.PropTypes.bool,
transitionLeave: React.PropTypes.bool
},
getDefaultProps: function() {
return {
transitionEnter: true,
transitionLeave: true
};
},
_wrapChild: function(child) {
// We need to provide this childFactory so that
// ReactCSSTransitionGroupChild can receive updates to name, enter, and
// leave while it is leaving.
return (
ReactCSSTransitionGroupChild(
{name:this.props.transitionName,
enter:this.props.transitionEnter,
leave:this.props.transitionLeave},
child
)
);
},
render: function() {
return this.transferPropsTo(
ReactTransitionGroup( {childFactory:this._wrapChild},
this.props.children
)
);
}
});
module.exports = ReactCSSTransitionGroup;
},{"./React":26,"./ReactCSSTransitionGroupChild":28,"./ReactTransitionGroup":79}],28:[function(require,module,exports){
/**
* 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.
*
* @typechecks
* @providesModule ReactCSSTransitionGroupChild
*/
"use strict";
var React = require("./React");
var CSSCore = require("./CSSCore");
var ReactTransitionEvents = require("./ReactTransitionEvents");
var onlyChild = require("./onlyChild");
// We don't remove the element from the DOM until we receive an animationend or
// transitionend event. If the user screws up and forgets to add an animation
// their node will be stuck in the DOM forever, so we detect if an animation
// does not start and if it doesn't, we just call the end listener immediately.
var TICK = 17;
var NO_EVENT_TIMEOUT = 5000;
var noEventListener = null;
if ("production" !== "development") {
noEventListener = function() {
console.warn(
'transition(): tried to perform an animation without ' +
'an animationend or transitionend event after timeout (' +
NO_EVENT_TIMEOUT + 'ms). You should either disable this ' +
'transition in JS or add a CSS animation/transition.'
);
};
}
var ReactCSSTransitionGroupChild = React.createClass({
transition: function(animationType, finishCallback) {
var node = this.getDOMNode();
var className = this.props.name + '-' + animationType;
var activeClassName = className + '-active';
var noEventTimeout = null;
var endListener = function() {
if ("production" !== "development") {
clearTimeout(noEventTimeout);
}
CSSCore.removeClass(node, className);
CSSCore.removeClass(node, activeClassName);
ReactTransitionEvents.removeEndEventListener(node, endListener);
// Usually this optional callback is used for informing an owner of
// a leave animation and telling it to remove the child.
finishCallback && finishCallback();
};
ReactTransitionEvents.addEndEventListener(node, endListener);
CSSCore.addClass(node, className);
// Need to do this to actually trigger a transition.
this.queueClass(activeClassName);
if ("production" !== "development") {
noEventTimeout = setTimeout(noEventListener, NO_EVENT_TIMEOUT);
}
},
queueClass: function(className) {
this.classNameQueue.push(className);
if (this.props.runNextTick) {
this.props.runNextTick(this.flushClassNameQueue);
return;
}
if (!this.timeout) {
this.timeout = setTimeout(this.flushClassNameQueue, TICK);
}
},
flushClassNameQueue: function() {
if (this.isMounted()) {
this.classNameQueue.forEach(
CSSCore.addClass.bind(CSSCore, this.getDOMNode())
);
}
this.classNameQueue.length = 0;
this.timeout = null;
},
componentWillMount: function() {
this.classNameQueue = [];
},
componentWillUnmount: function() {
if (this.timeout) {
clearTimeout(this.timeout);
}
},
componentWillEnter: function(done) {
if (this.props.enter) {
this.transition('enter', done);
} else {
done();
}
},
componentWillLeave: function(done) {
if (this.props.leave) {
this.transition('leave', done);
} else {
done();
}
},
render: function() {
return onlyChild(this.props.children);
}
});
module.exports = ReactCSSTransitionGroupChild;
},{"./CSSCore":2,"./React":26,"./ReactTransitionEvents":78,"./onlyChild":136}],29:[function(require,module,exports){
/**
* 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 ReactChildren
*/
"use strict";
var PooledClass = require("./PooledClass");
var invariant = require("./invariant");
var traverseAllChildren = require("./traverseAllChildren");
var twoArgumentPooler = PooledClass.twoArgumentPooler;
var threeArgumentPooler = PooledClass.threeArgumentPooler;
/**
* PooledClass representing the bookkeeping associated with performing a child
* traversal. Allows avoiding binding callbacks.
*
* @constructor ForEachBookKeeping
* @param {!function} forEachFunction Function to perform traversal with.
* @param {?*} forEachContext Context to perform context with.
*/
function ForEachBookKeeping(forEachFunction, forEachContext) {
this.forEachFunction = forEachFunction;
this.forEachContext = forEachContext;
}
PooledClass.addPoolingTo(ForEachBookKeeping, twoArgumentPooler);
function forEachSingleChild(traverseContext, child, name, i) {
var forEachBookKeeping = traverseContext;
forEachBookKeeping.forEachFunction.call(
forEachBookKeeping.forEachContext, child, i);
}
/**
* Iterates through children that are typically specified as `props.children`.
*
* The provided forEachFunc(child, index) will be called for each
* leaf child.
*
* @param {?*} children Children tree container.
* @param {function(*, int)} forEachFunc.
* @param {*} forEachContext Context for forEachContext.
*/
function forEachChildren(children, forEachFunc, forEachContext) {
if (children == null) {
return children;
}
var traverseContext =
ForEachBookKeeping.getPooled(forEachFunc, forEachContext);
traverseAllChildren(children, forEachSingleChild, traverseContext);
ForEachBookKeeping.release(traverseContext);
}
/**
* PooledClass representing the bookkeeping associated with performing a child
* mapping. Allows avoiding binding callbacks.
*
* @constructor MapBookKeeping
* @param {!*} mapResult Object containing the ordered map of results.
* @param {!function} mapFunction Function to perform mapping with.
* @param {?*} mapContext Context to perform mapping with.
*/
function MapBookKeeping(mapResult, mapFunction, mapContext) {
this.mapResult = mapResult;
this.mapFunction = mapFunction;
this.mapContext = mapContext;
}
PooledClass.addPoolingTo(MapBookKeeping, threeArgumentPooler);
function mapSingleChildIntoContext(traverseContext, child, name, i) {
var mapBookKeeping = traverseContext;
var mapResult = mapBookKeeping.mapResult;
var mappedChild =
mapBookKeeping.mapFunction.call(mapBookKeeping.mapContext, child, i);
// We found a component instance
("production" !== "development" ? invariant(
!mapResult.hasOwnProperty(name),
'ReactChildren.map(...): Encountered two children with the same key, ' +
'`%s`. Children keys must be unique.',
name
) : invariant(!mapResult.hasOwnProperty(name)));
mapResult[name] = mappedChild;
}
/**
* Maps children that are typically specified as `props.children`.
*
* The provided mapFunction(child, key, index) will be called for each
* leaf child.
*
* TODO: This may likely break any calls to `ReactChildren.map` that were
* previously relying on the fact that we guarded against null children.
*
* @param {?*} children Children tree container.
* @param {function(*, int)} mapFunction.
* @param {*} mapContext Context for mapFunction.
* @return {object} Object containing the ordered map of results.
*/
function mapChildren(children, func, context) {
if (children == null) {
return children;
}
var mapResult = {};
var traverseContext = MapBookKeeping.getPooled(mapResult, func, context);
traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
MapBookKeeping.release(traverseContext);
return mapResult;
}
var ReactChildren = {
forEach: forEachChildren,
map: mapChildren
};
module.exports = ReactChildren;
},{"./PooledClass":25,"./invariant":121,"./traverseAllChildren":141}],30:[function(require,module,exports){
/**
* 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 ReactComponent
*/
"use strict";
var ReactComponentEnvironment = require("./ReactComponentEnvironment");
var ReactCurrentOwner = require("./ReactCurrentOwner");
var ReactOwner = require("./ReactOwner");
var ReactUpdates = require("./ReactUpdates");
var invariant = require("./invariant");
var keyMirror = require("./keyMirror");
var merge = require("./merge");
/**
* Every React component is in one of these life cycles.
*/
var ComponentLifeCycle = keyMirror({
/**
* Mounted components have a DOM node representation and are capable of
* receiving new props.
*/
MOUNTED: null,
/**
* Unmounted components are inactive and cannot receive new props.
*/
UNMOUNTED: null
});
/**
* Warn if there's no key explicitly set on dynamic arrays of children or
* object keys are not valid. This allows us to keep track of children between
* updates.
*/
var ownerHasExplicitKeyWarning = {};
var ownerHasPropertyWarning = {};
var NUMERIC_PROPERTY_REGEX = /^\d+$/;
/**
* Warn if the component doesn't have an explicit key assigned to it.
* This component is in an array. The array could grow and shrink or be
* reordered. All children that haven't already been validated are required to
* have a "key" property assigned to it.
*
* @internal
* @param {ReactComponent} component Component that requires a key.
*/
function validateExplicitKey(component) {
if (component.__keyValidated__ || component.props.key != null) {
return;
}
component.__keyValidated__ = true;
// We can't provide friendly warnings for top level components.
if (!ReactCurrentOwner.current) {
return;
}
// Name of the component whose render method tried to pass children.
var currentName = ReactCurrentOwner.current.constructor.displayName;
if (ownerHasExplicitKeyWarning.hasOwnProperty(currentName)) {
return;
}
ownerHasExplicitKeyWarning[currentName] = true;
var message = 'Each child in an array should have a unique "key" prop. ' +
'Check the render method of ' + currentName + '.';
if (!component.isOwnedBy(ReactCurrentOwner.current)) {
// Name of the component that originally created this child.
var childOwnerName =
component._owner &&
component._owner.constructor.displayName;
// Usually the current owner is the offender, but if it accepts
// children as a property, it may be the creator of the child that's
// responsible for assigning it a key.
message += ' It was passed a child from ' + childOwnerName + '.';
}
message += ' See http://fb.me/react-warning-keys for more information.';
console.warn(message);
}
/**
* Warn if the key is being defined as an object property but has an incorrect
* value.
*
* @internal
* @param {string} name Property name of the key.
* @param {ReactComponent} component Component that requires a key.
*/
function validatePropertyKey(name) {
if (NUMERIC_PROPERTY_REGEX.test(name)) {
// Name of the component whose render method tried to pass children.
var currentName = ReactCurrentOwner.current.constructor.displayName;
if (ownerHasPropertyWarning.hasOwnProperty(currentName)) {
return;
}
ownerHasPropertyWarning[currentName] = true;
console.warn(
'Child objects should have non-numeric keys so ordering is preserved. ' +
'Check the render method of ' + currentName + '. ' +
'See http://fb.me/react-warning-keys for more information.'
);
}
}
/**
* Ensure that every component either is passed in a static location, in an
* array with an explicit keys property defined, or in an object literal
* with valid key property.
*
* @internal
* @param {*} component Statically passed child of any type.
* @return {boolean}
*/
function validateChildKeys(component) {
if (Array.isArray(component)) {
for (var i = 0; i < component.length; i++) {
var child = component[i];
if (ReactComponent.isValidComponent(child)) {
validateExplicitKey(child);
}
}
} else if (ReactComponent.isValidComponent(component)) {
// This component was passed in a valid location.
component.__keyValidated__ = true;
} else if (component && typeof component === 'object') {
for (var name in component) {
validatePropertyKey(name, component);
}
}
}
/**
* Components are the basic units of composition in React.
*
* Every component accepts a set of keyed input parameters known as "props" that
* are initialized by the constructor. Once a component is mounted, the props
* can be mutated using `setProps` or `replaceProps`.
*
* Every component is capable of the following operations:
*
* `mountComponent`
* Initializes the component, renders markup, and registers event listeners.
*
* `receiveComponent`
* Updates the rendered DOM nodes to match the given component.
*
* `unmountComponent`
* Releases any resources allocated by this component.
*
* Components can also be "owned" by other components. Being owned by another
* component means being constructed by that component. This is different from
* being the child of a component, which means having a DOM representation that
* is a child of the DOM representation of that component.
*
* @class ReactComponent
*/
var ReactComponent = {
/**
* @param {?object} object
* @return {boolean} True if `object` is a valid component.
* @final
*/
isValidComponent: function(object) {
if (!object || !object.type || !object.type.prototype) {
return false;
}
// This is the safer way of duck checking the type of instance this is.
// The object can be a generic descriptor but the type property refers to
// the constructor and it's prototype can be used to inspect the type that
// will actually get mounted.
var prototype = object.type.prototype;
return (
typeof prototype.mountComponentIntoNode === 'function' &&
typeof prototype.receiveComponent === 'function'
);
},
/**
* @internal
*/
LifeCycle: ComponentLifeCycle,
/**
* Injected module that provides ability to mutate individual properties.
* Injected into the base class because many different subclasses need access
* to this.
*
* @internal
*/
BackendIDOperations: ReactComponentEnvironment.BackendIDOperations,
/**
* Optionally injectable environment dependent cleanup hook. (server vs.
* browser etc). Example: A browser system caches DOM nodes based on component
* ID and must remove that cache entry when this instance is unmounted.
*
* @private
*/
unmountIDFromEnvironment: ReactComponentEnvironment.unmountIDFromEnvironment,
/**
* The "image" of a component tree, is the platform specific (typically
* serialized) data that represents a tree of lower level UI building blocks.
* On the web, this "image" is HTML markup which describes a construction of
* low level `div` and `span` nodes. Other platforms may have different
* encoding of this "image". This must be injected.
*
* @private
*/
mountImageIntoNode: ReactComponentEnvironment.mountImageIntoNode,
/**
* React references `ReactReconcileTransaction` using this property in order
* to allow dependency injection.
*
* @internal
*/
ReactReconcileTransaction:
ReactComponentEnvironment.ReactReconcileTransaction,
/**
* Base functionality for every ReactComponent constructor. Mixed into the
* `ReactComponent` prototype, but exposed statically for easy access.
*
* @lends {ReactComponent.prototype}
*/
Mixin: merge(ReactComponentEnvironment.Mixin, {
/**
* Checks whether or not this component is mounted.
*
* @return {boolean} True if mounted, false otherwise.
* @final
* @protected
*/
isMounted: function() {
return this._lifeCycleState === ComponentLifeCycle.MOUNTED;
},
/**
* Sets a subset of the props.
*
* @param {object} partialProps Subset of the next props.
* @param {?function} callback Called after props are updated.
* @final
* @public
*/
setProps: function(partialProps, callback) {
// Merge with `_pendingProps` if it exists, otherwise with existing props.
this.replaceProps(
merge(this._pendingProps || this.props, partialProps),
callback
);
},
/**
* Replaces all of the props.
*
* @param {object} props New props.
* @param {?function} callback Called after props are updated.
* @final
* @public
*/
replaceProps: function(props, callback) {
("production" !== "development" ? invariant(
this.isMounted(),
'replaceProps(...): Can only update a mounted component.'
) : invariant(this.isMounted()));
("production" !== "development" ? invariant(
this._mountDepth === 0,
'replaceProps(...): You called `setProps` or `replaceProps` on a ' +
'component with a parent. This is an anti-pattern since props will ' +
'get reactively updated when rendered. Instead, change the owner\'s ' +
'`render` method to pass the correct value as props to the component ' +
'where it is created.'
) : invariant(this._mountDepth === 0));
this._pendingProps = props;
ReactUpdates.enqueueUpdate(this, callback);
},
/**
* Base constructor for all React components.
*
* Subclasses that override this method should make sure to invoke
* `ReactComponent.Mixin.construct.call(this, ...)`.
*
* @param {?object} initialProps
* @param {*} children
* @internal
*/
construct: function(initialProps, children) {
this.props = initialProps || {};
// Record the component responsible for creating this component.
this._owner = ReactCurrentOwner.current;
// All components start unmounted.
this._lifeCycleState = ComponentLifeCycle.UNMOUNTED;
this._pendingProps = null;
this._pendingCallbacks = null;
// Unlike _pendingProps and _pendingCallbacks, we won't use null to
// indicate that nothing is pending because it's possible for a component
// to have a null owner. Instead, an owner change is pending when
// this._owner !== this._pendingOwner.
this._pendingOwner = this._owner;
// Children can be more than one argument
var childrenLength = arguments.length - 1;
if (childrenLength === 1) {
if ("production" !== "development") {
validateChildKeys(children);
}
this.props.children = children;
} else if (childrenLength > 1) {
var childArray = Array(childrenLength);
for (var i = 0; i < childrenLength; i++) {
if ("production" !== "development") {
validateChildKeys(arguments[i + 1]);
}
childArray[i] = arguments[i + 1];
}
this.props.children = childArray;
}
},
/**
* Initializes the component, renders markup, and registers event listeners.
*
* NOTE: This does not insert any nodes into the DOM.
*
* Subclasses that override this method should make sure to invoke
* `ReactComponent.Mixin.mountComponent.call(this, ...)`.
*
* @param {string} rootID DOM ID of the root node.
* @param {ReactReconcileTransaction} transaction
* @param {number} mountDepth number of components in the owner hierarchy.
* @return {?string} Rendered markup to be inserted into the DOM.
* @internal
*/
mountComponent: function(rootID, transaction, mountDepth) {
("production" !== "development" ? invariant(
!this.isMounted(),
'mountComponent(%s, ...): Can only mount an unmounted component. ' +
'Make sure to avoid storing components between renders or reusing a ' +
'single component instance in multiple places.',
rootID
) : invariant(!this.isMounted()));
var props = this.props;
if (props.ref != null) {
ReactOwner.addComponentAsRefTo(this, props.ref, this._owner);
}
this._rootNodeID = rootID;
this._lifeCycleState = ComponentLifeCycle.MOUNTED;
this._mountDepth = mountDepth;
// Effectively: return '';
},
/**
* Releases any resources allocated by `mountComponent`.
*
* NOTE: This does not remove any nodes from the DOM.
*
* Subclasses that override this method should make sure to invoke
* `ReactComponent.Mixin.unmountComponent.call(this)`.
*
* @internal
*/
unmountComponent: function() {
("production" !== "development" ? invariant(
this.isMounted(),
'unmountComponent(): Can only unmount a mounted component.'
) : invariant(this.isMounted()));
var props = this.props;
if (props.ref != null) {
ReactOwner.removeComponentAsRefFrom(this, props.ref, this._owner);
}
ReactComponent.unmountIDFromEnvironment(this._rootNodeID);
this._rootNodeID = null;
this._lifeCycleState = ComponentLifeCycle.UNMOUNTED;
},
/**
* Given a new instance of this component, updates the rendered DOM nodes
* as if that instance was rendered instead.
*
* Subclasses that override this method should make sure to invoke
* `ReactComponent.Mixin.receiveComponent.call(this, ...)`.
*
* @param {object} nextComponent Next set of properties.
* @param {ReactReconcileTransaction} transaction
* @internal
*/
receiveComponent: function(nextComponent, transaction) {
("production" !== "development" ? invariant(
this.isMounted(),
'receiveComponent(...): Can only update a mounted component.'
) : invariant(this.isMounted()));
this._pendingOwner = nextComponent._owner;
this._pendingProps = nextComponent.props;
this._performUpdateIfNecessary(transaction);
},
/**
* Call `_performUpdateIfNecessary` within a new transaction.
*
* @param {ReactReconcileTransaction} transaction
* @internal
*/
performUpdateIfNecessary: function() {
var transaction = ReactComponent.ReactReconcileTransaction.getPooled();
transaction.perform(this._performUpdateIfNecessary, this, transaction);
ReactComponent.ReactReconcileTransaction.release(transaction);
},
/**
* If `_pendingProps` is set, update the component.
*
* @param {ReactReconcileTransaction} transaction
* @internal
*/
_performUpdateIfNecessary: function(transaction) {
if (this._pendingProps == null) {
return;
}
var prevProps = this.props;
var prevOwner = this._owner;
this.props = this._pendingProps;
this._owner = this._pendingOwner;
this._pendingProps = null;
this.updateComponent(transaction, prevProps, prevOwner);
},
/**
* Updates the component's currently mounted representation.
*
* @param {ReactReconcileTransaction} transaction
* @param {object} prevProps
* @internal
*/
updateComponent: function(transaction, prevProps, prevOwner) {
var props = this.props;
// If either the owner or a `ref` has changed, make sure the newest owner
// has stored a reference to `this`, and the previous owner (if different)
// has forgotten the reference to `this`.
if (this._owner !== prevOwner || props.ref !== prevProps.ref) {
if (prevProps.ref != null) {
ReactOwner.removeComponentAsRefFrom(
this, prevProps.ref, prevOwner
);
}
// Correct, even if the owner is the same, and only the ref has changed.
if (props.ref != null) {
ReactOwner.addComponentAsRefTo(this, props.ref, this._owner);
}
}
},
/**
* Mounts this component and inserts it into the DOM.
*
* @param {string} rootID DOM ID of the root node.
* @param {DOMElement} container DOM element to mount into.
* @param {boolean} shouldReuseMarkup If true, do not insert markup
* @final
* @internal
* @see {ReactMount.renderComponent}
*/
mountComponentIntoNode: function(rootID, container, shouldReuseMarkup) {
var transaction = ReactComponent.ReactReconcileTransaction.getPooled();
transaction.perform(
this._mountComponentIntoNode,
this,
rootID,
container,
transaction,
shouldReuseMarkup
);
ReactComponent.ReactReconcileTransaction.release(transaction);
},
/**
* @param {string} rootID DOM ID of the root node.
* @param {DOMElement} container DOM element to mount into.
* @param {ReactReconcileTransaction} transaction
* @param {boolean} shouldReuseMarkup If true, do not insert markup
* @final
* @private
*/
_mountComponentIntoNode: function(
rootID,
container,
transaction,
shouldReuseMarkup) {
var markup = this.mountComponent(rootID, transaction, 0);
ReactComponent.mountImageIntoNode(markup, container, shouldReuseMarkup);
},
/**
* Checks if this component is owned by the supplied `owner` component.
*
* @param {ReactComponent} owner Component to check.
* @return {boolean} True if `owners` owns this component.
* @final
* @internal
*/
isOwnedBy: function(owner) {
return this._owner === owner;
},
/**
* Gets another component, that shares the same owner as this one, by ref.
*
* @param {string} ref of a sibling Component.
* @return {?ReactComponent} the actual sibling Component.
* @final
* @internal
*/
getSiblingByRef: function(ref) {
var owner = this._owner;
if (!owner || !owner.refs) {
return null;
}
return owner.refs[ref];
}
})
};
module.exports = ReactComponent;
},{"./ReactComponentEnvironment":32,"./ReactCurrentOwner":35,"./ReactOwner":64,"./ReactUpdates":80,"./invariant":121,"./keyMirror":127,"./merge":130}],31:[function(require,module,exports){
/**
* 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 ReactComponentBrowserEnvironment
*/
/*jslint evil: true */
"use strict";
var ReactDOMIDOperations = require("./ReactDOMIDOperations");
var ReactMarkupChecksum = require("./ReactMarkupChecksum");
var ReactMount = require("./ReactMount");
var ReactPerf = require("./ReactPerf");
var ReactReconcileTransaction = require("./ReactReconcileTransaction");
var getReactRootElementInContainer = require("./getReactRootElementInContainer");
var invariant = require("./invariant");
var ELEMENT_NODE_TYPE = 1;
var DOC_NODE_TYPE = 9;
/**
* Abstracts away all functionality of `ReactComponent` requires knowledge of
* the browser context.
*/
var ReactComponentBrowserEnvironment = {
/**
* Mixed into every component instance.
*/
Mixin: {
/**
* Returns the DOM node rendered by this component.
*
* @return {DOMElement} The root node of this component.
* @final
* @protected
*/
getDOMNode: function() {
("production" !== "development" ? invariant(
this.isMounted(),
'getDOMNode(): A component must be mounted to have a DOM node.'
) : invariant(this.isMounted()));
return ReactMount.getNode(this._rootNodeID);
}
},
ReactReconcileTransaction: ReactReconcileTransaction,
BackendIDOperations: ReactDOMIDOperations,
/**
* If a particular environment requires that some resources be cleaned up,
* specify this in the injected Mixin. In the DOM, we would likely want to
* purge any cached node ID lookups.
*
* @private
*/
unmountIDFromEnvironment: function(rootNodeID) {
ReactMount.purgeID(rootNodeID);
},
/**
* @param {string} markup Markup string to place into the DOM Element.
* @param {DOMElement} container DOM Element to insert markup into.
* @param {boolean} shouldReuseMarkup Should reuse the existing markup in the
* container if possible.
*/
mountImageIntoNode: ReactPerf.measure(
'ReactComponentBrowserEnvironment',
'mountImageIntoNode',
function(markup, container, shouldReuseMarkup) {
("production" !== "development" ? invariant(
container && (
container.nodeType === ELEMENT_NODE_TYPE ||
container.nodeType === DOC_NODE_TYPE
),
'mountComponentIntoNode(...): Target container is not valid.'
) : invariant(container && (
container.nodeType === ELEMENT_NODE_TYPE ||
container.nodeType === DOC_NODE_TYPE
)));
if (shouldReuseMarkup) {
if (ReactMarkupChecksum.canReuseMarkup(
markup,
getReactRootElementInContainer(container))) {
return;
} else {
("production" !== "development" ? invariant(
container.nodeType !== DOC_NODE_TYPE,
'You\'re trying to render a component to the document using ' +
'server rendering but the checksum was invalid. This usually ' +
'means you rendered a different component type or props on ' +
'the client from the one on the server, or your render() ' +
'methods are impure. React cannot handle this case due to ' +
'cross-browser quirks by rendering at the document root. You ' +
'should look for environment dependent code in your components ' +
'and ensure the props are the same client and server side.'
) : invariant(container.nodeType !== DOC_NODE_TYPE));
if ("production" !== "development") {
console.warn(
'React attempted to use reuse markup in a container but the ' +
'checksum was invalid. This generally means that you are ' +
'using server rendering and the markup generated on the ' +
'server was not what the client was expecting. React injected' +
'new markup to compensate which works but you have lost many ' +
'of the benefits of server rendering. Instead, figure out ' +
'why the markup being generated is different on the client ' +
'or server.'
);
}
}
}
("production" !== "development" ? invariant(
container.nodeType !== DOC_NODE_TYPE,
'You\'re trying to render a component to the document but ' +
'you didn\'t use server rendering. We can\'t do this ' +
'without using server rendering due to cross-browser quirks. ' +
'See renderComponentToString() for server rendering.'
) : invariant(container.nodeType !== DOC_NODE_TYPE));
// Asynchronously inject markup by ensuring that the container is not in
// the document when settings its `innerHTML`.
var parent = container.parentNode;
if (parent) {
var next = container.nextSibling;
parent.removeChild(container);
container.innerHTML = markup;
if (next) {
parent.insertBefore(container, next);
} else {
parent.appendChild(container);
}
} else {
container.innerHTML = markup;
}
}
)
};
module.exports = ReactComponentBrowserEnvironment;
},{"./ReactDOMIDOperations":40,"./ReactMarkupChecksum":59,"./ReactMount":60,"./ReactPerf":65,"./ReactReconcileTransaction":71,"./getReactRootElementInContainer":117,"./invariant":121}],32:[function(require,module,exports){
/**
* 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 ReactComponentEnvironment
*/
"use strict";
var ReactComponentBrowserEnvironment =
require("./ReactComponentBrowserEnvironment");
var ReactComponentEnvironment = ReactComponentBrowserEnvironment;
module.exports = ReactComponentEnvironment;
},{"./ReactComponentBrowserEnvironment":31}],33:[function(require,module,exports){
/**
* 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 ReactCompositeComponent
*/
"use strict";
var ReactComponent = require("./ReactComponent");
var ReactContext = require("./ReactContext");
var ReactCurrentOwner = require("./ReactCurrentOwner");
var ReactErrorUtils = require("./ReactErrorUtils");
var ReactOwner = require("./ReactOwner");
var ReactPerf = require("./ReactPerf");
var ReactPropTransferer = require("./ReactPropTransferer");
var ReactPropTypeLocations = require("./ReactPropTypeLocations");
var ReactPropTypeLocationNames = require("./ReactPropTypeLocationNames");
var ReactUpdates = require("./ReactUpdates");
var invariant = require("./invariant");
var keyMirror = require("./keyMirror");
var merge = require("./merge");
var mixInto = require("./mixInto");
var objMap = require("./objMap");
var shouldUpdateReactComponent = require("./shouldUpdateReactComponent");
/**
* Policies that describe methods in `ReactCompositeComponentInterface`.
*/
var SpecPolicy = keyMirror({
/**
* These methods may be defined only once by the class specification or mixin.
*/
DEFINE_ONCE: null,
/**
* These methods may be defined by both the class specification and mixins.
* Subsequent definitions will be chained. These methods must return void.
*/
DEFINE_MANY: null,
/**
* These methods are overriding the base ReactCompositeComponent class.
*/
OVERRIDE_BASE: null,
/**
* These methods are similar to DEFINE_MANY, except we assume they return
* objects. We try to merge the keys of the return values of all the mixed in
* functions. If there is a key conflict we throw.
*/
DEFINE_MANY_MERGED: null
});
/**
* Composite components are higher-level components that compose other composite
* or native components.
*
* To create a new type of `ReactCompositeComponent`, pass a specification of
* your new class to `React.createClass`. The only requirement of your class
* specification is that you implement a `render` method.
*
* var MyComponent = React.createClass({
* render: function() {
* return <div>Hello World</div>;
* }
* });
*
* The class specification supports a specific protocol of methods that have
* special meaning (e.g. `render`). See `ReactCompositeComponentInterface` for
* more the comprehensive protocol. Any other properties and methods in the
* class specification will available on the prototype.
*
* @interface ReactCompositeComponentInterface
* @internal
*/
var ReactCompositeComponentInterface = {
/**
* An array of Mixin objects to include when defining your component.
*
* @type {array}
* @optional
*/
mixins: SpecPolicy.DEFINE_MANY,
/**
* An object containing properties and methods that should be defined on
* the component's constructor instead of its prototype (static methods).
*
* @type {object}
* @optional
*/
statics: SpecPolicy.DEFINE_MANY,
/**
* Definition of prop types for this component.
*
* @type {object}
* @optional
*/
propTypes: SpecPolicy.DEFINE_MANY,
/**
* Definition of context types for this component.
*
* @type {object}
* @optional
*/
contextTypes: SpecPolicy.DEFINE_MANY,
/**
* Definition of context types this component sets for its children.
*
* @type {object}
* @optional
*/
childContextTypes: SpecPolicy.DEFINE_MANY,
// ==== Definition methods ====
/**
* Invoked when the component is mounted. Values in the mapping will be set on
* `this.props` if that prop is not specified (i.e. using an `in` check).
*
* This method is invoked before `getInitialState` and therefore cannot rely
* on `this.state` or use `this.setState`.
*
* @return {object}
* @optional
*/
getDefaultProps: SpecPolicy.DEFINE_MANY_MERGED,
/**
* Invoked once before the component is mounted. The return value will be used
* as the initial value of `this.state`.
*
* getInitialState: function() {
* return {
* isOn: false,
* fooBaz: new BazFoo()
* }
* }
*
* @return {object}
* @optional
*/
getInitialState: SpecPolicy.DEFINE_MANY_MERGED,
/**
* @return {object}
* @optional
*/
getChildContext: SpecPolicy.DEFINE_MANY_MERGED,
/**
* Uses props from `this.props` and state from `this.state` to render the
* structure of the component.
*
* No guarantees are made about when or how often this method is invoked, so
* it must not have side effects.
*
* render: function() {
* var name = this.props.name;
* return <div>Hello, {name}!</div>;
* }
*
* @return {ReactComponent}
* @nosideeffects
* @required
*/
render: SpecPolicy.DEFINE_ONCE,
// ==== Delegate methods ====
/**
* Invoked when the component is initially created and about to be mounted.
* This may have side effects, but any external subscriptions or data created
* by this method must be cleaned up in `componentWillUnmount`.
*
* @optional
*/
componentWillMount: SpecPolicy.DEFINE_MANY,
/**
* Invoked when the component has been mounted and has a DOM representation.
* However, there is no guarantee that the DOM node is in the document.
*
* Use this as an opportunity to operate on the DOM when the component has
* been mounted (initialized and rendered) for the first time.
*
* @param {DOMElement} rootNode DOM element representing the component.
* @optional
*/
componentDidMount: SpecPolicy.DEFINE_MANY,
/**
* Invoked before the component receives new props.
*
* Use this as an opportunity to react to a prop transition by updating the
* state using `this.setState`. Current props are accessed via `this.props`.
*
* componentWillReceiveProps: function(nextProps, nextContext) {
* this.setState({
* likesIncreasing: nextProps.likeCount > this.props.likeCount
* });
* }
*
* NOTE: There is no equivalent `componentWillReceiveState`. An incoming prop
* transition may cause a state change, but the opposite is not true. If you
* need it, you are probably looking for `componentWillUpdate`.
*
* @param {object} nextProps
* @optional
*/
componentWillReceiveProps: SpecPolicy.DEFINE_MANY,
/**
* Invoked while deciding if the component should be updated as a result of
* receiving new props, state and/or context.
*
* Use this as an opportunity to `return false` when you're certain that the
* transition to the new props/state/context will not require a component
* update.
*
* shouldComponentUpdate: function(nextProps, nextState, nextContext) {
* return !equal(nextProps, this.props) ||
* !equal(nextState, this.state) ||
* !equal(nextContext, this.context);
* }
*
* @param {object} nextProps
* @param {?object} nextState
* @param {?object} nextContext
* @return {boolean} True if the component should update.
* @optional
*/
shouldComponentUpdate: SpecPolicy.DEFINE_ONCE,
/**
* Invoked when the component is about to update due to a transition from
* `this.props`, `this.state` and `this.context` to `nextProps`, `nextState`
* and `nextContext`.
*
* Use this as an opportunity to perform preparation before an update occurs.
*
* NOTE: You **cannot** use `this.setState()` in this method.
*
* @param {object} nextProps
* @param {?object} nextState
* @param {?object} nextContext
* @param {ReactReconcileTransaction} transaction
* @optional
*/
componentWillUpdate: SpecPolicy.DEFINE_MANY,
/**
* Invoked when the component's DOM representation has been updated.
*
* Use this as an opportunity to operate on the DOM when the component has
* been updated.
*
* @param {object} prevProps
* @param {?object} prevState
* @param {?object} prevContext
* @param {DOMElement} rootNode DOM element representing the component.
* @optional
*/
componentDidUpdate: SpecPolicy.DEFINE_MANY,
/**
* Invoked when the component is about to be removed from its parent and have
* its DOM representation destroyed.
*
* Use this as an opportunity to deallocate any external resources.
*
* NOTE: There is no `componentDidUnmount` since your component will have been
* destroyed by that point.
*
* @optional
*/
componentWillUnmount: SpecPolicy.DEFINE_MANY,
// ==== Advanced methods ====
/**
* Updates the component's currently mounted DOM representation.
*
* By default, this implements React's rendering and reconciliation algorithm.
* Sophisticated clients may wish to override this.
*
* @param {ReactReconcileTransaction} transaction
* @internal
* @overridable
*/
updateComponent: SpecPolicy.OVERRIDE_BASE
};
/**
* Mapping from class specification keys to special processing functions.
*
* Although these are declared like instance properties in the specification
* when defining classes using `React.createClass`, they are actually static
* and are accessible on the constructor instead of the prototype. Despite
* being static, they must be defined outside of the "statics" key under
* which all other static methods are defined.
*/
var RESERVED_SPEC_KEYS = {
displayName: function(ConvenienceConstructor, displayName) {
ConvenienceConstructor.componentConstructor.displayName = displayName;
},
mixins: function(ConvenienceConstructor, mixins) {
if (mixins) {
for (var i = 0; i < mixins.length; i++) {
mixSpecIntoComponent(ConvenienceConstructor, mixins[i]);
}
}
},
childContextTypes: function(ConvenienceConstructor, childContextTypes) {
var Constructor = ConvenienceConstructor.componentConstructor;
validateTypeDef(
Constructor,
childContextTypes,
ReactPropTypeLocations.childContext
);
Constructor.childContextTypes = merge(
Constructor.childContextTypes,
childContextTypes
);
},
contextTypes: function(ConvenienceConstructor, contextTypes) {
var Constructor = ConvenienceConstructor.componentConstructor;
validateTypeDef(
Constructor,
contextTypes,
ReactPropTypeLocations.context
);
Constructor.contextTypes = merge(Constructor.contextTypes, contextTypes);
},
propTypes: function(ConvenienceConstructor, propTypes) {
var Constructor = ConvenienceConstructor.componentConstructor;
validateTypeDef(
Constructor,
propTypes,
ReactPropTypeLocations.prop
);
Constructor.propTypes = merge(Constructor.propTypes, propTypes);
},
statics: function(ConvenienceConstructor, statics) {
mixStaticSpecIntoComponent(ConvenienceConstructor, statics);
}
};
function validateTypeDef(Constructor, typeDef, location) {
for (var propName in typeDef) {
if (typeDef.hasOwnProperty(propName)) {
("production" !== "development" ? invariant(
typeof typeDef[propName] == 'function',
'%s: %s type `%s` is invalid; it must be a function, usually from ' +
'React.PropTypes.',
Constructor.displayName || 'ReactCompositeComponent',
ReactPropTypeLocationNames[location],
propName
) : invariant(typeof typeDef[propName] == 'function'));
}
}
}
function validateMethodOverride(proto, name) {
var specPolicy = ReactCompositeComponentInterface[name];
// Disallow overriding of base class methods unless explicitly allowed.
if (ReactCompositeComponentMixin.hasOwnProperty(name)) {
("production" !== "development" ? invariant(
specPolicy === SpecPolicy.OVERRIDE_BASE,
'ReactCompositeComponentInterface: You are attempting to override ' +
'`%s` from your class specification. Ensure that your method names ' +
'do not overlap with React methods.',
name
) : invariant(specPolicy === SpecPolicy.OVERRIDE_BASE));
}
// Disallow defining methods more than once unless explicitly allowed.
if (proto.hasOwnProperty(name)) {
("production" !== "development" ? invariant(
specPolicy === SpecPolicy.DEFINE_MANY ||
specPolicy === SpecPolicy.DEFINE_MANY_MERGED,
'ReactCompositeComponentInterface: You are attempting to define ' +
'`%s` on your component more than once. This conflict may be due ' +
'to a mixin.',
name
) : invariant(specPolicy === SpecPolicy.DEFINE_MANY ||
specPolicy === SpecPolicy.DEFINE_MANY_MERGED));
}
}
function validateLifeCycleOnReplaceState(instance) {
var compositeLifeCycleState = instance._compositeLifeCycleState;
("production" !== "development" ? invariant(
instance.isMounted() ||
compositeLifeCycleState === CompositeLifeCycle.MOUNTING,
'replaceState(...): Can only update a mounted or mounting component.'
) : invariant(instance.isMounted() ||
compositeLifeCycleState === CompositeLifeCycle.MOUNTING));
("production" !== "development" ? invariant(compositeLifeCycleState !== CompositeLifeCycle.RECEIVING_STATE,
'replaceState(...): Cannot update during an existing state transition ' +
'(such as within `render`). This could potentially cause an infinite ' +
'loop so it is forbidden.'
) : invariant(compositeLifeCycleState !== CompositeLifeCycle.RECEIVING_STATE));
("production" !== "development" ? invariant(compositeLifeCycleState !== CompositeLifeCycle.UNMOUNTING,
'replaceState(...): Cannot update while unmounting component. This ' +
'usually means you called setState() on an unmounted component.'
) : invariant(compositeLifeCycleState !== CompositeLifeCycle.UNMOUNTING));
}
/**
* Custom version of `mixInto` which handles policy validation and reserved
* specification keys when building `ReactCompositeComponent` classses.
*/
function mixSpecIntoComponent(ConvenienceConstructor, spec) {
("production" !== "development" ? invariant(
!isValidClass(spec),
'ReactCompositeComponent: You\'re attempting to ' +
'use a component class as a mixin. Instead, just use a regular object.'
) : invariant(!isValidClass(spec)));
("production" !== "development" ? invariant(
!ReactComponent.isValidComponent(spec),
'ReactCompositeComponent: You\'re attempting to ' +
'use a component as a mixin. Instead, just use a regular object.'
) : invariant(!ReactComponent.isValidComponent(spec)));
var Constructor = ConvenienceConstructor.componentConstructor;
var proto = Constructor.prototype;
for (var name in spec) {
var property = spec[name];
if (!spec.hasOwnProperty(name)) {
continue;
}
validateMethodOverride(proto, name);
if (RESERVED_SPEC_KEYS.hasOwnProperty(name)) {
RESERVED_SPEC_KEYS[name](ConvenienceConstructor, property);
} else {
// Setup methods on prototype:
// The following member methods should not be automatically bound:
// 1. Expected ReactCompositeComponent methods (in the "interface").
// 2. Overridden methods (that were mixed in).
var isCompositeComponentMethod = name in ReactCompositeComponentInterface;
var isInherited = name in proto;
var markedDontBind = property && property.__reactDontBind;
var isFunction = typeof property === 'function';
var shouldAutoBind =
isFunction &&
!isCompositeComponentMethod &&
!isInherited &&
!markedDontBind;
if (shouldAutoBind) {
if (!proto.__reactAutoBindMap) {
proto.__reactAutoBindMap = {};
}
proto.__reactAutoBindMap[name] = property;
proto[name] = property;
} else {
if (isInherited) {
// For methods which are defined more than once, call the existing
// methods before calling the new property.
if (ReactCompositeComponentInterface[name] ===
SpecPolicy.DEFINE_MANY_MERGED) {
proto[name] = createMergedResultFunction(proto[name], property);
} else {
proto[name] = createChainedFunction(proto[name], property);
}
} else {
proto[name] = property;
}
}
}
}
}
function mixStaticSpecIntoComponent(ConvenienceConstructor, statics) {
if (!statics) {
return;
}
for (var name in statics) {
var property = statics[name];
if (!statics.hasOwnProperty(name) || !property) {
return;
}
var isInherited = name in ConvenienceConstructor;
var result = property;
if (isInherited) {
var existingProperty = ConvenienceConstructor[name];
var existingType = typeof existingProperty;
var propertyType = typeof property;
("production" !== "development" ? invariant(
existingType === 'function' && propertyType === 'function',
'ReactCompositeComponent: You are attempting to define ' +
'`%s` on your component more than once, but that is only supported ' +
'for functions, which are chained together. This conflict may be ' +
'due to a mixin.',
name
) : invariant(existingType === 'function' && propertyType === 'function'));
result = createChainedFunction(existingProperty, property);
}
ConvenienceConstructor[name] = result;
ConvenienceConstructor.componentConstructor[name] = result;
}
}
/**
* Merge two objects, but throw if both contain the same key.
*
* @param {object} one The first object, which is mutated.
* @param {object} two The second object
* @return {object} one after it has been mutated to contain everything in two.
*/
function mergeObjectsWithNoDuplicateKeys(one, two) {
("production" !== "development" ? invariant(
one && two && typeof one === 'object' && typeof two === 'object',
'mergeObjectsWithNoDuplicateKeys(): Cannot merge non-objects'
) : invariant(one && two && typeof one === 'object' && typeof two === 'object'));
objMap(two, function(value, key) {
("production" !== "development" ? invariant(
one[key] === undefined,
'mergeObjectsWithNoDuplicateKeys(): ' +
'Tried to merge two objects with the same key: %s',
key
) : invariant(one[key] === undefined));
one[key] = value;
});
return one;
}
/**
* Creates a function that invokes two functions and merges their return values.
*
* @param {function} one Function to invoke first.
* @param {function} two Function to invoke second.
* @return {function} Function that invokes the two argument functions.
* @private
*/
function createMergedResultFunction(one, two) {
return function mergedResult() {
var a = one.apply(this, arguments);
var b = two.apply(this, arguments);
if (a == null) {
return b;
} else if (b == null) {
return a;
}
return mergeObjectsWithNoDuplicateKeys(a, b);
};
}
/**
* Creates a function that invokes two functions and ignores their return vales.
*
* @param {function} one Function to invoke first.
* @param {function} two Function to invoke second.
* @return {function} Function that invokes the two argument functions.
* @private
*/
function createChainedFunction(one, two) {
return function chainedFunction() {
one.apply(this, arguments);
two.apply(this, arguments);
};
}
if ("production" !== "development") {
var unmountedPropertyWhitelist = {
constructor: true,
construct: true,
isOwnedBy: true, // should be deprecated but can have code mod (internal)
mountComponent: true,
mountComponentIntoNode: true,
props: true,
type: true,
_checkPropTypes: true,
_mountComponentIntoNode: true,
_processContext: true
};
var hasWarnedOnComponentType = {};
var warnIfUnmounted = function(instance, key) {
if (instance.__hasBeenMounted) {
return;
}
var name = instance.constructor.displayName || 'Unknown';
var owner = ReactCurrentOwner.current;
var ownerName = (owner && owner.constructor.displayName) || 'Unknown';
var warningKey = key + '|' + name + '|' + ownerName;
if (hasWarnedOnComponentType.hasOwnProperty(warningKey)) {
// We have already warned for this combination. Skip it this time.
return;
}
hasWarnedOnComponentType[warningKey] = true;
var context = owner ? ' in ' + ownerName + '.' : ' at the top level.';
var staticMethodExample = '<' + name + ' />.type.' + key + '(...)';
console.warn(
'Invalid access to component property "' + key + '" on ' + name +
context + ' See http://fb.me/react-warning-descriptors .' +
' Use a static method instead: ' + staticMethodExample
);
};
var defineMembraneProperty = function(membrane, prototype, key) {
Object.defineProperty(membrane, key, {
configurable: false,
enumerable: true,
get: function() {
if (this !== membrane) {
// When this is accessed through a prototype chain we need to check if
// this component was mounted.
warnIfUnmounted(this, key);
}
return prototype[key];
},
set: function(value) {
if (this !== membrane) {
// When this is accessed through a prototype chain, we first check if
// this component was mounted. Then we define a value on "this"
// instance, effectively disabling the membrane on that prototype
// chain.
warnIfUnmounted(this, key);
Object.defineProperty(this, key, {
enumerable: true,
configurable: true,
writable: true,
value: value
});
} else {
// Otherwise, this should modify the prototype
prototype[key] = value;
}
}
});
};
/**
* Creates a membrane prototype which wraps the original prototype. If any
* property is accessed in an unmounted state, a warning is issued.
*
* @param {object} prototype Original prototype.
* @return {object} The membrane prototype.
* @private
*/
var createMountWarningMembrane = function(prototype) {
try {
var membrane = Object.create(prototype);
for (var key in prototype) {
if (unmountedPropertyWhitelist.hasOwnProperty(key)) {
continue;
}
defineMembraneProperty(membrane, prototype, key);
}
membrane.mountComponent = function() {
this.__hasBeenMounted = true;
return prototype.mountComponent.apply(this, arguments);
};
return membrane;
} catch(x) {
// In IE8 define property will fail on non-DOM objects. If anything in
// the membrane creation fails, we'll bail out and just use the prototype
// without warnings.
return prototype;
}
};
}
/**
* `ReactCompositeComponent` maintains an auxiliary life cycle state in
* `this._compositeLifeCycleState` (which can be null).
*
* This is different from the life cycle state maintained by `ReactComponent` in
* `this._lifeCycleState`. The following diagram shows how the states overlap in
* time. There are times when the CompositeLifeCycle is null - at those times it
* is only meaningful to look at ComponentLifeCycle alone.
*
* Top Row: ReactComponent.ComponentLifeCycle
* Low Row: ReactComponent.CompositeLifeCycle
*
* +-------+------------------------------------------------------+--------+
* | UN | MOUNTED | UN |
* |MOUNTED| | MOUNTED|
* +-------+------------------------------------------------------+--------+
* | ^--------+ +------+ +------+ +------+ +--------^ |
* | | | | | | | | | | | |
* | 0--|MOUNTING|-0-|RECEIV|-0-|RECEIV|-0-|RECEIV|-0-| UN |--->0 |
* | | | |PROPS | | PROPS| | STATE| |MOUNTING| |
* | | | | | | | | | | | |
* | | | | | | | | | | | |
* | +--------+ +------+ +------+ +------+ +--------+ |
* | | | |
* +-------+------------------------------------------------------+--------+
*/
var CompositeLifeCycle = keyMirror({
/**
* Components in the process of being mounted respond to state changes
* differently.
*/
MOUNTING: null,
/**
* Components in the process of being unmounted are guarded against state
* changes.
*/
UNMOUNTING: null,
/**
* Components that are mounted and receiving new props respond to state
* changes differently.
*/
RECEIVING_PROPS: null,
/**
* Components that are mounted and receiving new state are guarded against
* additional state changes.
*/
RECEIVING_STATE: null
});
/**
* @lends {ReactCompositeComponent.prototype}
*/
var ReactCompositeComponentMixin = {
/**
* Base constructor for all composite component.
*
* @param {?object} initialProps
* @param {*} children
* @final
* @internal
*/
construct: function(initialProps, children) {
// Children can be either an array or more than one argument
ReactComponent.Mixin.construct.apply(this, arguments);
this.state = null;
this._pendingState = null;
this.context = this._processContext(ReactContext.current);
this._currentContext = ReactContext.current;
this._pendingContext = null;
this._compositeLifeCycleState = null;
},
/**
* Checks whether or not this composite component is mounted.
* @return {boolean} True if mounted, false otherwise.
* @protected
* @final
*/
isMounted: function() {
return ReactComponent.Mixin.isMounted.call(this) &&
this._compositeLifeCycleState !== CompositeLifeCycle.MOUNTING;
},
/**
* Initializes the component, renders markup, and registers event listeners.
*
* @param {string} rootID DOM ID of the root node.
* @param {ReactReconcileTransaction} transaction
* @param {number} mountDepth number of components in the owner hierarchy
* @return {?string} Rendered markup to be inserted into the DOM.
* @final
* @internal
*/
mountComponent: ReactPerf.measure(
'ReactCompositeComponent',
'mountComponent',
function(rootID, transaction, mountDepth) {
ReactComponent.Mixin.mountComponent.call(
this,
rootID,
transaction,
mountDepth
);
this._compositeLifeCycleState = CompositeLifeCycle.MOUNTING;
this._defaultProps = this.getDefaultProps ? this.getDefaultProps() : null;
this.props = this._processProps(this.props);
if (this.__reactAutoBindMap) {
this._bindAutoBindMethods();
}
this.state = this.getInitialState ? this.getInitialState() : null;
("production" !== "development" ? invariant(
typeof this.state === 'object' && !Array.isArray(this.state),
'%s.getInitialState(): must return an object or null',
this.constructor.displayName || 'ReactCompositeComponent'
) : invariant(typeof this.state === 'object' && !Array.isArray(this.state)));
this._pendingState = null;
this._pendingForceUpdate = false;
if (this.componentWillMount) {
this.componentWillMount();
// When mounting, calls to `setState` by `componentWillMount` will set
// `this._pendingState` without triggering a re-render.
if (this._pendingState) {
this.state = this._pendingState;
this._pendingState = null;
}
}
this._renderedComponent = this._renderValidatedComponent();
// Done with mounting, `setState` will now trigger UI changes.
this._compositeLifeCycleState = null;
var markup = this._renderedComponent.mountComponent(
rootID,
transaction,
mountDepth + 1
);
if (this.componentDidMount) {
transaction.getReactMountReady().enqueue(this, this.componentDidMount);
}
return markup;
}
),
/**
* Releases any resources allocated by `mountComponent`.
*
* @final
* @internal
*/
unmountComponent: function() {
this._compositeLifeCycleState = CompositeLifeCycle.UNMOUNTING;
if (this.componentWillUnmount) {
this.componentWillUnmount();
}
this._compositeLifeCycleState = null;
this._defaultProps = null;
this._renderedComponent.unmountComponent();
this._renderedComponent = null;
ReactComponent.Mixin.unmountComponent.call(this);
if (this.refs) {
this.refs = null;
}
// Some existing components rely on this.props even after they've been
// destroyed (in event handlers).
// TODO: this.props = null;
// TODO: this.state = null;
},
/**
* Sets a subset of the state. Always use this or `replaceState` to mutate
* state. You should treat `this.state` as immutable.
*
* There is no guarantee that `this.state` will be immediately updated, so
* accessing `this.state` after calling this method may return the old value.
*
* There is no guarantee that calls to `setState` will run synchronously,
* as they may eventually be batched together. You can provide an optional
* callback that will be executed when the call to setState is actually
* completed.
*
* @param {object} partialState Next partial state to be merged with state.
* @param {?function} callback Called after state is updated.
* @final
* @protected
*/
setState: function(partialState, callback) {
("production" !== "development" ? invariant(
typeof partialState === 'object' || partialState == null,
'setState(...): takes an object of state variables to update.'
) : invariant(typeof partialState === 'object' || partialState == null));
if ("production" !== "development") {
if (partialState == null) {
console.warn(
'setState(...): You passed an undefined or null state object; ' +
'instead, use forceUpdate().'
);
}
}
// Merge with `_pendingState` if it exists, otherwise with existing state.
this.replaceState(
merge(this._pendingState || this.state, partialState),
callback
);
},
/**
* Replaces all of the state. Always use this or `setState` to mutate state.
* You should treat `this.state` as immutable.
*
* There is no guarantee that `this.state` will be immediately updated, so
* accessing `this.state` after calling this method may return the old value.
*
* @param {object} completeState Next state.
* @param {?function} callback Called after state is updated.
* @final
* @protected
*/
replaceState: function(completeState, callback) {
validateLifeCycleOnReplaceState(this);
this._pendingState = completeState;
ReactUpdates.enqueueUpdate(this, callback);
},
/**
* Filters the context object to only contain keys specified in
* `contextTypes`, and asserts that they are valid.
*
* @param {object} context
* @return {?object}
* @private
*/
_processContext: function(context) {
var maskedContext = null;
var contextTypes = this.constructor.contextTypes;
if (contextTypes) {
maskedContext = {};
for (var contextName in contextTypes) {
maskedContext[contextName] = context[contextName];
}
if ("production" !== "development") {
this._checkPropTypes(
contextTypes,
maskedContext,
ReactPropTypeLocations.context
);
}
}
return maskedContext;
},
/**
* @param {object} currentContext
* @return {object}
* @private
*/
_processChildContext: function(currentContext) {
var childContext = this.getChildContext && this.getChildContext();
var displayName = this.constructor.displayName || 'ReactCompositeComponent';
if (childContext) {
("production" !== "development" ? invariant(
typeof this.constructor.childContextTypes === 'object',
'%s.getChildContext(): childContextTypes must be defined in order to ' +
'use getChildContext().',
displayName
) : invariant(typeof this.constructor.childContextTypes === 'object'));
if ("production" !== "development") {
this._checkPropTypes(
this.constructor.childContextTypes,
childContext,
ReactPropTypeLocations.childContext
);
}
for (var name in childContext) {
("production" !== "development" ? invariant(
name in this.constructor.childContextTypes,
'%s.getChildContext(): key "%s" is not defined in childContextTypes.',
displayName,
name
) : invariant(name in this.constructor.childContextTypes));
}
return merge(currentContext, childContext);
}
return currentContext;
},
/**
* Processes props by setting default values for unspecified props and
* asserting that the props are valid. Does not mutate its argument; returns
* a new props object with defaults merged in.
*
* @param {object} newProps
* @return {object}
* @private
*/
_processProps: function(newProps) {
var props = merge(newProps);
var defaultProps = this._defaultProps;
for (var propName in defaultProps) {
if (typeof props[propName] === 'undefined') {
props[propName] = defaultProps[propName];
}
}
if ("production" !== "development") {
var propTypes = this.constructor.propTypes;
if (propTypes) {
this._checkPropTypes(propTypes, props, ReactPropTypeLocations.prop);
}
}
return props;
},
/**
* Assert that the props are valid
*
* @param {object} propTypes Map of prop name to a ReactPropType
* @param {object} props
* @param {string} location e.g. "prop", "context", "child context"
* @private
*/
_checkPropTypes: function(propTypes, props, location) {
var componentName = this.constructor.displayName;
for (var propName in propTypes) {
if (propTypes.hasOwnProperty(propName)) {
propTypes[propName](props, propName, componentName, location);
}
}
},
performUpdateIfNecessary: function() {
var compositeLifeCycleState = this._compositeLifeCycleState;
// Do not trigger a state transition if we are in the middle of mounting or
// receiving props because both of those will already be doing this.
if (compositeLifeCycleState === CompositeLifeCycle.MOUNTING ||
compositeLifeCycleState === CompositeLifeCycle.RECEIVING_PROPS) {
return;
}
ReactComponent.Mixin.performUpdateIfNecessary.call(this);
},
/**
* If any of `_pendingProps`, `_pendingState`, or `_pendingForceUpdate` is
* set, update the component.
*
* @param {ReactReconcileTransaction} transaction
* @internal
*/
_performUpdateIfNecessary: function(transaction) {
if (this._pendingProps == null &&
this._pendingState == null &&
this._pendingContext == null &&
!this._pendingForceUpdate) {
return;
}
var nextFullContext = this._pendingContext || this._currentContext;
var nextContext = this._processContext(nextFullContext);
this._pendingContext = null;
var nextProps = this.props;
if (this._pendingProps != null) {
nextProps = this._processProps(this._pendingProps);
this._pendingProps = null;
this._compositeLifeCycleState = CompositeLifeCycle.RECEIVING_PROPS;
if (this.componentWillReceiveProps) {
this.componentWillReceiveProps(nextProps, nextContext);
}
}
this._compositeLifeCycleState = CompositeLifeCycle.RECEIVING_STATE;
// Unlike props, state, and context, we specifically don't want to set
// _pendingOwner to null here because it's possible for a component to have
// a null owner, so we instead make `this._owner === this._pendingOwner`
// mean that there's no owner change pending.
var nextOwner = this._pendingOwner;
var nextState = this._pendingState || this.state;
this._pendingState = null;
try {
if (this._pendingForceUpdate ||
!this.shouldComponentUpdate ||
this.shouldComponentUpdate(nextProps, nextState, nextContext)) {
this._pendingForceUpdate = false;
// Will set `this.props`, `this.state` and `this.context`.
this._performComponentUpdate(
nextProps,
nextOwner,
nextState,
nextFullContext,
nextContext,
transaction
);
} else {
// If it's determined that a component should not update, we still want
// to set props and state.
this.props = nextProps;
this._owner = nextOwner;
this.state = nextState;
this._currentContext = nextFullContext;
this.context = nextContext;
}
} finally {
this._compositeLifeCycleState = null;
}
},
/**
* Merges new props and state, notifies delegate methods of update and
* performs update.
*
* @param {object} nextProps Next object to set as properties.
* @param {?ReactComponent} nextOwner Next component to set as owner
* @param {?object} nextState Next object to set as state.
* @param {?object} nextFullContext Next object to set as _currentContext.
* @param {?object} nextContext Next object to set as context.
* @param {ReactReconcileTransaction} transaction
* @private
*/
_performComponentUpdate: function(
nextProps,
nextOwner,
nextState,
nextFullContext,
nextContext,
transaction
) {
var prevProps = this.props;
var prevOwner = this._owner;
var prevState = this.state;
var prevContext = this.context;
if (this.componentWillUpdate) {
this.componentWillUpdate(nextProps, nextState, nextContext);
}
this.props = nextProps;
this._owner = nextOwner;
this.state = nextState;
this._currentContext = nextFullContext;
this.context = nextContext;
this.updateComponent(
transaction,
prevProps,
prevOwner,
prevState,
prevContext
);
if (this.componentDidUpdate) {
transaction.getReactMountReady().enqueue(
this,
this.componentDidUpdate.bind(this, prevProps, prevState, prevContext)
);
}
},
receiveComponent: function(nextComponent, transaction) {
if (nextComponent === this) {
// Since props and context are immutable after the component is
// mounted, we can do a cheap identity compare here to determine
// if this is a superfluous reconcile.
return;
}
this._pendingContext = nextComponent._currentContext;
ReactComponent.Mixin.receiveComponent.call(
this,
nextComponent,
transaction
);
},
/**
* Updates the component's currently mounted DOM representation.
*
* By default, this implements React's rendering and reconciliation algorithm.
* Sophisticated clients may wish to override this.
*
* @param {ReactReconcileTransaction} transaction
* @param {object} prevProps
* @param {?ReactComponent} prevOwner
* @param {?object} prevState
* @param {?object} prevContext
* @internal
* @overridable
*/
updateComponent: ReactPerf.measure(
'ReactCompositeComponent',
'updateComponent',
function(transaction, prevProps, prevOwner, prevState, prevContext) {
ReactComponent.Mixin.updateComponent.call(
this,
transaction,
prevProps,
prevOwner
);
var prevComponent = this._renderedComponent;
var nextComponent = this._renderValidatedComponent();
if (shouldUpdateReactComponent(prevComponent, nextComponent)) {
prevComponent.receiveComponent(nextComponent, transaction);
} else {
// These two IDs are actually the same! But nothing should rely on that.
var thisID = this._rootNodeID;
var prevComponentID = prevComponent._rootNodeID;
prevComponent.unmountComponent();
this._renderedComponent = nextComponent;
var nextMarkup = nextComponent.mountComponent(
thisID,
transaction,
this._mountDepth + 1
);
ReactComponent.BackendIDOperations.dangerouslyReplaceNodeWithMarkupByID(
prevComponentID,
nextMarkup
);
}
}
),
/**
* Forces an update. This should only be invoked when it is known with
* certainty that we are **not** in a DOM transaction.
*
* You may want to call this when you know that some deeper aspect of the
* component's state has changed but `setState` was not called.
*
* This will not invoke `shouldUpdateComponent`, but it will invoke
* `componentWillUpdate` and `componentDidUpdate`.
*
* @param {?function} callback Called after update is complete.
* @final
* @protected
*/
forceUpdate: function(callback) {
var compositeLifeCycleState = this._compositeLifeCycleState;
("production" !== "development" ? invariant(
this.isMounted() ||
compositeLifeCycleState === CompositeLifeCycle.MOUNTING,
'forceUpdate(...): Can only force an update on mounted or mounting ' +
'components.'
) : invariant(this.isMounted() ||
compositeLifeCycleState === CompositeLifeCycle.MOUNTING));
("production" !== "development" ? invariant(
compositeLifeCycleState !== CompositeLifeCycle.RECEIVING_STATE &&
compositeLifeCycleState !== CompositeLifeCycle.UNMOUNTING,
'forceUpdate(...): Cannot force an update while unmounting component ' +
'or during an existing state transition (such as within `render`).'
) : invariant(compositeLifeCycleState !== CompositeLifeCycle.RECEIVING_STATE &&
compositeLifeCycleState !== CompositeLifeCycle.UNMOUNTING));
this._pendingForceUpdate = true;
ReactUpdates.enqueueUpdate(this, callback);
},
/**
* @private
*/
_renderValidatedComponent: ReactPerf.measure(
'ReactCompositeComponent',
'_renderValidatedComponent',
function() {
var renderedComponent;
var previousContext = ReactContext.current;
ReactContext.current = this._processChildContext(this._currentContext);
ReactCurrentOwner.current = this;
try {
renderedComponent = this.render();
} finally {
ReactContext.current = previousContext;
ReactCurrentOwner.current = null;
}
("production" !== "development" ? invariant(
ReactComponent.isValidComponent(renderedComponent),
'%s.render(): A valid ReactComponent must be returned. You may have ' +
'returned null, undefined, an array, or some other invalid object.',
this.constructor.displayName || 'ReactCompositeComponent'
) : invariant(ReactComponent.isValidComponent(renderedComponent)));
return renderedComponent;
}
),
/**
* @private
*/
_bindAutoBindMethods: function() {
for (var autoBindKey in this.__reactAutoBindMap) {
if (!this.__reactAutoBindMap.hasOwnProperty(autoBindKey)) {
continue;
}
var method = this.__reactAutoBindMap[autoBindKey];
this[autoBindKey] = this._bindAutoBindMethod(ReactErrorUtils.guard(
method,
this.constructor.displayName + '.' + autoBindKey
));
}
},
/**
* Binds a method to the component.
*
* @param {function} method Method to be bound.
* @private
*/
_bindAutoBindMethod: function(method) {
var component = this;
var boundMethod = function() {
return method.apply(component, arguments);
};
if ("production" !== "development") {
boundMethod.__reactBoundContext = component;
boundMethod.__reactBoundMethod = method;
boundMethod.__reactBoundArguments = null;
var componentName = component.constructor.displayName;
var _bind = boundMethod.bind;
boundMethod.bind = function(newThis ) {var args=Array.prototype.slice.call(arguments,1);
// User is trying to bind() an autobound method; we effectively will
// ignore the value of "this" that the user is trying to use, so
// let's warn.
if (newThis !== component && newThis !== null) {
console.warn(
'bind(): React component methods may only be bound to the ' +
'component instance. See ' + componentName
);
} else if (!args.length) {
console.warn(
'bind(): You are binding a component method to the component. ' +
'React does this for you automatically in a high-performance ' +
'way, so you can safely remove this call. See ' + componentName
);
return boundMethod;
}
var reboundMethod = _bind.apply(boundMethod, arguments);
reboundMethod.__reactBoundContext = component;
reboundMethod.__reactBoundMethod = method;
reboundMethod.__reactBoundArguments = args;
return reboundMethod;
};
}
return boundMethod;
}
};
var ReactCompositeComponentBase = function() {};
mixInto(ReactCompositeComponentBase, ReactComponent.Mixin);
mixInto(ReactCompositeComponentBase, ReactOwner.Mixin);
mixInto(ReactCompositeComponentBase, ReactPropTransferer.Mixin);
mixInto(ReactCompositeComponentBase, ReactCompositeComponentMixin);
/**
* Checks if a value is a valid component constructor.
*
* @param {*}
* @return {boolean}
* @public
*/
function isValidClass(componentClass) {
return componentClass instanceof Function &&
'componentConstructor' in componentClass &&
componentClass.componentConstructor instanceof Function;
}
/**
* Module for creating composite components.
*
* @class ReactCompositeComponent
* @extends ReactComponent
* @extends ReactOwner
* @extends ReactPropTransferer
*/
var ReactCompositeComponent = {
LifeCycle: CompositeLifeCycle,
Base: ReactCompositeComponentBase,
/**
* Creates a composite component class given a class specification.
*
* @param {object} spec Class specification (which must define `render`).
* @return {function} Component constructor function.
* @public
*/
createClass: function(spec) {
var Constructor = function() {};
Constructor.prototype = new ReactCompositeComponentBase();
Constructor.prototype.constructor = Constructor;
var ConvenienceConstructor = function(props, children) {
var instance = new Constructor();
instance.construct.apply(instance, arguments);
return instance;
};
ConvenienceConstructor.componentConstructor = Constructor;
Constructor.ConvenienceConstructor = ConvenienceConstructor;
ConvenienceConstructor.originalSpec = spec;
mixSpecIntoComponent(ConvenienceConstructor, spec);
("production" !== "development" ? invariant(
Constructor.prototype.render,
'createClass(...): Class specification must implement a `render` method.'
) : invariant(Constructor.prototype.render));
if ("production" !== "development") {
if (Constructor.prototype.componentShouldUpdate) {
console.warn(
(spec.displayName || 'A component') + ' has a method called ' +
'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' +
'The name is phrased as a question because the function is ' +
'expected to return a value.'
);
}
}
// Expose the convience constructor on the prototype so that it can be
// easily accessed on descriptors. E.g. <Foo />.type === Foo.type and for
// static methods like <Foo />.type.staticMethod();
// This should not be named constructor since this may not be the function
// that created the descriptor, and it may not even be a constructor.
ConvenienceConstructor.type = Constructor;
Constructor.prototype.type = Constructor;
// Reduce time spent doing lookups by setting these on the prototype.
for (var methodName in ReactCompositeComponentInterface) {
if (!Constructor.prototype[methodName]) {
Constructor.prototype[methodName] = null;
}
}
if ("production" !== "development") {
Constructor.prototype = createMountWarningMembrane(Constructor.prototype);
}
return ConvenienceConstructor;
},
isValidClass: isValidClass
};
module.exports = ReactCompositeComponent;
},{"./ReactComponent":30,"./ReactContext":34,"./ReactCurrentOwner":35,"./ReactErrorUtils":51,"./ReactOwner":64,"./ReactPerf":65,"./ReactPropTransferer":66,"./ReactPropTypeLocationNames":67,"./ReactPropTypeLocations":68,"./ReactUpdates":80,"./invariant":121,"./keyMirror":127,"./merge":130,"./mixInto":133,"./objMap":134,"./shouldUpdateReactComponent":139}],34:[function(require,module,exports){
/**
* 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 ReactContext
*/
"use strict";
var merge = require("./merge");
/**
* Keeps track of the current context.
*
* The context is automatically passed down the component ownership hierarchy
* and is accessible via `this.context` on ReactCompositeComponents.
*/
var ReactContext = {
/**
* @internal
* @type {object}
*/
current: {},
/**
* Temporarily extends the current context while executing scopedCallback.
*
* A typical use case might look like
*
* render: function() {
* var children = ReactContext.withContext({foo: 'foo'} () => (
*
* ));
* return <div>{children}</div>;
* }
*
* @param {object} newContext New context to merge into the existing context
* @param {function} scopedCallback Callback to run with the new context
* @return {ReactComponent|array<ReactComponent>}
*/
withContext: function(newContext, scopedCallback) {
var result;
var previousContext = ReactContext.current;
ReactContext.current = merge(previousContext, newContext);
try {
result = scopedCallback();
} finally {
ReactContext.current = previousContext;
}
return result;
}
};
module.exports = ReactContext;
},{"./merge":130}],35:[function(require,module,exports){
/**
* 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 ReactCurrentOwner
*/
"use strict";
/**
* Keeps track of the current owner.
*
* The current owner is the component who should own any components that are
* currently being constructed.
*
* The depth indicate how many composite components are above this render level.
*/
var ReactCurrentOwner = {
/**
* @internal
* @type {ReactComponent}
*/
current: null
};
module.exports = ReactCurrentOwner;
},{}],36:[function(require,module,exports){
/**
* 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 ReactDOM
* @typechecks static-only
*/
"use strict";
var ReactDOMComponent = require("./ReactDOMComponent");
var mergeInto = require("./mergeInto");
var objMapKeyVal = require("./objMapKeyVal");
/**
* Creates a new React class that is idempotent and capable of containing other
* React components. It accepts event listeners and DOM properties that are
* valid according to `DOMProperty`.
*
* - Event listeners: `onClick`, `onMouseDown`, etc.
* - DOM properties: `className`, `name`, `title`, etc.
*
* The `style` property functions differently from the DOM API. It accepts an
* object mapping of style properties to values.
*
* @param {string} tag Tag name (e.g. `div`).
* @param {boolean} omitClose True if the close tag should be omitted.
* @private
*/
function createDOMComponentClass(tag, omitClose) {
var Constructor = function() {};
Constructor.prototype = new ReactDOMComponent(tag, omitClose);
Constructor.prototype.constructor = Constructor;
Constructor.displayName = tag;
var ConvenienceConstructor = function(props, children) {
var instance = new Constructor();
instance.construct.apply(instance, arguments);
return instance;
};
// Expose the constructor on the ConvenienceConstructor and prototype so that
// it can be easily easily accessed on descriptors.
// E.g. <div />.type === div.type
ConvenienceConstructor.type = Constructor;
Constructor.prototype.type = Constructor;
Constructor.ConvenienceConstructor = ConvenienceConstructor;
ConvenienceConstructor.componentConstructor = Constructor;
return ConvenienceConstructor;
}
/**
* Creates a mapping from supported HTML tags to `ReactDOMComponent` classes.
* This is also accessible via `React.DOM`.
*
* @public
*/
var ReactDOM = objMapKeyVal({
a: false,
abbr: false,
address: false,
area: false,
article: false,
aside: false,
audio: false,
b: false,
base: false,
bdi: false,
bdo: false,
big: false,
blockquote: false,
body: false,
br: true,
button: false,
canvas: false,
caption: false,
cite: false,
code: false,
col: true,
colgroup: false,
data: false,
datalist: false,
dd: false,
del: false,
details: false,
dfn: false,
div: false,
dl: false,
dt: false,
em: false,
embed: true,
fieldset: false,
figcaption: false,
figure: false,
footer: false,
form: false, // NOTE: Injected, see `ReactDOMForm`.
h1: false,
h2: false,
h3: false,
h4: false,
h5: false,
h6: false,
head: false,
header: false,
hr: true,
html: false,
i: false,
iframe: false,
img: true,
input: true,
ins: false,
kbd: false,
keygen: true,
label: false,
legend: false,
li: false,
link: false,
main: false,
map: false,
mark: false,
menu: false,
menuitem: false, // NOTE: Close tag should be omitted, but causes problems.
meta: true,
meter: false,
nav: false,
noscript: false,
object: false,
ol: false,
optgroup: false,
option: false,
output: false,
p: false,
param: true,
pre: false,
progress: false,
q: false,
rp: false,
rt: false,
ruby: false,
s: false,
samp: false,
script: false,
section: false,
select: false,
small: false,
source: false,
span: false,
strong: false,
style: false,
sub: false,
summary: false,
sup: false,
table: false,
tbody: false,
td: false,
textarea: false, // NOTE: Injected, see `ReactDOMTextarea`.
tfoot: false,
th: false,
thead: false,
time: false,
title: false,
tr: false,
track: true,
u: false,
ul: false,
'var': false,
video: false,
wbr: false,
// SVG
circle: false,
defs: false,
g: false,
line: false,
linearGradient: false,
path: false,
polygon: false,
polyline: false,
radialGradient: false,
rect: false,
stop: false,
svg: false,
text: false
}, createDOMComponentClass);
var injection = {
injectComponentClasses: function(componentClasses) {
mergeInto(ReactDOM, componentClasses);
}
};
ReactDOM.injection = injection;
module.exports = ReactDOM;
},{"./ReactDOMComponent":38,"./mergeInto":132,"./objMapKeyVal":135}],37:[function(require,module,exports){
/**
* 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 ReactDOMButton
*/
"use strict";
var AutoFocusMixin = require("./AutoFocusMixin");
var ReactCompositeComponent = require("./ReactCompositeComponent");
var ReactDOM = require("./ReactDOM");
var keyMirror = require("./keyMirror");
// Store a reference to the <button> `ReactDOMComponent`.
var button = ReactDOM.button;
var mouseListenerNames = keyMirror({
onClick: true,
onDoubleClick: true,
onMouseDown: true,
onMouseMove: true,
onMouseUp: true,
onClickCapture: true,
onDoubleClickCapture: true,
onMouseDownCapture: true,
onMouseMoveCapture: true,
onMouseUpCapture: true
});
/**
* Implements a <button> native component that does not receive mouse events
* when `disabled` is set.
*/
var ReactDOMButton = ReactCompositeComponent.createClass({
displayName: 'ReactDOMButton',
mixins: [AutoFocusMixin],
render: function() {
var props = {};
// Copy the props; except the mouse listeners if we're disabled
for (var key in this.props) {
if (this.props.hasOwnProperty(key) &&
(!this.props.disabled || !mouseListenerNames[key])) {
props[key] = this.props[key];
}
}
return button(props, this.props.children);
}
});
module.exports = ReactDOMButton;
},{"./AutoFocusMixin":1,"./ReactCompositeComponent":33,"./ReactDOM":36,"./keyMirror":127}],38:[function(require,module,exports){
/**
* 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 ReactDOMComponent
* @typechecks static-only
*/
"use strict";
var CSSPropertyOperations = require("./CSSPropertyOperations");
var DOMProperty = require("./DOMProperty");
var DOMPropertyOperations = require("./DOMPropertyOperations");
var ReactComponent = require("./ReactComponent");
var ReactEventEmitter = require("./ReactEventEmitter");
var ReactMount = require("./ReactMount");
var ReactMultiChild = require("./ReactMultiChild");
var ReactPerf = require("./ReactPerf");
var escapeTextForBrowser = require("./escapeTextForBrowser");
var invariant = require("./invariant");
var keyOf = require("./keyOf");
var merge = require("./merge");
var mixInto = require("./mixInto");
var deleteListener = ReactEventEmitter.deleteListener;
var listenTo = ReactEventEmitter.listenTo;
var registrationNameModules = ReactEventEmitter.registrationNameModules;
// For quickly matching children type, to test if can be treated as content.
var CONTENT_TYPES = {'string': true, 'number': true};
var STYLE = keyOf({style: null});
var ELEMENT_NODE_TYPE = 1;
/**
* @param {?object} props
*/
function assertValidProps(props) {
if (!props) {
return;
}
// Note the use of `==` which checks for null or undefined.
("production" !== "development" ? invariant(
props.children == null || props.dangerouslySetInnerHTML == null,
'Can only set one of `children` or `props.dangerouslySetInnerHTML`.'
) : invariant(props.children == null || props.dangerouslySetInnerHTML == null));
("production" !== "development" ? invariant(
props.style == null || typeof props.style === 'object',
'The `style` prop expects a mapping from style properties to values, ' +
'not a string.'
) : invariant(props.style == null || typeof props.style === 'object'));
}
function putListener(id, registrationName, listener, transaction) {
var container = ReactMount.findReactContainerForID(id);
if (container) {
var doc = container.nodeType === ELEMENT_NODE_TYPE ?
container.ownerDocument :
container;
listenTo(registrationName, doc);
}
transaction.getPutListenerQueue().enqueuePutListener(
id,
registrationName,
listener
);
}
/**
* @constructor ReactDOMComponent
* @extends ReactComponent
* @extends ReactMultiChild
*/
function ReactDOMComponent(tag, omitClose) {
this._tagOpen = '<' + tag;
this._tagClose = omitClose ? '' : '</' + tag + '>';
this.tagName = tag.toUpperCase();
}
ReactDOMComponent.Mixin = {
/**
* Generates root tag markup then recurses. This method has side effects and
* is not idempotent.
*
* @internal
* @param {string} rootID The root DOM ID for this node.
* @param {ReactReconcileTransaction} transaction
* @param {number} mountDepth number of components in the owner hierarchy
* @return {string} The computed markup.
*/
mountComponent: ReactPerf.measure(
'ReactDOMComponent',
'mountComponent',
function(rootID, transaction, mountDepth) {
ReactComponent.Mixin.mountComponent.call(
this,
rootID,
transaction,
mountDepth
);
assertValidProps(this.props);
return (
this._createOpenTagMarkupAndPutListeners(transaction) +
this._createContentMarkup(transaction) +
this._tagClose
);
}
),
/**
* Creates markup for the open tag and all attributes.
*
* This method has side effects because events get registered.
*
* Iterating over object properties is faster than iterating over arrays.
* @see http://jsperf.com/obj-vs-arr-iteration
*
* @private
* @param {ReactReconcileTransaction} transaction
* @return {string} Markup of opening tag.
*/
_createOpenTagMarkupAndPutListeners: function(transaction) {
var props = this.props;
var ret = this._tagOpen;
for (var propKey in props) {
if (!props.hasOwnProperty(propKey)) {
continue;
}
var propValue = props[propKey];
if (propValue == null) {
continue;
}
if (registrationNameModules[propKey]) {
putListener(this._rootNodeID, propKey, propValue, transaction);
} else {
if (propKey === STYLE) {
if (propValue) {
propValue = props.style = merge(props.style);
}
propValue = CSSPropertyOperations.createMarkupForStyles(propValue);
}
var markup =
DOMPropertyOperations.createMarkupForProperty(propKey, propValue);
if (markup) {
ret += ' ' + markup;
}
}
}
var idMarkup = DOMPropertyOperations.createMarkupForID(this._rootNodeID);
return ret + ' ' + idMarkup + '>';
},
/**
* Creates markup for the content between the tags.
*
* @private
* @param {ReactReconcileTransaction} transaction
* @return {string} Content markup.
*/
_createContentMarkup: function(transaction) {
// Intentional use of != to avoid catching zero/false.
var innerHTML = this.props.dangerouslySetInnerHTML;
if (innerHTML != null) {
if (innerHTML.__html != null) {
return innerHTML.__html;
}
} else {
var contentToUse =
CONTENT_TYPES[typeof this.props.children] ? this.props.children : null;
var childrenToUse = contentToUse != null ? null : this.props.children;
if (contentToUse != null) {
return escapeTextForBrowser(contentToUse);
} else if (childrenToUse != null) {
var mountImages = this.mountChildren(
childrenToUse,
transaction
);
return mountImages.join('');
}
}
return '';
},
receiveComponent: function(nextComponent, transaction) {
assertValidProps(nextComponent.props);
ReactComponent.Mixin.receiveComponent.call(
this,
nextComponent,
transaction
);
},
/**
* Updates a native DOM component after it has already been allocated and
* attached to the DOM. Reconciles the root DOM node, then recurses.
*
* @param {ReactReconcileTransaction} transaction
* @param {object} prevProps
* @internal
* @overridable
*/
updateComponent: ReactPerf.measure(
'ReactDOMComponent',
'updateComponent',
function(transaction, prevProps, prevOwner) {
ReactComponent.Mixin.updateComponent.call(
this,
transaction,
prevProps,
prevOwner
);
this._updateDOMProperties(prevProps, transaction);
this._updateDOMChildren(prevProps, transaction);
}
),
/**
* Reconciles the properties by detecting differences in property values and
* updating the DOM as necessary. This function is probably the single most
* critical path for performance optimization.
*
* TODO: Benchmark whether checking for changed values in memory actually
* improves performance (especially statically positioned elements).
* TODO: Benchmark the effects of putting this at the top since 99% of props
* do not change for a given reconciliation.
* TODO: Benchmark areas that can be improved with caching.
*
* @private
* @param {object} lastProps
* @param {ReactReconcileTransaction} transaction
*/
_updateDOMProperties: function(lastProps, transaction) {
var nextProps = this.props;
var propKey;
var styleName;
var styleUpdates;
for (propKey in lastProps) {
if (nextProps.hasOwnProperty(propKey) ||
!lastProps.hasOwnProperty(propKey)) {
continue;
}
if (propKey === STYLE) {
var lastStyle = lastProps[propKey];
for (styleName in lastStyle) {
if (lastStyle.hasOwnProperty(styleName)) {
styleUpdates = styleUpdates || {};
styleUpdates[styleName] = '';
}
}
} else if (registrationNameModules[propKey]) {
deleteListener(this._rootNodeID, propKey);
} else if (
DOMProperty.isStandardName[propKey] ||
DOMProperty.isCustomAttribute(propKey)) {
ReactComponent.BackendIDOperations.deletePropertyByID(
this._rootNodeID,
propKey
);
}
}
for (propKey in nextProps) {
var nextProp = nextProps[propKey];
var lastProp = lastProps[propKey];
if (!nextProps.hasOwnProperty(propKey) || nextProp === lastProp) {
continue;
}
if (propKey === STYLE) {
if (nextProp) {
nextProp = nextProps.style = merge(nextProp);
}
if (lastProp) {
// Unset styles on `lastProp` but not on `nextProp`.
for (styleName in lastProp) {
if (lastProp.hasOwnProperty(styleName) &&
!nextProp.hasOwnProperty(styleName)) {
styleUpdates = styleUpdates || {};
styleUpdates[styleName] = '';
}
}
// Update styles that changed since `lastProp`.
for (styleName in nextProp) {
if (nextProp.hasOwnProperty(styleName) &&
lastProp[styleName] !== nextProp[styleName]) {
styleUpdates = styleUpdates || {};
styleUpdates[styleName] = nextProp[styleName];
}
}
} else {
// Relies on `updateStylesByID` not mutating `styleUpdates`.
styleUpdates = nextProp;
}
} else if (registrationNameModules[propKey]) {
putListener(this._rootNodeID, propKey, nextProp, transaction);
} else if (
DOMProperty.isStandardName[propKey] ||
DOMProperty.isCustomAttribute(propKey)) {
ReactComponent.BackendIDOperations.updatePropertyByID(
this._rootNodeID,
propKey,
nextProp
);
}
}
if (styleUpdates) {
ReactComponent.BackendIDOperations.updateStylesByID(
this._rootNodeID,
styleUpdates
);
}
},
/**
* Reconciles the children with the various properties that affect the
* children content.
*
* @param {object} lastProps
* @param {ReactReconcileTransaction} transaction
*/
_updateDOMChildren: function(lastProps, transaction) {
var nextProps = this.props;
var lastContent =
CONTENT_TYPES[typeof lastProps.children] ? lastProps.children : null;
var nextContent =
CONTENT_TYPES[typeof nextProps.children] ? nextProps.children : null;
var lastHtml =
lastProps.dangerouslySetInnerHTML &&
lastProps.dangerouslySetInnerHTML.__html;
var nextHtml =
nextProps.dangerouslySetInnerHTML &&
nextProps.dangerouslySetInnerHTML.__html;
// Note the use of `!=` which checks for null or undefined.
var lastChildren = lastContent != null ? null : lastProps.children;
var nextChildren = nextContent != null ? null : nextProps.children;
// If we're switching from children to content/html or vice versa, remove
// the old content
var lastHasContentOrHtml = lastContent != null || lastHtml != null;
var nextHasContentOrHtml = nextContent != null || nextHtml != null;
if (lastChildren != null && nextChildren == null) {
this.updateChildren(null, transaction);
} else if (lastHasContentOrHtml && !nextHasContentOrHtml) {
this.updateTextContent('');
}
if (nextContent != null) {
if (lastContent !== nextContent) {
this.updateTextContent('' + nextContent);
}
} else if (nextHtml != null) {
if (lastHtml !== nextHtml) {
ReactComponent.BackendIDOperations.updateInnerHTMLByID(
this._rootNodeID,
nextHtml
);
}
} else if (nextChildren != null) {
this.updateChildren(nextChildren, transaction);
}
},
/**
* Destroys all event registrations for this instance. Does not remove from
* the DOM. That must be done by the parent.
*
* @internal
*/
unmountComponent: function() {
this.unmountChildren();
ReactEventEmitter.deleteAllListeners(this._rootNodeID);
ReactComponent.Mixin.unmountComponent.call(this);
}
};
mixInto(ReactDOMComponent, ReactComponent.Mixin);
mixInto(ReactDOMComponent, ReactDOMComponent.Mixin);
mixInto(ReactDOMComponent, ReactMultiChild.Mixin);
module.exports = ReactDOMComponent;
},{"./CSSPropertyOperations":4,"./DOMProperty":9,"./DOMPropertyOperations":10,"./ReactComponent":30,"./ReactEventEmitter":52,"./ReactMount":60,"./ReactMultiChild":62,"./ReactPerf":65,"./escapeTextForBrowser":109,"./invariant":121,"./keyOf":128,"./merge":130,"./mixInto":133}],39:[function(require,module,exports){
/**
* 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 ReactDOMForm
*/
"use strict";
var ReactCompositeComponent = require("./ReactCompositeComponent");
var ReactDOM = require("./ReactDOM");
var ReactEventEmitter = require("./ReactEventEmitter");
var EventConstants = require("./EventConstants");
// Store a reference to the <form> `ReactDOMComponent`.
var form = ReactDOM.form;
/**
* Since onSubmit doesn't bubble OR capture on the top level in IE8, we need
* to capture it on the <form> element itself. There are lots of hacks we could
* do to accomplish this, but the most reliable is to make <form> a
* composite component and use `componentDidMount` to attach the event handlers.
*/
var ReactDOMForm = ReactCompositeComponent.createClass({
displayName: 'ReactDOMForm',
render: function() {
// TODO: Instead of using `ReactDOM` directly, we should use JSX. However,
// `jshint` fails to parse JSX so in order for linting to work in the open
// source repo, we need to just use `ReactDOM.form`.
return this.transferPropsTo(form(null, this.props.children));
},
componentDidMount: function() {
ReactEventEmitter.trapBubbledEvent(
EventConstants.topLevelTypes.topReset,
'reset',
this.getDOMNode()
);
ReactEventEmitter.trapBubbledEvent(
EventConstants.topLevelTypes.topSubmit,
'submit',
this.getDOMNode()
);
}
});
module.exports = ReactDOMForm;
},{"./EventConstants":15,"./ReactCompositeComponent":33,"./ReactDOM":36,"./ReactEventEmitter":52}],40:[function(require,module,exports){
/**
* 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 ReactDOMIDOperations
* @typechecks static-only
*/
/*jslint evil: true */
"use strict";
var CSSPropertyOperations = require("./CSSPropertyOperations");
var DOMChildrenOperations = require("./DOMChildrenOperations");
var DOMPropertyOperations = require("./DOMPropertyOperations");
var ReactMount = require("./ReactMount");
var ReactPerf = require("./ReactPerf");
var invariant = require("./invariant");
/**
* Errors for properties that should not be updated with `updatePropertyById()`.
*
* @type {object}
* @private
*/
var INVALID_PROPERTY_ERRORS = {
dangerouslySetInnerHTML:
'`dangerouslySetInnerHTML` must be set using `updateInnerHTMLByID()`.',
style: '`style` must be set using `updateStylesByID()`.'
};
var useWhitespaceWorkaround;
/**
* Operations used to process updates to DOM nodes. This is made injectable via
* `ReactComponent.BackendIDOperations`.
*/
var ReactDOMIDOperations = {
/**
* Updates a DOM node with new property values. This should only be used to
* update DOM properties in `DOMProperty`.
*
* @param {string} id ID of the node to update.
* @param {string} name A valid property name, see `DOMProperty`.
* @param {*} value New value of the property.
* @internal
*/
updatePropertyByID: ReactPerf.measure(
'ReactDOMIDOperations',
'updatePropertyByID',
function(id, name, value) {
var node = ReactMount.getNode(id);
("production" !== "development" ? invariant(
!INVALID_PROPERTY_ERRORS.hasOwnProperty(name),
'updatePropertyByID(...): %s',
INVALID_PROPERTY_ERRORS[name]
) : invariant(!INVALID_PROPERTY_ERRORS.hasOwnProperty(name)));
// If we're updating to null or undefined, we should remove the property
// from the DOM node instead of inadvertantly setting to a string. This
// brings us in line with the same behavior we have on initial render.
if (value != null) {
DOMPropertyOperations.setValueForProperty(node, name, value);
} else {
DOMPropertyOperations.deleteValueForProperty(node, name);
}
}
),
/**
* Updates a DOM node to remove a property. This should only be used to remove
* DOM properties in `DOMProperty`.
*
* @param {string} id ID of the node to update.
* @param {string} name A property name to remove, see `DOMProperty`.
* @internal
*/
deletePropertyByID: ReactPerf.measure(
'ReactDOMIDOperations',
'deletePropertyByID',
function(id, name, value) {
var node = ReactMount.getNode(id);
("production" !== "development" ? invariant(
!INVALID_PROPERTY_ERRORS.hasOwnProperty(name),
'updatePropertyByID(...): %s',
INVALID_PROPERTY_ERRORS[name]
) : invariant(!INVALID_PROPERTY_ERRORS.hasOwnProperty(name)));
DOMPropertyOperations.deleteValueForProperty(node, name, value);
}
),
/**
* Updates a DOM node with new style values. If a value is specified as '',
* the corresponding style property will be unset.
*
* @param {string} id ID of the node to update.
* @param {object} styles Mapping from styles to values.
* @internal
*/
updateStylesByID: ReactPerf.measure(
'ReactDOMIDOperations',
'updateStylesByID',
function(id, styles) {
var node = ReactMount.getNode(id);
CSSPropertyOperations.setValueForStyles(node, styles);
}
),
/**
* Updates a DOM node's innerHTML.
*
* @param {string} id ID of the node to update.
* @param {string} html An HTML string.
* @internal
*/
updateInnerHTMLByID: ReactPerf.measure(
'ReactDOMIDOperations',
'updateInnerHTMLByID',
function(id, html) {
var node = ReactMount.getNode(id);
// IE8: When updating a just created node with innerHTML only leading
// whitespace is removed. When updating an existing node with innerHTML
// whitespace in root TextNodes is also collapsed.
// @see quirksmode.org/bugreports/archives/2004/11/innerhtml_and_t.html
if (useWhitespaceWorkaround === undefined) {
// Feature detection; only IE8 is known to behave improperly like this.
var temp = document.createElement('div');
temp.innerHTML = ' ';
useWhitespaceWorkaround = temp.innerHTML === '';
}
if (useWhitespaceWorkaround) {
// Magic theory: IE8 supposedly differentiates between added and updated
// nodes when processing innerHTML, innerHTML on updated nodes suffers
// from worse whitespace behavior. Re-adding a node like this triggers
// the initial and more favorable whitespace behavior.
node.parentNode.replaceChild(node, node);
}
if (useWhitespaceWorkaround && html.match(/^[ \r\n\t\f]/)) {
// Recover leading whitespace by temporarily prepending any character.
// \uFEFF has the potential advantage of being zero-width/invisible.
node.innerHTML = '\uFEFF' + html;
node.firstChild.deleteData(0, 1);
} else {
node.innerHTML = html;
}
}
),
/**
* Updates a DOM node's text content set by `props.content`.
*
* @param {string} id ID of the node to update.
* @param {string} content Text content.
* @internal
*/
updateTextContentByID: ReactPerf.measure(
'ReactDOMIDOperations',
'updateTextContentByID',
function(id, content) {
var node = ReactMount.getNode(id);
DOMChildrenOperations.updateTextContent(node, content);
}
),
/**
* Replaces a DOM node that exists in the document with markup.
*
* @param {string} id ID of child to be replaced.
* @param {string} markup Dangerous markup to inject in place of child.
* @internal
* @see {Danger.dangerouslyReplaceNodeWithMarkup}
*/
dangerouslyReplaceNodeWithMarkupByID: ReactPerf.measure(
'ReactDOMIDOperations',
'dangerouslyReplaceNodeWithMarkupByID',
function(id, markup) {
var node = ReactMount.getNode(id);
DOMChildrenOperations.dangerouslyReplaceNodeWithMarkup(node, markup);
}
),
/**
* Updates a component's children by processing a series of updates.
*
* @param {array<object>} updates List of update configurations.
* @param {array<string>} markup List of markup strings.
* @internal
*/
dangerouslyProcessChildrenUpdates: ReactPerf.measure(
'ReactDOMIDOperations',
'dangerouslyProcessChildrenUpdates',
function(updates, markup) {
for (var i = 0; i < updates.length; i++) {
updates[i].parentNode = ReactMount.getNode(updates[i].parentID);
}
DOMChildrenOperations.processUpdates(updates, markup);
}
)
};
module.exports = ReactDOMIDOperations;
},{"./CSSPropertyOperations":4,"./DOMChildrenOperations":8,"./DOMPropertyOperations":10,"./ReactMount":60,"./ReactPerf":65,"./invariant":121}],41:[function(require,module,exports){
/**
* 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 ReactDOMImg
*/
"use strict";
var ReactCompositeComponent = require("./ReactCompositeComponent");
var ReactDOM = require("./ReactDOM");
var ReactEventEmitter = require("./ReactEventEmitter");
var EventConstants = require("./EventConstants");
// Store a reference to the <img> `ReactDOMComponent`.
var img = ReactDOM.img;
/**
* Since onLoad doesn't bubble OR capture on the top level in IE8, we need to
* capture it on the <img> element itself. There are lots of hacks we could do
* to accomplish this, but the most reliable is to make <img> a composite
* component and use `componentDidMount` to attach the event handlers.
*/
var ReactDOMImg = ReactCompositeComponent.createClass({
displayName: 'ReactDOMImg',
tagName: 'IMG',
render: function() {
return img(this.props);
},
componentDidMount: function() {
var node = this.getDOMNode();
ReactEventEmitter.trapBubbledEvent(
EventConstants.topLevelTypes.topLoad,
'load',
node
);
ReactEventEmitter.trapBubbledEvent(
EventConstants.topLevelTypes.topError,
'error',
node
);
}
});
module.exports = ReactDOMImg;
},{"./EventConstants":15,"./ReactCompositeComponent":33,"./ReactDOM":36,"./ReactEventEmitter":52}],42:[function(require,module,exports){
/**
* 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 ReactDOMInput
*/
"use strict";
var AutoFocusMixin = require("./AutoFocusMixin");
var DOMPropertyOperations = require("./DOMPropertyOperations");
var LinkedValueUtils = require("./LinkedValueUtils");
var ReactCompositeComponent = require("./ReactCompositeComponent");
var ReactDOM = require("./ReactDOM");
var ReactMount = require("./ReactMount");
var invariant = require("./invariant");
var merge = require("./merge");
// Store a reference to the <input> `ReactDOMComponent`.
var input = ReactDOM.input;
var instancesByReactID = {};
/**
* Implements an <input> native component that allows setting these optional
* props: `checked`, `value`, `defaultChecked`, and `defaultValue`.
*
* If `checked` or `value` are not supplied (or null/undefined), user actions
* that affect the checked state or value will trigger updates to the element.
*
* If they are supplied (and not null/undefined), the rendered element will not
* trigger updates to the element. Instead, the props must change in order for
* the rendered element to be updated.
*
* The rendered element will be initialized as unchecked (or `defaultChecked`)
* with an empty value (or `defaultValue`).
*
* @see http://www.w3.org/TR/2012/WD-html5-20121025/the-input-element.html
*/
var ReactDOMInput = ReactCompositeComponent.createClass({
displayName: 'ReactDOMInput',
mixins: [AutoFocusMixin, LinkedValueUtils.Mixin],
getInitialState: function() {
var defaultValue = this.props.defaultValue;
return {
checked: this.props.defaultChecked || false,
value: defaultValue != null ? defaultValue : null
};
},
shouldComponentUpdate: function() {
// Defer any updates to this component during the `onChange` handler.
return !this._isChanging;
},
render: function() {
// Clone `this.props` so we don't mutate the input.
var props = merge(this.props);
props.defaultChecked = null;
props.defaultValue = null;
var value = LinkedValueUtils.getValue(this);
props.value = value != null ? value : this.state.value;
var checked = LinkedValueUtils.getChecked(this);
props.checked = checked != null ? checked : this.state.checked;
props.onChange = this._handleChange;
return input(props, this.props.children);
},
componentDidMount: function() {
var id = ReactMount.getID(this.getDOMNode());
instancesByReactID[id] = this;
},
componentWillUnmount: function() {
var rootNode = this.getDOMNode();
var id = ReactMount.getID(rootNode);
delete instancesByReactID[id];
},
componentDidUpdate: function(prevProps, prevState, prevContext) {
var rootNode = this.getDOMNode();
if (this.props.checked != null) {
DOMPropertyOperations.setValueForProperty(
rootNode,
'checked',
this.props.checked || false
);
}
var value = LinkedValueUtils.getValue(this);
if (value != null) {
// Cast `value` to a string to ensure the value is set correctly. While
// browsers typically do this as necessary, jsdom doesn't.
DOMPropertyOperations.setValueForProperty(rootNode, 'value', '' + value);
}
},
_handleChange: function(event) {
var returnValue;
var onChange = LinkedValueUtils.getOnChange(this);
if (onChange) {
this._isChanging = true;
returnValue = onChange.call(this, event);
this._isChanging = false;
}
this.setState({
checked: event.target.checked,
value: event.target.value
});
var name = this.props.name;
if (this.props.type === 'radio' && name != null) {
var rootNode = this.getDOMNode();
var queryRoot = rootNode;
while (queryRoot.parentNode) {
queryRoot = queryRoot.parentNode;
}
// If `rootNode.form` was non-null, then we could try `form.elements`,
// but that sometimes behaves strangely in IE8. We could also try using
// `form.getElementsByName`, but that will only return direct children
// and won't include inputs that use the HTML5 `form=` attribute. Since
// the input might not even be in a form, let's just use the global
// `querySelectorAll` to ensure we don't miss anything.
var group = queryRoot.querySelectorAll(
'input[name=' + JSON.stringify('' + name) + '][type="radio"]');
for (var i = 0, groupLen = group.length; i < groupLen; i++) {
var otherNode = group[i];
if (otherNode === rootNode ||
otherNode.form !== rootNode.form) {
continue;
}
var otherID = ReactMount.getID(otherNode);
("production" !== "development" ? invariant(
otherID,
'ReactDOMInput: Mixing React and non-React radio inputs with the ' +
'same `name` is not supported.'
) : invariant(otherID));
var otherInstance = instancesByReactID[otherID];
("production" !== "development" ? invariant(
otherInstance,
'ReactDOMInput: Unknown radio button ID %s.',
otherID
) : invariant(otherInstance));
// In some cases, this will actually change the `checked` state value.
// In other cases, there's no change but this forces a reconcile upon
// which componentDidUpdate will reset the DOM property to whatever it
// should be.
otherInstance.setState({
checked: false
});
}
}
return returnValue;
}
});
module.exports = ReactDOMInput;
},{"./AutoFocusMixin":1,"./DOMPropertyOperations":10,"./LinkedValueUtils":23,"./ReactCompositeComponent":33,"./ReactDOM":36,"./ReactMount":60,"./invariant":121,"./merge":130}],43:[function(require,module,exports){
/**
* 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 ReactDOMOption
*/
"use strict";
var ReactCompositeComponent = require("./ReactCompositeComponent");
var ReactDOM = require("./ReactDOM");
// Store a reference to the <option> `ReactDOMComponent`.
var option = ReactDOM.option;
/**
* Implements an <option> native component that warns when `selected` is set.
*/
var ReactDOMOption = ReactCompositeComponent.createClass({
displayName: 'ReactDOMOption',
componentWillMount: function() {
// TODO (yungsters): Remove support for `selected` in <option>.
if (this.props.selected != null) {
if ("production" !== "development") {
console.warn(
'Use the `defaultValue` or `value` props on <select> instead of ' +
'setting `selected` on <option>.'
);
}
}
},
render: function() {
return option(this.props, this.props.children);
}
});
module.exports = ReactDOMOption;
},{"./ReactCompositeComponent":33,"./ReactDOM":36}],44:[function(require,module,exports){
/**
* 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 ReactDOMSelect
*/
"use strict";
var AutoFocusMixin = require("./AutoFocusMixin");
var LinkedValueUtils = require("./LinkedValueUtils");
var ReactCompositeComponent = require("./ReactCompositeComponent");
var ReactDOM = require("./ReactDOM");
var invariant = require("./invariant");
var merge = require("./merge");
// Store a reference to the <select> `ReactDOMComponent`.
var select = ReactDOM.select;
/**
* Validation function for `value` and `defaultValue`.
* @private
*/
function selectValueType(props, propName, componentName) {
if (props[propName] == null) {
return;
}
if (props.multiple) {
("production" !== "development" ? invariant(
Array.isArray(props[propName]),
'The `%s` prop supplied to <select> must be an array if `multiple` is ' +
'true.',
propName
) : invariant(Array.isArray(props[propName])));
} else {
("production" !== "development" ? invariant(
!Array.isArray(props[propName]),
'The `%s` prop supplied to <select> must be a scalar value if ' +
'`multiple` is false.',
propName
) : invariant(!Array.isArray(props[propName])));
}
}
/**
* If `value` is supplied, updates <option> elements on mount and update.
* @param {ReactComponent} component Instance of ReactDOMSelect
* @param {?*} propValue For uncontrolled components, null/undefined. For
* controlled components, a string (or with `multiple`, a list of strings).
* @private
*/
function updateOptions(component, propValue) {
var multiple = component.props.multiple;
var value = propValue != null ? propValue : component.state.value;
var options = component.getDOMNode().options;
var selectedValue, i, l;
if (multiple) {
selectedValue = {};
for (i = 0, l = value.length; i < l; ++i) {
selectedValue['' + value[i]] = true;
}
} else {
selectedValue = '' + value;
}
for (i = 0, l = options.length; i < l; i++) {
var selected = multiple ?
selectedValue.hasOwnProperty(options[i].value) :
options[i].value === selectedValue;
if (selected !== options[i].selected) {
options[i].selected = selected;
}
}
}
/**
* Implements a <select> native component that allows optionally setting the
* props `value` and `defaultValue`. If `multiple` is false, the prop must be a
* string. If `multiple` is true, the prop must be an array of strings.
*
* If `value` is not supplied (or null/undefined), user actions that change the
* selected option will trigger updates to the rendered options.
*
* If it is supplied (and not null/undefined), the rendered options will not
* update in response to user actions. Instead, the `value` prop must change in
* order for the rendered options to update.
*
* If `defaultValue` is provided, any options with the supplied values will be
* selected.
*/
var ReactDOMSelect = ReactCompositeComponent.createClass({
displayName: 'ReactDOMSelect',
mixins: [AutoFocusMixin, LinkedValueUtils.Mixin],
propTypes: {
defaultValue: selectValueType,
value: selectValueType
},
getInitialState: function() {
return {value: this.props.defaultValue || (this.props.multiple ? [] : '')};
},
componentWillReceiveProps: function(nextProps) {
if (!this.props.multiple && nextProps.multiple) {
this.setState({value: [this.state.value]});
} else if (this.props.multiple && !nextProps.multiple) {
this.setState({value: this.state.value[0]});
}
},
shouldComponentUpdate: function() {
// Defer any updates to this component during the `onChange` handler.
return !this._isChanging;
},
render: function() {
// Clone `this.props` so we don't mutate the input.
var props = merge(this.props);
props.onChange = this._handleChange;
props.value = null;
return select(props, this.props.children);
},
componentDidMount: function() {
updateOptions(this, LinkedValueUtils.getValue(this));
},
componentDidUpdate: function() {
var value = LinkedValueUtils.getValue(this);
if (value != null) {
updateOptions(this, value);
}
},
_handleChange: function(event) {
var returnValue;
var onChange = LinkedValueUtils.getOnChange(this);
if (onChange) {
this._isChanging = true;
returnValue = onChange.call(this, event);
this._isChanging = false;
}
var selectedValue;
if (this.props.multiple) {
selectedValue = [];
var options = event.target.options;
for (var i = 0, l = options.length; i < l; i++) {
if (options[i].selected) {
selectedValue.push(options[i].value);
}
}
} else {
selectedValue = event.target.value;
}
this.setState({value: selectedValue});
return returnValue;
}
});
module.exports = ReactDOMSelect;
},{"./AutoFocusMixin":1,"./LinkedValueUtils":23,"./ReactCompositeComponent":33,"./ReactDOM":36,"./invariant":121,"./merge":130}],45:[function(require,module,exports){
/**
* 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 ReactDOMSelection
*/
"use strict";
var getNodeForCharacterOffset = require("./getNodeForCharacterOffset");
var getTextContentAccessor = require("./getTextContentAccessor");
/**
* Get the appropriate anchor and focus node/offset pairs for IE.
*
* The catch here is that IE's selection API doesn't provide information
* about whether the selection is forward or backward, so we have to
* behave as though it's always forward.
*
* IE text differs from modern selection in that it behaves as though
* block elements end with a new line. This means character offsets will
* differ between the two APIs.
*
* @param {DOMElement} node
* @return {object}
*/
function getIEOffsets(node) {
var selection = document.selection;
var selectedRange = selection.createRange();
var selectedLength = selectedRange.text.length;
// Duplicate selection so we can move range without breaking user selection.
var fromStart = selectedRange.duplicate();
fromStart.moveToElementText(node);
fromStart.setEndPoint('EndToStart', selectedRange);
var startOffset = fromStart.text.length;
var endOffset = startOffset + selectedLength;
return {
start: startOffset,
end: endOffset
};
}
/**
* @param {DOMElement} node
* @return {?object}
*/
function getModernOffsets(node) {
var selection = window.getSelection();
if (selection.rangeCount === 0) {
return null;
}
var anchorNode = selection.anchorNode;
var anchorOffset = selection.anchorOffset;
var focusNode = selection.focusNode;
var focusOffset = selection.focusOffset;
var currentRange = selection.getRangeAt(0);
var rangeLength = currentRange.toString().length;
var tempRange = currentRange.cloneRange();
tempRange.selectNodeContents(node);
tempRange.setEnd(currentRange.startContainer, currentRange.startOffset);
var start = tempRange.toString().length;
var end = start + rangeLength;
// Detect whether the selection is backward.
var detectionRange = document.createRange();
detectionRange.setStart(anchorNode, anchorOffset);
detectionRange.setEnd(focusNode, focusOffset);
var isBackward = detectionRange.collapsed;
detectionRange.detach();
return {
start: isBackward ? end : start,
end: isBackward ? start : end
};
}
/**
* @param {DOMElement|DOMTextNode} node
* @param {object} offsets
*/
function setIEOffsets(node, offsets) {
var range = document.selection.createRange().duplicate();
var start, end;
if (typeof offsets.end === 'undefined') {
start = offsets.start;
end = start;
} else if (offsets.start > offsets.end) {
start = offsets.end;
end = offsets.start;
} else {
start = offsets.start;
end = offsets.end;
}
range.moveToElementText(node);
range.moveStart('character', start);
range.setEndPoint('EndToStart', range);
range.moveEnd('character', end - start);
range.select();
}
/**
* In modern non-IE browsers, we can support both forward and backward
* selections.
*
* Note: IE10+ supports the Selection object, but it does not support
* the `extend` method, which means that even in modern IE, it's not possible
* to programatically create a backward selection. Thus, for all IE
* versions, we use the old IE API to create our selections.
*
* @param {DOMElement|DOMTextNode} node
* @param {object} offsets
*/
function setModernOffsets(node, offsets) {
var selection = window.getSelection();
var length = node[getTextContentAccessor()].length;
var start = Math.min(offsets.start, length);
var end = typeof offsets.end === 'undefined' ?
start : Math.min(offsets.end, length);
// IE 11 uses modern selection, but doesn't support the extend method.
// Flip backward selections, so we can set with a single range.
if (!selection.extend && start > end) {
var temp = end;
end = start;
start = temp;
}
var startMarker = getNodeForCharacterOffset(node, start);
var endMarker = getNodeForCharacterOffset(node, end);
if (startMarker && endMarker) {
var range = document.createRange();
range.setStart(startMarker.node, startMarker.offset);
selection.removeAllRanges();
if (start > end) {
selection.addRange(range);
selection.extend(endMarker.node, endMarker.offset);
} else {
range.setEnd(endMarker.node, endMarker.offset);
selection.addRange(range);
}
range.detach();
}
}
var ReactDOMSelection = {
/**
* @param {DOMElement} node
*/
getOffsets: function(node) {
var getOffsets = document.selection ? getIEOffsets : getModernOffsets;
return getOffsets(node);
},
/**
* @param {DOMElement|DOMTextNode} node
* @param {object} offsets
*/
setOffsets: function(node, offsets) {
var setOffsets = document.selection ? setIEOffsets : setModernOffsets;
setOffsets(node, offsets);
}
};
module.exports = ReactDOMSelection;
},{"./getNodeForCharacterOffset":116,"./getTextContentAccessor":118}],46:[function(require,module,exports){
/**
* 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 ReactDOMTextarea
*/
"use strict";
var AutoFocusMixin = require("./AutoFocusMixin");
var DOMPropertyOperations = require("./DOMPropertyOperations");
var LinkedValueUtils = require("./LinkedValueUtils");
var ReactCompositeComponent = require("./ReactCompositeComponent");
var ReactDOM = require("./ReactDOM");
var invariant = require("./invariant");
var merge = require("./merge");
// Store a reference to the <textarea> `ReactDOMComponent`.
var textarea = ReactDOM.textarea;
/**
* Implements a <textarea> native component that allows setting `value`, and
* `defaultValue`. This differs from the traditional DOM API because value is
* usually set as PCDATA children.
*
* If `value` is not supplied (or null/undefined), user actions that affect the
* value will trigger updates to the element.
*
* If `value` is supplied (and not null/undefined), the rendered element will
* not trigger updates to the element. Instead, the `value` prop must change in
* order for the rendered element to be updated.
*
* The rendered element will be initialized with an empty value, the prop
* `defaultValue` if specified, or the children content (deprecated).
*/
var ReactDOMTextarea = ReactCompositeComponent.createClass({
displayName: 'ReactDOMTextarea',
mixins: [AutoFocusMixin, LinkedValueUtils.Mixin],
getInitialState: function() {
var defaultValue = this.props.defaultValue;
// TODO (yungsters): Remove support for children content in <textarea>.
var children = this.props.children;
if (children != null) {
if ("production" !== "development") {
console.warn(
'Use the `defaultValue` or `value` props instead of setting ' +
'children on <textarea>.'
);
}
("production" !== "development" ? invariant(
defaultValue == null,
'If you supply `defaultValue` on a <textarea>, do not pass children.'
) : invariant(defaultValue == null));
if (Array.isArray(children)) {
("production" !== "development" ? invariant(
children.length <= 1,
'<textarea> can only have at most one child.'
) : invariant(children.length <= 1));
children = children[0];
}
defaultValue = '' + children;
}
if (defaultValue == null) {
defaultValue = '';
}
var value = LinkedValueUtils.getValue(this);
return {
// We save the initial value so that `ReactDOMComponent` doesn't update
// `textContent` (unnecessary since we update value).
// The initial value can be a boolean or object so that's why it's
// forced to be a string.
initialValue: '' + (value != null ? value : defaultValue),
value: defaultValue
};
},
shouldComponentUpdate: function() {
// Defer any updates to this component during the `onChange` handler.
return !this._isChanging;
},
render: function() {
// Clone `this.props` so we don't mutate the input.
var props = merge(this.props);
var value = LinkedValueUtils.getValue(this);
("production" !== "development" ? invariant(
props.dangerouslySetInnerHTML == null,
'`dangerouslySetInnerHTML` does not make sense on <textarea>.'
) : invariant(props.dangerouslySetInnerHTML == null));
props.defaultValue = null;
props.value = value != null ? value : this.state.value;
props.onChange = this._handleChange;
// Always set children to the same thing. In IE9, the selection range will
// get reset if `textContent` is mutated.
return textarea(props, this.state.initialValue);
},
componentDidUpdate: function(prevProps, prevState, prevContext) {
var value = LinkedValueUtils.getValue(this);
if (value != null) {
var rootNode = this.getDOMNode();
// Cast `value` to a string to ensure the value is set correctly. While
// browsers typically do this as necessary, jsdom doesn't.
DOMPropertyOperations.setValueForProperty(rootNode, 'value', '' + value);
}
},
_handleChange: function(event) {
var returnValue;
var onChange = LinkedValueUtils.getOnChange(this);
if (onChange) {
this._isChanging = true;
returnValue = onChange.call(this, event);
this._isChanging = false;
}
this.setState({value: event.target.value});
return returnValue;
}
});
module.exports = ReactDOMTextarea;
},{"./AutoFocusMixin":1,"./DOMPropertyOperations":10,"./LinkedValueUtils":23,"./ReactCompositeComponent":33,"./ReactDOM":36,"./invariant":121,"./merge":130}],47:[function(require,module,exports){
/**
* 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 ReactDefaultBatchingStrategy
*/
"use strict";
var ReactUpdates = require("./ReactUpdates");
var Transaction = require("./Transaction");
var emptyFunction = require("./emptyFunction");
var mixInto = require("./mixInto");
var RESET_BATCHED_UPDATES = {
initialize: emptyFunction,
close: function() {
ReactDefaultBatchingStrategy.isBatchingUpdates = false;
}
};
var FLUSH_BATCHED_UPDATES = {
initialize: emptyFunction,
close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)
};
var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];
function ReactDefaultBatchingStrategyTransaction() {
this.reinitializeTransaction();
}
mixInto(ReactDefaultBatchingStrategyTransaction, Transaction.Mixin);
mixInto(ReactDefaultBatchingStrategyTransaction, {
getTransactionWrappers: function() {
return TRANSACTION_WRAPPERS;
}
});
var transaction = new ReactDefaultBatchingStrategyTransaction();
var ReactDefaultBatchingStrategy = {
isBatchingUpdates: false,
/**
* Call the provided function in a context within which calls to `setState`
* and friends are batched such that components aren't updated unnecessarily.
*/
batchedUpdates: function(callback, param) {
var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
ReactDefaultBatchingStrategy.isBatchingUpdates = true;
// The code is written this way to avoid extra allocations
if (alreadyBatchingUpdates) {
callback(param);
} else {
transaction.perform(callback, null, param);
}
}
};
module.exports = ReactDefaultBatchingStrategy;
},{"./ReactUpdates":80,"./Transaction":95,"./emptyFunction":108,"./mixInto":133}],48:[function(require,module,exports){
/**
* 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 ReactDefaultInjection
*/
"use strict";
var ReactInjection = require("./ReactInjection");
var ExecutionEnvironment = require("./ExecutionEnvironment");
var DefaultDOMPropertyConfig = require("./DefaultDOMPropertyConfig");
var ChangeEventPlugin = require("./ChangeEventPlugin");
var ClientReactRootIndex = require("./ClientReactRootIndex");
var CompositionEventPlugin = require("./CompositionEventPlugin");
var DefaultEventPluginOrder = require("./DefaultEventPluginOrder");
var EnterLeaveEventPlugin = require("./EnterLeaveEventPlugin");
var MobileSafariClickEventPlugin = require("./MobileSafariClickEventPlugin");
var ReactEventTopLevelCallback = require("./ReactEventTopLevelCallback");
var ReactDOM = require("./ReactDOM");
var ReactDOMButton = require("./ReactDOMButton");
var ReactDOMForm = require("./ReactDOMForm");
var ReactDOMImg = require("./ReactDOMImg");
var ReactDOMInput = require("./ReactDOMInput");
var ReactDOMOption = require("./ReactDOMOption");
var ReactDOMSelect = require("./ReactDOMSelect");
var ReactDOMTextarea = require("./ReactDOMTextarea");
var ReactInstanceHandles = require("./ReactInstanceHandles");
var ReactMount = require("./ReactMount");
var SelectEventPlugin = require("./SelectEventPlugin");
var ServerReactRootIndex = require("./ServerReactRootIndex");
var SimpleEventPlugin = require("./SimpleEventPlugin");
var ReactDefaultBatchingStrategy = require("./ReactDefaultBatchingStrategy");
var createFullPageComponent = require("./createFullPageComponent");
function inject() {
ReactInjection.EventEmitter.injectTopLevelCallbackCreator(
ReactEventTopLevelCallback
);
/**
* Inject modules for resolving DOM hierarchy and plugin ordering.
*/
ReactInjection.EventPluginHub.injectEventPluginOrder(DefaultEventPluginOrder);
ReactInjection.EventPluginHub.injectInstanceHandle(ReactInstanceHandles);
ReactInjection.EventPluginHub.injectMount(ReactMount);
/**
* Some important event plugins included by default (without having to require
* them).
*/
ReactInjection.EventPluginHub.injectEventPluginsByName({
SimpleEventPlugin: SimpleEventPlugin,
EnterLeaveEventPlugin: EnterLeaveEventPlugin,
ChangeEventPlugin: ChangeEventPlugin,
CompositionEventPlugin: CompositionEventPlugin,
MobileSafariClickEventPlugin: MobileSafariClickEventPlugin,
SelectEventPlugin: SelectEventPlugin
});
ReactInjection.DOM.injectComponentClasses({
button: ReactDOMButton,
form: ReactDOMForm,
img: ReactDOMImg,
input: ReactDOMInput,
option: ReactDOMOption,
select: ReactDOMSelect,
textarea: ReactDOMTextarea,
html: createFullPageComponent(ReactDOM.html),
head: createFullPageComponent(ReactDOM.head),
title: createFullPageComponent(ReactDOM.title),
body: createFullPageComponent(ReactDOM.body)
});
ReactInjection.DOMProperty.injectDOMPropertyConfig(DefaultDOMPropertyConfig);
ReactInjection.Updates.injectBatchingStrategy(
ReactDefaultBatchingStrategy
);
ReactInjection.RootIndex.injectCreateReactRootIndex(
ExecutionEnvironment.canUseDOM ?
ClientReactRootIndex.createReactRootIndex :
ServerReactRootIndex.createReactRootIndex
);
if ("production" !== "development") {
var url = (ExecutionEnvironment.canUseDOM && window.location.href) || '';
if ((/[?&]react_perf\b/).test(url)) {
var ReactDefaultPerf = require("./ReactDefaultPerf");
ReactDefaultPerf.start();
}
}
}
module.exports = {
inject: inject
};
},{"./ChangeEventPlugin":5,"./ClientReactRootIndex":6,"./CompositionEventPlugin":7,"./DefaultDOMPropertyConfig":12,"./DefaultEventPluginOrder":13,"./EnterLeaveEventPlugin":14,"./ExecutionEnvironment":21,"./MobileSafariClickEventPlugin":24,"./ReactDOM":36,"./ReactDOMButton":37,"./ReactDOMForm":39,"./ReactDOMImg":41,"./ReactDOMInput":42,"./ReactDOMOption":43,"./ReactDOMSelect":44,"./ReactDOMTextarea":46,"./ReactDefaultBatchingStrategy":47,"./ReactDefaultPerf":49,"./ReactEventTopLevelCallback":54,"./ReactInjection":55,"./ReactInstanceHandles":57,"./ReactMount":60,"./SelectEventPlugin":82,"./ServerReactRootIndex":83,"./SimpleEventPlugin":84,"./createFullPageComponent":103}],49:[function(require,module,exports){
/**
* 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 ReactDefaultPerf
* @typechecks static-only
*/
"use strict";
var DOMProperty = require("./DOMProperty");
var ReactDefaultPerfAnalysis = require("./ReactDefaultPerfAnalysis");
var ReactMount = require("./ReactMount");
var ReactPerf = require("./ReactPerf");
var performanceNow = require("./performanceNow");
function roundFloat(val) {
return Math.floor(val * 100) / 100;
}
var ReactDefaultPerf = {
_allMeasurements: [], // last item in the list is the current one
_injected: false,
start: function() {
if (!ReactDefaultPerf._injected) {
ReactPerf.injection.injectMeasure(ReactDefaultPerf.measure);
}
ReactDefaultPerf._allMeasurements.length = 0;
ReactPerf.enableMeasure = true;
},
stop: function() {
ReactPerf.enableMeasure = false;
},
getLastMeasurements: function() {
return ReactDefaultPerf._allMeasurements;
},
printExclusive: function(measurements) {
measurements = measurements || ReactDefaultPerf._allMeasurements;
var summary = ReactDefaultPerfAnalysis.getExclusiveSummary(measurements);
console.table(summary.map(function(item) {
return {
'Component class name': item.componentName,
'Total inclusive time (ms)': roundFloat(item.inclusive),
'Total exclusive time (ms)': roundFloat(item.exclusive),
'Exclusive time per instance (ms)': roundFloat(item.exclusive / item.count),
'Instances': item.count
};
}));
console.log(
'Total time:',
ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms'
);
},
printInclusive: function(measurements) {
measurements = measurements || ReactDefaultPerf._allMeasurements;
var summary = ReactDefaultPerfAnalysis.getInclusiveSummary(measurements);
console.table(summary.map(function(item) {
return {
'Owner > component': item.componentName,
'Inclusive time (ms)': roundFloat(item.time),
'Instances': item.count
};
}));
console.log(
'Total time:',
ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms'
);
},
printWasted: function(measurements) {
measurements = measurements || ReactDefaultPerf._allMeasurements;
var summary = ReactDefaultPerfAnalysis.getInclusiveSummary(
measurements,
true
);
console.table(summary.map(function(item) {
return {
'Owner > component': item.componentName,
'Wasted time (ms)': item.time,
'Instances': item.count
};
}));
console.log(
'Total time:',
ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms'
);
},
printDOM: function(measurements) {
measurements = measurements || ReactDefaultPerf._allMeasurements;
var summary = ReactDefaultPerfAnalysis.getDOMSummary(measurements);
console.table(summary.map(function(item) {
var result = {};
result[DOMProperty.ID_ATTRIBUTE_NAME] = item.id;
result['type'] = item.type;
result['args'] = JSON.stringify(item.args);
return result;
}));
console.log(
'Total time:',
ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms'
);
},
_recordWrite: function(id, fnName, totalTime, args) {
// TODO: totalTime isn't that useful since it doesn't count paints/reflows
var writes =
ReactDefaultPerf
._allMeasurements[ReactDefaultPerf._allMeasurements.length - 1]
.writes;
writes[id] = writes[id] || [];
writes[id].push({
type: fnName,
time: totalTime,
args: args
});
},
measure: function(moduleName, fnName, func) {
return function() {var args=Array.prototype.slice.call(arguments,0);
var totalTime;
var rv;
var start;
if (fnName === '_renderNewRootComponent' ||
fnName === 'flushBatchedUpdates') {
// A "measurement" is a set of metrics recorded for each flush. We want
// to group the metrics for a given flush together so we can look at the
// components that rendered and the DOM operations that actually
// happened to determine the amount of "wasted work" performed.
ReactDefaultPerf._allMeasurements.push({
exclusive: {},
inclusive: {},
counts: {},
writes: {},
displayNames: {},
totalTime: 0
});
start = performanceNow();
rv = func.apply(this, args);
ReactDefaultPerf._allMeasurements[
ReactDefaultPerf._allMeasurements.length - 1
].totalTime = performanceNow() - start;
return rv;
} else if (moduleName === 'ReactDOMIDOperations' ||
moduleName === 'ReactComponentBrowserEnvironment') {
start = performanceNow();
rv = func.apply(this, args);
totalTime = performanceNow() - start;
if (fnName === 'mountImageIntoNode') {
var mountID = ReactMount.getID(args[1]);
ReactDefaultPerf._recordWrite(mountID, fnName, totalTime, args[0]);
} else if (fnName === 'dangerouslyProcessChildrenUpdates') {
// special format
args[0].forEach(function(update) {
var writeArgs = {};
if (update.fromIndex !== null) {
writeArgs.fromIndex = update.fromIndex;
}
if (update.toIndex !== null) {
writeArgs.toIndex = update.toIndex;
}
if (update.textContent !== null) {
writeArgs.textContent = update.textContent;
}
if (update.markupIndex !== null) {
writeArgs.markup = args[1][update.markupIndex];
}
ReactDefaultPerf._recordWrite(
update.parentID,
update.type,
totalTime,
writeArgs
);
});
} else {
// basic format
ReactDefaultPerf._recordWrite(
args[0],
fnName,
totalTime,
Array.prototype.slice.call(args, 1)
);
}
return rv;
} else if (moduleName === 'ReactCompositeComponent' && (
fnName === 'mountComponent' ||
fnName === 'updateComponent' || // TODO: receiveComponent()?
fnName === '_renderValidatedComponent')) {
var rootNodeID = fnName === 'mountComponent' ?
args[0] :
this._rootNodeID;
var isRender = fnName === '_renderValidatedComponent';
var entry = ReactDefaultPerf._allMeasurements[
ReactDefaultPerf._allMeasurements.length - 1
];
if (isRender) {
entry.counts[rootNodeID] = entry.counts[rootNodeID] || 0;
entry.counts[rootNodeID] += 1;
}
start = performanceNow();
rv = func.apply(this, args);
totalTime = performanceNow() - start;
var typeOfLog = isRender ? entry.exclusive : entry.inclusive;
typeOfLog[rootNodeID] = typeOfLog[rootNodeID] || 0;
typeOfLog[rootNodeID] += totalTime;
entry.displayNames[rootNodeID] = {
current: this.constructor.displayName,
owner: this._owner ? this._owner.constructor.displayName : '<root>'
};
return rv;
} else {
return func.apply(this, args);
}
};
}
};
module.exports = ReactDefaultPerf;
},{"./DOMProperty":9,"./ReactDefaultPerfAnalysis":50,"./ReactMount":60,"./ReactPerf":65,"./performanceNow":137}],50:[function(require,module,exports){
/**
* 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 ReactDefaultPerfAnalysis
*/
var merge = require("./merge");
// Don't try to save users less than 1.2ms (a number I made up)
var DONT_CARE_THRESHOLD = 1.2;
var DOM_OPERATION_TYPES = {
'mountImageIntoNode': 'set innerHTML',
INSERT_MARKUP: 'set innerHTML',
MOVE_EXISTING: 'move',
REMOVE_NODE: 'remove',
TEXT_CONTENT: 'set textContent',
'updatePropertyByID': 'update attribute',
'deletePropertyByID': 'delete attribute',
'updateStylesByID': 'update styles',
'updateInnerHTMLByID': 'set innerHTML',
'dangerouslyReplaceNodeWithMarkupByID': 'replace'
};
function getTotalTime(measurements) {
// TODO: return number of DOM ops? could be misleading.
// TODO: measure dropped frames after reconcile?
// TODO: log total time of each reconcile and the top-level component
// class that triggered it.
var totalTime = 0;
for (var i = 0; i < measurements.length; i++) {
var measurement = measurements[i];
totalTime += measurement.totalTime;
}
return totalTime;
}
function getDOMSummary(measurements) {
var items = [];
for (var i = 0; i < measurements.length; i++) {
var measurement = measurements[i];
var id;
for (id in measurement.writes) {
measurement.writes[id].forEach(function(write) {
items.push({
id: id,
type: DOM_OPERATION_TYPES[write.type] || write.type,
args: write.args
});
});
}
}
return items;
}
function getExclusiveSummary(measurements) {
var candidates = {};
var displayName;
for (var i = 0; i < measurements.length; i++) {
var measurement = measurements[i];
var allIDs = merge(measurement.exclusive, measurement.inclusive);
for (var id in allIDs) {
displayName = measurement.displayNames[id].current;
candidates[displayName] = candidates[displayName] || {
componentName: displayName,
inclusive: 0,
exclusive: 0,
count: 0
};
if (measurement.exclusive[id]) {
candidates[displayName].exclusive += measurement.exclusive[id];
}
if (measurement.inclusive[id]) {
candidates[displayName].inclusive += measurement.inclusive[id];
}
if (measurement.counts[id]) {
candidates[displayName].count += measurement.counts[id];
}
}
}
// Now make a sorted array with the results.
var arr = [];
for (displayName in candidates) {
if (candidates[displayName].exclusive >= DONT_CARE_THRESHOLD) {
arr.push(candidates[displayName]);
}
}
arr.sort(function(a, b) {
return b.exclusive - a.exclusive;
});
return arr;
}
function getInclusiveSummary(measurements, onlyClean) {
var candidates = {};
var inclusiveKey;
for (var i = 0; i < measurements.length; i++) {
var measurement = measurements[i];
var allIDs = merge(measurement.exclusive, measurement.inclusive);
var cleanComponents;
if (onlyClean) {
cleanComponents = getUnchangedComponents(measurement);
}
for (var id in allIDs) {
if (onlyClean && !cleanComponents[id]) {
continue;
}
var displayName = measurement.displayNames[id];
// Inclusive time is not useful for many components without knowing where
// they are instantiated. So we aggregate inclusive time with both the
// owner and current displayName as the key.
inclusiveKey = displayName.owner + ' > ' + displayName.current;
candidates[inclusiveKey] = candidates[inclusiveKey] || {
componentName: inclusiveKey,
time: 0,
count: 0
};
if (measurement.inclusive[id]) {
candidates[inclusiveKey].time += measurement.inclusive[id];
}
if (measurement.counts[id]) {
candidates[inclusiveKey].count += measurement.counts[id];
}
}
}
// Now make a sorted array with the results.
var arr = [];
for (inclusiveKey in candidates) {
if (candidates[inclusiveKey].time >= DONT_CARE_THRESHOLD) {
arr.push(candidates[inclusiveKey]);
}
}
arr.sort(function(a, b) {
return b.time - a.time;
});
return arr;
}
function getUnchangedComponents(measurement) {
// For a given reconcile, look at which components did not actually
// render anything to the DOM and return a mapping of their ID to
// the amount of time it took to render the entire subtree.
var cleanComponents = {};
var dirtyLeafIDs = Object.keys(measurement.writes);
var allIDs = merge(measurement.exclusive, measurement.inclusive);
for (var id in allIDs) {
var isDirty = false;
// For each component that rendered, see if a component that triggerd
// a DOM op is in its subtree.
for (var i = 0; i < dirtyLeafIDs.length; i++) {
if (dirtyLeafIDs[i].indexOf(id) === 0) {
isDirty = true;
break;
}
}
if (!isDirty && measurement.counts[id] > 0) {
cleanComponents[id] = true;
}
}
return cleanComponents;
}
var ReactDefaultPerfAnalysis = {
getExclusiveSummary: getExclusiveSummary,
getInclusiveSummary: getInclusiveSummary,
getDOMSummary: getDOMSummary,
getTotalTime: getTotalTime
};
module.exports = ReactDefaultPerfAnalysis;
},{"./merge":130}],51:[function(require,module,exports){
/**
* 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 ReactErrorUtils
* @typechecks
*/
"use strict";
var ReactErrorUtils = {
/**
* Creates a guarded version of a function. This is supposed to make debugging
* of event handlers easier. To aid debugging with the browser's debugger,
* this currently simply returns the original function.
*
* @param {function} func Function to be executed
* @param {string} name The name of the guard
* @return {function}
*/
guard: function(func, name) {
return func;
}
};
module.exports = ReactErrorUtils;
},{}],52:[function(require,module,exports){
/**
* 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 ReactEventEmitter
* @typechecks static-only
*/
"use strict";
var EventConstants = require("./EventConstants");
var EventListener = require("./EventListener");
var EventPluginHub = require("./EventPluginHub");
var EventPluginRegistry = require("./EventPluginRegistry");
var ExecutionEnvironment = require("./ExecutionEnvironment");
var ReactEventEmitterMixin = require("./ReactEventEmitterMixin");
var ViewportMetrics = require("./ViewportMetrics");
var invariant = require("./invariant");
var isEventSupported = require("./isEventSupported");
var merge = require("./merge");
/**
* Summary of `ReactEventEmitter` event handling:
*
* - Top-level delegation is used to trap native browser events. We normalize
* and de-duplicate events to account for browser quirks.
*
* - Forward these native events (with the associated top-level type used to
* trap it) to `EventPluginHub`, which in turn will ask plugins if they want
* to extract any synthetic events.
*
* - The `EventPluginHub` will then process each event by annotating them with
* "dispatches", a sequence of listeners and IDs that care about that event.
*
* - The `EventPluginHub` then dispatches the events.
*
* Overview of React and the event system:
*
* .
* +------------+ .
* | DOM | .
* +------------+ . +-----------+
* + . +--------+|SimpleEvent|
* | . | |Plugin |
* +-----|------+ . v +-----------+
* | | | . +--------------+ +------------+
* | +-----------.--->|EventPluginHub| | Event |
* | | . | | +-----------+ | Propagators|
* | ReactEvent | . | | |TapEvent | |------------|
* | Emitter | . | |<---+|Plugin | |other plugin|
* | | . | | +-----------+ | utilities |
* | +-----------.--->| | +------------+
* | | | . +--------------+
* +-----|------+ . ^ +-----------+
* | . | |Enter/Leave|
* + . +-------+|Plugin |
* +-------------+ . +-----------+
* | application | .
* |-------------| .
* | | .
* | | .
* +-------------+ .
* .
* React Core . General Purpose Event Plugin System
*/
var alreadyListeningTo = {};
var isMonitoringScrollValue = false;
var reactTopListenersCounter = 0;
// For events like 'submit' which don't consistently bubble (which we trap at a
// lower node than `document`), binding at `document` would cause duplicate
// events so we don't include them here
var topEventMapping = {
topBlur: 'blur',
topChange: 'change',
topClick: 'click',
topCompositionEnd: 'compositionend',
topCompositionStart: 'compositionstart',
topCompositionUpdate: 'compositionupdate',
topContextMenu: 'contextmenu',
topCopy: 'copy',
topCut: 'cut',
topDoubleClick: 'dblclick',
topDrag: 'drag',
topDragEnd: 'dragend',
topDragEnter: 'dragenter',
topDragExit: 'dragexit',
topDragLeave: 'dragleave',
topDragOver: 'dragover',
topDragStart: 'dragstart',
topDrop: 'drop',
topFocus: 'focus',
topInput: 'input',
topKeyDown: 'keydown',
topKeyPress: 'keypress',
topKeyUp: 'keyup',
topMouseDown: 'mousedown',
topMouseMove: 'mousemove',
topMouseOut: 'mouseout',
topMouseOver: 'mouseover',
topMouseUp: 'mouseup',
topPaste: 'paste',
topScroll: 'scroll',
topSelectionChange: 'selectionchange',
topTouchCancel: 'touchcancel',
topTouchEnd: 'touchend',
topTouchMove: 'touchmove',
topTouchStart: 'touchstart',
topWheel: 'wheel'
};
/**
* To ensure no conflicts with other potential React instances on the page
*/
var topListenersIDKey = "_reactListenersID" + String(Math.random()).slice(2);
function getListeningForDocument(mountAt) {
if (mountAt[topListenersIDKey] == null) {
mountAt[topListenersIDKey] = reactTopListenersCounter++;
alreadyListeningTo[mountAt[topListenersIDKey]] = {};
}
return alreadyListeningTo[mountAt[topListenersIDKey]];
}
/**
* Traps top-level events by using event bubbling.
*
* @param {string} topLevelType Record from `EventConstants`.
* @param {string} handlerBaseName Event name (e.g. "click").
* @param {DOMEventTarget} element Element on which to attach listener.
* @internal
*/
function trapBubbledEvent(topLevelType, handlerBaseName, element) {
EventListener.listen(
element,
handlerBaseName,
ReactEventEmitter.TopLevelCallbackCreator.createTopLevelCallback(
topLevelType
)
);
}
/**
* Traps a top-level event by using event capturing.
*
* @param {string} topLevelType Record from `EventConstants`.
* @param {string} handlerBaseName Event name (e.g. "click").
* @param {DOMEventTarget} element Element on which to attach listener.
* @internal
*/
function trapCapturedEvent(topLevelType, handlerBaseName, element) {
EventListener.capture(
element,
handlerBaseName,
ReactEventEmitter.TopLevelCallbackCreator.createTopLevelCallback(
topLevelType
)
);
}
/**
* `ReactEventEmitter` is used to attach top-level event listeners. For example:
*
* ReactEventEmitter.putListener('myID', 'onClick', myFunction);
*
* This would allocate a "registration" of `('onClick', myFunction)` on 'myID'.
*
* @internal
*/
var ReactEventEmitter = merge(ReactEventEmitterMixin, {
/**
* React references `ReactEventTopLevelCallback` using this property in order
* to allow dependency injection.
*/
TopLevelCallbackCreator: null,
injection: {
/**
* @param {function} TopLevelCallbackCreator
*/
injectTopLevelCallbackCreator: function(TopLevelCallbackCreator) {
ReactEventEmitter.TopLevelCallbackCreator = TopLevelCallbackCreator;
}
},
/**
* Sets whether or not any created callbacks should be enabled.
*
* @param {boolean} enabled True if callbacks should be enabled.
*/
setEnabled: function(enabled) {
("production" !== "development" ? invariant(
ExecutionEnvironment.canUseDOM,
'setEnabled(...): Cannot toggle event listening in a Worker thread. ' +
'This is likely a bug in the framework. Please report immediately.'
) : invariant(ExecutionEnvironment.canUseDOM));
if (ReactEventEmitter.TopLevelCallbackCreator) {
ReactEventEmitter.TopLevelCallbackCreator.setEnabled(enabled);
}
},
/**
* @return {boolean} True if callbacks are enabled.
*/
isEnabled: function() {
return !!(
ReactEventEmitter.TopLevelCallbackCreator &&
ReactEventEmitter.TopLevelCallbackCreator.isEnabled()
);
},
/**
* We listen for bubbled touch events on the document object.
*
* Firefox v8.01 (and possibly others) exhibited strange behavior when
* mounting `onmousemove` events at some node that was not the document
* element. The symptoms were that if your mouse is not moving over something
* contained within that mount point (for example on the background) the
* top-level listeners for `onmousemove` won't be called. However, if you
* register the `mousemove` on the document object, then it will of course
* catch all `mousemove`s. This along with iOS quirks, justifies restricting
* top-level listeners to the document object only, at least for these
* movement types of events and possibly all events.
*
* @see http://www.quirksmode.org/blog/archives/2010/09/click_event_del.html
*
* Also, `keyup`/`keypress`/`keydown` do not bubble to the window on IE, but
* they bubble to document.
*
* @param {string} registrationName Name of listener (e.g. `onClick`).
* @param {DOMDocument} contentDocument Document which owns the container
*/
listenTo: function(registrationName, contentDocument) {
var mountAt = contentDocument;
var isListening = getListeningForDocument(mountAt);
var dependencies = EventPluginRegistry.
registrationNameDependencies[registrationName];
var topLevelTypes = EventConstants.topLevelTypes;
for (var i = 0, l = dependencies.length; i < l; i++) {
var dependency = dependencies[i];
if (!isListening[dependency]) {
var topLevelType = topLevelTypes[dependency];
if (topLevelType === topLevelTypes.topWheel) {
if (isEventSupported('wheel')) {
trapBubbledEvent(topLevelTypes.topWheel, 'wheel', mountAt);
} else if (isEventSupported('mousewheel')) {
trapBubbledEvent(topLevelTypes.topWheel, 'mousewheel', mountAt);
} else {
// Firefox needs to capture a different mouse scroll event.
// @see http://www.quirksmode.org/dom/events/tests/scroll.html
trapBubbledEvent(
topLevelTypes.topWheel,
'DOMMouseScroll',
mountAt);
}
} else if (topLevelType === topLevelTypes.topScroll) {
if (isEventSupported('scroll', true)) {
trapCapturedEvent(topLevelTypes.topScroll, 'scroll', mountAt);
} else {
trapBubbledEvent(topLevelTypes.topScroll, 'scroll', window);
}
} else if (topLevelType === topLevelTypes.topFocus ||
topLevelType === topLevelTypes.topBlur) {
if (isEventSupported('focus', true)) {
trapCapturedEvent(topLevelTypes.topFocus, 'focus', mountAt);
trapCapturedEvent(topLevelTypes.topBlur, 'blur', mountAt);
} else if (isEventSupported('focusin')) {
// IE has `focusin` and `focusout` events which bubble.
// @see http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html
trapBubbledEvent(topLevelTypes.topFocus, 'focusin', mountAt);
trapBubbledEvent(topLevelTypes.topBlur, 'focusout', mountAt);
}
// to make sure blur and focus event listeners are only attached once
isListening[topLevelTypes.topBlur] = true;
isListening[topLevelTypes.topFocus] = true;
} else if (topEventMapping[dependency]) {
trapBubbledEvent(topLevelType, topEventMapping[dependency], mountAt);
}
isListening[dependency] = true;
}
}
},
/**
* Listens to window scroll and resize events. We cache scroll values so that
* application code can access them without triggering reflows.
*
* NOTE: Scroll events do not bubble.
*
* @see http://www.quirksmode.org/dom/events/scroll.html
*/
ensureScrollValueMonitoring: function(){
if (!isMonitoringScrollValue) {
var refresh = ViewportMetrics.refreshScrollValues;
EventListener.listen(window, 'scroll', refresh);
EventListener.listen(window, 'resize', refresh);
isMonitoringScrollValue = true;
}
},
eventNameDispatchConfigs: EventPluginHub.eventNameDispatchConfigs,
registrationNameModules: EventPluginHub.registrationNameModules,
putListener: EventPluginHub.putListener,
getListener: EventPluginHub.getListener,
deleteListener: EventPluginHub.deleteListener,
deleteAllListeners: EventPluginHub.deleteAllListeners,
trapBubbledEvent: trapBubbledEvent,
trapCapturedEvent: trapCapturedEvent
});
module.exports = ReactEventEmitter;
},{"./EventConstants":15,"./EventListener":16,"./EventPluginHub":17,"./EventPluginRegistry":18,"./ExecutionEnvironment":21,"./ReactEventEmitterMixin":53,"./ViewportMetrics":96,"./invariant":121,"./isEventSupported":122,"./merge":130}],53:[function(require,module,exports){
/**
* 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 ReactEventEmitterMixin
*/
"use strict";
var EventPluginHub = require("./EventPluginHub");
var ReactUpdates = require("./ReactUpdates");
function runEventQueueInBatch(events) {
EventPluginHub.enqueueEvents(events);
EventPluginHub.processEventQueue();
}
var ReactEventEmitterMixin = {
/**
* Streams a fired top-level event to `EventPluginHub` where plugins have the
* opportunity to create `ReactEvent`s to be dispatched.
*
* @param {string} topLevelType Record from `EventConstants`.
* @param {object} topLevelTarget The listening component root node.
* @param {string} topLevelTargetID ID of `topLevelTarget`.
* @param {object} nativeEvent Native environment event.
*/
handleTopLevel: function(
topLevelType,
topLevelTarget,
topLevelTargetID,
nativeEvent) {
var events = EventPluginHub.extractEvents(
topLevelType,
topLevelTarget,
topLevelTargetID,
nativeEvent
);
// Event queue being processed in the same cycle allows `preventDefault`.
ReactUpdates.batchedUpdates(runEventQueueInBatch, events);
}
};
module.exports = ReactEventEmitterMixin;
},{"./EventPluginHub":17,"./ReactUpdates":80}],54:[function(require,module,exports){
/**
* 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 ReactEventTopLevelCallback
* @typechecks static-only
*/
"use strict";
var PooledClass = require("./PooledClass");
var ReactEventEmitter = require("./ReactEventEmitter");
var ReactInstanceHandles = require("./ReactInstanceHandles");
var ReactMount = require("./ReactMount");
var getEventTarget = require("./getEventTarget");
var mixInto = require("./mixInto");
/**
* @type {boolean}
* @private
*/
var _topLevelListenersEnabled = true;
/**
* Finds the parent React component of `node`.
*
* @param {*} node
* @return {?DOMEventTarget} Parent container, or `null` if the specified node
* is not nested.
*/
function findParent(node) {
// TODO: It may be a good idea to cache this to prevent unnecessary DOM
// traversal, but caching is difficult to do correctly without using a
// mutation observer to listen for all DOM changes.
var nodeID = ReactMount.getID(node);
var rootID = ReactInstanceHandles.getReactRootIDFromNodeID(nodeID);
var container = ReactMount.findReactContainerForID(rootID);
var parent = ReactMount.getFirstReactDOM(container);
return parent;
}
/**
* Calls ReactEventEmitter.handleTopLevel for each node stored in bookKeeping's
* ancestor list. Separated from createTopLevelCallback to avoid try/finally
* deoptimization.
*
* @param {string} topLevelType
* @param {DOMEvent} nativeEvent
* @param {TopLevelCallbackBookKeeping} bookKeeping
*/
function handleTopLevelImpl(topLevelType, nativeEvent, bookKeeping) {
var topLevelTarget = ReactMount.getFirstReactDOM(
getEventTarget(nativeEvent)
) || window;
// Loop through the hierarchy, in case there's any nested components.
// It's important that we build the array of ancestors before calling any
// event handlers, because event handlers can modify the DOM, leading to
// inconsistencies with ReactMount's node cache. See #1105.
var ancestor = topLevelTarget;
while (ancestor) {
bookKeeping.ancestors.push(ancestor);
ancestor = findParent(ancestor);
}
for (var i = 0, l = bookKeeping.ancestors.length; i < l; i++) {
topLevelTarget = bookKeeping.ancestors[i];
var topLevelTargetID = ReactMount.getID(topLevelTarget) || '';
ReactEventEmitter.handleTopLevel(
topLevelType,
topLevelTarget,
topLevelTargetID,
nativeEvent
);
}
}
// Used to store ancestor hierarchy in top level callback
function TopLevelCallbackBookKeeping() {
this.ancestors = [];
}
mixInto(TopLevelCallbackBookKeeping, {
destructor: function() {
this.ancestors.length = 0;
}
});
PooledClass.addPoolingTo(TopLevelCallbackBookKeeping);
/**
* Top-level callback creator used to implement event handling using delegation.
* This is used via dependency injection.
*/
var ReactEventTopLevelCallback = {
/**
* Sets whether or not any created callbacks should be enabled.
*
* @param {boolean} enabled True if callbacks should be enabled.
*/
setEnabled: function(enabled) {
_topLevelListenersEnabled = !!enabled;
},
/**
* @return {boolean} True if callbacks are enabled.
*/
isEnabled: function() {
return _topLevelListenersEnabled;
},
/**
* Creates a callback for the supplied `topLevelType` that could be added as
* a listener to the document. The callback computes a `topLevelTarget` which
* should be the root node of a mounted React component where the listener
* is attached.
*
* @param {string} topLevelType Record from `EventConstants`.
* @return {function} Callback for handling top-level events.
*/
createTopLevelCallback: function(topLevelType) {
return function(nativeEvent) {
if (!_topLevelListenersEnabled) {
return;
}
var bookKeeping = TopLevelCallbackBookKeeping.getPooled();
try {
handleTopLevelImpl(topLevelType, nativeEvent, bookKeeping);
} finally {
TopLevelCallbackBookKeeping.release(bookKeeping);
}
};
}
};
module.exports = ReactEventTopLevelCallback;
},{"./PooledClass":25,"./ReactEventEmitter":52,"./ReactInstanceHandles":57,"./ReactMount":60,"./getEventTarget":114,"./mixInto":133}],55:[function(require,module,exports){
/**
* 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 ReactInjection
*/
"use strict";
var DOMProperty = require("./DOMProperty");
var EventPluginHub = require("./EventPluginHub");
var ReactDOM = require("./ReactDOM");
var ReactEventEmitter = require("./ReactEventEmitter");
var ReactPerf = require("./ReactPerf");
var ReactRootIndex = require("./ReactRootIndex");
var ReactUpdates = require("./ReactUpdates");
var ReactInjection = {
DOMProperty: DOMProperty.injection,
EventPluginHub: EventPluginHub.injection,
DOM: ReactDOM.injection,
EventEmitter: ReactEventEmitter.injection,
Perf: ReactPerf.injection,
RootIndex: ReactRootIndex.injection,
Updates: ReactUpdates.injection
};
module.exports = ReactInjection;
},{"./DOMProperty":9,"./EventPluginHub":17,"./ReactDOM":36,"./ReactEventEmitter":52,"./ReactPerf":65,"./ReactRootIndex":72,"./ReactUpdates":80}],56:[function(require,module,exports){
/**
* 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 ReactInputSelection
*/
"use strict";
var ReactDOMSelection = require("./ReactDOMSelection");
var containsNode = require("./containsNode");
var getActiveElement = require("./getActiveElement");
function isInDocument(node) {
return containsNode(document.documentElement, node);
}
/**
* @ReactInputSelection: React input selection module. Based on Selection.js,
* but modified to be suitable for react and has a couple of bug fixes (doesn't
* assume buttons have range selections allowed).
* Input selection module for React.
*/
var ReactInputSelection = {
hasSelectionCapabilities: function(elem) {
return elem && (
(elem.nodeName === 'INPUT' && elem.type === 'text') ||
elem.nodeName === 'TEXTAREA' ||
elem.contentEditable === 'true'
);
},
getSelectionInformation: function() {
var focusedElem = getActiveElement();
return {
focusedElem: focusedElem,
selectionRange:
ReactInputSelection.hasSelectionCapabilities(focusedElem) ?
ReactInputSelection.getSelection(focusedElem) :
null
};
},
/**
* @restoreSelection: If any selection information was potentially lost,
* restore it. This is useful when performing operations that could remove dom
* nodes and place them back in, resulting in focus being lost.
*/
restoreSelection: function(priorSelectionInformation) {
var curFocusedElem = getActiveElement();
var priorFocusedElem = priorSelectionInformation.focusedElem;
var priorSelectionRange = priorSelectionInformation.selectionRange;
if (curFocusedElem !== priorFocusedElem &&
isInDocument(priorFocusedElem)) {
if (ReactInputSelection.hasSelectionCapabilities(priorFocusedElem)) {
ReactInputSelection.setSelection(
priorFocusedElem,
priorSelectionRange
);
}
priorFocusedElem.focus();
}
},
/**
* @getSelection: Gets the selection bounds of a focused textarea, input or
* contentEditable node.
* -@input: Look up selection bounds of this input
* -@return {start: selectionStart, end: selectionEnd}
*/
getSelection: function(input) {
var selection;
if ('selectionStart' in input) {
// Modern browser with input or textarea.
selection = {
start: input.selectionStart,
end: input.selectionEnd
};
} else if (document.selection && input.nodeName === 'INPUT') {
// IE8 input.
var range = document.selection.createRange();
// There can only be one selection per document in IE, so it must
// be in our element.
if (range.parentElement() === input) {
selection = {
start: -range.moveStart('character', -input.value.length),
end: -range.moveEnd('character', -input.value.length)
};
}
} else {
// Content editable or old IE textarea.
selection = ReactDOMSelection.getOffsets(input);
}
return selection || {start: 0, end: 0};
},
/**
* @setSelection: Sets the selection bounds of a textarea or input and focuses
* the input.
* -@input Set selection bounds of this input or textarea
* -@offsets Object of same form that is returned from get*
*/
setSelection: function(input, offsets) {
var start = offsets.start;
var end = offsets.end;
if (typeof end === 'undefined') {
end = start;
}
if ('selectionStart' in input) {
input.selectionStart = start;
input.selectionEnd = Math.min(end, input.value.length);
} else if (document.selection && input.nodeName === 'INPUT') {
var range = input.createTextRange();
range.collapse(true);
range.moveStart('character', start);
range.moveEnd('character', end - start);
range.select();
} else {
ReactDOMSelection.setOffsets(input, offsets);
}
}
};
module.exports = ReactInputSelection;
},{"./ReactDOMSelection":45,"./containsNode":100,"./getActiveElement":112}],57:[function(require,module,exports){
/**
* 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 ReactInstanceHandles
* @typechecks static-only
*/
"use strict";
var ReactRootIndex = require("./ReactRootIndex");
var invariant = require("./invariant");
var SEPARATOR = '.';
var SEPARATOR_LENGTH = SEPARATOR.length;
/**
* Maximum depth of traversals before we consider the possibility of a bad ID.
*/
var MAX_TREE_DEPTH = 100;
/**
* Creates a DOM ID prefix to use when mounting React components.
*
* @param {number} index A unique integer
* @return {string} React root ID.
* @internal
*/
function getReactRootIDString(index) {
return SEPARATOR + index.toString(36);
}
/**
* Checks if a character in the supplied ID is a separator or the end.
*
* @param {string} id A React DOM ID.
* @param {number} index Index of the character to check.
* @return {boolean} True if the character is a separator or end of the ID.
* @private
*/
function isBoundary(id, index) {
return id.charAt(index) === SEPARATOR || index === id.length;
}
/**
* Checks if the supplied string is a valid React DOM ID.
*
* @param {string} id A React DOM ID, maybe.
* @return {boolean} True if the string is a valid React DOM ID.
* @private
*/
function isValidID(id) {
return id === '' || (
id.charAt(0) === SEPARATOR && id.charAt(id.length - 1) !== SEPARATOR
);
}
/**
* Checks if the first ID is an ancestor of or equal to the second ID.
*
* @param {string} ancestorID
* @param {string} descendantID
* @return {boolean} True if `ancestorID` is an ancestor of `descendantID`.
* @internal
*/
function isAncestorIDOf(ancestorID, descendantID) {
return (
descendantID.indexOf(ancestorID) === 0 &&
isBoundary(descendantID, ancestorID.length)
);
}
/**
* Gets the parent ID of the supplied React DOM ID, `id`.
*
* @param {string} id ID of a component.
* @return {string} ID of the parent, or an empty string.
* @private
*/
function getParentID(id) {
return id ? id.substr(0, id.lastIndexOf(SEPARATOR)) : '';
}
/**
* Gets the next DOM ID on the tree path from the supplied `ancestorID` to the
* supplied `destinationID`. If they are equal, the ID is returned.
*
* @param {string} ancestorID ID of an ancestor node of `destinationID`.
* @param {string} destinationID ID of the destination node.
* @return {string} Next ID on the path from `ancestorID` to `destinationID`.
* @private
*/
function getNextDescendantID(ancestorID, destinationID) {
("production" !== "development" ? invariant(
isValidID(ancestorID) && isValidID(destinationID),
'getNextDescendantID(%s, %s): Received an invalid React DOM ID.',
ancestorID,
destinationID
) : invariant(isValidID(ancestorID) && isValidID(destinationID)));
("production" !== "development" ? invariant(
isAncestorIDOf(ancestorID, destinationID),
'getNextDescendantID(...): React has made an invalid assumption about ' +
'the DOM hierarchy. Expected `%s` to be an ancestor of `%s`.',
ancestorID,
destinationID
) : invariant(isAncestorIDOf(ancestorID, destinationID)));
if (ancestorID === destinationID) {
return ancestorID;
}
// Skip over the ancestor and the immediate separator. Traverse until we hit
// another separator or we reach the end of `destinationID`.
var start = ancestorID.length + SEPARATOR_LENGTH;
for (var i = start; i < destinationID.length; i++) {
if (isBoundary(destinationID, i)) {
break;
}
}
return destinationID.substr(0, i);
}
/**
* Gets the nearest common ancestor ID of two IDs.
*
* Using this ID scheme, the nearest common ancestor ID is the longest common
* prefix of the two IDs that immediately preceded a "marker" in both strings.
*
* @param {string} oneID
* @param {string} twoID
* @return {string} Nearest common ancestor ID, or the empty string if none.
* @private
*/
function getFirstCommonAncestorID(oneID, twoID) {
var minLength = Math.min(oneID.length, twoID.length);
if (minLength === 0) {
return '';
}
var lastCommonMarkerIndex = 0;
// Use `<=` to traverse until the "EOL" of the shorter string.
for (var i = 0; i <= minLength; i++) {
if (isBoundary(oneID, i) && isBoundary(twoID, i)) {
lastCommonMarkerIndex = i;
} else if (oneID.charAt(i) !== twoID.charAt(i)) {
break;
}
}
var longestCommonID = oneID.substr(0, lastCommonMarkerIndex);
("production" !== "development" ? invariant(
isValidID(longestCommonID),
'getFirstCommonAncestorID(%s, %s): Expected a valid React DOM ID: %s',
oneID,
twoID,
longestCommonID
) : invariant(isValidID(longestCommonID)));
return longestCommonID;
}
/**
* Traverses the parent path between two IDs (either up or down). The IDs must
* not be the same, and there must exist a parent path between them. If the
* callback returns `false`, traversal is stopped.
*
* @param {?string} start ID at which to start traversal.
* @param {?string} stop ID at which to end traversal.
* @param {function} cb Callback to invoke each ID with.
* @param {?boolean} skipFirst Whether or not to skip the first node.
* @param {?boolean} skipLast Whether or not to skip the last node.
* @private
*/
function traverseParentPath(start, stop, cb, arg, skipFirst, skipLast) {
start = start || '';
stop = stop || '';
("production" !== "development" ? invariant(
start !== stop,
'traverseParentPath(...): Cannot traverse from and to the same ID, `%s`.',
start
) : invariant(start !== stop));
var traverseUp = isAncestorIDOf(stop, start);
("production" !== "development" ? invariant(
traverseUp || isAncestorIDOf(start, stop),
'traverseParentPath(%s, %s, ...): Cannot traverse from two IDs that do ' +
'not have a parent path.',
start,
stop
) : invariant(traverseUp || isAncestorIDOf(start, stop)));
// Traverse from `start` to `stop` one depth at a time.
var depth = 0;
var traverse = traverseUp ? getParentID : getNextDescendantID;
for (var id = start; /* until break */; id = traverse(id, stop)) {
var ret;
if ((!skipFirst || id !== start) && (!skipLast || id !== stop)) {
ret = cb(id, traverseUp, arg);
}
if (ret === false || id === stop) {
// Only break //after// visiting `stop`.
break;
}
("production" !== "development" ? invariant(
depth++ < MAX_TREE_DEPTH,
'traverseParentPath(%s, %s, ...): Detected an infinite loop while ' +
'traversing the React DOM ID tree. This may be due to malformed IDs: %s',
start, stop
) : invariant(depth++ < MAX_TREE_DEPTH));
}
}
/**
* Manages the IDs assigned to DOM representations of React components. This
* uses a specific scheme in order to traverse the DOM efficiently (e.g. in
* order to simulate events).
*
* @internal
*/
var ReactInstanceHandles = {
/**
* Constructs a React root ID
* @return {string} A React root ID.
*/
createReactRootID: function() {
return getReactRootIDString(ReactRootIndex.createReactRootIndex());
},
/**
* Constructs a React ID by joining a root ID with a name.
*
* @param {string} rootID Root ID of a parent component.
* @param {string} name A component's name (as flattened children).
* @return {string} A React ID.
* @internal
*/
createReactID: function(rootID, name) {
return rootID + name;
},
/**
* Gets the DOM ID of the React component that is the root of the tree that
* contains the React component with the supplied DOM ID.
*
* @param {string} id DOM ID of a React component.
* @return {?string} DOM ID of the React component that is the root.
* @internal
*/
getReactRootIDFromNodeID: function(id) {
if (id && id.charAt(0) === SEPARATOR && id.length > 1) {
var index = id.indexOf(SEPARATOR, 1);
return index > -1 ? id.substr(0, index) : id;
}
return null;
},
/**
* Traverses the ID hierarchy and invokes the supplied `cb` on any IDs that
* should would receive a `mouseEnter` or `mouseLeave` event.
*
* NOTE: Does not invoke the callback on the nearest common ancestor because
* nothing "entered" or "left" that element.
*
* @param {string} leaveID ID being left.
* @param {string} enterID ID being entered.
* @param {function} cb Callback to invoke on each entered/left ID.
* @param {*} upArg Argument to invoke the callback with on left IDs.
* @param {*} downArg Argument to invoke the callback with on entered IDs.
* @internal
*/
traverseEnterLeave: function(leaveID, enterID, cb, upArg, downArg) {
var ancestorID = getFirstCommonAncestorID(leaveID, enterID);
if (ancestorID !== leaveID) {
traverseParentPath(leaveID, ancestorID, cb, upArg, false, true);
}
if (ancestorID !== enterID) {
traverseParentPath(ancestorID, enterID, cb, downArg, true, false);
}
},
/**
* Simulates the traversal of a two-phase, capture/bubble event dispatch.
*
* NOTE: This traversal happens on IDs without touching the DOM.
*
* @param {string} targetID ID of the target node.
* @param {function} cb Callback to invoke.
* @param {*} arg Argument to invoke the callback with.
* @internal
*/
traverseTwoPhase: function(targetID, cb, arg) {
if (targetID) {
traverseParentPath('', targetID, cb, arg, true, false);
traverseParentPath(targetID, '', cb, arg, false, true);
}
},
/**
* Traverse a node ID, calling the supplied `cb` for each ancestor ID. For
* example, passing `.0.$row-0.1` would result in `cb` getting called
* with `.0`, `.0.$row-0`, and `.0.$row-0.1`.
*
* NOTE: This traversal happens on IDs without touching the DOM.
*
* @param {string} targetID ID of the target node.
* @param {function} cb Callback to invoke.
* @param {*} arg Argument to invoke the callback with.
* @internal
*/
traverseAncestors: function(targetID, cb, arg) {
traverseParentPath('', targetID, cb, arg, true, false);
},
/**
* Exposed for unit testing.
* @private
*/
_getFirstCommonAncestorID: getFirstCommonAncestorID,
/**
* Exposed for unit testing.
* @private
*/
_getNextDescendantID: getNextDescendantID,
isAncestorIDOf: isAncestorIDOf,
SEPARATOR: SEPARATOR
};
module.exports = ReactInstanceHandles;
},{"./ReactRootIndex":72,"./invariant":121}],58:[function(require,module,exports){
/**
* 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 ReactLink
* @typechecks static-only
*/
"use strict";
/**
* ReactLink encapsulates a common pattern in which a component wants to modify
* a prop received from its parent. ReactLink allows the parent to pass down a
* value coupled with a callback that, when invoked, expresses an intent to
* modify that value. For example:
*
* React.createClass({
* getInitialState: function() {
* return {value: ''};
* },
* render: function() {
* var valueLink = new ReactLink(this.state.value, this._handleValueChange);
* return <input valueLink={valueLink} />;
* },
* this._handleValueChange: function(newValue) {
* this.setState({value: newValue});
* }
* });
*
* We have provided some sugary mixins to make the creation and
* consumption of ReactLink easier; see LinkedValueUtils and LinkedStateMixin.
*/
/**
* @param {*} value current value of the link
* @param {function} requestChange callback to request a change
*/
function ReactLink(value, requestChange) {
this.value = value;
this.requestChange = requestChange;
}
module.exports = ReactLink;
},{}],59:[function(require,module,exports){
/**
* 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 ReactMarkupChecksum
*/
"use strict";
var adler32 = require("./adler32");
var ReactMarkupChecksum = {
CHECKSUM_ATTR_NAME: 'data-react-checksum',
/**
* @param {string} markup Markup string
* @return {string} Markup string with checksum attribute attached
*/
addChecksumToMarkup: function(markup) {
var checksum = adler32(markup);
return markup.replace(
'>',
' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="' + checksum + '">'
);
},
/**
* @param {string} markup to use
* @param {DOMElement} element root React element
* @returns {boolean} whether or not the markup is the same
*/
canReuseMarkup: function(markup, element) {
var existingChecksum = element.getAttribute(
ReactMarkupChecksum.CHECKSUM_ATTR_NAME
);
existingChecksum = existingChecksum && parseInt(existingChecksum, 10);
var markupChecksum = adler32(markup);
return markupChecksum === existingChecksum;
}
};
module.exports = ReactMarkupChecksum;
},{"./adler32":98}],60:[function(require,module,exports){
/**
* 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 ReactMount
*/
"use strict";
var DOMProperty = require("./DOMProperty");
var ReactEventEmitter = require("./ReactEventEmitter");
var ReactInstanceHandles = require("./ReactInstanceHandles");
var ReactPerf = require("./ReactPerf");
var containsNode = require("./containsNode");
var getReactRootElementInContainer = require("./getReactRootElementInContainer");
var invariant = require("./invariant");
var shouldUpdateReactComponent = require("./shouldUpdateReactComponent");
var SEPARATOR = ReactInstanceHandles.SEPARATOR;
var ATTR_NAME = DOMProperty.ID_ATTRIBUTE_NAME;
var nodeCache = {};
var ELEMENT_NODE_TYPE = 1;
var DOC_NODE_TYPE = 9;
/** Mapping from reactRootID to React component instance. */
var instancesByReactRootID = {};
/** Mapping from reactRootID to `container` nodes. */
var containersByReactRootID = {};
if ("production" !== "development") {
/** __DEV__-only mapping from reactRootID to root elements. */
var rootElementsByReactRootID = {};
}
// Used to store breadth-first search state in findComponentRoot.
var findComponentRootReusableArray = [];
/**
* @param {DOMElement} container DOM element that may contain a React component.
* @return {?string} A "reactRoot" ID, if a React component is rendered.
*/
function getReactRootID(container) {
var rootElement = getReactRootElementInContainer(container);
return rootElement && ReactMount.getID(rootElement);
}
/**
* Accessing node[ATTR_NAME] or calling getAttribute(ATTR_NAME) on a form
* element can return its control whose name or ID equals ATTR_NAME. All
* DOM nodes support `getAttributeNode` but this can also get called on
* other objects so just return '' if we're given something other than a
* DOM node (such as window).
*
* @param {?DOMElement|DOMWindow|DOMDocument|DOMTextNode} node DOM node.
* @return {string} ID of the supplied `domNode`.
*/
function getID(node) {
var id = internalGetID(node);
if (id) {
if (nodeCache.hasOwnProperty(id)) {
var cached = nodeCache[id];
if (cached !== node) {
("production" !== "development" ? invariant(
!isValid(cached, id),
'ReactMount: Two valid but unequal nodes with the same `%s`: %s',
ATTR_NAME, id
) : invariant(!isValid(cached, id)));
nodeCache[id] = node;
}
} else {
nodeCache[id] = node;
}
}
return id;
}
function internalGetID(node) {
// If node is something like a window, document, or text node, none of
// which support attributes or a .getAttribute method, gracefully return
// the empty string, as if the attribute were missing.
return node && node.getAttribute && node.getAttribute(ATTR_NAME) || '';
}
/**
* Sets the React-specific ID of the given node.
*
* @param {DOMElement} node The DOM node whose ID will be set.
* @param {string} id The value of the ID attribute.
*/
function setID(node, id) {
var oldID = internalGetID(node);
if (oldID !== id) {
delete nodeCache[oldID];
}
node.setAttribute(ATTR_NAME, id);
nodeCache[id] = node;
}
/**
* Finds the node with the supplied React-generated DOM ID.
*
* @param {string} id A React-generated DOM ID.
* @return {DOMElement} DOM node with the suppled `id`.
* @internal
*/
function getNode(id) {
if (!nodeCache.hasOwnProperty(id) || !isValid(nodeCache[id], id)) {
nodeCache[id] = ReactMount.findReactNodeByID(id);
}
return nodeCache[id];
}
/**
* A node is "valid" if it is contained by a currently mounted container.
*
* This means that the node does not have to be contained by a document in
* order to be considered valid.
*
* @param {?DOMElement} node The candidate DOM node.
* @param {string} id The expected ID of the node.
* @return {boolean} Whether the node is contained by a mounted container.
*/
function isValid(node, id) {
if (node) {
("production" !== "development" ? invariant(
internalGetID(node) === id,
'ReactMount: Unexpected modification of `%s`',
ATTR_NAME
) : invariant(internalGetID(node) === id));
var container = ReactMount.findReactContainerForID(id);
if (container && containsNode(container, node)) {
return true;
}
}
return false;
}
/**
* Causes the cache to forget about one React-specific ID.
*
* @param {string} id The ID to forget.
*/
function purgeID(id) {
delete nodeCache[id];
}
var deepestNodeSoFar = null;
function findDeepestCachedAncestorImpl(ancestorID) {
var ancestor = nodeCache[ancestorID];
if (ancestor && isValid(ancestor, ancestorID)) {
deepestNodeSoFar = ancestor;
} else {
// This node isn't populated in the cache, so presumably none of its
// descendants are. Break out of the loop.
return false;
}
}
/**
* Return the deepest cached node whose ID is a prefix of `targetID`.
*/
function findDeepestCachedAncestor(targetID) {
deepestNodeSoFar = null;
ReactInstanceHandles.traverseAncestors(
targetID,
findDeepestCachedAncestorImpl
);
var foundNode = deepestNodeSoFar;
deepestNodeSoFar = null;
return foundNode;
}
/**
* Mounting is the process of initializing a React component by creatings its
* representative DOM elements and inserting them into a supplied `container`.
* Any prior content inside `container` is destroyed in the process.
*
* ReactMount.renderComponent(
* component,
* document.getElementById('container')
* );
*
* <div id="container"> <-- Supplied `container`.
* <div data-reactid=".3"> <-- Rendered reactRoot of React
* // ... component.
* </div>
* </div>
*
* Inside of `container`, the first element rendered is the "reactRoot".
*/
var ReactMount = {
/** Time spent generating markup. */
totalInstantiationTime: 0,
/** Time spent inserting markup into the DOM. */
totalInjectionTime: 0,
/** Whether support for touch events should be initialized. */
useTouchEvents: false,
/** Exposed for debugging purposes **/
_instancesByReactRootID: instancesByReactRootID,
/**
* This is a hook provided to support rendering React components while
* ensuring that the apparent scroll position of its `container` does not
* change.
*
* @param {DOMElement} container The `container` being rendered into.
* @param {function} renderCallback This must be called once to do the render.
*/
scrollMonitor: function(container, renderCallback) {
renderCallback();
},
/**
* Take a component that's already mounted into the DOM and replace its props
* @param {ReactComponent} prevComponent component instance already in the DOM
* @param {ReactComponent} nextComponent component instance to render
* @param {DOMElement} container container to render into
* @param {?function} callback function triggered on completion
*/
_updateRootComponent: function(
prevComponent,
nextComponent,
container,
callback) {
var nextProps = nextComponent.props;
ReactMount.scrollMonitor(container, function() {
prevComponent.replaceProps(nextProps, callback);
});
if ("production" !== "development") {
// Record the root element in case it later gets transplanted.
rootElementsByReactRootID[getReactRootID(container)] =
getReactRootElementInContainer(container);
}
return prevComponent;
},
/**
* Register a component into the instance map and starts scroll value
* monitoring
* @param {ReactComponent} nextComponent component instance to render
* @param {DOMElement} container container to render into
* @return {string} reactRoot ID prefix
*/
_registerComponent: function(nextComponent, container) {
("production" !== "development" ? invariant(
container && (
container.nodeType === ELEMENT_NODE_TYPE ||
container.nodeType === DOC_NODE_TYPE
),
'_registerComponent(...): Target container is not a DOM element.'
) : invariant(container && (
container.nodeType === ELEMENT_NODE_TYPE ||
container.nodeType === DOC_NODE_TYPE
)));
ReactEventEmitter.ensureScrollValueMonitoring();
var reactRootID = ReactMount.registerContainer(container);
instancesByReactRootID[reactRootID] = nextComponent;
return reactRootID;
},
/**
* Render a new component into the DOM.
* @param {ReactComponent} nextComponent component instance to render
* @param {DOMElement} container container to render into
* @param {boolean} shouldReuseMarkup if we should skip the markup insertion
* @return {ReactComponent} nextComponent
*/
_renderNewRootComponent: ReactPerf.measure(
'ReactMount',
'_renderNewRootComponent',
function(
nextComponent,
container,
shouldReuseMarkup) {
var reactRootID = ReactMount._registerComponent(nextComponent, container);
nextComponent.mountComponentIntoNode(
reactRootID,
container,
shouldReuseMarkup
);
if ("production" !== "development") {
// Record the root element in case it later gets transplanted.
rootElementsByReactRootID[reactRootID] =
getReactRootElementInContainer(container);
}
return nextComponent;
}
),
/**
* Renders a React component into the DOM in the supplied `container`.
*
* If the React component was previously rendered into `container`, this will
* perform an update on it and only mutate the DOM as necessary to reflect the
* latest React component.
*
* @param {ReactComponent} nextComponent Component instance to render.
* @param {DOMElement} container DOM element to render into.
* @param {?function} callback function triggered on completion
* @return {ReactComponent} Component instance rendered in `container`.
*/
renderComponent: function(nextComponent, container, callback) {
var prevComponent = instancesByReactRootID[getReactRootID(container)];
if (prevComponent) {
if (shouldUpdateReactComponent(prevComponent, nextComponent)) {
return ReactMount._updateRootComponent(
prevComponent,
nextComponent,
container,
callback
);
} else {
ReactMount.unmountComponentAtNode(container);
}
}
var reactRootElement = getReactRootElementInContainer(container);
var containerHasReactMarkup =
reactRootElement && ReactMount.isRenderedByReact(reactRootElement);
var shouldReuseMarkup = containerHasReactMarkup && !prevComponent;
var component = ReactMount._renderNewRootComponent(
nextComponent,
container,
shouldReuseMarkup
);
callback && callback.call(component);
return component;
},
/**
* Constructs a component instance of `constructor` with `initialProps` and
* renders it into the supplied `container`.
*
* @param {function} constructor React component constructor.
* @param {?object} props Initial props of the component instance.
* @param {DOMElement} container DOM element to render into.
* @return {ReactComponent} Component instance rendered in `container`.
*/
constructAndRenderComponent: function(constructor, props, container) {
return ReactMount.renderComponent(constructor(props), container);
},
/**
* Constructs a component instance of `constructor` with `initialProps` and
* renders it into a container node identified by supplied `id`.
*
* @param {function} componentConstructor React component constructor
* @param {?object} props Initial props of the component instance.
* @param {string} id ID of the DOM element to render into.
* @return {ReactComponent} Component instance rendered in the container node.
*/
constructAndRenderComponentByID: function(constructor, props, id) {
var domNode = document.getElementById(id);
("production" !== "development" ? invariant(
domNode,
'Tried to get element with id of "%s" but it is not present on the page.',
id
) : invariant(domNode));
return ReactMount.constructAndRenderComponent(constructor, props, domNode);
},
/**
* Registers a container node into which React components will be rendered.
* This also creates the "reactRoot" ID that will be assigned to the element
* rendered within.
*
* @param {DOMElement} container DOM element to register as a container.
* @return {string} The "reactRoot" ID of elements rendered within.
*/
registerContainer: function(container) {
var reactRootID = getReactRootID(container);
if (reactRootID) {
// If one exists, make sure it is a valid "reactRoot" ID.
reactRootID = ReactInstanceHandles.getReactRootIDFromNodeID(reactRootID);
}
if (!reactRootID) {
// No valid "reactRoot" ID found, create one.
reactRootID = ReactInstanceHandles.createReactRootID();
}
containersByReactRootID[reactRootID] = container;
return reactRootID;
},
/**
* Unmounts and destroys the React component rendered in the `container`.
*
* @param {DOMElement} container DOM element containing a React component.
* @return {boolean} True if a component was found in and unmounted from
* `container`
*/
unmountComponentAtNode: function(container) {
var reactRootID = getReactRootID(container);
var component = instancesByReactRootID[reactRootID];
if (!component) {
return false;
}
ReactMount.unmountComponentFromNode(component, container);
delete instancesByReactRootID[reactRootID];
delete containersByReactRootID[reactRootID];
if ("production" !== "development") {
delete rootElementsByReactRootID[reactRootID];
}
return true;
},
/**
* Unmounts a component and removes it from the DOM.
*
* @param {ReactComponent} instance React component instance.
* @param {DOMElement} container DOM element to unmount from.
* @final
* @internal
* @see {ReactMount.unmountComponentAtNode}
*/
unmountComponentFromNode: function(instance, container) {
instance.unmountComponent();
if (container.nodeType === DOC_NODE_TYPE) {
container = container.documentElement;
}
// http://jsperf.com/emptying-a-node
while (container.lastChild) {
container.removeChild(container.lastChild);
}
},
/**
* Finds the container DOM element that contains React component to which the
* supplied DOM `id` belongs.
*
* @param {string} id The ID of an element rendered by a React component.
* @return {?DOMElement} DOM element that contains the `id`.
*/
findReactContainerForID: function(id) {
var reactRootID = ReactInstanceHandles.getReactRootIDFromNodeID(id);
var container = containersByReactRootID[reactRootID];
if ("production" !== "development") {
var rootElement = rootElementsByReactRootID[reactRootID];
if (rootElement && rootElement.parentNode !== container) {
("production" !== "development" ? invariant(
// Call internalGetID here because getID calls isValid which calls
// findReactContainerForID (this function).
internalGetID(rootElement) === reactRootID,
'ReactMount: Root element ID differed from reactRootID.'
) : invariant(// Call internalGetID here because getID calls isValid which calls
// findReactContainerForID (this function).
internalGetID(rootElement) === reactRootID));
var containerChild = container.firstChild;
if (containerChild &&
reactRootID === internalGetID(containerChild)) {
// If the container has a new child with the same ID as the old
// root element, then rootElementsByReactRootID[reactRootID] is
// just stale and needs to be updated. The case that deserves a
// warning is when the container is empty.
rootElementsByReactRootID[reactRootID] = containerChild;
} else {
console.warn(
'ReactMount: Root element has been removed from its original ' +
'container. New container:', rootElement.parentNode
);
}
}
}
return container;
},
/**
* Finds an element rendered by React with the supplied ID.
*
* @param {string} id ID of a DOM node in the React component.
* @return {DOMElement} Root DOM node of the React component.
*/
findReactNodeByID: function(id) {
var reactRoot = ReactMount.findReactContainerForID(id);
return ReactMount.findComponentRoot(reactRoot, id);
},
/**
* True if the supplied `node` is rendered by React.
*
* @param {*} node DOM Element to check.
* @return {boolean} True if the DOM Element appears to be rendered by React.
* @internal
*/
isRenderedByReact: function(node) {
if (node.nodeType !== 1) {
// Not a DOMElement, therefore not a React component
return false;
}
var id = ReactMount.getID(node);
return id ? id.charAt(0) === SEPARATOR : false;
},
/**
* Traverses up the ancestors of the supplied node to find a node that is a
* DOM representation of a React component.
*
* @param {*} node
* @return {?DOMEventTarget}
* @internal
*/
getFirstReactDOM: function(node) {
var current = node;
while (current && current.parentNode !== current) {
if (ReactMount.isRenderedByReact(current)) {
return current;
}
current = current.parentNode;
}
return null;
},
/**
* Finds a node with the supplied `targetID` inside of the supplied
* `ancestorNode`. Exploits the ID naming scheme to perform the search
* quickly.
*
* @param {DOMEventTarget} ancestorNode Search from this root.
* @pararm {string} targetID ID of the DOM representation of the component.
* @return {DOMEventTarget} DOM node with the supplied `targetID`.
* @internal
*/
findComponentRoot: function(ancestorNode, targetID) {
var firstChildren = findComponentRootReusableArray;
var childIndex = 0;
var deepestAncestor = findDeepestCachedAncestor(targetID) || ancestorNode;
firstChildren[0] = deepestAncestor.firstChild;
firstChildren.length = 1;
while (childIndex < firstChildren.length) {
var child = firstChildren[childIndex++];
var targetChild;
while (child) {
var childID = ReactMount.getID(child);
if (childID) {
// Even if we find the node we're looking for, we finish looping
// through its siblings to ensure they're cached so that we don't have
// to revisit this node again. Otherwise, we make n^2 calls to getID
// when visiting the many children of a single node in order.
if (targetID === childID) {
targetChild = child;
} else if (ReactInstanceHandles.isAncestorIDOf(childID, targetID)) {
// If we find a child whose ID is an ancestor of the given ID,
// then we can be sure that we only want to search the subtree
// rooted at this child, so we can throw out the rest of the
// search state.
firstChildren.length = childIndex = 0;
firstChildren.push(child.firstChild);
}
} else {
// If this child had no ID, then there's a chance that it was
// injected automatically by the browser, as when a `<table>`
// element sprouts an extra `<tbody>` child as a side effect of
// `.innerHTML` parsing. Optimistically continue down this
// branch, but not before examining the other siblings.
firstChildren.push(child.firstChild);
}
child = child.nextSibling;
}
if (targetChild) {
// Emptying firstChildren/findComponentRootReusableArray is
// not necessary for correctness, but it helps the GC reclaim
// any nodes that were left at the end of the search.
firstChildren.length = 0;
return targetChild;
}
}
firstChildren.length = 0;
("production" !== "development" ? invariant(
false,
'findComponentRoot(..., %s): Unable to find element. This probably ' +
'means the DOM was unexpectedly mutated (e.g., by the browser). ' +
'Try inspecting the child nodes of the element with React ID `%s`.',
targetID,
ReactMount.getID(ancestorNode)
) : invariant(false));
},
/**
* React ID utilities.
*/
getReactRootID: getReactRootID,
getID: getID,
setID: setID,
getNode: getNode,
purgeID: purgeID
};
module.exports = ReactMount;
},{"./DOMProperty":9,"./ReactEventEmitter":52,"./ReactInstanceHandles":57,"./ReactPerf":65,"./containsNode":100,"./getReactRootElementInContainer":117,"./invariant":121,"./shouldUpdateReactComponent":139}],61:[function(require,module,exports){
/**
* 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 ReactMountReady
*/
"use strict";
var PooledClass = require("./PooledClass");
var mixInto = require("./mixInto");
/**
* A specialized pseudo-event module to help keep track of components waiting to
* be notified when their DOM representations are available for use.
*
* This implements `PooledClass`, so you should never need to instantiate this.
* Instead, use `ReactMountReady.getPooled()`.
*
* @param {?array<function>} initialCollection
* @class ReactMountReady
* @implements PooledClass
* @internal
*/
function ReactMountReady(initialCollection) {
this._queue = initialCollection || null;
}
mixInto(ReactMountReady, {
/**
* Enqueues a callback to be invoked when `notifyAll` is invoked. This is used
* to enqueue calls to `componentDidMount` and `componentDidUpdate`.
*
* @param {ReactComponent} component Component being rendered.
* @param {function(DOMElement)} callback Invoked when `notifyAll` is invoked.
* @internal
*/
enqueue: function(component, callback) {
this._queue = this._queue || [];
this._queue.push({component: component, callback: callback});
},
/**
* Invokes all enqueued callbacks and clears the queue. This is invoked after
* the DOM representation of a component has been created or updated.
*
* @internal
*/
notifyAll: function() {
var queue = this._queue;
if (queue) {
this._queue = null;
for (var i = 0, l = queue.length; i < l; i++) {
var component = queue[i].component;
var callback = queue[i].callback;
callback.call(component);
}
queue.length = 0;
}
},
/**
* Resets the internal queue.
*
* @internal
*/
reset: function() {
this._queue = null;
},
/**
* `PooledClass` looks for this.
*/
destructor: function() {
this.reset();
}
});
PooledClass.addPoolingTo(ReactMountReady);
module.exports = ReactMountReady;
},{"./PooledClass":25,"./mixInto":133}],62:[function(require,module,exports){
/**
* 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 ReactMultiChild
* @typechecks static-only
*/
"use strict";
var ReactComponent = require("./ReactComponent");
var ReactMultiChildUpdateTypes = require("./ReactMultiChildUpdateTypes");
var flattenChildren = require("./flattenChildren");
var shouldUpdateReactComponent = require("./shouldUpdateReactComponent");
/**
* Updating children of a component may trigger recursive updates. The depth is
* used to batch recursive updates to render markup more efficiently.
*
* @type {number}
* @private
*/
var updateDepth = 0;
/**
* Queue of update configuration objects.
*
* Each object has a `type` property that is in `ReactMultiChildUpdateTypes`.
*
* @type {array<object>}
* @private
*/
var updateQueue = [];
/**
* Queue of markup to be rendered.
*
* @type {array<string>}
* @private
*/
var markupQueue = [];
/**
* Enqueues markup to be rendered and inserted at a supplied index.
*
* @param {string} parentID ID of the parent component.
* @param {string} markup Markup that renders into an element.
* @param {number} toIndex Destination index.
* @private
*/
function enqueueMarkup(parentID, markup, toIndex) {
// NOTE: Null values reduce hidden classes.
updateQueue.push({
parentID: parentID,
parentNode: null,
type: ReactMultiChildUpdateTypes.INSERT_MARKUP,
markupIndex: markupQueue.push(markup) - 1,
textContent: null,
fromIndex: null,
toIndex: toIndex
});
}
/**
* Enqueues moving an existing element to another index.
*
* @param {string} parentID ID of the parent component.
* @param {number} fromIndex Source index of the existing element.
* @param {number} toIndex Destination index of the element.
* @private
*/
function enqueueMove(parentID, fromIndex, toIndex) {
// NOTE: Null values reduce hidden classes.
updateQueue.push({
parentID: parentID,
parentNode: null,
type: ReactMultiChildUpdateTypes.MOVE_EXISTING,
markupIndex: null,
textContent: null,
fromIndex: fromIndex,
toIndex: toIndex
});
}
/**
* Enqueues removing an element at an index.
*
* @param {string} parentID ID of the parent component.
* @param {number} fromIndex Index of the element to remove.
* @private
*/
function enqueueRemove(parentID, fromIndex) {
// NOTE: Null values reduce hidden classes.
updateQueue.push({
parentID: parentID,
parentNode: null,
type: ReactMultiChildUpdateTypes.REMOVE_NODE,
markupIndex: null,
textContent: null,
fromIndex: fromIndex,
toIndex: null
});
}
/**
* Enqueues setting the text content.
*
* @param {string} parentID ID of the parent component.
* @param {string} textContent Text content to set.
* @private
*/
function enqueueTextContent(parentID, textContent) {
// NOTE: Null values reduce hidden classes.
updateQueue.push({
parentID: parentID,
parentNode: null,
type: ReactMultiChildUpdateTypes.TEXT_CONTENT,
markupIndex: null,
textContent: textContent,
fromIndex: null,
toIndex: null
});
}
/**
* Processes any enqueued updates.
*
* @private
*/
function processQueue() {
if (updateQueue.length) {
ReactComponent.BackendIDOperations.dangerouslyProcessChildrenUpdates(
updateQueue,
markupQueue
);
clearQueue();
}
}
/**
* Clears any enqueued updates.
*
* @private
*/
function clearQueue() {
updateQueue.length = 0;
markupQueue.length = 0;
}
/**
* ReactMultiChild are capable of reconciling multiple children.
*
* @class ReactMultiChild
* @internal
*/
var ReactMultiChild = {
/**
* Provides common functionality for components that must reconcile multiple
* children. This is used by `ReactDOMComponent` to mount, update, and
* unmount child components.
*
* @lends {ReactMultiChild.prototype}
*/
Mixin: {
/**
* Generates a "mount image" for each of the supplied children. In the case
* of `ReactDOMComponent`, a mount image is a string of markup.
*
* @param {?object} nestedChildren Nested child maps.
* @return {array} An array of mounted representations.
* @internal
*/
mountChildren: function(nestedChildren, transaction) {
var children = flattenChildren(nestedChildren);
var mountImages = [];
var index = 0;
this._renderedChildren = children;
for (var name in children) {
var child = children[name];
if (children.hasOwnProperty(name)) {
// Inlined for performance, see `ReactInstanceHandles.createReactID`.
var rootID = this._rootNodeID + name;
var mountImage = child.mountComponent(
rootID,
transaction,
this._mountDepth + 1
);
child._mountIndex = index;
mountImages.push(mountImage);
index++;
}
}
return mountImages;
},
/**
* Replaces any rendered children with a text content string.
*
* @param {string} nextContent String of content.
* @internal
*/
updateTextContent: function(nextContent) {
updateDepth++;
var errorThrown = true;
try {
var prevChildren = this._renderedChildren;
// Remove any rendered children.
for (var name in prevChildren) {
if (prevChildren.hasOwnProperty(name)) {
this._unmountChildByName(prevChildren[name], name);
}
}
// Set new text content.
this.setTextContent(nextContent);
errorThrown = false;
} finally {
updateDepth--;
if (!updateDepth) {
errorThrown ? clearQueue() : processQueue();
}
}
},
/**
* Updates the rendered children with new children.
*
* @param {?object} nextNestedChildren Nested child maps.
* @param {ReactReconcileTransaction} transaction
* @internal
*/
updateChildren: function(nextNestedChildren, transaction) {
updateDepth++;
var errorThrown = true;
try {
this._updateChildren(nextNestedChildren, transaction);
errorThrown = false;
} finally {
updateDepth--;
if (!updateDepth) {
errorThrown ? clearQueue() : processQueue();
}
}
},
/**
* Improve performance by isolating this hot code path from the try/catch
* block in `updateChildren`.
*
* @param {?object} nextNestedChildren Nested child maps.
* @param {ReactReconcileTransaction} transaction
* @final
* @protected
*/
_updateChildren: function(nextNestedChildren, transaction) {
var nextChildren = flattenChildren(nextNestedChildren);
var prevChildren = this._renderedChildren;
if (!nextChildren && !prevChildren) {
return;
}
var name;
// `nextIndex` will increment for each child in `nextChildren`, but
// `lastIndex` will be the last index visited in `prevChildren`.
var lastIndex = 0;
var nextIndex = 0;
for (name in nextChildren) {
if (!nextChildren.hasOwnProperty(name)) {
continue;
}
var prevChild = prevChildren && prevChildren[name];
var nextChild = nextChildren[name];
if (shouldUpdateReactComponent(prevChild, nextChild)) {
this.moveChild(prevChild, nextIndex, lastIndex);
lastIndex = Math.max(prevChild._mountIndex, lastIndex);
prevChild.receiveComponent(nextChild, transaction);
prevChild._mountIndex = nextIndex;
} else {
if (prevChild) {
// Update `lastIndex` before `_mountIndex` gets unset by unmounting.
lastIndex = Math.max(prevChild._mountIndex, lastIndex);
this._unmountChildByName(prevChild, name);
}
this._mountChildByNameAtIndex(
nextChild, name, nextIndex, transaction
);
}
nextIndex++;
}
// Remove children that are no longer present.
for (name in prevChildren) {
if (prevChildren.hasOwnProperty(name) &&
!(nextChildren && nextChildren[name])) {
this._unmountChildByName(prevChildren[name], name);
}
}
},
/**
* Unmounts all rendered children. This should be used to clean up children
* when this component is unmounted.
*
* @internal
*/
unmountChildren: function() {
var renderedChildren = this._renderedChildren;
for (var name in renderedChildren) {
var renderedChild = renderedChildren[name];
// TODO: When is this not true?
if (renderedChild.unmountComponent) {
renderedChild.unmountComponent();
}
}
this._renderedChildren = null;
},
/**
* Moves a child component to the supplied index.
*
* @param {ReactComponent} child Component to move.
* @param {number} toIndex Destination index of the element.
* @param {number} lastIndex Last index visited of the siblings of `child`.
* @protected
*/
moveChild: function(child, toIndex, lastIndex) {
// If the index of `child` is less than `lastIndex`, then it needs to
// be moved. Otherwise, we do not need to move it because a child will be
// inserted or moved before `child`.
if (child._mountIndex < lastIndex) {
enqueueMove(this._rootNodeID, child._mountIndex, toIndex);
}
},
/**
* Creates a child component.
*
* @param {ReactComponent} child Component to create.
* @param {string} mountImage Markup to insert.
* @protected
*/
createChild: function(child, mountImage) {
enqueueMarkup(this._rootNodeID, mountImage, child._mountIndex);
},
/**
* Removes a child component.
*
* @param {ReactComponent} child Child to remove.
* @protected
*/
removeChild: function(child) {
enqueueRemove(this._rootNodeID, child._mountIndex);
},
/**
* Sets this text content string.
*
* @param {string} textContent Text content to set.
* @protected
*/
setTextContent: function(textContent) {
enqueueTextContent(this._rootNodeID, textContent);
},
/**
* Mounts a child with the supplied name.
*
* NOTE: This is part of `updateChildren` and is here for readability.
*
* @param {ReactComponent} child Component to mount.
* @param {string} name Name of the child.
* @param {number} index Index at which to insert the child.
* @param {ReactReconcileTransaction} transaction
* @private
*/
_mountChildByNameAtIndex: function(child, name, index, transaction) {
// Inlined for performance, see `ReactInstanceHandles.createReactID`.
var rootID = this._rootNodeID + name;
var mountImage = child.mountComponent(
rootID,
transaction,
this._mountDepth + 1
);
child._mountIndex = index;
this.createChild(child, mountImage);
this._renderedChildren = this._renderedChildren || {};
this._renderedChildren[name] = child;
},
/**
* Unmounts a rendered child by name.
*
* NOTE: This is part of `updateChildren` and is here for readability.
*
* @param {ReactComponent} child Component to unmount.
* @param {string} name Name of the child in `this._renderedChildren`.
* @private
*/
_unmountChildByName: function(child, name) {
// TODO: When is this not true?
if (ReactComponent.isValidComponent(child)) {
this.removeChild(child);
child._mountIndex = null;
child.unmountComponent();
delete this._renderedChildren[name];
}
}
}
};
module.exports = ReactMultiChild;
},{"./ReactComponent":30,"./ReactMultiChildUpdateTypes":63,"./flattenChildren":110,"./shouldUpdateReactComponent":139}],63:[function(require,module,exports){
/**
* 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 ReactMultiChildUpdateTypes
*/
"use strict";
var keyMirror = require("./keyMirror");
/**
* When a component's children are updated, a series of update configuration
* objects are created in order to batch and serialize the required changes.
*
* Enumerates all the possible types of update configurations.
*
* @internal
*/
var ReactMultiChildUpdateTypes = keyMirror({
INSERT_MARKUP: null,
MOVE_EXISTING: null,
REMOVE_NODE: null,
TEXT_CONTENT: null
});
module.exports = ReactMultiChildUpdateTypes;
},{"./keyMirror":127}],64:[function(require,module,exports){
/**
* 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 ReactOwner
*/
"use strict";
var invariant = require("./invariant");
/**
* ReactOwners are capable of storing references to owned components.
*
* All components are capable of //being// referenced by owner components, but
* only ReactOwner components are capable of //referencing// owned components.
* The named reference is known as a "ref".
*
* Refs are available when mounted and updated during reconciliation.
*
* var MyComponent = React.createClass({
* render: function() {
* return (
* <div onClick={this.handleClick}>
* <CustomComponent ref="custom" />
* </div>
* );
* },
* handleClick: function() {
* this.refs.custom.handleClick();
* },
* componentDidMount: function() {
* this.refs.custom.initialize();
* }
* });
*
* Refs should rarely be used. When refs are used, they should only be done to
* control data that is not handled by React's data flow.
*
* @class ReactOwner
*/
var ReactOwner = {
/**
* @param {?object} object
* @return {boolean} True if `object` is a valid owner.
* @final
*/
isValidOwner: function(object) {
return !!(
object &&
typeof object.attachRef === 'function' &&
typeof object.detachRef === 'function'
);
},
/**
* Adds a component by ref to an owner component.
*
* @param {ReactComponent} component Component to reference.
* @param {string} ref Name by which to refer to the component.
* @param {ReactOwner} owner Component on which to record the ref.
* @final
* @internal
*/
addComponentAsRefTo: function(component, ref, owner) {
("production" !== "development" ? invariant(
ReactOwner.isValidOwner(owner),
'addComponentAsRefTo(...): Only a ReactOwner can have refs. This ' +
'usually means that you\'re trying to add a ref to a component that ' +
'doesn\'t have an owner (that is, was not created inside of another ' +
'component\'s `render` method). Try rendering this component inside of ' +
'a new top-level component which will hold the ref.'
) : invariant(ReactOwner.isValidOwner(owner)));
owner.attachRef(ref, component);
},
/**
* Removes a component by ref from an owner component.
*
* @param {ReactComponent} component Component to dereference.
* @param {string} ref Name of the ref to remove.
* @param {ReactOwner} owner Component on which the ref is recorded.
* @final
* @internal
*/
removeComponentAsRefFrom: function(component, ref, owner) {
("production" !== "development" ? invariant(
ReactOwner.isValidOwner(owner),
'removeComponentAsRefFrom(...): Only a ReactOwner can have refs. This ' +
'usually means that you\'re trying to remove a ref to a component that ' +
'doesn\'t have an owner (that is, was not created inside of another ' +
'component\'s `render` method). Try rendering this component inside of ' +
'a new top-level component which will hold the ref.'
) : invariant(ReactOwner.isValidOwner(owner)));
// Check that `component` is still the current ref because we do not want to
// detach the ref if another component stole it.
if (owner.refs[ref] === component) {
owner.detachRef(ref);
}
},
/**
* A ReactComponent must mix this in to have refs.
*
* @lends {ReactOwner.prototype}
*/
Mixin: {
/**
* Lazily allocates the refs object and stores `component` as `ref`.
*
* @param {string} ref Reference name.
* @param {component} component Component to store as `ref`.
* @final
* @private
*/
attachRef: function(ref, component) {
("production" !== "development" ? invariant(
component.isOwnedBy(this),
'attachRef(%s, ...): Only a component\'s owner can store a ref to it.',
ref
) : invariant(component.isOwnedBy(this)));
var refs = this.refs || (this.refs = {});
refs[ref] = component;
},
/**
* Detaches a reference name.
*
* @param {string} ref Name to dereference.
* @final
* @private
*/
detachRef: function(ref) {
delete this.refs[ref];
}
}
};
module.exports = ReactOwner;
},{"./invariant":121}],65:[function(require,module,exports){
/**
* 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 ReactPerf
* @typechecks static-only
*/
"use strict";
/**
* ReactPerf is a general AOP system designed to measure performance. This
* module only has the hooks: see ReactDefaultPerf for the analysis tool.
*/
var ReactPerf = {
/**
* Boolean to enable/disable measurement. Set to false by default to prevent
* accidental logging and perf loss.
*/
enableMeasure: false,
/**
* Holds onto the measure function in use. By default, don't measure
* anything, but we'll override this if we inject a measure function.
*/
storedMeasure: _noMeasure,
/**
* Use this to wrap methods you want to measure. Zero overhead in production.
*
* @param {string} objName
* @param {string} fnName
* @param {function} func
* @return {function}
*/
measure: function(objName, fnName, func) {
if ("production" !== "development") {
var measuredFunc = null;
return function() {
if (ReactPerf.enableMeasure) {
if (!measuredFunc) {
measuredFunc = ReactPerf.storedMeasure(objName, fnName, func);
}
return measuredFunc.apply(this, arguments);
}
return func.apply(this, arguments);
};
}
return func;
},
injection: {
/**
* @param {function} measure
*/
injectMeasure: function(measure) {
ReactPerf.storedMeasure = measure;
}
}
};
/**
* Simply passes through the measured function, without measuring it.
*
* @param {string} objName
* @param {string} fnName
* @param {function} func
* @return {function}
*/
function _noMeasure(objName, fnName, func) {
return func;
}
module.exports = ReactPerf;
},{}],66:[function(require,module,exports){
/**
* 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 ReactPropTransferer
*/
"use strict";
var emptyFunction = require("./emptyFunction");
var invariant = require("./invariant");
var joinClasses = require("./joinClasses");
var merge = require("./merge");
/**
* Creates a transfer strategy that will merge prop values using the supplied
* `mergeStrategy`. If a prop was previously unset, this just sets it.
*
* @param {function} mergeStrategy
* @return {function}
*/
function createTransferStrategy(mergeStrategy) {
return function(props, key, value) {
if (!props.hasOwnProperty(key)) {
props[key] = value;
} else {
props[key] = mergeStrategy(props[key], value);
}
};
}
/**
* Transfer strategies dictate how props are transferred by `transferPropsTo`.
* NOTE: if you add any more exceptions to this list you should be sure to
* update `cloneWithProps()` accordingly.
*/
var TransferStrategies = {
/**
* Never transfer `children`.
*/
children: emptyFunction,
/**
* Transfer the `className` prop by merging them.
*/
className: createTransferStrategy(joinClasses),
/**
* Never transfer the `key` prop.
*/
key: emptyFunction,
/**
* Never transfer the `ref` prop.
*/
ref: emptyFunction,
/**
* Transfer the `style` prop (which is an object) by merging them.
*/
style: createTransferStrategy(merge)
};
/**
* ReactPropTransferer are capable of transferring props to another component
* using a `transferPropsTo` method.
*
* @class ReactPropTransferer
*/
var ReactPropTransferer = {
TransferStrategies: TransferStrategies,
/**
* Merge two props objects using TransferStrategies.
*
* @param {object} oldProps original props (they take precedence)
* @param {object} newProps new props to merge in
* @return {object} a new object containing both sets of props merged.
*/
mergeProps: function(oldProps, newProps) {
var props = merge(oldProps);
for (var thisKey in newProps) {
if (!newProps.hasOwnProperty(thisKey)) {
continue;
}
var transferStrategy = TransferStrategies[thisKey];
if (transferStrategy) {
transferStrategy(props, thisKey, newProps[thisKey]);
} else if (!props.hasOwnProperty(thisKey)) {
props[thisKey] = newProps[thisKey];
}
}
return props;
},
/**
* @lends {ReactPropTransferer.prototype}
*/
Mixin: {
/**
* Transfer props from this component to a target component.
*
* Props that do not have an explicit transfer strategy will be transferred
* only if the target component does not already have the prop set.
*
* This is usually used to pass down props to a returned root component.
*
* @param {ReactComponent} component Component receiving the properties.
* @return {ReactComponent} The supplied `component`.
* @final
* @protected
*/
transferPropsTo: function(component) {
("production" !== "development" ? invariant(
component._owner === this,
'%s: You can\'t call transferPropsTo() on a component that you ' +
'don\'t own, %s. This usually means you are calling ' +
'transferPropsTo() on a component passed in as props or children.',
this.constructor.displayName,
component.constructor.displayName
) : invariant(component._owner === this));
component.props = ReactPropTransferer.mergeProps(
component.props,
this.props
);
return component;
}
}
};
module.exports = ReactPropTransferer;
},{"./emptyFunction":108,"./invariant":121,"./joinClasses":126,"./merge":130}],67:[function(require,module,exports){
/**
* 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 ReactPropTypeLocationNames
*/
"use strict";
var ReactPropTypeLocationNames = {};
if ("production" !== "development") {
ReactPropTypeLocationNames = {
prop: 'prop',
context: 'context',
childContext: 'child context'
};
}
module.exports = ReactPropTypeLocationNames;
},{}],68:[function(require,module,exports){
/**
* 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 ReactPropTypeLocations
*/
"use strict";
var keyMirror = require("./keyMirror");
var ReactPropTypeLocations = keyMirror({
prop: null,
context: null,
childContext: null
});
module.exports = ReactPropTypeLocations;
},{"./keyMirror":127}],69:[function(require,module,exports){
/**
* 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 ReactPropTypes
*/
"use strict";
var ReactComponent = require("./ReactComponent");
var ReactPropTypeLocationNames = require("./ReactPropTypeLocationNames");
var warning = require("./warning");
var createObjectFrom = require("./createObjectFrom");
/**
* Collection of methods that allow declaration and validation of props that are
* supplied to React components. Example usage:
*
* var Props = require('ReactPropTypes');
* var MyArticle = React.createClass({
* propTypes: {
* // An optional string prop named "description".
* description: Props.string,
*
* // A required enum prop named "category".
* category: Props.oneOf(['News','Photos']).isRequired,
*
* // A prop named "dialog" that requires an instance of Dialog.
* dialog: Props.instanceOf(Dialog).isRequired
* },
* render: function() { ... }
* });
*
* A more formal specification of how these methods are used:
*
* type := array|bool|func|object|number|string|oneOf([...])|instanceOf(...)
* decl := ReactPropTypes.{type}(.isRequired)?
*
* Each and every declaration produces a function with the same signature. This
* allows the creation of custom validation functions. For example:
*
* var Props = require('ReactPropTypes');
* var MyLink = React.createClass({
* propTypes: {
* // An optional string or URI prop named "href".
* href: function(props, propName, componentName) {
* var propValue = props[propName];
* warning(
* propValue == null ||
* typeof propValue === 'string' ||
* propValue instanceof URI,
* 'Invalid `%s` supplied to `%s`, expected string or URI.',
* propName,
* componentName
* );
* }
* },
* render: function() { ... }
* });
*
* @internal
*/
var Props = {
array: createPrimitiveTypeChecker('array'),
bool: createPrimitiveTypeChecker('boolean'),
func: createPrimitiveTypeChecker('function'),
number: createPrimitiveTypeChecker('number'),
object: createPrimitiveTypeChecker('object'),
string: createPrimitiveTypeChecker('string'),
shape: createShapeTypeChecker,
oneOf: createEnumTypeChecker,
oneOfType: createUnionTypeChecker,
arrayOf: createArrayOfTypeChecker,
instanceOf: createInstanceTypeChecker,
renderable: createRenderableTypeChecker(),
component: createComponentTypeChecker(),
any: createAnyTypeChecker()
};
var ANONYMOUS = '<<anonymous>>';
function isRenderable(propValue) {
switch(typeof propValue) {
case 'number':
case 'string':
return true;
case 'object':
if (Array.isArray(propValue)) {
return propValue.every(isRenderable);
}
if (ReactComponent.isValidComponent(propValue)) {
return true;
}
for (var k in propValue) {
if (!isRenderable(propValue[k])) {
return false;
}
}
return true;
default:
return false;
}
}
// Equivalent of typeof but with special handling for arrays
function getPropType(propValue) {
var propType = typeof propValue;
if (propType === 'object' && Array.isArray(propValue)) {
return 'array';
}
return propType;
}
function createAnyTypeChecker() {
function validateAnyType(
shouldWarn, propValue, propName, componentName, location
) {
return true; // is always valid
}
return createChainableTypeChecker(validateAnyType);
}
function createPrimitiveTypeChecker(expectedType) {
function validatePrimitiveType(
shouldWarn, propValue, propName, componentName, location
) {
var propType = getPropType(propValue);
var isValid = propType === expectedType;
if (shouldWarn) {
("production" !== "development" ? warning(
isValid,
'Invalid %s `%s` of type `%s` supplied to `%s`, expected `%s`.',
ReactPropTypeLocationNames[location],
propName,
propType,
componentName,
expectedType
) : null);
}
return isValid;
}
return createChainableTypeChecker(validatePrimitiveType);
}
function createEnumTypeChecker(expectedValues) {
var expectedEnum = createObjectFrom(expectedValues);
function validateEnumType(
shouldWarn, propValue, propName, componentName, location
) {
var isValid = expectedEnum[propValue];
if (shouldWarn) {
("production" !== "development" ? warning(
isValid,
'Invalid %s `%s` supplied to `%s`, expected one of %s.',
ReactPropTypeLocationNames[location],
propName,
componentName,
JSON.stringify(Object.keys(expectedEnum))
) : null);
}
return isValid;
}
return createChainableTypeChecker(validateEnumType);
}
function createShapeTypeChecker(shapeTypes) {
function validateShapeType(
shouldWarn, propValue, propName, componentName, location
) {
var propType = getPropType(propValue);
var isValid = propType === 'object';
if (isValid) {
for (var key in shapeTypes) {
var checker = shapeTypes[key];
if (checker && !checker(propValue, key, componentName, location)) {
return false;
}
}
}
if (shouldWarn) {
("production" !== "development" ? warning(
isValid,
'Invalid %s `%s` of type `%s` supplied to `%s`, expected `object`.',
ReactPropTypeLocationNames[location],
propName,
propType,
componentName
) : null);
}
return isValid;
}
return createChainableTypeChecker(validateShapeType);
}
function createInstanceTypeChecker(expectedClass) {
function validateInstanceType(
shouldWarn, propValue, propName, componentName, location
) {
var isValid = propValue instanceof expectedClass;
if (shouldWarn) {
("production" !== "development" ? warning(
isValid,
'Invalid %s `%s` supplied to `%s`, expected instance of `%s`.',
ReactPropTypeLocationNames[location],
propName,
componentName,
expectedClass.name || ANONYMOUS
) : null);
}
return isValid;
}
return createChainableTypeChecker(validateInstanceType);
}
function createArrayOfTypeChecker(propTypeChecker) {
function validateArrayType(
shouldWarn, propValue, propName, componentName, location
) {
var isValid = Array.isArray(propValue);
if (isValid) {
for (var i = 0; i < propValue.length; i++) {
if (!propTypeChecker(propValue, i, componentName, location)) {
return false;
}
}
}
if (shouldWarn) {
("production" !== "development" ? warning(
isValid,
'Invalid %s `%s` supplied to `%s`, expected an array.',
ReactPropTypeLocationNames[location],
propName,
componentName
) : null);
}
return isValid;
}
return createChainableTypeChecker(validateArrayType);
}
function createRenderableTypeChecker() {
function validateRenderableType(
shouldWarn, propValue, propName, componentName, location
) {
var isValid = isRenderable(propValue);
if (shouldWarn) {
("production" !== "development" ? warning(
isValid,
'Invalid %s `%s` supplied to `%s`, expected a renderable prop.',
ReactPropTypeLocationNames[location],
propName,
componentName
) : null);
}
return isValid;
}
return createChainableTypeChecker(validateRenderableType);
}
function createComponentTypeChecker() {
function validateComponentType(
shouldWarn, propValue, propName, componentName, location
) {
var isValid = ReactComponent.isValidComponent(propValue);
if (shouldWarn) {
("production" !== "development" ? warning(
isValid,
'Invalid %s `%s` supplied to `%s`, expected a React component.',
ReactPropTypeLocationNames[location],
propName,
componentName
) : null);
}
return isValid;
}
return createChainableTypeChecker(validateComponentType);
}
function createUnionTypeChecker(arrayOfValidators) {
return function(props, propName, componentName, location) {
var isValid = false;
for (var ii = 0; ii < arrayOfValidators.length; ii++) {
var validate = arrayOfValidators[ii];
if (typeof validate.weak === 'function') {
validate = validate.weak;
}
if (validate(props, propName, componentName, location)) {
isValid = true;
break;
}
}
("production" !== "development" ? warning(
isValid,
'Invalid %s `%s` supplied to `%s`.',
ReactPropTypeLocationNames[location],
propName,
componentName || ANONYMOUS
) : null);
return isValid;
};
}
function createChainableTypeChecker(validate) {
function checkType(
isRequired, shouldWarn, props, propName, componentName, location
) {
var propValue = props[propName];
if (propValue != null) {
// Only validate if there is a value to check.
return validate(
shouldWarn,
propValue,
propName,
componentName || ANONYMOUS,
location
);
} else {
var isValid = !isRequired;
if (shouldWarn) {
("production" !== "development" ? warning(
isValid,
'Required %s `%s` was not specified in `%s`.',
ReactPropTypeLocationNames[location],
propName,
componentName || ANONYMOUS
) : null);
}
return isValid;
}
}
var checker = checkType.bind(null, false, true);
checker.weak = checkType.bind(null, false, false);
checker.isRequired = checkType.bind(null, true, true);
checker.weak.isRequired = checkType.bind(null, true, false);
checker.isRequired.weak = checker.weak.isRequired;
return checker;
}
module.exports = Props;
},{"./ReactComponent":30,"./ReactPropTypeLocationNames":67,"./createObjectFrom":105,"./warning":142}],70:[function(require,module,exports){
/**
* 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 ReactPutListenerQueue
*/
"use strict";
var PooledClass = require("./PooledClass");
var ReactEventEmitter = require("./ReactEventEmitter");
var mixInto = require("./mixInto");
function ReactPutListenerQueue() {
this.listenersToPut = [];
}
mixInto(ReactPutListenerQueue, {
enqueuePutListener: function(rootNodeID, propKey, propValue) {
this.listenersToPut.push({
rootNodeID: rootNodeID,
propKey: propKey,
propValue: propValue
});
},
putListeners: function() {
for (var i = 0; i < this.listenersToPut.length; i++) {
var listenerToPut = this.listenersToPut[i];
ReactEventEmitter.putListener(
listenerToPut.rootNodeID,
listenerToPut.propKey,
listenerToPut.propValue
);
}
},
reset: function() {
this.listenersToPut.length = 0;
},
destructor: function() {
this.reset();
}
});
PooledClass.addPoolingTo(ReactPutListenerQueue);
module.exports = ReactPutListenerQueue;
},{"./PooledClass":25,"./ReactEventEmitter":52,"./mixInto":133}],71:[function(require,module,exports){
/**
* 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 ReactReconcileTransaction
* @typechecks static-only
*/
"use strict";
var ExecutionEnvironment = require("./ExecutionEnvironment");
var PooledClass = require("./PooledClass");
var ReactEventEmitter = require("./ReactEventEmitter");
var ReactInputSelection = require("./ReactInputSelection");
var ReactMountReady = require("./ReactMountReady");
var ReactPutListenerQueue = require("./ReactPutListenerQueue");
var Transaction = require("./Transaction");
var mixInto = require("./mixInto");
/**
* Ensures that, when possible, the selection range (currently selected text
* input) is not disturbed by performing the transaction.
*/
var SELECTION_RESTORATION = {
/**
* @return {Selection} Selection information.
*/
initialize: ReactInputSelection.getSelectionInformation,
/**
* @param {Selection} sel Selection information returned from `initialize`.
*/
close: ReactInputSelection.restoreSelection
};
/**
* Suppresses events (blur/focus) that could be inadvertently dispatched due to
* high level DOM manipulations (like temporarily removing a text input from the
* DOM).
*/
var EVENT_SUPPRESSION = {
/**
* @return {boolean} The enabled status of `ReactEventEmitter` before the
* reconciliation.
*/
initialize: function() {
var currentlyEnabled = ReactEventEmitter.isEnabled();
ReactEventEmitter.setEnabled(false);
return currentlyEnabled;
},
/**
* @param {boolean} previouslyEnabled Enabled status of `ReactEventEmitter`
* before the reconciliation occured. `close` restores the previous value.
*/
close: function(previouslyEnabled) {
ReactEventEmitter.setEnabled(previouslyEnabled);
}
};
/**
* Provides a `ReactMountReady` queue for collecting `onDOMReady` callbacks
* during the performing of the transaction.
*/
var ON_DOM_READY_QUEUEING = {
/**
* Initializes the internal `onDOMReady` queue.
*/
initialize: function() {
this.reactMountReady.reset();
},
/**
* After DOM is flushed, invoke all registered `onDOMReady` callbacks.
*/
close: function() {
this.reactMountReady.notifyAll();
}
};
var PUT_LISTENER_QUEUEING = {
initialize: function() {
this.putListenerQueue.reset();
},
close: function() {
this.putListenerQueue.putListeners();
}
};
/**
* Executed within the scope of the `Transaction` instance. Consider these as
* being member methods, but with an implied ordering while being isolated from
* each other.
*/
var TRANSACTION_WRAPPERS = [
PUT_LISTENER_QUEUEING,
SELECTION_RESTORATION,
EVENT_SUPPRESSION,
ON_DOM_READY_QUEUEING
];
/**
* Currently:
* - The order that these are listed in the transaction is critical:
* - Suppresses events.
* - Restores selection range.
*
* Future:
* - Restore document/overflow scroll positions that were unintentionally
* modified via DOM insertions above the top viewport boundary.
* - Implement/integrate with customized constraint based layout system and keep
* track of which dimensions must be remeasured.
*
* @class ReactReconcileTransaction
*/
function ReactReconcileTransaction() {
this.reinitializeTransaction();
this.reactMountReady = ReactMountReady.getPooled(null);
this.putListenerQueue = ReactPutListenerQueue.getPooled();
}
var Mixin = {
/**
* @see Transaction
* @abstract
* @final
* @return {array<object>} List of operation wrap proceedures.
* TODO: convert to array<TransactionWrapper>
*/
getTransactionWrappers: function() {
if (ExecutionEnvironment.canUseDOM) {
return TRANSACTION_WRAPPERS;
} else {
return [];
}
},
/**
* @return {object} The queue to collect `onDOMReady` callbacks with.
* TODO: convert to ReactMountReady
*/
getReactMountReady: function() {
return this.reactMountReady;
},
getPutListenerQueue: function() {
return this.putListenerQueue;
},
/**
* `PooledClass` looks for this, and will invoke this before allowing this
* instance to be resused.
*/
destructor: function() {
ReactMountReady.release(this.reactMountReady);
this.reactMountReady = null;
ReactPutListenerQueue.release(this.putListenerQueue);
this.putListenerQueue = null;
}
};
mixInto(ReactReconcileTransaction, Transaction.Mixin);
mixInto(ReactReconcileTransaction, Mixin);
PooledClass.addPoolingTo(ReactReconcileTransaction);
module.exports = ReactReconcileTransaction;
},{"./ExecutionEnvironment":21,"./PooledClass":25,"./ReactEventEmitter":52,"./ReactInputSelection":56,"./ReactMountReady":61,"./ReactPutListenerQueue":70,"./Transaction":95,"./mixInto":133}],72:[function(require,module,exports){
/**
* 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 ReactRootIndex
* @typechecks
*/
"use strict";
var ReactRootIndexInjection = {
/**
* @param {function} _createReactRootIndex
*/
injectCreateReactRootIndex: function(_createReactRootIndex) {
ReactRootIndex.createReactRootIndex = _createReactRootIndex;
}
};
var ReactRootIndex = {
createReactRootIndex: null,
injection: ReactRootIndexInjection
};
module.exports = ReactRootIndex;
},{}],73:[function(require,module,exports){
/**
* 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.
*
* @typechecks static-only
* @providesModule ReactServerRendering
*/
"use strict";
var ReactComponent = require("./ReactComponent");
var ReactInstanceHandles = require("./ReactInstanceHandles");
var ReactMarkupChecksum = require("./ReactMarkupChecksum");
var ReactReconcileTransaction = require("./ReactReconcileTransaction");
var invariant = require("./invariant");
/**
* @param {ReactComponent} component
* @return {string} the markup
*/
function renderComponentToString(component) {
("production" !== "development" ? invariant(
ReactComponent.isValidComponent(component),
'renderComponentToString(): You must pass a valid ReactComponent.'
) : invariant(ReactComponent.isValidComponent(component)));
("production" !== "development" ? invariant(
!(arguments.length === 2 && typeof arguments[1] === 'function'),
'renderComponentToString(): This function became synchronous and now ' +
'returns the generated markup. Please remove the second parameter.'
) : invariant(!(arguments.length === 2 && typeof arguments[1] === 'function')));
var id = ReactInstanceHandles.createReactRootID();
var transaction = ReactReconcileTransaction.getPooled();
transaction.reinitializeTransaction();
try {
return transaction.perform(function() {
var markup = component.mountComponent(id, transaction, 0);
return ReactMarkupChecksum.addChecksumToMarkup(markup);
}, null);
} finally {
ReactReconcileTransaction.release(transaction);
}
}
module.exports = {
renderComponentToString: renderComponentToString
};
},{"./ReactComponent":30,"./ReactInstanceHandles":57,"./ReactMarkupChecksum":59,"./ReactReconcileTransaction":71,"./invariant":121}],74:[function(require,module,exports){
/**
* 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 ReactStateSetters
*/
"use strict";
var ReactStateSetters = {
/**
* Returns a function that calls the provided function, and uses the result
* of that to set the component's state.
*
* @param {ReactCompositeComponent} component
* @param {function} funcReturningState Returned callback uses this to
* determine how to update state.
* @return {function} callback that when invoked uses funcReturningState to
* determined the object literal to setState.
*/
createStateSetter: function(component, funcReturningState) {
return function(a, b, c, d, e, f) {
var partialState = funcReturningState.call(component, a, b, c, d, e, f);
if (partialState) {
component.setState(partialState);
}
};
},
/**
* Returns a single-argument callback that can be used to update a single
* key in the component's state.
*
* Note: this is memoized function, which makes it inexpensive to call.
*
* @param {ReactCompositeComponent} component
* @param {string} key The key in the state that you should update.
* @return {function} callback of 1 argument which calls setState() with
* the provided keyName and callback argument.
*/
createStateKeySetter: function(component, key) {
// Memoize the setters.
var cache = component.__keySetters || (component.__keySetters = {});
return cache[key] || (cache[key] = createStateKeySetter(component, key));
}
};
function createStateKeySetter(component, key) {
// Partial state is allocated outside of the function closure so it can be
// reused with every call, avoiding memory allocation when this function
// is called.
var partialState = {};
return function stateKeySetter(value) {
partialState[key] = value;
component.setState(partialState);
};
}
ReactStateSetters.Mixin = {
/**
* Returns a function that calls the provided function, and uses the result
* of that to set the component's state.
*
* For example, these statements are equivalent:
*
* this.setState({x: 1});
* this.createStateSetter(function(xValue) {
* return {x: xValue};
* })(1);
*
* @param {function} funcReturningState Returned callback uses this to
* determine how to update state.
* @return {function} callback that when invoked uses funcReturningState to
* determined the object literal to setState.
*/
createStateSetter: function(funcReturningState) {
return ReactStateSetters.createStateSetter(this, funcReturningState);
},
/**
* Returns a single-argument callback that can be used to update a single
* key in the component's state.
*
* For example, these statements are equivalent:
*
* this.setState({x: 1});
* this.createStateKeySetter('x')(1);
*
* Note: this is memoized function, which makes it inexpensive to call.
*
* @param {string} key The key in the state that you should update.
* @return {function} callback of 1 argument which calls setState() with
* the provided keyName and callback argument.
*/
createStateKeySetter: function(key) {
return ReactStateSetters.createStateKeySetter(this, key);
}
};
module.exports = ReactStateSetters;
},{}],75:[function(require,module,exports){
/**
* 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 ReactTestUtils
*/
"use strict";
var EventConstants = require("./EventConstants");
var EventPluginHub = require("./EventPluginHub");
var EventPropagators = require("./EventPropagators");
var React = require("./React");
var ReactComponent = require("./ReactComponent");
var ReactDOM = require("./ReactDOM");
var ReactEventEmitter = require("./ReactEventEmitter");
var ReactMount = require("./ReactMount");
var ReactTextComponent = require("./ReactTextComponent");
var ReactUpdates = require("./ReactUpdates");
var SyntheticEvent = require("./SyntheticEvent");
var mergeInto = require("./mergeInto");
var copyProperties = require("./copyProperties");
var topLevelTypes = EventConstants.topLevelTypes;
function Event(suffix) {}
/**
* @class ReactTestUtils
*/
/**
* Todo: Support the entire DOM.scry query syntax. For now, these simple
* utilities will suffice for testing purposes.
* @lends ReactTestUtils
*/
var ReactTestUtils = {
renderIntoDocument: function(instance) {
var div = document.createElement('div');
// None of our tests actually require attaching the container to the
// DOM, and doing so creates a mess that we rely on test isolation to
// clean up, so we're going to stop honoring the name of this method
// (and probably rename it eventually) if no problems arise.
// document.documentElement.appendChild(div);
return React.renderComponent(instance, div);
},
isComponentOfType: function(inst, convenienceConstructor) {
return (
ReactComponent.isValidComponent(inst) &&
inst.type === convenienceConstructor.type
);
},
isDOMComponent: function(inst) {
return !!(inst &&
ReactComponent.isValidComponent(inst) &&
!!inst.tagName);
},
isCompositeComponent: function(inst) {
if (!ReactComponent.isValidComponent(inst)) {
return false;
}
// We check the prototype of the type that will get mounted, not the
// instance itself. This is a future proof way of duck typing.
var prototype = inst.type.prototype;
return (
typeof prototype.render === 'function' &&
typeof prototype.setState === 'function' &&
typeof prototype.updateComponent === 'function'
);
},
isCompositeComponentWithType: function(inst, type) {
return !!(ReactTestUtils.isCompositeComponent(inst) &&
(inst.constructor === type.componentConstructor ||
inst.constructor === type));
},
isTextComponent: function(inst) {
return inst instanceof ReactTextComponent;
},
findAllInRenderedTree: function(inst, test) {
if (!inst) {
return [];
}
var ret = test(inst) ? [inst] : [];
if (ReactTestUtils.isDOMComponent(inst)) {
var renderedChildren = inst._renderedChildren;
var key;
for (key in renderedChildren) {
if (!renderedChildren.hasOwnProperty(key)) {
continue;
}
ret = ret.concat(
ReactTestUtils.findAllInRenderedTree(renderedChildren[key], test)
);
}
} else if (ReactTestUtils.isCompositeComponent(inst)) {
ret = ret.concat(
ReactTestUtils.findAllInRenderedTree(inst._renderedComponent, test)
);
}
return ret;
},
/**
* Finds all instance of components in the rendered tree that are DOM
* components with the class name matching `className`.
* @return an array of all the matches.
*/
scryRenderedDOMComponentsWithClass: function(root, className) {
return ReactTestUtils.findAllInRenderedTree(root, function(inst) {
var instClassName = inst.props.className;
return ReactTestUtils.isDOMComponent(inst) && (
instClassName &&
(' ' + instClassName + ' ').indexOf(' ' + className + ' ') !== -1
);
});
},
/**
* Like scryRenderedDOMComponentsWithClass but expects there to be one result,
* and returns that one result, or throws exception if there is any other
* number of matches besides one.
* @return {!ReactDOMComponent} The one match.
*/
findRenderedDOMComponentWithClass: function(root, className) {
var all =
ReactTestUtils.scryRenderedDOMComponentsWithClass(root, className);
if (all.length !== 1) {
throw new Error('Did not find exactly one match for class:' + className);
}
return all[0];
},
/**
* Finds all instance of components in the rendered tree that are DOM
* components with the tag name matching `tagName`.
* @return an array of all the matches.
*/
scryRenderedDOMComponentsWithTag: function(root, tagName) {
return ReactTestUtils.findAllInRenderedTree(root, function(inst) {
return ReactTestUtils.isDOMComponent(inst) &&
inst.tagName === tagName.toUpperCase();
});
},
/**
* Like scryRenderedDOMComponentsWithTag but expects there to be one result,
* and returns that one result, or throws exception if there is any other
* number of matches besides one.
* @return {!ReactDOMComponent} The one match.
*/
findRenderedDOMComponentWithTag: function(root, tagName) {
var all = ReactTestUtils.scryRenderedDOMComponentsWithTag(root, tagName);
if (all.length !== 1) {
throw new Error('Did not find exactly one match for tag:' + tagName);
}
return all[0];
},
/**
* Finds all instances of components with type equal to `componentType`.
* @return an array of all the matches.
*/
scryRenderedComponentsWithType: function(root, componentType) {
return ReactTestUtils.findAllInRenderedTree(root, function(inst) {
return ReactTestUtils.isCompositeComponentWithType(inst, componentType);
});
},
/**
* Same as `scryRenderedComponentsWithType` but expects there to be one result
* and returns that one result, or throws exception if there is any other
* number of matches besides one.
* @return {!ReactComponent} The one match.
*/
findRenderedComponentWithType: function(root, componentType) {
var all = ReactTestUtils.scryRenderedComponentsWithType(
root,
componentType
);
if (all.length !== 1) {
throw new Error(
'Did not find exactly one match for componentType:' + componentType
);
}
return all[0];
},
/**
* Pass a mocked component module to this method to augment it with
* useful methods that allow it to be used as a dummy React component.
* Instead of rendering as usual, the component will become a simple
* <div> containing any provided children.
*
* @param {object} module the mock function object exported from a
* module that defines the component to be mocked
* @param {?string} mockTagName optional dummy root tag name to return
* from render method (overrides
* module.mockTagName if provided)
* @return {object} the ReactTestUtils object (for chaining)
*/
mockComponent: function(module, mockTagName) {
var ConvenienceConstructor = React.createClass({
render: function() {
var mockTagName = mockTagName || module.mockTagName || "div";
return ReactDOM[mockTagName](null, this.props.children);
}
});
copyProperties(module, ConvenienceConstructor);
module.mockImplementation(ConvenienceConstructor);
return this;
},
/**
* Simulates a top level event being dispatched from a raw event that occured
* on an `Element` node.
* @param topLevelType {Object} A type from `EventConstants.topLevelTypes`
* @param {!Element} node The dom to simulate an event occurring on.
* @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent.
*/
simulateNativeEventOnNode: function(topLevelType, node, fakeNativeEvent) {
var virtualHandler =
ReactEventEmitter.TopLevelCallbackCreator.createTopLevelCallback(
topLevelType
);
fakeNativeEvent.target = node;
virtualHandler(fakeNativeEvent);
},
/**
* Simulates a top level event being dispatched from a raw event that occured
* on the `ReactDOMComponent` `comp`.
* @param topLevelType {Object} A type from `EventConstants.topLevelTypes`.
* @param comp {!ReactDOMComponent}
* @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent.
*/
simulateNativeEventOnDOMComponent: function(
topLevelType,
comp,
fakeNativeEvent) {
ReactTestUtils.simulateNativeEventOnNode(
topLevelType,
comp.getDOMNode(),
fakeNativeEvent
);
},
nativeTouchData: function(x, y) {
return {
touches: [
{pageX: x, pageY: y}
]
};
},
Simulate: null,
SimulateNative: {}
};
/**
* Exports:
*
* - `ReactTestUtils.Simulate.click(Element/ReactDOMComponent)`
* - `ReactTestUtils.Simulate.mouseMove(Element/ReactDOMComponent)`
* - `ReactTestUtils.Simulate.change(Element/ReactDOMComponent)`
* - ... (All keys from event plugin `eventTypes` objects)
*/
function makeSimulator(eventType) {
return function(domComponentOrNode, eventData) {
var node;
if (ReactTestUtils.isDOMComponent(domComponentOrNode)) {
node = domComponentOrNode.getDOMNode();
} else if (domComponentOrNode.tagName) {
node = domComponentOrNode;
}
var fakeNativeEvent = new Event();
fakeNativeEvent.target = node;
// We don't use SyntheticEvent.getPooled in order to not have to worry about
// properly destroying any properties assigned from `eventData` upon release
var event = new SyntheticEvent(
ReactEventEmitter.eventNameDispatchConfigs[eventType],
ReactMount.getID(node),
fakeNativeEvent
);
mergeInto(event, eventData);
EventPropagators.accumulateTwoPhaseDispatches(event);
ReactUpdates.batchedUpdates(function() {
EventPluginHub.enqueueEvents(event);
EventPluginHub.processEventQueue();
});
};
}
function buildSimulators() {
ReactTestUtils.Simulate = {};
var eventType;
for (eventType in ReactEventEmitter.eventNameDispatchConfigs) {
/**
* @param {!Element || ReactDOMComponent} domComponentOrNode
* @param {?object} eventData Fake event data to use in SyntheticEvent.
*/
ReactTestUtils.Simulate[eventType] = makeSimulator(eventType);
}
}
// Rebuild ReactTestUtils.Simulate whenever event plugins are injected
var oldInjectEventPluginOrder = EventPluginHub.injection.injectEventPluginOrder;
EventPluginHub.injection.injectEventPluginOrder = function() {
oldInjectEventPluginOrder.apply(this, arguments);
buildSimulators();
};
var oldInjectEventPlugins = EventPluginHub.injection.injectEventPluginsByName;
EventPluginHub.injection.injectEventPluginsByName = function() {
oldInjectEventPlugins.apply(this, arguments);
buildSimulators();
};
buildSimulators();
/**
* Exports:
*
* - `ReactTestUtils.SimulateNative.click(Element/ReactDOMComponent)`
* - `ReactTestUtils.SimulateNative.mouseMove(Element/ReactDOMComponent)`
* - `ReactTestUtils.SimulateNative.mouseIn/ReactDOMComponent)`
* - `ReactTestUtils.SimulateNative.mouseOut(Element/ReactDOMComponent)`
* - ... (All keys from `EventConstants.topLevelTypes`)
*
* Note: Top level event types are a subset of the entire set of handler types
* (which include a broader set of "synthetic" events). For example, onDragDone
* is a synthetic event. Except when testing an event plugin or React's event
* handling code specifically, you probably want to use ReactTestUtils.Simulate
* to dispatch synthetic events.
*/
function makeNativeSimulator(eventType) {
return function(domComponentOrNode, nativeEventData) {
var fakeNativeEvent = new Event(eventType);
mergeInto(fakeNativeEvent, nativeEventData);
if (ReactTestUtils.isDOMComponent(domComponentOrNode)) {
ReactTestUtils.simulateNativeEventOnDOMComponent(
eventType,
domComponentOrNode,
fakeNativeEvent
);
} else if (!!domComponentOrNode.tagName) {
// Will allow on actual dom nodes.
ReactTestUtils.simulateNativeEventOnNode(
eventType,
domComponentOrNode,
fakeNativeEvent
);
}
};
}
var eventType;
for (eventType in topLevelTypes) {
// Event type is stored as 'topClick' - we transform that to 'click'
var convenienceName = eventType.indexOf('top') === 0 ?
eventType.charAt(3).toLowerCase() + eventType.substr(4) : eventType;
/**
* @param {!Element || ReactDOMComponent} domComponentOrNode
* @param {?Event} nativeEventData Fake native event to use in SyntheticEvent.
*/
ReactTestUtils.SimulateNative[convenienceName] =
makeNativeSimulator(eventType);
}
module.exports = ReactTestUtils;
},{"./EventConstants":15,"./EventPluginHub":17,"./EventPropagators":20,"./React":26,"./ReactComponent":30,"./ReactDOM":36,"./ReactEventEmitter":52,"./ReactMount":60,"./ReactTextComponent":76,"./ReactUpdates":80,"./SyntheticEvent":88,"./copyProperties":101,"./mergeInto":132}],76:[function(require,module,exports){
/**
* 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 ReactTextComponent
* @typechecks static-only
*/
"use strict";
var DOMPropertyOperations = require("./DOMPropertyOperations");
var ReactComponent = require("./ReactComponent");
var escapeTextForBrowser = require("./escapeTextForBrowser");
var mixInto = require("./mixInto");
/**
* Text nodes violate a couple assumptions that React makes about components:
*
* - When mounting text into the DOM, adjacent text nodes are merged.
* - Text nodes cannot be assigned a React root ID.
*
* This component is used to wrap strings in elements so that they can undergo
* the same reconciliation that is applied to elements.
*
* TODO: Investigate representing React components in the DOM with text nodes.
*
* @class ReactTextComponent
* @extends ReactComponent
* @internal
*/
var ReactTextComponent = function(initialText) {
this.construct({text: initialText});
};
mixInto(ReactTextComponent, ReactComponent.Mixin);
mixInto(ReactTextComponent, {
/**
* Creates the markup for this text node. This node is not intended to have
* any features besides containing text content.
*
* @param {string} rootID DOM ID of the root node.
* @param {ReactReconcileTransaction} transaction
* @param {number} mountDepth number of components in the owner hierarchy
* @return {string} Markup for this text node.
* @internal
*/
mountComponent: function(rootID, transaction, mountDepth) {
ReactComponent.Mixin.mountComponent.call(
this,
rootID,
transaction,
mountDepth
);
return (
'<span ' + DOMPropertyOperations.createMarkupForID(rootID) + '>' +
escapeTextForBrowser(this.props.text) +
'</span>'
);
},
/**
* Updates this component by updating the text content.
*
* @param {object} nextComponent Contains the next text content.
* @param {ReactReconcileTransaction} transaction
* @internal
*/
receiveComponent: function(nextComponent, transaction) {
var nextProps = nextComponent.props;
if (nextProps.text !== this.props.text) {
this.props.text = nextProps.text;
ReactComponent.BackendIDOperations.updateTextContentByID(
this._rootNodeID,
nextProps.text
);
}
}
});
// Expose the constructor on itself and the prototype for consistency with other
// descriptors.
ReactTextComponent.type = ReactTextComponent;
ReactTextComponent.prototype.type = ReactTextComponent;
module.exports = ReactTextComponent;
},{"./DOMPropertyOperations":10,"./ReactComponent":30,"./escapeTextForBrowser":109,"./mixInto":133}],77:[function(require,module,exports){
/**
* 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.
*
* @typechecks static-only
* @providesModule ReactTransitionChildMapping
*/
"use strict";
var ReactChildren = require("./ReactChildren");
var ReactTransitionChildMapping = {
/**
* Given `this.props.children`, return an object mapping key to child. Just
* simple syntactic sugar around ReactChildren.map().
*
* @param {*} children `this.props.children`
* @return {object} Mapping of key to child
*/
getChildMapping: function(children) {
return ReactChildren.map(children, function(child) {
return child;
});
},
/**
* When you're adding or removing children some may be added or removed in the
* same render pass. We want ot show *both* since we want to simultaneously
* animate elements in and out. This function takes a previous set of keys
* and a new set of keys and merges them with its best guess of the correct
* ordering. In the future we may expose some of the utilities in
* ReactMultiChild to make this easy, but for now React itself does not
* directly have this concept of the union of prevChildren and nextChildren
* so we implement it here.
*
* @param {object} prev prev children as returned from
* `ReactTransitionChildMapping.getChildMapping()`.
* @param {object} next next children as returned from
* `ReactTransitionChildMapping.getChildMapping()`.
* @return {object} a key set that contains all keys in `prev` and all keys
* in `next` in a reasonable order.
*/
mergeChildMappings: function(prev, next) {
prev = prev || {};
next = next || {};
function getValueForKey(key) {
if (next.hasOwnProperty(key)) {
return next[key];
} else {
return prev[key];
}
}
// For each key of `next`, the list of keys to insert before that key in
// the combined list
var nextKeysPending = {};
var pendingKeys = [];
for (var prevKey in prev) {
if (next[prevKey]) {
if (pendingKeys.length) {
nextKeysPending[prevKey] = pendingKeys;
pendingKeys = [];
}
} else {
pendingKeys.push(prevKey);
}
}
var i;
var childMapping = {};
for (var nextKey in next) {
if (nextKeysPending[nextKey]) {
for (i = 0; i < nextKeysPending[nextKey].length; i++) {
var pendingNextKey = nextKeysPending[nextKey][i];
childMapping[nextKeysPending[nextKey][i]] = getValueForKey(
pendingNextKey
);
}
}
childMapping[nextKey] = getValueForKey(nextKey);
}
// Finally, add the keys which didn't appear before any key in `next`
for (i = 0; i < pendingKeys.length; i++) {
childMapping[pendingKeys[i]] = getValueForKey(pendingKeys[i]);
}
return childMapping;
}
};
module.exports = ReactTransitionChildMapping;
},{"./ReactChildren":29}],78:[function(require,module,exports){
/**
* 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 ReactTransitionEvents
*/
"use strict";
var ExecutionEnvironment = require("./ExecutionEnvironment");
var EVENT_NAME_MAP = {
transitionend: {
'transition': 'transitionend',
'WebkitTransition': 'webkitTransitionEnd',
'MozTransition': 'mozTransitionEnd',
'OTransition': 'oTransitionEnd',
'msTransition': 'MSTransitionEnd'
},
animationend: {
'animation': 'animationend',
'WebkitAnimation': 'webkitAnimationEnd',
'MozAnimation': 'mozAnimationEnd',
'OAnimation': 'oAnimationEnd',
'msAnimation': 'MSAnimationEnd'
}
};
var endEvents = [];
function detectEvents() {
var testEl = document.createElement('div');
var style = testEl.style;
for (var baseEventName in EVENT_NAME_MAP) {
var baseEvents = EVENT_NAME_MAP[baseEventName];
for (var styleName in baseEvents) {
if (styleName in style) {
endEvents.push(baseEvents[styleName]);
break;
}
}
}
}
if (ExecutionEnvironment.canUseDOM) {
detectEvents();
}
// We use the raw {add|remove}EventListener() call because EventListener
// does not know how to remove event listeners and we really should
// clean up. Also, these events are not triggered in older browsers
// so we should be A-OK here.
function addEventListener(node, eventName, eventListener) {
node.addEventListener(eventName, eventListener, false);
}
function removeEventListener(node, eventName, eventListener) {
node.removeEventListener(eventName, eventListener, false);
}
var ReactTransitionEvents = {
addEndEventListener: function(node, eventListener) {
if (endEvents.length === 0) {
// If CSS transitions are not supported, trigger an "end animation"
// event immediately.
window.setTimeout(eventListener, 0);
return;
}
endEvents.forEach(function(endEvent) {
addEventListener(node, endEvent, eventListener);
});
},
removeEndEventListener: function(node, eventListener) {
if (endEvents.length === 0) {
return;
}
endEvents.forEach(function(endEvent) {
removeEventListener(node, endEvent, eventListener);
});
}
};
module.exports = ReactTransitionEvents;
},{"./ExecutionEnvironment":21}],79:[function(require,module,exports){
/**
* 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 ReactTransitionGroup
*/
"use strict";
var React = require("./React");
var ReactTransitionChildMapping = require("./ReactTransitionChildMapping");
var cloneWithProps = require("./cloneWithProps");
var emptyFunction = require("./emptyFunction");
var merge = require("./merge");
var ReactTransitionGroup = React.createClass({
propTypes: {
component: React.PropTypes.func,
childFactory: React.PropTypes.func
},
getDefaultProps: function() {
return {
component: React.DOM.span,
childFactory: emptyFunction.thatReturnsArgument
};
},
getInitialState: function() {
return {
children: ReactTransitionChildMapping.getChildMapping(this.props.children)
};
},
componentWillReceiveProps: function(nextProps) {
var nextChildMapping = ReactTransitionChildMapping.getChildMapping(
nextProps.children
);
var prevChildMapping = this.state.children;
this.setState({
children: ReactTransitionChildMapping.mergeChildMappings(
prevChildMapping,
nextChildMapping
)
});
var key;
for (key in nextChildMapping) {
if (!prevChildMapping.hasOwnProperty(key) &&
!this.currentlyTransitioningKeys[key]) {
this.keysToEnter.push(key);
}
}
for (key in prevChildMapping) {
if (!nextChildMapping.hasOwnProperty(key) &&
!this.currentlyTransitioningKeys[key]) {
this.keysToLeave.push(key);
}
}
// If we want to someday check for reordering, we could do it here.
},
componentWillMount: function() {
this.currentlyTransitioningKeys = {};
this.keysToEnter = [];
this.keysToLeave = [];
},
componentDidUpdate: function() {
var keysToEnter = this.keysToEnter;
this.keysToEnter = [];
keysToEnter.forEach(this.performEnter);
var keysToLeave = this.keysToLeave;
this.keysToLeave = [];
keysToLeave.forEach(this.performLeave);
},
performEnter: function(key) {
this.currentlyTransitioningKeys[key] = true;
var component = this.refs[key];
if (component.componentWillEnter) {
component.componentWillEnter(
this._handleDoneEntering.bind(this, key)
);
} else {
this._handleDoneEntering(key);
}
},
_handleDoneEntering: function(key) {
var component = this.refs[key];
if (component.componentDidEnter) {
component.componentDidEnter();
}
delete this.currentlyTransitioningKeys[key];
var currentChildMapping = ReactTransitionChildMapping.getChildMapping(
this.props.children
);
if (!currentChildMapping.hasOwnProperty(key)) {
// This was removed before it had fully entered. Remove it.
this.performLeave(key);
}
},
performLeave: function(key) {
this.currentlyTransitioningKeys[key] = true;
var component = this.refs[key];
if (component.componentWillLeave) {
component.componentWillLeave(this._handleDoneLeaving.bind(this, key));
} else {
// Note that this is somewhat dangerous b/c it calls setState()
// again, effectively mutating the component before all the work
// is done.
this._handleDoneLeaving(key);
}
},
_handleDoneLeaving: function(key) {
var component = this.refs[key];
if (component.componentDidLeave) {
component.componentDidLeave();
}
delete this.currentlyTransitioningKeys[key];
var currentChildMapping = ReactTransitionChildMapping.getChildMapping(
this.props.children
);
if (currentChildMapping.hasOwnProperty(key)) {
// This entered again before it fully left. Add it again.
this.performEnter(key);
} else {
var newChildren = merge(this.state.children);
delete newChildren[key];
this.setState({children: newChildren});
}
},
render: function() {
// TODO: we could get rid of the need for the wrapper node
// by cloning a single child
var childrenToRender = {};
for (var key in this.state.children) {
var child = this.state.children[key];
if (child) {
// You may need to apply reactive updates to a child as it is leaving.
// The normal React way to do it won't work since the child will have
// already been removed. In case you need this behavior you can provide
// a childFactory function to wrap every child, even the ones that are
// leaving.
childrenToRender[key] = cloneWithProps(
this.props.childFactory(child),
{ref: key}
);
}
}
return this.transferPropsTo(this.props.component(null, childrenToRender));
}
});
module.exports = ReactTransitionGroup;
},{"./React":26,"./ReactTransitionChildMapping":77,"./cloneWithProps":99,"./emptyFunction":108,"./merge":130}],80:[function(require,module,exports){
/**
* 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 ReactUpdates
*/
"use strict";
var ReactPerf = require("./ReactPerf");
var invariant = require("./invariant");
var dirtyComponents = [];
var batchingStrategy = null;
function ensureBatchingStrategy() {
("production" !== "development" ? invariant(batchingStrategy, 'ReactUpdates: must inject a batching strategy') : invariant(batchingStrategy));
}
function batchedUpdates(callback, param) {
ensureBatchingStrategy();
batchingStrategy.batchedUpdates(callback, param);
}
/**
* Array comparator for ReactComponents by owner depth
*
* @param {ReactComponent} c1 first component you're comparing
* @param {ReactComponent} c2 second component you're comparing
* @return {number} Return value usable by Array.prototype.sort().
*/
function mountDepthComparator(c1, c2) {
return c1._mountDepth - c2._mountDepth;
}
function runBatchedUpdates() {
// Since reconciling a component higher in the owner hierarchy usually (not
// always -- see shouldComponentUpdate()) will reconcile children, reconcile
// them before their children by sorting the array.
dirtyComponents.sort(mountDepthComparator);
for (var i = 0; i < dirtyComponents.length; i++) {
// If a component is unmounted before pending changes apply, ignore them
// TODO: Queue unmounts in the same list to avoid this happening at all
var component = dirtyComponents[i];
if (component.isMounted()) {
// If performUpdateIfNecessary happens to enqueue any new updates, we
// shouldn't execute the callbacks until the next render happens, so
// stash the callbacks first
var callbacks = component._pendingCallbacks;
component._pendingCallbacks = null;
component.performUpdateIfNecessary();
if (callbacks) {
for (var j = 0; j < callbacks.length; j++) {
callbacks[j].call(component);
}
}
}
}
}
function clearDirtyComponents() {
dirtyComponents.length = 0;
}
var flushBatchedUpdates = ReactPerf.measure(
'ReactUpdates',
'flushBatchedUpdates',
function() {
// Run these in separate functions so the JIT can optimize
try {
runBatchedUpdates();
} finally {
clearDirtyComponents();
}
}
);
/**
* Mark a component as needing a rerender, adding an optional callback to a
* list of functions which will be executed once the rerender occurs.
*/
function enqueueUpdate(component, callback) {
("production" !== "development" ? invariant(
!callback || typeof callback === "function",
'enqueueUpdate(...): You called `setProps`, `replaceProps`, ' +
'`setState`, `replaceState`, or `forceUpdate` with a callback that ' +
'isn\'t callable.'
) : invariant(!callback || typeof callback === "function"));
ensureBatchingStrategy();
if (!batchingStrategy.isBatchingUpdates) {
component.performUpdateIfNecessary();
callback && callback.call(component);
return;
}
dirtyComponents.push(component);
if (callback) {
if (component._pendingCallbacks) {
component._pendingCallbacks.push(callback);
} else {
component._pendingCallbacks = [callback];
}
}
}
var ReactUpdatesInjection = {
injectBatchingStrategy: function(_batchingStrategy) {
("production" !== "development" ? invariant(
_batchingStrategy,
'ReactUpdates: must provide a batching strategy'
) : invariant(_batchingStrategy));
("production" !== "development" ? invariant(
typeof _batchingStrategy.batchedUpdates === 'function',
'ReactUpdates: must provide a batchedUpdates() function'
) : invariant(typeof _batchingStrategy.batchedUpdates === 'function'));
("production" !== "development" ? invariant(
typeof _batchingStrategy.isBatchingUpdates === 'boolean',
'ReactUpdates: must provide an isBatchingUpdates boolean attribute'
) : invariant(typeof _batchingStrategy.isBatchingUpdates === 'boolean'));
batchingStrategy = _batchingStrategy;
}
};
var ReactUpdates = {
batchedUpdates: batchedUpdates,
enqueueUpdate: enqueueUpdate,
flushBatchedUpdates: flushBatchedUpdates,
injection: ReactUpdatesInjection
};
module.exports = ReactUpdates;
},{"./ReactPerf":65,"./invariant":121}],81:[function(require,module,exports){
/**
* 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 ReactWithAddons
*/
/**
* This module exists purely in the open source project, and is meant as a way
* to create a separate standalone build of React. This build has "addons", or
* functionality we've built and think might be useful but doesn't have a good
* place to live inside React core.
*/
"use strict";
var LinkedStateMixin = require("./LinkedStateMixin");
var React = require("./React");
var ReactCSSTransitionGroup = require("./ReactCSSTransitionGroup");
var ReactTransitionGroup = require("./ReactTransitionGroup");
var ReactCSSTransitionGroup = require("./ReactCSSTransitionGroup");
var cx = require("./cx");
var cloneWithProps = require("./cloneWithProps");
React.addons = {
LinkedStateMixin: LinkedStateMixin,
CSSTransitionGroup: ReactCSSTransitionGroup,
TransitionGroup: ReactTransitionGroup,
classSet: cx,
cloneWithProps: cloneWithProps
};
if ("production" !== "development") {
React.addons.TestUtils = require("./ReactTestUtils");
}
module.exports = React;
},{"./LinkedStateMixin":22,"./React":26,"./ReactCSSTransitionGroup":27,"./ReactTestUtils":75,"./ReactTransitionGroup":79,"./cloneWithProps":99,"./cx":106}],82:[function(require,module,exports){
/**
* 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 SelectEventPlugin
*/
"use strict";
var EventConstants = require("./EventConstants");
var EventPropagators = require("./EventPropagators");
var ReactInputSelection = require("./ReactInputSelection");
var SyntheticEvent = require("./SyntheticEvent");
var getActiveElement = require("./getActiveElement");
var isTextInputElement = require("./isTextInputElement");
var keyOf = require("./keyOf");
var shallowEqual = require("./shallowEqual");
var topLevelTypes = EventConstants.topLevelTypes;
var eventTypes = {
select: {
phasedRegistrationNames: {
bubbled: keyOf({onSelect: null}),
captured: keyOf({onSelectCapture: null})
},
dependencies: [
topLevelTypes.topBlur,
topLevelTypes.topContextMenu,
topLevelTypes.topFocus,
topLevelTypes.topKeyDown,
topLevelTypes.topMouseDown,
topLevelTypes.topMouseUp,
topLevelTypes.topSelectionChange
]
}
};
var activeElement = null;
var activeElementID = null;
var lastSelection = null;
var mouseDown = false;
/**
* Get an object which is a unique representation of the current selection.
*
* The return value will not be consistent across nodes or browsers, but
* two identical selections on the same node will return identical objects.
*
* @param {DOMElement} node
* @param {object}
*/
function getSelection(node) {
if ('selectionStart' in node &&
ReactInputSelection.hasSelectionCapabilities(node)) {
return {
start: node.selectionStart,
end: node.selectionEnd
};
} else if (document.selection) {
var range = document.selection.createRange();
return {
parentElement: range.parentElement(),
text: range.text,
top: range.boundingTop,
left: range.boundingLeft
};
} else {
var selection = window.getSelection();
return {
anchorNode: selection.anchorNode,
anchorOffset: selection.anchorOffset,
focusNode: selection.focusNode,
focusOffset: selection.focusOffset
};
}
}
/**
* Poll selection to see whether it's changed.
*
* @param {object} nativeEvent
* @return {?SyntheticEvent}
*/
function constructSelectEvent(nativeEvent) {
// Ensure we have the right element, and that the user is not dragging a
// selection (this matches native `select` event behavior). In HTML5, select
// fires only on input and textarea thus if there's no focused element we
// won't dispatch.
if (mouseDown ||
activeElement == null ||
activeElement != getActiveElement()) {
return;
}
// Only fire when selection has actually changed.
var currentSelection = getSelection(activeElement);
if (!lastSelection || !shallowEqual(lastSelection, currentSelection)) {
lastSelection = currentSelection;
var syntheticEvent = SyntheticEvent.getPooled(
eventTypes.select,
activeElementID,
nativeEvent
);
syntheticEvent.type = 'select';
syntheticEvent.target = activeElement;
EventPropagators.accumulateTwoPhaseDispatches(syntheticEvent);
return syntheticEvent;
}
}
/**
* This plugin creates an `onSelect` event that normalizes select events
* across form elements.
*
* Supported elements are:
* - input (see `isTextInputElement`)
* - textarea
* - contentEditable
*
* This differs from native browser implementations in the following ways:
* - Fires on contentEditable fields as well as inputs.
* - Fires for collapsed selection.
* - Fires after user input.
*/
var SelectEventPlugin = {
eventTypes: eventTypes,
/**
* @param {string} topLevelType Record from `EventConstants`.
* @param {DOMEventTarget} topLevelTarget The listening component root node.
* @param {string} topLevelTargetID ID of `topLevelTarget`.
* @param {object} nativeEvent Native browser event.
* @return {*} An accumulation of synthetic events.
* @see {EventPluginHub.extractEvents}
*/
extractEvents: function(
topLevelType,
topLevelTarget,
topLevelTargetID,
nativeEvent) {
switch (topLevelType) {
// Track the input node that has focus.
case topLevelTypes.topFocus:
if (isTextInputElement(topLevelTarget) ||
topLevelTarget.contentEditable === 'true') {
activeElement = topLevelTarget;
activeElementID = topLevelTargetID;
lastSelection = null;
}
break;
case topLevelTypes.topBlur:
activeElement = null;
activeElementID = null;
lastSelection = null;
break;
// Don't fire the event while the user is dragging. This matches the
// semantics of the native select event.
case topLevelTypes.topMouseDown:
mouseDown = true;
break;
case topLevelTypes.topContextMenu:
case topLevelTypes.topMouseUp:
mouseDown = false;
return constructSelectEvent(nativeEvent);
// Chrome and IE fire non-standard event when selection is changed (and
// sometimes when it hasn't).
// Firefox doesn't support selectionchange, so check selection status
// after each key entry. The selection changes after keydown and before
// keyup, but we check on keydown as well in the case of holding down a
// key, when multiple keydown events are fired but only one keyup is.
case topLevelTypes.topSelectionChange:
case topLevelTypes.topKeyDown:
case topLevelTypes.topKeyUp:
return constructSelectEvent(nativeEvent);
}
}
};
module.exports = SelectEventPlugin;
},{"./EventConstants":15,"./EventPropagators":20,"./ReactInputSelection":56,"./SyntheticEvent":88,"./getActiveElement":112,"./isTextInputElement":124,"./keyOf":128,"./shallowEqual":138}],83:[function(require,module,exports){
/**
* 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 ServerReactRootIndex
* @typechecks
*/
"use strict";
/**
* Size of the reactRoot ID space. We generate random numbers for React root
* IDs and if there's a collision the events and DOM update system will
* get confused. In the future we need a way to generate GUIDs but for
* now this will work on a smaller scale.
*/
var GLOBAL_MOUNT_POINT_MAX = Math.pow(2, 53);
var ServerReactRootIndex = {
createReactRootIndex: function() {
return Math.ceil(Math.random() * GLOBAL_MOUNT_POINT_MAX);
}
};
module.exports = ServerReactRootIndex;
},{}],84:[function(require,module,exports){
/**
* 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 SimpleEventPlugin
*/
"use strict";
var EventConstants = require("./EventConstants");
var EventPluginUtils = require("./EventPluginUtils");
var EventPropagators = require("./EventPropagators");
var SyntheticClipboardEvent = require("./SyntheticClipboardEvent");
var SyntheticEvent = require("./SyntheticEvent");
var SyntheticFocusEvent = require("./SyntheticFocusEvent");
var SyntheticKeyboardEvent = require("./SyntheticKeyboardEvent");
var SyntheticMouseEvent = require("./SyntheticMouseEvent");
var SyntheticDragEvent = require("./SyntheticDragEvent");
var SyntheticTouchEvent = require("./SyntheticTouchEvent");
var SyntheticUIEvent = require("./SyntheticUIEvent");
var SyntheticWheelEvent = require("./SyntheticWheelEvent");
var invariant = require("./invariant");
var keyOf = require("./keyOf");
var topLevelTypes = EventConstants.topLevelTypes;
var eventTypes = {
blur: {
phasedRegistrationNames: {
bubbled: keyOf({onBlur: true}),
captured: keyOf({onBlurCapture: true})
}
},
click: {
phasedRegistrationNames: {
bubbled: keyOf({onClick: true}),
captured: keyOf({onClickCapture: true})
}
},
contextMenu: {
phasedRegistrationNames: {
bubbled: keyOf({onContextMenu: true}),
captured: keyOf({onContextMenuCapture: true})
}
},
copy: {
phasedRegistrationNames: {
bubbled: keyOf({onCopy: true}),
captured: keyOf({onCopyCapture: true})
}
},
cut: {
phasedRegistrationNames: {
bubbled: keyOf({onCut: true}),
captured: keyOf({onCutCapture: true})
}
},
doubleClick: {
phasedRegistrationNames: {
bubbled: keyOf({onDoubleClick: true}),
captured: keyOf({onDoubleClickCapture: true})
}
},
drag: {
phasedRegistrationNames: {
bubbled: keyOf({onDrag: true}),
captured: keyOf({onDragCapture: true})
}
},
dragEnd: {
phasedRegistrationNames: {
bubbled: keyOf({onDragEnd: true}),
captured: keyOf({onDragEndCapture: true})
}
},
dragEnter: {
phasedRegistrationNames: {
bubbled: keyOf({onDragEnter: true}),
captured: keyOf({onDragEnterCapture: true})
}
},
dragExit: {
phasedRegistrationNames: {
bubbled: keyOf({onDragExit: true}),
captured: keyOf({onDragExitCapture: true})
}
},
dragLeave: {
phasedRegistrationNames: {
bubbled: keyOf({onDragLeave: true}),
captured: keyOf({onDragLeaveCapture: true})
}
},
dragOver: {
phasedRegistrationNames: {
bubbled: keyOf({onDragOver: true}),
captured: keyOf({onDragOverCapture: true})
}
},
dragStart: {
phasedRegistrationNames: {
bubbled: keyOf({onDragStart: true}),
captured: keyOf({onDragStartCapture: true})
}
},
drop: {
phasedRegistrationNames: {
bubbled: keyOf({onDrop: true}),
captured: keyOf({onDropCapture: true})
}
},
focus: {
phasedRegistrationNames: {
bubbled: keyOf({onFocus: true}),
captured: keyOf({onFocusCapture: true})
}
},
input: {
phasedRegistrationNames: {
bubbled: keyOf({onInput: true}),
captured: keyOf({onInputCapture: true})
}
},
keyDown: {
phasedRegistrationNames: {
bubbled: keyOf({onKeyDown: true}),
captured: keyOf({onKeyDownCapture: true})
}
},
keyPress: {
phasedRegistrationNames: {
bubbled: keyOf({onKeyPress: true}),
captured: keyOf({onKeyPressCapture: true})
}
},
keyUp: {
phasedRegistrationNames: {
bubbled: keyOf({onKeyUp: true}),
captured: keyOf({onKeyUpCapture: true})
}
},
load: {
phasedRegistrationNames: {
bubbled: keyOf({onLoad: true}),
captured: keyOf({onLoadCapture: true})
}
},
error: {
phasedRegistrationNames: {
bubbled: keyOf({onError: true}),
captured: keyOf({onErrorCapture: true})
}
},
// Note: We do not allow listening to mouseOver events. Instead, use the
// onMouseEnter/onMouseLeave created by `EnterLeaveEventPlugin`.
mouseDown: {
phasedRegistrationNames: {
bubbled: keyOf({onMouseDown: true}),
captured: keyOf({onMouseDownCapture: true})
}
},
mouseMove: {
phasedRegistrationNames: {
bubbled: keyOf({onMouseMove: true}),
captured: keyOf({onMouseMoveCapture: true})
}
},
mouseOut: {
phasedRegistrationNames: {
bubbled: keyOf({onMouseOut: true}),
captured: keyOf({onMouseOutCapture: true})
}
},
mouseOver: {
phasedRegistrationNames: {
bubbled: keyOf({onMouseOver: true}),
captured: keyOf({onMouseOverCapture: true})
}
},
mouseUp: {
phasedRegistrationNames: {
bubbled: keyOf({onMouseUp: true}),
captured: keyOf({onMouseUpCapture: true})
}
},
paste: {
phasedRegistrationNames: {
bubbled: keyOf({onPaste: true}),
captured: keyOf({onPasteCapture: true})
}
},
reset: {
phasedRegistrationNames: {
bubbled: keyOf({onReset: true}),
captured: keyOf({onResetCapture: true})
}
},
scroll: {
phasedRegistrationNames: {
bubbled: keyOf({onScroll: true}),
captured: keyOf({onScrollCapture: true})
}
},
submit: {
phasedRegistrationNames: {
bubbled: keyOf({onSubmit: true}),
captured: keyOf({onSubmitCapture: true})
}
},
touchCancel: {
phasedRegistrationNames: {
bubbled: keyOf({onTouchCancel: true}),
captured: keyOf({onTouchCancelCapture: true})
}
},
touchEnd: {
phasedRegistrationNames: {
bubbled: keyOf({onTouchEnd: true}),
captured: keyOf({onTouchEndCapture: true})
}
},
touchMove: {
phasedRegistrationNames: {
bubbled: keyOf({onTouchMove: true}),
captured: keyOf({onTouchMoveCapture: true})
}
},
touchStart: {
phasedRegistrationNames: {
bubbled: keyOf({onTouchStart: true}),
captured: keyOf({onTouchStartCapture: true})
}
},
wheel: {
phasedRegistrationNames: {
bubbled: keyOf({onWheel: true}),
captured: keyOf({onWheelCapture: true})
}
}
};
var topLevelEventsToDispatchConfig = {
topBlur: eventTypes.blur,
topClick: eventTypes.click,
topContextMenu: eventTypes.contextMenu,
topCopy: eventTypes.copy,
topCut: eventTypes.cut,
topDoubleClick: eventTypes.doubleClick,
topDrag: eventTypes.drag,
topDragEnd: eventTypes.dragEnd,
topDragEnter: eventTypes.dragEnter,
topDragExit: eventTypes.dragExit,
topDragLeave: eventTypes.dragLeave,
topDragOver: eventTypes.dragOver,
topDragStart: eventTypes.dragStart,
topDrop: eventTypes.drop,
topError: eventTypes.error,
topFocus: eventTypes.focus,
topInput: eventTypes.input,
topKeyDown: eventTypes.keyDown,
topKeyPress: eventTypes.keyPress,
topKeyUp: eventTypes.keyUp,
topLoad: eventTypes.load,
topMouseDown: eventTypes.mouseDown,
topMouseMove: eventTypes.mouseMove,
topMouseOut: eventTypes.mouseOut,
topMouseOver: eventTypes.mouseOver,
topMouseUp: eventTypes.mouseUp,
topPaste: eventTypes.paste,
topReset: eventTypes.reset,
topScroll: eventTypes.scroll,
topSubmit: eventTypes.submit,
topTouchCancel: eventTypes.touchCancel,
topTouchEnd: eventTypes.touchEnd,
topTouchMove: eventTypes.touchMove,
topTouchStart: eventTypes.touchStart,
topWheel: eventTypes.wheel
};
for (var topLevelType in topLevelEventsToDispatchConfig) {
topLevelEventsToDispatchConfig[topLevelType].dependencies = [topLevelType];
}
var SimpleEventPlugin = {
eventTypes: eventTypes,
/**
* Same as the default implementation, except cancels the event when return
* value is false.
*
* @param {object} Event to be dispatched.
* @param {function} Application-level callback.
* @param {string} domID DOM ID to pass to the callback.
*/
executeDispatch: function(event, listener, domID) {
var returnValue = EventPluginUtils.executeDispatch(event, listener, domID);
if (returnValue === false) {
event.stopPropagation();
event.preventDefault();
}
},
/**
* @param {string} topLevelType Record from `EventConstants`.
* @param {DOMEventTarget} topLevelTarget The listening component root node.
* @param {string} topLevelTargetID ID of `topLevelTarget`.
* @param {object} nativeEvent Native browser event.
* @return {*} An accumulation of synthetic events.
* @see {EventPluginHub.extractEvents}
*/
extractEvents: function(
topLevelType,
topLevelTarget,
topLevelTargetID,
nativeEvent) {
var dispatchConfig = topLevelEventsToDispatchConfig[topLevelType];
if (!dispatchConfig) {
return null;
}
var EventConstructor;
switch (topLevelType) {
case topLevelTypes.topInput:
case topLevelTypes.topLoad:
case topLevelTypes.topError:
case topLevelTypes.topReset:
case topLevelTypes.topSubmit:
// HTML Events
// @see http://www.w3.org/TR/html5/index.html#events-0
EventConstructor = SyntheticEvent;
break;
case topLevelTypes.topKeyDown:
case topLevelTypes.topKeyPress:
case topLevelTypes.topKeyUp:
EventConstructor = SyntheticKeyboardEvent;
break;
case topLevelTypes.topBlur:
case topLevelTypes.topFocus:
EventConstructor = SyntheticFocusEvent;
break;
case topLevelTypes.topClick:
// Firefox creates a click event on right mouse clicks. This removes the
// unwanted click events.
if (nativeEvent.button === 2) {
return null;
}
/* falls through */
case topLevelTypes.topContextMenu:
case topLevelTypes.topDoubleClick:
case topLevelTypes.topMouseDown:
case topLevelTypes.topMouseMove:
case topLevelTypes.topMouseOut:
case topLevelTypes.topMouseOver:
case topLevelTypes.topMouseUp:
EventConstructor = SyntheticMouseEvent;
break;
case topLevelTypes.topDrag:
case topLevelTypes.topDragEnd:
case topLevelTypes.topDragEnter:
case topLevelTypes.topDragExit:
case topLevelTypes.topDragLeave:
case topLevelTypes.topDragOver:
case topLevelTypes.topDragStart:
case topLevelTypes.topDrop:
EventConstructor = SyntheticDragEvent;
break;
case topLevelTypes.topTouchCancel:
case topLevelTypes.topTouchEnd:
case topLevelTypes.topTouchMove:
case topLevelTypes.topTouchStart:
EventConstructor = SyntheticTouchEvent;
break;
case topLevelTypes.topScroll:
EventConstructor = SyntheticUIEvent;
break;
case topLevelTypes.topWheel:
EventConstructor = SyntheticWheelEvent;
break;
case topLevelTypes.topCopy:
case topLevelTypes.topCut:
case topLevelTypes.topPaste:
EventConstructor = SyntheticClipboardEvent;
break;
}
("production" !== "development" ? invariant(
EventConstructor,
'SimpleEventPlugin: Unhandled event type, `%s`.',
topLevelType
) : invariant(EventConstructor));
var event = EventConstructor.getPooled(
dispatchConfig,
topLevelTargetID,
nativeEvent
);
EventPropagators.accumulateTwoPhaseDispatches(event);
return event;
}
};
module.exports = SimpleEventPlugin;
},{"./EventConstants":15,"./EventPluginUtils":19,"./EventPropagators":20,"./SyntheticClipboardEvent":85,"./SyntheticDragEvent":87,"./SyntheticEvent":88,"./SyntheticFocusEvent":89,"./SyntheticKeyboardEvent":90,"./SyntheticMouseEvent":91,"./SyntheticTouchEvent":92,"./SyntheticUIEvent":93,"./SyntheticWheelEvent":94,"./invariant":121,"./keyOf":128}],85:[function(require,module,exports){
/**
* 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 SyntheticClipboardEvent
* @typechecks static-only
*/
"use strict";
var SyntheticEvent = require("./SyntheticEvent");
/**
* @interface Event
* @see http://www.w3.org/TR/clipboard-apis/
*/
var ClipboardEventInterface = {
clipboardData: function(event) {
return (
'clipboardData' in event ?
event.clipboardData :
window.clipboardData
);
}
};
/**
* @param {object} dispatchConfig Configuration used to dispatch this event.
* @param {string} dispatchMarker Marker identifying the event target.
* @param {object} nativeEvent Native browser event.
* @extends {SyntheticUIEvent}
*/
function SyntheticClipboardEvent(dispatchConfig, dispatchMarker, nativeEvent) {
SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent);
}
SyntheticEvent.augmentClass(SyntheticClipboardEvent, ClipboardEventInterface);
module.exports = SyntheticClipboardEvent;
},{"./SyntheticEvent":88}],86:[function(require,module,exports){
/**
* 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 SyntheticCompositionEvent
* @typechecks static-only
*/
"use strict";
var SyntheticEvent = require("./SyntheticEvent");
/**
* @interface Event
* @see http://www.w3.org/TR/DOM-Level-3-Events/#events-compositionevents
*/
var CompositionEventInterface = {
data: null
};
/**
* @param {object} dispatchConfig Configuration used to dispatch this event.
* @param {string} dispatchMarker Marker identifying the event target.
* @param {object} nativeEvent Native browser event.
* @extends {SyntheticUIEvent}
*/
function SyntheticCompositionEvent(
dispatchConfig,
dispatchMarker,
nativeEvent) {
SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent);
}
SyntheticEvent.augmentClass(
SyntheticCompositionEvent,
CompositionEventInterface
);
module.exports = SyntheticCompositionEvent;
},{"./SyntheticEvent":88}],87:[function(require,module,exports){
/**
* 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 SyntheticDragEvent
* @typechecks static-only
*/
"use strict";
var SyntheticMouseEvent = require("./SyntheticMouseEvent");
/**
* @interface DragEvent
* @see http://www.w3.org/TR/DOM-Level-3-Events/
*/
var DragEventInterface = {
dataTransfer: null
};
/**
* @param {object} dispatchConfig Configuration used to dispatch this event.
* @param {string} dispatchMarker Marker identifying the event target.
* @param {object} nativeEvent Native browser event.
* @extends {SyntheticUIEvent}
*/
function SyntheticDragEvent(dispatchConfig, dispatchMarker, nativeEvent) {
SyntheticMouseEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent);
}
SyntheticMouseEvent.augmentClass(SyntheticDragEvent, DragEventInterface);
module.exports = SyntheticDragEvent;
},{"./SyntheticMouseEvent":91}],88:[function(require,module,exports){
/**
* 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 SyntheticEvent
* @typechecks static-only
*/
"use strict";
var PooledClass = require("./PooledClass");
var emptyFunction = require("./emptyFunction");
var getEventTarget = require("./getEventTarget");
var merge = require("./merge");
var mergeInto = require("./mergeInto");
/**
* @interface Event
* @see http://www.w3.org/TR/DOM-Level-3-Events/
*/
var EventInterface = {
type: null,
target: getEventTarget,
// currentTarget is set when dispatching; no use in copying it here
currentTarget: emptyFunction.thatReturnsNull,
eventPhase: null,
bubbles: null,
cancelable: null,
timeStamp: function(event) {
return event.timeStamp || Date.now();
},
defaultPrevented: null,
isTrusted: null
};
/**
* Synthetic events are dispatched by event plugins, typically in response to a
* top-level event delegation handler.
*
* These systems should generally use pooling to reduce the frequency of garbage
* collection. The system should check `isPersistent` to determine whether the
* event should be released into the pool after being dispatched. Users that
* need a persisted event should invoke `persist`.
*
* Synthetic events (and subclasses) implement the DOM Level 3 Events API by
* normalizing browser quirks. Subclasses do not necessarily have to implement a
* DOM interface; custom application-specific events can also subclass this.
*
* @param {object} dispatchConfig Configuration used to dispatch this event.
* @param {string} dispatchMarker Marker identifying the event target.
* @param {object} nativeEvent Native browser event.
*/
function SyntheticEvent(dispatchConfig, dispatchMarker, nativeEvent) {
this.dispatchConfig = dispatchConfig;
this.dispatchMarker = dispatchMarker;
this.nativeEvent = nativeEvent;
var Interface = this.constructor.Interface;
for (var propName in Interface) {
if (!Interface.hasOwnProperty(propName)) {
continue;
}
var normalize = Interface[propName];
if (normalize) {
this[propName] = normalize(nativeEvent);
} else {
this[propName] = nativeEvent[propName];
}
}
var defaultPrevented = nativeEvent.defaultPrevented != null ?
nativeEvent.defaultPrevented :
nativeEvent.returnValue === false;
if (defaultPrevented) {
this.isDefaultPrevented = emptyFunction.thatReturnsTrue;
} else {
this.isDefaultPrevented = emptyFunction.thatReturnsFalse;
}
this.isPropagationStopped = emptyFunction.thatReturnsFalse;
}
mergeInto(SyntheticEvent.prototype, {
preventDefault: function() {
this.defaultPrevented = true;
var event = this.nativeEvent;
event.preventDefault ? event.preventDefault() : event.returnValue = false;
this.isDefaultPrevented = emptyFunction.thatReturnsTrue;
},
stopPropagation: function() {
var event = this.nativeEvent;
event.stopPropagation ? event.stopPropagation() : event.cancelBubble = true;
this.isPropagationStopped = emptyFunction.thatReturnsTrue;
},
/**
* We release all dispatched `SyntheticEvent`s after each event loop, adding
* them back into the pool. This allows a way to hold onto a reference that
* won't be added back into the pool.
*/
persist: function() {
this.isPersistent = emptyFunction.thatReturnsTrue;
},
/**
* Checks if this event should be released back into the pool.
*
* @return {boolean} True if this should not be released, false otherwise.
*/
isPersistent: emptyFunction.thatReturnsFalse,
/**
* `PooledClass` looks for `destructor` on each instance it releases.
*/
destructor: function() {
var Interface = this.constructor.Interface;
for (var propName in Interface) {
this[propName] = null;
}
this.dispatchConfig = null;
this.dispatchMarker = null;
this.nativeEvent = null;
}
});
SyntheticEvent.Interface = EventInterface;
/**
* Helper to reduce boilerplate when creating subclasses.
*
* @param {function} Class
* @param {?object} Interface
*/
SyntheticEvent.augmentClass = function(Class, Interface) {
var Super = this;
var prototype = Object.create(Super.prototype);
mergeInto(prototype, Class.prototype);
Class.prototype = prototype;
Class.prototype.constructor = Class;
Class.Interface = merge(Super.Interface, Interface);
Class.augmentClass = Super.augmentClass;
PooledClass.addPoolingTo(Class, PooledClass.threeArgumentPooler);
};
PooledClass.addPoolingTo(SyntheticEvent, PooledClass.threeArgumentPooler);
module.exports = SyntheticEvent;
},{"./PooledClass":25,"./emptyFunction":108,"./getEventTarget":114,"./merge":130,"./mergeInto":132}],89:[function(require,module,exports){
/**
* 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 SyntheticFocusEvent
* @typechecks static-only
*/
"use strict";
var SyntheticUIEvent = require("./SyntheticUIEvent");
/**
* @interface FocusEvent
* @see http://www.w3.org/TR/DOM-Level-3-Events/
*/
var FocusEventInterface = {
relatedTarget: null
};
/**
* @param {object} dispatchConfig Configuration used to dispatch this event.
* @param {string} dispatchMarker Marker identifying the event target.
* @param {object} nativeEvent Native browser event.
* @extends {SyntheticUIEvent}
*/
function SyntheticFocusEvent(dispatchConfig, dispatchMarker, nativeEvent) {
SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent);
}
SyntheticUIEvent.augmentClass(SyntheticFocusEvent, FocusEventInterface);
module.exports = SyntheticFocusEvent;
},{"./SyntheticUIEvent":93}],90:[function(require,module,exports){
/**
* 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 SyntheticKeyboardEvent
* @typechecks static-only
*/
"use strict";
var SyntheticUIEvent = require("./SyntheticUIEvent");
var getEventKey = require("./getEventKey");
/**
* @interface KeyboardEvent
* @see http://www.w3.org/TR/DOM-Level-3-Events/
*/
var KeyboardEventInterface = {
key: getEventKey,
location: null,
ctrlKey: null,
shiftKey: null,
altKey: null,
metaKey: null,
repeat: null,
locale: null,
// Legacy Interface
'char': null,
charCode: null,
keyCode: null,
which: null
};
/**
* @param {object} dispatchConfig Configuration used to dispatch this event.
* @param {string} dispatchMarker Marker identifying the event target.
* @param {object} nativeEvent Native browser event.
* @extends {SyntheticUIEvent}
*/
function SyntheticKeyboardEvent(dispatchConfig, dispatchMarker, nativeEvent) {
SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent);
}
SyntheticUIEvent.augmentClass(SyntheticKeyboardEvent, KeyboardEventInterface);
module.exports = SyntheticKeyboardEvent;
},{"./SyntheticUIEvent":93,"./getEventKey":113}],91:[function(require,module,exports){
/**
* 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 SyntheticMouseEvent
* @typechecks static-only
*/
"use strict";
var SyntheticUIEvent = require("./SyntheticUIEvent");
var ViewportMetrics = require("./ViewportMetrics");
/**
* @interface MouseEvent
* @see http://www.w3.org/TR/DOM-Level-3-Events/
*/
var MouseEventInterface = {
screenX: null,
screenY: null,
clientX: null,
clientY: null,
ctrlKey: null,
shiftKey: null,
altKey: null,
metaKey: null,
button: function(event) {
// Webkit, Firefox, IE9+
// which: 1 2 3
// button: 0 1 2 (standard)
var button = event.button;
if ('which' in event) {
return button;
}
// IE<9
// which: undefined
// button: 0 0 0
// button: 1 4 2 (onmouseup)
return button === 2 ? 2 : button === 4 ? 1 : 0;
},
buttons: null,
relatedTarget: function(event) {
return event.relatedTarget || (
event.fromElement === event.srcElement ?
event.toElement :
event.fromElement
);
},
// "Proprietary" Interface.
pageX: function(event) {
return 'pageX' in event ?
event.pageX :
event.clientX + ViewportMetrics.currentScrollLeft;
},
pageY: function(event) {
return 'pageY' in event ?
event.pageY :
event.clientY + ViewportMetrics.currentScrollTop;
}
};
/**
* @param {object} dispatchConfig Configuration used to dispatch this event.
* @param {string} dispatchMarker Marker identifying the event target.
* @param {object} nativeEvent Native browser event.
* @extends {SyntheticUIEvent}
*/
function SyntheticMouseEvent(dispatchConfig, dispatchMarker, nativeEvent) {
SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent);
}
SyntheticUIEvent.augmentClass(SyntheticMouseEvent, MouseEventInterface);
module.exports = SyntheticMouseEvent;
},{"./SyntheticUIEvent":93,"./ViewportMetrics":96}],92:[function(require,module,exports){
/**
* 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 SyntheticTouchEvent
* @typechecks static-only
*/
"use strict";
var SyntheticUIEvent = require("./SyntheticUIEvent");
/**
* @interface TouchEvent
* @see http://www.w3.org/TR/touch-events/
*/
var TouchEventInterface = {
touches: null,
targetTouches: null,
changedTouches: null,
altKey: null,
metaKey: null,
ctrlKey: null,
shiftKey: null
};
/**
* @param {object} dispatchConfig Configuration used to dispatch this event.
* @param {string} dispatchMarker Marker identifying the event target.
* @param {object} nativeEvent Native browser event.
* @extends {SyntheticUIEvent}
*/
function SyntheticTouchEvent(dispatchConfig, dispatchMarker, nativeEvent) {
SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent);
}
SyntheticUIEvent.augmentClass(SyntheticTouchEvent, TouchEventInterface);
module.exports = SyntheticTouchEvent;
},{"./SyntheticUIEvent":93}],93:[function(require,module,exports){
/**
* 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 SyntheticUIEvent
* @typechecks static-only
*/
"use strict";
var SyntheticEvent = require("./SyntheticEvent");
/**
* @interface UIEvent
* @see http://www.w3.org/TR/DOM-Level-3-Events/
*/
var UIEventInterface = {
view: null,
detail: null
};
/**
* @param {object} dispatchConfig Configuration used to dispatch this event.
* @param {string} dispatchMarker Marker identifying the event target.
* @param {object} nativeEvent Native browser event.
* @extends {SyntheticEvent}
*/
function SyntheticUIEvent(dispatchConfig, dispatchMarker, nativeEvent) {
SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent);
}
SyntheticEvent.augmentClass(SyntheticUIEvent, UIEventInterface);
module.exports = SyntheticUIEvent;
},{"./SyntheticEvent":88}],94:[function(require,module,exports){
/**
* 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 SyntheticWheelEvent
* @typechecks static-only
*/
"use strict";
var SyntheticMouseEvent = require("./SyntheticMouseEvent");
/**
* @interface WheelEvent
* @see http://www.w3.org/TR/DOM-Level-3-Events/
*/
var WheelEventInterface = {
deltaX: function(event) {
return (
'deltaX' in event ? event.deltaX :
// Fallback to `wheelDeltaX` for Webkit and normalize (right is positive).
'wheelDeltaX' in event ? -event.wheelDeltaX : 0
);
},
deltaY: function(event) {
return (
'deltaY' in event ? event.deltaY :
// Fallback to `wheelDeltaY` for Webkit and normalize (down is positive).
'wheelDeltaY' in event ? -event.wheelDeltaY :
// Fallback to `wheelDelta` for IE<9 and normalize (down is positive).
'wheelDelta' in event ? -event.wheelDelta : 0
);
},
deltaZ: null,
// Browsers without "deltaMode" is reporting in raw wheel delta where one
// notch on the scroll is always +/- 120, roughly equivalent to pixels.
// A good approximation of DOM_DELTA_LINE (1) is 5% of viewport size or
// ~40 pixels, for DOM_DELTA_SCREEN (2) it is 87.5% of viewport size.
deltaMode: null
};
/**
* @param {object} dispatchConfig Configuration used to dispatch this event.
* @param {string} dispatchMarker Marker identifying the event target.
* @param {object} nativeEvent Native browser event.
* @extends {SyntheticMouseEvent}
*/
function SyntheticWheelEvent(dispatchConfig, dispatchMarker, nativeEvent) {
SyntheticMouseEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent);
}
SyntheticMouseEvent.augmentClass(SyntheticWheelEvent, WheelEventInterface);
module.exports = SyntheticWheelEvent;
},{"./SyntheticMouseEvent":91}],95:[function(require,module,exports){
/**
* 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 Transaction
*/
"use strict";
var invariant = require("./invariant");
/**
* `Transaction` creates a black box that is able to wrap any method such that
* certain invariants are maintained before and after the method is invoked
* (Even if an exception is thrown while invoking the wrapped method). Whoever
* instantiates a transaction can provide enforcers of the invariants at
* creation time. The `Transaction` class itself will supply one additional
* automatic invariant for you - the invariant that any transaction instance
* should not be run while it is already being run. You would typically create a
* single instance of a `Transaction` for reuse multiple times, that potentially
* is used to wrap several different methods. Wrappers are extremely simple -
* they only require implementing two methods.
*
* <pre>
* wrappers (injected at creation time)
* + +
* | |
* +-----------------|--------|--------------+
* | v | |
* | +---------------+ | |
* | +--| wrapper1 |---|----+ |
* | | +---------------+ v | |
* | | +-------------+ | |
* | | +----| wrapper2 |--------+ |
* | | | +-------------+ | | |
* | | | | | |
* | v v v v | wrapper
* | +---+ +---+ +---------+ +---+ +---+ | invariants
* perform(anyMethod) | | | | | | | | | | | | maintained
* +----------------->|-|---|-|---|-->|anyMethod|---|---|-|---|-|-------->
* | | | | | | | | | | | |
* | | | | | | | | | | | |
* | | | | | | | | | | | |
* | +---+ +---+ +---------+ +---+ +---+ |
* | initialize close |
* +-----------------------------------------+
* </pre>
*
* Bonus:
* - Reports timing metrics by method name and wrapper index.
*
* Use cases:
* - Preserving the input selection ranges before/after reconciliation.
* Restoring selection even in the event of an unexpected error.
* - Deactivating events while rearranging the DOM, preventing blurs/focuses,
* while guaranteeing that afterwards, the event system is reactivated.
* - Flushing a queue of collected DOM mutations to the main UI thread after a
* reconciliation takes place in a worker thread.
* - Invoking any collected `componentDidUpdate` callbacks after rendering new
* content.
* - (Future use case): Wrapping particular flushes of the `ReactWorker` queue
* to preserve the `scrollTop` (an automatic scroll aware DOM).
* - (Future use case): Layout calculations before and after DOM upates.
*
* Transactional plugin API:
* - A module that has an `initialize` method that returns any precomputation.
* - and a `close` method that accepts the precomputation. `close` is invoked
* when the wrapped process is completed, or has failed.
*
* @param {Array<TransactionalWrapper>} transactionWrapper Wrapper modules
* that implement `initialize` and `close`.
* @return {Transaction} Single transaction for reuse in thread.
*
* @class Transaction
*/
var Mixin = {
/**
* Sets up this instance so that it is prepared for collecting metrics. Does
* so such that this setup method may be used on an instance that is already
* initialized, in a way that does not consume additional memory upon reuse.
* That can be useful if you decide to make your subclass of this mixin a
* "PooledClass".
*/
reinitializeTransaction: function() {
this.transactionWrappers = this.getTransactionWrappers();
if (!this.wrapperInitData) {
this.wrapperInitData = [];
} else {
this.wrapperInitData.length = 0;
}
if (!this.timingMetrics) {
this.timingMetrics = {};
}
this.timingMetrics.methodInvocationTime = 0;
if (!this.timingMetrics.wrapperInitTimes) {
this.timingMetrics.wrapperInitTimes = [];
} else {
this.timingMetrics.wrapperInitTimes.length = 0;
}
if (!this.timingMetrics.wrapperCloseTimes) {
this.timingMetrics.wrapperCloseTimes = [];
} else {
this.timingMetrics.wrapperCloseTimes.length = 0;
}
this._isInTransaction = false;
},
_isInTransaction: false,
/**
* @abstract
* @return {Array<TransactionWrapper>} Array of transaction wrappers.
*/
getTransactionWrappers: null,
isInTransaction: function() {
return !!this._isInTransaction;
},
/**
* Executes the function within a safety window. Use this for the top level
* methods that result in large amounts of computation/mutations that would
* need to be safety checked.
*
* @param {function} method Member of scope to call.
* @param {Object} scope Scope to invoke from.
* @param {Object?=} args... Arguments to pass to the method (optional).
* Helps prevent need to bind in many cases.
* @return Return value from `method`.
*/
perform: function(method, scope, a, b, c, d, e, f) {
("production" !== "development" ? invariant(
!this.isInTransaction(),
'Transaction.perform(...): Cannot initialize a transaction when there ' +
'is already an outstanding transaction.'
) : invariant(!this.isInTransaction()));
var memberStart = Date.now();
var errorThrown;
var ret;
try {
this._isInTransaction = true;
// Catching errors makes debugging more difficult, so we start with
// errorThrown set to true before setting it to false after calling
// close -- if it's still set to true in the finally block, it means
// one of these calls threw.
errorThrown = true;
this.initializeAll(0);
ret = method.call(scope, a, b, c, d, e, f);
errorThrown = false;
} finally {
var memberEnd = Date.now();
this.methodInvocationTime += (memberEnd - memberStart);
try {
if (errorThrown) {
// If `method` throws, prefer to show that stack trace over any thrown
// by invoking `closeAll`.
try {
this.closeAll(0);
} catch (err) {
}
} else {
// Since `method` didn't throw, we don't want to silence the exception
// here.
this.closeAll(0);
}
} finally {
this._isInTransaction = false;
}
}
return ret;
},
initializeAll: function(startIndex) {
var transactionWrappers = this.transactionWrappers;
var wrapperInitTimes = this.timingMetrics.wrapperInitTimes;
for (var i = startIndex; i < transactionWrappers.length; i++) {
var initStart = Date.now();
var wrapper = transactionWrappers[i];
try {
// Catching errors makes debugging more difficult, so we start with the
// OBSERVED_ERROR state before overwriting it with the real return value
// of initialize -- if it's still set to OBSERVED_ERROR in the finally
// block, it means wrapper.initialize threw.
this.wrapperInitData[i] = Transaction.OBSERVED_ERROR;
this.wrapperInitData[i] = wrapper.initialize ?
wrapper.initialize.call(this) :
null;
} finally {
var curInitTime = wrapperInitTimes[i];
var initEnd = Date.now();
wrapperInitTimes[i] = (curInitTime || 0) + (initEnd - initStart);
if (this.wrapperInitData[i] === Transaction.OBSERVED_ERROR) {
// The initializer for wrapper i threw an error; initialize the
// remaining wrappers but silence any exceptions from them to ensure
// that the first error is the one to bubble up.
try {
this.initializeAll(i + 1);
} catch (err) {
}
}
}
}
},
/**
* Invokes each of `this.transactionWrappers.close[i]` functions, passing into
* them the respective return values of `this.transactionWrappers.init[i]`
* (`close`rs that correspond to initializers that failed will not be
* invoked).
*/
closeAll: function(startIndex) {
("production" !== "development" ? invariant(
this.isInTransaction(),
'Transaction.closeAll(): Cannot close transaction when none are open.'
) : invariant(this.isInTransaction()));
var transactionWrappers = this.transactionWrappers;
var wrapperCloseTimes = this.timingMetrics.wrapperCloseTimes;
for (var i = startIndex; i < transactionWrappers.length; i++) {
var wrapper = transactionWrappers[i];
var closeStart = Date.now();
var initData = this.wrapperInitData[i];
var errorThrown;
try {
// Catching errors makes debugging more difficult, so we start with
// errorThrown set to true before setting it to false after calling
// close -- if it's still set to true in the finally block, it means
// wrapper.close threw.
errorThrown = true;
if (initData !== Transaction.OBSERVED_ERROR) {
wrapper.close && wrapper.close.call(this, initData);
}
errorThrown = false;
} finally {
var closeEnd = Date.now();
var curCloseTime = wrapperCloseTimes[i];
wrapperCloseTimes[i] = (curCloseTime || 0) + (closeEnd - closeStart);
if (errorThrown) {
// The closer for wrapper i threw an error; close the remaining
// wrappers but silence any exceptions from them to ensure that the
// first error is the one to bubble up.
try {
this.closeAll(i + 1);
} catch (e) {
}
}
}
}
this.wrapperInitData.length = 0;
}
};
var Transaction = {
Mixin: Mixin,
/**
* Token to look for to determine if an error occured.
*/
OBSERVED_ERROR: {}
};
module.exports = Transaction;
},{"./invariant":121}],96:[function(require,module,exports){
/**
* 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 ViewportMetrics
*/
"use strict";
var getUnboundedScrollPosition = require("./getUnboundedScrollPosition");
var ViewportMetrics = {
currentScrollLeft: 0,
currentScrollTop: 0,
refreshScrollValues: function() {
var scrollPosition = getUnboundedScrollPosition(window);
ViewportMetrics.currentScrollLeft = scrollPosition.x;
ViewportMetrics.currentScrollTop = scrollPosition.y;
}
};
module.exports = ViewportMetrics;
},{"./getUnboundedScrollPosition":119}],97:[function(require,module,exports){
/**
* 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 accumulate
*/
"use strict";
var invariant = require("./invariant");
/**
* Accumulates items that must not be null or undefined.
*
* This is used to conserve memory by avoiding array allocations.
*
* @return {*|array<*>} An accumulation of items.
*/
function accumulate(current, next) {
("production" !== "development" ? invariant(
next != null,
'accumulate(...): Accumulated items must be not be null or undefined.'
) : invariant(next != null));
if (current == null) {
return next;
} else {
// Both are not empty. Warning: Never call x.concat(y) when you are not
// certain that x is an Array (x could be a string with concat method).
var currentIsArray = Array.isArray(current);
var nextIsArray = Array.isArray(next);
if (currentIsArray) {
return current.concat(next);
} else {
if (nextIsArray) {
return [current].concat(next);
} else {
return [current, next];
}
}
}
}
module.exports = accumulate;
},{"./invariant":121}],98:[function(require,module,exports){
/**
* 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 adler32
*/
/* jslint bitwise:true */
"use strict";
var MOD = 65521;
// This is a clean-room implementation of adler32 designed for detecting
// if markup is not what we expect it to be. It does not need to be
// cryptographically strong, only reasonable good at detecting if markup
// generated on the server is different than that on the client.
function adler32(data) {
var a = 1;
var b = 0;
for (var i = 0; i < data.length; i++) {
a = (a + data.charCodeAt(i)) % MOD;
b = (b + a) % MOD;
}
return a | (b << 16);
}
module.exports = adler32;
},{}],99:[function(require,module,exports){
/**
* 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.
*
* @typechecks
* @providesModule cloneWithProps
*/
"use strict";
var ReactPropTransferer = require("./ReactPropTransferer");
var keyOf = require("./keyOf");
var CHILDREN_PROP = keyOf({children: null});
/**
* Sometimes you want to change the props of a child passed to you. Usually
* this is to add a CSS class.
*
* @param {object} child child component you'd like to clone
* @param {object} props props you'd like to modify. They will be merged
* as if you used `transferPropsTo()`.
* @return {object} a clone of child with props merged in.
*/
function cloneWithProps(child, props) {
if ("production" !== "development") {
if (child.props.ref) {
console.warn(
'You are calling cloneWithProps() on a child with a ref. This is ' +
'dangerous because you\'re creating a new child which will not be ' +
'added as a ref to its parent.'
);
}
}
var newProps = ReactPropTransferer.mergeProps(props, child.props);
// Use `child.props.children` if it is provided.
if (!newProps.hasOwnProperty(CHILDREN_PROP) &&
child.props.hasOwnProperty(CHILDREN_PROP)) {
newProps.children = child.props.children;
}
return child.constructor.ConvenienceConstructor(newProps);
}
module.exports = cloneWithProps;
},{"./ReactPropTransferer":66,"./keyOf":128}],100:[function(require,module,exports){
/**
* 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 containsNode
* @typechecks
*/
var isTextNode = require("./isTextNode");
/*jslint bitwise:true */
/**
* Checks if a given DOM node contains or is another DOM node.
*
* @param {?DOMNode} outerNode Outer DOM node.
* @param {?DOMNode} innerNode Inner DOM node.
* @return {boolean} True if `outerNode` contains or is `innerNode`.
*/
function containsNode(outerNode, innerNode) {
if (!outerNode || !innerNode) {
return false;
} else if (outerNode === innerNode) {
return true;
} else if (isTextNode(outerNode)) {
return false;
} else if (isTextNode(innerNode)) {
return containsNode(outerNode, innerNode.parentNode);
} else if (outerNode.contains) {
return outerNode.contains(innerNode);
} else if (outerNode.compareDocumentPosition) {
return !!(outerNode.compareDocumentPosition(innerNode) & 16);
} else {
return false;
}
}
module.exports = containsNode;
},{"./isTextNode":125}],101:[function(require,module,exports){
/**
* 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 copyProperties
*/
/**
* Copy properties from one or more objects (up to 5) into the first object.
* This is a shallow copy. It mutates the first object and also returns it.
*
* NOTE: `arguments` has a very significant performance penalty, which is why
* we don't support unlimited arguments.
*/
function copyProperties(obj, a, b, c, d, e, f) {
obj = obj || {};
if ("production" !== "development") {
if (f) {
throw new Error('Too many arguments passed to copyProperties');
}
}
var args = [a, b, c, d, e];
var ii = 0, v;
while (args[ii]) {
v = args[ii++];
for (var k in v) {
obj[k] = v[k];
}
// IE ignores toString in object iteration.. See:
// webreflection.blogspot.com/2007/07/quick-fix-internet-explorer-and.html
if (v.hasOwnProperty && v.hasOwnProperty('toString') &&
(typeof v.toString != 'undefined') && (obj.toString !== v.toString)) {
obj.toString = v.toString;
}
}
return obj;
}
module.exports = copyProperties;
},{}],102:[function(require,module,exports){
/**
* 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 createArrayFrom
* @typechecks
*/
var toArray = require("./toArray");
/**
* Perform a heuristic test to determine if an object is "array-like".
*
* A monk asked Joshu, a Zen master, "Has a dog Buddha nature?"
* Joshu replied: "Mu."
*
* This function determines if its argument has "array nature": it returns
* true if the argument is an actual array, an `arguments' object, or an
* HTMLCollection (e.g. node.childNodes or node.getElementsByTagName()).
*
* It will return false for other array-like objects like Filelist.
*
* @param {*} obj
* @return {boolean}
*/
function hasArrayNature(obj) {
return (
// not null/false
!!obj &&
// arrays are objects, NodeLists are functions in Safari
(typeof obj == 'object' || typeof obj == 'function') &&
// quacks like an array
('length' in obj) &&
// not window
!('setInterval' in obj) &&
// no DOM node should be considered an array-like
// a 'select' element has 'length' and 'item' properties on IE8
(typeof obj.nodeType != 'number') &&
(
// a real array
(// HTMLCollection/NodeList
(Array.isArray(obj) ||
// arguments
('callee' in obj) || 'item' in obj))
)
);
}
/**
* Ensure that the argument is an array by wrapping it in an array if it is not.
* Creates a copy of the argument if it is already an array.
*
* This is mostly useful idiomatically:
*
* var createArrayFrom = require('createArrayFrom');
*
* function takesOneOrMoreThings(things) {
* things = createArrayFrom(things);
* ...
* }
*
* This allows you to treat `things' as an array, but accept scalars in the API.
*
* If you need to convert an array-like object, like `arguments`, into an array
* use toArray instead.
*
* @param {*} obj
* @return {array}
*/
function createArrayFrom(obj) {
if (!hasArrayNature(obj)) {
return [obj];
} else if (Array.isArray(obj)) {
return obj.slice();
} else {
return toArray(obj);
}
}
module.exports = createArrayFrom;
},{"./toArray":140}],103:[function(require,module,exports){
/**
* 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 createFullPageComponent
* @typechecks
*/
"use strict";
// Defeat circular references by requiring this directly.
var ReactCompositeComponent = require("./ReactCompositeComponent");
var invariant = require("./invariant");
/**
* Create a component that will throw an exception when unmounted.
*
* Components like <html> <head> and <body> can't be removed or added
* easily in a cross-browser way, however it's valuable to be able to
* take advantage of React's reconciliation for styling and <title>
* management. So we just document it and throw in dangerous cases.
*
* @param {function} componentClass convenience constructor to wrap
* @return {function} convenience constructor of new component
*/
function createFullPageComponent(componentClass) {
var FullPageComponent = ReactCompositeComponent.createClass({
displayName: 'ReactFullPageComponent' + (
componentClass.componentConstructor.displayName || ''
),
componentWillUnmount: function() {
("production" !== "development" ? invariant(
false,
'%s tried to unmount. Because of cross-browser quirks it is ' +
'impossible to unmount some top-level components (eg <html>, <head>, ' +
'and <body>) reliably and efficiently. To fix this, have a single ' +
'top-level component that never unmounts render these elements.',
this.constructor.displayName
) : invariant(false));
},
render: function() {
return this.transferPropsTo(componentClass(null, this.props.children));
}
});
return FullPageComponent;
}
module.exports = createFullPageComponent;
},{"./ReactCompositeComponent":33,"./invariant":121}],104:[function(require,module,exports){
/**
* 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 createNodesFromMarkup
* @typechecks
*/
/*jslint evil: true, sub: true */
var ExecutionEnvironment = require("./ExecutionEnvironment");
var createArrayFrom = require("./createArrayFrom");
var getMarkupWrap = require("./getMarkupWrap");
var invariant = require("./invariant");
/**
* Dummy container used to render all markup.
*/
var dummyNode =
ExecutionEnvironment.canUseDOM ? document.createElement('div') : null;
/**
* Pattern used by `getNodeName`.
*/
var nodeNamePattern = /^\s*<(\w+)/;
/**
* Extracts the `nodeName` of the first element in a string of markup.
*
* @param {string} markup String of markup.
* @return {?string} Node name of the supplied markup.
*/
function getNodeName(markup) {
var nodeNameMatch = markup.match(nodeNamePattern);
return nodeNameMatch && nodeNameMatch[1].toLowerCase();
}
/**
* Creates an array containing the nodes rendered from the supplied markup. The
* optionally supplied `handleScript` function will be invoked once for each
* <script> element that is rendered. If no `handleScript` function is supplied,
* an exception is thrown if any <script> elements are rendered.
*
* @param {string} markup A string of valid HTML markup.
* @param {?function} handleScript Invoked once for each rendered <script>.
* @return {array<DOMElement|DOMTextNode>} An array of rendered nodes.
*/
function createNodesFromMarkup(markup, handleScript) {
var node = dummyNode;
("production" !== "development" ? invariant(!!dummyNode, 'createNodesFromMarkup dummy not initialized') : invariant(!!dummyNode));
var nodeName = getNodeName(markup);
var wrap = nodeName && getMarkupWrap(nodeName);
if (wrap) {
node.innerHTML = wrap[1] + markup + wrap[2];
var wrapDepth = wrap[0];
while (wrapDepth--) {
node = node.lastChild;
}
} else {
node.innerHTML = markup;
}
var scripts = node.getElementsByTagName('script');
if (scripts.length) {
("production" !== "development" ? invariant(
handleScript,
'createNodesFromMarkup(...): Unexpected <script> element rendered.'
) : invariant(handleScript));
createArrayFrom(scripts).forEach(handleScript);
}
var nodes = createArrayFrom(node.childNodes);
while (node.lastChild) {
node.removeChild(node.lastChild);
}
return nodes;
}
module.exports = createNodesFromMarkup;
},{"./ExecutionEnvironment":21,"./createArrayFrom":102,"./getMarkupWrap":115,"./invariant":121}],105:[function(require,module,exports){
/**
* 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 createObjectFrom
*/
/**
* Construct an object from an array of keys
* and optionally specified value or list of values.
*
* >>> createObjectFrom(['a','b','c']);
* {a: true, b: true, c: true}
*
* >>> createObjectFrom(['a','b','c'], false);
* {a: false, b: false, c: false}
*
* >>> createObjectFrom(['a','b','c'], 'monkey');
* {c:'monkey', b:'monkey' c:'monkey'}
*
* >>> createObjectFrom(['a','b','c'], [1,2,3]);
* {a: 1, b: 2, c: 3}
*
* >>> createObjectFrom(['women', 'men'], [true, false]);
* {women: true, men: false}
*
* @param Array list of keys
* @param mixed optional value or value array. defaults true.
* @returns object
*/
function createObjectFrom(keys, values /* = true */) {
if ("production" !== "development") {
if (!Array.isArray(keys)) {
throw new TypeError('Must pass an array of keys.');
}
}
var object = {};
var isArray = Array.isArray(values);
if (typeof values == 'undefined') {
values = true;
}
for (var ii = keys.length; ii--;) {
object[keys[ii]] = isArray ? values[ii] : values;
}
return object;
}
module.exports = createObjectFrom;
},{}],106:[function(require,module,exports){
/**
* 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 cx
*/
/**
* This function is used to mark string literals representing CSS class names
* so that they can be transformed statically. This allows for modularization
* and minification of CSS class names.
*
* In static_upstream, this function is actually implemented, but it should
* eventually be replaced with something more descriptive, and the transform
* that is used in the main stack should be ported for use elsewhere.
*
* @param string|object className to modularize, or an object of key/values.
* In the object case, the values are conditions that
* determine if the className keys should be included.
* @param [string ...] Variable list of classNames in the string case.
* @return string Renderable space-separated CSS className.
*/
function cx(classNames) {
if (typeof classNames == 'object') {
return Object.keys(classNames).filter(function(className) {
return classNames[className];
}).join(' ');
} else {
return Array.prototype.join.call(arguments, ' ');
}
}
module.exports = cx;
},{}],107:[function(require,module,exports){
/**
* 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 dangerousStyleValue
* @typechecks static-only
*/
"use strict";
var CSSProperty = require("./CSSProperty");
/**
* Convert a value into the proper css writable value. The `styleName` name
* name should be logical (no hyphens), as specified
* in `CSSProperty.isUnitlessNumber`.
*
* @param {string} styleName CSS property name such as `topMargin`.
* @param {*} value CSS property value such as `10px`.
* @return {string} Normalized style value with dimensions applied.
*/
function dangerousStyleValue(styleName, value) {
// Note that we've removed escapeTextForBrowser() calls here since the
// whole string will be escaped when the attribute is injected into
// the markup. If you provide unsafe user data here they can inject
// arbitrary CSS which may be problematic (I couldn't repro this):
// https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
// http://www.thespanner.co.uk/2007/11/26/ultimate-xss-css-injection/
// This is not an XSS hole but instead a potential CSS injection issue
// which has lead to a greater discussion about how we're going to
// trust URLs moving forward. See #2115901
var isEmpty = value == null || typeof value === 'boolean' || value === '';
if (isEmpty) {
return '';
}
var isNonNumeric = isNaN(value);
if (isNonNumeric || value === 0 || CSSProperty.isUnitlessNumber[styleName]) {
return '' + value; // cast to string
}
return value + 'px';
}
module.exports = dangerousStyleValue;
},{"./CSSProperty":3}],108:[function(require,module,exports){
/**
* 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 emptyFunction
*/
var copyProperties = require("./copyProperties");
function makeEmptyFunction(arg) {
return function() {
return arg;
};
}
/**
* This function accepts and discards inputs; it has no side effects. This is
* primarily useful idiomatically for overridable function endpoints which
* always need to be callable, since JS lacks a null-call idiom ala Cocoa.
*/
function emptyFunction() {}
copyProperties(emptyFunction, {
thatReturns: makeEmptyFunction,
thatReturnsFalse: makeEmptyFunction(false),
thatReturnsTrue: makeEmptyFunction(true),
thatReturnsNull: makeEmptyFunction(null),
thatReturnsThis: function() { return this; },
thatReturnsArgument: function(arg) { return arg; }
});
module.exports = emptyFunction;
},{"./copyProperties":101}],109:[function(require,module,exports){
/**
* 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 escapeTextForBrowser
* @typechecks static-only
*/
"use strict";
var ESCAPE_LOOKUP = {
"&": "&amp;",
">": "&gt;",
"<": "&lt;",
"\"": "&quot;",
"'": "&#x27;",
"/": "&#x2f;"
};
var ESCAPE_REGEX = /[&><"'\/]/g;
function escaper(match) {
return ESCAPE_LOOKUP[match];
}
/**
* Escapes text to prevent scripting attacks.
*
* @param {*} text Text value to escape.
* @return {string} An escaped string.
*/
function escapeTextForBrowser(text) {
return ('' + text).replace(ESCAPE_REGEX, escaper);
}
module.exports = escapeTextForBrowser;
},{}],110:[function(require,module,exports){
/**
* 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 flattenChildren
*/
"use strict";
var invariant = require("./invariant");
var traverseAllChildren = require("./traverseAllChildren");
/**
* @param {function} traverseContext Context passed through traversal.
* @param {?ReactComponent} child React child component.
* @param {!string} name String name of key path to child.
*/
function flattenSingleChildIntoContext(traverseContext, child, name) {
// We found a component instance.
var result = traverseContext;
("production" !== "development" ? invariant(
!result.hasOwnProperty(name),
'flattenChildren(...): Encountered two children with the same key, `%s`. ' +
'Children keys must be unique.',
name
) : invariant(!result.hasOwnProperty(name)));
if (child != null) {
result[name] = child;
}
}
/**
* Flattens children that are typically specified as `props.children`. Any null
* children will not be included in the resulting object.
* @return {!object} flattened children keyed by name.
*/
function flattenChildren(children) {
if (children == null) {
return children;
}
var result = {};
traverseAllChildren(children, flattenSingleChildIntoContext, result);
return result;
}
module.exports = flattenChildren;
},{"./invariant":121,"./traverseAllChildren":141}],111:[function(require,module,exports){
/**
* 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 forEachAccumulated
*/
"use strict";
/**
* @param {array} an "accumulation" of items which is either an Array or
* a single item. Useful when paired with the `accumulate` module. This is a
* simple utility that allows us to reason about a collection of items, but
* handling the case when there is exactly one item (and we do not need to
* allocate an array).
*/
var forEachAccumulated = function(arr, cb, scope) {
if (Array.isArray(arr)) {
arr.forEach(cb, scope);
} else if (arr) {
cb.call(scope, arr);
}
};
module.exports = forEachAccumulated;
},{}],112:[function(require,module,exports){
/**
* 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 getActiveElement
* @typechecks
*/
/**
* Same as document.activeElement but wraps in a try-catch block. In IE it is
* not safe to call document.activeElement if there is nothing focused.
*
* The activeElement will be null only if the document body is not yet defined.
*/
function getActiveElement() /*?DOMElement*/ {
try {
return document.activeElement || document.body;
} catch (e) {
return document.body;
}
}
module.exports = getActiveElement;
},{}],113:[function(require,module,exports){
/**
* 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 getEventKey
* @typechecks static-only
*/
"use strict";
/**
* Normalization of deprecated HTML5 "key" values
* @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names
*/
var normalizeKey = {
'Esc': 'Escape',
'Spacebar': ' ',
'Left': 'ArrowLeft',
'Up': 'ArrowUp',
'Right': 'ArrowRight',
'Down': 'ArrowDown',
'Del': 'Delete',
'Win': 'OS',
'Menu': 'ContextMenu',
'Apps': 'ContextMenu',
'Scroll': 'ScrollLock',
'MozPrintableKey': 'Unidentified'
};
/**
* Translation from legacy "which/keyCode" to HTML5 "key"
* Only special keys supported, all others depend on keyboard layout or browser
* @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names
*/
var translateToKey = {
8: 'Backspace',
9: 'Tab',
12: 'Clear',
13: 'Enter',
16: 'Shift',
17: 'Control',
18: 'Alt',
19: 'Pause',
20: 'CapsLock',
27: 'Escape',
32: ' ',
33: 'PageUp',
34: 'PageDown',
35: 'End',
36: 'Home',
37: 'ArrowLeft',
38: 'ArrowUp',
39: 'ArrowRight',
40: 'ArrowDown',
45: 'Insert',
46: 'Delete',
112: 'F1', 113: 'F2', 114: 'F3', 115: 'F4', 116: 'F5', 117: 'F6',
118: 'F7', 119: 'F8', 120: 'F9', 121: 'F10', 122: 'F11', 123: 'F12',
144: 'NumLock',
145: 'ScrollLock',
224: 'Meta'
};
/**
* @param {object} nativeEvent Native browser event.
* @return {string} Normalized `key` property.
*/
function getEventKey(nativeEvent) {
return 'key' in nativeEvent ?
normalizeKey[nativeEvent.key] || nativeEvent.key :
translateToKey[nativeEvent.which || nativeEvent.keyCode] || 'Unidentified';
}
module.exports = getEventKey;
},{}],114:[function(require,module,exports){
/**
* 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 getEventTarget
* @typechecks static-only
*/
"use strict";
/**
* Gets the target node from a native browser event by accounting for
* inconsistencies in browser DOM APIs.
*
* @param {object} nativeEvent Native browser event.
* @return {DOMEventTarget} Target node.
*/
function getEventTarget(nativeEvent) {
var target = nativeEvent.target || nativeEvent.srcElement || window;
// Safari may fire events on text nodes (Node.TEXT_NODE is 3).
// @see http://www.quirksmode.org/js/events_properties.html
return target.nodeType === 3 ? target.parentNode : target;
}
module.exports = getEventTarget;
},{}],115:[function(require,module,exports){
/**
* 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 getMarkupWrap
*/
var ExecutionEnvironment = require("./ExecutionEnvironment");
var invariant = require("./invariant");
/**
* Dummy container used to detect which wraps are necessary.
*/
var dummyNode =
ExecutionEnvironment.canUseDOM ? document.createElement('div') : null;
/**
* Some browsers cannot use `innerHTML` to render certain elements standalone,
* so we wrap them, render the wrapped nodes, then extract the desired node.
*
* In IE8, certain elements cannot render alone, so wrap all elements ('*').
*/
var shouldWrap = {
// Force wrapping for SVG elements because if they get created inside a <div>,
// they will be initialized in the wrong namespace (and will not display).
'circle': true,
'defs': true,
'g': true,
'line': true,
'linearGradient': true,
'path': true,
'polygon': true,
'polyline': true,
'radialGradient': true,
'rect': true,
'stop': true,
'text': true
};
var selectWrap = [1, '<select multiple="true">', '</select>'];
var tableWrap = [1, '<table>', '</table>'];
var trWrap = [3, '<table><tbody><tr>', '</tr></tbody></table>'];
var svgWrap = [1, '<svg>', '</svg>'];
var markupWrap = {
'*': [1, '?<div>', '</div>'],
'area': [1, '<map>', '</map>'],
'col': [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>'],
'legend': [1, '<fieldset>', '</fieldset>'],
'param': [1, '<object>', '</object>'],
'tr': [2, '<table><tbody>', '</tbody></table>'],
'optgroup': selectWrap,
'option': selectWrap,
'caption': tableWrap,
'colgroup': tableWrap,
'tbody': tableWrap,
'tfoot': tableWrap,
'thead': tableWrap,
'td': trWrap,
'th': trWrap,
'circle': svgWrap,
'defs': svgWrap,
'g': svgWrap,
'line': svgWrap,
'linearGradient': svgWrap,
'path': svgWrap,
'polygon': svgWrap,
'polyline': svgWrap,
'radialGradient': svgWrap,
'rect': svgWrap,
'stop': svgWrap,
'text': svgWrap
};
/**
* Gets the markup wrap configuration for the supplied `nodeName`.
*
* NOTE: This lazily detects which wraps are necessary for the current browser.
*
* @param {string} nodeName Lowercase `nodeName`.
* @return {?array} Markup wrap configuration, if applicable.
*/
function getMarkupWrap(nodeName) {
("production" !== "development" ? invariant(!!dummyNode, 'Markup wrapping node not initialized') : invariant(!!dummyNode));
if (!markupWrap.hasOwnProperty(nodeName)) {
nodeName = '*';
}
if (!shouldWrap.hasOwnProperty(nodeName)) {
if (nodeName === '*') {
dummyNode.innerHTML = '<link />';
} else {
dummyNode.innerHTML = '<' + nodeName + '></' + nodeName + '>';
}
shouldWrap[nodeName] = !dummyNode.firstChild;
}
return shouldWrap[nodeName] ? markupWrap[nodeName] : null;
}
module.exports = getMarkupWrap;
},{"./ExecutionEnvironment":21,"./invariant":121}],116:[function(require,module,exports){
/**
* 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 getNodeForCharacterOffset
*/
"use strict";
/**
* Given any node return the first leaf node without children.
*
* @param {DOMElement|DOMTextNode} node
* @return {DOMElement|DOMTextNode}
*/
function getLeafNode(node) {
while (node && node.firstChild) {
node = node.firstChild;
}
return node;
}
/**
* Get the next sibling within a container. This will walk up the
* DOM if a node's siblings have been exhausted.
*
* @param {DOMElement|DOMTextNode} node
* @return {?DOMElement|DOMTextNode}
*/
function getSiblingNode(node) {
while (node) {
if (node.nextSibling) {
return node.nextSibling;
}
node = node.parentNode;
}
}
/**
* Get object describing the nodes which contain characters at offset.
*
* @param {DOMElement|DOMTextNode} root
* @param {number} offset
* @return {?object}
*/
function getNodeForCharacterOffset(root, offset) {
var node = getLeafNode(root);
var nodeStart = 0;
var nodeEnd = 0;
while (node) {
if (node.nodeType == 3) {
nodeEnd = nodeStart + node.textContent.length;
if (nodeStart <= offset && nodeEnd >= offset) {
return {
node: node,
offset: offset - nodeStart
};
}
nodeStart = nodeEnd;
}
node = getLeafNode(getSiblingNode(node));
}
}
module.exports = getNodeForCharacterOffset;
},{}],117:[function(require,module,exports){
/**
* 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 getReactRootElementInContainer
*/
"use strict";
var DOC_NODE_TYPE = 9;
/**
* @param {DOMElement|DOMDocument} container DOM element that may contain
* a React component
* @return {?*} DOM element that may have the reactRoot ID, or null.
*/
function getReactRootElementInContainer(container) {
if (!container) {
return null;
}
if (container.nodeType === DOC_NODE_TYPE) {
return container.documentElement;
} else {
return container.firstChild;
}
}
module.exports = getReactRootElementInContainer;
},{}],118:[function(require,module,exports){
/**
* 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 getTextContentAccessor
*/
"use strict";
var ExecutionEnvironment = require("./ExecutionEnvironment");
var contentKey = null;
/**
* Gets the key used to access text content on a DOM node.
*
* @return {?string} Key used to access text content.
* @internal
*/
function getTextContentAccessor() {
if (!contentKey && ExecutionEnvironment.canUseDOM) {
// Prefer textContent to innerText because many browsers support both but
// SVG <text> elements don't support innerText even when <div> does.
contentKey = 'textContent' in document.createElement('div') ?
'textContent' :
'innerText';
}
return contentKey;
}
module.exports = getTextContentAccessor;
},{"./ExecutionEnvironment":21}],119:[function(require,module,exports){
/**
* 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 getUnboundedScrollPosition
* @typechecks
*/
"use strict";
/**
* Gets the scroll position of the supplied element or window.
*
* The return values are unbounded, unlike `getScrollPosition`. This means they
* may be negative or exceed the element boundaries (which is possible using
* inertial scrolling).
*
* @param {DOMWindow|DOMElement} scrollable
* @return {object} Map with `x` and `y` keys.
*/
function getUnboundedScrollPosition(scrollable) {
if (scrollable === window) {
return {
x: window.pageXOffset || document.documentElement.scrollLeft,
y: window.pageYOffset || document.documentElement.scrollTop
};
}
return {
x: scrollable.scrollLeft,
y: scrollable.scrollTop
};
}
module.exports = getUnboundedScrollPosition;
},{}],120:[function(require,module,exports){
/**
* 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 hyphenate
* @typechecks
*/
var _uppercasePattern = /([A-Z])/g;
/**
* Hyphenates a camelcased string, for example:
*
* > hyphenate('backgroundColor')
* < "background-color"
*
* @param {string} string
* @return {string}
*/
function hyphenate(string) {
return string.replace(_uppercasePattern, '-$1').toLowerCase();
}
module.exports = hyphenate;
},{}],121:[function(require,module,exports){
/**
* 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 invariant
*/
"use strict";
/**
* Use invariant() to assert state which your program assumes to be true.
*
* Provide sprintf-style format (only %s is supported) and arguments
* to provide information about what broke and what you were
* expecting.
*
* The invariant message will be stripped in production, but the invariant
* will remain to ensure logic does not differ in production.
*/
var invariant = function(condition) {
if (!condition) {
var error = new Error(
'Minified exception occured; use the non-minified dev environment for ' +
'the full error message and additional helpful warnings.'
);
error.framesToPop = 1;
throw error;
}
};
if ("production" !== "development") {
invariant = function(condition, format, a, b, c, d, e, f) {
if (format === undefined) {
throw new Error('invariant requires an error message argument');
}
if (!condition) {
var args = [a, b, c, d, e, f];
var argIndex = 0;
var error = new Error(
'Invariant Violation: ' +
format.replace(/%s/g, function() { return args[argIndex++]; })
);
error.framesToPop = 1; // we don't care about invariant's own frame
throw error;
}
};
}
module.exports = invariant;
},{}],122:[function(require,module,exports){
/**
* 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 isEventSupported
*/
"use strict";
var ExecutionEnvironment = require("./ExecutionEnvironment");
var useHasFeature;
if (ExecutionEnvironment.canUseDOM) {
useHasFeature =
document.implementation &&
document.implementation.hasFeature &&
// always returns true in newer browsers as per the standard.
// @see http://dom.spec.whatwg.org/#dom-domimplementation-hasfeature
document.implementation.hasFeature('', '') !== true;
}
/**
* Checks if an event is supported in the current execution environment.
*
* NOTE: This will not work correctly for non-generic events such as `change`,
* `reset`, `load`, `error`, and `select`.
*
* Borrows from Modernizr.
*
* @param {string} eventNameSuffix Event name, e.g. "click".
* @param {?boolean} capture Check if the capture phase is supported.
* @return {boolean} True if the event is supported.
* @internal
* @license Modernizr 3.0.0pre (Custom Build) | MIT
*/
function isEventSupported(eventNameSuffix, capture) {
if (!ExecutionEnvironment.canUseDOM ||
capture && !('addEventListener' in document)) {
return false;
}
var eventName = 'on' + eventNameSuffix;
var isSupported = eventName in document;
if (!isSupported) {
var element = document.createElement('div');
element.setAttribute(eventName, 'return;');
isSupported = typeof element[eventName] === 'function';
}
if (!isSupported && useHasFeature && eventNameSuffix === 'wheel') {
// This is the only way to test support for the `wheel` event in IE9+.
isSupported = document.implementation.hasFeature('Events.wheel', '3.0');
}
return isSupported;
}
module.exports = isEventSupported;
},{"./ExecutionEnvironment":21}],123:[function(require,module,exports){
/**
* 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 isNode
* @typechecks
*/
/**
* @param {*} object The object to check.
* @return {boolean} Whether or not the object is a DOM node.
*/
function isNode(object) {
return !!(object && (
typeof Node !== 'undefined' ? object instanceof Node :
typeof object === 'object' &&
typeof object.nodeType === 'number' &&
typeof object.nodeName === 'string'
));
}
module.exports = isNode;
},{}],124:[function(require,module,exports){
/**
* 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 isTextInputElement
*/
"use strict";
/**
* @see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#input-type-attr-summary
*/
var supportedInputTypes = {
'color': true,
'date': true,
'datetime': true,
'datetime-local': true,
'email': true,
'month': true,
'number': true,
'password': true,
'range': true,
'search': true,
'tel': true,
'text': true,
'time': true,
'url': true,
'week': true
};
function isTextInputElement(elem) {
return elem && (
(elem.nodeName === 'INPUT' && supportedInputTypes[elem.type]) ||
elem.nodeName === 'TEXTAREA'
);
}
module.exports = isTextInputElement;
},{}],125:[function(require,module,exports){
/**
* 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 isTextNode
* @typechecks
*/
var isNode = require("./isNode");
/**
* @param {*} object The object to check.
* @return {boolean} Whether or not the object is a DOM text node.
*/
function isTextNode(object) {
return isNode(object) && object.nodeType == 3;
}
module.exports = isTextNode;
},{"./isNode":123}],126:[function(require,module,exports){
/**
* 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 joinClasses
* @typechecks static-only
*/
"use strict";
/**
* Combines multiple className strings into one.
* http://jsperf.com/joinclasses-args-vs-array
*
* @param {...?string} classes
* @return {string}
*/
function joinClasses(className/*, ... */) {
if (!className) {
className = '';
}
var nextClass;
var argLength = arguments.length;
if (argLength > 1) {
for (var ii = 1; ii < argLength; ii++) {
nextClass = arguments[ii];
nextClass && (className += ' ' + nextClass);
}
}
return className;
}
module.exports = joinClasses;
},{}],127:[function(require,module,exports){
/**
* 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 keyMirror
* @typechecks static-only
*/
"use strict";
var invariant = require("./invariant");
/**
* Constructs an enumeration with keys equal to their value.
*
* For example:
*
* var COLORS = keyMirror({blue: null, red: null});
* var myColor = COLORS.blue;
* var isColorValid = !!COLORS[myColor];
*
* The last line could not be performed if the values of the generated enum were
* not equal to their keys.
*
* Input: {key1: val1, key2: val2}
* Output: {key1: key1, key2: key2}
*
* @param {object} obj
* @return {object}
*/
var keyMirror = function(obj) {
var ret = {};
var key;
("production" !== "development" ? invariant(
obj instanceof Object && !Array.isArray(obj),
'keyMirror(...): Argument must be an object.'
) : invariant(obj instanceof Object && !Array.isArray(obj)));
for (key in obj) {
if (!obj.hasOwnProperty(key)) {
continue;
}
ret[key] = key;
}
return ret;
};
module.exports = keyMirror;
},{"./invariant":121}],128:[function(require,module,exports){
/**
* 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 keyOf
*/
/**
* Allows extraction of a minified key. Let's the build system minify keys
* without loosing the ability to dynamically use key strings as values
* themselves. Pass in an object with a single key/val pair and it will return
* you the string key of that single record. Suppose you want to grab the
* value for a key 'className' inside of an object. Key/val minification may
* have aliased that key to be 'xa12'. keyOf({className: null}) will return
* 'xa12' in that case. Resolve keys you want to use once at startup time, then
* reuse those resolutions.
*/
var keyOf = function(oneKeyObj) {
var key;
for (key in oneKeyObj) {
if (!oneKeyObj.hasOwnProperty(key)) {
continue;
}
return key;
}
return null;
};
module.exports = keyOf;
},{}],129:[function(require,module,exports){
/**
* 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 memoizeStringOnly
* @typechecks static-only
*/
"use strict";
/**
* Memoizes the return value of a function that accepts one string argument.
*
* @param {function} callback
* @return {function}
*/
function memoizeStringOnly(callback) {
var cache = {};
return function(string) {
if (cache.hasOwnProperty(string)) {
return cache[string];
} else {
return cache[string] = callback.call(this, string);
}
};
}
module.exports = memoizeStringOnly;
},{}],130:[function(require,module,exports){
/**
* 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 merge
*/
"use strict";
var mergeInto = require("./mergeInto");
/**
* Shallow merges two structures into a return value, without mutating either.
*
* @param {?object} one Optional object with properties to merge from.
* @param {?object} two Optional object with properties to merge from.
* @return {object} The shallow extension of one by two.
*/
var merge = function(one, two) {
var result = {};
mergeInto(result, one);
mergeInto(result, two);
return result;
};
module.exports = merge;
},{"./mergeInto":132}],131:[function(require,module,exports){
/**
* 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 mergeHelpers
*
* requiresPolyfills: Array.isArray
*/
"use strict";
var invariant = require("./invariant");
var keyMirror = require("./keyMirror");
/**
* Maximum number of levels to traverse. Will catch circular structures.
* @const
*/
var MAX_MERGE_DEPTH = 36;
/**
* We won't worry about edge cases like new String('x') or new Boolean(true).
* Functions are considered terminals, and arrays are not.
* @param {*} o The item/object/value to test.
* @return {boolean} true iff the argument is a terminal.
*/
var isTerminal = function(o) {
return typeof o !== 'object' || o === null;
};
var mergeHelpers = {
MAX_MERGE_DEPTH: MAX_MERGE_DEPTH,
isTerminal: isTerminal,
/**
* Converts null/undefined values into empty object.
*
* @param {?Object=} arg Argument to be normalized (nullable optional)
* @return {!Object}
*/
normalizeMergeArg: function(arg) {
return arg === undefined || arg === null ? {} : arg;
},
/**
* If merging Arrays, a merge strategy *must* be supplied. If not, it is
* likely the caller's fault. If this function is ever called with anything
* but `one` and `two` being `Array`s, it is the fault of the merge utilities.
*
* @param {*} one Array to merge into.
* @param {*} two Array to merge from.
*/
checkMergeArrayArgs: function(one, two) {
("production" !== "development" ? invariant(
Array.isArray(one) && Array.isArray(two),
'Tried to merge arrays, instead got %s and %s.',
one,
two
) : invariant(Array.isArray(one) && Array.isArray(two)));
},
/**
* @param {*} one Object to merge into.
* @param {*} two Object to merge from.
*/
checkMergeObjectArgs: function(one, two) {
mergeHelpers.checkMergeObjectArg(one);
mergeHelpers.checkMergeObjectArg(two);
},
/**
* @param {*} arg
*/
checkMergeObjectArg: function(arg) {
("production" !== "development" ? invariant(
!isTerminal(arg) && !Array.isArray(arg),
'Tried to merge an object, instead got %s.',
arg
) : invariant(!isTerminal(arg) && !Array.isArray(arg)));
},
/**
* Checks that a merge was not given a circular object or an object that had
* too great of depth.
*
* @param {number} Level of recursion to validate against maximum.
*/
checkMergeLevel: function(level) {
("production" !== "development" ? invariant(
level < MAX_MERGE_DEPTH,
'Maximum deep merge depth exceeded. You may be attempting to merge ' +
'circular structures in an unsupported way.'
) : invariant(level < MAX_MERGE_DEPTH));
},
/**
* Checks that the supplied merge strategy is valid.
*
* @param {string} Array merge strategy.
*/
checkArrayStrategy: function(strategy) {
("production" !== "development" ? invariant(
strategy === undefined || strategy in mergeHelpers.ArrayStrategies,
'You must provide an array strategy to deep merge functions to ' +
'instruct the deep merge how to resolve merging two arrays.'
) : invariant(strategy === undefined || strategy in mergeHelpers.ArrayStrategies));
},
/**
* Set of possible behaviors of merge algorithms when encountering two Arrays
* that must be merged together.
* - `clobber`: The left `Array` is ignored.
* - `indexByIndex`: The result is achieved by recursively deep merging at
* each index. (not yet supported.)
*/
ArrayStrategies: keyMirror({
Clobber: true,
IndexByIndex: true
})
};
module.exports = mergeHelpers;
},{"./invariant":121,"./keyMirror":127}],132:[function(require,module,exports){
/**
* 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 mergeInto
* @typechecks static-only
*/
"use strict";
var mergeHelpers = require("./mergeHelpers");
var checkMergeObjectArg = mergeHelpers.checkMergeObjectArg;
/**
* Shallow merges two structures by mutating the first parameter.
*
* @param {object} one Object to be merged into.
* @param {?object} two Optional object with properties to merge from.
*/
function mergeInto(one, two) {
checkMergeObjectArg(one);
if (two != null) {
checkMergeObjectArg(two);
for (var key in two) {
if (!two.hasOwnProperty(key)) {
continue;
}
one[key] = two[key];
}
}
}
module.exports = mergeInto;
},{"./mergeHelpers":131}],133:[function(require,module,exports){
/**
* 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 mixInto
*/
"use strict";
/**
* Simply copies properties to the prototype.
*/
var mixInto = function(constructor, methodBag) {
var methodName;
for (methodName in methodBag) {
if (!methodBag.hasOwnProperty(methodName)) {
continue;
}
constructor.prototype[methodName] = methodBag[methodName];
}
};
module.exports = mixInto;
},{}],134:[function(require,module,exports){
/**
* 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 objMap
*/
"use strict";
/**
* For each key/value pair, invokes callback func and constructs a resulting
* object which contains, for every key in obj, values that are the result of
* of invoking the function:
*
* func(value, key, iteration)
*
* @param {?object} obj Object to map keys over
* @param {function} func Invoked for each key/val pair.
* @param {?*} context
* @return {?object} Result of mapping or null if obj is falsey
*/
function objMap(obj, func, context) {
if (!obj) {
return null;
}
var i = 0;
var ret = {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
ret[key] = func.call(context, obj[key], key, i++);
}
}
return ret;
}
module.exports = objMap;
},{}],135:[function(require,module,exports){
/**
* 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 objMapKeyVal
*/
"use strict";
/**
* Behaves the same as `objMap` but invokes func with the key first, and value
* second. Use `objMap` unless you need this special case.
* Invokes func as:
*
* func(key, value, iteration)
*
* @param {?object} obj Object to map keys over
* @param {!function} func Invoked for each key/val pair.
* @param {?*} context
* @return {?object} Result of mapping or null if obj is falsey
*/
function objMapKeyVal(obj, func, context) {
if (!obj) {
return null;
}
var i = 0;
var ret = {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
ret[key] = func.call(context, key, obj[key], i++);
}
}
return ret;
}
module.exports = objMapKeyVal;
},{}],136:[function(require,module,exports){
/**
* 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 onlyChild
*/
"use strict";
var ReactComponent = require("./ReactComponent");
var invariant = require("./invariant");
/**
* Returns the first child in a collection of children and verifies that there
* is only one child in the collection. The current implementation of this
* function assumes that a single child gets passed without a wrapper, but the
* purpose of this helper function is to abstract away the particular structure
* of children.
*
* @param {?object} children Child collection structure.
* @return {ReactComponent} The first and only `ReactComponent` contained in the
* structure.
*/
function onlyChild(children) {
("production" !== "development" ? invariant(
ReactComponent.isValidComponent(children),
'onlyChild must be passed a children with exactly one child.'
) : invariant(ReactComponent.isValidComponent(children)));
return children;
}
module.exports = onlyChild;
},{"./ReactComponent":30,"./invariant":121}],137:[function(require,module,exports){
/**
* 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 performanceNow
* @typechecks static-only
*/
"use strict";
var ExecutionEnvironment = require("./ExecutionEnvironment");
/**
* Detect if we can use window.performance.now() and gracefully
* fallback to Date.now() if it doesn't exist.
* We need to support Firefox < 15 for now due to Facebook's webdriver
* infrastructure.
*/
var performance = null;
if (ExecutionEnvironment.canUseDOM) {
performance = window.performance || window.webkitPerformance;
}
if (!performance || !performance.now) {
performance = Date;
}
var performanceNow = performance.now.bind(performance);
module.exports = performanceNow;
},{"./ExecutionEnvironment":21}],138:[function(require,module,exports){
/**
* 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 shallowEqual
*/
"use strict";
/**
* Performs equality by iterating through keys on an object and returning
* false when any key has values which are not strictly equal between
* objA and objB. Returns true when the values of all keys are strictly equal.
*
* @return {boolean}
*/
function shallowEqual(objA, objB) {
if (objA === objB) {
return true;
}
var key;
// Test for A's keys different from B.
for (key in objA) {
if (objA.hasOwnProperty(key) &&
(!objB.hasOwnProperty(key) || objA[key] !== objB[key])) {
return false;
}
}
// Test for B'a keys missing from A.
for (key in objB) {
if (objB.hasOwnProperty(key) && !objA.hasOwnProperty(key)) {
return false;
}
}
return true;
}
module.exports = shallowEqual;
},{}],139:[function(require,module,exports){
/**
* 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 shouldUpdateReactComponent
* @typechecks static-only
*/
"use strict";
/**
* Given a `prevComponent` and `nextComponent`, determines if `prevComponent`
* should be updated as opposed to being destroyed or replaced.
*
* @param {?object} prevComponent
* @param {?object} nextComponent
* @return {boolean} True if `prevComponent` should be updated.
* @protected
*/
function shouldUpdateReactComponent(prevComponent, nextComponent) {
// TODO: Remove warning after a release.
if (prevComponent && nextComponent &&
prevComponent.constructor === nextComponent.constructor && (
(prevComponent.props && prevComponent.props.key) ===
(nextComponent.props && nextComponent.props.key)
)) {
if (prevComponent._owner === nextComponent._owner) {
return true;
} else {
if ("production" !== "development") {
if (prevComponent.state) {
console.warn(
'A recent change to React has been found to impact your code. ' +
'A mounted component will now be unmounted and replaced by a ' +
'component (of the same class) if their owners are different. ' +
'Previously, ownership was not considered when updating.',
prevComponent,
nextComponent
);
}
}
}
}
return false;
}
module.exports = shouldUpdateReactComponent;
},{}],140:[function(require,module,exports){
/**
* Copyright 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 toArray
* @typechecks
*/
var invariant = require("./invariant");
/**
* Convert array-like objects to arrays.
*
* This API assumes the caller knows the contents of the data type. For less
* well defined inputs use createArrayFrom.
*
* @param {object|function} obj
* @return {array}
*/
function toArray(obj) {
var length = obj.length;
// Some browse builtin objects can report typeof 'function' (e.g. NodeList in
// old versions of Safari).
("production" !== "development" ? invariant(
!Array.isArray(obj) &&
(typeof obj === 'object' || typeof obj === 'function'),
'toArray: Array-like object expected'
) : invariant(!Array.isArray(obj) &&
(typeof obj === 'object' || typeof obj === 'function')));
("production" !== "development" ? invariant(
typeof length === 'number',
'toArray: Object needs a length property'
) : invariant(typeof length === 'number'));
("production" !== "development" ? invariant(
length === 0 ||
(length - 1) in obj,
'toArray: Object should have keys for indices'
) : invariant(length === 0 ||
(length - 1) in obj));
// Old IE doesn't give collections access to hasOwnProperty. Assume inputs
// without method will throw during the slice call and skip straight to the
// fallback.
if (obj.hasOwnProperty) {
try {
return Array.prototype.slice.call(obj);
} catch (e) {
// IE < 9 does not support Array#slice on collections objects
}
}
// Fall back to copying key by key. This assumes all keys have a value,
// so will not preserve sparsely populated inputs.
var ret = Array(length);
for (var ii = 0; ii < length; ii++) {
ret[ii] = obj[ii];
}
return ret;
}
module.exports = toArray;
},{"./invariant":121}],141:[function(require,module,exports){
/**
* 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 traverseAllChildren
*/
"use strict";
var ReactInstanceHandles = require("./ReactInstanceHandles");
var ReactTextComponent = require("./ReactTextComponent");
var invariant = require("./invariant");
var SEPARATOR = ReactInstanceHandles.SEPARATOR;
var SUBSEPARATOR = ':';
/**
* TODO: Test that:
* 1. `mapChildren` transforms strings and numbers into `ReactTextComponent`.
* 2. it('should fail when supplied duplicate key', function() {
* 3. That a single child and an array with one item have the same key pattern.
* });
*/
var userProvidedKeyEscaperLookup = {
'=': '=0',
'.': '=1',
':': '=2'
};
var userProvidedKeyEscapeRegex = /[=.:]/g;
function userProvidedKeyEscaper(match) {
return userProvidedKeyEscaperLookup[match];
}
/**
* Generate a key string that identifies a component within a set.
*
* @param {*} component A component that could contain a manual key.
* @param {number} index Index that is used if a manual key is not provided.
* @return {string}
*/
function getComponentKey(component, index) {
if (component && component.props && component.props.key != null) {
// Explicit key
return wrapUserProvidedKey(component.props.key);
}
// Implicit key determined by the index in the set
return index.toString(36);
}
/**
* Escape a component key so that it is safe to use in a reactid.
*
* @param {*} key Component key to be escaped.
* @return {string} An escaped string.
*/
function escapeUserProvidedKey(text) {
return ('' + text).replace(
userProvidedKeyEscapeRegex,
userProvidedKeyEscaper
);
}
/**
* Wrap a `key` value explicitly provided by the user to distinguish it from
* implicitly-generated keys generated by a component's index in its parent.
*
* @param {string} key Value of a user-provided `key` attribute
* @return {string}
*/
function wrapUserProvidedKey(key) {
return '$' + escapeUserProvidedKey(key);
}
/**
* @param {?*} children Children tree container.
* @param {!string} nameSoFar Name of the key path so far.
* @param {!number} indexSoFar Number of children encountered until this point.
* @param {!function} callback Callback to invoke with each child found.
* @param {?*} traverseContext Used to pass information throughout the traversal
* process.
* @return {!number} The number of children in this subtree.
*/
var traverseAllChildrenImpl =
function(children, nameSoFar, indexSoFar, callback, traverseContext) {
var subtreeCount = 0; // Count of children found in the current subtree.
if (Array.isArray(children)) {
for (var i = 0; i < children.length; i++) {
var child = children[i];
var nextName = (
nameSoFar +
(nameSoFar ? SUBSEPARATOR : SEPARATOR) +
getComponentKey(child, i)
);
var nextIndex = indexSoFar + subtreeCount;
subtreeCount += traverseAllChildrenImpl(
child,
nextName,
nextIndex,
callback,
traverseContext
);
}
} else {
var type = typeof children;
var isOnlyChild = nameSoFar === '';
// If it's the only child, treat the name as if it was wrapped in an array
// so that it's consistent if the number of children grows
var storageName =
isOnlyChild ? SEPARATOR + getComponentKey(children, 0) : nameSoFar;
if (children == null || type === 'boolean') {
// All of the above are perceived as null.
callback(traverseContext, null, storageName, indexSoFar);
subtreeCount = 1;
} else if (children.mountComponentIntoNode) {
callback(traverseContext, children, storageName, indexSoFar);
subtreeCount = 1;
} else {
if (type === 'object') {
("production" !== "development" ? invariant(
!children || children.nodeType !== 1,
'traverseAllChildren(...): Encountered an invalid child; DOM ' +
'elements are not valid children of React components.'
) : invariant(!children || children.nodeType !== 1));
for (var key in children) {
if (children.hasOwnProperty(key)) {
subtreeCount += traverseAllChildrenImpl(
children[key],
(
nameSoFar + (nameSoFar ? SUBSEPARATOR : SEPARATOR) +
wrapUserProvidedKey(key) + SUBSEPARATOR +
getComponentKey(children[key], 0)
),
indexSoFar + subtreeCount,
callback,
traverseContext
);
}
}
} else if (type === 'string') {
var normalizedText = new ReactTextComponent(children);
callback(traverseContext, normalizedText, storageName, indexSoFar);
subtreeCount += 1;
} else if (type === 'number') {
var normalizedNumber = new ReactTextComponent('' + children);
callback(traverseContext, normalizedNumber, storageName, indexSoFar);
subtreeCount += 1;
}
}
}
return subtreeCount;
};
/**
* Traverses children that are typically specified as `props.children`, but
* might also be specified through attributes:
*
* - `traverseAllChildren(this.props.children, ...)`
* - `traverseAllChildren(this.props.leftPanelChildren, ...)`
*
* The `traverseContext` is an optional argument that is passed through the
* entire traversal. It can be used to store accumulations or anything else that
* the callback might find relevant.
*
* @param {?*} children Children tree object.
* @param {!function} callback To invoke upon traversing each child.
* @param {?*} traverseContext Context for traversal.
*/
function traverseAllChildren(children, callback, traverseContext) {
if (children !== null && children !== undefined) {
traverseAllChildrenImpl(children, '', 0, callback, traverseContext);
}
}
module.exports = traverseAllChildren;
},{"./ReactInstanceHandles":57,"./ReactTextComponent":76,"./invariant":121}],142:[function(require,module,exports){
/**
* Copyright 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 warning
*/
"use strict";
var emptyFunction = require("./emptyFunction");
/**
* Similar to invariant but only logs a warning if the condition is not met.
* This can be used to log issues in development environments in critical
* paths. Removing the logging code for production environments will keep the
* same logic and follow the same code paths.
*/
var warning = emptyFunction;
if ("production" !== "development") {
warning = function(condition, format ) {var args=Array.prototype.slice.call(arguments,2);
if (format === undefined) {
throw new Error(
'`warning(condition, format, ...args)` requires a warning ' +
'message argument'
);
}
if (!condition) {
var argIndex = 0;
console.warn('Warning: ' + format.replace(/%s/g, function() {return args[argIndex++];}));
}
};
}
module.exports = warning;
},{"./emptyFunction":108}]},{},[81])
(81)
});