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