/** * React v0.8.0 */ !function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.React=e():"undefined"!=typeof global?global.React=e():"undefined"!=typeof self&&(self.React=e())}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 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":82,"./escapeTextForBrowser":84,"./hyphenate":97,"./memoizeStringOnly":106}],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){ /** * 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 isTextInputElement = require("./isTextInputElement"); 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 ); } /** * (For old IE.) Replacement getter/setter for the `value` property that gets * set on the active element. */ var newValueProp = { get: function() { return activeElementValueProp.get.call(this); }, set: function(val) { // Cast to a string so we can do equality checks. activeElementValue = '' + val; activeElementValueProp.set.call(this, val); } }; /** * (For old IE.) Starts tracking propertychange events on the passed-in element * and override the value property so that we can distinguish user events from * value changes in JS. */ function startWatchingForValueChange(target, targetID) { activeElement = target; activeElementID = targetID; activeElementValue = target.value; activeElementValueProp = Object.getOwnPropertyDescriptor( target.constructor.prototype, 'value' ); Object.defineProperty(activeElement, 'value', newValueProp); activeElement.attachEvent('onpropertychange', handlePropertyChange); } /** * (For old IE.) Removes the event listeners from the currently-tracked element, * if any exists. */ function stopWatchingForValueChange() { if (!activeElement) { return; } // delete restores the original property definition delete activeElement.value; activeElement.detachEvent('onpropertychange', handlePropertyChange); activeElement = null; activeElementID = null; activeElementValue = null; activeElementValueProp = null; } /** * (For old IE.) Handles a propertychange event, sending a `change` event if * the value of the active element has changed. */ function handlePropertyChange(nativeEvent) { if (nativeEvent.propertyName !== 'value') { return; } var value = nativeEvent.srcElement.value; if (value === activeElementValue) { return; } activeElementValue = value; manualDispatchChangeEvent(nativeEvent); } /** * If a `change` event should be fired, returns the target's ID. */ function getTargetIDForInputEvent( topLevelType, topLevelTarget, topLevelTargetID) { if (topLevelType === topLevelTypes.topInput) { // In modern browsers (i.e., not IE8 or IE9), the input event is exactly // what we want so fall through here and trigger an abstract event return topLevelTargetID; } } // For IE8 and IE9. function handleEventsForInputEventIE( topLevelType, topLevelTarget, topLevelTargetID) { if (topLevelType === topLevelTypes.topFocus) { // In IE8, we can capture almost all .value changes by adding a // propertychange handler and looking for events with propertyName // equal to 'value' // In IE9, propertychange fires for most input events but is buggy and // doesn't fire when text is deleted, but conveniently, selectionchange // appears to fire in all of the remaining cases so we catch those and // forward the event if the value has changed // In either case, we don't want to call the event handler if the value // is changed from JS so we redefine a setter for `.value` that updates // our activeElementValue variable, allowing us to ignore those changes // // stopWatching() should be a noop here but we call it just in case we // missed a blur event somehow. stopWatchingForValueChange(); startWatchingForValueChange(topLevelTarget, topLevelTargetID); } else if (topLevelType === topLevelTypes.topBlur) { stopWatchingForValueChange(); } } // For IE8 and IE9. function getTargetIDForInputEventIE( topLevelType, topLevelTarget, topLevelTargetID) { if (topLevelType === topLevelTypes.topSelectionChange || topLevelType === topLevelTypes.topKeyUp || topLevelType === topLevelTypes.topKeyDown) { // On the selectionchange event, the target is just document which isn't // helpful for us so just check activeElement instead. // // 99% of the time, keydown and keyup aren't necessary. IE8 fails to fire // propertychange on the first input event after setting `value` from a // script and fires only keydown, keypress, keyup. Catching keyup usually // gets it and catching keydown lets us fire an event for the first // keystroke if user does a key repeat (it'll be a little delayed: right // before the second keystroke). Other input methods (e.g., paste) seem to // fire selectionchange normally. if (activeElement && activeElement.value !== activeElementValue) { activeElementValue = activeElement.value; return activeElementID; } } } /** * SECTION: handle `click` event */ function shouldUseClickEvent(elem) { // Use the `click` event to detect changes to checkbox and radio inputs. // This approach works across all browsers, whereas `change` does not fire // until `blur` in IE8. return ( elem.nodeName === 'INPUT' && (elem.type === 'checkbox' || elem.type === 'radio') ); } function getTargetIDForClickEvent( topLevelType, topLevelTarget, topLevelTargetID) { if (topLevelType === topLevelTypes.topClick) { return topLevelTargetID; } } /** * This plugin creates an `onChange` event that normalizes change events * across form elements. This event fires at a time when it's possible to * change the element's value without seeing a flicker. * * Supported elements are: * - input (see `isTextInputElement`) * - textarea * - select */ var ChangeEventPlugin = { eventTypes: eventTypes, /** * @param {string} topLevelType Record from `EventConstants`. * @param {DOMEventTarget} topLevelTarget The listening component root node. * @param {string} topLevelTargetID ID of `topLevelTarget`. * @param {object} nativeEvent Native browser event. * @return {*} An accumulation of synthetic events. * @see {EventPluginHub.extractEvents} */ extractEvents: function( topLevelType, topLevelTarget, topLevelTargetID, nativeEvent) { var getTargetIDFunc, handleEventFunc; if (shouldUseChangeEvent(topLevelTarget)) { if (doesChangeEventBubble) { getTargetIDFunc = getTargetIDForChangeEvent; } else { handleEventFunc = handleEventsForChangeEventIE8; } } else if (isTextInputElement(topLevelTarget)) { if (isInputEventSupported) { getTargetIDFunc = getTargetIDForInputEvent; } else { getTargetIDFunc = getTargetIDForInputEventIE; handleEventFunc = handleEventsForInputEventIE; } } else if (shouldUseClickEvent(topLevelTarget)) { getTargetIDFunc = getTargetIDForClickEvent; } if (getTargetIDFunc) { var targetID = getTargetIDFunc( topLevelType, topLevelTarget, topLevelTargetID ); if (targetID) { var event = SyntheticEvent.getPooled( eventTypes.change, targetID, nativeEvent ); EventPropagators.accumulateTwoPhaseDispatches(event); return event; } } if (handleEventFunc) { handleEventFunc( topLevelType, topLevelTarget, topLevelTargetID ); } } }; module.exports = ChangeEventPlugin; },{"./EventConstants":14,"./EventPluginHub":16,"./EventPropagators":19,"./ExecutionEnvironment":20,"./SyntheticEvent":66,"./isEventSupported":99,"./isTextInputElement":101,"./keyOf":105}],6:[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 CompositionEventPlugin * @typechecks static-only */ "use strict"; var EventConstants = require("./EventConstants"); var EventPropagators = require("./EventPropagators"); var ExecutionEnvironment = require("./ExecutionEnvironment"); var ReactInputSelection = require("./ReactInputSelection"); var SyntheticCompositionEvent = require("./SyntheticCompositionEvent"); var getTextContentAccessor = require("./getTextContentAccessor"); var keyOf = require("./keyOf"); var END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space var START_KEYCODE = 229; var useCompositionEvent = ExecutionEnvironment.canUseDOM && 'CompositionEvent' in window; var topLevelTypes = EventConstants.topLevelTypes; var currentComposition = null; // Events and their corresponding property names. var eventTypes = { compositionEnd: { phasedRegistrationNames: { bubbled: keyOf({onCompositionEnd: null}), captured: keyOf({onCompositionEndCapture: null}) } }, compositionStart: { phasedRegistrationNames: { bubbled: keyOf({onCompositionStart: null}), captured: keyOf({onCompositionStartCapture: null}) } }, compositionUpdate: { phasedRegistrationNames: { bubbled: keyOf({onCompositionUpdate: null}), captured: keyOf({onCompositionUpdateCapture: null}) } } }; /** * Translate native top level events into event types. * * @param {string} topLevelType * @return {object} */ function getCompositionEventType(topLevelType) { switch (topLevelType) { case topLevelTypes.topCompositionStart: return eventTypes.compositionStart; case topLevelTypes.topCompositionEnd: return eventTypes.compositionEnd; case topLevelTypes.topCompositionUpdate: return eventTypes.compositionUpdate; } } /** * Does our fallback best-guess model think this event signifies that * composition has begun? * * @param {string} topLevelType * @param {object} nativeEvent * @return {boolean} */ function isFallbackStart(topLevelType, nativeEvent) { return ( topLevelType === topLevelTypes.topKeyDown && nativeEvent.keyCode === START_KEYCODE ); } /** * Does our fallback mode think that this event is the end of composition? * * @param {string} topLevelType * @param {object} nativeEvent * @return {boolean} */ function isFallbackEnd(topLevelType, nativeEvent) { switch (topLevelType) { case topLevelTypes.topKeyUp: // Command keys insert or clear IME input. return (END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1); case topLevelTypes.topKeyDown: // Expect IME keyCode on each keydown. If we get any other // code we must have exited earlier. return (nativeEvent.keyCode !== START_KEYCODE); case topLevelTypes.topKeyPress: case topLevelTypes.topMouseDown: case topLevelTypes.topBlur: // Events are not possible without cancelling IME. return true; default: return false; } } /** * Helper class stores information about selection and document state * so we can figure out what changed at a later date. * * @param {DOMEventTarget} root */ function FallbackCompositionState(root) { this.root = root; this.startSelection = ReactInputSelection.getSelection(root); this.startValue = this.getText(); } /** * Get current text of input. * * @return {string} */ FallbackCompositionState.prototype.getText = function() { return this.root.value || this.root[getTextContentAccessor()]; }; /** * Text that has changed since the start of composition. * * @return {string} */ FallbackCompositionState.prototype.getData = function() { var endValue = this.getText(); var prefixLength = this.startSelection.start; var suffixLength = this.startValue.length - this.startSelection.end; return endValue.substr( prefixLength, endValue.length - suffixLength - prefixLength ); }; /** * This plugin creates `onCompositionStart`, `onCompositionUpdate` and * `onCompositionEnd` events on inputs, textareas and contentEditable * nodes. */ var CompositionEventPlugin = { eventTypes: eventTypes, /** * @param {string} topLevelType Record from `EventConstants`. * @param {DOMEventTarget} topLevelTarget The listening component root node. * @param {string} topLevelTargetID ID of `topLevelTarget`. * @param {object} nativeEvent Native browser event. * @return {*} An accumulation of synthetic events. * @see {EventPluginHub.extractEvents} */ extractEvents: function( topLevelType, topLevelTarget, topLevelTargetID, nativeEvent) { var eventType; var data; if (useCompositionEvent) { eventType = getCompositionEventType(topLevelType); } else if (!currentComposition) { if (isFallbackStart(topLevelType, nativeEvent)) { eventType = eventTypes.start; currentComposition = new FallbackCompositionState(topLevelTarget); } } else if (isFallbackEnd(topLevelType, nativeEvent)) { eventType = eventTypes.compositionEnd; data = currentComposition.getData(); currentComposition = null; } if (eventType) { var event = SyntheticCompositionEvent.getPooled( eventType, topLevelTargetID, nativeEvent ); if (data) { // Inject data generated from fallback path into the synthetic event. // This matches the property of native CompositionEventInterface. event.data = data; } EventPropagators.accumulateTwoPhaseDispatches(event); return event; } } }; module.exports = CompositionEventPlugin; },{"./EventConstants":14,"./EventPropagators":19,"./ExecutionEnvironment":20,"./ReactInputSelection":47,"./SyntheticCompositionEvent":65,"./getTextContentAccessor":95,"./keyOf":105}],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 DOMChildrenOperations * @typechecks static-only */ "use strict"; var Danger = require("./Danger"); var ReactMultiChildUpdateTypes = require("./ReactMultiChildUpdateTypes"); var getTextContentAccessor = require("./getTextContentAccessor"); /** * The DOM property to use when setting text content. * * @type {string} * @private */ var textContentAccessor = getTextContentAccessor() || 'NA'; /** * Inserts `childNode` as a child of `parentNode` at the `index`. * * @param {DOMElement} parentNode Parent node in which to insert. * @param {DOMElement} childNode Child node to insert. * @param {number} index Index at which to insert the child. * @internal */ function insertChildAt(parentNode, childNode, index) { var childNodes = parentNode.childNodes; if (childNodes[index] === childNode) { return; } // If `childNode` is already a child of `parentNode`, remove it so that // computing `childNodes[index]` takes into account the removal. if (childNode.parentNode === parentNode) { parentNode.removeChild(childNode); } if (index >= childNodes.length) { parentNode.appendChild(childNode); } else { parentNode.insertBefore(childNode, childNodes[index]); } } /** * Operations for updating with DOM children. */ var DOMChildrenOperations = { dangerouslyReplaceNodeWithMarkup: Danger.dangerouslyReplaceNodeWithMarkup, /** * Updates a component's children by processing a series of updates. The * update configurations are each expected to have a `parentNode` property. * * @param {array} updates List of update configurations. * @param {array} markupList List of markup strings. * @internal */ processUpdates: function(updates, markupList) { var update; // Mapping from parent IDs to initial child orderings. var initialChildren = null; // List of children that will be moved or removed. var updatedChildren = null; for (var i = 0; update = updates[i]; i++) { if (update.type === ReactMultiChildUpdateTypes.MOVE_EXISTING || update.type === ReactMultiChildUpdateTypes.REMOVE_NODE) { var updatedIndex = update.fromIndex; var updatedChild = update.parentNode.childNodes[updatedIndex]; var parentID = update.parentID; initialChildren = initialChildren || {}; initialChildren[parentID] = initialChildren[parentID] || []; initialChildren[parentID][updatedIndex] = updatedChild; updatedChildren = updatedChildren || []; updatedChildren.push(updatedChild); } } var renderedMarkup = Danger.dangerouslyRenderMarkup(markupList); // Remove updated children first so that `toIndex` is consistent. if (updatedChildren) { for (var j = 0; j < updatedChildren.length; j++) { updatedChildren[j].parentNode.removeChild(updatedChildren[j]); } } for (var k = 0; update = updates[k]; k++) { switch (update.type) { case ReactMultiChildUpdateTypes.INSERT_MARKUP: insertChildAt( update.parentNode, renderedMarkup[update.markupIndex], update.toIndex ); break; case ReactMultiChildUpdateTypes.MOVE_EXISTING: insertChildAt( update.parentNode, initialChildren[update.parentID][update.fromIndex], update.toIndex ); break; case ReactMultiChildUpdateTypes.TEXT_CONTENT: update.parentNode[textContentAccessor] = update.textContent; break; case ReactMultiChildUpdateTypes.REMOVE_NODE: // Already removed by the for-loop above. break; } } } }; module.exports = DOMChildrenOperations; },{"./Danger":10,"./ReactMultiChildUpdateTypes":53,"./getTextContentAccessor":95}],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 DOMProperty * @typechecks static-only */ /*jslint bitwise: true */ "use strict"; var invariant = require("./invariant"); var DOMPropertyInjection = { /** * Mapping from normalized, camelcased property names to a configuration that * specifies how the associated DOM property should be accessed or rendered. */ MUST_USE_ATTRIBUTE: 0x1, MUST_USE_PROPERTY: 0x2, HAS_SIDE_EFFECTS: 0x4, HAS_BOOLEAN_VALUE: 0x8, HAS_POSITIVE_NUMERIC_VALUE: 0x10, /** * Inject some specialized knowledge about the DOM. This takes a config object * with the following properties: * * isCustomAttribute: function that given an attribute name will return true * if it can be inserted into the DOM verbatim. Useful for data-* or aria-* * attributes where it's impossible to enumerate all of the possible * attribute names, * * Properties: object mapping DOM property name to one of the * DOMPropertyInjection constants or null. If your attribute isn't in here, * it won't get written to the DOM. * * DOMAttributeNames: object mapping React attribute name to the DOM * attribute name. Attribute names not specified use the **lowercase** * normalized name. * * DOMPropertyNames: similar to DOMAttributeNames but for DOM properties. * Property names not specified use the normalized name. * * DOMMutationMethods: Properties that require special mutation methods. If * `value` is undefined, the mutation method should unset the property. * * @param {object} domPropertyConfig the config as described above. */ injectDOMPropertyConfig: function(domPropertyConfig) { var Properties = domPropertyConfig.Properties || {}; var DOMAttributeNames = domPropertyConfig.DOMAttributeNames || {}; var DOMPropertyNames = domPropertyConfig.DOMPropertyNames || {}; var DOMMutationMethods = domPropertyConfig.DOMMutationMethods || {}; if (domPropertyConfig.isCustomAttribute) { DOMProperty._isCustomAttributeFunctions.push( domPropertyConfig.isCustomAttribute ); } for (var propName in Properties) { ("production" !== "development" ? invariant( !DOMProperty.isStandardName[propName], 'injectDOMPropertyConfig(...): You\'re trying to inject DOM property ' + '\'%s\' which has already been injected. You may be accidentally ' + 'injecting the same DOM property config twice, or you may be ' + 'injecting two configs that have conflicting property names.', propName ) : invariant(!DOMProperty.isStandardName[propName])); DOMProperty.isStandardName[propName] = true; var lowerCased = propName.toLowerCase(); DOMProperty.getPossibleStandardName[lowerCased] = propName; var attributeName = DOMAttributeNames[propName]; if (attributeName) { DOMProperty.getPossibleStandardName[attributeName] = propName; } DOMProperty.getAttributeName[propName] = attributeName || lowerCased; DOMProperty.getPropertyName[propName] = DOMPropertyNames[propName] || propName; var mutationMethod = DOMMutationMethods[propName]; if (mutationMethod) { DOMProperty.getMutationMethod[propName] = mutationMethod; } var propConfig = Properties[propName]; DOMProperty.mustUseAttribute[propName] = propConfig & DOMPropertyInjection.MUST_USE_ATTRIBUTE; DOMProperty.mustUseProperty[propName] = propConfig & DOMPropertyInjection.MUST_USE_PROPERTY; DOMProperty.hasSideEffects[propName] = propConfig & DOMPropertyInjection.HAS_SIDE_EFFECTS; DOMProperty.hasBooleanValue[propName] = propConfig & DOMPropertyInjection.HAS_BOOLEAN_VALUE; DOMProperty.hasPositiveNumericValue[propName] = propConfig & DOMPropertyInjection.HAS_POSITIVE_NUMERIC_VALUE; ("production" !== "development" ? invariant( !DOMProperty.mustUseAttribute[propName] || !DOMProperty.mustUseProperty[propName], 'DOMProperty: Cannot require using both attribute and property: %s', propName ) : invariant(!DOMProperty.mustUseAttribute[propName] || !DOMProperty.mustUseProperty[propName])); ("production" !== "development" ? invariant( DOMProperty.mustUseProperty[propName] || !DOMProperty.hasSideEffects[propName], 'DOMProperty: Properties that have side effects must use property: %s', propName ) : invariant(DOMProperty.mustUseProperty[propName] || !DOMProperty.hasSideEffects[propName])); ("production" !== "development" ? invariant( !DOMProperty.hasBooleanValue[propName] || !DOMProperty.hasPositiveNumericValue[propName], 'DOMProperty: Cannot have both boolean and positive numeric value: %s', propName ) : invariant(!DOMProperty.hasBooleanValue[propName] || !DOMProperty.hasPositiveNumericValue[propName])); } } }; var defaultValueCache = {}; /** * DOMProperty exports lookup objects that can be used like functions: * * > DOMProperty.isValid['id'] * true * > DOMProperty.isValid['foobar'] * undefined * * Although this may be confusing, it performs better in general. * * @see http://jsperf.com/key-exists * @see http://jsperf.com/key-missing */ var DOMProperty = { /** * Checks whether a property name is a standard property. * @type {Object} */ isStandardName: {}, /** * Mapping from lowercase property names to the properly cased version, used * to warn in the case of missing properties. * @type {Object} */ getPossibleStandardName: {}, /** * Mapping from normalized names to attribute names that differ. Attribute * names are used when rendering markup or with `*Attribute()`. * @type {Object} */ getAttributeName: {}, /** * Mapping from normalized names to properties on DOM node instances. * (This includes properties that mutate due to external factors.) * @type {Object} */ getPropertyName: {}, /** * Mapping from normalized names to mutation methods. This will only exist if * mutation cannot be set simply by the property or `setAttribute()`. * @type {Object} */ getMutationMethod: {}, /** * Whether the property must be accessed and mutated as an object property. * @type {Object} */ mustUseAttribute: {}, /** * Whether the property must be accessed and mutated using `*Attribute()`. * (This includes anything that fails ` in `.) * @type {Object} */ mustUseProperty: {}, /** * Whether or not setting a value causes side effects such as triggering * resources to be loaded or text selection changes. We must ensure that * the value is only set if it has changed. * @type {Object} */ hasSideEffects: {}, /** * Whether the property should be removed when set to a falsey value. * @type {Object} */ hasBooleanValue: {}, /** * Whether the property must be positive numeric or parse as a positive * numeric and should be removed when set to a falsey value. * @type {Object} */ hasPositiveNumericValue: {}, /** * All of the isCustomAttribute() functions that have been injected. */ _isCustomAttributeFunctions: [], /** * Checks whether a property name is a custom attribute. * @method */ isCustomAttribute: function(attributeName) { return DOMProperty._isCustomAttributeFunctions.some( function(isCustomAttributeFn) { return isCustomAttributeFn.call(null, attributeName); } ); }, /** * Returns the default property value for a DOM property (i.e., not an * attribute). Most default values are '' or false, but not all. Worse yet, * some (in particular, `type`) vary depending on the type of element. * * TODO: Is it better to grab all the possible properties when creating an * element to avoid having to create the same element twice? */ getDefaultValueForProperty: function(nodeName, prop) { var nodeDefaults = defaultValueCache[nodeName]; var testElement; if (!nodeDefaults) { defaultValueCache[nodeName] = nodeDefaults = {}; } if (!(prop in nodeDefaults)) { testElement = document.createElement(nodeName); nodeDefaults[prop] = testElement[prop]; } return nodeDefaults[prop]; }, injection: DOMPropertyInjection }; module.exports = DOMProperty; },{"./invariant":98}],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 DOMPropertyOperations * @typechecks static-only */ "use strict"; var DOMProperty = require("./DOMProperty"); var escapeTextForBrowser = require("./escapeTextForBrowser"); var memoizeStringOnly = require("./memoizeStringOnly"); function shouldIgnoreValue(name, value) { return value == null || DOMProperty.hasBooleanValue[name] && !value || DOMProperty.hasPositiveNumericValue[name] && (isNaN(value) || value < 1); } var processAttributeNameAndPrefix = memoizeStringOnly(function(name) { return escapeTextForBrowser(name) + '="'; }); if ("production" !== "development") { var reactProps = { __owner__: true, children: true, dangerouslySetInnerHTML: true, key: true, ref: true }; var warnedProperties = {}; var warnUnknownProperty = function(name) { if (reactProps[name] || warnedProperties[name]) { return; } warnedProperties[name] = true; var lowerCasedName = name.toLowerCase(); // data-* attributes should be lowercase; suggest the lowercase version var standardName = DOMProperty.isCustomAttribute(lowerCasedName) ? lowerCasedName : DOMProperty.getPossibleStandardName[lowerCasedName]; // For now, only warn when we have a suggested correction. This prevents // logging too much when using transferPropsTo. if (standardName != null) { console.warn( 'Unknown DOM property ' + name + '. Did you mean ' + standardName + '?' ); } }; } /** * Operations for dealing with DOM properties. */ var DOMPropertyOperations = { /** * Creates markup for a property. * * @param {string} name * @param {*} value * @return {?string} Markup string, or null if the property was invalid. */ createMarkupForProperty: function(name, value) { if (DOMProperty.isStandardName[name]) { if (shouldIgnoreValue(name, value)) { return ''; } var attributeName = DOMProperty.getAttributeName[name]; return processAttributeNameAndPrefix(attributeName) + escapeTextForBrowser(value) + '"'; } else if (DOMProperty.isCustomAttribute(name)) { if (value == null) { return ''; } return processAttributeNameAndPrefix(name) + escapeTextForBrowser(value) + '"'; } else if ("production" !== "development") { warnUnknownProperty(name); } return null; }, /** * Sets the value for a property on a node. * * @param {DOMElement} node * @param {string} name * @param {*} value */ setValueForProperty: function(node, name, value) { if (DOMProperty.isStandardName[name]) { var mutationMethod = DOMProperty.getMutationMethod[name]; if (mutationMethod) { mutationMethod(node, value); } else if (shouldIgnoreValue(name, value)) { this.deleteValueForProperty(node, name); } else if (DOMProperty.mustUseAttribute[name]) { node.setAttribute(DOMProperty.getAttributeName[name], '' + value); } else { var propName = DOMProperty.getPropertyName[name]; if (!DOMProperty.hasSideEffects[name] || node[propName] !== value) { node[propName] = value; } } } else if (DOMProperty.isCustomAttribute(name)) { if (value == null) { node.removeAttribute(DOMProperty.getAttributeName[name]); } else { node.setAttribute(name, '' + value); } } else if ("production" !== "development") { warnUnknownProperty(name); } }, /** * Deletes the value for a property on a node. * * @param {DOMElement} node * @param {string} name */ deleteValueForProperty: function(node, name) { if (DOMProperty.isStandardName[name]) { var mutationMethod = DOMProperty.getMutationMethod[name]; if (mutationMethod) { mutationMethod(node, undefined); } else if (DOMProperty.mustUseAttribute[name]) { node.removeAttribute(DOMProperty.getAttributeName[name]); } else { var propName = DOMProperty.getPropertyName[name]; var defaultValue = DOMProperty.getDefaultValueForProperty( node.nodeName, name ); if (!DOMProperty.hasSideEffects[name] || node[propName] !== defaultValue) { node[propName] = defaultValue; } } } else if (DOMProperty.isCustomAttribute(name)) { node.removeAttribute(name); } else if ("production" !== "development") { warnUnknownProperty(name); } } }; module.exports = DOMPropertyOperations; },{"./DOMProperty":8,"./escapeTextForBrowser":84,"./memoizeStringOnly":106}],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 Danger * @typechecks static-only */ /*jslint evil: true, sub: true */ "use strict"; var ExecutionEnvironment = require("./ExecutionEnvironment"); var createNodesFromMarkup = require("./createNodesFromMarkup"); var emptyFunction = require("./emptyFunction"); var getMarkupWrap = require("./getMarkupWrap"); var invariant = require("./invariant"); var mutateHTMLNodeWithMarkup = require("./mutateHTMLNodeWithMarkup"); var OPEN_TAG_NAME_EXP = /^(<[^ \/>]+)/; var RESULT_INDEX_ATTR = 'data-danger-index'; /** * Extracts the `nodeName` from a string of markup. * * NOTE: Extracting the `nodeName` does not require a regular expression match * because we make assumptions about React-generated markup (i.e. there are no * spaces surrounding the opening tag and there is at least one attribute). * * @param {string} markup String of markup. * @return {string} Node name of the supplied markup. * @see http://jsperf.com/extract-nodename */ function getNodeName(markup) { return markup.substring(1, markup.indexOf(' ')); } var Danger = { /** * Renders markup into an array of nodes. The markup is expected to render * into a list of root nodes. Also, the length of `resultList` and * `markupList` should be the same. * * @param {array} markupList List of markup strings to render. * @return {array} List of rendered nodes. * @internal */ dangerouslyRenderMarkup: function(markupList) { ("production" !== "development" ? invariant( ExecutionEnvironment.canUseDOM, 'dangerouslyRenderMarkup(...): Cannot render markup in a Worker ' + 'thread. This is likely a bug in the framework. Please report ' + 'immediately.' ) : invariant(ExecutionEnvironment.canUseDOM)); var nodeName; var markupByNodeName = {}; // Group markup by `nodeName` if a wrap is necessary, else by '*'. for (var i = 0; i < markupList.length; i++) { ("production" !== "development" ? invariant( markupList[i], 'dangerouslyRenderMarkup(...): Missing markup.' ) : invariant(markupList[i])); nodeName = getNodeName(markupList[i]); nodeName = getMarkupWrap(nodeName) ? nodeName : '*'; markupByNodeName[nodeName] = markupByNodeName[nodeName] || []; markupByNodeName[nodeName][i] = markupList[i]; } var resultList = []; var resultListAssignmentCount = 0; for (nodeName in markupByNodeName) { if (!markupByNodeName.hasOwnProperty(nodeName)) { continue; } var markupListByNodeName = markupByNodeName[nodeName]; // This for-in loop skips the holes of the sparse array. The order of // iteration should follow the order of assignment, which happens to match // numerical index order, but we don't rely on that. for (var resultIndex in markupListByNodeName) { if (markupListByNodeName.hasOwnProperty(resultIndex)) { var markup = markupListByNodeName[resultIndex]; // Push the requested markup with an additional RESULT_INDEX_ATTR // attribute. If the markup does not start with a < character, it // will be discarded below (with an appropriate console.error). markupListByNodeName[resultIndex] = markup.replace( OPEN_TAG_NAME_EXP, // This index will be parsed back out below. '$1 ' + RESULT_INDEX_ATTR + '="' + resultIndex + '" ' ); } } // Render each group of markup with similar wrapping `nodeName`. var renderNodes = createNodesFromMarkup( markupListByNodeName.join(''), emptyFunction // Do nothing special with