11491 lines
352 KiB
JavaScript
11491 lines
352 KiB
JavaScript
/*
|
|
|
|
Copyright 2008-2013 Clipperz Srl
|
|
|
|
This file is part of Clipperz, the online password manager.
|
|
For further information about its features and functionalities please
|
|
refer to http://www.clipperz.com.
|
|
|
|
* Clipperz is free software: you can redistribute it and/or modify it
|
|
under the terms of the GNU Affero General Public License as published
|
|
by the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
* Clipperz is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
See the GNU Affero General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU Affero General Public
|
|
License along with Clipperz. If not, see http://www.gnu.org/licenses/.
|
|
|
|
*/
|
|
|
|
/**
|
|
* React v0.4.1
|
|
*/
|
|
(function(e){if("function"==typeof bootstrap)bootstrap("react",e);else if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeReact=e}else"undefined"!=typeof window?window.React=e():global.React=e()})(function(){var define,ses,bootstrap,module,exports;
|
|
return (function(e,t,n){function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&require;if(!s&&o)return o(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var u=t[n]={exports:{}};e[n][0].call(u.exports,function(t){var r=e[n][1][t];return i(r?r:t)},u,u.exports)}return t[n].exports}var r=typeof require=="function"&&require;for(var s=0;s<n.length;s++)i(n[s]);return i})({1:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 $
|
|
* @typechecks
|
|
*/
|
|
|
|
var ge = require("./ge");
|
|
var ex = require("./ex");
|
|
|
|
/**
|
|
* @param {string|DOMDocument|DOMElement|DOMTextNode} id
|
|
* @return {DOMDocument|DOMElement|DOMTextNode}
|
|
*
|
|
* Find a node by ID.
|
|
*
|
|
* If your application code depends on the existence of the element, use $,
|
|
* which will throw if the element doesn't exist.
|
|
*
|
|
* If you're not sure whether or not the element exists, use ge instead, and
|
|
* manually check for the element's existence in your application code.
|
|
*/
|
|
function $(id) {
|
|
var element = ge(id);
|
|
if (!element) {
|
|
throw new Error(ex(
|
|
'Tried to get element with id of "%s" but it is not present on the page.',
|
|
id
|
|
));
|
|
}
|
|
return element;
|
|
}
|
|
|
|
module.exports = $;
|
|
|
|
},{"./ex":68,"./ge":71}],2:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 = {
|
|
fillOpacity: true,
|
|
fontWeight: true,
|
|
opacity: true,
|
|
orphans: true,
|
|
zIndex: true,
|
|
zoom: true
|
|
};
|
|
|
|
/**
|
|
* 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;
|
|
|
|
},{}],3:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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":2,"./dangerousStyleValue":65,"./escapeTextForBrowser":67,"./hyphenate":76,"./memoizeStringOnly":83}],4:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 CallbackRegistry
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
var listenerBank = {};
|
|
|
|
/**
|
|
* Stores "listeners" by `registrationName`/`id`. There should be at most one
|
|
* "listener" per `registrationName`/`id` in the `listenerBank`.
|
|
*
|
|
* Access listeners via `listenerBank[registrationName][id]`.
|
|
*
|
|
* @class CallbackRegistry
|
|
* @internal
|
|
*/
|
|
var CallbackRegistry = {
|
|
|
|
/**
|
|
* 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) {
|
|
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];
|
|
}
|
|
},
|
|
|
|
/**
|
|
* This is needed for tests only. Do not use!
|
|
*/
|
|
__purge: function() {
|
|
listenerBank = {};
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = CallbackRegistry;
|
|
|
|
},{}],5:[function(require,module,exports){
|
|
(function(){/**
|
|
* Copyright 2013 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 SyntheticEvent = require("./SyntheticEvent");
|
|
|
|
var isEventSupported = require("./isEventSupported");
|
|
var keyOf = require("./keyOf");
|
|
|
|
var topLevelTypes = EventConstants.topLevelTypes;
|
|
|
|
var eventTypes = {
|
|
change: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({onChange: null}),
|
|
captured: keyOf({onChangeCapture: null})
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 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 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 change event and so we have to enqueue and
|
|
// process the abstract event manually.
|
|
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
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* @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 shouldUseInputEvent(elem) {
|
|
return (
|
|
(elem.nodeName === 'INPUT' && supportedInputTypes[elem.type]) ||
|
|
elem.nodeName === 'TEXTAREA'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* (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 `supportedInputTypes`)
|
|
* - 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 (shouldUseInputEvent(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":13,"./EventPluginHub":15,"./EventPropagators":18,"./ExecutionEnvironment":19,"./SyntheticEvent":51,"./isEventSupported":79,"./keyOf":82}],6:[function(require,module,exports){
|
|
(function(){/**
|
|
* Copyright 2013 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
|
|
*/
|
|
|
|
// Empty blocks improve readability so disable that warning
|
|
// jshint -W035
|
|
|
|
"use strict";
|
|
|
|
var Danger = require("./Danger");
|
|
|
|
var insertNodeAt = require("./insertNodeAt");
|
|
var keyOf = require("./keyOf");
|
|
var throwIf = require("./throwIf");
|
|
|
|
var NON_INCREASING_OPERATIONS;
|
|
if (true) {
|
|
NON_INCREASING_OPERATIONS =
|
|
'DOM child management operations must be provided in order ' +
|
|
'of increasing destination index. This is likely an issue with ' +
|
|
'the core framework.';
|
|
}
|
|
|
|
var MOVE_NODE_AT_ORIG_INDEX = keyOf({moveFrom: null});
|
|
var INSERT_MARKUP = keyOf({insertMarkup: null});
|
|
var REMOVE_AT = keyOf({removeAt: null});
|
|
|
|
/**
|
|
* In order to carry out movement of DOM nodes without knowing their IDs, we
|
|
* have to first store knowledge about nodes' original indices before beginning
|
|
* to carry out the sequence of operations. Once we begin the sequence, the DOM
|
|
* indices in future instructions are no longer valid.
|
|
*
|
|
* @param {Element} parent Parent DOM node.
|
|
* @param {Object} childOperations Description of child operations.
|
|
* @return {Array?} Sparse array containing elements by their current index in
|
|
* the DOM.
|
|
*/
|
|
var _getNodesByOriginalIndex = function(parent, childOperations) {
|
|
var nodesByOriginalIndex; // Sparse array.
|
|
var childOperation;
|
|
var origIndex;
|
|
for (var i = 0; i < childOperations.length; i++) {
|
|
childOperation = childOperations[i];
|
|
if (MOVE_NODE_AT_ORIG_INDEX in childOperation) {
|
|
nodesByOriginalIndex = nodesByOriginalIndex || [];
|
|
origIndex = childOperation.moveFrom;
|
|
nodesByOriginalIndex[origIndex] = parent.childNodes[origIndex];
|
|
} else if (REMOVE_AT in childOperation) {
|
|
nodesByOriginalIndex = nodesByOriginalIndex || [];
|
|
origIndex = childOperation.removeAt;
|
|
nodesByOriginalIndex[origIndex] = parent.childNodes[origIndex];
|
|
}
|
|
}
|
|
return nodesByOriginalIndex;
|
|
};
|
|
|
|
/**
|
|
* Removes DOM elements from their parent, or moved.
|
|
* @param {Element} parent Parent DOM node.
|
|
* @param {Array} nodesByOriginalIndex Child nodes by their original index
|
|
* (potentially sparse.)
|
|
*/
|
|
var _removeChildrenByOriginalIndex = function(parent, nodesByOriginalIndex) {
|
|
for (var j = 0; j < nodesByOriginalIndex.length; j++) {
|
|
var nodeToRemove = nodesByOriginalIndex[j];
|
|
if (nodeToRemove) { // We used a sparse array.
|
|
parent.removeChild(nodesByOriginalIndex[j]);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Once all nodes that will be removed or moved - are removed from the parent
|
|
* node, we can begin the process of placing nodes into their final locations.
|
|
* We must perform all operations in the order of the final DOM index -
|
|
* otherwise, we couldn't count on the fact that an insertion at index X, will
|
|
* remain at index X. This will iterate through the child operations, adding
|
|
* content where needed, skip over removals (they've already been removed) and
|
|
* insert "moved" Elements that were previously removed. The "moved" elements
|
|
* are only temporarily removed from the parent, so that index calculations can
|
|
* be manageable and perform well in the cases that matter.
|
|
*/
|
|
var _placeNodesAtDestination =
|
|
function(parent, childOperations, nodesByOriginalIndex) {
|
|
var origNode;
|
|
var finalIndex;
|
|
var lastFinalIndex = -1;
|
|
var childOperation;
|
|
for (var k = 0; k < childOperations.length; k++) {
|
|
childOperation = childOperations[k];
|
|
if (MOVE_NODE_AT_ORIG_INDEX in childOperation) {
|
|
origNode = nodesByOriginalIndex[childOperation.moveFrom];
|
|
finalIndex = childOperation.finalIndex;
|
|
insertNodeAt(parent, origNode, finalIndex);
|
|
if (true) {
|
|
throwIf(finalIndex <= lastFinalIndex, NON_INCREASING_OPERATIONS);
|
|
lastFinalIndex = finalIndex;
|
|
}
|
|
} else if (REMOVE_AT in childOperation) {
|
|
} else if (INSERT_MARKUP in childOperation) {
|
|
finalIndex = childOperation.finalIndex;
|
|
var markup = childOperation.insertMarkup;
|
|
Danger.dangerouslyInsertMarkupAt(parent, markup, finalIndex);
|
|
if (true) {
|
|
throwIf(finalIndex <= lastFinalIndex, NON_INCREASING_OPERATIONS);
|
|
lastFinalIndex = finalIndex;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
var manageChildren = function(parent, childOperations) {
|
|
var nodesByOriginalIndex = _getNodesByOriginalIndex(parent, childOperations);
|
|
if (nodesByOriginalIndex) {
|
|
_removeChildrenByOriginalIndex(parent, nodesByOriginalIndex);
|
|
}
|
|
_placeNodesAtDestination(parent, childOperations, nodesByOriginalIndex);
|
|
};
|
|
|
|
/**
|
|
* Also reexport all of the dangerous functions. It helps to have all dangerous
|
|
* functions located in a single module `Danger`.
|
|
*/
|
|
var DOMChildrenOperations = {
|
|
dangerouslyReplaceNodeWithMarkup: Danger.dangerouslyReplaceNodeWithMarkup,
|
|
manageChildren: manageChildren
|
|
};
|
|
|
|
module.exports = DOMChildrenOperations;
|
|
|
|
})()
|
|
},{"./Danger":9,"./insertNodeAt":77,"./keyOf":82,"./throwIf":89}],7:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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_BOOLEAN_VALUE: 0x4,
|
|
HAS_SIDE_EFFECTS: 0x8,
|
|
|
|
/**
|
|
* 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) {
|
|
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
|
|
);
|
|
|
|
DOMProperty.isStandardName[propName] = true;
|
|
|
|
DOMProperty.getAttributeName[propName] =
|
|
DOMAttributeNames[propName] || propName.toLowerCase();
|
|
|
|
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.hasBooleanValue[propName] =
|
|
propConfig & DOMPropertyInjection.HAS_BOOLEAN_VALUE;
|
|
DOMProperty.hasSideEffects[propName] =
|
|
propConfig & DOMPropertyInjection.HAS_SIDE_EFFECTS;
|
|
|
|
invariant(
|
|
!DOMProperty.mustUseAttribute[propName] ||
|
|
!DOMProperty.mustUseProperty[propName],
|
|
'DOMProperty: Cannot use require using both attribute and property: %s',
|
|
propName
|
|
);
|
|
invariant(
|
|
DOMProperty.mustUseProperty[propName] ||
|
|
!DOMProperty.hasSideEffects[propName],
|
|
'DOMProperty: Properties that have side effects must use property: %s',
|
|
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 = {
|
|
|
|
/**
|
|
* Checks whether a property name is a standard property.
|
|
* @type {Object}
|
|
*/
|
|
isStandardName: {},
|
|
|
|
/**
|
|
* 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 the property should be removed when set to a falsey value.
|
|
* @type {Object}
|
|
*/
|
|
hasBooleanValue: {},
|
|
|
|
/**
|
|
* 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: {},
|
|
|
|
/**
|
|
* 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":78}],8:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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");
|
|
|
|
var processAttributeNameAndPrefix = memoizeStringOnly(function(name) {
|
|
return escapeTextForBrowser(name) + '="';
|
|
});
|
|
|
|
/**
|
|
* Operations for dealing with DOM properties.
|
|
*/
|
|
var DOMPropertyOperations = {
|
|
|
|
/**
|
|
* 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 (value == null || DOMProperty.hasBooleanValue[name] && !value) {
|
|
return '';
|
|
}
|
|
var attributeName = DOMProperty.getAttributeName[name];
|
|
return processAttributeNameAndPrefix(attributeName) +
|
|
escapeTextForBrowser(value) + '"';
|
|
} else if (DOMProperty.isCustomAttribute(name)) {
|
|
if (value == null) {
|
|
return '';
|
|
}
|
|
return processAttributeNameAndPrefix(name) +
|
|
escapeTextForBrowser(value) + '"';
|
|
} else {
|
|
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 (DOMProperty.mustUseAttribute[name]) {
|
|
if (DOMProperty.hasBooleanValue[name] && !value) {
|
|
node.removeAttribute(DOMProperty.getAttributeName[name]);
|
|
} else {
|
|
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)) {
|
|
node.setAttribute(name, value);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 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];
|
|
node[propName] = DOMProperty.getDefaultValueForProperty(
|
|
node.nodeName,
|
|
name
|
|
);
|
|
}
|
|
} else if (DOMProperty.isCustomAttribute(name)) {
|
|
node.removeAttribute(name);
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = DOMPropertyOperations;
|
|
|
|
},{"./DOMProperty":7,"./escapeTextForBrowser":67,"./memoizeStringOnly":83}],9:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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
|
|
*/
|
|
|
|
/*jslint evil: true, sub: true */
|
|
|
|
"use strict";
|
|
|
|
var ExecutionEnvironment = require("./ExecutionEnvironment");
|
|
|
|
var throwIf = require("./throwIf");
|
|
|
|
var DOM_UNSUPPORTED;
|
|
var NO_MARKUP_PARENT;
|
|
var NO_MULTI_MARKUP;
|
|
if (true) {
|
|
DOM_UNSUPPORTED =
|
|
'You may not insert markup into the document while you are in a worker ' +
|
|
'thread. It\'s not you, it\'s me. This is likely the fault of the ' +
|
|
'framework. Please report this immediately.';
|
|
NO_MARKUP_PARENT =
|
|
'You have attempted to inject markup without a suitable parent. This is ' +
|
|
'likely the fault of the framework - please report immediately.';
|
|
NO_MULTI_MARKUP =
|
|
'The framework has attempted to either insert zero or multiple markup ' +
|
|
'roots into a single location when it should not. This is a serious ' +
|
|
'error - a fault of the framework - please report immediately.';
|
|
}
|
|
|
|
var validateMarkupParams;
|
|
if (true) {
|
|
validateMarkupParams = function(parentNode, markup) {
|
|
throwIf(!ExecutionEnvironment.canUseDOM, DOM_UNSUPPORTED);
|
|
throwIf(!parentNode || !parentNode.tagName, NO_MARKUP_PARENT);
|
|
throwIf(!markup, NO_MULTI_MARKUP);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Dummy container used to render all markup.
|
|
*/
|
|
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.
|
|
*/
|
|
var markupWrap = {
|
|
'option': [1, '<select multiple="true">', '</select>'],
|
|
'legend': [1, '<fieldset>', '</fieldset>'],
|
|
'area': [1, '<map>', '</map>'],
|
|
'param': [1, '<object>', '</object>'],
|
|
'thead': [1, '<table>', '</table>'],
|
|
'tr': [2, '<table><tbody>', '</tbody></table>'],
|
|
'col': [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>'],
|
|
'td': [3, '<table><tbody><tr>', '</tr></tbody></table>']
|
|
};
|
|
markupWrap['optgroup'] = markupWrap['option'];
|
|
markupWrap['tbody'] = markupWrap['thead'];
|
|
markupWrap['tfoot'] = markupWrap['thead'];
|
|
markupWrap['colgroup'] = markupWrap['thead'];
|
|
markupWrap['caption'] = markupWrap['thead'];
|
|
markupWrap['th'] = markupWrap['td'];
|
|
|
|
/**
|
|
* In IE8, certain elements cannot render alone, so wrap all elements.
|
|
*/
|
|
var defaultWrap = [1, '?<div>', '</div>'];
|
|
|
|
/**
|
|
* Feature detection, remove wraps that are unnecessary for the current browser.
|
|
*/
|
|
if (dummyNode) {
|
|
for (var nodeName in markupWrap) {
|
|
if (!markupWrap.hasOwnProperty(nodeName)) {
|
|
continue;
|
|
}
|
|
dummyNode.innerHTML = '<' + nodeName + '></' + nodeName + '>';
|
|
if (dummyNode.firstChild) {
|
|
markupWrap[nodeName] = null;
|
|
}
|
|
}
|
|
dummyNode.innerHTML = '<link />';
|
|
if (dummyNode.firstChild) {
|
|
defaultWrap = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Renders markup into nodes. The returned HTMLCollection is live and should be
|
|
* used immediately (or at least before the next invocation to `renderMarkup`).
|
|
*
|
|
* 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).
|
|
* @see http://jsperf.com/extract-nodename
|
|
*
|
|
* @param {string} markup
|
|
* @return {*} An HTMLCollection.
|
|
*/
|
|
function renderMarkup(markup) {
|
|
var node = dummyNode;
|
|
var nodeName = markup.substring(1, markup.indexOf(' '));
|
|
|
|
var wrap = markupWrap[nodeName.toLowerCase()] || defaultWrap;
|
|
if (wrap) {
|
|
node.innerHTML = wrap[1] + markup + wrap[2];
|
|
|
|
var wrapDepth = wrap[0];
|
|
while (wrapDepth--) {
|
|
node = node.lastChild;
|
|
}
|
|
} else {
|
|
node.innerHTML = markup;
|
|
}
|
|
return node.childNodes;
|
|
}
|
|
|
|
/**
|
|
* Inserts node after 'after'. If 'after' is null, inserts it after nothing,
|
|
* which is inserting it at the beginning.
|
|
*
|
|
* @param {Element} elem Parent element.
|
|
* @param {Element} insert Element to insert.
|
|
* @param {Element} after Element to insert after.
|
|
* @return {Element} Element that was inserted.
|
|
*/
|
|
function insertNodeAfterNode(elem, insert, after) {
|
|
if (true) {
|
|
throwIf(!ExecutionEnvironment.canUseDOM, DOM_UNSUPPORTED);
|
|
}
|
|
if (after) {
|
|
if (after.nextSibling) {
|
|
return elem.insertBefore(insert, after.nextSibling);
|
|
} else {
|
|
return elem.appendChild(insert);
|
|
}
|
|
} else {
|
|
return elem.insertBefore(insert, elem.firstChild);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Slow: Should only be used when it is known there are a few (or one) element
|
|
* in the node list.
|
|
* @param {Element} parentRootDomNode Parent element.
|
|
* @param {HTMLCollection} htmlCollection HTMLCollection to insert.
|
|
* @param {Element} after Element to insert the node list after.
|
|
*/
|
|
function inefficientlyInsertHTMLCollectionAfter(
|
|
parentRootDomNode,
|
|
htmlCollection,
|
|
after) {
|
|
|
|
if (true) {
|
|
throwIf(!ExecutionEnvironment.canUseDOM, DOM_UNSUPPORTED);
|
|
}
|
|
var ret;
|
|
var originalLength = htmlCollection.length;
|
|
// Access htmlCollection[0] because htmlCollection shrinks as we remove items.
|
|
// `insertNodeAfterNode` will remove items from the htmlCollection.
|
|
for (var i = 0; i < originalLength; i++) {
|
|
ret =
|
|
insertNodeAfterNode(parentRootDomNode, htmlCollection[0], ret || after);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Super-dangerously inserts markup into existing DOM structure. Seriously, you
|
|
* don't want to use this module unless you are building a framework. This
|
|
* requires that the markup that you are inserting represents the root of a
|
|
* tree. We do not support the case where there `markup` represents several
|
|
* roots.
|
|
*
|
|
* @param {Element} parentNode Parent DOM element.
|
|
* @param {string} markup Markup to dangerously insert.
|
|
* @param {number} index Position to insert markup at.
|
|
*/
|
|
function dangerouslyInsertMarkupAt(parentNode, markup, index) {
|
|
if (true) {
|
|
validateMarkupParams(parentNode, markup);
|
|
}
|
|
var htmlCollection = renderMarkup(markup);
|
|
var afterNode = index ? parentNode.childNodes[index - 1] : null;
|
|
inefficientlyInsertHTMLCollectionAfter(parentNode, htmlCollection, afterNode);
|
|
}
|
|
|
|
/**
|
|
* Replaces a node with a string of markup at its current position within its
|
|
* parent. `childNode` must be in the document (or at least within a parent
|
|
* node). The string of markup must represent a tree of markup with a single
|
|
* root.
|
|
*
|
|
* @param {Element} childNode Child node to replace.
|
|
* @param {string} markup Markup to dangerously replace child with.
|
|
*/
|
|
function dangerouslyReplaceNodeWithMarkup(childNode, markup) {
|
|
var parentNode = childNode.parentNode;
|
|
if (true) {
|
|
validateMarkupParams(parentNode, markup);
|
|
}
|
|
var htmlCollection = renderMarkup(markup);
|
|
if (true) {
|
|
throwIf(htmlCollection.length !== 1, NO_MULTI_MARKUP);
|
|
}
|
|
parentNode.replaceChild(htmlCollection[0], childNode);
|
|
}
|
|
|
|
var Danger = {
|
|
dangerouslyInsertMarkupAt: dangerouslyInsertMarkupAt,
|
|
dangerouslyReplaceNodeWithMarkup: dangerouslyReplaceNodeWithMarkup
|
|
};
|
|
|
|
module.exports = Danger;
|
|
|
|
},{"./ExecutionEnvironment":19,"./throwIf":89}],10:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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
|
|
*/
|
|
|
|
"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 DefaultDOMPropertyConfig = {
|
|
isCustomAttribute: RegExp.prototype.test.bind(
|
|
/^(data|aria)-[a-z_][a-z\d_.\-]*$/
|
|
),
|
|
Properties: {
|
|
/**
|
|
* Standard Properties
|
|
*/
|
|
accessKey: null,
|
|
accept: null,
|
|
action: null,
|
|
ajaxify: MUST_USE_ATTRIBUTE,
|
|
allowFullScreen: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE,
|
|
allowTransparency: MUST_USE_ATTRIBUTE,
|
|
alt: null,
|
|
autoComplete: null,
|
|
autoFocus: HAS_BOOLEAN_VALUE,
|
|
autoPlay: HAS_BOOLEAN_VALUE,
|
|
cellPadding: null,
|
|
cellSpacing: null,
|
|
checked: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
|
|
className: MUST_USE_PROPERTY,
|
|
colSpan: null,
|
|
contentEditable: null,
|
|
contextMenu: MUST_USE_ATTRIBUTE,
|
|
controls: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
|
|
data: null, // For `<object />` acts as `src`.
|
|
dateTime: MUST_USE_ATTRIBUTE,
|
|
dir: null,
|
|
disabled: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
|
|
draggable: null,
|
|
encType: null,
|
|
frameBorder: MUST_USE_ATTRIBUTE,
|
|
height: MUST_USE_ATTRIBUTE,
|
|
hidden: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE,
|
|
href: null,
|
|
htmlFor: null,
|
|
icon: null,
|
|
id: MUST_USE_PROPERTY,
|
|
label: null,
|
|
lang: null,
|
|
list: null,
|
|
max: null,
|
|
maxLength: MUST_USE_ATTRIBUTE,
|
|
method: null,
|
|
min: null,
|
|
multiple: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
|
|
name: null,
|
|
pattern: null,
|
|
poster: null,
|
|
preload: null,
|
|
placeholder: null,
|
|
radioGroup: null,
|
|
rel: null,
|
|
readOnly: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
|
|
required: HAS_BOOLEAN_VALUE,
|
|
role: MUST_USE_ATTRIBUTE,
|
|
scrollLeft: MUST_USE_PROPERTY,
|
|
scrollTop: MUST_USE_PROPERTY,
|
|
selected: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
|
|
size: null,
|
|
spellCheck: null,
|
|
src: null,
|
|
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,
|
|
/**
|
|
* SVG Properties
|
|
*/
|
|
cx: MUST_USE_PROPERTY,
|
|
cy: MUST_USE_PROPERTY,
|
|
d: MUST_USE_PROPERTY,
|
|
fill: MUST_USE_PROPERTY,
|
|
fx: MUST_USE_PROPERTY,
|
|
fy: MUST_USE_PROPERTY,
|
|
points: MUST_USE_PROPERTY,
|
|
r: MUST_USE_PROPERTY,
|
|
stroke: MUST_USE_PROPERTY,
|
|
strokeLinecap: MUST_USE_PROPERTY,
|
|
strokeWidth: MUST_USE_PROPERTY,
|
|
transform: MUST_USE_PROPERTY,
|
|
x: MUST_USE_PROPERTY,
|
|
x1: MUST_USE_PROPERTY,
|
|
x2: MUST_USE_PROPERTY,
|
|
version: MUST_USE_PROPERTY,
|
|
viewBox: MUST_USE_PROPERTY,
|
|
y: MUST_USE_PROPERTY,
|
|
y1: MUST_USE_PROPERTY,
|
|
y2: MUST_USE_PROPERTY,
|
|
spreadMethod: MUST_USE_PROPERTY,
|
|
offset: MUST_USE_PROPERTY,
|
|
stopColor: MUST_USE_PROPERTY,
|
|
stopOpacity: MUST_USE_PROPERTY,
|
|
gradientUnits: MUST_USE_PROPERTY,
|
|
gradientTransform: MUST_USE_PROPERTY
|
|
},
|
|
DOMAttributeNames: {
|
|
className: 'class',
|
|
htmlFor: 'for',
|
|
strokeLinecap: 'stroke-linecap',
|
|
strokeWidth: 'stroke-width',
|
|
stopColor: 'stop-color',
|
|
stopOpacity: 'stop-opacity'
|
|
},
|
|
DOMPropertyNames: {
|
|
autoComplete: 'autocomplete',
|
|
autoFocus: 'autofocus',
|
|
autoPlay: 'autoplay',
|
|
encType: 'enctype',
|
|
radioGroup: 'radiogroup',
|
|
spellCheck: 'spellcheck'
|
|
},
|
|
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":7}],11:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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({AnalyticsEventPlugin: null}),
|
|
keyOf({MobileSafariClickEventPlugin: null})
|
|
];
|
|
|
|
module.exports = DefaultEventPluginOrder;
|
|
|
|
},{"./keyOf":82}],12:[function(require,module,exports){
|
|
(function(){/**
|
|
* Copyright 2013 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 ExecutionEnvironment = require("./ExecutionEnvironment");
|
|
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})},
|
|
mouseLeave: {registrationName: keyOf({onMouseLeave: 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 from, to;
|
|
if (topLevelType === topLevelTypes.topMouseOut) {
|
|
from = topLevelTarget;
|
|
to =
|
|
getFirstReactDOM(nativeEvent.relatedTarget || nativeEvent.toElement) ||
|
|
ExecutionEnvironment.global;
|
|
} else {
|
|
from = ExecutionEnvironment.global;
|
|
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
|
|
);
|
|
var enter = SyntheticMouseEvent.getPooled(
|
|
eventTypes.mouseEnter,
|
|
toID,
|
|
nativeEvent
|
|
);
|
|
|
|
EventPropagators.accumulateEnterLeaveDispatches(leave, enter, fromID, toID);
|
|
return [leave, enter];
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = EnterLeaveEventPlugin;
|
|
|
|
})()
|
|
},{"./EventConstants":13,"./EventPropagators":18,"./ExecutionEnvironment":19,"./ReactMount":39,"./SyntheticMouseEvent":54,"./keyOf":82}],13:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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,
|
|
topDOMCharacterDataModified: null,
|
|
topDoubleClick: null,
|
|
topDrag: null,
|
|
topDragEnd: null,
|
|
topDragEnter: null,
|
|
topDragExit: null,
|
|
topDragLeave: null,
|
|
topDragOver: null,
|
|
topDragStart: null,
|
|
topDrop: null,
|
|
topFocus: null,
|
|
topInput: null,
|
|
topKeyDown: null,
|
|
topKeyPress: null,
|
|
topKeyUp: null,
|
|
topMouseDown: null,
|
|
topMouseMove: null,
|
|
topMouseOut: null,
|
|
topMouseOver: null,
|
|
topMouseUp: 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":81}],14:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 EventListener
|
|
*/
|
|
|
|
/**
|
|
* Upstream version of event listener. Does not take into account specific
|
|
* nature of platform.
|
|
*/
|
|
var EventListener = {
|
|
/**
|
|
* Listens to bubbled events on a DOM node.
|
|
*
|
|
* @param {Element} el DOM element to register listener on.
|
|
* @param {string} handlerBaseName 'click'/'mouseover'
|
|
* @param {Function!} cb Callback function
|
|
*/
|
|
listen: function(el, handlerBaseName, cb) {
|
|
if (el.addEventListener) {
|
|
el.addEventListener(handlerBaseName, cb, false);
|
|
} else if (el.attachEvent) {
|
|
el.attachEvent('on' + handlerBaseName, cb);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Listens to captured events on a DOM node.
|
|
*
|
|
* @see `EventListener.listen` for params.
|
|
* @throws Exception if addEventListener is not supported.
|
|
*/
|
|
capture: function(el, handlerBaseName, cb) {
|
|
if (!el.addEventListener) {
|
|
if (true) {
|
|
console.error(
|
|
'You are attempting to use addEventlistener ' +
|
|
'in a browser that does not support it support it.' +
|
|
'This likely means that you will not receive events that ' +
|
|
'your application relies on (such as scroll).');
|
|
}
|
|
return;
|
|
} else {
|
|
el.addEventListener(handlerBaseName, cb, true);
|
|
}
|
|
}
|
|
};
|
|
|
|
module.exports = EventListener;
|
|
|
|
},{}],15:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 CallbackRegistry = require("./CallbackRegistry");
|
|
var EventPluginRegistry = require("./EventPluginRegistry");
|
|
var EventPluginUtils = require("./EventPluginUtils");
|
|
var EventPropagators = require("./EventPropagators");
|
|
var ExecutionEnvironment = require("./ExecutionEnvironment");
|
|
|
|
var accumulate = require("./accumulate");
|
|
var forEachAccumulated = require("./forEachAccumulated");
|
|
var invariant = require("./invariant");
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 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} InjectedInstanceHandle
|
|
* @public
|
|
*/
|
|
injectInstanceHandle: EventPropagators.injection.injectInstanceHandle,
|
|
|
|
/**
|
|
* @param {array} InjectedEventPluginOrder
|
|
* @public
|
|
*/
|
|
injectEventPluginOrder: EventPluginRegistry.injectEventPluginOrder,
|
|
|
|
/**
|
|
* @param {object} injectedNamesToPlugins Map from names to plugin modules.
|
|
*/
|
|
injectEventPluginsByName: EventPluginRegistry.injectEventPluginsByName
|
|
|
|
},
|
|
|
|
registrationNames: EventPluginRegistry.registrationNames,
|
|
|
|
putListener: CallbackRegistry.putListener,
|
|
|
|
getListener: CallbackRegistry.getListener,
|
|
|
|
deleteListener: CallbackRegistry.deleteListener,
|
|
|
|
deleteAllListeners: CallbackRegistry.deleteAllListeners,
|
|
|
|
/**
|
|
* 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);
|
|
invariant(
|
|
!eventQueue,
|
|
'processEventQueue(): Additional events were enqueued while processing ' +
|
|
'an event queue. Support for this has not yet been implemented.'
|
|
);
|
|
}
|
|
|
|
};
|
|
|
|
if (ExecutionEnvironment.canUseDOM) {
|
|
window.EventPluginHub = EventPluginHub;
|
|
}
|
|
|
|
module.exports = EventPluginHub;
|
|
|
|
},{"./CallbackRegistry":4,"./EventPluginRegistry":16,"./EventPluginUtils":17,"./EventPropagators":18,"./ExecutionEnvironment":19,"./accumulate":61,"./forEachAccumulated":70,"./invariant":78}],16:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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);
|
|
invariant(
|
|
pluginIndex > -1,
|
|
'EventPluginRegistry: Cannot inject event plugins that do not exist in ' +
|
|
'the plugin ordering, `%s`.',
|
|
pluginName
|
|
);
|
|
if (EventPluginRegistry.plugins[pluginIndex]) {
|
|
continue;
|
|
}
|
|
invariant(
|
|
PluginModule.extractEvents,
|
|
'EventPluginRegistry: Event plugins must implement an `extractEvents` ' +
|
|
'method, but `%s` does not.',
|
|
pluginName
|
|
);
|
|
EventPluginRegistry.plugins[pluginIndex] = PluginModule;
|
|
var publishedEvents = PluginModule.eventTypes;
|
|
for (var eventName in publishedEvents) {
|
|
invariant(
|
|
publishEventForPlugin(publishedEvents[eventName], PluginModule),
|
|
'EventPluginRegistry: Failed to publish event `%s` for plugin `%s`.',
|
|
eventName,
|
|
pluginName
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
var phasedRegistrationNames = dispatchConfig.phasedRegistrationNames;
|
|
if (phasedRegistrationNames) {
|
|
for (var phaseName in phasedRegistrationNames) {
|
|
if (phasedRegistrationNames.hasOwnProperty(phaseName)) {
|
|
var phasedRegistrationName = phasedRegistrationNames[phaseName];
|
|
publishRegistrationName(phasedRegistrationName, PluginModule);
|
|
}
|
|
}
|
|
return true;
|
|
} else if (dispatchConfig.registrationName) {
|
|
publishRegistrationName(dispatchConfig.registrationName, PluginModule);
|
|
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) {
|
|
invariant(
|
|
!EventPluginRegistry.registrationNames[registrationName],
|
|
'EventPluginHub: More than one plugin attempted to publish the same ' +
|
|
'registration name, `%s`.',
|
|
registrationName
|
|
);
|
|
EventPluginRegistry.registrationNames[registrationName] = PluginModule;
|
|
EventPluginRegistry.registrationNamesKeys.push(registrationName);
|
|
}
|
|
|
|
/**
|
|
* Registers plugins so that they can extract and dispatch events.
|
|
*
|
|
* @see {EventPluginHub}
|
|
*/
|
|
var EventPluginRegistry = {
|
|
|
|
/**
|
|
* Ordered list of injected plugins.
|
|
*/
|
|
plugins: [],
|
|
|
|
/**
|
|
* Mapping from registration names to plugin modules.
|
|
*/
|
|
registrationNames: {},
|
|
|
|
/**
|
|
* The keys of `registrationNames`.
|
|
*/
|
|
registrationNamesKeys: [],
|
|
|
|
/**
|
|
* 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) {
|
|
invariant(
|
|
!EventPluginOrder,
|
|
'EventPluginRegistry: Cannot inject event plugin ordering more than once.'
|
|
);
|
|
// 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) {
|
|
invariant(
|
|
!namesToPlugins[pluginName],
|
|
'EventPluginRegistry: Cannot inject two different event plugins ' +
|
|
'using the same name, `%s`.',
|
|
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.registrationNames[
|
|
dispatchConfig.registrationName
|
|
] || null;
|
|
}
|
|
for (var phase in dispatchConfig.phasedRegistrationNames) {
|
|
if (!dispatchConfig.phasedRegistrationNames.hasOwnProperty(phase)) {
|
|
continue;
|
|
}
|
|
var PluginModule = EventPluginRegistry.registrationNames[
|
|
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 registrationNames = EventPluginRegistry.registrationNames;
|
|
for (var registrationName in registrationNames) {
|
|
if (registrationNames.hasOwnProperty(registrationName)) {
|
|
delete registrationNames[registrationName];
|
|
}
|
|
}
|
|
EventPluginRegistry.registrationNamesKeys.length = 0;
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = EventPluginRegistry;
|
|
|
|
},{"./invariant":78}],17:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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");
|
|
|
|
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 (true) {
|
|
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;
|
|
|
|
invariant(
|
|
idsIsArr === listenersIsArr && IDsLen === listenersLen,
|
|
'EventPluginUtils: Invalid `event`.'
|
|
);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 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 (true) {
|
|
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) {
|
|
listener(event, domID);
|
|
}
|
|
|
|
/**
|
|
* 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 (true) {
|
|
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 (true) {
|
|
validateEventDispatches(event);
|
|
}
|
|
var dispatchListener = event._dispatchListeners;
|
|
var dispatchID = event._dispatchIDs;
|
|
invariant(
|
|
!Array.isArray(dispatchListener),
|
|
'executeDirectDispatch(...): Invalid `event`.'
|
|
);
|
|
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,
|
|
executeDispatchesInOrder: executeDispatchesInOrder,
|
|
executeDispatchesInOrderStopAtTrue: executeDispatchesInOrderStopAtTrue,
|
|
executeDirectDispatch: executeDirectDispatch,
|
|
hasDispatches: hasDispatches,
|
|
executeDispatch: executeDispatch
|
|
};
|
|
|
|
module.exports = EventPluginUtils;
|
|
|
|
},{"./EventConstants":13,"./invariant":78}],18:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 CallbackRegistry = require("./CallbackRegistry");
|
|
var EventConstants = require("./EventConstants");
|
|
|
|
var accumulate = require("./accumulate");
|
|
var forEachAccumulated = require("./forEachAccumulated");
|
|
var getListener = CallbackRegistry.getListener;
|
|
var PropagationPhases = EventConstants.PropagationPhases;
|
|
|
|
/**
|
|
* Injected dependencies:
|
|
*/
|
|
|
|
/**
|
|
* - `InstanceHandle`: [required] Module that performs logical traversals of DOM
|
|
* hierarchy given ids of the logical DOM elements involved.
|
|
*/
|
|
var injection = {
|
|
InstanceHandle: null,
|
|
injectInstanceHandle: function(InjectedInstanceHandle) {
|
|
injection.InstanceHandle = InjectedInstanceHandle;
|
|
if (true) {
|
|
injection.validate();
|
|
}
|
|
},
|
|
validate: function() {
|
|
var invalid = !injection.InstanceHandle||
|
|
!injection.InstanceHandle.traverseTwoPhase ||
|
|
!injection.InstanceHandle.traverseEnterLeave;
|
|
if (invalid) {
|
|
throw new Error('InstanceHandle not injected before use!');
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 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 (true) {
|
|
if (!domID) {
|
|
throw new Error('Dispatching id must not be null');
|
|
}
|
|
injection.validate();
|
|
}
|
|
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) {
|
|
injection.InstanceHandle.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) {
|
|
if (true) {
|
|
injection.validate();
|
|
}
|
|
forEachAccumulated(events, accumulateTwoPhaseDispatchesSingle);
|
|
}
|
|
|
|
function accumulateEnterLeaveDispatches(leave, enter, fromID, toID) {
|
|
if (true) {
|
|
injection.validate();
|
|
}
|
|
injection.InstanceHandle.traverseEnterLeave(
|
|
fromID,
|
|
toID,
|
|
accumulateDispatches,
|
|
leave,
|
|
enter
|
|
);
|
|
}
|
|
|
|
|
|
function accumulateDirectDispatches(events) {
|
|
if (true) {
|
|
injection.validate();
|
|
}
|
|
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,
|
|
injection: injection
|
|
};
|
|
|
|
module.exports = EventPropagators;
|
|
|
|
},{"./CallbackRegistry":4,"./EventConstants":13,"./accumulate":61,"./forEachAccumulated":70}],19:[function(require,module,exports){
|
|
(function(){/**
|
|
* Copyright 2013 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',
|
|
|
|
isInWorker: !canUseDOM, // For now, this is true - might change in the future.
|
|
|
|
global: new Function('return this;')()
|
|
|
|
};
|
|
|
|
module.exports = ExecutionEnvironment;
|
|
|
|
})()
|
|
},{}],20:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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":13,"./emptyFunction":66}],21:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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";
|
|
|
|
/**
|
|
* 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;
|
|
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;
|
|
|
|
},{}],22:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 ReactCompositeComponent = require("./ReactCompositeComponent");
|
|
var ReactComponent = require("./ReactComponent");
|
|
var ReactDOM = require("./ReactDOM");
|
|
var ReactMount = require("./ReactMount");
|
|
var ReactPropTypes = require("./ReactPropTypes");
|
|
var ReactServerRendering = require("./ReactServerRendering");
|
|
|
|
var ReactDefaultInjection = require("./ReactDefaultInjection");
|
|
|
|
ReactDefaultInjection.inject();
|
|
|
|
var React = {
|
|
DOM: ReactDOM,
|
|
PropTypes: ReactPropTypes,
|
|
initializeTouchEvents: function(shouldUseTouch) {
|
|
ReactMount.useTouchEvents = shouldUseTouch;
|
|
},
|
|
autoBind: ReactCompositeComponent.autoBind,
|
|
createClass: ReactCompositeComponent.createClass,
|
|
constructAndRenderComponent: ReactMount.constructAndRenderComponent,
|
|
constructAndRenderComponentByID: ReactMount.constructAndRenderComponentByID,
|
|
renderComponent: ReactMount.renderComponent,
|
|
renderComponentToString: ReactServerRendering.renderComponentToString,
|
|
unmountAndReleaseReactRootNode: ReactMount.unmountAndReleaseReactRootNode,
|
|
isValidComponent: ReactComponent.isValidComponent
|
|
};
|
|
|
|
module.exports = React;
|
|
|
|
},{"./ReactComponent":23,"./ReactCompositeComponent":24,"./ReactDOM":26,"./ReactDefaultInjection":33,"./ReactMount":39,"./ReactPropTypes":45,"./ReactServerRendering":47}],23:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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
|
|
*/
|
|
|
|
/*jslint evil: true */
|
|
|
|
"use strict";
|
|
|
|
var getReactRootElementInContainer = require("./getReactRootElementInContainer");
|
|
var ReactCurrentOwner = require("./ReactCurrentOwner");
|
|
var ReactDOMIDOperations = require("./ReactDOMIDOperations");
|
|
var ReactMarkupChecksum = require("./ReactMarkupChecksum");
|
|
var ReactMount = require("./ReactMount");
|
|
var ReactOwner = require("./ReactOwner");
|
|
var ReactReconcileTransaction = require("./ReactReconcileTransaction");
|
|
var ReactUpdates = require("./ReactUpdates");
|
|
|
|
var invariant = require("./invariant");
|
|
var keyMirror = require("./keyMirror");
|
|
var merge = require("./merge");
|
|
|
|
/**
|
|
* Prop key that references a component's owner.
|
|
* @private
|
|
*/
|
|
var OWNER = '{owner}';
|
|
|
|
/**
|
|
* Props key that determines if a component's key was already validated.
|
|
* @private
|
|
*/
|
|
var IS_KEY_VALIDATED = '{is.key.validated}';
|
|
|
|
/**
|
|
* 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.
|
|
* This allows us to keep track of children between updates.
|
|
*/
|
|
|
|
var ownerHasWarned = {};
|
|
|
|
/**
|
|
* 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 hasn'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[IS_KEY_VALIDATED] || component.props.key != null) {
|
|
return;
|
|
}
|
|
component[IS_KEY_VALIDATED] = 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 (ownerHasWarned.hasOwnProperty(currentName)) {
|
|
return;
|
|
}
|
|
ownerHasWarned[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.props[OWNER] && component.props[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 + '.';
|
|
}
|
|
|
|
console.warn(message);
|
|
}
|
|
|
|
/**
|
|
* Ensure that every component either is passed in a static location or, if
|
|
* if it's passed in an array, has an explicit key property defined.
|
|
*
|
|
* @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[IS_KEY_VALIDATED] = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* `receiveProps`
|
|
* Updates the rendered DOM nodes given a new set of props.
|
|
*
|
|
* `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) {
|
|
return !!(
|
|
object &&
|
|
typeof object.mountComponentIntoNode === 'function' &&
|
|
typeof object.receiveProps === 'function'
|
|
);
|
|
},
|
|
|
|
/**
|
|
* 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}
|
|
* @internal
|
|
*/
|
|
getKey: function(component, index) {
|
|
if (component && component.props && component.props.key != null) {
|
|
// Explicit key
|
|
return '' + component.props.key;
|
|
}
|
|
// Implicit key determined by the index in the set
|
|
return '' + index;
|
|
},
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
LifeCycle: ComponentLifeCycle,
|
|
|
|
/**
|
|
* React references `ReactDOMIDOperations` using this property in order to
|
|
* allow dependency injection.
|
|
*
|
|
* @internal
|
|
*/
|
|
DOMIDOperations: ReactDOMIDOperations,
|
|
|
|
/**
|
|
* React references `ReactReconcileTransaction` using this property in order
|
|
* to allow dependency injection.
|
|
*
|
|
* @internal
|
|
*/
|
|
ReactReconcileTransaction: ReactReconcileTransaction,
|
|
|
|
/**
|
|
* @param {object} DOMIDOperations
|
|
* @final
|
|
*/
|
|
setDOMOperations: function(DOMIDOperations) {
|
|
ReactComponent.DOMIDOperations = DOMIDOperations;
|
|
},
|
|
|
|
/**
|
|
* @param {Transaction} ReactReconcileTransaction
|
|
* @final
|
|
*/
|
|
setReactReconcileTransaction: function(ReactReconcileTransaction) {
|
|
ReactComponent.ReactReconcileTransaction = ReactReconcileTransaction;
|
|
},
|
|
|
|
/**
|
|
* Base functionality for every ReactComponent constructor.
|
|
*
|
|
* @lends {ReactComponent.prototype}
|
|
*/
|
|
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;
|
|
},
|
|
|
|
/**
|
|
* Returns the DOM node rendered by this component.
|
|
*
|
|
* @return {DOMElement} The root node of this component.
|
|
* @final
|
|
* @protected
|
|
*/
|
|
getDOMNode: function() {
|
|
invariant(
|
|
this.isMounted(),
|
|
'getDOMNode(): A component must be mounted to have a DOM node.'
|
|
);
|
|
return ReactMount.getNode(this._rootNodeID);
|
|
},
|
|
|
|
/**
|
|
* 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) {
|
|
invariant(
|
|
!this.props[OWNER],
|
|
'replaceProps(...): You called `setProps` or `replaceProps` on a ' +
|
|
'component with an owner. 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.'
|
|
);
|
|
this._pendingProps = props;
|
|
ReactUpdates.enqueueUpdate(this, callback);
|
|
},
|
|
|
|
/**
|
|
* Base constructor for all React component.
|
|
*
|
|
* 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.props[OWNER] = ReactCurrentOwner.current;
|
|
// All components start unmounted.
|
|
this._lifeCycleState = ComponentLifeCycle.UNMOUNTED;
|
|
|
|
this._pendingProps = null;
|
|
this._pendingCallbacks = null;
|
|
|
|
// Children can be more than one argument
|
|
var childrenLength = arguments.length - 1;
|
|
if (childrenLength === 1) {
|
|
if (true) {
|
|
validateChildKeys(children);
|
|
}
|
|
this.props.children = children;
|
|
} else if (childrenLength > 1) {
|
|
var childArray = Array(childrenLength);
|
|
for (var i = 0; i < childrenLength; i++) {
|
|
if (true) {
|
|
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
|
|
* @return {?string} Rendered markup to be inserted into the DOM.
|
|
* @internal
|
|
*/
|
|
mountComponent: function(rootID, transaction) {
|
|
invariant(
|
|
!this.isMounted(),
|
|
'mountComponent(%s, ...): Can only mount an unmounted component.',
|
|
rootID
|
|
);
|
|
var props = this.props;
|
|
if (props.ref != null) {
|
|
ReactOwner.addComponentAsRefTo(this, props.ref, props[OWNER]);
|
|
}
|
|
this._rootNodeID = rootID;
|
|
this._lifeCycleState = ComponentLifeCycle.MOUNTED;
|
|
// 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() {
|
|
invariant(
|
|
this.isMounted(),
|
|
'unmountComponent(): Can only unmount a mounted component.'
|
|
);
|
|
var props = this.props;
|
|
if (props.ref != null) {
|
|
ReactOwner.removeComponentAsRefFrom(this, props.ref, props[OWNER]);
|
|
}
|
|
ReactMount.purgeID(this._rootNodeID);
|
|
this._rootNodeID = null;
|
|
this._lifeCycleState = ComponentLifeCycle.UNMOUNTED;
|
|
},
|
|
|
|
/**
|
|
* Updates the rendered DOM nodes given a new set of props.
|
|
*
|
|
* Subclasses that override this method should make sure to invoke
|
|
* `ReactComponent.Mixin.receiveProps.call(this, ...)`.
|
|
*
|
|
* @param {object} nextProps Next set of properties.
|
|
* @param {ReactReconcileTransaction} transaction
|
|
* @internal
|
|
*/
|
|
receiveProps: function(nextProps, transaction) {
|
|
invariant(
|
|
this.isMounted(),
|
|
'receiveProps(...): Can only update a mounted component.'
|
|
);
|
|
this._pendingProps = nextProps;
|
|
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;
|
|
this.props = this._pendingProps;
|
|
this._pendingProps = null;
|
|
this.updateComponent(transaction, prevProps);
|
|
},
|
|
|
|
/**
|
|
* Updates the component's currently mounted representation.
|
|
*
|
|
* @param {ReactReconcileTransaction} transaction
|
|
* @param {object} prevProps
|
|
* @internal
|
|
*/
|
|
updateComponent: function(transaction, prevProps) {
|
|
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 (props[OWNER] !== prevProps[OWNER] || props.ref !== prevProps.ref) {
|
|
if (prevProps.ref != null) {
|
|
ReactOwner.removeComponentAsRefFrom(
|
|
this, prevProps.ref, prevProps[OWNER]
|
|
);
|
|
}
|
|
// Correct, even if the owner is the same, and only the ref has changed.
|
|
if (props.ref != null) {
|
|
ReactOwner.addComponentAsRefTo(this, props.ref, props[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) {
|
|
invariant(
|
|
container && container.nodeType === 1,
|
|
'mountComponentIntoNode(...): Target container is not a DOM element.'
|
|
);
|
|
var markup = this.mountComponent(rootID, transaction);
|
|
|
|
if (shouldReuseMarkup) {
|
|
if (ReactMarkupChecksum.canReuseMarkup(
|
|
markup,
|
|
getReactRootElementInContainer(container))) {
|
|
return;
|
|
} else {
|
|
if (true) {
|
|
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.'
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Unmounts this component and removes it from the DOM.
|
|
*
|
|
* @param {DOMElement} container DOM element to unmount from.
|
|
* @final
|
|
* @internal
|
|
* @see {ReactMount.unmountAndReleaseReactRootNode}
|
|
*/
|
|
unmountComponentFromNode: function(container) {
|
|
this.unmountComponent();
|
|
// http://jsperf.com/emptying-a-node
|
|
while (container.lastChild) {
|
|
container.removeChild(container.lastChild);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 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.props[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.props[OWNER];
|
|
if (!owner || !owner.refs) {
|
|
return null;
|
|
}
|
|
return owner.refs[ref];
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = ReactComponent;
|
|
|
|
},{"./ReactCurrentOwner":25,"./ReactDOMIDOperations":28,"./ReactMarkupChecksum":38,"./ReactMount":39,"./ReactOwner":43,"./ReactReconcileTransaction":46,"./ReactUpdates":49,"./getReactRootElementInContainer":73,"./invariant":78,"./keyMirror":81,"./merge":84}],24:[function(require,module,exports){
|
|
(function(){/**
|
|
* Copyright 2013 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 ReactCurrentOwner = require("./ReactCurrentOwner");
|
|
var ReactOwner = require("./ReactOwner");
|
|
var ReactPropTransferer = require("./ReactPropTransferer");
|
|
var ReactUpdates = require("./ReactUpdates");
|
|
|
|
var invariant = require("./invariant");
|
|
var keyMirror = require("./keyMirror");
|
|
var merge = require("./merge");
|
|
var mixInto = require("./mixInto");
|
|
|
|
/**
|
|
* 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
|
|
});
|
|
|
|
/**
|
|
* 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,
|
|
|
|
/**
|
|
* Definition of prop types for this component.
|
|
*
|
|
* @type {object}
|
|
* @optional
|
|
*/
|
|
propTypes: SpecPolicy.DEFINE_ONCE,
|
|
|
|
|
|
|
|
// ==== 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_ONCE,
|
|
|
|
/**
|
|
* 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_ONCE,
|
|
|
|
/**
|
|
* 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) {
|
|
* 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 and state.
|
|
*
|
|
* Use this as an opportunity to `return false` when you're certain that the
|
|
* transition to the new props and state will not require a component update.
|
|
*
|
|
* shouldComponentUpdate: function(nextProps, nextState) {
|
|
* return !equal(nextProps, this.props) || !equal(nextState, this.state);
|
|
* }
|
|
*
|
|
* @param {object} nextProps
|
|
* @param {?object} nextState
|
|
* @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` and `this.state` to `nextProps` and `nextState`.
|
|
*
|
|
* 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 {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 {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 in the specification when defining classes
|
|
* using `React.createClass`, they will not be on the component's prototype.
|
|
*/
|
|
var RESERVED_SPEC_KEYS = {
|
|
displayName: function(Constructor, displayName) {
|
|
Constructor.displayName = displayName;
|
|
},
|
|
mixins: function(Constructor, mixins) {
|
|
if (mixins) {
|
|
for (var i = 0; i < mixins.length; i++) {
|
|
mixSpecIntoComponent(Constructor, mixins[i]);
|
|
}
|
|
}
|
|
},
|
|
propTypes: function(Constructor, propTypes) {
|
|
Constructor.propTypes = propTypes;
|
|
}
|
|
};
|
|
|
|
function validateMethodOverride(proto, name) {
|
|
var specPolicy = ReactCompositeComponentInterface[name];
|
|
|
|
// Disallow overriding of base class methods unless explicitly allowed.
|
|
if (ReactCompositeComponentMixin.hasOwnProperty(name)) {
|
|
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
|
|
);
|
|
}
|
|
|
|
// Disallow defining methods more than once unless explicitly allowed.
|
|
if (proto.hasOwnProperty(name)) {
|
|
invariant(
|
|
specPolicy === SpecPolicy.DEFINE_MANY,
|
|
'ReactCompositeComponentInterface: You are attempting to define ' +
|
|
'`%s` on your component more than once. This conflict may be due ' +
|
|
'to a mixin.',
|
|
name
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
function validateLifeCycleOnReplaceState(instance) {
|
|
var compositeLifeCycleState = instance._compositeLifeCycleState;
|
|
invariant(
|
|
instance.isMounted() ||
|
|
compositeLifeCycleState === CompositeLifeCycle.MOUNTING,
|
|
'replaceState(...): Can only update a mounted or mounting component.'
|
|
);
|
|
invariant(
|
|
compositeLifeCycleState !== CompositeLifeCycle.RECEIVING_STATE &&
|
|
compositeLifeCycleState !== CompositeLifeCycle.UNMOUNTING,
|
|
'replaceState(...): Cannot update while unmounting component or during ' +
|
|
'an existing state transition (such as within `render`).'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Custom version of `mixInto` which handles policy validation and reserved
|
|
* specification keys when building `ReactCompositeComponent` classses.
|
|
*/
|
|
function mixSpecIntoComponent(Constructor, spec) {
|
|
var proto = Constructor.prototype;
|
|
for (var name in spec) {
|
|
var property = spec[name];
|
|
if (!spec.hasOwnProperty(name) || !property) {
|
|
continue;
|
|
}
|
|
validateMethodOverride(proto, name);
|
|
|
|
if (RESERVED_SPEC_KEYS.hasOwnProperty(name)) {
|
|
RESERVED_SPEC_KEYS[name](Constructor, 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.__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.
|
|
proto[name] = createChainedFunction(proto[name], property);
|
|
} else {
|
|
proto[name] = property;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* `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._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
|
|
* @return {?string} Rendered markup to be inserted into the DOM.
|
|
* @final
|
|
* @internal
|
|
*/
|
|
mountComponent: function(rootID, transaction) {
|
|
ReactComponent.Mixin.mountComponent.call(this, rootID, transaction);
|
|
this._compositeLifeCycleState = CompositeLifeCycle.MOUNTING;
|
|
|
|
this._defaultProps = this.getDefaultProps ? this.getDefaultProps() : null;
|
|
this._processProps(this.props);
|
|
|
|
if (this.__reactAutoBindMap) {
|
|
this._bindAutoBindMethods();
|
|
}
|
|
|
|
this.state = this.getInitialState ? this.getInitialState() : null;
|
|
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);
|
|
if (this.componentDidMount) {
|
|
transaction.getReactOnDOMReady().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;
|
|
|
|
ReactComponent.Mixin.unmountComponent.call(this);
|
|
this._renderedComponent.unmountComponent();
|
|
this._renderedComponent = null;
|
|
|
|
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) {
|
|
// 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);
|
|
},
|
|
|
|
/**
|
|
* Processes props by setting default values for unspecified props and
|
|
* asserting that the props are valid.
|
|
*
|
|
* @param {object} props
|
|
* @private
|
|
*/
|
|
_processProps: function(props) {
|
|
var propName;
|
|
var defaultProps = this._defaultProps;
|
|
for (propName in defaultProps) {
|
|
if (!(propName in props)) {
|
|
props[propName] = defaultProps[propName];
|
|
}
|
|
}
|
|
var propTypes = this.constructor.propTypes;
|
|
if (propTypes) {
|
|
var componentName = this.constructor.displayName;
|
|
for (propName in propTypes) {
|
|
var checkProp = propTypes[propName];
|
|
if (checkProp) {
|
|
checkProp(props, propName, componentName);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
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._pendingForceUpdate) {
|
|
return;
|
|
}
|
|
|
|
var nextProps = this.props;
|
|
if (this._pendingProps != null) {
|
|
nextProps = this._pendingProps;
|
|
this._processProps(nextProps);
|
|
this._pendingProps = null;
|
|
|
|
this._compositeLifeCycleState = CompositeLifeCycle.RECEIVING_PROPS;
|
|
if (this.componentWillReceiveProps) {
|
|
this.componentWillReceiveProps(nextProps, transaction);
|
|
}
|
|
}
|
|
|
|
this._compositeLifeCycleState = CompositeLifeCycle.RECEIVING_STATE;
|
|
|
|
var nextState = this._pendingState || this.state;
|
|
this._pendingState = null;
|
|
|
|
if (this._pendingForceUpdate ||
|
|
!this.shouldComponentUpdate ||
|
|
this.shouldComponentUpdate(nextProps, nextState)) {
|
|
this._pendingForceUpdate = false;
|
|
// Will set `this.props` and `this.state`.
|
|
this._performComponentUpdate(nextProps, nextState, transaction);
|
|
} else {
|
|
// If it's determined that a component should not update, we still want
|
|
// to set props and state.
|
|
this.props = nextProps;
|
|
this.state = nextState;
|
|
}
|
|
|
|
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 {?object} nextState Next object to set as state.
|
|
* @param {ReactReconcileTransaction} transaction
|
|
* @private
|
|
*/
|
|
_performComponentUpdate: function(nextProps, nextState, transaction) {
|
|
var prevProps = this.props;
|
|
var prevState = this.state;
|
|
|
|
if (this.componentWillUpdate) {
|
|
this.componentWillUpdate(nextProps, nextState, transaction);
|
|
}
|
|
|
|
this.props = nextProps;
|
|
this.state = nextState;
|
|
|
|
this.updateComponent(transaction, prevProps, prevState);
|
|
|
|
if (this.componentDidUpdate) {
|
|
transaction.getReactOnDOMReady().enqueue(
|
|
this,
|
|
this.componentDidUpdate.bind(this, prevProps, prevState)
|
|
);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 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 {?object} prevState
|
|
* @internal
|
|
* @overridable
|
|
*/
|
|
updateComponent: function(transaction, prevProps, prevState) {
|
|
ReactComponent.Mixin.updateComponent.call(this, transaction, prevProps);
|
|
var currentComponent = this._renderedComponent;
|
|
var nextComponent = this._renderValidatedComponent();
|
|
if (currentComponent.constructor === nextComponent.constructor) {
|
|
currentComponent.receiveProps(nextComponent.props, transaction);
|
|
} else {
|
|
// These two IDs are actually the same! But nothing should rely on that.
|
|
var thisID = this._rootNodeID;
|
|
var currentComponentID = currentComponent._rootNodeID;
|
|
currentComponent.unmountComponent();
|
|
var nextMarkup = nextComponent.mountComponent(thisID, transaction);
|
|
ReactComponent.DOMIDOperations.dangerouslyReplaceNodeWithMarkupByID(
|
|
currentComponentID,
|
|
nextMarkup
|
|
);
|
|
this._renderedComponent = nextComponent;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 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;
|
|
invariant(
|
|
this.isMounted() ||
|
|
compositeLifeCycleState === CompositeLifeCycle.MOUNTING,
|
|
'forceUpdate(...): Can only force an update on mounted or mounting ' +
|
|
'components.'
|
|
);
|
|
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`).'
|
|
);
|
|
this._pendingForceUpdate = true;
|
|
ReactUpdates.enqueueUpdate(this, callback);
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
_renderValidatedComponent: function() {
|
|
var renderedComponent;
|
|
ReactCurrentOwner.current = this;
|
|
try {
|
|
renderedComponent = this.render();
|
|
} catch (error) {
|
|
// IE8 requires `catch` in order to use `finally`.
|
|
throw error;
|
|
} finally {
|
|
ReactCurrentOwner.current = null;
|
|
}
|
|
invariant(
|
|
ReactComponent.isValidComponent(renderedComponent),
|
|
'%s.render(): A valid ReactComponent must be returned.',
|
|
this.constructor.displayName || 'ReactCompositeComponent'
|
|
);
|
|
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(method);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 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 (true) {
|
|
var componentName = component.constructor.displayName;
|
|
var _bind = boundMethod.bind;
|
|
boundMethod.bind = function(newThis) {
|
|
// 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) {
|
|
console.warn(
|
|
'bind(): React component methods may only be bound to the ' +
|
|
'component instance. See ' + componentName
|
|
);
|
|
} else if (arguments.length === 1) {
|
|
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;
|
|
}
|
|
return _bind.apply(boundMethod, arguments);
|
|
};
|
|
}
|
|
return boundMethod;
|
|
}
|
|
};
|
|
|
|
var ReactCompositeComponentBase = function() {};
|
|
mixInto(ReactCompositeComponentBase, ReactComponent.Mixin);
|
|
mixInto(ReactCompositeComponentBase, ReactOwner.Mixin);
|
|
mixInto(ReactCompositeComponentBase, ReactPropTransferer.Mixin);
|
|
mixInto(ReactCompositeComponentBase, ReactCompositeComponentMixin);
|
|
|
|
/**
|
|
* 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;
|
|
mixSpecIntoComponent(Constructor, spec);
|
|
invariant(
|
|
Constructor.prototype.render,
|
|
'createClass(...): Class specification must implement a `render` method.'
|
|
);
|
|
// Reduce time spent doing lookups by setting these on the prototype.
|
|
for (var methodName in ReactCompositeComponentInterface) {
|
|
if (!Constructor.prototype[methodName]) {
|
|
Constructor.prototype[methodName] = null;
|
|
}
|
|
}
|
|
|
|
var ConvenienceConstructor = function(props, children) {
|
|
var instance = new Constructor();
|
|
instance.construct.apply(instance, arguments);
|
|
return instance;
|
|
};
|
|
ConvenienceConstructor.componentConstructor = Constructor;
|
|
ConvenienceConstructor.originalSpec = spec;
|
|
return ConvenienceConstructor;
|
|
},
|
|
|
|
/**
|
|
* TODO: Delete this when all callers have been updated to rely on this
|
|
* behavior being the default.
|
|
*
|
|
* Backwards compatible stub for what is now the default behavior.
|
|
* @param {function} method Method to be bound.
|
|
* @public
|
|
*/
|
|
autoBind: function(method) {
|
|
if (true) {
|
|
console.warn(
|
|
'React.autoBind() is now deprecated. All React component methods ' +
|
|
'are auto bound by default, so React.autoBind() is a no-op. It ' +
|
|
'will be removed in the next version of React'
|
|
);
|
|
}
|
|
return method;
|
|
}
|
|
};
|
|
|
|
module.exports = ReactCompositeComponent;
|
|
|
|
})()
|
|
},{"./ReactComponent":23,"./ReactCurrentOwner":25,"./ReactOwner":43,"./ReactPropTransferer":44,"./ReactUpdates":49,"./invariant":78,"./keyMirror":81,"./merge":84,"./mixInto":87}],25:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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;
|
|
|
|
},{}],26:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 ReactNativeComponent = require("./ReactNativeComponent");
|
|
|
|
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 ReactNativeComponent(tag, omitClose);
|
|
Constructor.prototype.constructor = Constructor;
|
|
|
|
var ConvenienceConstructor = function(props, children) {
|
|
var instance = new Constructor();
|
|
instance.construct.apply(instance, arguments);
|
|
return instance;
|
|
};
|
|
ConvenienceConstructor.componentConstructor = Constructor;
|
|
return ConvenienceConstructor;
|
|
}
|
|
|
|
/**
|
|
* Creates a mapping from supported HTML tags to `ReactNativeComponent` 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,
|
|
g: false,
|
|
line: false,
|
|
path: false,
|
|
polyline: false,
|
|
rect: false,
|
|
svg: false,
|
|
text: false
|
|
}, createDOMComponentClass);
|
|
|
|
var injection = {
|
|
injectComponentClasses: function(componentClasses) {
|
|
mergeInto(ReactDOM, componentClasses);
|
|
}
|
|
};
|
|
|
|
ReactDOM.injection = injection;
|
|
|
|
module.exports = ReactDOM;
|
|
|
|
},{"./ReactNativeComponent":41,"./mergeInto":86,"./objMapKeyVal":88}],27:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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> `ReactNativeComponent`.
|
|
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({
|
|
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(node) {
|
|
ReactEventEmitter.trapBubbledEvent(
|
|
EventConstants.topLevelTypes.topSubmit,
|
|
'submit',
|
|
node
|
|
);
|
|
}
|
|
});
|
|
|
|
module.exports = ReactDOMForm;
|
|
|
|
},{"./EventConstants":13,"./ReactCompositeComponent":24,"./ReactDOM":26,"./ReactEventEmitter":34}],28:[function(require,module,exports){
|
|
(function(){/**
|
|
* Copyright 2013 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 getTextContentAccessor = require("./getTextContentAccessor");
|
|
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()`.'
|
|
};
|
|
|
|
/**
|
|
* The DOM property to use when setting text content.
|
|
*
|
|
* @type {string}
|
|
* @private
|
|
*/
|
|
var textContentAccessor = getTextContentAccessor() || 'NA';
|
|
|
|
/**
|
|
* Operations used to process updates to DOM nodes. This is made injectable via
|
|
* `ReactComponent.DOMIDOperations`.
|
|
*/
|
|
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: function(id, name, value) {
|
|
var node = ReactMount.getNode(id);
|
|
invariant(
|
|
!INVALID_PROPERTY_ERRORS.hasOwnProperty(name),
|
|
'updatePropertyByID(...): %s',
|
|
INVALID_PROPERTY_ERRORS[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: function(id, name, value) {
|
|
var node = ReactMount.getNode(id);
|
|
invariant(
|
|
!INVALID_PROPERTY_ERRORS.hasOwnProperty(name),
|
|
'updatePropertyByID(...): %s',
|
|
INVALID_PROPERTY_ERRORS[name]
|
|
);
|
|
DOMPropertyOperations.deleteValueForProperty(node, name, value);
|
|
},
|
|
|
|
/**
|
|
* This should almost never be used instead of `updatePropertyByID()` due to
|
|
* the extra object allocation required by the API. That said, this is useful
|
|
* for batching up several operations across worker thread boundaries.
|
|
*
|
|
* @param {string} id ID of the node to update.
|
|
* @param {object} properties A mapping of valid property names.
|
|
* @internal
|
|
* @see {ReactDOMIDOperations.updatePropertyByID}
|
|
*/
|
|
updatePropertiesByID: function(id, properties) {
|
|
for (var name in properties) {
|
|
if (!properties.hasOwnProperty(name)) {
|
|
continue;
|
|
}
|
|
ReactDOMIDOperations.updatePropertiesByID(id, name, properties[name]);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 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: function(id, styles) {
|
|
var node = ReactMount.getNode(id);
|
|
CSSPropertyOperations.setValueForStyles(node, styles);
|
|
},
|
|
|
|
/**
|
|
* Updates a DOM node's innerHTML set by `props.dangerouslySetInnerHTML`.
|
|
*
|
|
* @param {string} id ID of the node to update.
|
|
* @param {object} html An HTML object with the `__html` property.
|
|
* @internal
|
|
*/
|
|
updateInnerHTMLByID: function(id, html) {
|
|
var node = ReactMount.getNode(id);
|
|
// HACK: IE8- normalize whitespace in innerHTML, removing leading spaces.
|
|
// @see quirksmode.org/bugreports/archives/2004/11/innerhtml_and_t.html
|
|
node.innerHTML = (html && html.__html || '').replace(/^ /g, ' ');
|
|
},
|
|
|
|
/**
|
|
* 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: function(id, content) {
|
|
var node = ReactMount.getNode(id);
|
|
node[textContentAccessor] = 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: function(id, markup) {
|
|
var node = ReactMount.getNode(id);
|
|
DOMChildrenOperations.dangerouslyReplaceNodeWithMarkup(node, markup);
|
|
},
|
|
|
|
/**
|
|
* TODO: We only actually *need* to purge the cache when we remove elements.
|
|
* Detect if any elements were removed instead of blindly purging.
|
|
*/
|
|
manageChildrenByParentID: function(parentID, domOperations) {
|
|
var parent = ReactMount.getNode(parentID);
|
|
DOMChildrenOperations.manageChildren(parent, domOperations);
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = ReactDOMIDOperations;
|
|
|
|
})()
|
|
},{"./CSSPropertyOperations":3,"./DOMChildrenOperations":6,"./DOMPropertyOperations":8,"./ReactMount":39,"./getTextContentAccessor":74,"./invariant":78}],29:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 DOMPropertyOperations = require("./DOMPropertyOperations");
|
|
var ReactCompositeComponent = require("./ReactCompositeComponent");
|
|
var ReactDOM = require("./ReactDOM");
|
|
|
|
var merge = require("./merge");
|
|
|
|
// Store a reference to the <input> `ReactNativeComponent`.
|
|
var input = ReactDOM.input;
|
|
|
|
/**
|
|
* 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({
|
|
|
|
getInitialState: function() {
|
|
return {
|
|
checked: this.props.defaultChecked || false,
|
|
value: this.props.defaultValue || ''
|
|
};
|
|
},
|
|
|
|
shouldComponentUpdate: function() {
|
|
// Defer any updates to this component during the `onChange` handler.
|
|
return !this._isChanging;
|
|
},
|
|
|
|
getChecked: function() {
|
|
return this.props.checked != null ? this.props.checked : this.state.checked;
|
|
},
|
|
|
|
getValue: function() {
|
|
// Cast `this.props.value` to a string so equality checks pass.
|
|
return this.props.value != null ? '' + this.props.value : this.state.value;
|
|
},
|
|
|
|
render: function() {
|
|
// Clone `this.props` so we don't mutate the input.
|
|
var props = merge(this.props);
|
|
|
|
props.checked = this.getChecked();
|
|
props.value = this.getValue();
|
|
props.onChange = this.handleChange;
|
|
|
|
return input(props, this.props.children);
|
|
},
|
|
|
|
componentDidUpdate: function(prevProps, prevState, rootNode) {
|
|
if (this.props.checked != null) {
|
|
DOMPropertyOperations.setValueForProperty(
|
|
rootNode,
|
|
'checked',
|
|
this.props.checked || false
|
|
);
|
|
}
|
|
if (this.props.value != null) {
|
|
// Cast `this.props.value` to a string so falsey values that cast to
|
|
// truthy strings are not ignored.
|
|
DOMPropertyOperations.setValueForProperty(
|
|
rootNode,
|
|
'value',
|
|
'' + this.props.value || ''
|
|
);
|
|
}
|
|
},
|
|
|
|
handleChange: function(event) {
|
|
var returnValue;
|
|
if (this.props.onChange) {
|
|
this._isChanging = true;
|
|
returnValue = this.props.onChange(event);
|
|
this._isChanging = false;
|
|
}
|
|
this.setState({
|
|
checked: event.target.checked,
|
|
value: event.target.value
|
|
});
|
|
return returnValue;
|
|
}
|
|
|
|
});
|
|
|
|
module.exports = ReactDOMInput;
|
|
|
|
},{"./DOMPropertyOperations":8,"./ReactCompositeComponent":24,"./ReactDOM":26,"./merge":84}],30:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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> `ReactNativeComponent`.
|
|
var option = ReactDOM.option;
|
|
|
|
/**
|
|
* Implements an <option> native component that warns when `selected` is set.
|
|
*/
|
|
var ReactDOMOption = ReactCompositeComponent.createClass({
|
|
|
|
componentWillMount: function() {
|
|
// TODO (yungsters): Remove support for `selected` in <option>.
|
|
if (this.props.selected != null) {
|
|
if (true) {
|
|
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":24,"./ReactDOM":26}],31:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 ReactCompositeComponent = require("./ReactCompositeComponent");
|
|
var ReactDOM = require("./ReactDOM");
|
|
|
|
var invariant = require("./invariant");
|
|
var merge = require("./merge");
|
|
|
|
// Store a reference to the <select> `ReactNativeComponent`.
|
|
var select = ReactDOM.select;
|
|
|
|
/**
|
|
* Validation function for `value` and `defaultValue`.
|
|
* @private
|
|
*/
|
|
function selectValueType(props, propName, componentName) {
|
|
if (props[propName] == null) {
|
|
return;
|
|
}
|
|
if (props.multiple) {
|
|
invariant(
|
|
Array.isArray(props[propName]),
|
|
'The `%s` prop supplied to <select> must be an array if `multiple` is ' +
|
|
'true.',
|
|
propName
|
|
);
|
|
} else {
|
|
invariant(
|
|
!Array.isArray(props[propName]),
|
|
'The `%s` prop supplied to <select> must be a scalar value if ' +
|
|
'`multiple` is false.',
|
|
propName
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If `value` is supplied, updates <option> elements on mount and update.
|
|
* @private
|
|
*/
|
|
function updateOptions() {
|
|
/*jshint validthis:true */
|
|
if (this.props.value == null) {
|
|
return;
|
|
}
|
|
var options = this.getDOMNode().options;
|
|
var selectedValue = '' + this.props.value;
|
|
|
|
for (var i = 0, l = options.length; i < l; i++) {
|
|
var selected = this.props.multiple ?
|
|
selectedValue.indexOf(options[i].value) >= 0 :
|
|
selected = 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({
|
|
|
|
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: updateOptions,
|
|
|
|
componentDidUpdate: updateOptions,
|
|
|
|
handleChange: function(event) {
|
|
var returnValue;
|
|
if (this.props.onChange) {
|
|
this._isChanging = true;
|
|
returnValue = this.props.onChange(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;
|
|
|
|
},{"./ReactCompositeComponent":24,"./ReactDOM":26,"./invariant":78,"./merge":84}],32:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 DOMPropertyOperations = require("./DOMPropertyOperations");
|
|
var ReactCompositeComponent = require("./ReactCompositeComponent");
|
|
var ReactDOM = require("./ReactDOM");
|
|
|
|
var invariant = require("./invariant");
|
|
var merge = require("./merge");
|
|
|
|
// Store a reference to the <textarea> `ReactNativeComponent`.
|
|
var textarea = ReactDOM.textarea;
|
|
|
|
// For quickly matching children type, to test if can be treated as content.
|
|
var CONTENT_TYPES = {'string': true, 'number': true};
|
|
|
|
/**
|
|
* 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({
|
|
|
|
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 (true) {
|
|
console.warn(
|
|
'Use the `defaultValue` or `value` props instead of setting ' +
|
|
'children on <textarea>.'
|
|
);
|
|
}
|
|
invariant(
|
|
defaultValue == null,
|
|
'If you supply `defaultValue` on a <textarea>, do not pass children.'
|
|
);
|
|
if (Array.isArray(children)) {
|
|
invariant(
|
|
children.length <= 1,
|
|
'<textarea> can only have at most one child.'
|
|
);
|
|
children = children[0];
|
|
}
|
|
invariant(
|
|
CONTENT_TYPES[typeof children],
|
|
'If you specify children to <textarea>, it must be a single string ' +
|
|
'or number., not an array or object.'
|
|
);
|
|
defaultValue = '' + children;
|
|
}
|
|
defaultValue = defaultValue || '';
|
|
return {
|
|
// We save the initial value so that `ReactNativeComponent` doesn't update
|
|
// `textContent` (unnecessary since we update value).
|
|
initialValue: this.props.value != null ? this.props.value : defaultValue,
|
|
value: defaultValue
|
|
};
|
|
},
|
|
|
|
shouldComponentUpdate: function() {
|
|
// Defer any updates to this component during the `onChange` handler.
|
|
return !this._isChanging;
|
|
},
|
|
|
|
getValue: function() {
|
|
return this.props.value != null ? this.props.value : this.state.value;
|
|
},
|
|
|
|
render: function() {
|
|
// Clone `this.props` so we don't mutate the input.
|
|
var props = merge(this.props);
|
|
|
|
invariant(
|
|
props.dangerouslySetInnerHTML == null,
|
|
'`dangerouslySetInnerHTML` does not make sense on <textarea>.'
|
|
);
|
|
|
|
props.value = this.getValue();
|
|
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, rootNode) {
|
|
if (this.props.value != null) {
|
|
DOMPropertyOperations.setValueForProperty(
|
|
rootNode,
|
|
'value',
|
|
this.props.value || ''
|
|
);
|
|
}
|
|
},
|
|
|
|
handleChange: function(event) {
|
|
var returnValue;
|
|
if (this.props.onChange) {
|
|
this._isChanging = true;
|
|
returnValue = this.props.onChange(event);
|
|
this._isChanging = false;
|
|
}
|
|
this.setState({value: event.target.value});
|
|
return returnValue;
|
|
}
|
|
|
|
});
|
|
|
|
module.exports = ReactDOMTextarea;
|
|
|
|
},{"./DOMPropertyOperations":8,"./ReactCompositeComponent":24,"./ReactDOM":26,"./invariant":78,"./merge":84}],33:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 ReactDOM = require("./ReactDOM");
|
|
var ReactDOMForm = require("./ReactDOMForm");
|
|
var ReactDOMInput = require("./ReactDOMInput");
|
|
var ReactDOMOption = require("./ReactDOMOption");
|
|
var ReactDOMSelect = require("./ReactDOMSelect");
|
|
var ReactDOMTextarea = require("./ReactDOMTextarea");
|
|
var ReactEventEmitter = require("./ReactEventEmitter");
|
|
var ReactEventTopLevelCallback = require("./ReactEventTopLevelCallback");
|
|
|
|
var DefaultDOMPropertyConfig = require("./DefaultDOMPropertyConfig");
|
|
var DOMProperty = require("./DOMProperty");
|
|
|
|
var DefaultEventPluginOrder = require("./DefaultEventPluginOrder");
|
|
var EnterLeaveEventPlugin = require("./EnterLeaveEventPlugin");
|
|
var ChangeEventPlugin = require("./ChangeEventPlugin");
|
|
var EventPluginHub = require("./EventPluginHub");
|
|
var ReactInstanceHandles = require("./ReactInstanceHandles");
|
|
var SimpleEventPlugin = require("./SimpleEventPlugin");
|
|
var MobileSafariClickEventPlugin = require("./MobileSafariClickEventPlugin");
|
|
|
|
function inject() {
|
|
ReactEventEmitter.TopLevelCallbackCreator = ReactEventTopLevelCallback;
|
|
/**
|
|
* Inject module for resolving DOM hierarchy and plugin ordering.
|
|
*/
|
|
EventPluginHub.injection.injectEventPluginOrder(DefaultEventPluginOrder);
|
|
EventPluginHub.injection.injectInstanceHandle(ReactInstanceHandles);
|
|
|
|
/**
|
|
* Some important event plugins included by default (without having to require
|
|
* them).
|
|
*/
|
|
EventPluginHub.injection.injectEventPluginsByName({
|
|
'SimpleEventPlugin': SimpleEventPlugin,
|
|
'EnterLeaveEventPlugin': EnterLeaveEventPlugin,
|
|
'ChangeEventPlugin': ChangeEventPlugin,
|
|
'MobileSafariClickEventPlugin': MobileSafariClickEventPlugin
|
|
});
|
|
|
|
ReactDOM.injection.injectComponentClasses({
|
|
form: ReactDOMForm,
|
|
input: ReactDOMInput,
|
|
option: ReactDOMOption,
|
|
select: ReactDOMSelect,
|
|
textarea: ReactDOMTextarea
|
|
});
|
|
|
|
DOMProperty.injection.injectDOMPropertyConfig(DefaultDOMPropertyConfig);
|
|
}
|
|
|
|
module.exports = {
|
|
inject: inject
|
|
};
|
|
|
|
},{"./ChangeEventPlugin":5,"./DOMProperty":7,"./DefaultDOMPropertyConfig":10,"./DefaultEventPluginOrder":11,"./EnterLeaveEventPlugin":12,"./EventPluginHub":15,"./MobileSafariClickEventPlugin":20,"./ReactDOM":26,"./ReactDOMForm":27,"./ReactDOMInput":29,"./ReactDOMOption":30,"./ReactDOMSelect":31,"./ReactDOMTextarea":32,"./ReactEventEmitter":34,"./ReactEventTopLevelCallback":35,"./ReactInstanceHandles":37,"./SimpleEventPlugin":50}],34:[function(require,module,exports){
|
|
(function(){/**
|
|
* Copyright 2013 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 ExecutionEnvironment = require("./ExecutionEnvironment");
|
|
var ReactUpdates = require("./ReactUpdates");
|
|
var ViewportMetrics = require("./ViewportMetrics");
|
|
|
|
var invariant = require("./invariant");
|
|
var isEventSupported = require("./isEventSupported");
|
|
|
|
/**
|
|
* 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 |
|
|
* +-------------+ . v +-----------+
|
|
* | application | . +----------+
|
|
* |-------------| . | callback |
|
|
* | | . | registry |
|
|
* | | . +----------+
|
|
* +-------------+ .
|
|
* .
|
|
* React Core . General Purpose Event Plugin System
|
|
*/
|
|
|
|
/**
|
|
* Whether or not `ensureListening` has been invoked.
|
|
* @type {boolean}
|
|
* @private
|
|
*/
|
|
var _isListening = false;
|
|
|
|
/**
|
|
* 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
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* @private
|
|
* @see http://www.quirksmode.org/dom/events/scroll.html
|
|
*/
|
|
function registerScrollValueMonitoring() {
|
|
var refresh = ViewportMetrics.refreshScrollValues;
|
|
EventListener.listen(window, 'scroll', refresh);
|
|
EventListener.listen(window, 'resize', refresh);
|
|
}
|
|
|
|
/**
|
|
* 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 {boolean} touchNotMouse Listen to touch events instead of mouse.
|
|
* @private
|
|
* @see http://www.quirksmode.org/dom/events/keys.html.
|
|
*/
|
|
function listenAtTopLevel(touchNotMouse) {
|
|
invariant(
|
|
!_isListening,
|
|
'listenAtTopLevel(...): Cannot setup top-level listener more than once.'
|
|
);
|
|
var topLevelTypes = EventConstants.topLevelTypes;
|
|
var mountAt = document;
|
|
|
|
registerScrollValueMonitoring();
|
|
trapBubbledEvent(topLevelTypes.topMouseOver, 'mouseover', mountAt);
|
|
trapBubbledEvent(topLevelTypes.topMouseDown, 'mousedown', mountAt);
|
|
trapBubbledEvent(topLevelTypes.topMouseUp, 'mouseup', mountAt);
|
|
trapBubbledEvent(topLevelTypes.topMouseMove, 'mousemove', mountAt);
|
|
trapBubbledEvent(topLevelTypes.topMouseOut, 'mouseout', mountAt);
|
|
trapBubbledEvent(topLevelTypes.topClick, 'click', mountAt);
|
|
trapBubbledEvent(topLevelTypes.topDoubleClick, 'dblclick', mountAt);
|
|
if (touchNotMouse) {
|
|
trapBubbledEvent(topLevelTypes.topTouchStart, 'touchstart', mountAt);
|
|
trapBubbledEvent(topLevelTypes.topTouchEnd, 'touchend', mountAt);
|
|
trapBubbledEvent(topLevelTypes.topTouchMove, 'touchmove', mountAt);
|
|
trapBubbledEvent(topLevelTypes.topTouchCancel, 'touchcancel', mountAt);
|
|
}
|
|
trapBubbledEvent(topLevelTypes.topKeyUp, 'keyup', mountAt);
|
|
trapBubbledEvent(topLevelTypes.topKeyPress, 'keypress', mountAt);
|
|
trapBubbledEvent(topLevelTypes.topKeyDown, 'keydown', mountAt);
|
|
trapBubbledEvent(topLevelTypes.topInput, 'input', mountAt);
|
|
trapBubbledEvent(topLevelTypes.topChange, 'change', mountAt);
|
|
trapBubbledEvent(
|
|
topLevelTypes.topSelectionChange,
|
|
'selectionchange',
|
|
mountAt
|
|
);
|
|
trapBubbledEvent(
|
|
topLevelTypes.topDOMCharacterDataModified,
|
|
'DOMCharacterDataModified',
|
|
mountAt
|
|
);
|
|
|
|
if (isEventSupported('drag')) {
|
|
trapBubbledEvent(topLevelTypes.topDrag, 'drag', mountAt);
|
|
trapBubbledEvent(topLevelTypes.topDragEnd, 'dragend', mountAt);
|
|
trapBubbledEvent(topLevelTypes.topDragEnter, 'dragenter', mountAt);
|
|
trapBubbledEvent(topLevelTypes.topDragExit, 'dragexit', mountAt);
|
|
trapBubbledEvent(topLevelTypes.topDragLeave, 'dragleave', mountAt);
|
|
trapBubbledEvent(topLevelTypes.topDragOver, 'dragover', mountAt);
|
|
trapBubbledEvent(topLevelTypes.topDragStart, 'dragstart', mountAt);
|
|
trapBubbledEvent(topLevelTypes.topDrop, 'drop', mountAt);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
// IE<9 does not support capturing so just trap the bubbled event there.
|
|
if (isEventSupported('scroll', true)) {
|
|
trapCapturedEvent(topLevelTypes.topScroll, 'scroll', mountAt);
|
|
} else {
|
|
trapBubbledEvent(topLevelTypes.topScroll, 'scroll', window);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* `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 = {
|
|
|
|
/**
|
|
* React references `ReactEventTopLevelCallback` using this property in order
|
|
* to allow dependency injection.
|
|
*/
|
|
TopLevelCallbackCreator: null,
|
|
|
|
/**
|
|
* Ensures that top-level event delegation listeners are installed.
|
|
*
|
|
* There are issues with listening to both touch events and mouse events on
|
|
* the top-level, so we make the caller choose which one to listen to. (If
|
|
* there's a touch top-level listeners, anchors don't receive clicks for some
|
|
* reason, and only in some cases).
|
|
*
|
|
* @param {boolean} touchNotMouse Listen to touch events instead of mouse.
|
|
*/
|
|
ensureListening: function(touchNotMouse) {
|
|
invariant(
|
|
ExecutionEnvironment.canUseDOM,
|
|
'ensureListening(...): Cannot toggle event listening in a Worker ' +
|
|
'thread. This is likely a bug in the framework. Please report ' +
|
|
'immediately.'
|
|
);
|
|
invariant(
|
|
ReactEventEmitter.TopLevelCallbackCreator,
|
|
'ensureListening(...): Cannot be called without a top level callback ' +
|
|
'creator being injected.'
|
|
);
|
|
if (!_isListening) {
|
|
listenAtTopLevel(touchNotMouse);
|
|
_isListening = true;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Sets whether or not any created callbacks should be enabled.
|
|
*
|
|
* @param {boolean} enabled True if callbacks should be enabled.
|
|
*/
|
|
setEnabled: function(enabled) {
|
|
invariant(
|
|
ExecutionEnvironment.canUseDOM,
|
|
'setEnabled(...): Cannot toggle event listening in a Worker thread. ' +
|
|
'This is likely a bug in the framework. Please report immediately.'
|
|
);
|
|
if (ReactEventEmitter.TopLevelCallbackCreator) {
|
|
ReactEventEmitter.TopLevelCallbackCreator.setEnabled(enabled);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @return {boolean} True if callbacks are enabled.
|
|
*/
|
|
isEnabled: function() {
|
|
return !!(
|
|
ReactEventEmitter.TopLevelCallbackCreator &&
|
|
ReactEventEmitter.TopLevelCallbackCreator.isEnabled()
|
|
);
|
|
},
|
|
|
|
/**
|
|
* 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 {DOMEventTarget} topLevelTarget The listening component root node.
|
|
* @param {string} topLevelTargetID ID of `topLevelTarget`.
|
|
* @param {object} nativeEvent Native browser 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(function() {
|
|
EventPluginHub.enqueueEvents(events);
|
|
EventPluginHub.processEventQueue();
|
|
});
|
|
},
|
|
|
|
registrationNames: EventPluginHub.registrationNames,
|
|
|
|
putListener: EventPluginHub.putListener,
|
|
|
|
getListener: EventPluginHub.getListener,
|
|
|
|
deleteListener: EventPluginHub.deleteListener,
|
|
|
|
deleteAllListeners: EventPluginHub.deleteAllListeners,
|
|
|
|
trapBubbledEvent: trapBubbledEvent,
|
|
|
|
trapCapturedEvent: trapCapturedEvent
|
|
|
|
};
|
|
|
|
module.exports = ReactEventEmitter;
|
|
|
|
})()
|
|
},{"./EventConstants":13,"./EventListener":14,"./EventPluginHub":15,"./ExecutionEnvironment":19,"./ReactUpdates":49,"./ViewportMetrics":60,"./invariant":78,"./isEventSupported":79}],35:[function(require,module,exports){
|
|
(function(){/**
|
|
* Copyright 2013 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 ExecutionEnvironment = require("./ExecutionEnvironment");
|
|
var ReactEventEmitter = require("./ReactEventEmitter");
|
|
var ReactMount = require("./ReactMount");
|
|
|
|
var getEventTarget = require("./getEventTarget");
|
|
|
|
/**
|
|
* @type {boolean}
|
|
* @private
|
|
*/
|
|
var _topLevelListenersEnabled = true;
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
// TODO: Remove when synthetic events are ready, this is for IE<9.
|
|
if (nativeEvent.srcElement &&
|
|
nativeEvent.srcElement !== nativeEvent.target) {
|
|
nativeEvent.target = nativeEvent.srcElement;
|
|
}
|
|
var topLevelTarget = ReactMount.getFirstReactDOM(
|
|
getEventTarget(nativeEvent)
|
|
) || ExecutionEnvironment.global;
|
|
var topLevelTargetID = ReactMount.getID(topLevelTarget) || '';
|
|
ReactEventEmitter.handleTopLevel(
|
|
topLevelType,
|
|
topLevelTarget,
|
|
topLevelTargetID,
|
|
nativeEvent
|
|
);
|
|
};
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = ReactEventTopLevelCallback;
|
|
|
|
})()
|
|
},{"./ExecutionEnvironment":19,"./ReactEventEmitter":34,"./ReactMount":39,"./getEventTarget":72}],36:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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";
|
|
|
|
// It is not safe to read the document.activeElement property in IE if there's
|
|
// nothing focused.
|
|
function getActiveElement() {
|
|
try {
|
|
return document.activeElement;
|
|
} catch (e) {
|
|
}
|
|
}
|
|
|
|
function isInDocument(node) {
|
|
return document.documentElement.contains(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 textarea or input.
|
|
* -@input: Look up selection bounds of this input or textarea
|
|
* -@return {start: selectionStart, end: selectionEnd}
|
|
*/
|
|
getSelection: function(input) {
|
|
var range;
|
|
if (input.contentEditable === 'true' && window.getSelection) {
|
|
range = window.getSelection().getRangeAt(0);
|
|
var commonAncestor = range.commonAncestorContainer;
|
|
if (commonAncestor && commonAncestor.nodeType === 3) {
|
|
commonAncestor = commonAncestor.parentNode;
|
|
}
|
|
if (commonAncestor !== input) {
|
|
return {start: 0, end: 0};
|
|
} else {
|
|
return {start: range.startOffset, end: range.endOffset};
|
|
}
|
|
}
|
|
|
|
if (!document.selection) {
|
|
// Mozilla, Safari, etc.
|
|
return {start: input.selectionStart, end: input.selectionEnd};
|
|
}
|
|
|
|
range = document.selection.createRange();
|
|
if (range.parentElement() !== input) {
|
|
// There can only be one selection per document in IE, so if the
|
|
// containing element of the document's selection isn't our text field,
|
|
// our text field must have no selection.
|
|
return {start: 0, end: 0};
|
|
}
|
|
|
|
var length = input.value.length;
|
|
|
|
if (input.nodeName === 'INPUT') {
|
|
return {
|
|
start: -range.moveStart('character', -length),
|
|
end: -range.moveEnd('character', -length)
|
|
};
|
|
} else {
|
|
var range2 = range.duplicate();
|
|
range2.moveToElementText(input);
|
|
range2.setEndPoint('StartToEnd', range);
|
|
var end = length - range2.text.length;
|
|
range2.setEndPoint('StartToStart', range);
|
|
return {
|
|
start: length - range2.text.length,
|
|
end: end
|
|
};
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @setSelection: Sets the selection bounds of a textarea or input and focuses
|
|
* the input.
|
|
* -@input Set selection bounds of this input or textarea
|
|
* -@rangeObj Object of same form that is returned from get*
|
|
*/
|
|
setSelection: function(input, rangeObj) {
|
|
var range;
|
|
var start = rangeObj.start;
|
|
var end = rangeObj.end;
|
|
if (typeof end === 'undefined') {
|
|
end = start;
|
|
}
|
|
if (document.selection) {
|
|
// IE is inconsistent about character offsets when it comes to carriage
|
|
// returns, so we need to manually take them into account
|
|
if (input.tagName === 'TEXTAREA') {
|
|
var cr_before =
|
|
(input.value.slice(0, start).match(/\r/g) || []).length;
|
|
var cr_inside =
|
|
(input.value.slice(start, end).match(/\r/g) || []).length;
|
|
start -= cr_before;
|
|
end -= cr_before + cr_inside;
|
|
}
|
|
range = input.createTextRange();
|
|
range.collapse(true);
|
|
range.moveStart('character', start);
|
|
range.moveEnd('character', end - start);
|
|
range.select();
|
|
} else {
|
|
if (input.contentEditable === 'true') {
|
|
if (input.childNodes.length === 1) {
|
|
range = document.createRange();
|
|
range.setStart(input.childNodes[0], start);
|
|
range.setEnd(input.childNodes[0], end);
|
|
var sel = window.getSelection();
|
|
sel.removeAllRanges();
|
|
sel.addRange(range);
|
|
}
|
|
} else {
|
|
input.selectionStart = start;
|
|
input.selectionEnd = Math.min(end, input.value.length);
|
|
input.focus();
|
|
}
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = ReactInputSelection;
|
|
|
|
},{}],37:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 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;
|
|
|
|
/**
|
|
* 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. If we assume 100 React components per page, and a user
|
|
* loads 1 page per minute 24/7 for 50 years, with a mount point space of
|
|
* 9,999,999 the likelihood of never having a collision is 99.997%.
|
|
*/
|
|
var GLOBAL_MOUNT_POINT_MAX = 9999999;
|
|
|
|
/**
|
|
* 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 + 'r[' + 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) {
|
|
invariant(
|
|
isValidID(ancestorID) && isValidID(destinationID),
|
|
'getNextDescendantID(%s, %s): Received an invalid React DOM ID.',
|
|
ancestorID,
|
|
destinationID
|
|
);
|
|
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
|
|
);
|
|
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);
|
|
invariant(
|
|
isValidID(longestCommonID),
|
|
'getFirstCommonAncestorID(%s, %s): Expected a valid React DOM ID: %s',
|
|
oneID,
|
|
twoID,
|
|
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.
|
|
*
|
|
* @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 || '';
|
|
invariant(
|
|
start !== stop,
|
|
'traverseParentPath(...): Cannot traverse from and to the same ID, `%s`.',
|
|
start
|
|
);
|
|
var traverseUp = isAncestorIDOf(stop, start);
|
|
invariant(
|
|
traverseUp || isAncestorIDOf(start, stop),
|
|
'traverseParentPath(%s, %s, ...): Cannot traverse from two IDs that do ' +
|
|
'not have a parent path.',
|
|
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)) {
|
|
if ((!skipFirst || id !== start) && (!skipLast || id !== stop)) {
|
|
cb(id, traverseUp, arg);
|
|
}
|
|
if (id === stop) {
|
|
// Only break //after// visiting `stop`.
|
|
break;
|
|
}
|
|
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
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 = {
|
|
|
|
separator: SEPARATOR,
|
|
|
|
createReactRootID: function() {
|
|
return getReactRootIDString(
|
|
Math.ceil(Math.random() * GLOBAL_MOUNT_POINT_MAX)
|
|
);
|
|
},
|
|
|
|
/**
|
|
* 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) {
|
|
var regexResult = /\.r\[[^\]]+\]/.exec(id);
|
|
return regexResult && regexResult[0];
|
|
},
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Exposed for unit testing.
|
|
* @private
|
|
*/
|
|
_getFirstCommonAncestorID: getFirstCommonAncestorID,
|
|
|
|
/**
|
|
* Exposed for unit testing.
|
|
* @private
|
|
*/
|
|
_getNextDescendantID: getNextDescendantID,
|
|
|
|
isAncestorIDOf: isAncestorIDOf,
|
|
|
|
SEPARATOR: SEPARATOR
|
|
|
|
};
|
|
|
|
module.exports = ReactInstanceHandles;
|
|
|
|
},{"./invariant":78}],38:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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":62}],39:[function(require,module,exports){
|
|
(function(){/**
|
|
* Copyright 2013 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 invariant = require("./invariant");
|
|
var getReactRootElementInContainer = require("./getReactRootElementInContainer");
|
|
var ReactEventEmitter = require("./ReactEventEmitter");
|
|
var ReactInstanceHandles = require("./ReactInstanceHandles");
|
|
|
|
var SEPARATOR = ReactInstanceHandles.SEPARATOR;
|
|
|
|
var ATTR_NAME = 'data-reactid';
|
|
var nodeCache = {};
|
|
|
|
var $ = require("./$");
|
|
|
|
/** Mapping from reactRootID to React component instance. */
|
|
var instanceByReactRootID = {};
|
|
|
|
/** Mapping from reactRootID to `container` nodes. */
|
|
var containersByReactRootID = {};
|
|
|
|
if (true) {
|
|
/** __DEV__-only mapping from reactRootID to root elements. */
|
|
var rootElementsByReactRootID = {};
|
|
}
|
|
|
|
/**
|
|
* @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) {
|
|
invariant(
|
|
!isValid(cached, id),
|
|
'ReactMount: Two valid but unequal nodes with the same `%s`: %s',
|
|
ATTR_NAME, 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) {
|
|
invariant(
|
|
internalGetID(node) === id,
|
|
'ReactMount: Unexpected modification of `%s`',
|
|
ATTR_NAME
|
|
);
|
|
|
|
var container = ReactMount.findReactContainerForID(id);
|
|
if (container && contains(container, node)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function contains(ancestor, descendant) {
|
|
if (ancestor.contains) {
|
|
// Supported natively in virtually all browsers, but not in jsdom.
|
|
return ancestor.contains(descendant);
|
|
}
|
|
|
|
if (descendant === ancestor) {
|
|
return true;
|
|
}
|
|
|
|
if (descendant.nodeType === 3) {
|
|
// If descendant is a text node, start from descendant.parentNode
|
|
// instead, so that we can assume all ancestors worth considering are
|
|
// element nodes with nodeType === 1.
|
|
descendant = descendant.parentNode;
|
|
}
|
|
|
|
while (descendant && descendant.nodeType === 1) {
|
|
if (descendant === ancestor) {
|
|
return true;
|
|
}
|
|
descendant = descendant.parentNode;
|
|
}
|
|
|
|
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];
|
|
}
|
|
|
|
/**
|
|
* 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, $('container'));
|
|
*
|
|
* <div id="container"> <-- Supplied `container`.
|
|
* <div data-reactid=".r[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,
|
|
|
|
/**
|
|
* 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();
|
|
},
|
|
|
|
/**
|
|
* Ensures that the top-level event delegation listener is set up. This will
|
|
* be invoked some time before the first time any React component is rendered.
|
|
*
|
|
* @private
|
|
*/
|
|
prepareTopLevelEvents: function() {
|
|
ReactEventEmitter.ensureListening(ReactMount.useTouchEvents);
|
|
},
|
|
|
|
/**
|
|
* 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 (true) {
|
|
// 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 start the events system.
|
|
* @param {ReactComponent} nextComponent component instance to render
|
|
* @param {DOMElement} container container to render into
|
|
* @return {string} reactRoot ID prefix
|
|
*/
|
|
_registerComponent: function(nextComponent, container) {
|
|
ReactMount.prepareTopLevelEvents();
|
|
|
|
var reactRootID = ReactMount.registerContainer(container);
|
|
instanceByReactRootID[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: function(
|
|
nextComponent,
|
|
container,
|
|
shouldReuseMarkup) {
|
|
var reactRootID = ReactMount._registerComponent(nextComponent, container);
|
|
nextComponent.mountComponentIntoNode(
|
|
reactRootID,
|
|
container,
|
|
shouldReuseMarkup
|
|
);
|
|
|
|
if (true) {
|
|
// 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 registeredComponent = instanceByReactRootID[getReactRootID(container)];
|
|
|
|
if (registeredComponent) {
|
|
if (registeredComponent.constructor === nextComponent.constructor) {
|
|
return ReactMount._updateRootComponent(
|
|
registeredComponent,
|
|
nextComponent,
|
|
container,
|
|
callback
|
|
);
|
|
} else {
|
|
ReactMount.unmountAndReleaseReactRootNode(container);
|
|
}
|
|
}
|
|
|
|
var reactRootElement = getReactRootElementInContainer(container);
|
|
var containerHasReactMarkup =
|
|
reactRootElement && ReactMount.isRenderedByReact(reactRootElement);
|
|
|
|
var shouldReuseMarkup = containerHasReactMarkup && !registeredComponent;
|
|
|
|
var component = ReactMount._renderNewRootComponent(
|
|
nextComponent,
|
|
container,
|
|
shouldReuseMarkup
|
|
);
|
|
callback && callback();
|
|
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) {
|
|
return ReactMount.constructAndRenderComponent(constructor, props, $(id));
|
|
},
|
|
|
|
/**
|
|
* Registers a container node into which React components will be rendered.
|
|
* This also creates the "reatRoot" 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`
|
|
*/
|
|
unmountAndReleaseReactRootNode: function(container) {
|
|
var reactRootID = getReactRootID(container);
|
|
var component = instanceByReactRootID[reactRootID];
|
|
if (!component) {
|
|
return false;
|
|
}
|
|
component.unmountComponentFromNode(container);
|
|
delete instanceByReactRootID[reactRootID];
|
|
delete containersByReactRootID[reactRootID];
|
|
if (true) {
|
|
delete rootElementsByReactRootID[reactRootID];
|
|
}
|
|
return true;
|
|
},
|
|
|
|
/**
|
|
* 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 (true) {
|
|
var rootElement = rootElementsByReactRootID[reactRootID];
|
|
if (rootElement && rootElement.parentNode !== container) {
|
|
invariant(
|
|
// Call internalGetID here because getID calls isValid which calls
|
|
// findReactContainerForID (this function).
|
|
internalGetID(rootElement) === reactRootID,
|
|
'ReactMount: Root element ID differed from 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 `id` inside of the supplied `ancestorNode`.
|
|
* Exploits the ID naming scheme to perform the search quickly.
|
|
*
|
|
* @param {DOMEventTarget} ancestorNode Search from this root.
|
|
* @pararm {string} id ID of the DOM representation of the component.
|
|
* @return {DOMEventTarget} DOM node with the supplied `id`.
|
|
* @internal
|
|
*/
|
|
findComponentRoot: function(ancestorNode, id) {
|
|
var firstChildren = [ancestorNode.firstChild];
|
|
var childIndex = 0;
|
|
|
|
while (childIndex < firstChildren.length) {
|
|
var child = firstChildren[childIndex++];
|
|
while (child) {
|
|
var childID = ReactMount.getID(child);
|
|
if (childID) {
|
|
if (id === childID) {
|
|
return child;
|
|
} else if (ReactInstanceHandles.isAncestorIDOf(childID, id)) {
|
|
// 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);
|
|
break;
|
|
} else {
|
|
// TODO This should not be necessary if the ID hierarchy is
|
|
// correct, but is occasionally necessary if the DOM has been
|
|
// modified in unexpected ways.
|
|
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 (true) {
|
|
console.error(
|
|
'Error while invoking `findComponentRoot` with the following ' +
|
|
'ancestor node:',
|
|
ancestorNode
|
|
);
|
|
}
|
|
invariant(
|
|
false,
|
|
'findComponentRoot(..., %s): Unable to find element. This probably ' +
|
|
'means the DOM was unexpectedly mutated (e.g. by the browser).',
|
|
id,
|
|
ReactMount.getID(ancestorNode)
|
|
);
|
|
},
|
|
|
|
|
|
/**
|
|
* React ID utilities.
|
|
*/
|
|
|
|
ATTR_NAME: ATTR_NAME,
|
|
|
|
getID: getID,
|
|
|
|
setID: setID,
|
|
|
|
getNode: getNode,
|
|
|
|
purgeID: purgeID,
|
|
|
|
injection: {}
|
|
};
|
|
|
|
module.exports = ReactMount;
|
|
|
|
})()
|
|
},{"./$":1,"./ReactEventEmitter":34,"./ReactInstanceHandles":37,"./getReactRootElementInContainer":73,"./invariant":78}],40:[function(require,module,exports){
|
|
(function(){/**
|
|
* Copyright 2013 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
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
var ReactComponent = require("./ReactComponent");
|
|
|
|
/**
|
|
* Given a `curChild` and `newChild`, determines if `curChild` should be managed
|
|
* as it exists, as opposed to being destroyed and/or replaced.
|
|
* @param {?ReactComponent} curChild
|
|
* @param {?ReactComponent} newChild
|
|
* @return {!boolean} Whether or not `curChild` should be updated with
|
|
* `newChild`'s props
|
|
*/
|
|
function shouldManageExisting(curChild, newChild) {
|
|
return curChild && newChild && curChild.constructor === newChild.constructor;
|
|
}
|
|
|
|
/**
|
|
* `ReactMultiChild` provides common functionality for components that have
|
|
* multiple children. Standard `ReactCompositeComponent`s do not currently have
|
|
* multiple children. `ReactNativeComponent`s do, however. Other specially
|
|
* reconciled components will also have multiple children. Contains three
|
|
* internally used properties that are used to keep track of state throughout
|
|
* the `updateMultiChild` process.
|
|
*
|
|
* @class ReactMultiChild
|
|
*/
|
|
|
|
/**
|
|
* @lends `ReactMultiChildMixin`.
|
|
*/
|
|
var ReactMultiChildMixin = {
|
|
|
|
enqueueMarkupAt: function(markup, insertAt) {
|
|
this.domOperations = this.domOperations || [];
|
|
this.domOperations.push({insertMarkup: markup, finalIndex: insertAt});
|
|
},
|
|
|
|
enqueueMove: function(originalIndex, finalIndex) {
|
|
this.domOperations = this.domOperations || [];
|
|
this.domOperations.push({moveFrom: originalIndex, finalIndex: finalIndex});
|
|
},
|
|
|
|
enqueueUnmountChildByName: function(name, removeChild) {
|
|
if (ReactComponent.isValidComponent(removeChild)) {
|
|
this.domOperations = this.domOperations || [];
|
|
this.domOperations.push({removeAt: removeChild._domIndex});
|
|
removeChild.unmountComponent && removeChild.unmountComponent();
|
|
delete this._renderedChildren[name];
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Process any pending DOM operations that have been accumulated when updating
|
|
* the UI. By default, we execute the injected `DOMIDOperations` module's
|
|
* `manageChildrenByParentID` which does executes the DOM operations without
|
|
* any animation. It can be used as a reference implementation for special
|
|
* animation based implementations.
|
|
*
|
|
* @abstract
|
|
*/
|
|
processChildDOMOperationsQueue: function() {
|
|
if (this.domOperations) {
|
|
ReactComponent.DOMIDOperations
|
|
.manageChildrenByParentID(this._rootNodeID, this.domOperations);
|
|
this.domOperations = null;
|
|
}
|
|
},
|
|
|
|
unmountMultiChild: function() {
|
|
var renderedChildren = this._renderedChildren;
|
|
for (var name in renderedChildren) {
|
|
if (renderedChildren.hasOwnProperty(name) && renderedChildren[name]) {
|
|
var renderedChild = renderedChildren[name];
|
|
renderedChild.unmountComponent && renderedChild.unmountComponent();
|
|
}
|
|
}
|
|
this._renderedChildren = null;
|
|
},
|
|
|
|
/**
|
|
* Generates markup for a component that holds multiple children. #todo: Allow
|
|
* all `ReactMultiChildMixin`s to support having arrays of children without a
|
|
* container node. This current implementation may assume that children exist
|
|
* at domIndices [0, parentNode.length].
|
|
*
|
|
* Has side effects of (likely) causing events to be registered. Also, every
|
|
* component instance may only be rendered once.
|
|
*
|
|
* @param {?Object} children Flattened children object.
|
|
* @return {!String} The rendered markup.
|
|
*/
|
|
mountMultiChild: function(children, transaction) {
|
|
var accum = '';
|
|
var index = 0;
|
|
for (var name in children) {
|
|
var child = children[name];
|
|
if (children.hasOwnProperty(name) && child) {
|
|
accum += child.mountComponent(
|
|
this._rootNodeID + '.' + name,
|
|
transaction
|
|
);
|
|
child._domIndex = index;
|
|
index++;
|
|
}
|
|
}
|
|
this._renderedChildren = children; // children are in just the right form!
|
|
this.domOperations = null;
|
|
return accum;
|
|
},
|
|
|
|
/**
|
|
* Reconciles new children with old children in three phases.
|
|
*
|
|
* - Adds new content while updating existing children that should remain.
|
|
* - Remove children that are no longer present in the next children.
|
|
* - As a very last step, moves existing dom structures around.
|
|
* - (Comment 1) `curChildrenDOMIndex` is the largest index of the current
|
|
* rendered children that appears in the next children and did not need to
|
|
* be "moved".
|
|
* - (Comment 2) This is the key insight. If any non-removed child's previous
|
|
* index is less than `curChildrenDOMIndex` it must be moved.
|
|
*
|
|
* @param {?Object} children Flattened children object.
|
|
*/
|
|
updateMultiChild: function(nextChildren, transaction) {
|
|
if (!nextChildren && !this._renderedChildren) {
|
|
return;
|
|
} else if (nextChildren && !this._renderedChildren) {
|
|
this._renderedChildren = {}; // lazily allocate backing store with nothing
|
|
} else if (!nextChildren && this._renderedChildren) {
|
|
nextChildren = {};
|
|
}
|
|
var rootDomIdDot = this._rootNodeID + '.';
|
|
var markupBuffer = null; // Accumulate adjacent new children markup.
|
|
var numPendingInsert = 0; // How many root nodes are waiting in markupBuffer
|
|
var loopDomIndex = 0; // Index of loop through new children.
|
|
var curChildrenDOMIndex = 0; // See (Comment 1)
|
|
for (var name in nextChildren) {
|
|
if (!nextChildren.hasOwnProperty(name)) {continue;}
|
|
var curChild = this._renderedChildren[name];
|
|
var nextChild = nextChildren[name];
|
|
if (shouldManageExisting(curChild, nextChild)) {
|
|
if (markupBuffer) {
|
|
this.enqueueMarkupAt(markupBuffer, loopDomIndex - numPendingInsert);
|
|
markupBuffer = null;
|
|
}
|
|
numPendingInsert = 0;
|
|
if (curChild._domIndex < curChildrenDOMIndex) { // (Comment 2)
|
|
this.enqueueMove(curChild._domIndex, loopDomIndex);
|
|
}
|
|
curChildrenDOMIndex = Math.max(curChild._domIndex, curChildrenDOMIndex);
|
|
curChild.receiveProps(nextChild.props, transaction);
|
|
curChild._domIndex = loopDomIndex;
|
|
} else {
|
|
if (curChild) { // !shouldUpdate && curChild => delete
|
|
this.enqueueUnmountChildByName(name, curChild);
|
|
curChildrenDOMIndex =
|
|
Math.max(curChild._domIndex, curChildrenDOMIndex);
|
|
}
|
|
if (nextChild) { // !shouldUpdate && nextChild => insert
|
|
this._renderedChildren[name] = nextChild;
|
|
var nextMarkup =
|
|
nextChild.mountComponent(rootDomIdDot + name, transaction);
|
|
markupBuffer = markupBuffer ? markupBuffer + nextMarkup : nextMarkup;
|
|
numPendingInsert++;
|
|
nextChild._domIndex = loopDomIndex;
|
|
}
|
|
}
|
|
loopDomIndex = nextChild ? loopDomIndex + 1 : loopDomIndex;
|
|
}
|
|
if (markupBuffer) {
|
|
this.enqueueMarkupAt(markupBuffer, loopDomIndex - numPendingInsert);
|
|
}
|
|
for (var childName in this._renderedChildren) { // from other direction
|
|
if (!this._renderedChildren.hasOwnProperty(childName)) { continue; }
|
|
var child = this._renderedChildren[childName];
|
|
if (child && !nextChildren[childName]) {
|
|
this.enqueueUnmountChildByName(childName, child);
|
|
}
|
|
}
|
|
this.processChildDOMOperationsQueue();
|
|
}
|
|
};
|
|
|
|
var ReactMultiChild = {
|
|
Mixin: ReactMultiChildMixin
|
|
};
|
|
|
|
module.exports = ReactMultiChild;
|
|
|
|
})()
|
|
},{"./ReactComponent":23}],41:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 ReactNativeComponent
|
|
* @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 ReactMultiChild = require("./ReactMultiChild");
|
|
var ReactMount = require("./ReactMount");
|
|
|
|
var escapeTextForBrowser = require("./escapeTextForBrowser");
|
|
var flattenChildren = require("./flattenChildren");
|
|
var invariant = require("./invariant");
|
|
var keyOf = require("./keyOf");
|
|
var merge = require("./merge");
|
|
var mixInto = require("./mixInto");
|
|
|
|
var putListener = ReactEventEmitter.putListener;
|
|
var deleteListener = ReactEventEmitter.deleteListener;
|
|
var registrationNames = ReactEventEmitter.registrationNames;
|
|
|
|
// For quickly matching children type, to test if can be treated as content.
|
|
var CONTENT_TYPES = {'string': true, 'number': true};
|
|
|
|
var DANGEROUSLY_SET_INNER_HTML = keyOf({dangerouslySetInnerHTML: null});
|
|
var STYLE = keyOf({style: null});
|
|
|
|
/**
|
|
* @param {?object} props
|
|
*/
|
|
function assertValidProps(props) {
|
|
if (!props) {
|
|
return;
|
|
}
|
|
// Note the use of `==` which checks for null or undefined.
|
|
invariant(
|
|
props.children == null || props.dangerouslySetInnerHTML == null,
|
|
'Can only set one of `children` or `props.dangerouslySetInnerHTML`.'
|
|
);
|
|
invariant(
|
|
props.style == null || typeof props.style === 'object',
|
|
'The `style` prop expects a mapping from style properties to values, ' +
|
|
'not a string.'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @constructor ReactNativeComponent
|
|
* @extends ReactComponent
|
|
* @extends ReactMultiChild
|
|
*/
|
|
function ReactNativeComponent(tag, omitClose) {
|
|
this._tagOpen = '<' + tag;
|
|
this._tagClose = omitClose ? '' : '</' + tag + '>';
|
|
this.tagName = tag.toUpperCase();
|
|
}
|
|
|
|
ReactNativeComponent.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
|
|
* @return {string} The computed markup.
|
|
*/
|
|
mountComponent: function(rootID, transaction) {
|
|
ReactComponent.Mixin.mountComponent.call(this, rootID, transaction);
|
|
assertValidProps(this.props);
|
|
return (
|
|
this._createOpenTagMarkup() +
|
|
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
|
|
* @return {string} Markup of opening tag.
|
|
*/
|
|
_createOpenTagMarkup: function() {
|
|
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 (registrationNames[propKey]) {
|
|
putListener(this._rootNodeID, propKey, propValue);
|
|
} 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 escapedID = escapeTextForBrowser(this._rootNodeID);
|
|
return ret + ' ' + ReactMount.ATTR_NAME + '="' + escapedID + '">';
|
|
},
|
|
|
|
/**
|
|
* 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) {
|
|
return this.mountMultiChild(
|
|
flattenChildren(childrenToUse),
|
|
transaction
|
|
);
|
|
}
|
|
}
|
|
return '';
|
|
},
|
|
|
|
receiveProps: function(nextProps, transaction) {
|
|
assertValidProps(nextProps);
|
|
ReactComponent.Mixin.receiveProps.call(this, nextProps, 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: function(transaction, prevProps) {
|
|
ReactComponent.Mixin.updateComponent.call(this, transaction, prevProps);
|
|
this._updateDOMProperties(prevProps);
|
|
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
|
|
*/
|
|
_updateDOMProperties: function(lastProps) {
|
|
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 (propKey === DANGEROUSLY_SET_INNER_HTML) {
|
|
// http://jsperf.com/emptying-speed
|
|
ReactComponent.DOMIDOperations.updateTextContentByID(
|
|
this._rootNodeID,
|
|
''
|
|
);
|
|
} else if (registrationNames[propKey]) {
|
|
deleteListener(this._rootNodeID, propKey);
|
|
} else {
|
|
ReactComponent.DOMIDOperations.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 (propKey === DANGEROUSLY_SET_INNER_HTML) {
|
|
var lastHtml = lastProp && lastProp.__html;
|
|
var nextHtml = nextProp && nextProp.__html;
|
|
if (lastHtml !== nextHtml) {
|
|
ReactComponent.DOMIDOperations.updateInnerHTMLByID(
|
|
this._rootNodeID,
|
|
nextProp
|
|
);
|
|
}
|
|
} else if (registrationNames[propKey]) {
|
|
putListener(this._rootNodeID, propKey, nextProp);
|
|
} else if (
|
|
DOMProperty.isStandardName[propKey] ||
|
|
DOMProperty.isCustomAttribute(propKey)) {
|
|
ReactComponent.DOMIDOperations.updatePropertyByID(
|
|
this._rootNodeID,
|
|
propKey,
|
|
nextProp
|
|
);
|
|
}
|
|
}
|
|
if (styleUpdates) {
|
|
ReactComponent.DOMIDOperations.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 lastUsedContent =
|
|
CONTENT_TYPES[typeof lastProps.children] ? lastProps.children : null;
|
|
var contentToUse =
|
|
CONTENT_TYPES[typeof nextProps.children] ? nextProps.children : null;
|
|
|
|
// Note the use of `!=` which checks for null or undefined.
|
|
|
|
var lastUsedChildren =
|
|
lastUsedContent != null ? null : lastProps.children;
|
|
var childrenToUse = contentToUse != null ? null : nextProps.children;
|
|
|
|
if (contentToUse != null) {
|
|
var childrenRemoved = lastUsedChildren != null && childrenToUse == null;
|
|
if (childrenRemoved) {
|
|
this.updateMultiChild(null, transaction);
|
|
}
|
|
if (lastUsedContent !== contentToUse) {
|
|
ReactComponent.DOMIDOperations.updateTextContentByID(
|
|
this._rootNodeID,
|
|
'' + contentToUse
|
|
);
|
|
}
|
|
} else {
|
|
var contentRemoved = lastUsedContent != null && contentToUse == null;
|
|
if (contentRemoved) {
|
|
ReactComponent.DOMIDOperations.updateTextContentByID(
|
|
this._rootNodeID,
|
|
''
|
|
);
|
|
}
|
|
this.updateMultiChild(flattenChildren(nextProps.children), transaction);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Destroys all event registrations for this instance. Does not remove from
|
|
* the DOM. That must be done by the parent.
|
|
*
|
|
* @internal
|
|
*/
|
|
unmountComponent: function() {
|
|
ReactEventEmitter.deleteAllListeners(this._rootNodeID);
|
|
ReactComponent.Mixin.unmountComponent.call(this);
|
|
this.unmountMultiChild();
|
|
}
|
|
|
|
};
|
|
|
|
mixInto(ReactNativeComponent, ReactComponent.Mixin);
|
|
mixInto(ReactNativeComponent, ReactNativeComponent.Mixin);
|
|
mixInto(ReactNativeComponent, ReactMultiChild.Mixin);
|
|
|
|
module.exports = ReactNativeComponent;
|
|
|
|
},{"./CSSPropertyOperations":3,"./DOMProperty":7,"./DOMPropertyOperations":8,"./ReactComponent":23,"./ReactEventEmitter":34,"./ReactMount":39,"./ReactMultiChild":40,"./escapeTextForBrowser":67,"./flattenChildren":69,"./invariant":78,"./keyOf":82,"./merge":84,"./mixInto":87}],42:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 ReactOnDOMReady
|
|
*/
|
|
|
|
"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 `ReactOnDOMReady.getPooled()`.
|
|
*
|
|
* @param {?array<function>} initialCollection
|
|
* @class ReactOnDOMReady
|
|
* @implements PooledClass
|
|
* @internal
|
|
*/
|
|
function ReactOnDOMReady(initialCollection) {
|
|
this._queue = initialCollection || null;
|
|
}
|
|
|
|
mixInto(ReactOnDOMReady, {
|
|
|
|
/**
|
|
* 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, component.getDOMNode());
|
|
}
|
|
queue.length = 0;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Resets the internal queue.
|
|
*
|
|
* @internal
|
|
*/
|
|
reset: function() {
|
|
this._queue = null;
|
|
},
|
|
|
|
/**
|
|
* `PooledClass` looks for this.
|
|
*/
|
|
destructor: function() {
|
|
this.reset();
|
|
}
|
|
|
|
});
|
|
|
|
PooledClass.addPoolingTo(ReactOnDOMReady);
|
|
|
|
module.exports = ReactOnDOMReady;
|
|
|
|
},{"./PooledClass":21,"./mixInto":87}],43:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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) {
|
|
invariant(
|
|
ReactOwner.isValidOwner(owner),
|
|
'addComponentAsRefTo(...): Only a ReactOwner can have refs.'
|
|
);
|
|
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) {
|
|
invariant(
|
|
ReactOwner.isValidOwner(owner),
|
|
'removeComponentAsRefFrom(...): Only a ReactOwner can have refs.'
|
|
);
|
|
// 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) {
|
|
invariant(
|
|
component.isOwnedBy(this),
|
|
'attachRef(%s, ...): Only a component\'s owner can store a ref to it.',
|
|
ref
|
|
);
|
|
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":78}],44:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 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`.
|
|
*/
|
|
var TransferStrategies = {
|
|
/**
|
|
* Never transfer `children`.
|
|
*/
|
|
children: emptyFunction,
|
|
/**
|
|
* Transfer the `className` prop by merging them.
|
|
*/
|
|
className: createTransferStrategy(joinClasses),
|
|
/**
|
|
* 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,
|
|
|
|
/**
|
|
* @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) {
|
|
var props = {};
|
|
for (var thatKey in component.props) {
|
|
if (component.props.hasOwnProperty(thatKey)) {
|
|
props[thatKey] = component.props[thatKey];
|
|
}
|
|
}
|
|
for (var thisKey in this.props) {
|
|
if (!this.props.hasOwnProperty(thisKey)) {
|
|
continue;
|
|
}
|
|
var transferStrategy = TransferStrategies[thisKey];
|
|
if (transferStrategy) {
|
|
transferStrategy(props, thisKey, this.props[thisKey]);
|
|
} else if (!props.hasOwnProperty(thisKey)) {
|
|
props[thisKey] = this.props[thisKey];
|
|
}
|
|
}
|
|
component.props = props;
|
|
return component;
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = ReactPropTransferer;
|
|
|
|
},{"./emptyFunction":66,"./joinClasses":80,"./merge":84}],45:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 createObjectFrom = require("./createObjectFrom");
|
|
var invariant = require("./invariant");
|
|
|
|
/**
|
|
* 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|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];
|
|
* invariant(
|
|
* 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'),
|
|
|
|
oneOf: createEnumTypeChecker,
|
|
|
|
instanceOf: createInstanceTypeChecker
|
|
|
|
};
|
|
|
|
var ANONYMOUS = '<<anonymous>>';
|
|
|
|
function createPrimitiveTypeChecker(expectedType) {
|
|
function validatePrimitiveType(propValue, propName, componentName) {
|
|
var propType = typeof propValue;
|
|
if (propType === 'object' && Array.isArray(propValue)) {
|
|
propType = 'array';
|
|
}
|
|
invariant(
|
|
propType === expectedType,
|
|
'Invalid prop `%s` of type `%s` supplied to `%s`, expected `%s`.',
|
|
propName,
|
|
propType,
|
|
componentName,
|
|
expectedType
|
|
);
|
|
}
|
|
return createChainableTypeChecker(validatePrimitiveType);
|
|
}
|
|
|
|
function createEnumTypeChecker(expectedValues) {
|
|
var expectedEnum = createObjectFrom(expectedValues);
|
|
function validateEnumType(propValue, propName, componentName) {
|
|
invariant(
|
|
expectedEnum[propValue],
|
|
'Invalid prop `%s` supplied to `%s`, expected one of %s.',
|
|
propName,
|
|
componentName,
|
|
JSON.stringify(Object.keys(expectedEnum))
|
|
);
|
|
}
|
|
return createChainableTypeChecker(validateEnumType);
|
|
}
|
|
|
|
function createInstanceTypeChecker(expectedClass) {
|
|
function validateInstanceType(propValue, propName, componentName) {
|
|
invariant(
|
|
propValue instanceof expectedClass,
|
|
'Invalid prop `%s` supplied to `%s`, expected instance of `%s`.',
|
|
propName,
|
|
componentName,
|
|
expectedClass.name || ANONYMOUS
|
|
);
|
|
}
|
|
return createChainableTypeChecker(validateInstanceType);
|
|
}
|
|
|
|
function createChainableTypeChecker(validate) {
|
|
function createTypeChecker(isRequired) {
|
|
function checkType(props, propName, componentName) {
|
|
var propValue = props[propName];
|
|
if (propValue != null) {
|
|
// Only validate if there is a value to check.
|
|
validate(propValue, propName, componentName || ANONYMOUS);
|
|
} else {
|
|
invariant(
|
|
!isRequired,
|
|
'Required prop `%s` was not specified in `%s`.',
|
|
propName,
|
|
componentName || ANONYMOUS
|
|
);
|
|
}
|
|
}
|
|
if (!isRequired) {
|
|
checkType.isRequired = createTypeChecker(true);
|
|
}
|
|
return checkType;
|
|
}
|
|
return createTypeChecker(false);
|
|
}
|
|
|
|
module.exports = Props;
|
|
|
|
},{"./createObjectFrom":64,"./invariant":78}],46:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 ReactOnDOMReady = require("./ReactOnDOMReady");
|
|
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 `ReactOnDOMReady` queue for collecting `onDOMReady` callbacks
|
|
* during the performing of the transaction.
|
|
*/
|
|
var ON_DOM_READY_QUEUEING = {
|
|
/**
|
|
* Initializes the internal `onDOMReady` queue.
|
|
*/
|
|
initialize: function() {
|
|
this.reactOnDOMReady.reset();
|
|
},
|
|
|
|
/**
|
|
* After DOM is flushed, invoke all registered `onDOMReady` callbacks.
|
|
*/
|
|
close: function() {
|
|
this.reactOnDOMReady.notifyAll();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 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 = [
|
|
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.reactOnDOMReady = ReactOnDOMReady.getPooled(null);
|
|
}
|
|
|
|
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 ReactOnDOMReady
|
|
*/
|
|
getReactOnDOMReady: function() {
|
|
return this.reactOnDOMReady;
|
|
},
|
|
|
|
/**
|
|
* `PooledClass` looks for this, and will invoke this before allowing this
|
|
* instance to be resused.
|
|
*/
|
|
destructor: function() {
|
|
ReactOnDOMReady.release(this.reactOnDOMReady);
|
|
this.reactOnDOMReady = null;
|
|
}
|
|
};
|
|
|
|
|
|
mixInto(ReactReconcileTransaction, Transaction.Mixin);
|
|
mixInto(ReactReconcileTransaction, Mixin);
|
|
|
|
PooledClass.addPoolingTo(ReactReconcileTransaction);
|
|
|
|
module.exports = ReactReconcileTransaction;
|
|
|
|
},{"./ExecutionEnvironment":19,"./PooledClass":21,"./ReactEventEmitter":34,"./ReactInputSelection":36,"./ReactOnDOMReady":42,"./Transaction":59,"./mixInto":87}],47:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 ReactMarkupChecksum = require("./ReactMarkupChecksum");
|
|
var ReactReconcileTransaction = require("./ReactReconcileTransaction");
|
|
var ReactInstanceHandles = require("./ReactInstanceHandles");
|
|
|
|
/**
|
|
* @param {object} component
|
|
* @param {function} callback
|
|
*/
|
|
function renderComponentToString(component, callback) {
|
|
// We use a callback API to keep the API async in case in the future we ever
|
|
// need it, but in reality this is a synchronous operation.
|
|
var id = ReactInstanceHandles.createReactRootID();
|
|
var transaction = ReactReconcileTransaction.getPooled();
|
|
transaction.reinitializeTransaction();
|
|
try {
|
|
transaction.perform(function() {
|
|
var markup = component.mountComponent(id, transaction);
|
|
markup = ReactMarkupChecksum.addChecksumToMarkup(markup);
|
|
callback(markup);
|
|
}, null);
|
|
} finally {
|
|
ReactReconcileTransaction.release(transaction);
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
renderComponentToString: renderComponentToString
|
|
};
|
|
|
|
},{"./ReactInstanceHandles":37,"./ReactMarkupChecksum":38,"./ReactReconcileTransaction":46}],48:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 ReactComponent = require("./ReactComponent");
|
|
var ReactMount = require("./ReactMount");
|
|
|
|
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.
|
|
* @return {string} Markup for this text node.
|
|
* @internal
|
|
*/
|
|
mountComponent: function(rootID) {
|
|
ReactComponent.Mixin.mountComponent.call(this, rootID);
|
|
return (
|
|
'<span ' + ReactMount.ATTR_NAME + '="' + rootID + '">' +
|
|
escapeTextForBrowser(this.props.text) +
|
|
'</span>'
|
|
);
|
|
},
|
|
|
|
/**
|
|
* Updates this component by updating the text content.
|
|
*
|
|
* @param {object} nextProps Contains the next text content.
|
|
* @param {ReactReconcileTransaction} transaction
|
|
* @internal
|
|
*/
|
|
receiveProps: function(nextProps, transaction) {
|
|
if (nextProps.text !== this.props.text) {
|
|
this.props.text = nextProps.text;
|
|
ReactComponent.DOMIDOperations.updateTextContentByID(
|
|
this._rootNodeID,
|
|
nextProps.text
|
|
);
|
|
}
|
|
}
|
|
|
|
});
|
|
|
|
module.exports = ReactTextComponent;
|
|
|
|
},{"./ReactComponent":23,"./ReactMount":39,"./escapeTextForBrowser":67,"./mixInto":87}],49:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 invariant = require("./invariant");
|
|
|
|
var isBatchingUpdates = false;
|
|
|
|
var dirtyComponents = [];
|
|
|
|
/**
|
|
* Call the provided function in a context within which calls to `setState` and
|
|
* friends are batched such that components aren't updated unnecessarily.
|
|
*/
|
|
function batchedUpdates(callback) {
|
|
if (isBatchingUpdates) {
|
|
// We're already executing in an environment where updates will be batched,
|
|
// so this is a no-op.
|
|
callback();
|
|
return;
|
|
}
|
|
|
|
isBatchingUpdates = true;
|
|
|
|
try {
|
|
callback();
|
|
// TODO: Sort components by depth such that parent components update first
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} catch (error) {
|
|
// IE8 requires `catch` in order to use `finally`.
|
|
throw error;
|
|
} finally {
|
|
dirtyComponents.length = 0;
|
|
isBatchingUpdates = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
invariant(
|
|
!callback || typeof callback === "function",
|
|
'enqueueUpdate(...): You called `setProps`, `replaceProps`, ' +
|
|
'`setState`, `replaceState`, or `forceUpdate` with a callback that ' +
|
|
'isn\'t callable.'
|
|
);
|
|
|
|
if (!isBatchingUpdates) {
|
|
component.performUpdateIfNecessary();
|
|
callback && callback();
|
|
return;
|
|
}
|
|
|
|
dirtyComponents.push(component);
|
|
|
|
if (callback) {
|
|
if (component._pendingCallbacks) {
|
|
component._pendingCallbacks.push(callback);
|
|
} else {
|
|
component._pendingCallbacks = [callback];
|
|
}
|
|
}
|
|
}
|
|
|
|
var ReactUpdates = {
|
|
batchedUpdates: batchedUpdates,
|
|
enqueueUpdate: enqueueUpdate
|
|
};
|
|
|
|
module.exports = ReactUpdates;
|
|
|
|
},{"./invariant":78}],50:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 EventPropagators = require("./EventPropagators");
|
|
var SyntheticEvent = require("./SyntheticEvent");
|
|
var SyntheticFocusEvent = require("./SyntheticFocusEvent");
|
|
var SyntheticKeyboardEvent = require("./SyntheticKeyboardEvent");
|
|
var SyntheticMouseEvent = require("./SyntheticMouseEvent");
|
|
var SyntheticMutationEvent = require("./SyntheticMutationEvent");
|
|
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})
|
|
}
|
|
},
|
|
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})
|
|
}
|
|
},
|
|
DOMCharacterDataModified: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({onDOMCharacterDataModified: true}),
|
|
captured: keyOf({onDOMCharacterDataModifiedCapture: 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})
|
|
}
|
|
},
|
|
// 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})
|
|
}
|
|
},
|
|
mouseUp: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({onMouseUp: true}),
|
|
captured: keyOf({onMouseUpCapture: 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,
|
|
topDoubleClick: eventTypes.doubleClick,
|
|
topDOMCharacterDataModified: eventTypes.DOMCharacterDataModified,
|
|
topDrag: eventTypes.drag,
|
|
topDragEnd: eventTypes.dragEnd,
|
|
topDragEnter: eventTypes.dragEnter,
|
|
topDragExit: eventTypes.dragExit,
|
|
topDragLeave: eventTypes.dragLeave,
|
|
topDragOver: eventTypes.dragOver,
|
|
topDragStart: eventTypes.dragStart,
|
|
topDrop: eventTypes.drop,
|
|
topFocus: eventTypes.focus,
|
|
topInput: eventTypes.input,
|
|
topKeyDown: eventTypes.keyDown,
|
|
topKeyPress: eventTypes.keyPress,
|
|
topKeyUp: eventTypes.keyUp,
|
|
topMouseDown: eventTypes.mouseDown,
|
|
topMouseMove: eventTypes.mouseMove,
|
|
topMouseUp: eventTypes.mouseUp,
|
|
topScroll: eventTypes.scroll,
|
|
topSubmit: eventTypes.submit,
|
|
topTouchCancel: eventTypes.touchCancel,
|
|
topTouchEnd: eventTypes.touchEnd,
|
|
topTouchMove: eventTypes.touchMove,
|
|
topTouchStart: eventTypes.touchStart,
|
|
topWheel: eventTypes.wheel
|
|
};
|
|
|
|
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 = listener(event, 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.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:
|
|
case topLevelTypes.topDoubleClick:
|
|
case topLevelTypes.topDrag:
|
|
case topLevelTypes.topDragEnd:
|
|
case topLevelTypes.topDragEnter:
|
|
case topLevelTypes.topDragExit:
|
|
case topLevelTypes.topDragLeave:
|
|
case topLevelTypes.topDragOver:
|
|
case topLevelTypes.topDragStart:
|
|
case topLevelTypes.topDrop:
|
|
case topLevelTypes.topMouseDown:
|
|
case topLevelTypes.topMouseMove:
|
|
case topLevelTypes.topMouseUp:
|
|
EventConstructor = SyntheticMouseEvent;
|
|
break;
|
|
case topLevelTypes.topDOMCharacterDataModified:
|
|
EventConstructor = SyntheticMutationEvent;
|
|
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;
|
|
}
|
|
invariant(
|
|
EventConstructor,
|
|
'SimpleEventPlugin: Unhandled event type, `%s`.',
|
|
topLevelType
|
|
);
|
|
var event = EventConstructor.getPooled(
|
|
dispatchConfig,
|
|
topLevelTargetID,
|
|
nativeEvent
|
|
);
|
|
EventPropagators.accumulateTwoPhaseDispatches(event);
|
|
return event;
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = SimpleEventPlugin;
|
|
|
|
},{"./EventConstants":13,"./EventPropagators":18,"./SyntheticEvent":51,"./SyntheticFocusEvent":52,"./SyntheticKeyboardEvent":53,"./SyntheticMouseEvent":54,"./SyntheticMutationEvent":55,"./SyntheticTouchEvent":56,"./SyntheticUIEvent":57,"./SyntheticWheelEvent":58,"./invariant":78,"./keyOf":82}],51:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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: null,
|
|
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];
|
|
}
|
|
}
|
|
|
|
if (nativeEvent.defaultPrevented || nativeEvent.returnValue === false) {
|
|
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":21,"./emptyFunction":66,"./getEventTarget":72,"./merge":84,"./mergeInto":86}],52:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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":57}],53:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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");
|
|
|
|
/**
|
|
* @interface KeyboardEvent
|
|
* @see http://www.w3.org/TR/DOM-Level-3-Events/
|
|
*/
|
|
var KeyboardEventInterface = {
|
|
'char': null,
|
|
key: null,
|
|
location: null,
|
|
ctrlKey: null,
|
|
shiftKey: null,
|
|
altKey: null,
|
|
metaKey: null,
|
|
repeat: null,
|
|
locale: null,
|
|
// Legacy Interface
|
|
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":57}],54:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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":57,"./ViewportMetrics":60}],55:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 SyntheticMutationEvent
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
var SyntheticEvent = require("./SyntheticEvent");
|
|
|
|
/**
|
|
* @interface MutationEvent
|
|
* @see http://www.w3.org/TR/DOM-Level-3-Events/
|
|
*/
|
|
var MutationEventInterface = {
|
|
relatedNode: null,
|
|
prevValue: null,
|
|
newValue: null,
|
|
attrName: null,
|
|
attrChange: 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 SyntheticMutationEvent(dispatchConfig, dispatchMarker, nativeEvent) {
|
|
SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent);
|
|
}
|
|
|
|
SyntheticEvent.augmentClass(SyntheticMutationEvent, MutationEventInterface);
|
|
|
|
module.exports = SyntheticMutationEvent;
|
|
|
|
},{"./SyntheticEvent":51}],56:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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/DOM-Level-3-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":57}],57:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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":51}],58:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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) {
|
|
// NOTE: IE<9 does not support x-axis delta.
|
|
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 (
|
|
// Normalize (up is positive).
|
|
'deltaY' in event ? -event.deltaY :
|
|
// Fallback to `wheelDeltaY` for Webkit.
|
|
'wheelDeltaY' in event ? event.wheelDeltaY :
|
|
// Fallback to `wheelDelta` for IE<9.
|
|
'wheelDelta' in event ? event.wheelData : 0
|
|
);
|
|
},
|
|
deltaZ: null,
|
|
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":54}],59:[function(require,module,exports){
|
|
(function(){/**
|
|
* Copyright 2013 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 ran while it is already being ran. 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 `componentDidRender` 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) {
|
|
invariant(
|
|
!this.isInTransaction(),
|
|
'Transaction.perform(...): Cannot initialize a transaction when there ' +
|
|
'is already an outstanding transaction.'
|
|
);
|
|
var memberStart = Date.now();
|
|
var errorToThrow = null;
|
|
var ret;
|
|
try {
|
|
this.initializeAll();
|
|
ret = method.call(scope, a, b, c, d, e, f);
|
|
} catch (error) {
|
|
// IE8 requires `catch` in order to use `finally`.
|
|
errorToThrow = error;
|
|
} finally {
|
|
var memberEnd = Date.now();
|
|
this.methodInvocationTime += (memberEnd - memberStart);
|
|
try {
|
|
this.closeAll();
|
|
} catch (closeError) {
|
|
// If `method` throws, prefer to show that stack trace over any thrown
|
|
// by invoking `closeAll`.
|
|
errorToThrow = errorToThrow || closeError;
|
|
}
|
|
}
|
|
if (errorToThrow) {
|
|
throw errorToThrow;
|
|
}
|
|
return ret;
|
|
},
|
|
|
|
initializeAll: function() {
|
|
this._isInTransaction = true;
|
|
var transactionWrappers = this.transactionWrappers;
|
|
var wrapperInitTimes = this.timingMetrics.wrapperInitTimes;
|
|
var errorToThrow = null;
|
|
for (var i = 0; i < transactionWrappers.length; i++) {
|
|
var initStart = Date.now();
|
|
var wrapper = transactionWrappers[i];
|
|
try {
|
|
this.wrapperInitData[i] = wrapper.initialize ?
|
|
wrapper.initialize.call(this) :
|
|
null;
|
|
} catch (initError) {
|
|
// Prefer to show the stack trace of the first error.
|
|
errorToThrow = errorToThrow || initError;
|
|
this.wrapperInitData[i] = Transaction.OBSERVED_ERROR;
|
|
} finally {
|
|
var curInitTime = wrapperInitTimes[i];
|
|
var initEnd = Date.now();
|
|
wrapperInitTimes[i] = (curInitTime || 0) + (initEnd - initStart);
|
|
}
|
|
}
|
|
if (errorToThrow) {
|
|
throw errorToThrow;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 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() {
|
|
invariant(
|
|
this.isInTransaction(),
|
|
'Transaction.closeAll(): Cannot close transaction when none are open.'
|
|
);
|
|
var transactionWrappers = this.transactionWrappers;
|
|
var wrapperCloseTimes = this.timingMetrics.wrapperCloseTimes;
|
|
var errorToThrow = null;
|
|
for (var i = 0; i < transactionWrappers.length; i++) {
|
|
var wrapper = transactionWrappers[i];
|
|
var closeStart = Date.now();
|
|
var initData = this.wrapperInitData[i];
|
|
try {
|
|
if (initData !== Transaction.OBSERVED_ERROR) {
|
|
wrapper.close && wrapper.close.call(this, initData);
|
|
}
|
|
} catch (closeError) {
|
|
// Prefer to show the stack trace of the first error.
|
|
errorToThrow = errorToThrow || closeError;
|
|
} finally {
|
|
var closeEnd = Date.now();
|
|
var curCloseTime = wrapperCloseTimes[i];
|
|
wrapperCloseTimes[i] = (curCloseTime || 0) + (closeEnd - closeStart);
|
|
}
|
|
}
|
|
this.wrapperInitData.length = 0;
|
|
this._isInTransaction = false;
|
|
if (errorToThrow) {
|
|
throw errorToThrow;
|
|
}
|
|
}
|
|
};
|
|
|
|
var Transaction = {
|
|
|
|
Mixin: Mixin,
|
|
|
|
/**
|
|
* Token to look for to determine if an error occured.
|
|
*/
|
|
OBSERVED_ERROR: {}
|
|
|
|
};
|
|
|
|
module.exports = Transaction;
|
|
|
|
})()
|
|
},{"./invariant":78}],60:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 ViewportMetrics = {
|
|
|
|
currentScrollLeft: 0,
|
|
|
|
currentScrollTop: 0,
|
|
|
|
refreshScrollValues: function() {
|
|
ViewportMetrics.currentScrollLeft =
|
|
document.body.scrollLeft + document.documentElement.scrollLeft;
|
|
ViewportMetrics.currentScrollTop =
|
|
document.body.scrollTop + document.documentElement.scrollTop;
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = ViewportMetrics;
|
|
|
|
},{}],61:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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) {
|
|
invariant(
|
|
next != null,
|
|
'accumulate(...): Accumulated items must be not be null or undefined.'
|
|
);
|
|
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":78}],62:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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
|
|
*/
|
|
|
|
"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;
|
|
|
|
},{}],63:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 (true) {
|
|
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;
|
|
|
|
},{}],64:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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
|
|
*/
|
|
|
|
var hasArrayNature = require("./hasArrayNature");
|
|
|
|
/**
|
|
* 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 (true) {
|
|
if (!hasArrayNature(keys)) {
|
|
throw new TypeError('Must pass an array of keys.');
|
|
}
|
|
}
|
|
|
|
var object = {};
|
|
var is_array = hasArrayNature(values);
|
|
if (typeof values == 'undefined') {
|
|
values = true;
|
|
}
|
|
|
|
for (var ii = keys.length; ii--;) {
|
|
object[keys[ii]] = is_array ? values[ii] : values;
|
|
}
|
|
return object;
|
|
}
|
|
|
|
module.exports = createObjectFrom;
|
|
|
|
},{"./hasArrayNature":75}],65:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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":2}],66:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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":63}],67:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 invariant = require("./invariant");
|
|
|
|
var ESCAPE_LOOKUP = {
|
|
"&": "&",
|
|
">": ">",
|
|
"<": "<",
|
|
"\"": """,
|
|
"'": "'",
|
|
"/": "/"
|
|
};
|
|
|
|
function escaper(match) {
|
|
return ESCAPE_LOOKUP[match];
|
|
}
|
|
|
|
/**
|
|
* Escapes text to prevent scripting attacks.
|
|
*
|
|
* @param {number|string} text Text value to escape.
|
|
* @return {string} An escaped string.
|
|
*/
|
|
function escapeTextForBrowser(text) {
|
|
var type = typeof text;
|
|
invariant(
|
|
type !== 'object',
|
|
'escapeTextForBrowser(...): Attempted to escape an object.'
|
|
);
|
|
if (text === '') {
|
|
return '';
|
|
} else {
|
|
if (type === 'string') {
|
|
return text.replace(/[&><"'\/]/g, escaper);
|
|
} else {
|
|
return (''+text).replace(/[&><"'\/]/g, escaper);
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = escapeTextForBrowser;
|
|
|
|
},{"./invariant":78}],68:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 ex
|
|
* @typechecks
|
|
* @nostacktrace
|
|
*/
|
|
|
|
/**
|
|
* This function transforms error message with arguments into plain text error
|
|
* message, so that it can be passed to window.onerror without losing anything.
|
|
* It can then be transformed back by `erx()` function.
|
|
*
|
|
* Usage:
|
|
* throw new Error(ex('Error %s from %s', errorCode, userID));
|
|
*
|
|
* @param {string} errorMessage
|
|
*/
|
|
|
|
var ex = function(errorMessage/*, arg1, arg2, ...*/) {
|
|
var args = Array.prototype.slice.call(arguments).map(function(arg) {
|
|
return String(arg);
|
|
});
|
|
var expectedLength = errorMessage.split('%s').length - 1;
|
|
|
|
if (expectedLength !== args.length - 1) {
|
|
// something wrong with the formatting string
|
|
return ex('ex args number mismatch: %s', JSON.stringify(args));
|
|
}
|
|
|
|
return ex._prefix + JSON.stringify(args) + ex._suffix;
|
|
};
|
|
|
|
ex._prefix = '<![EX[';
|
|
ex._suffix = ']]>';
|
|
|
|
module.exports = ex;
|
|
|
|
},{}],69:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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;
|
|
invariant(
|
|
!result.hasOwnProperty(name),
|
|
'flattenChildren(...): Encountered two children with the same key, `%s`. ' +
|
|
'Children keys must be unique.',
|
|
name
|
|
);
|
|
result[name] = child;
|
|
}
|
|
|
|
/**
|
|
* Flattens children that are typically specified as `props.children`.
|
|
* @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":78,"./traverseAllChildren":90}],70:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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;
|
|
|
|
},{}],71:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 ge
|
|
*/
|
|
|
|
/**
|
|
* Find a node by ID. Optionally search a sub-tree outside of the document
|
|
*
|
|
* Use ge if you're not sure whether or not the element exists. You can test
|
|
* for existence yourself in your application code.
|
|
*
|
|
* If your application code depends on the existence of the element, use $
|
|
* instead, which will throw in DEV if the element doesn't exist.
|
|
*/
|
|
function ge(arg, root, tag) {
|
|
return typeof arg != 'string' ? arg :
|
|
!root ? document.getElementById(arg) :
|
|
_geFromSubtree(arg, root, tag);
|
|
}
|
|
|
|
function _geFromSubtree(id, root, tag) {
|
|
var elem, children, ii;
|
|
|
|
if (_getNodeID(root) == id) {
|
|
return root;
|
|
} else if (root.getElementsByTagName) {
|
|
// All Elements implement this, which does an iterative DFS, which is
|
|
// faster than recursion and doesn't run into stack depth issues.
|
|
children = root.getElementsByTagName(tag || '*');
|
|
for (ii = 0; ii < children.length; ii++) {
|
|
if (_getNodeID(children[ii]) == id) {
|
|
return children[ii];
|
|
}
|
|
}
|
|
} else {
|
|
// DocumentFragment does not implement getElementsByTagName, so
|
|
// recurse over its children. Its children must be Elements, so
|
|
// each child will use the getElementsByTagName case instead.
|
|
children = root.childNodes;
|
|
for (ii = 0; ii < children.length; ii++) {
|
|
elem = _geFromSubtree(id, children[ii]);
|
|
if (elem) {
|
|
return elem;
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Return the ID value for a given node. This allows us to avoid issues
|
|
* with forms that contain inputs with name="id".
|
|
*
|
|
* @return string (null if attribute not set)
|
|
*/
|
|
function _getNodeID(node) {
|
|
// #document and #document-fragment do not have getAttributeNode.
|
|
var id = node.getAttributeNode && node.getAttributeNode('id');
|
|
return id ? id.value : null;
|
|
}
|
|
|
|
module.exports = ge;
|
|
|
|
},{}],72:[function(require,module,exports){
|
|
(function(){/**
|
|
* Copyright 2013 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";
|
|
|
|
var ExecutionEnvironment = require("./ExecutionEnvironment");
|
|
|
|
/**
|
|
* 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 ||
|
|
ExecutionEnvironment.global;
|
|
// 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;
|
|
|
|
})()
|
|
},{"./ExecutionEnvironment":19}],73:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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";
|
|
|
|
/**
|
|
* @param {DOMElement} container DOM element that may contain a React component
|
|
* @return {?*} DOM element that may have the reactRoot ID, or null.
|
|
*/
|
|
function getReactRootElementInContainer(container) {
|
|
return container && container.firstChild;
|
|
}
|
|
|
|
module.exports = getReactRootElementInContainer;
|
|
|
|
},{}],74:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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) {
|
|
contentKey = 'innerText' in document.createElement('div') ?
|
|
'innerText' :
|
|
'textContent';
|
|
}
|
|
return contentKey;
|
|
}
|
|
|
|
module.exports = getTextContentAccessor;
|
|
|
|
},{"./ExecutionEnvironment":19}],75:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 hasArrayNature
|
|
*/
|
|
|
|
/**
|
|
* 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()).
|
|
*
|
|
* @param obj An object to test.
|
|
* @return bool True if the object is array-like.
|
|
*/
|
|
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
|
|
(typeof obj.nodeType != 'number') &&
|
|
(
|
|
// a real array
|
|
(Array.isArray(obj) ||
|
|
// arguments
|
|
('callee' in obj) || // HTMLCollection/NodeList
|
|
'item' in obj)
|
|
)
|
|
);
|
|
}
|
|
|
|
module.exports = hasArrayNature;
|
|
|
|
},{}],76:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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;
|
|
|
|
},{}],77:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 insertNodeAt
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
/**
|
|
* Inserts `node` at a particular child index. Other nodes move to make room.
|
|
* @param {!Element} root The parent root node to insert into.
|
|
* @param {!node} node The node to insert.
|
|
* @param {!number} atIndex The index in `root` that `node` should exist at.
|
|
*/
|
|
function insertNodeAt(root, node, atIndex) {
|
|
var childNodes = root.childNodes;
|
|
// Remove from parent so that if node is already child of root,
|
|
// `childNodes[atIndex]` already takes into account the removal.
|
|
var curAtIndex = root.childNodes[atIndex];
|
|
if (curAtIndex === node) {
|
|
return node;
|
|
}
|
|
if (node.parentNode) {
|
|
node.parentNode.removeChild(node);
|
|
}
|
|
if (atIndex >= childNodes.length) {
|
|
root.appendChild(node);
|
|
} else {
|
|
root.insertBefore(node, childNodes[atIndex]);
|
|
}
|
|
return node;
|
|
}
|
|
|
|
module.exports = insertNodeAt;
|
|
|
|
},{}],78:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 invariant() to assert state which your program assumes to be true.
|
|
*
|
|
* Provide sprintf style format 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.
|
|
*/
|
|
|
|
function invariant(condition) {
|
|
if (!condition) {
|
|
throw new Error('Invariant Violation');
|
|
}
|
|
}
|
|
|
|
module.exports = invariant;
|
|
|
|
if (true) {
|
|
var invariantDev = 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;
|
|
throw new Error(
|
|
'Invariant Violation: ' +
|
|
format.replace(/%s/g, function() { return args[argIndex++]; })
|
|
);
|
|
}
|
|
};
|
|
|
|
module.exports = invariantDev;
|
|
}
|
|
|
|
},{}],79:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 testNode, useHasFeature;
|
|
if (ExecutionEnvironment.canUseDOM) {
|
|
testNode = document.createElement('div');
|
|
useHasFeature =
|
|
document.implementation &&
|
|
document.implementation.hasFeature &&
|
|
// `hasFeature` always returns true in Firefox 19+.
|
|
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 (!testNode || (capture && !testNode.addEventListener)) {
|
|
return false;
|
|
}
|
|
var element = document.createElement('div');
|
|
|
|
var eventName = 'on' + eventNameSuffix;
|
|
var isSupported = eventName in element;
|
|
|
|
if (!isSupported) {
|
|
element.setAttribute(eventName, '');
|
|
isSupported = typeof element[eventName] === 'function';
|
|
if (typeof element[eventName] !== 'undefined') {
|
|
element[eventName] = undefined;
|
|
}
|
|
element.removeAttribute(eventName);
|
|
}
|
|
|
|
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');
|
|
}
|
|
|
|
element = null;
|
|
return isSupported;
|
|
}
|
|
|
|
module.exports = isEventSupported;
|
|
|
|
},{"./ExecutionEnvironment":19}],80:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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;
|
|
|
|
},{}],81:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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;
|
|
invariant(
|
|
obj instanceof Object && !Array.isArray(obj),
|
|
'keyMirror(...): Argument must be an object.'
|
|
);
|
|
for (key in obj) {
|
|
if (!obj.hasOwnProperty(key)) {
|
|
continue;
|
|
}
|
|
ret[key] = key;
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
module.exports = keyMirror;
|
|
|
|
},{"./invariant":78}],82:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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;
|
|
|
|
},{}],83:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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;
|
|
|
|
},{}],84:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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":86}],85:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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) {
|
|
invariant(
|
|
Array.isArray(one) && Array.isArray(two),
|
|
'Critical assumptions about the merge functions have been violated. ' +
|
|
'This is the fault of the merge functions themselves, not necessarily ' +
|
|
'the callers.'
|
|
);
|
|
},
|
|
|
|
/**
|
|
* @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) {
|
|
invariant(
|
|
!isTerminal(arg) && !Array.isArray(arg),
|
|
'Critical assumptions about the merge functions have been violated. ' +
|
|
'This is the fault of the merge functions themselves, not necessarily ' +
|
|
'the callers.'
|
|
);
|
|
},
|
|
|
|
/**
|
|
* 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) {
|
|
invariant(
|
|
level < MAX_MERGE_DEPTH,
|
|
'Maximum deep merge depth exceeded. You may be attempting to merge ' +
|
|
'circular structures in an unsupported way.'
|
|
);
|
|
},
|
|
|
|
/**
|
|
* Checks that the supplied merge strategy is valid.
|
|
*
|
|
* @param {string} Array merge strategy.
|
|
*/
|
|
checkArrayStrategy: function(strategy) {
|
|
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.'
|
|
);
|
|
},
|
|
|
|
/**
|
|
* 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":78,"./keyMirror":81}],86:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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":85}],87:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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;
|
|
|
|
},{}],88:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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;
|
|
|
|
},{}],89:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013 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 throwIf
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
var throwIf = function(condition, err) {
|
|
if (condition) {
|
|
throw new Error(err);
|
|
}
|
|
};
|
|
|
|
module.exports = throwIf;
|
|
|
|
},{}],90:[function(require,module,exports){
|
|
(function(){/**
|
|
* Copyright 2013 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 ReactComponent = require("./ReactComponent");
|
|
var ReactTextComponent = require("./ReactTextComponent");
|
|
|
|
var invariant = require("./invariant");
|
|
|
|
/**
|
|
* 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.
|
|
* });
|
|
*/
|
|
|
|
/**
|
|
* @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 + '[' + ReactComponent.getKey(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 ?
|
|
'[' + ReactComponent.getKey(children, 0) + ']' :
|
|
nameSoFar;
|
|
if (children === null || children === undefined || 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') {
|
|
invariant(
|
|
children || children.nodeType !== 1,
|
|
'traverseAllChildren(...): Encountered an invalid child; DOM ' +
|
|
'elements are not valid children of React components.'
|
|
);
|
|
for (var key in children) {
|
|
if (children.hasOwnProperty(key)) {
|
|
subtreeCount += traverseAllChildrenImpl(
|
|
children[key],
|
|
nameSoFar + '{' + key + '}',
|
|
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;
|
|
|
|
})()
|
|
},{"./ReactComponent":23,"./ReactTextComponent":48,"./invariant":78}]},{},[22])(22)
|
|
});
|
|
; |