diff --git a/app/__tests__/button.js b/app/__tests__/button.js index 9deef15..5d6c402 100644 --- a/app/__tests__/button.js +++ b/app/__tests__/button.js @@ -1,34 +1,27 @@ /** @jsx React.DOM */ jest.dontMock('../js/button.jsx'); var React = require('react/addons'); -var gronkButton = require('../js/button.jsx'); +var GronkButton = require('../js/button.jsx'); var TestUtils = React.addons.TestUtils; describe('button test', function() { it('changes the text after click', function() { - var button = ; - TestUtils.renderIntoDocument(button); - var div = TestUtils.findRenderedDOMComponentWithTag( - button, 'div'); - var buttonDom = TestUtils.findRenderedDOMComponentWithTag( - button, 'button'); - expect(div.getDOMNode().textContent).toEqual('hi Count : 0'); - React.addons.TestUtils.Simulate.click(buttonDom.getDOMNode()); - expect(div.getDOMNode().textContent).toEqual('hi Count : 1'); + var button = ; + var DOM = TestUtils.renderIntoDocument(button); + expect(DOM.refs.gronkButton.getDOMNode().textContent).toEqual('hi Count : 0'); + React.addons.TestUtils.Simulate.click(DOM.refs.button.getDOMNode()); + expect(DOM.refs.gronkButton.getDOMNode().textContent).toEqual('hi Count : 1'); }); it('changes the text after multiple clicks', function() { - var button = ; + var button = ; TestUtils.renderIntoDocument(button); - var div = TestUtils.findRenderedDOMComponentWithTag( - button, 'div'); - var buttonDom = TestUtils.findRenderedDOMComponentWithTag( - button, 'button'); - expect(div.getDOMNode().textContent).toEqual('hi Count : 0'); + var DOM = TestUtils.renderIntoDocument(button); + expect(DOM.refs.gronkButton.getDOMNode().textContent).toEqual('hi Count : 0'); for(var i = 1; i < 10; i++){ - React.addons.TestUtils.Simulate.click(buttonDom.getDOMNode()); - expect(div.getDOMNode().textContent).toEqual('hi Count : '+i); + React.addons.TestUtils.Simulate.click(DOM.refs.button.getDOMNode()); + expect(DOM.refs.gronkButton.getDOMNode().textContent).toEqual('hi Count : '+i); } }); diff --git a/app/__tests__/container.js b/app/__tests__/container.js index 899e6fe..e87dab5 100644 --- a/app/__tests__/container.js +++ b/app/__tests__/container.js @@ -7,25 +7,23 @@ var TestUtils = React.addons.TestUtils; describe('load container and check text', function() { it('check text', function() { var container = ; - TestUtils.renderIntoDocument(container); - var h1 = TestUtils.findRenderedDOMComponentWithTag( - container, 'h1'); - expect(h1.getDOMNode().textContent).toEqual('hi'); + var DOM = TestUtils.renderIntoDocument(container); + expect(DOM.refs.header.getDOMNode().textContent).toEqual('hi'); }); }); describe('load container and the right number of objects', function() { - it('check buttons', function() { + it('should have 3 buttons', function() { var container = ; - TestUtils.renderIntoDocument(container); + var DOM = TestUtils.renderIntoDocument(container); var buttons = TestUtils.scryRenderedDOMComponentsWithTag( - container, 'button'); + DOM, 'button'); expect(buttons.length).toEqual(3); }); - it('check buttons', function() { + it('should have 1 input', function() { var container = ; - TestUtils.renderIntoDocument(container); + var DOM = TestUtils.renderIntoDocument(container); var input = TestUtils.scryRenderedDOMComponentsWithTag( - container, 'input'); + DOM, 'input'); expect(input.length).toEqual(1); }); }); diff --git a/app/__tests__/task.js b/app/__tests__/task.js index a0ff33b..2bc420a 100644 --- a/app/__tests__/task.js +++ b/app/__tests__/task.js @@ -8,9 +8,8 @@ describe('task text', function() { it('the task text is accurate', function() { var index = 0; var task = ; - TestUtils.renderIntoDocument(task); - var span = TestUtils.findRenderedDOMComponentWithTag( - task, 'span'); + var DOM = TestUtils.renderIntoDocument(task); + var span = DOM.refs.text; expect(span.getDOMNode().textContent).toEqual('1: hi'); }); @@ -24,9 +23,8 @@ describe('task when clicked fires on destroy event', function() { testing = true; }; var task = ; - TestUtils.renderIntoDocument(task); - var input = TestUtils.findRenderedDOMComponentWithTag( - task, 'button'); + var DOM = TestUtils.renderIntoDocument(task); + var input = DOM.refs.completeButton React.addons.TestUtils.Simulate.click(input.getDOMNode()); expect(testing).toBe(true); }); diff --git a/app/__tests__/taskmanager.js b/app/__tests__/taskmanager.js index 1ec9557..4aa061b 100644 --- a/app/__tests__/taskmanager.js +++ b/app/__tests__/taskmanager.js @@ -7,7 +7,7 @@ var TestUtils = React.addons.TestUtils; describe('load container and check text', function() { it('check text', function() { var taskmanager = ; - TestUtils.renderIntoDocument(taskmanager); + var taskmanager = TestUtils.renderIntoDocument(taskmanager); var button = TestUtils.findRenderedDOMComponentWithTag( taskmanager, 'button'); expect(button.getDOMNode().textContent).toEqual('Add Task'); @@ -17,7 +17,7 @@ describe('load container and check text', function() { describe('load container and check text', function() { it('check text', function() { var taskmanager = ; - TestUtils.renderIntoDocument(taskmanager); + var taskmanager = TestUtils.renderIntoDocument(taskmanager); //the value of the input should be empty var input = TestUtils.findRenderedDOMComponentWithTag(taskmanager, 'input'); expect(input.getDOMNode().value).toBe(''); diff --git a/app/js/app.js b/app/js/app.js index 9ba2d57..643cdbc 100644 --- a/app/js/app.js +++ b/app/js/app.js @@ -3,47 +3,51 @@ var React = require('react'); -module.exports = React.createClass({displayName: 'exports', +module.exports = React.createClass({displayName: "exports", getInitialState: function() { return {count: 0}; }, + propTypes: { + name: React.PropTypes.string.isRequired + }, oClick: function(e) { var count = this.state.count+ 1; this.setState({count: count}); }, render: function () { + var text = "i hope there is a class for this"; return ( - React.DOM.div(null, - React.DOM.button( {onClick:this.oClick}, this.props.name), " Count : ", this.state.count + React.createElement("div", null, + React.createElement("button", {onClick: this.oClick}, this.props.name, " ", text), " Count : ", this.state.count ) ); } }); -},{"react":151}],2:[function(require,module,exports){ +},{"react":180}],2:[function(require,module,exports){ /** @jsx React.DOM */ var React = require('react'), -gronkButton = require('./button.jsx'), -taskManager = require('./taskmanager.jsx'); +GronkButton = require('./button.jsx'), +TaskManager = require('./taskmanager.jsx'); -module.exports = React.createClass({displayName: 'exports', +module.exports = React.createClass({displayName: "exports", render: function () { return ( - React.DOM.div( {className:"ui-builder"}, - React.DOM.h1(null, this.props.name), - gronkButton( {name:"james"} ), - React.DOM.br(null), - gronkButton( {name:"not james"}), - taskManager(null) + React.createElement("div", {className: "ui-builder"}, + React.createElement("h1", null, this.props.name), + React.createElement(GronkButton, {name: "james"}), + React.createElement("br", null), + React.createElement(GronkButton, {name: "not james"}), + React.createElement(TaskManager, null) ) ); } }); -},{"./button.jsx":1,"./taskmanager.jsx":5,"react":151}],3:[function(require,module,exports){ +},{"./button.jsx":1,"./taskmanager.jsx":5,"react":180}],3:[function(require,module,exports){ (function (global){ /** @jsx React.DOM */ var React = require('react/addons'); @@ -51,29 +55,29 @@ var Main = require('./container.jsx'); global.$ = require('jquery'); -React.renderComponent(Main( {name:"Click Me"}), document.getElementById("content")); +React.render(React.createElement(Main, {name: "Click Me"}), document.getElementById("content")); }).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"./container.jsx":2,"jquery":7,"react/addons":8}],4:[function(require,module,exports){ /** @jsx React.DOM */ var React = require('react'); -module.exports = React.createClass({displayName: 'exports', +module.exports = React.createClass({displayName: "exports", render: function () { return ( - React.DOM.div( {className:"task"}, - React.DOM.button( {type:"checkbox", onClick:this.props.onDestroy} , "Complete"), " ", React.DOM.span(null, this.props.index + 1,": ", this.props.name) + React.createElement("div", {className: "task"}, + React.createElement("button", {type: "checkbox", onClick: this.props.onDestroy}, "Complete"), " ", React.createElement("span", null, this.props.index + 1, ": ", this.props.name) ) ); } }); -},{"react":151}],5:[function(require,module,exports){ +},{"react":180}],5:[function(require,module,exports){ /** @jsx React.DOM */ var React = require('react'); var Task = require('./task.jsx'); -module.exports = React.createClass({displayName: 'exports', +module.exports = React.createClass({displayName: "exports", getInitialState: function() { return {items: [], text: '', error: ''}; }, @@ -99,23 +103,23 @@ module.exports = React.createClass({displayName: 'exports', render: function () { var self = this; var createItem = function(itemText,i) { - return React.DOM.li(null , Task( {name:itemText, onDestroy:self.destroyThis.bind(self, i), index:i, key:i})); + return React.createElement("li", {key: i}, React.createElement(Task, {name: itemText, onDestroy: self.destroyThis.bind(self, i), index: i})); }; return ( - React.DOM.div( {className:"taskManager"}, - React.DOM.h4(null, "To Do List"), - React.DOM.form( {onSubmit:this.handleSubmit}, - React.DOM.input( {onChange:this.onChange, value:this.state.text} ), - React.DOM.button(null, "Add Task") - ), - React.DOM.span( {className:"errorText"}, this.state.error), - React.DOM.ul(null, this.state.items.map(createItem)) + React.createElement("div", {className: "taskManager"}, + React.createElement("h4", null, "To Do List"), + React.createElement("form", {onSubmit: this.handleSubmit}, + React.createElement("input", {onChange: this.onChange, value: this.state.text}), + React.createElement("button", null, "Add Task") + ), + React.createElement("span", {className: "errorText"}, this.state.error), + React.createElement("ul", null, this.state.items.map(createItem)) ) ); } }); -},{"./task.jsx":4,"react":151}],6:[function(require,module,exports){ +},{"./task.jsx":4,"react":180}],6:[function(require,module,exports){ // shim for using process in browser var process = module.exports = {}; @@ -9375,1000 +9379,1360 @@ return jQuery; },{}],8:[function(require,module,exports){ module.exports = require('./lib/ReactWithAddons'); -},{"./lib/ReactWithAddons":89}],9:[function(require,module,exports){ +},{"./lib/ReactWithAddons":108}],9:[function(require,module,exports){ /** - * Copyright 2013-2014 Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. * - * 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. + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule AutoFocusMixin * @typechecks static-only */ -"use strict"; +'use strict'; + +var focusNode = require("./focusNode"); var AutoFocusMixin = { componentDidMount: function() { if (this.props.autoFocus) { - this.getDOMNode().focus(); + focusNode(this.getDOMNode()); } } }; module.exports = AutoFocusMixin; -},{}],10:[function(require,module,exports){ -(function (process){ +},{"./focusNode":142}],10:[function(require,module,exports){ /** - * Copyright 2013-2014 Facebook, Inc. + * Copyright 2013-2015 Facebook, Inc. + * All rights reserved. * - * 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. + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. * - * @providesModule CSSCore - * @typechecks + * @providesModule BeforeInputEventPlugin + * @typechecks static-only */ -var invariant = require("./invariant"); +'use strict'; + +var EventConstants = require("./EventConstants"); +var EventPropagators = require("./EventPropagators"); +var ExecutionEnvironment = require("./ExecutionEnvironment"); +var FallbackCompositionState = require("./FallbackCompositionState"); +var SyntheticCompositionEvent = require("./SyntheticCompositionEvent"); +var SyntheticInputEvent = require("./SyntheticInputEvent"); + +var keyOf = require("./keyOf"); + +var END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space +var START_KEYCODE = 229; + +var canUseCompositionEvent = ( + ExecutionEnvironment.canUseDOM && + 'CompositionEvent' in window +); + +var documentMode = null; +if (ExecutionEnvironment.canUseDOM && 'documentMode' in document) { + documentMode = document.documentMode; +} + +// Webkit offers a very useful `textInput` event that can be used to +// directly represent `beforeInput`. The IE `textinput` event is not as +// useful, so we don't use it. +var canUseTextInputEvent = ( + ExecutionEnvironment.canUseDOM && + 'TextEvent' in window && + !documentMode && + !isPresto() +); + +// In IE9+, we have access to composition events, but the data supplied +// by the native compositionend event may be incorrect. Japanese ideographic +// spaces, for instance (\u3000) are not recorded correctly. +var useFallbackCompositionData = ( + ExecutionEnvironment.canUseDOM && + ( + (!canUseCompositionEvent || documentMode && documentMode > 8 && documentMode <= 11) + ) +); /** - * The CSSCore module specifies the API (and implements most of the methods) - * that should be used when dealing with the display of elements (via their - * CSS classes and visibility on screen. It is an API focused on mutating the - * display and not reading it as no logical state should be encoded in the - * display of elements. + * Opera <= 12 includes TextEvent in window, but does not fire + * text input events. Rely on keypress instead. */ +function isPresto() { + var opera = window.opera; + return ( + typeof opera === 'object' && + typeof opera.version === 'function' && + parseInt(opera.version(), 10) <= 12 + ); +} -var CSSCore = { +var SPACEBAR_CODE = 32; +var SPACEBAR_CHAR = String.fromCharCode(SPACEBAR_CODE); - /** - * Adds the class passed in to the element if it doesn't already have it. - * - * @param {DOMElement} element the element to set the class on - * @param {string} className the CSS className - * @return {DOMElement} the element passed in - */ - addClass: function(element, className) { - ("production" !== process.env.NODE_ENV ? invariant( - !/\s/.test(className), - 'CSSCore.addClass takes only a single class name. "%s" contains ' + - 'multiple classes.', className - ) : invariant(!/\s/.test(className))); +var topLevelTypes = EventConstants.topLevelTypes; - if (className) { - if (element.classList) { - element.classList.add(className); - } else if (!CSSCore.hasClass(element, className)) { - element.className = element.className + ' ' + className; - } - } - return element; +// Events and their corresponding property names. +var eventTypes = { + beforeInput: { + phasedRegistrationNames: { + bubbled: keyOf({onBeforeInput: null}), + captured: keyOf({onBeforeInputCapture: null}) + }, + dependencies: [ + topLevelTypes.topCompositionEnd, + topLevelTypes.topKeyPress, + topLevelTypes.topTextInput, + topLevelTypes.topPaste + ] }, - - /** - * Removes the class passed in from the element - * - * @param {DOMElement} element the element to set the class on - * @param {string} className the CSS className - * @return {DOMElement} the element passed in - */ - removeClass: function(element, className) { - ("production" !== process.env.NODE_ENV ? invariant( - !/\s/.test(className), - 'CSSCore.removeClass takes only a single class name. "%s" contains ' + - 'multiple classes.', className - ) : invariant(!/\s/.test(className))); - - if (className) { - if (element.classList) { - element.classList.remove(className); - } else if (CSSCore.hasClass(element, className)) { - element.className = element.className - .replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)', 'g'), '$1') - .replace(/\s+/g, ' ') // multiple spaces to one - .replace(/^\s*|\s*$/g, ''); // trim the ends - } - } - return element; + compositionEnd: { + phasedRegistrationNames: { + bubbled: keyOf({onCompositionEnd: null}), + captured: keyOf({onCompositionEndCapture: null}) + }, + dependencies: [ + topLevelTypes.topBlur, + topLevelTypes.topCompositionEnd, + topLevelTypes.topKeyDown, + topLevelTypes.topKeyPress, + topLevelTypes.topKeyUp, + topLevelTypes.topMouseDown + ] }, - - /** - * Helper to add or remove a class from an element based on a condition. - * - * @param {DOMElement} element the element to set the class on - * @param {string} className the CSS className - * @param {*} bool condition to whether to add or remove the class - * @return {DOMElement} the element passed in - */ - conditionClass: function(element, className, bool) { - return (bool ? CSSCore.addClass : CSSCore.removeClass)(element, className); + compositionStart: { + phasedRegistrationNames: { + bubbled: keyOf({onCompositionStart: null}), + captured: keyOf({onCompositionStartCapture: null}) + }, + dependencies: [ + topLevelTypes.topBlur, + topLevelTypes.topCompositionStart, + topLevelTypes.topKeyDown, + topLevelTypes.topKeyPress, + topLevelTypes.topKeyUp, + topLevelTypes.topMouseDown + ] }, - - /** - * Tests whether the element has the class specified. - * - * @param {DOMNode|DOMWindow} element the element to set the class on - * @param {string} className the CSS className - * @returns {boolean} true if the element has the class, false if not - */ - hasClass: function(element, className) { - ("production" !== process.env.NODE_ENV ? invariant( - !/\s/.test(className), - 'CSS.hasClass takes only a single class name.' - ) : invariant(!/\s/.test(className))); - if (element.classList) { - return !!className && element.classList.contains(className); - } - return (' ' + element.className + ' ').indexOf(' ' + className + ' ') > -1; + compositionUpdate: { + phasedRegistrationNames: { + bubbled: keyOf({onCompositionUpdate: null}), + captured: keyOf({onCompositionUpdateCapture: null}) + }, + dependencies: [ + topLevelTypes.topBlur, + topLevelTypes.topCompositionUpdate, + topLevelTypes.topKeyDown, + topLevelTypes.topKeyPress, + topLevelTypes.topKeyUp, + topLevelTypes.topMouseDown + ] } - }; -module.exports = CSSCore; +// Track whether we've ever handled a keypress on the space key. +var hasSpaceKeypress = false; -}).call(this,require("FWaASH")) -},{"./invariant":129,"FWaASH":6}],11:[function(require,module,exports){ /** - * Copyright 2013-2014 Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @providesModule CSSProperty + * Return whether a native keypress event is assumed to be a command. + * This is required because Firefox fires `keypress` events for key commands + * (cut, copy, select-all, etc.) even though no character is inserted. */ +function isKeypressCommand(nativeEvent) { + return ( + (nativeEvent.ctrlKey || nativeEvent.altKey || nativeEvent.metaKey) && + // ctrlKey && altKey is equivalent to AltGr, and is not a command. + !(nativeEvent.ctrlKey && nativeEvent.altKey) + ); +} -"use strict"; /** - * CSS properties which accept numbers but are not in units of "px". + * Translate native top level events into event types. + * + * @param {string} topLevelType + * @return {object} */ -var isUnitlessNumber = { - columnCount: true, - fillOpacity: true, - flex: true, - flexGrow: true, - flexShrink: true, - fontWeight: true, - lineClamp: true, - lineHeight: true, - opacity: true, - order: true, - orphans: true, - widows: true, - zIndex: true, - zoom: true -}; +function getCompositionEventType(topLevelType) { + switch (topLevelType) { + case topLevelTypes.topCompositionStart: + return eventTypes.compositionStart; + case topLevelTypes.topCompositionEnd: + return eventTypes.compositionEnd; + case topLevelTypes.topCompositionUpdate: + return eventTypes.compositionUpdate; + } +} /** - * @param {string} prefix vendor-specific prefix, eg: Webkit - * @param {string} key style name, eg: transitionDuration - * @return {string} style name prefixed with `prefix`, properly camelCased, eg: - * WebkitTransitionDuration + * Does our fallback best-guess model think this event signifies that + * composition has begun? + * + * @param {string} topLevelType + * @param {object} nativeEvent + * @return {boolean} */ -function prefixKey(prefix, key) { - return prefix + key.charAt(0).toUpperCase() + key.substring(1); +function isFallbackCompositionStart(topLevelType, nativeEvent) { + return ( + topLevelType === topLevelTypes.topKeyDown && + nativeEvent.keyCode === START_KEYCODE + ); } /** - * Support style names that may come passed in prefixed by adding permutations - * of vendor prefixes. - */ -var prefixes = ['Webkit', 'ms', 'Moz', 'O']; - -// Using Object.keys here, or else the vanilla for-in loop makes IE8 go into an -// infinite loop, because it iterates over the newly added props too. -Object.keys(isUnitlessNumber).forEach(function(prop) { - prefixes.forEach(function(prefix) { - isUnitlessNumber[prefixKey(prefix, prop)] = isUnitlessNumber[prop]; - }); -}); + * Does our fallback mode think that this event is the end of composition? + * + * @param {string} topLevelType + * @param {object} nativeEvent + * @return {boolean} + */ +function isFallbackCompositionEnd(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; + } +} /** - * 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. + * Google Input Tools provides composition data via a CustomEvent, + * with the `data` property populated in the `detail` object. If this + * is available on the event object, use it. If not, this is a plain + * composition event and we have nothing special to extract. + * + * @param {object} nativeEvent + * @return {?string} */ -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 +function getDataFromCustomEvent(nativeEvent) { + var detail = nativeEvent.detail; + if (typeof detail === 'object' && 'data' in detail) { + return detail.data; } -}; - -var CSSProperty = { - isUnitlessNumber: isUnitlessNumber, - shorthandPropertyExpansions: shorthandPropertyExpansions -}; + return null; +} -module.exports = CSSProperty; +// Track the current IME composition fallback object, if any. +var currentComposition = null; -},{}],12:[function(require,module,exports){ /** - * Copyright 2013-2014 Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @providesModule CSSPropertyOperations - * @typechecks static-only - */ + * @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 {?object} A SyntheticCompositionEvent. + */ +function extractCompositionEvent( + topLevelType, + topLevelTarget, + topLevelTargetID, + nativeEvent +) { + var eventType; + var fallbackData; -"use strict"; + if (canUseCompositionEvent) { + eventType = getCompositionEventType(topLevelType); + } else if (!currentComposition) { + if (isFallbackCompositionStart(topLevelType, nativeEvent)) { + eventType = eventTypes.compositionStart; + } + } else if (isFallbackCompositionEnd(topLevelType, nativeEvent)) { + eventType = eventTypes.compositionEnd; + } -var CSSProperty = require("./CSSProperty"); + if (!eventType) { + return null; + } -var dangerousStyleValue = require("./dangerousStyleValue"); -var escapeTextForBrowser = require("./escapeTextForBrowser"); -var hyphenate = require("./hyphenate"); -var memoizeStringOnly = require("./memoizeStringOnly"); + if (useFallbackCompositionData) { + // The current composition is stored statically and must not be + // overwritten while composition continues. + if (!currentComposition && eventType === eventTypes.compositionStart) { + currentComposition = FallbackCompositionState.getPooled(topLevelTarget); + } else if (eventType === eventTypes.compositionEnd) { + if (currentComposition) { + fallbackData = currentComposition.getData(); + } + } + } -var processStyleName = memoizeStringOnly(function(styleName) { - return escapeTextForBrowser(hyphenate(styleName)); -}); + var event = SyntheticCompositionEvent.getPooled( + eventType, + topLevelTargetID, + nativeEvent + ); + + if (fallbackData) { + // Inject data generated from fallback path into the synthetic event. + // This matches the property of native CompositionEventInterface. + event.data = fallbackData; + } else { + var customData = getDataFromCustomEvent(nativeEvent); + if (customData !== null) { + event.data = customData; + } + } + + EventPropagators.accumulateTwoPhaseDispatches(event); + return event; +} /** - * Operations for dealing with CSS properties. + * @param {string} topLevelType Record from `EventConstants`. + * @param {object} nativeEvent Native browser event. + * @return {?string} The string corresponding to this `beforeInput` event. */ -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; +function getNativeBeforeInputChars(topLevelType, nativeEvent) { + switch (topLevelType) { + case topLevelTypes.topCompositionEnd: + return getDataFromCustomEvent(nativeEvent); + case topLevelTypes.topKeyPress: + /** + * If native `textInput` events are available, our goal is to make + * use of them. However, there is a special case: the spacebar key. + * In Webkit, preventing default on a spacebar `textInput` event + * cancels character insertion, but it *also* causes the browser + * to fall back to its default spacebar behavior of scrolling the + * page. + * + * Tracking at: + * https://code.google.com/p/chromium/issues/detail?id=355103 + * + * To avoid this issue, use the keypress event as if no `textInput` + * event is available. + */ + var which = nativeEvent.which; + if (which !== SPACEBAR_CODE) { + return null; } - var styleValue = styles[styleName]; - if (styleValue != null) { - serialized += processStyleName(styleName) + ':'; - serialized += dangerousStyleValue(styleName, styleValue) + ';'; + + hasSpaceKeypress = true; + return SPACEBAR_CHAR; + + case topLevelTypes.topTextInput: + // Record the characters to be added to the DOM. + var chars = nativeEvent.data; + + // If it's a spacebar character, assume that we have already handled + // it at the keypress level and bail immediately. Android Chrome + // doesn't give us keycodes, so we need to blacklist it. + if (chars === SPACEBAR_CHAR && hasSpaceKeypress) { + return null; } + + return chars; + + default: + // For other native event types, do nothing. + return null; + } +} + +/** + * For browsers that do not provide the `textInput` event, extract the + * appropriate string to use for SyntheticInputEvent. + * + * @param {string} topLevelType Record from `EventConstants`. + * @param {object} nativeEvent Native browser event. + * @return {?string} The fallback string for this `beforeInput` event. + */ +function getFallbackBeforeInputChars(topLevelType, nativeEvent) { + // If we are currently composing (IME) and using a fallback to do so, + // try to extract the composed characters from the fallback object. + if (currentComposition) { + if ( + topLevelType === topLevelTypes.topCompositionEnd || + isFallbackCompositionEnd(topLevelType, nativeEvent) + ) { + var chars = currentComposition.getData(); + FallbackCompositionState.release(currentComposition); + currentComposition = null; + return chars; } - return serialized || null; - }, + return 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] = ''; - } + switch (topLevelType) { + case topLevelTypes.topPaste: + // If a paste event occurs after a keypress, throw out the input + // chars. Paste events should not lead to BeforeInput events. + return null; + case topLevelTypes.topKeyPress: + /** + * As of v27, Firefox may fire keypress events even when no character + * will be inserted. A few possibilities: + * + * - `which` is `0`. Arrow keys, Esc key, etc. + * + * - `which` is the pressed key code, but no char is available. + * Ex: 'AltGr + d` in Polish. There is no modified character for + * this key combination and no character is inserted into the + * document, but FF fires the keypress for char code `100` anyway. + * No `input` event will occur. + * + * - `which` is the pressed key code, but a command combination is + * being used. Ex: `Cmd+C`. No character is inserted, and no + * `input` event will occur. + */ + if (nativeEvent.which && !isKeypressCommand(nativeEvent)) { + return String.fromCharCode(nativeEvent.which); } - } + return null; + case topLevelTypes.topCompositionEnd: + return useFallbackCompositionData ? null : nativeEvent.data; + default: + return null; } +} -}; +/** + * Extract a SyntheticInputEvent for `beforeInput`, based on either native + * `textInput` or fallback behavior. + * + * @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 {?object} A SyntheticInputEvent. + */ +function extractBeforeInputEvent( + topLevelType, + topLevelTarget, + topLevelTargetID, + nativeEvent +) { + var chars; + + if (canUseTextInputEvent) { + chars = getNativeBeforeInputChars(topLevelType, nativeEvent); + } else { + chars = getFallbackBeforeInputChars(topLevelType, nativeEvent); + } -module.exports = CSSPropertyOperations; + // If no characters are being inserted, no BeforeInput event should + // be fired. + if (!chars) { + return null; + } + + var event = SyntheticInputEvent.getPooled( + eventTypes.beforeInput, + topLevelTargetID, + nativeEvent + ); + + event.data = chars; + EventPropagators.accumulateTwoPhaseDispatches(event); + return event; +} -},{"./CSSProperty":11,"./dangerousStyleValue":115,"./escapeTextForBrowser":117,"./hyphenate":128,"./memoizeStringOnly":137}],13:[function(require,module,exports){ /** - * Copyright 2013-2014 Facebook, Inc. + * Create an `onBeforeInput` event to match + * http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105/#events-inputevents. * - * 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 + * This event plugin is based on the native `textInput` event + * available in Chrome, Safari, Opera, and IE. This event fires after + * `onKeyPress` and `onCompositionEnd`, but before `onInput`. * - * 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. + * `beforeInput` is spec'd but not implemented in any browsers, and + * the `input` event does not provide any useful information about what has + * actually been added, contrary to the spec. Thus, `textInput` is the best + * available event to identify the characters that have actually been inserted + * into the target node. * - * @providesModule ChangeEventPlugin + * This plugin is also responsible for emitting `composition` events, thus + * allowing us to share composition fallback code for both `beforeInput` and + * `composition` event types. */ +var BeforeInputEventPlugin = { -"use strict"; - -var EventConstants = require("./EventConstants"); -var EventPluginHub = require("./EventPluginHub"); -var EventPropagators = require("./EventPropagators"); -var ExecutionEnvironment = require("./ExecutionEnvironment"); -var ReactUpdates = require("./ReactUpdates"); -var SyntheticEvent = require("./SyntheticEvent"); - -var isEventSupported = require("./isEventSupported"); -var isTextInputElement = require("./isTextInputElement"); -var keyOf = require("./keyOf"); + eventTypes: eventTypes, -var topLevelTypes = EventConstants.topLevelTypes; - -var eventTypes = { - change: { - phasedRegistrationNames: { - bubbled: keyOf({onChange: null}), - captured: keyOf({onChangeCapture: null}) - }, - dependencies: [ - topLevelTypes.topBlur, - topLevelTypes.topChange, - topLevelTypes.topClick, - topLevelTypes.topFocus, - topLevelTypes.topInput, - topLevelTypes.topKeyDown, - topLevelTypes.topKeyUp, - topLevelTypes.topSelectionChange - ] + /** + * @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 + ) { + return [ + extractCompositionEvent( + topLevelType, + topLevelTarget, + topLevelTargetID, + nativeEvent + ), + extractBeforeInputEvent( + topLevelType, + topLevelTarget, + topLevelTargetID, + nativeEvent + ) + ]; } }; +module.exports = BeforeInputEventPlugin; + +},{"./EventConstants":23,"./EventPropagators":28,"./ExecutionEnvironment":29,"./FallbackCompositionState":30,"./SyntheticCompositionEvent":114,"./SyntheticInputEvent":118,"./keyOf":165}],11:[function(require,module,exports){ +(function (process){ /** - * For IE shims + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule CSSCore + * @typechecks */ -var activeElement = null; -var activeElementID = null; -var activeElementValue = null; -var activeElementValueProp = null; + +var invariant = require("./invariant"); /** - * SECTION: handle `change` event + * The CSSCore module specifies the API (and implements most of the methods) + * that should be used when dealing with the display of elements (via their + * CSS classes and visibility on screen. It is an API focused on mutating the + * display and not reading it as no logical state should be encoded in the + * display of elements. */ -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 - ); -} +var CSSCore = { -function manualDispatchChangeEvent(nativeEvent) { - var event = SyntheticEvent.getPooled( - eventTypes.change, - activeElementID, - nativeEvent - ); - EventPropagators.accumulateTwoPhaseDispatches(event); + /** + * Adds the class passed in to the element if it doesn't already have it. + * + * @param {DOMElement} element the element to set the class on + * @param {string} className the CSS className + * @return {DOMElement} the element passed in + */ + addClass: function(element, className) { + ("production" !== process.env.NODE_ENV ? invariant( + !/\s/.test(className), + 'CSSCore.addClass takes only a single class name. "%s" contains ' + + 'multiple classes.', className + ) : invariant(!/\s/.test(className))); - // If change and propertychange bubbled, we'd just bind to it like all the - // other events and have it go through ReactEventTopLevelCallback. Since it - // doesn't, we manually listen for the events and so we have to enqueue and - // process the abstract event manually. - // - // Batching is necessary here in order to ensure that all event handlers run - // before the next rerender (including event handlers attached to ancestor - // elements instead of directly on the input). Without this, controlled - // components don't work properly in conjunction with event bubbling because - // the component is rerendered and the value reverted before all the event - // handlers can run. See https://github.com/facebook/react/issues/708. - ReactUpdates.batchedUpdates(runEventInBatch, event); -} + if (className) { + if (element.classList) { + element.classList.add(className); + } else if (!CSSCore.hasClass(element, className)) { + element.className = element.className + ' ' + className; + } + } + return element; + }, -function runEventInBatch(event) { - EventPluginHub.enqueueEvents(event); - EventPluginHub.processEventQueue(); -} + /** + * Removes the class passed in from the element + * + * @param {DOMElement} element the element to set the class on + * @param {string} className the CSS className + * @return {DOMElement} the element passed in + */ + removeClass: function(element, className) { + ("production" !== process.env.NODE_ENV ? invariant( + !/\s/.test(className), + 'CSSCore.removeClass takes only a single class name. "%s" contains ' + + 'multiple classes.', className + ) : invariant(!/\s/.test(className))); -function startWatchingForChangeEventIE8(target, targetID) { - activeElement = target; - activeElementID = targetID; - activeElement.attachEvent('onchange', manualDispatchChangeEvent); -} + if (className) { + if (element.classList) { + element.classList.remove(className); + } else if (CSSCore.hasClass(element, className)) { + element.className = element.className + .replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)', 'g'), '$1') + .replace(/\s+/g, ' ') // multiple spaces to one + .replace(/^\s*|\s*$/g, ''); // trim the ends + } + } + return element; + }, -function stopWatchingForChangeEventIE8() { - if (!activeElement) { - return; - } - activeElement.detachEvent('onchange', manualDispatchChangeEvent); - activeElement = null; - activeElementID = null; -} + /** + * Helper to add or remove a class from an element based on a condition. + * + * @param {DOMElement} element the element to set the class on + * @param {string} className the CSS className + * @param {*} bool condition to whether to add or remove the class + * @return {DOMElement} the element passed in + */ + conditionClass: function(element, className, bool) { + return (bool ? CSSCore.addClass : CSSCore.removeClass)(element, className); + }, -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(); + /** + * Tests whether the element has the class specified. + * + * @param {DOMNode|DOMWindow} element the element to set the class on + * @param {string} className the CSS className + * @return {boolean} true if the element has the class, false if not + */ + hasClass: function(element, className) { + ("production" !== process.env.NODE_ENV ? invariant( + !/\s/.test(className), + 'CSS.hasClass takes only a single class name.' + ) : invariant(!/\s/.test(className))); + if (element.classList) { + return !!className && element.classList.contains(className); + } + return (' ' + element.className + ' ').indexOf(' ' + className + ' ') > -1; } -} +}; +module.exports = CSSCore; + +}).call(this,require("FWaASH")) +},{"./invariant":158,"FWaASH":6}],12:[function(require,module,exports){ /** - * SECTION: handle `input` event + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule CSSProperty */ -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 - ); -} + +'use strict'; /** - * (For old IE.) Replacement getter/setter for the `value` property that gets - * set on the active element. + * CSS properties which accept numbers but are not in units of "px". */ -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); - } +var isUnitlessNumber = { + boxFlex: true, + boxFlexGroup: true, + columnCount: true, + flex: true, + flexGrow: true, + flexShrink: true, + fontWeight: true, + lineClamp: true, + lineHeight: true, + opacity: true, + order: true, + orphans: true, + widows: true, + zIndex: true, + zoom: true, + + // SVG-related properties + fillOpacity: true, + strokeOpacity: true }; /** - * (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. + * @param {string} prefix vendor-specific prefix, eg: Webkit + * @param {string} key style name, eg: transitionDuration + * @return {string} style name prefixed with `prefix`, properly camelCased, eg: + * WebkitTransitionDuration */ -function 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); +function prefixKey(prefix, key) { + return prefix + key.charAt(0).toUpperCase() + key.substring(1); } /** - * (For old IE.) Removes the event listeners from the currently-tracked element, - * if any exists. + * Support style names that may come passed in prefixed by adding permutations + * of vendor prefixes. */ -function stopWatchingForValueChange() { - if (!activeElement) { - return; +var prefixes = ['Webkit', 'ms', 'Moz', 'O']; + +// Using Object.keys here, or else the vanilla for-in loop makes IE8 go into an +// infinite loop, because it iterates over the newly added props too. +Object.keys(isUnitlessNumber).forEach(function(prop) { + prefixes.forEach(function(prefix) { + isUnitlessNumber[prefixKey(prefix, prop)] = isUnitlessNumber[prop]; + }); +}); + +/** + * Most style properties can be unset by doing .style[prop] = '' but IE8 + * doesn't like doing that with shorthand properties so for the properties that + * IE8 breaks on, which are listed here, we instead unset each of the + * individual properties. See http://bugs.jquery.com/ticket/12385. + * The 4-value 'clock' properties like margin, padding, border-width seem to + * behave without any problems. Curiously, list-style works too without any + * special prodding. + */ +var shorthandPropertyExpansions = { + background: { + backgroundImage: true, + backgroundPosition: true, + backgroundRepeat: true, + backgroundColor: true + }, + border: { + borderWidth: true, + borderStyle: true, + borderColor: true + }, + borderBottom: { + borderBottomWidth: true, + borderBottomStyle: true, + borderBottomColor: true + }, + borderLeft: { + borderLeftWidth: true, + borderLeftStyle: true, + borderLeftColor: true + }, + borderRight: { + borderRightWidth: true, + borderRightStyle: true, + borderRightColor: true + }, + borderTop: { + borderTopWidth: true, + borderTopStyle: true, + borderTopColor: true + }, + font: { + fontStyle: true, + fontVariant: true, + fontWeight: true, + fontSize: true, + lineHeight: true, + fontFamily: true } +}; - // delete restores the original property definition - delete activeElement.value; - activeElement.detachEvent('onpropertychange', handlePropertyChange); +var CSSProperty = { + isUnitlessNumber: isUnitlessNumber, + shorthandPropertyExpansions: shorthandPropertyExpansions +}; - activeElement = null; - activeElementID = null; - activeElementValue = null; - activeElementValueProp = null; -} +module.exports = CSSProperty; +},{}],13:[function(require,module,exports){ +(function (process){ /** - * (For old IE.) Handles a propertychange event, sending a `change` event if - * the value of the active element has changed. + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule CSSPropertyOperations + * @typechecks static-only */ -function handlePropertyChange(nativeEvent) { - if (nativeEvent.propertyName !== 'value') { - return; - } - var value = nativeEvent.srcElement.value; - if (value === activeElementValue) { - return; - } - activeElementValue = value; - manualDispatchChangeEvent(nativeEvent); -} +'use strict'; + +var CSSProperty = require("./CSSProperty"); +var ExecutionEnvironment = require("./ExecutionEnvironment"); + +var camelizeStyleName = require("./camelizeStyleName"); +var dangerousStyleValue = require("./dangerousStyleValue"); +var hyphenateStyleName = require("./hyphenateStyleName"); +var memoizeStringOnly = require("./memoizeStringOnly"); +var warning = require("./warning"); -/** - * 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; - } -} +var processStyleName = memoizeStringOnly(function(styleName) { + return hyphenateStyleName(styleName); +}); -// 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(); +var styleFloatAccessor = 'cssFloat'; +if (ExecutionEnvironment.canUseDOM) { + // IE8 only supports accessing cssFloat (standard) as styleFloat + if (document.documentElement.style.cssFloat === undefined) { + styleFloatAccessor = 'styleFloat'; } } -// 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; +if ("production" !== process.env.NODE_ENV) { + // 'msTransform' is correct, but the other prefixes should be capitalized + var badVendoredStyleNamePattern = /^(?:webkit|moz|o)[A-Z]/; + + // style values shouldn't contain a semicolon + var badStyleValueWithSemicolonPattern = /;\s*$/; + + var warnedStyleNames = {}; + var warnedStyleValues = {}; + + var warnHyphenatedStyleName = function(name) { + if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) { + return; } - } -} + warnedStyleNames[name] = true; + ("production" !== process.env.NODE_ENV ? warning( + false, + 'Unsupported style property %s. Did you mean %s?', + name, + camelizeStyleName(name) + ) : null); + }; -/** - * 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') - ); -} + var warnBadVendoredStyleName = function(name) { + if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) { + return; + } -function getTargetIDForClickEvent( - topLevelType, - topLevelTarget, - topLevelTargetID) { - if (topLevelType === topLevelTypes.topClick) { - return topLevelTargetID; - } + warnedStyleNames[name] = true; + ("production" !== process.env.NODE_ENV ? warning( + false, + 'Unsupported vendor-prefixed style property %s. Did you mean %s?', + name, + name.charAt(0).toUpperCase() + name.slice(1) + ) : null); + }; + + var warnStyleValueWithSemicolon = function(name, value) { + if (warnedStyleValues.hasOwnProperty(value) && warnedStyleValues[value]) { + return; + } + + warnedStyleValues[value] = true; + ("production" !== process.env.NODE_ENV ? warning( + false, + 'Style property values shouldn\'t contain a semicolon. ' + + 'Try "%s: %s" instead.', + name, + value.replace(badStyleValueWithSemicolonPattern, '') + ) : null); + }; + + /** + * @param {string} name + * @param {*} value + */ + var warnValidStyle = function(name, value) { + if (name.indexOf('-') > -1) { + warnHyphenatedStyleName(name); + } else if (badVendoredStyleNamePattern.test(name)) { + warnBadVendoredStyleName(name); + } else if (badStyleValueWithSemicolonPattern.test(value)) { + warnStyleValueWithSemicolon(name, value); + } + }; } /** - * 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 + * Operations for dealing with CSS properties. */ -var ChangeEventPlugin = { - - eventTypes: eventTypes, +var CSSPropertyOperations = { /** - * @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} + * 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. + * The result should be HTML-escaped before insertion into the DOM. + * + * @param {object} styles + * @return {?string} */ - extractEvents: function( - topLevelType, - topLevelTarget, - topLevelTargetID, - nativeEvent) { - - var getTargetIDFunc, handleEventFunc; - if (shouldUseChangeEvent(topLevelTarget)) { - if (doesChangeEventBubble) { - getTargetIDFunc = getTargetIDForChangeEvent; - } else { - handleEventFunc = handleEventsForChangeEventIE8; + createMarkupForStyles: function(styles) { + var serialized = ''; + for (var styleName in styles) { + if (!styles.hasOwnProperty(styleName)) { + continue; } - } else if (isTextInputElement(topLevelTarget)) { - if (isInputEventSupported) { - getTargetIDFunc = getTargetIDForInputEvent; - } else { - getTargetIDFunc = getTargetIDForInputEventIE; - handleEventFunc = handleEventsForInputEventIE; + var styleValue = styles[styleName]; + if ("production" !== process.env.NODE_ENV) { + warnValidStyle(styleName, styleValue); } - } 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 (styleValue != null) { + serialized += processStyleName(styleName) + ':'; + serialized += dangerousStyleValue(styleName, styleValue) + ';'; } } + return serialized || null; + }, - if (handleEventFunc) { - handleEventFunc( - topLevelType, - topLevelTarget, - topLevelTargetID - ); + /** + * 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; + } + if ("production" !== process.env.NODE_ENV) { + warnValidStyle(styleName, styles[styleName]); + } + var styleValue = dangerousStyleValue(styleName, styles[styleName]); + if (styleName === 'float') { + styleName = styleFloatAccessor; + } + 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 = ChangeEventPlugin; +module.exports = CSSPropertyOperations; -},{"./EventConstants":23,"./EventPluginHub":25,"./EventPropagators":28,"./ExecutionEnvironment":29,"./ReactUpdates":88,"./SyntheticEvent":96,"./isEventSupported":130,"./isTextInputElement":132,"./keyOf":136}],14:[function(require,module,exports){ +}).call(this,require("FWaASH")) +},{"./CSSProperty":12,"./ExecutionEnvironment":29,"./camelizeStyleName":129,"./dangerousStyleValue":136,"./hyphenateStyleName":156,"./memoizeStringOnly":167,"./warning":179,"FWaASH":6}],14:[function(require,module,exports){ +(function (process){ /** - * Copyright 2013-2014 Facebook, Inc. + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. * - * 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 + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. * - * http://www.apache.org/licenses/LICENSE-2.0 + * @providesModule CallbackQueue + */ + +'use strict'; + +var PooledClass = require("./PooledClass"); + +var assign = require("./Object.assign"); +var invariant = require("./invariant"); + +/** + * A specialized pseudo-event module to help keep track of components waiting to + * be notified when their DOM representations are available for use. * - * 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. + * This implements `PooledClass`, so you should never need to instantiate this. + * Instead, use `CallbackQueue.getPooled()`. * - * @providesModule ClientReactRootIndex - * @typechecks + * @class ReactMountReady + * @implements PooledClass + * @internal */ +function CallbackQueue() { + this._callbacks = null; + this._contexts = null; +} + +assign(CallbackQueue.prototype, { + + /** + * Enqueues a callback to be invoked when `notifyAll` is invoked. + * + * @param {function} callback Invoked when `notifyAll` is invoked. + * @param {?object} context Context to call `callback` with. + * @internal + */ + enqueue: function(callback, context) { + this._callbacks = this._callbacks || []; + this._contexts = this._contexts || []; + this._callbacks.push(callback); + this._contexts.push(context); + }, -"use strict"; + /** + * 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 callbacks = this._callbacks; + var contexts = this._contexts; + if (callbacks) { + ("production" !== process.env.NODE_ENV ? invariant( + callbacks.length === contexts.length, + 'Mismatched list of contexts in callback queue' + ) : invariant(callbacks.length === contexts.length)); + this._callbacks = null; + this._contexts = null; + for (var i = 0, l = callbacks.length; i < l; i++) { + callbacks[i].call(contexts[i]); + } + callbacks.length = 0; + contexts.length = 0; + } + }, -var nextReactRootIndex = 0; + /** + * Resets the internal queue. + * + * @internal + */ + reset: function() { + this._callbacks = null; + this._contexts = null; + }, -var ClientReactRootIndex = { - createReactRootIndex: function() { - return nextReactRootIndex++; + /** + * `PooledClass` looks for this. + */ + destructor: function() { + this.reset(); } -}; -module.exports = ClientReactRootIndex; +}); -},{}],15:[function(require,module,exports){ +PooledClass.addPoolingTo(CallbackQueue); + +module.exports = CallbackQueue; + +}).call(this,require("FWaASH")) +},{"./Object.assign":36,"./PooledClass":37,"./invariant":158,"FWaASH":6}],15:[function(require,module,exports){ /** - * Copyright 2013-2014 Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. * - * 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. + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. * - * @providesModule CompositionEventPlugin - * @typechecks static-only + * @providesModule ChangeEventPlugin */ -"use strict"; +'use strict'; var EventConstants = require("./EventConstants"); +var EventPluginHub = require("./EventPluginHub"); var EventPropagators = require("./EventPropagators"); var ExecutionEnvironment = require("./ExecutionEnvironment"); -var ReactInputSelection = require("./ReactInputSelection"); -var SyntheticCompositionEvent = require("./SyntheticCompositionEvent"); +var ReactUpdates = require("./ReactUpdates"); +var SyntheticEvent = require("./SyntheticEvent"); -var getTextContentAccessor = require("./getTextContentAccessor"); +var isEventSupported = require("./isEventSupported"); +var isTextInputElement = require("./isTextInputElement"); var keyOf = require("./keyOf"); -var END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space -var START_KEYCODE = 229; - -var useCompositionEvent = ( - ExecutionEnvironment.canUseDOM && - 'CompositionEvent' in window -); - -// In IE9+, we have access to composition events, but the data supplied -// by the native compositionend event may be incorrect. In Korean, for example, -// the compositionend event contains only one character regardless of -// how many characters have been composed since compositionstart. -// We therefore use the fallback data while still using the native -// events as triggers. -var useFallbackData = ( - !useCompositionEvent || - 'documentMode' in document && document.documentMode > 8 -); - var topLevelTypes = EventConstants.topLevelTypes; -var currentComposition = null; -// Events and their corresponding property names. var eventTypes = { - compositionEnd: { - phasedRegistrationNames: { - bubbled: keyOf({onCompositionEnd: null}), - captured: keyOf({onCompositionEndCapture: null}) - }, - dependencies: [ - topLevelTypes.topBlur, - topLevelTypes.topCompositionEnd, - topLevelTypes.topKeyDown, - topLevelTypes.topKeyPress, - topLevelTypes.topKeyUp, - topLevelTypes.topMouseDown - ] - }, - compositionStart: { - phasedRegistrationNames: { - bubbled: keyOf({onCompositionStart: null}), - captured: keyOf({onCompositionStartCapture: null}) - }, - dependencies: [ - topLevelTypes.topBlur, - topLevelTypes.topCompositionStart, - topLevelTypes.topKeyDown, - topLevelTypes.topKeyPress, - topLevelTypes.topKeyUp, - topLevelTypes.topMouseDown - ] - }, - compositionUpdate: { + change: { phasedRegistrationNames: { - bubbled: keyOf({onCompositionUpdate: null}), - captured: keyOf({onCompositionUpdateCapture: null}) + bubbled: keyOf({onChange: null}), + captured: keyOf({onChangeCapture: null}) }, dependencies: [ topLevelTypes.topBlur, - topLevelTypes.topCompositionUpdate, + topLevelTypes.topChange, + topLevelTypes.topClick, + topLevelTypes.topFocus, + topLevelTypes.topInput, topLevelTypes.topKeyDown, - topLevelTypes.topKeyPress, topLevelTypes.topKeyUp, - topLevelTypes.topMouseDown + topLevelTypes.topSelectionChange ] } }; /** - * Translate native top level events into event types. - * - * @param {string} topLevelType - * @return {object} + * For IE shims */ -function getCompositionEventType(topLevelType) { - switch (topLevelType) { - case topLevelTypes.topCompositionStart: - return eventTypes.compositionStart; - case topLevelTypes.topCompositionEnd: - return eventTypes.compositionEnd; - case topLevelTypes.topCompositionUpdate: - return eventTypes.compositionUpdate; +var activeElement = null; +var activeElementID = null; +var activeElementValue = null; +var activeElementValueProp = null; + +/** + * SECTION: handle `change` event + */ +function shouldUseChangeEvent(elem) { + return ( + elem.nodeName === 'SELECT' || + (elem.nodeName === 'INPUT' && elem.type === 'file') + ); +} + +var doesChangeEventBubble = false; +if (ExecutionEnvironment.canUseDOM) { + // See `handleChange` comment below + doesChangeEventBubble = isEventSupported('change') && ( + (!('documentMode' in document) || document.documentMode > 8) + ); +} + +function manualDispatchChangeEvent(nativeEvent) { + var event = SyntheticEvent.getPooled( + eventTypes.change, + activeElementID, + nativeEvent + ); + EventPropagators.accumulateTwoPhaseDispatches(event); + + // If change and propertychange bubbled, we'd just bind to it like all the + // other events and have it go through ReactBrowserEventEmitter. Since it + // doesn't, we manually listen for the events and so we have to enqueue and + // process the abstract event manually. + // + // Batching is necessary here in order to ensure that all event handlers run + // before the next rerender (including event handlers attached to ancestor + // elements instead of directly on the input). Without this, controlled + // components don't work properly in conjunction with event bubbling because + // the component is rerendered and the value reverted before all the event + // handlers can run. See https://github.com/facebook/react/issues/708. + ReactUpdates.batchedUpdates(runEventInBatch, event); +} + +function runEventInBatch(event) { + EventPluginHub.enqueueEvents(event); + EventPluginHub.processEventQueue(); +} + +function startWatchingForChangeEventIE8(target, targetID) { + activeElement = target; + activeElementID = targetID; + activeElement.attachEvent('onchange', manualDispatchChangeEvent); +} + +function stopWatchingForChangeEventIE8() { + if (!activeElement) { + return; + } + activeElement.detachEvent('onchange', manualDispatchChangeEvent); + activeElement = null; + activeElementID = null; +} + +function getTargetIDForChangeEvent( + topLevelType, + topLevelTarget, + topLevelTargetID) { + if (topLevelType === topLevelTypes.topChange) { + return topLevelTargetID; + } +} +function handleEventsForChangeEventIE8( + topLevelType, + topLevelTarget, + topLevelTargetID) { + if (topLevelType === topLevelTypes.topFocus) { + // stopWatching() should be a noop here but we call it just in case we + // missed a blur event somehow. + stopWatchingForChangeEventIE8(); + startWatchingForChangeEventIE8(topLevelTarget, topLevelTargetID); + } else if (topLevelType === topLevelTypes.topBlur) { + stopWatchingForChangeEventIE8(); } } + /** - * Does our fallback best-guess model think this event signifies that - * composition has begun? - * - * @param {string} topLevelType - * @param {object} nativeEvent - * @return {boolean} + * SECTION: handle `input` event */ -function isFallbackStart(topLevelType, nativeEvent) { - return ( - topLevelType === topLevelTypes.topKeyDown && - nativeEvent.keyCode === START_KEYCODE +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); } /** - * Does our fallback mode think that this event is the end of composition? - * - * @param {string} topLevelType - * @param {object} nativeEvent - * @return {boolean} + * (For old IE.) Removes the event listeners from the currently-tracked element, + * if any exists. */ -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; +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; } /** - * Helper class stores information about selection and document state - * so we can figure out what changed at a later date. - * - * @param {DOMEventTarget} root + * (For old IE.) Handles a propertychange event, sending a `change` event if + * the value of the active element has changed. */ -function FallbackCompositionState(root) { - this.root = root; - this.startSelection = ReactInputSelection.getSelection(root); - this.startValue = this.getText(); +function handlePropertyChange(nativeEvent) { + if (nativeEvent.propertyName !== 'value') { + return; + } + var value = nativeEvent.srcElement.value; + if (value === activeElementValue) { + return; + } + activeElementValue = value; + + manualDispatchChangeEvent(nativeEvent); } /** - * Get current text of input. - * - * @return {string} + * If a `change` event should be fired, returns the target's ID. */ -FallbackCompositionState.prototype.getText = function() { - return this.root.value || this.root[getTextContentAccessor()]; -}; +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; + } + } +} + /** - * Text that has changed since the start of composition. - * - * @return {string} + * SECTION: handle `click` event */ -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 +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 `onCompositionStart`, `onCompositionUpdate` and - * `onCompositionEnd` events on inputs, textareas and contentEditable - * nodes. + * 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 CompositionEventPlugin = { +var ChangeEventPlugin = { eventTypes: eventTypes, @@ -10386,85 +10750,100 @@ var CompositionEventPlugin = { topLevelTargetID, nativeEvent) { - var eventType; - var data; - - if (useCompositionEvent) { - eventType = getCompositionEventType(topLevelType); - } else if (!currentComposition) { - if (isFallbackStart(topLevelType, nativeEvent)) { - eventType = eventTypes.compositionStart; + var getTargetIDFunc, handleEventFunc; + if (shouldUseChangeEvent(topLevelTarget)) { + if (doesChangeEventBubble) { + getTargetIDFunc = getTargetIDForChangeEvent; + } else { + handleEventFunc = handleEventsForChangeEventIE8; } - } else if (isFallbackEnd(topLevelType, nativeEvent)) { - eventType = eventTypes.compositionEnd; - } - - if (useFallbackData) { - // The current composition is stored statically and must not be - // overwritten while composition continues. - if (!currentComposition && eventType === eventTypes.compositionStart) { - currentComposition = new FallbackCompositionState(topLevelTarget); - } else if (eventType === eventTypes.compositionEnd) { - if (currentComposition) { - data = currentComposition.getData(); - currentComposition = null; - } + } else if (isTextInputElement(topLevelTarget)) { + if (isInputEventSupported) { + getTargetIDFunc = getTargetIDForInputEvent; + } else { + getTargetIDFunc = getTargetIDForInputEventIE; + handleEventFunc = handleEventsForInputEventIE; } + } else if (shouldUseClickEvent(topLevelTarget)) { + getTargetIDFunc = getTargetIDForClickEvent; } - if (eventType) { - var event = SyntheticCompositionEvent.getPooled( - eventType, - topLevelTargetID, - nativeEvent + if (getTargetIDFunc) { + var targetID = getTargetIDFunc( + topLevelType, + topLevelTarget, + topLevelTargetID ); - if (data) { - // Inject data generated from fallback path into the synthetic event. - // This matches the property of native CompositionEventInterface. - event.data = data; + if (targetID) { + var event = SyntheticEvent.getPooled( + eventTypes.change, + targetID, + nativeEvent + ); + EventPropagators.accumulateTwoPhaseDispatches(event); + return event; } - EventPropagators.accumulateTwoPhaseDispatches(event); - return event; + } + + if (handleEventFunc) { + handleEventFunc( + topLevelType, + topLevelTarget, + topLevelTargetID + ); } } + }; -module.exports = CompositionEventPlugin; +module.exports = ChangeEventPlugin; -},{"./EventConstants":23,"./EventPropagators":28,"./ExecutionEnvironment":29,"./ReactInputSelection":64,"./SyntheticCompositionEvent":94,"./getTextContentAccessor":126,"./keyOf":136}],16:[function(require,module,exports){ +},{"./EventConstants":23,"./EventPluginHub":25,"./EventPropagators":28,"./ExecutionEnvironment":29,"./ReactUpdates":107,"./SyntheticEvent":116,"./isEventSupported":159,"./isTextInputElement":161,"./keyOf":165}],16:[function(require,module,exports){ /** - * Copyright 2013-2014 Facebook, Inc. + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. * - * 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 + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. * - * http://www.apache.org/licenses/LICENSE-2.0 + * @providesModule ClientReactRootIndex + * @typechecks + */ + +'use strict'; + +var nextReactRootIndex = 0; + +var ClientReactRootIndex = { + createReactRootIndex: function() { + return nextReactRootIndex++; + } +}; + +module.exports = ClientReactRootIndex; + +},{}],17:[function(require,module,exports){ +(function (process){ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. * - * 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. + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule DOMChildrenOperations * @typechecks static-only */ -"use strict"; +'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(); +var setTextContent = require("./setTextContent"); +var invariant = require("./invariant"); /** * Inserts `childNode` as a child of `parentNode` at the `index`. @@ -10475,45 +10854,14 @@ var textContentAccessor = getTextContentAccessor(); * @internal */ function insertChildAt(parentNode, childNode, index) { - var childNodes = parentNode.childNodes; - if (childNodes[index] === childNode) { - return; - } - // If `childNode` is already a child of `parentNode`, remove it so that - // computing `childNodes[index]` takes into account the removal. - if (childNode.parentNode === parentNode) { - parentNode.removeChild(childNode); - } - if (index >= childNodes.length) { - parentNode.appendChild(childNode); - } else { - parentNode.insertBefore(childNode, childNodes[index]); - } -} - -/** - * Sets the text content of `node` to `text`. - * - * @param {DOMElement} node Node to change - * @param {string} text New text content - */ -var updateTextContent; -if (textContentAccessor === 'textContent') { - updateTextContent = function(node, text) { - node.textContent = text; - }; -} else { - updateTextContent = function(node, text) { - // In order to preserve newlines correctly, we can't use .innerText to set - // the contents (see #1080), so we empty the element then append a text node - while (node.firstChild) { - node.removeChild(node.firstChild); - } - if (text) { - var doc = node.ownerDocument || document; - node.appendChild(doc.createTextNode(text)); - } - }; + // By exploiting arrays returning `undefined` for an undefined index, we can + // rely exclusively on `insertBefore(node, null)` instead of also using + // `appendChild(node)`. However, using `undefined` is not allowed by all + // browsers so we must replace it with `null`. + parentNode.insertBefore( + childNode, + parentNode.childNodes[index] || null + ); } /** @@ -10523,7 +10871,7 @@ var DOMChildrenOperations = { dangerouslyReplaceNodeWithMarkup: Danger.dangerouslyReplaceNodeWithMarkup, - updateTextContent: updateTextContent, + updateTextContent: setTextContent, /** * Updates a component's children by processing a series of updates. The @@ -10540,13 +10888,26 @@ var DOMChildrenOperations = { // List of children that will be moved or removed. var updatedChildren = null; - for (var i = 0; update = updates[i]; i++) { + for (var i = 0; i < updates.length; i++) { + update = updates[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; + ("production" !== process.env.NODE_ENV ? invariant( + updatedChild, + 'processUpdates(): Unable to find child %s of element. This ' + + 'probably means the DOM was unexpectedly mutated (e.g., by the ' + + 'browser), usually due to forgetting a when using tables, ' + + 'nesting tags like
,

, or , or using non-SVG elements ' + + 'in an parent. Try inspecting the child nodes of the element ' + + 'with React ID `%s`.', + updatedIndex, + parentID + ) : invariant(updatedChild)); + initialChildren = initialChildren || {}; initialChildren[parentID] = initialChildren[parentID] || []; initialChildren[parentID][updatedIndex] = updatedChild; @@ -10565,7 +10926,8 @@ var DOMChildrenOperations = { } } - for (var k = 0; update = updates[k]; k++) { + for (var k = 0; k < updates.length; k++) { + update = updates[k]; switch (update.type) { case ReactMultiChildUpdateTypes.INSERT_MARKUP: insertChildAt( @@ -10582,7 +10944,7 @@ var DOMChildrenOperations = { ); break; case ReactMultiChildUpdateTypes.TEXT_CONTENT: - updateTextContent( + setTextContent( update.parentNode, update.textContent ); @@ -10598,22 +10960,16 @@ var DOMChildrenOperations = { module.exports = DOMChildrenOperations; -},{"./Danger":19,"./ReactMultiChildUpdateTypes":71,"./getTextContentAccessor":126}],17:[function(require,module,exports){ +}).call(this,require("FWaASH")) +},{"./Danger":20,"./ReactMultiChildUpdateTypes":86,"./invariant":158,"./setTextContent":173,"FWaASH":6}],18:[function(require,module,exports){ (function (process){ /** - * Copyright 2013-2014 Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. * - * 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. + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule DOMProperty * @typechecks static-only @@ -10621,10 +10977,14 @@ module.exports = DOMChildrenOperations; /*jslint bitwise: true */ -"use strict"; +'use strict'; var invariant = require("./invariant"); +function checkMask(value, bitmask) { + return (value & bitmask) === bitmask; +} + var DOMPropertyInjection = { /** * Mapping from normalized, camelcased property names to a configuration that @@ -10634,7 +10994,9 @@ var DOMPropertyInjection = { MUST_USE_PROPERTY: 0x2, HAS_SIDE_EFFECTS: 0x4, HAS_BOOLEAN_VALUE: 0x8, - HAS_POSITIVE_NUMERIC_VALUE: 0x10, + HAS_NUMERIC_VALUE: 0x10, + HAS_POSITIVE_NUMERIC_VALUE: 0x20 | 0x10, + HAS_OVERLOADED_BOOLEAN_VALUE: 0x40, /** * Inject some specialized knowledge about the DOM. This takes a config object @@ -10675,45 +11037,53 @@ var DOMPropertyInjection = { for (var propName in Properties) { ("production" !== process.env.NODE_ENV ? invariant( - !DOMProperty.isStandardName[propName], + !DOMProperty.isStandardName.hasOwnProperty(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])); + ) : invariant(!DOMProperty.isStandardName.hasOwnProperty(propName))); DOMProperty.isStandardName[propName] = true; var lowerCased = propName.toLowerCase(); DOMProperty.getPossibleStandardName[lowerCased] = propName; - var attributeName = DOMAttributeNames[propName]; - if (attributeName) { + if (DOMAttributeNames.hasOwnProperty(propName)) { + var attributeName = DOMAttributeNames[propName]; DOMProperty.getPossibleStandardName[attributeName] = propName; + DOMProperty.getAttributeName[propName] = attributeName; + } else { + DOMProperty.getAttributeName[propName] = lowerCased; } - DOMProperty.getAttributeName[propName] = attributeName || lowerCased; - DOMProperty.getPropertyName[propName] = - DOMPropertyNames[propName] || propName; + DOMPropertyNames.hasOwnProperty(propName) ? + DOMPropertyNames[propName] : + propName; - var mutationMethod = DOMMutationMethods[propName]; - if (mutationMethod) { - DOMProperty.getMutationMethod[propName] = mutationMethod; + if (DOMMutationMethods.hasOwnProperty(propName)) { + DOMProperty.getMutationMethod[propName] = DOMMutationMethods[propName]; + } else { + DOMProperty.getMutationMethod[propName] = null; } var propConfig = Properties[propName]; DOMProperty.mustUseAttribute[propName] = - propConfig & DOMPropertyInjection.MUST_USE_ATTRIBUTE; + checkMask(propConfig, DOMPropertyInjection.MUST_USE_ATTRIBUTE); DOMProperty.mustUseProperty[propName] = - propConfig & DOMPropertyInjection.MUST_USE_PROPERTY; + checkMask(propConfig, DOMPropertyInjection.MUST_USE_PROPERTY); DOMProperty.hasSideEffects[propName] = - propConfig & DOMPropertyInjection.HAS_SIDE_EFFECTS; + checkMask(propConfig, DOMPropertyInjection.HAS_SIDE_EFFECTS); DOMProperty.hasBooleanValue[propName] = - propConfig & DOMPropertyInjection.HAS_BOOLEAN_VALUE; + checkMask(propConfig, DOMPropertyInjection.HAS_BOOLEAN_VALUE); + DOMProperty.hasNumericValue[propName] = + checkMask(propConfig, DOMPropertyInjection.HAS_NUMERIC_VALUE); DOMProperty.hasPositiveNumericValue[propName] = - propConfig & DOMPropertyInjection.HAS_POSITIVE_NUMERIC_VALUE; + checkMask(propConfig, DOMPropertyInjection.HAS_POSITIVE_NUMERIC_VALUE); + DOMProperty.hasOverloadedBooleanValue[propName] = + checkMask(propConfig, DOMPropertyInjection.HAS_OVERLOADED_BOOLEAN_VALUE); ("production" !== process.env.NODE_ENV ? invariant( !DOMProperty.mustUseAttribute[propName] || @@ -10730,12 +11100,15 @@ var DOMPropertyInjection = { ) : invariant(DOMProperty.mustUseProperty[propName] || !DOMProperty.hasSideEffects[propName])); ("production" !== process.env.NODE_ENV ? invariant( - !DOMProperty.hasBooleanValue[propName] || - !DOMProperty.hasPositiveNumericValue[propName], - 'DOMProperty: Cannot have both boolean and positive numeric value: %s', + !!DOMProperty.hasBooleanValue[propName] + + !!DOMProperty.hasNumericValue[propName] + + !!DOMProperty.hasOverloadedBooleanValue[propName] <= 1, + 'DOMProperty: Value can be one of boolean, overloaded boolean, or ' + + 'numeric value, but not a combination: %s', propName - ) : invariant(!DOMProperty.hasBooleanValue[propName] || - !DOMProperty.hasPositiveNumericValue[propName])); + ) : invariant(!!DOMProperty.hasBooleanValue[propName] + + !!DOMProperty.hasNumericValue[propName] + + !!DOMProperty.hasOverloadedBooleanValue[propName] <= 1)); } } }; @@ -10819,6 +11192,13 @@ var DOMProperty = { */ hasBooleanValue: {}, + /** + * Whether the property must be numeric or parse as a + * numeric and should be removed when set to a falsey value. + * @type {Object} + */ + hasNumericValue: {}, + /** * Whether the property must be positive numeric or parse as a positive * numeric and should be removed when set to a falsey value. @@ -10826,6 +11206,14 @@ var DOMProperty = { */ hasPositiveNumericValue: {}, + /** + * Whether the property can be used as a flag as well as with a value. Removed + * when strictly equal to false; present without a value when strictly equal + * to true; present with a value otherwise. + * @type {Object} + */ + hasOverloadedBooleanValue: {}, + /** * All of the isCustomAttribute() functions that have been injected. */ @@ -10836,11 +11224,13 @@ var DOMProperty = { * @method */ isCustomAttribute: function(attributeName) { - return DOMProperty._isCustomAttributeFunctions.some( - function(isCustomAttributeFn) { - return isCustomAttributeFn.call(null, attributeName); + for (var i = 0; i < DOMProperty._isCustomAttributeFunctions.length; i++) { + var isCustomAttributeFn = DOMProperty._isCustomAttributeFunctions[i]; + if (isCustomAttributeFn(attributeName)) { + return true; } - ); + } + return false; }, /** @@ -10870,44 +11260,35 @@ var DOMProperty = { module.exports = DOMProperty; }).call(this,require("FWaASH")) -},{"./invariant":129,"FWaASH":6}],18:[function(require,module,exports){ +},{"./invariant":158,"FWaASH":6}],19:[function(require,module,exports){ (function (process){ /** - * Copyright 2013-2014 Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. * - * 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. + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule DOMPropertyOperations * @typechecks static-only */ -"use strict"; +'use strict'; var DOMProperty = require("./DOMProperty"); -var escapeTextForBrowser = require("./escapeTextForBrowser"); -var memoizeStringOnly = require("./memoizeStringOnly"); +var quoteAttributeValueForBrowser = require("./quoteAttributeValueForBrowser"); +var warning = require("./warning"); function shouldIgnoreValue(name, value) { return value == null || - DOMProperty.hasBooleanValue[name] && !value || - DOMProperty.hasPositiveNumericValue[name] && (isNaN(value) || value < 1); + (DOMProperty.hasBooleanValue[name] && !value) || + (DOMProperty.hasNumericValue[name] && isNaN(value)) || + (DOMProperty.hasPositiveNumericValue[name] && (value < 1)) || + (DOMProperty.hasOverloadedBooleanValue[name] && value === false); } -var processAttributeNameAndPrefix = memoizeStringOnly(function(name) { - return escapeTextForBrowser(name) + '="'; -}); - if ("production" !== process.env.NODE_ENV) { var reactProps = { children: true, @@ -10918,7 +11299,8 @@ if ("production" !== process.env.NODE_ENV) { var warnedProperties = {}; var warnUnknownProperty = function(name) { - if (reactProps[name] || warnedProperties[name]) { + if (reactProps.hasOwnProperty(name) && reactProps[name] || + warnedProperties.hasOwnProperty(name) && warnedProperties[name]) { return; } @@ -10926,16 +11308,22 @@ if ("production" !== process.env.NODE_ENV) { var lowerCasedName = name.toLowerCase(); // data-* attributes should be lowercase; suggest the lowercase version - var standardName = DOMProperty.isCustomAttribute(lowerCasedName) ? - lowerCasedName : DOMProperty.getPossibleStandardName[lowerCasedName]; + var standardName = ( + DOMProperty.isCustomAttribute(lowerCasedName) ? + lowerCasedName : + DOMProperty.getPossibleStandardName.hasOwnProperty(lowerCasedName) ? + DOMProperty.getPossibleStandardName[lowerCasedName] : + null + ); // 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 + '?' - ); - } + ("production" !== process.env.NODE_ENV ? warning( + standardName == null, + 'Unknown DOM property %s. Did you mean %s?', + name, + standardName + ) : null); }; } @@ -10952,8 +11340,8 @@ var DOMPropertyOperations = { * @return {string} Markup string. */ createMarkupForID: function(id) { - return processAttributeNameAndPrefix(DOMProperty.ID_ATTRIBUTE_NAME) + - escapeTextForBrowser(id) + '"'; + return DOMProperty.ID_ATTRIBUTE_NAME + '=' + + quoteAttributeValueForBrowser(id); }, /** @@ -10964,22 +11352,22 @@ var DOMPropertyOperations = { * @return {?string} Markup string, or null if the property was invalid. */ createMarkupForProperty: function(name, value) { - if (DOMProperty.isStandardName[name]) { + if (DOMProperty.isStandardName.hasOwnProperty(name) && + DOMProperty.isStandardName[name]) { if (shouldIgnoreValue(name, value)) { return ''; } var attributeName = DOMProperty.getAttributeName[name]; - if (DOMProperty.hasBooleanValue[name]) { - return escapeTextForBrowser(attributeName); + if (DOMProperty.hasBooleanValue[name] || + (DOMProperty.hasOverloadedBooleanValue[name] && value === true)) { + return attributeName; } - return processAttributeNameAndPrefix(attributeName) + - escapeTextForBrowser(value) + '"'; + return attributeName + '=' + quoteAttributeValueForBrowser(value); } else if (DOMProperty.isCustomAttribute(name)) { if (value == null) { return ''; } - return processAttributeNameAndPrefix(name) + - escapeTextForBrowser(value) + '"'; + return name + '=' + quoteAttributeValueForBrowser(value); } else if ("production" !== process.env.NODE_ENV) { warnUnknownProperty(name); } @@ -10994,23 +11382,31 @@ var DOMPropertyOperations = { * @param {*} value */ setValueForProperty: function(node, name, value) { - if (DOMProperty.isStandardName[name]) { + if (DOMProperty.isStandardName.hasOwnProperty(name) && + 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]) { + // `setAttribute` with objects becomes only `[object]` in IE8/9, + // ('' + value) makes it output the correct toString()-value. node.setAttribute(DOMProperty.getAttributeName[name], '' + value); } else { var propName = DOMProperty.getPropertyName[name]; - if (!DOMProperty.hasSideEffects[name] || node[propName] !== value) { + // Must explicitly cast values for HAS_SIDE_EFFECTS-properties to the + // property type before comparing; only `value` does and is string. + if (!DOMProperty.hasSideEffects[name] || + ('' + node[propName]) !== ('' + value)) { + // Contrary to `setAttribute`, object properties are properly + // `toString`ed by IE8/9. node[propName] = value; } } } else if (DOMProperty.isCustomAttribute(name)) { if (value == null) { - node.removeAttribute(DOMProperty.getAttributeName[name]); + node.removeAttribute(name); } else { node.setAttribute(name, '' + value); } @@ -11026,7 +11422,8 @@ var DOMPropertyOperations = { * @param {string} name */ deleteValueForProperty: function(node, name) { - if (DOMProperty.isStandardName[name]) { + if (DOMProperty.isStandardName.hasOwnProperty(name) && + DOMProperty.isStandardName[name]) { var mutationMethod = DOMProperty.getMutationMethod[name]; if (mutationMethod) { mutationMethod(node, undefined); @@ -11036,10 +11433,10 @@ var DOMPropertyOperations = { var propName = DOMProperty.getPropertyName[name]; var defaultValue = DOMProperty.getDefaultValueForProperty( node.nodeName, - name + propName ); if (!DOMProperty.hasSideEffects[name] || - node[propName] !== defaultValue) { + ('' + node[propName]) !== defaultValue) { node[propName] = defaultValue; } } @@ -11055,22 +11452,15 @@ var DOMPropertyOperations = { module.exports = DOMPropertyOperations; }).call(this,require("FWaASH")) -},{"./DOMProperty":17,"./escapeTextForBrowser":117,"./memoizeStringOnly":137,"FWaASH":6}],19:[function(require,module,exports){ +},{"./DOMProperty":18,"./quoteAttributeValueForBrowser":171,"./warning":179,"FWaASH":6}],20:[function(require,module,exports){ (function (process){ /** - * Copyright 2013-2014 Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. * - * 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. + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule Danger * @typechecks static-only @@ -11078,7 +11468,7 @@ module.exports = DOMPropertyOperations; /*jslint evil: true, sub: true */ -"use strict"; +'use strict'; var ExecutionEnvironment = require("./ExecutionEnvironment"); @@ -11119,9 +11509,10 @@ var Danger = { dangerouslyRenderMarkup: function(markupList) { ("production" !== process.env.NODE_ENV ? invariant( ExecutionEnvironment.canUseDOM, - 'dangerouslyRenderMarkup(...): Cannot render markup in a Worker ' + - 'thread. This is likely a bug in the framework. Please report ' + - 'immediately.' + 'dangerouslyRenderMarkup(...): Cannot render markup in a worker ' + + 'thread. Make sure `window` and `document` are available globally ' + + 'before requiring React when unit testing or use ' + + 'React.renderToString for server rendering.' ) : invariant(ExecutionEnvironment.canUseDOM)); var nodeName; var markupByNodeName = {}; @@ -11147,7 +11538,8 @@ var Danger = { // 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) { + var resultIndex; + for (resultIndex in markupListByNodeName) { if (markupListByNodeName.hasOwnProperty(resultIndex)) { var markup = markupListByNodeName[resultIndex]; @@ -11156,323 +11548,112 @@ var Danger = { // 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

; + * } + * }); + * + * Note: This only checks shallow equality for props and state. If these contain + * complex data structures this mixin may have false-negatives for deeper + * differences. Only mixin to components which have simple props and state, or + * use `forceUpdate()` when you know deep data structures have changed. + */ +var ReactComponentWithPureRenderMixin = { + shouldComponentUpdate: function(nextProps, nextState) { + return !shallowEqual(this.props, nextProps) || + !shallowEqual(this.state, nextState); + } +}; + +module.exports = ReactComponentWithPureRenderMixin; + +},{"./shallowEqual":174}],50:[function(require,module,exports){ +(function (process){ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactCompositeComponent + */ + +'use strict'; + +var ReactComponentEnvironment = require("./ReactComponentEnvironment"); +var ReactContext = require("./ReactContext"); +var ReactCurrentOwner = require("./ReactCurrentOwner"); +var ReactElement = require("./ReactElement"); +var ReactElementValidator = require("./ReactElementValidator"); +var ReactInstanceMap = require("./ReactInstanceMap"); +var ReactLifeCycle = require("./ReactLifeCycle"); +var ReactNativeComponent = require("./ReactNativeComponent"); +var ReactPerf = require("./ReactPerf"); +var ReactPropTypeLocations = require("./ReactPropTypeLocations"); +var ReactPropTypeLocationNames = require("./ReactPropTypeLocationNames"); +var ReactReconciler = require("./ReactReconciler"); +var ReactUpdates = require("./ReactUpdates"); + +var assign = require("./Object.assign"); +var emptyObject = require("./emptyObject"); +var invariant = require("./invariant"); +var shouldUpdateReactComponent = require("./shouldUpdateReactComponent"); +var warning = require("./warning"); + +function getDeclarationErrorAddendum(component) { + var owner = component._currentElement._owner || null; + if (owner) { + var name = owner.getName(); + if (name) { + return ' Check the render method of `' + name + '`.'; + } + } + return ''; +} + +/** + * ------------------ The Life-Cycle of a Composite Component ------------------ + * + * - constructor: Initialization of state. The instance is now retained. + * - componentWillMount + * - render + * - [children's constructors] + * - [children's componentWillMount and render] + * - [children's componentDidMount] + * - componentDidMount + * + * Update Phases: + * - componentWillReceiveProps (only called if parent updated) + * - shouldComponentUpdate + * - componentWillUpdate + * - render + * - [children's constructors or receive props phases] + * - componentDidUpdate + * + * - componentWillUnmount + * - [children's componentWillUnmount] + * - [children destroyed] + * - (destroyed): The instance is now blank, released by React and ready for GC. + * + * ----------------------------------------------------------------------------- + */ + +/** + * An incrementing ID assigned to each component when it is mounted. This is + * used to enforce the order in which `ReactUpdates` updates dirty components. + * + * @private + */ +var nextMountID = 1; /** * @lends {ReactCompositeComponent.prototype} @@ -15136,100 +16080,167 @@ var ReactCompositeComponentMixin = { /** * Base constructor for all composite component. * - * @param {?object} initialProps - * @param {*} children + * @param {ReactElement} element * @final * @internal */ - construct: function(initialProps, children) { - // Children can be either an array or more than one argument - ReactComponent.Mixin.construct.apply(this, arguments); + construct: function(element) { + this._currentElement = element; + this._rootNodeID = null; + this._instance = null; - this.state = null; - this._pendingState = null; + // See ReactUpdateQueue + this._pendingElement = null; + this._pendingStateQueue = null; + this._pendingReplaceState = false; + this._pendingForceUpdate = false; - this.context = this._processContext(ReactContext.current); - this._currentContext = ReactContext.current; - this._pendingContext = null; + this._renderedComponent = null; - this._compositeLifeCycleState = null; - }, + this._context = null; + this._mountOrder = 0; + this._isTopLevel = false; - /** - * 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; + // See ReactUpdates and ReactUpdateQueue. + this._pendingCallbacks = null; }, /** * Initializes the component, renders markup, and registers event listeners. * * @param {string} rootID DOM ID of the root node. - * @param {ReactReconcileTransaction} transaction - * @param {number} mountDepth number of components in the owner hierarchy + * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction * @return {?string} Rendered markup to be inserted into the DOM. * @final * @internal */ - mountComponent: ReactPerf.measure( - 'ReactCompositeComponent', - 'mountComponent', - function(rootID, transaction, mountDepth) { - ReactComponent.Mixin.mountComponent.call( - this, - rootID, - transaction, - mountDepth - ); - this._compositeLifeCycleState = CompositeLifeCycle.MOUNTING; + mountComponent: function(rootID, transaction, context) { + this._context = context; + this._mountOrder = nextMountID++; + this._rootNodeID = rootID; - this._defaultProps = this.getDefaultProps ? this.getDefaultProps() : null; - this.props = this._processProps(this.props); + var publicProps = this._processProps(this._currentElement.props); + var publicContext = this._processContext(this._currentElement._context); - if (this.__reactAutoBindMap) { - this._bindAutoBindMethods(); - } + var Component = ReactNativeComponent.getComponentClassForElement( + this._currentElement + ); + + // Initialize the public class + var inst = new Component(publicProps, publicContext); + + if ("production" !== process.env.NODE_ENV) { + // This will throw later in _renderValidatedComponent, but add an early + // warning now to help debugging + ("production" !== process.env.NODE_ENV ? warning( + inst.render != null, + '%s(...): No `render` method found on the returned component ' + + 'instance: you may have forgotten to define `render` in your ' + + 'component or you may have accidentally tried to render an element ' + + 'whose type is a function that isn\'t a React component.', + Component.displayName || Component.name || 'Component' + ) : null); + } + + // These should be set up in the constructor, but as a convenience for + // simpler class abstractions, we set them up after the fact. + inst.props = publicProps; + inst.context = publicContext; + inst.refs = emptyObject; + + this._instance = inst; + + // Store a reference from the instance back to the internal representation + ReactInstanceMap.set(inst, this); + + if ("production" !== process.env.NODE_ENV) { + this._warnIfContextsDiffer(this._currentElement._context, context); + } + + if ("production" !== process.env.NODE_ENV) { + // Since plain JS classes are defined without any special initialization + // logic, we can not catch common errors early. Therefore, we have to + // catch them here, at initialization time, instead. + ("production" !== process.env.NODE_ENV ? warning( + !inst.getInitialState || + inst.getInitialState.isReactClassApproved, + 'getInitialState was defined on %s, a plain JavaScript class. ' + + 'This is only supported for classes created using React.createClass. ' + + 'Did you mean to define a state property instead?', + this.getName() || 'a component' + ) : null); + ("production" !== process.env.NODE_ENV ? warning( + !inst.propTypes, + 'propTypes was defined as an instance property on %s. Use a static ' + + 'property to define propTypes instead.', + this.getName() || 'a component' + ) : null); + ("production" !== process.env.NODE_ENV ? warning( + !inst.contextTypes, + 'contextTypes was defined as an instance property on %s. Use a ' + + 'static property to define contextTypes instead.', + this.getName() || 'a component' + ) : null); + ("production" !== process.env.NODE_ENV ? warning( + typeof inst.componentShouldUpdate !== 'function', + '%s has a method called ' + + 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' + + 'The name is phrased as a question because the function is ' + + 'expected to return a value.', + (this.getName() || 'A component') + ) : null); + } + + var initialState = inst.state; + if (initialState === undefined) { + inst.state = initialState = null; + } + ("production" !== process.env.NODE_ENV ? invariant( + typeof initialState === 'object' && !Array.isArray(initialState), + '%s.state: must be set to an object or null', + this.getName() || 'ReactCompositeComponent' + ) : invariant(typeof initialState === 'object' && !Array.isArray(initialState))); - this.state = this.getInitialState ? this.getInitialState() : null; - ("production" !== process.env.NODE_ENV ? invariant( - typeof this.state === 'object' && !Array.isArray(this.state), - '%s.getInitialState(): must return an object or null', - this.constructor.displayName || 'ReactCompositeComponent' - ) : invariant(typeof this.state === 'object' && !Array.isArray(this.state))); + this._pendingStateQueue = null; + this._pendingReplaceState = false; + this._pendingForceUpdate = false; - this._pendingState = null; - this._pendingForceUpdate = false; + var renderedElement; - if (this.componentWillMount) { - this.componentWillMount(); + var previouslyMounting = ReactLifeCycle.currentlyMountingInstance; + ReactLifeCycle.currentlyMountingInstance = this; + try { + if (inst.componentWillMount) { + inst.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._pendingStateQueue` without triggering a re-render. + if (this._pendingStateQueue) { + inst.state = this._processPendingState(inst.props, inst.context); } } - this._renderedComponent = this._renderValidatedComponent(); + renderedElement = this._renderValidatedComponent(); + } finally { + ReactLifeCycle.currentlyMountingInstance = previouslyMounting; + } + + this._renderedComponent = this._instantiateReactComponent( + renderedElement, + this._currentElement.type // The wrapping type + ); - // Done with mounting, `setState` will now trigger UI changes. - this._compositeLifeCycleState = null; - var markup = this._renderedComponent.mountComponent( - rootID, - transaction, - mountDepth + 1 - ); - if (this.componentDidMount) { - transaction.getReactMountReady().enqueue(this, this.componentDidMount); - } - return markup; + var markup = ReactReconciler.mountComponent( + this._renderedComponent, + rootID, + transaction, + this._processChildContext(context) + ); + if (inst.componentDidMount) { + transaction.getReactMountReady().enqueue(inst.componentDidMount, inst); } - ), + + return markup; + }, /** * Releases any resources allocated by `mountComponent`. @@ -15238,82 +16249,88 @@ var ReactCompositeComponentMixin = { * @internal */ unmountComponent: function() { - this._compositeLifeCycleState = CompositeLifeCycle.UNMOUNTING; - if (this.componentWillUnmount) { - this.componentWillUnmount(); - } - this._compositeLifeCycleState = null; + var inst = this._instance; - this._defaultProps = null; + if (inst.componentWillUnmount) { + var previouslyUnmounting = ReactLifeCycle.currentlyUnmountingInstance; + ReactLifeCycle.currentlyUnmountingInstance = this; + try { + inst.componentWillUnmount(); + } finally { + ReactLifeCycle.currentlyUnmountingInstance = previouslyUnmounting; + } + } - this._renderedComponent.unmountComponent(); + ReactReconciler.unmountComponent(this._renderedComponent); this._renderedComponent = null; - ReactComponent.Mixin.unmountComponent.call(this); + // Reset pending fields + this._pendingStateQueue = null; + this._pendingReplaceState = false; + this._pendingForceUpdate = false; + this._pendingCallbacks = null; + this._pendingElement = null; - if (this.refs) { - this.refs = null; - } + // These fields do not really need to be reset since this object is no + // longer accessible. + this._context = null; + this._rootNodeID = null; - // Some existing components rely on this.props even after they've been + // Delete the reference from the instance to this internal representation + // which allow the internals to be properly cleaned up even if the user + // leaks a reference to the public instance. + ReactInstanceMap.remove(inst); + + // Some existing components rely on inst.props even after they've been // destroyed (in event handlers). - // TODO: this.props = null; - // TODO: this.state = null; + // TODO: inst.props = null; + // TODO: inst.state = null; + // TODO: inst.context = 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. + * Schedule a partial update to the props. Only used for internal testing. * - * 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. + * @param {object} partialProps Subset of the next props. + * @param {?function} callback Called after props are updated. * @final - * @protected + * @internal */ - setState: function(partialState, callback) { - ("production" !== process.env.NODE_ENV ? invariant( - typeof partialState === 'object' || partialState == null, - 'setState(...): takes an object of state variables to update.' - ) : invariant(typeof partialState === 'object' || partialState == null)); - if ("production" !== process.env.NODE_ENV) { - if (partialState == null) { - console.warn( - 'setState(...): You passed an undefined or null state object; ' + - 'instead, use forceUpdate().' - ); - } - } - // Merge with `_pendingState` if it exists, otherwise with existing state. - this.replaceState( - merge(this._pendingState || this.state, partialState), - callback + _setPropsInternal: function(partialProps, callback) { + // This is a deoptimized path. We optimize for always having an element. + // This creates an extra internal element. + var element = this._pendingElement || this._currentElement; + this._pendingElement = ReactElement.cloneAndReplaceProps( + element, + assign({}, element.props, partialProps) ); + ReactUpdates.enqueueUpdate(this, 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. + * Filters the context object to only contain keys specified in + * `contextTypes` * - * @param {object} completeState Next state. - * @param {?function} callback Called after state is updated. - * @final - * @protected + * @param {object} context + * @return {?object} + * @private */ - replaceState: function(completeState, callback) { - validateLifeCycleOnReplaceState(this); - this._pendingState = completeState; - ReactUpdates.enqueueUpdate(this, callback); + _maskContext: function(context) { + var maskedContext = null; + // This really should be getting the component class for the element, + // but we know that we're not going to need it for built-ins. + if (typeof this._currentElement.type === 'string') { + return emptyObject; + } + var contextTypes = this._currentElement.type.contextTypes; + if (!contextTypes) { + return emptyObject; + } + maskedContext = {}; + for (var contextName in contextTypes) { + maskedContext[contextName] = context[contextName]; + } + return maskedContext; }, /** @@ -15325,16 +16342,14 @@ var ReactCompositeComponentMixin = { * @private */ _processContext: function(context) { - var maskedContext = null; - var contextTypes = this.constructor.contextTypes; - if (contextTypes) { - maskedContext = {}; - for (var contextName in contextTypes) { - maskedContext[contextName] = context[contextName]; - } - if ("production" !== process.env.NODE_ENV) { + var maskedContext = this._maskContext(context); + if ("production" !== process.env.NODE_ENV) { + var Component = ReactNativeComponent.getComponentClassForElement( + this._currentElement + ); + if (Component.contextTypes) { this._checkPropTypes( - contextTypes, + Component.contextTypes, maskedContext, ReactPropTypeLocations.context ); @@ -15349,31 +16364,31 @@ var ReactCompositeComponentMixin = { * @private */ _processChildContext: function(currentContext) { - var childContext = this.getChildContext && this.getChildContext(); - var displayName = this.constructor.displayName || 'ReactCompositeComponent'; + var inst = this._instance; + var childContext = inst.getChildContext && inst.getChildContext(); if (childContext) { ("production" !== process.env.NODE_ENV ? invariant( - typeof this.constructor.childContextTypes === 'object', + typeof inst.constructor.childContextTypes === 'object', '%s.getChildContext(): childContextTypes must be defined in order to ' + 'use getChildContext().', - displayName - ) : invariant(typeof this.constructor.childContextTypes === 'object')); + this.getName() || 'ReactCompositeComponent' + ) : invariant(typeof inst.constructor.childContextTypes === 'object')); if ("production" !== process.env.NODE_ENV) { this._checkPropTypes( - this.constructor.childContextTypes, + inst.constructor.childContextTypes, childContext, ReactPropTypeLocations.childContext ); } for (var name in childContext) { ("production" !== process.env.NODE_ENV ? invariant( - name in this.constructor.childContextTypes, + name in inst.constructor.childContextTypes, '%s.getChildContext(): key "%s" is not defined in childContextTypes.', - displayName, + this.getName() || 'ReactCompositeComponent', name - ) : invariant(name in this.constructor.childContextTypes)); + ) : invariant(name in inst.constructor.childContextTypes)); } - return merge(currentContext, childContext); + return assign({}, currentContext, childContext); } return currentContext; }, @@ -15388,20 +16403,19 @@ var ReactCompositeComponentMixin = { * @private */ _processProps: function(newProps) { - var props = merge(newProps); - var defaultProps = this._defaultProps; - for (var propName in defaultProps) { - if (typeof props[propName] === 'undefined') { - props[propName] = defaultProps[propName]; - } - } if ("production" !== process.env.NODE_ENV) { - var propTypes = this.constructor.propTypes; - if (propTypes) { - this._checkPropTypes(propTypes, props, ReactPropTypeLocations.prop); + var Component = ReactNativeComponent.getComponentClassForElement( + this._currentElement + ); + if (Component.propTypes) { + this._checkPropTypes( + Component.propTypes, + newProps, + ReactPropTypeLocations.prop + ); } } - return props; + return newProps; }, /** @@ -15413,451 +16427,480 @@ var ReactCompositeComponentMixin = { * @private */ _checkPropTypes: function(propTypes, props, location) { - var componentName = this.constructor.displayName; + // TODO: Stop validating prop types here and only use the element + // validation. + var componentName = this.getName(); for (var propName in propTypes) { if (propTypes.hasOwnProperty(propName)) { - propTypes[propName](props, propName, componentName, location); + var error; + try { + // This is intentionally an invariant that gets caught. It's the same + // behavior as without this statement except with a better message. + ("production" !== process.env.NODE_ENV ? invariant( + typeof propTypes[propName] === 'function', + '%s: %s type `%s` is invalid; it must be a function, usually ' + + 'from React.PropTypes.', + componentName || 'React class', + ReactPropTypeLocationNames[location], + propName + ) : invariant(typeof propTypes[propName] === 'function')); + error = propTypes[propName](props, propName, componentName, location); + } catch (ex) { + error = ex; + } + if (error instanceof Error) { + // We may want to extend this logic for similar errors in + // React.render calls, so I'm abstracting it away into + // a function to minimize refactoring in the future + var addendum = getDeclarationErrorAddendum(this); + + if (location === ReactPropTypeLocations.prop) { + // Preface gives us something to blacklist in warning module + ("production" !== process.env.NODE_ENV ? warning( + false, + 'Failed Composite propType: %s%s', + error.message, + addendum + ) : null); + } else { + ("production" !== process.env.NODE_ENV ? warning( + false, + 'Failed Context Types: %s%s', + error.message, + addendum + ) : null); + } + } } } }, - 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); + receiveComponent: function(nextElement, transaction, nextContext) { + var prevElement = this._currentElement; + var prevContext = this._context; + + this._pendingElement = null; + + this.updateComponent( + transaction, + prevElement, + nextElement, + prevContext, + nextContext + ); }, /** - * If any of `_pendingProps`, `_pendingState`, or `_pendingForceUpdate` is - * set, update the component. + * If any of `_pendingElement`, `_pendingStateQueue`, or `_pendingForceUpdate` + * is set, update the component. * * @param {ReactReconcileTransaction} transaction * @internal */ - _performUpdateIfNecessary: function(transaction) { - if (this._pendingProps == null && - this._pendingState == null && - this._pendingContext == null && - !this._pendingForceUpdate) { - return; + performUpdateIfNecessary: function(transaction) { + if (this._pendingElement != null) { + ReactReconciler.receiveComponent( + this, + this._pendingElement || this._currentElement, + transaction, + this._context + ); } - var nextFullContext = this._pendingContext || this._currentContext; - var nextContext = this._processContext(nextFullContext); - this._pendingContext = null; + if (this._pendingStateQueue !== null || this._pendingForceUpdate) { + if ("production" !== process.env.NODE_ENV) { + ReactElementValidator.checkAndWarnForMutatedProps( + this._currentElement + ); + } - var nextProps = this.props; - if (this._pendingProps != null) { - nextProps = this._processProps(this._pendingProps); - this._pendingProps = null; + this.updateComponent( + transaction, + this._currentElement, + this._currentElement, + this._context, + this._context + ); + } + }, - this._compositeLifeCycleState = CompositeLifeCycle.RECEIVING_PROPS; - if (this.componentWillReceiveProps) { - this.componentWillReceiveProps(nextProps, nextContext); - } + /** + * Compare two contexts, warning if they are different + * TODO: Remove this check when owner-context is removed + */ + _warnIfContextsDiffer: function(ownerBasedContext, parentBasedContext) { + ownerBasedContext = this._maskContext(ownerBasedContext); + parentBasedContext = this._maskContext(parentBasedContext); + var parentKeys = Object.keys(parentBasedContext).sort(); + var displayName = this.getName() || 'ReactCompositeComponent'; + for (var i = 0; i < parentKeys.length; i++) { + var key = parentKeys[i]; + ("production" !== process.env.NODE_ENV ? warning( + ownerBasedContext[key] === parentBasedContext[key], + 'owner-based and parent-based contexts differ ' + + '(values: `%s` vs `%s`) for key (%s) while mounting %s ' + + '(see: http://fb.me/react-context-by-parent)', + ownerBasedContext[key], + parentBasedContext[key], + key, + displayName + ) : null); } + }, + + /** + * Perform an update to a mounted component. The componentWillReceiveProps and + * shouldComponentUpdate methods are called, then (assuming the update isn't + * skipped) the remaining update lifecycle methods are called and the DOM + * representation is updated. + * + * By default, this implements React's rendering and reconciliation algorithm. + * Sophisticated clients may wish to override this. + * + * @param {ReactReconcileTransaction} transaction + * @param {ReactElement} prevParentElement + * @param {ReactElement} nextParentElement + * @internal + * @overridable + */ + updateComponent: function( + transaction, + prevParentElement, + nextParentElement, + prevUnmaskedContext, + nextUnmaskedContext + ) { + var inst = this._instance; - this._compositeLifeCycleState = CompositeLifeCycle.RECEIVING_STATE; + var nextContext = inst.context; + var nextProps = inst.props; - // Unlike props, state, and context, we specifically don't want to set - // _pendingOwner to null here because it's possible for a component to have - // a null owner, so we instead make `this._owner === this._pendingOwner` - // mean that there's no owner change pending. - var nextOwner = this._pendingOwner; + // Distinguish between a props update versus a simple state update + if (prevParentElement !== nextParentElement) { + nextContext = this._processContext(nextParentElement._context); + nextProps = this._processProps(nextParentElement.props); - var nextState = this._pendingState || this.state; - this._pendingState = null; + if ("production" !== process.env.NODE_ENV) { + if (nextUnmaskedContext != null) { + this._warnIfContextsDiffer( + nextParentElement._context, + nextUnmaskedContext + ); + } + } - try { - if (this._pendingForceUpdate || - !this.shouldComponentUpdate || - this.shouldComponentUpdate(nextProps, nextState, nextContext)) { - this._pendingForceUpdate = false; - // Will set `this.props`, `this.state` and `this.context`. - this._performComponentUpdate( - nextProps, - nextOwner, - nextState, - nextFullContext, - nextContext, - transaction - ); - } else { - // If it's determined that a component should not update, we still want - // to set props and state. - this.props = nextProps; - this._owner = nextOwner; - this.state = nextState; - this._currentContext = nextFullContext; - this.context = nextContext; + // An update here will schedule an update but immediately set + // _pendingStateQueue which will ensure that any state updates gets + // immediately reconciled instead of waiting for the next batch. + + if (inst.componentWillReceiveProps) { + inst.componentWillReceiveProps(nextProps, nextContext); } - } finally { - this._compositeLifeCycleState = null; + } + + var nextState = this._processPendingState(nextProps, nextContext); + + var shouldUpdate = + this._pendingForceUpdate || + !inst.shouldComponentUpdate || + inst.shouldComponentUpdate(nextProps, nextState, nextContext); + + if ("production" !== process.env.NODE_ENV) { + ("production" !== process.env.NODE_ENV ? warning( + typeof shouldUpdate !== 'undefined', + '%s.shouldComponentUpdate(): Returned undefined instead of a ' + + 'boolean value. Make sure to return true or false.', + this.getName() || 'ReactCompositeComponent' + ) : null); + } + + if (shouldUpdate) { + this._pendingForceUpdate = false; + // Will set `this.props`, `this.state` and `this.context`. + this._performComponentUpdate( + nextParentElement, + nextProps, + nextState, + nextContext, + transaction, + nextUnmaskedContext + ); + } else { + // If it's determined that a component should not update, we still want + // to set props and state but we shortcut the rest of the update. + this._currentElement = nextParentElement; + this._context = nextUnmaskedContext; + inst.props = nextProps; + inst.state = nextState; + inst.context = nextContext; } }, + _processPendingState: function(props, context) { + var inst = this._instance; + var queue = this._pendingStateQueue; + var replace = this._pendingReplaceState; + this._pendingReplaceState = false; + this._pendingStateQueue = null; + + if (!queue) { + return inst.state; + } + + var nextState = assign({}, replace ? queue[0] : inst.state); + for (var i = replace ? 1 : 0; i < queue.length; i++) { + var partial = queue[i]; + assign( + nextState, + typeof partial === 'function' ? + partial.call(inst, nextState, props, context) : + partial + ); + } + + return nextState; + }, + /** * Merges new props and state, notifies delegate methods of update and * performs update. * - * @param {object} nextProps Next object to set as properties. - * @param {?ReactComponent} nextOwner Next component to set as owner + * @param {ReactElement} nextElement Next element + * @param {object} nextProps Next public object to set as properties. * @param {?object} nextState Next object to set as state. - * @param {?object} nextFullContext Next object to set as _currentContext. - * @param {?object} nextContext Next object to set as context. + * @param {?object} nextContext Next public object to set as context. * @param {ReactReconcileTransaction} transaction + * @param {?object} unmaskedContext * @private */ _performComponentUpdate: function( + nextElement, nextProps, - nextOwner, nextState, - nextFullContext, nextContext, - transaction + transaction, + unmaskedContext ) { - var prevProps = this.props; - var prevOwner = this._owner; - var prevState = this.state; - var prevContext = this.context; + var inst = this._instance; - if (this.componentWillUpdate) { - this.componentWillUpdate(nextProps, nextState, nextContext); + var prevProps = inst.props; + var prevState = inst.state; + var prevContext = inst.context; + + if (inst.componentWillUpdate) { + inst.componentWillUpdate(nextProps, nextState, nextContext); } - this.props = nextProps; - this._owner = nextOwner; - this.state = nextState; - this._currentContext = nextFullContext; - this.context = nextContext; + this._currentElement = nextElement; + this._context = unmaskedContext; + inst.props = nextProps; + inst.state = nextState; + inst.context = nextContext; - this.updateComponent( - transaction, - prevProps, - prevOwner, - prevState, - prevContext - ); + this._updateRenderedComponent(transaction, unmaskedContext); - if (this.componentDidUpdate) { + if (inst.componentDidUpdate) { transaction.getReactMountReady().enqueue( - this, - this.componentDidUpdate.bind(this, prevProps, prevState, prevContext) + inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext), + inst ); } }, - receiveComponent: function(nextComponent, transaction) { - if (nextComponent === this) { - // Since props and context are immutable after the component is - // mounted, we can do a cheap identity compare here to determine - // if this is a superfluous reconcile. - return; - } - - this._pendingContext = nextComponent._currentContext; - ReactComponent.Mixin.receiveComponent.call( - this, - nextComponent, - transaction - ); - }, - /** - * Updates the component's currently mounted DOM representation. - * - * By default, this implements React's rendering and reconciliation algorithm. - * Sophisticated clients may wish to override this. + * Call the component's `render` method and update the DOM accordingly. * * @param {ReactReconcileTransaction} transaction - * @param {object} prevProps - * @param {?ReactComponent} prevOwner - * @param {?object} prevState - * @param {?object} prevContext * @internal - * @overridable */ - updateComponent: ReactPerf.measure( - 'ReactCompositeComponent', - 'updateComponent', - function(transaction, prevProps, prevOwner, prevState, prevContext) { - ReactComponent.Mixin.updateComponent.call( - this, + _updateRenderedComponent: function(transaction, context) { + var prevComponentInstance = this._renderedComponent; + var prevRenderedElement = prevComponentInstance._currentElement; + var nextRenderedElement = this._renderValidatedComponent(); + if (shouldUpdateReactComponent(prevRenderedElement, nextRenderedElement)) { + ReactReconciler.receiveComponent( + prevComponentInstance, + nextRenderedElement, transaction, - prevProps, - prevOwner + this._processChildContext(context) ); - var prevComponent = this._renderedComponent; - var nextComponent = this._renderValidatedComponent(); - if (shouldUpdateReactComponent(prevComponent, nextComponent)) { - prevComponent.receiveComponent(nextComponent, transaction); - } else { - // These two IDs are actually the same! But nothing should rely on that. - var thisID = this._rootNodeID; - var prevComponentID = prevComponent._rootNodeID; - prevComponent.unmountComponent(); - this._renderedComponent = nextComponent; - var nextMarkup = nextComponent.mountComponent( - thisID, - transaction, - this._mountDepth + 1 - ); - ReactComponent.BackendIDOperations.dangerouslyReplaceNodeWithMarkupByID( - prevComponentID, - nextMarkup - ); - } + } else { + // These two IDs are actually the same! But nothing should rely on that. + var thisID = this._rootNodeID; + var prevComponentID = prevComponentInstance._rootNodeID; + ReactReconciler.unmountComponent(prevComponentInstance); + + this._renderedComponent = this._instantiateReactComponent( + nextRenderedElement, + this._currentElement.type + ); + var nextMarkup = ReactReconciler.mountComponent( + this._renderedComponent, + thisID, + transaction, + context + ); + this._replaceNodeWithMarkupByID(prevComponentID, nextMarkup); } - ), + }, /** - * Forces an update. This should only be invoked when it is known with - * certainty that we are **not** in a DOM transaction. - * - * You may want to call this when you know that some deeper aspect of the - * component's state has changed but `setState` was not called. - * - * This will not invoke `shouldUpdateComponent`, but it will invoke - * `componentWillUpdate` and `componentDidUpdate`. - * - * @param {?function} callback Called after update is complete. - * @final * @protected */ - forceUpdate: function(callback) { - var compositeLifeCycleState = this._compositeLifeCycleState; - ("production" !== process.env.NODE_ENV ? invariant( - this.isMounted() || - compositeLifeCycleState === CompositeLifeCycle.MOUNTING, - 'forceUpdate(...): Can only force an update on mounted or mounting ' + - 'components.' - ) : invariant(this.isMounted() || - compositeLifeCycleState === CompositeLifeCycle.MOUNTING)); - ("production" !== process.env.NODE_ENV ? invariant( - compositeLifeCycleState !== CompositeLifeCycle.RECEIVING_STATE && - compositeLifeCycleState !== CompositeLifeCycle.UNMOUNTING, - 'forceUpdate(...): Cannot force an update while unmounting component ' + - 'or during an existing state transition (such as within `render`).' - ) : invariant(compositeLifeCycleState !== CompositeLifeCycle.RECEIVING_STATE && - compositeLifeCycleState !== CompositeLifeCycle.UNMOUNTING)); - this._pendingForceUpdate = true; - ReactUpdates.enqueueUpdate(this, callback); + _replaceNodeWithMarkupByID: function(prevComponentID, nextMarkup) { + ReactComponentEnvironment.replaceNodeWithMarkupByID( + prevComponentID, + nextMarkup + ); }, /** - * @private + * @protected */ - _renderValidatedComponent: ReactPerf.measure( - 'ReactCompositeComponent', - '_renderValidatedComponent', - function() { - var renderedComponent; - var previousContext = ReactContext.current; - ReactContext.current = this._processChildContext(this._currentContext); - ReactCurrentOwner.current = this; - try { - renderedComponent = this.render(); - } finally { - ReactContext.current = previousContext; - ReactCurrentOwner.current = null; + _renderValidatedComponentWithoutOwnerOrContext: function() { + var inst = this._instance; + var renderedComponent = inst.render(); + if ("production" !== process.env.NODE_ENV) { + // We allow auto-mocks to proceed as if they're returning null. + if (typeof renderedComponent === 'undefined' && + inst.render._isMockFunction) { + // This is probably bad practice. Consider warning here and + // deprecating this convenience. + renderedComponent = null; } - ("production" !== process.env.NODE_ENV ? invariant( - ReactComponent.isValidComponent(renderedComponent), - '%s.render(): A valid ReactComponent must be returned. You may have ' + - 'returned null, undefined, an array, or some other invalid object.', - this.constructor.displayName || 'ReactCompositeComponent' - ) : invariant(ReactComponent.isValidComponent(renderedComponent))); - return renderedComponent; } - ), + + return renderedComponent; + }, /** * @private */ - _bindAutoBindMethods: function() { - for (var autoBindKey in this.__reactAutoBindMap) { - if (!this.__reactAutoBindMap.hasOwnProperty(autoBindKey)) { - continue; - } - var method = this.__reactAutoBindMap[autoBindKey]; - this[autoBindKey] = this._bindAutoBindMethod(ReactErrorUtils.guard( - method, - this.constructor.displayName + '.' + autoBindKey - )); + _renderValidatedComponent: function() { + var renderedComponent; + var previousContext = ReactContext.current; + ReactContext.current = this._processChildContext( + this._currentElement._context + ); + ReactCurrentOwner.current = this; + try { + renderedComponent = + this._renderValidatedComponentWithoutOwnerOrContext(); + } finally { + ReactContext.current = previousContext; + ReactCurrentOwner.current = null; } + ("production" !== process.env.NODE_ENV ? invariant( + // TODO: An `isValidNode` function would probably be more appropriate + renderedComponent === null || renderedComponent === false || + ReactElement.isValidElement(renderedComponent), + '%s.render(): A valid ReactComponent must be returned. You may have ' + + 'returned undefined, an array or some other invalid object.', + this.getName() || 'ReactCompositeComponent' + ) : invariant(// TODO: An `isValidNode` function would probably be more appropriate + renderedComponent === null || renderedComponent === false || + ReactElement.isValidElement(renderedComponent))); + return renderedComponent; }, /** - * Binds a method to the component. + * Lazily allocates the refs object and stores `component` as `ref`. * - * @param {function} method Method to be bound. + * @param {string} ref Reference name. + * @param {component} component Component to store as `ref`. + * @final * @private */ - _bindAutoBindMethod: function(method) { - var component = this; - var boundMethod = function() { - return method.apply(component, arguments); - }; - if ("production" !== process.env.NODE_ENV) { - boundMethod.__reactBoundContext = component; - boundMethod.__reactBoundMethod = method; - boundMethod.__reactBoundArguments = null; - var componentName = component.constructor.displayName; - var _bind = boundMethod.bind; - boundMethod.bind = function(newThis ) {var args=Array.prototype.slice.call(arguments,1); - // User is trying to bind() an autobound method; we effectively will - // ignore the value of "this" that the user is trying to use, so - // let's warn. - if (newThis !== component && newThis !== null) { - console.warn( - 'bind(): React component methods may only be bound to the ' + - 'component instance. See ' + componentName - ); - } else if (!args.length) { - console.warn( - 'bind(): You are binding a component method to the component. ' + - 'React does this for you automatically in a high-performance ' + - 'way, so you can safely remove this call. See ' + componentName - ); - return boundMethod; - } - var reboundMethod = _bind.apply(boundMethod, arguments); - reboundMethod.__reactBoundContext = component; - reboundMethod.__reactBoundMethod = method; - reboundMethod.__reactBoundArguments = args; - return reboundMethod; - }; - } - return boundMethod; - } -}; - -var ReactCompositeComponentBase = function() {}; -mixInto(ReactCompositeComponentBase, ReactComponent.Mixin); -mixInto(ReactCompositeComponentBase, ReactOwner.Mixin); -mixInto(ReactCompositeComponentBase, ReactPropTransferer.Mixin); -mixInto(ReactCompositeComponentBase, ReactCompositeComponentMixin); - -/** - * Checks if a value is a valid component constructor. - * - * @param {*} - * @return {boolean} - * @public - */ -function isValidClass(componentClass) { - return componentClass instanceof Function && - 'componentConstructor' in componentClass && - componentClass.componentConstructor instanceof Function; -} -/** - * Module for creating composite components. - * - * @class ReactCompositeComponent - * @extends ReactComponent - * @extends ReactOwner - * @extends ReactPropTransferer - */ -var ReactCompositeComponent = { - - LifeCycle: CompositeLifeCycle, - - Base: ReactCompositeComponentBase, + attachRef: function(ref, component) { + var inst = this.getPublicInstance(); + var refs = inst.refs === emptyObject ? (inst.refs = {}) : inst.refs; + refs[ref] = component.getPublicInstance(); + }, /** - * Creates a composite component class given a class specification. + * Detaches a reference name. * - * @param {object} spec Class specification (which must define `render`). - * @return {function} Component constructor function. - * @public + * @param {string} ref Name to dereference. + * @final + * @private */ - createClass: function(spec) { - var Constructor = function() {}; - Constructor.prototype = new ReactCompositeComponentBase(); - Constructor.prototype.constructor = Constructor; - - var ConvenienceConstructor = function(props, children) { - var instance = new Constructor(); - instance.construct.apply(instance, arguments); - return instance; - }; - ConvenienceConstructor.componentConstructor = Constructor; - Constructor.ConvenienceConstructor = ConvenienceConstructor; - ConvenienceConstructor.originalSpec = spec; + detachRef: function(ref) { + var refs = this.getPublicInstance().refs; + delete refs[ref]; + }, - mixSpecIntoComponent(ConvenienceConstructor, spec); + /** + * Get a text description of the component that can be used to identify it + * in error messages. + * @return {string} The name or null. + * @internal + */ + getName: function() { + var type = this._currentElement.type; + var constructor = this._instance && this._instance.constructor; + return ( + type.displayName || (constructor && constructor.displayName) || + type.name || (constructor && constructor.name) || + null + ); + }, - ("production" !== process.env.NODE_ENV ? invariant( - Constructor.prototype.render, - 'createClass(...): Class specification must implement a `render` method.' - ) : invariant(Constructor.prototype.render)); + /** + * Get the publicly accessible representation of this component - i.e. what + * is exposed by refs and returned by React.render. Can be null for stateless + * components. + * + * @return {ReactComponent} the public component instance. + * @internal + */ + getPublicInstance: function() { + return this._instance; + }, - if ("production" !== process.env.NODE_ENV) { - if (Constructor.prototype.componentShouldUpdate) { - console.warn( - (spec.displayName || 'A component') + ' has a method called ' + - 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' + - 'The name is phrased as a question because the function is ' + - 'expected to return a value.' - ); - } - } + // Stub + _instantiateReactComponent: null - // Expose the convience constructor on the prototype so that it can be - // easily accessed on descriptors. E.g. .type === Foo.type and for - // static methods like .type.staticMethod(); - // This should not be named constructor since this may not be the function - // that created the descriptor, and it may not even be a constructor. - ConvenienceConstructor.type = Constructor; - Constructor.prototype.type = Constructor; +}; - // Reduce time spent doing lookups by setting these on the prototype. - for (var methodName in ReactCompositeComponentInterface) { - if (!Constructor.prototype[methodName]) { - Constructor.prototype[methodName] = null; - } - } +ReactPerf.measureMethods( + ReactCompositeComponentMixin, + 'ReactCompositeComponent', + { + mountComponent: 'mountComponent', + updateComponent: 'updateComponent', + _renderValidatedComponent: '_renderValidatedComponent' + } +); - if ("production" !== process.env.NODE_ENV) { - Constructor.prototype = createMountWarningMembrane(Constructor.prototype); - } +var ReactCompositeComponent = { - return ConvenienceConstructor; - }, + Mixin: ReactCompositeComponentMixin - isValidClass: isValidClass }; module.exports = ReactCompositeComponent; }).call(this,require("FWaASH")) -},{"./ReactComponent":38,"./ReactContext":42,"./ReactCurrentOwner":43,"./ReactErrorUtils":59,"./ReactOwner":72,"./ReactPerf":73,"./ReactPropTransferer":74,"./ReactPropTypeLocationNames":75,"./ReactPropTypeLocations":76,"./ReactUpdates":88,"./invariant":129,"./keyMirror":135,"./merge":138,"./mixInto":141,"./objMap":142,"./shouldUpdateReactComponent":147,"FWaASH":6}],42:[function(require,module,exports){ +},{"./Object.assign":36,"./ReactComponentEnvironment":48,"./ReactContext":51,"./ReactCurrentOwner":52,"./ReactElement":70,"./ReactElementValidator":71,"./ReactInstanceMap":80,"./ReactLifeCycle":81,"./ReactNativeComponent":87,"./ReactPerf":89,"./ReactPropTypeLocationNames":91,"./ReactPropTypeLocations":92,"./ReactReconciler":96,"./ReactUpdates":107,"./emptyObject":138,"./invariant":158,"./shouldUpdateReactComponent":175,"./warning":179,"FWaASH":6}],51:[function(require,module,exports){ +(function (process){ /** - * Copyright 2013-2014 Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. * - * 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. + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule ReactContext */ -"use strict"; +'use strict'; + +var assign = require("./Object.assign"); +var emptyObject = require("./emptyObject"); +var warning = require("./warning"); -var merge = require("./merge"); +var didWarn = false; /** * Keeps track of the current context. @@ -15871,7 +16914,7 @@ var ReactContext = { * @internal * @type {object} */ - current: {}, + current: emptyObject, /** * Temporarily extends the current context while executing scopedCallback. @@ -15879,7 +16922,7 @@ var ReactContext = { * A typical use case might look like * * render: function() { - * var children = ReactContext.withContext({foo: 'foo'} () => ( + * var children = ReactContext.withContext({foo: 'foo'}, () => ( * * )); * return
{children}
; @@ -15890,9 +16933,19 @@ var ReactContext = { * @return {ReactComponent|array} */ withContext: function(newContext, scopedCallback) { + if ("production" !== process.env.NODE_ENV) { + ("production" !== process.env.NODE_ENV ? warning( + didWarn, + 'withContext is deprecated and will be removed in a future version. ' + + 'Use a wrapper component with getChildContext instead.' + ) : null); + + didWarn = true; + } + var result; var previousContext = ReactContext.current; - ReactContext.current = merge(previousContext, newContext); + ReactContext.current = assign({}, previousContext, newContext); try { result = scopedCallback(); } finally { @@ -15905,26 +16958,20 @@ var ReactContext = { module.exports = ReactContext; -},{"./merge":138}],43:[function(require,module,exports){ +}).call(this,require("FWaASH")) +},{"./Object.assign":36,"./emptyObject":138,"./warning":179,"FWaASH":6}],52:[function(require,module,exports){ /** - * Copyright 2013-2014 Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. * - * 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. + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule ReactCurrentOwner */ -"use strict"; +'use strict'; /** * Keeps track of the current owner. @@ -15946,69 +16993,38 @@ var ReactCurrentOwner = { module.exports = ReactCurrentOwner; -},{}],44:[function(require,module,exports){ +},{}],53:[function(require,module,exports){ +(function (process){ /** - * Copyright 2013-2014 Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. * - * 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. + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule ReactDOM * @typechecks static-only */ -"use strict"; +'use strict'; -var ReactDOMComponent = require("./ReactDOMComponent"); +var ReactElement = require("./ReactElement"); +var ReactElementValidator = require("./ReactElementValidator"); -var mergeInto = require("./mergeInto"); -var objMapKeyVal = require("./objMapKeyVal"); +var mapObject = require("./mapObject"); /** - * 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. + * Create a factory that creates HTML tag elements. * * @param {string} tag Tag name (e.g. `div`). - * @param {boolean} omitClose True if the close tag should be omitted. * @private */ -function createDOMComponentClass(tag, omitClose) { - var Constructor = function() {}; - Constructor.prototype = new ReactDOMComponent(tag, omitClose); - Constructor.prototype.constructor = Constructor; - Constructor.displayName = tag; - - var ConvenienceConstructor = function(props, children) { - var instance = new Constructor(); - instance.construct.apply(instance, arguments); - return instance; - }; - - // Expose the constructor on the ConvenienceConstructor and prototype so that - // it can be easily easily accessed on descriptors. - // E.g.
.type === div.type - ConvenienceConstructor.type = Constructor; - Constructor.prototype.type = Constructor; - - Constructor.ConvenienceConstructor = ConvenienceConstructor; - ConvenienceConstructor.componentConstructor = Constructor; - return ConvenienceConstructor; +function createDOMFactory(tag) { + if ("production" !== process.env.NODE_ENV) { + return ReactElementValidator.createFactory(tag); + } + return ReactElement.createFactory(tag); } /** @@ -16017,173 +17033,166 @@ function createDOMComponentClass(tag, omitClose) { * * @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, +var ReactDOM = mapObject({ + a: 'a', + abbr: 'abbr', + address: 'address', + area: 'area', + article: 'article', + aside: 'aside', + audio: 'audio', + b: 'b', + base: 'base', + bdi: 'bdi', + bdo: 'bdo', + big: 'big', + blockquote: 'blockquote', + body: 'body', + br: 'br', + button: 'button', + canvas: 'canvas', + caption: 'caption', + cite: 'cite', + code: 'code', + col: 'col', + colgroup: 'colgroup', + data: 'data', + datalist: 'datalist', + dd: 'dd', + del: 'del', + details: 'details', + dfn: 'dfn', + dialog: 'dialog', + div: 'div', + dl: 'dl', + dt: 'dt', + em: 'em', + embed: 'embed', + fieldset: 'fieldset', + figcaption: 'figcaption', + figure: 'figure', + footer: 'footer', + form: 'form', + h1: 'h1', + h2: 'h2', + h3: 'h3', + h4: 'h4', + h5: 'h5', + h6: 'h6', + head: 'head', + header: 'header', + hr: 'hr', + html: 'html', + i: 'i', + iframe: 'iframe', + img: 'img', + input: 'input', + ins: 'ins', + kbd: 'kbd', + keygen: 'keygen', + label: 'label', + legend: 'legend', + li: 'li', + link: 'link', + main: 'main', + map: 'map', + mark: 'mark', + menu: 'menu', + menuitem: 'menuitem', + meta: 'meta', + meter: 'meter', + nav: 'nav', + noscript: 'noscript', + object: 'object', + ol: 'ol', + optgroup: 'optgroup', + option: 'option', + output: 'output', + p: 'p', + param: 'param', + picture: 'picture', + pre: 'pre', + progress: 'progress', + q: 'q', + rp: 'rp', + rt: 'rt', + ruby: 'ruby', + s: 's', + samp: 'samp', + script: 'script', + section: 'section', + select: 'select', + small: 'small', + source: 'source', + span: 'span', + strong: 'strong', + style: 'style', + sub: 'sub', + summary: 'summary', + sup: 'sup', + table: 'table', + tbody: 'tbody', + td: 'td', + textarea: 'textarea', + tfoot: 'tfoot', + th: 'th', + thead: 'thead', + time: 'time', + title: 'title', + tr: 'tr', + track: 'track', + u: 'u', + ul: 'ul', + 'var': 'var', + video: 'video', + wbr: 'wbr', // SVG - circle: false, - defs: false, - g: false, - line: false, - linearGradient: false, - path: false, - polygon: false, - polyline: false, - radialGradient: false, - rect: false, - stop: false, - svg: false, - text: false -}, createDOMComponentClass); - -var injection = { - injectComponentClasses: function(componentClasses) { - mergeInto(ReactDOM, componentClasses); - } -}; - -ReactDOM.injection = injection; + circle: 'circle', + defs: 'defs', + ellipse: 'ellipse', + g: 'g', + line: 'line', + linearGradient: 'linearGradient', + mask: 'mask', + path: 'path', + pattern: 'pattern', + polygon: 'polygon', + polyline: 'polyline', + radialGradient: 'radialGradient', + rect: 'rect', + stop: 'stop', + svg: 'svg', + text: 'text', + tspan: 'tspan' + +}, createDOMFactory); module.exports = ReactDOM; -},{"./ReactDOMComponent":46,"./mergeInto":140,"./objMapKeyVal":143}],45:[function(require,module,exports){ +}).call(this,require("FWaASH")) +},{"./ReactElement":70,"./ReactElementValidator":71,"./mapObject":166,"FWaASH":6}],54:[function(require,module,exports){ /** - * Copyright 2013-2014 Facebook, Inc. + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. * - * 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. + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule ReactDOMButton */ -"use strict"; +'use strict'; var AutoFocusMixin = require("./AutoFocusMixin"); -var ReactCompositeComponent = require("./ReactCompositeComponent"); -var ReactDOM = require("./ReactDOM"); +var ReactBrowserComponentMixin = require("./ReactBrowserComponentMixin"); +var ReactClass = require("./ReactClass"); +var ReactElement = require("./ReactElement"); var keyMirror = require("./keyMirror"); -// Store a reference to the