author | Ryan VanderMeulen <ryanvm@gmail.com> |
Mon, 04 Aug 2014 16:03:44 -0400 | |
changeset 197503 | 25bf2f1664e91cc2192bb38aa825c80a5a758bd0 |
parent 197491 | adb44e1f75599081eeffad443f8d87350717bb5e (current diff) |
parent 197502 | 9595e2bb6f04e3dac1664f7c372f2db3d498f912 (diff) |
child 197734 | 7f81be7db52884f813fd2d6ff2ae4fd0d8aeb452 |
push id | 27248 |
push user | ryanvm@gmail.com |
push date | Mon, 04 Aug 2014 20:03:52 +0000 |
treeherder | mozilla-central@25bf2f1664e9 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 34.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
browser/components/loop/content/shared/libs/react-0.10.0.js | file | annotate | diff | comparison | revisions |
--- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1570,20 +1570,24 @@ pref("image.mem.max_decoded_image_kb", 2 #ifndef RELEASE_BUILD pref("loop.enabled", true); #else pref("loop.enabled", false); #endif pref("loop.server", "https://loop.services.mozilla.com"); pref("loop.seenToS", "unseen"); +pref("loop.legal.ToS_url", "https://accounts.firefox.com/legal/terms"); +pref("loop.legal.privacy_url", "https://www.mozilla.org/privacy/"); pref("loop.do_not_disturb", false); pref("loop.ringtone", "chrome://browser/content/loop/shared/sounds/Firefox-Long.ogg"); pref("loop.retry_delay.start", 60000); pref("loop.retry_delay.limit", 300000); +pref("loop.feedback.baseUrl", "https://input.mozilla.org/api/v1/feedback"); +pref("loop.feedback.product", "Loop"); // serverURL to be assigned by services team pref("services.push.serverURL", "wss://push.services.mozilla.com/"); pref("social.sidebar.unload_timeout_ms", 10000); pref("dom.identity.enabled", false);
--- a/browser/base/content/webrtcIndicator.js +++ b/browser/base/content/webrtcIndicator.js @@ -1,13 +1,13 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -const Cu = Components.utils; +const {classes: Cc, interfaces: Ci, utils: Cu} = Components; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource:///modules/webrtcUI.jsm"); const BUNDLE_URL = "chrome://browser/locale/webrtcIndicator.properties"; let gStringBundle; function init(event) { gStringBundle = Services.strings.createBundle(BUNDLE_URL); @@ -124,17 +124,22 @@ function onFirefoxButtonClick(event) { } let PositionHandler = { positionCustomized: false, threshold: 10, adjustPosition: function() { if (!this.positionCustomized) { // Center the window horizontally on the screen (not the available area). - window.moveTo((screen.width - document.documentElement.clientWidth) / 2, 0); + // Until we have moved the window to y=0, 'screen.width' may give a value + // for a secondary screen, so use values from the screen manager instead. + let width = {}; + Cc["@mozilla.org/gfx/screenmanager;1"].getService(Ci.nsIScreenManager) + .primaryScreen.GetRectDisplayPix({}, {}, width, {}); + window.moveTo((width.value - document.documentElement.clientWidth) / 2, 0); } else { // This will ensure we're at y=0. this.setXPosition(window.screenX); } }, setXPosition: function(desiredX) { // Ensure the indicator isn't moved outside the available area of the screen. let desiredX = Math.max(desiredX, screen.availLeft);
--- a/browser/components/loop/content/conversation.html +++ b/browser/components/loop/content/conversation.html @@ -21,22 +21,23 @@ window.OTProperties = { cdnURL: 'loop/', }; window.OTProperties.assetURL = window.OTProperties.cdnURL + 'sdk-content/'; window.OTProperties.configURL = window.OTProperties.assetURL + 'js/dynamic_config.min.js'; window.OTProperties.cssURL = window.OTProperties.assetURL + 'css/ot.css'; </script> <script type="text/javascript" src="loop/libs/sdk.js"></script> - <script type="text/javascript" src="loop/shared/libs/react-0.10.0.js"></script> + <script type="text/javascript" src="loop/shared/libs/react-0.11.1.js"></script> <script type="text/javascript" src="loop/shared/libs/jquery-2.1.0.js"></script> <script type="text/javascript" src="loop/shared/libs/lodash-2.4.1.js"></script> <script type="text/javascript" src="loop/shared/libs/backbone-1.1.2.js"></script> <script type="text/javascript" src="loop/shared/js/utils.js"></script> <script type="text/javascript" src="loop/shared/js/models.js"></script> <script type="text/javascript" src="loop/shared/js/router.js"></script> <script type="text/javascript" src="loop/shared/js/views.js"></script> + <script type="text/javascript" src="loop/shared/js/feedbackApiClient.js"></script> <script type="text/javascript" src="loop/js/client.js"></script> <script type="text/javascript" src="loop/js/desktopRouter.js"></script> <script type="text/javascript" src="loop/js/conversation.js"></script> </body> </html>
--- a/browser/components/loop/content/js/conversation.js +++ b/browser/components/loop/content/js/conversation.js @@ -112,80 +112,56 @@ loop.conversation = (function(OT, mozL10 ) ) ); /* jshint ignore:end */ } }); /** - * Call ended view. - * @type {loop.shared.views.BaseView} - */ - var EndedCallView = sharedViews.BaseView.extend({ - template: _.template([ - '<p>', - ' <button class="btn btn-info" data-l10n-id="close_window"></button>', - '</p>' - ].join("")), - - className: "call-ended", - - events: { - "click button": "closeWindow" - }, - - closeWindow: function(event) { - event.preventDefault(); - // XXX For now, we just close the window. - window.close(); - } - }); - - /** * Conversation router. * * Required options: * - {loop.shared.models.ConversationModel} conversation Conversation model. * - {loop.shared.components.Notifier} notifier Notifier component. * * @type {loop.shared.router.BaseConversationRouter} */ var ConversationRouter = loop.desktopRouter.DesktopConversationRouter.extend({ routes: { "incoming/:version": "incoming", "call/accept": "accept", "call/decline": "decline", "call/ongoing": "conversation", - "call/ended": "ended", - "call/declineAndBlock": "declineAndBlock" + "call/declineAndBlock": "declineAndBlock", + "call/feedback": "feedback" }, /** * @override {loop.shared.router.BaseConversationRouter.startCall} */ startCall: function() { this.navigate("call/ongoing", {trigger: true}); }, /** * @override {loop.shared.router.BaseConversationRouter.endCall} */ endCall: function() { - this.navigate("call/ended", {trigger: true}); + this.navigate("call/feedback", {trigger: true}); }, /** * Incoming call route. * * @param {String} loopVersion The version from the push notification, set * by the router from the URL. */ incoming: function(loopVersion) { - window.navigator.mozLoop.startAlerting(); + navigator.mozLoop.startAlerting(); this._conversation.set({loopVersion: loopVersion}); this._conversation.once("accept", function() { this.navigate("call/accept", {trigger: true}); }.bind(this)); this._conversation.once("decline", function() { this.navigate("call/decline", {trigger: true}); }.bind(this)); this._conversation.once("declineAndBlock", function() { @@ -195,40 +171,40 @@ loop.conversation = (function(OT, mozL10 model: this._conversation })); }, /** * Accepts an incoming call. */ accept: function() { - window.navigator.mozLoop.stopAlerting(); + navigator.mozLoop.stopAlerting(); this._conversation.initiate({ client: new loop.Client(), outgoing: false }); }, /** * Declines an incoming call. */ decline: function() { - window.navigator.mozLoop.stopAlerting(); + navigator.mozLoop.stopAlerting(); // XXX For now, we just close the window window.close(); }, /** * Decline and block an incoming call * @note: * - loopToken is the callUrl identifier. It gets set in the panel * after a callUrl is received */ declineAndBlock: function() { - window.navigator.mozLoop.stopAlerting(); + navigator.mozLoop.stopAlerting(); var token = navigator.mozLoop.getLoopCharPref('loopToken'); var client = new loop.Client(); client.deleteCallUrl(token, function(error) { // XXX The conversation window will be closed when this cb is triggered // figure out if there is a better way to report the error to the user console.log(error); }); window.close(); @@ -249,39 +225,45 @@ loop.conversation = (function(OT, mozL10 /*jshint newcap:false*/ this.loadReactComponent(sharedViews.ConversationView({ sdk: OT, model: this._conversation })); }, /** - * XXX: load a view with a close button for now? + * Call has ended, display a feedback form. */ - ended: function() { - this.loadView(new EndedCallView()); + feedback: function() { + document.title = mozL10n.get("call_has_ended"); + + this.loadReactComponent(sharedViews.FeedbackView({ + feedbackApiClient: new loop.FeedbackAPIClient({ + baseUrl: navigator.mozLoop.getLoopCharPref("feedback.baseUrl"), + product: navigator.mozLoop.getLoopCharPref("feedback.product") + }) + })); } }); /** * Panel initialisation. */ function init() { // Do the initial L10n setup, we do this before anything // else to ensure the L10n environment is setup correctly. - mozL10n.initialize(window.navigator.mozLoop); + mozL10n.initialize(navigator.mozLoop); document.title = mozL10n.get("incoming_call_title"); router = new ConversationRouter({ conversation: new loop.shared.models.ConversationModel({}, {sdk: OT}), notifier: new sharedViews.NotificationListView({el: "#messages"}) }); Backbone.history.start(); } return { ConversationRouter: ConversationRouter, - EndedCallView: EndedCallView, IncomingCallView: IncomingCallView, init: init }; })(window.OT, document.mozL10n);
--- a/browser/components/loop/content/js/conversation.jsx +++ b/browser/components/loop/content/js/conversation.jsx @@ -112,80 +112,56 @@ loop.conversation = (function(OT, mozL10 </div> </div> ); /* jshint ignore:end */ } }); /** - * Call ended view. - * @type {loop.shared.views.BaseView} - */ - var EndedCallView = sharedViews.BaseView.extend({ - template: _.template([ - '<p>', - ' <button class="btn btn-info" data-l10n-id="close_window"></button>', - '</p>' - ].join("")), - - className: "call-ended", - - events: { - "click button": "closeWindow" - }, - - closeWindow: function(event) { - event.preventDefault(); - // XXX For now, we just close the window. - window.close(); - } - }); - - /** * Conversation router. * * Required options: * - {loop.shared.models.ConversationModel} conversation Conversation model. * - {loop.shared.components.Notifier} notifier Notifier component. * * @type {loop.shared.router.BaseConversationRouter} */ var ConversationRouter = loop.desktopRouter.DesktopConversationRouter.extend({ routes: { "incoming/:version": "incoming", "call/accept": "accept", "call/decline": "decline", "call/ongoing": "conversation", - "call/ended": "ended", - "call/declineAndBlock": "declineAndBlock" + "call/declineAndBlock": "declineAndBlock", + "call/feedback": "feedback" }, /** * @override {loop.shared.router.BaseConversationRouter.startCall} */ startCall: function() { this.navigate("call/ongoing", {trigger: true}); }, /** * @override {loop.shared.router.BaseConversationRouter.endCall} */ endCall: function() { - this.navigate("call/ended", {trigger: true}); + this.navigate("call/feedback", {trigger: true}); }, /** * Incoming call route. * * @param {String} loopVersion The version from the push notification, set * by the router from the URL. */ incoming: function(loopVersion) { - window.navigator.mozLoop.startAlerting(); + navigator.mozLoop.startAlerting(); this._conversation.set({loopVersion: loopVersion}); this._conversation.once("accept", function() { this.navigate("call/accept", {trigger: true}); }.bind(this)); this._conversation.once("decline", function() { this.navigate("call/decline", {trigger: true}); }.bind(this)); this._conversation.once("declineAndBlock", function() { @@ -195,40 +171,40 @@ loop.conversation = (function(OT, mozL10 model: this._conversation })); }, /** * Accepts an incoming call. */ accept: function() { - window.navigator.mozLoop.stopAlerting(); + navigator.mozLoop.stopAlerting(); this._conversation.initiate({ client: new loop.Client(), outgoing: false }); }, /** * Declines an incoming call. */ decline: function() { - window.navigator.mozLoop.stopAlerting(); + navigator.mozLoop.stopAlerting(); // XXX For now, we just close the window window.close(); }, /** * Decline and block an incoming call * @note: * - loopToken is the callUrl identifier. It gets set in the panel * after a callUrl is received */ declineAndBlock: function() { - window.navigator.mozLoop.stopAlerting(); + navigator.mozLoop.stopAlerting(); var token = navigator.mozLoop.getLoopCharPref('loopToken'); var client = new loop.Client(); client.deleteCallUrl(token, function(error) { // XXX The conversation window will be closed when this cb is triggered // figure out if there is a better way to report the error to the user console.log(error); }); window.close(); @@ -249,39 +225,45 @@ loop.conversation = (function(OT, mozL10 /*jshint newcap:false*/ this.loadReactComponent(sharedViews.ConversationView({ sdk: OT, model: this._conversation })); }, /** - * XXX: load a view with a close button for now? + * Call has ended, display a feedback form. */ - ended: function() { - this.loadView(new EndedCallView()); + feedback: function() { + document.title = mozL10n.get("call_has_ended"); + + this.loadReactComponent(sharedViews.FeedbackView({ + feedbackApiClient: new loop.FeedbackAPIClient({ + baseUrl: navigator.mozLoop.getLoopCharPref("feedback.baseUrl"), + product: navigator.mozLoop.getLoopCharPref("feedback.product") + }) + })); } }); /** * Panel initialisation. */ function init() { // Do the initial L10n setup, we do this before anything // else to ensure the L10n environment is setup correctly. - mozL10n.initialize(window.navigator.mozLoop); + mozL10n.initialize(navigator.mozLoop); document.title = mozL10n.get("incoming_call_title"); router = new ConversationRouter({ conversation: new loop.shared.models.ConversationModel({}, {sdk: OT}), notifier: new sharedViews.NotificationListView({el: "#messages"}) }); Backbone.history.start(); } return { ConversationRouter: ConversationRouter, - EndedCallView: EndedCallView, IncomingCallView: IncomingCallView, init: init }; })(window.OT, document.mozL10n);
--- a/browser/components/loop/content/js/panel.js +++ b/browser/components/loop/content/js/panel.js @@ -103,22 +103,31 @@ loop.panel = (function(_, mozL10n) { }); var ToSView = React.createClass({displayName: 'ToSView', getInitialState: function() { return {seenToS: navigator.mozLoop.getLoopCharPref('seenToS')}; }, render: function() { - var tosHTML = __("legal_text_and_links", { - "terms_of_use_url": "https://accounts.firefox.com/legal/terms", - "privacy_notice_url": "www.mozilla.org/privacy/" - }); - if (this.state.seenToS == "unseen") { + var terms_of_use_url = navigator.mozLoop.getLoopCharPref('legal.ToS_url'); + var privacy_notice_url = navigator.mozLoop.getLoopCharPref('legal.privacy_url'); + var tosHTML = __("legal_text_and_links2", { + "terms_of_use": React.renderComponentToStaticMarkup( + React.DOM.a({href: terms_of_use_url, target: "_blank"}, + __("legal_text_tos") + ) + ), + "privacy_notice": React.renderComponentToStaticMarkup( + React.DOM.a({href: privacy_notice_url, target: "_blank"}, + __("legal_text_privacy") + ) + ), + }); navigator.mozLoop.setLoopCharPref('seenToS', 'seen'); return React.DOM.p({className: "terms-service", dangerouslySetInnerHTML: {__html: tosHTML}}); } else { return React.DOM.div(null); } } });
--- a/browser/components/loop/content/js/panel.jsx +++ b/browser/components/loop/content/js/panel.jsx @@ -103,22 +103,31 @@ loop.panel = (function(_, mozL10n) { }); var ToSView = React.createClass({ getInitialState: function() { return {seenToS: navigator.mozLoop.getLoopCharPref('seenToS')}; }, render: function() { - var tosHTML = __("legal_text_and_links", { - "terms_of_use_url": "https://accounts.firefox.com/legal/terms", - "privacy_notice_url": "www.mozilla.org/privacy/" - }); - if (this.state.seenToS == "unseen") { + var terms_of_use_url = navigator.mozLoop.getLoopCharPref('legal.ToS_url'); + var privacy_notice_url = navigator.mozLoop.getLoopCharPref('legal.privacy_url'); + var tosHTML = __("legal_text_and_links2", { + "terms_of_use": React.renderComponentToStaticMarkup( + <a href={terms_of_use_url} target="_blank"> + {__("legal_text_tos")} + </a> + ), + "privacy_notice": React.renderComponentToStaticMarkup( + <a href={privacy_notice_url} target="_blank"> + {__("legal_text_privacy")} + </a> + ), + }); navigator.mozLoop.setLoopCharPref('seenToS', 'seen'); return <p className="terms-service" dangerouslySetInnerHTML={{__html: tosHTML}}></p>; } else { return <div />; } } });
--- a/browser/components/loop/content/panel.html +++ b/browser/components/loop/content/panel.html @@ -10,17 +10,17 @@ <link rel="stylesheet" type="text/css" href="loop/shared/css/panel.css"> </head> <body class="panel" onload="loop.panel.init();"> <div id="messages"></div> <div id="main"></div> - <script type="text/javascript" src="loop/shared/libs/react-0.10.0.js"></script> + <script type="text/javascript" src="loop/shared/libs/react-0.11.1.js"></script> <script type="text/javascript" src="loop/libs/l10n.js"></script> <script type="text/javascript" src="loop/shared/libs/jquery-2.1.0.js"></script> <script type="text/javascript" src="loop/shared/libs/lodash-2.4.1.js"></script> <script type="text/javascript" src="loop/shared/libs/backbone-1.1.2.js"></script> <script type="text/javascript" src="loop/shared/js/models.js"></script> <script type="text/javascript" src="loop/shared/js/router.js"></script> <script type="text/javascript" src="loop/shared/js/views.js"></script>
--- a/browser/components/loop/content/shared/css/conversation.css +++ b/browser/components/loop/content/shared/css/conversation.css @@ -233,8 +233,110 @@ color: #000; cursor: pointer; } .decline-block-menu li:hover { color: #FFF; background: #111; } + +/* Expired call url page */ + +.expired-url-info { + width: 400px; + margin: 0 auto; +} + +.promote-firefox { + text-align: center; + font-size: 18px; + line-height: 24px; + margin: 2em 0; +} + +.promote-firefox h3 { + font-weight: 300; +} + +/* Feedback form */ + +.feedback { + padding: 1em; +} + +.feedback h3 { + color: #666; + font-size: 12px; + font-weight: 700; + text-align: center; + margin-bottom: 10px; + margin-top: 15px; +} + +.feedback .faces { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + padding: 20px 0; +} + +.feedback .face { + border: 1px solid transparent; + box-shadow: 0px 1px 2px #CCC; + cursor: pointer; + border-radius: 4px; + margin: 0px 10px; + width: 80px; + height: 80px; + background-color: #fbfbfb; + background-size: 60px auto; + background-position: center center; + background-repeat: no-repeat; +} + +.feedback .face:hover { + border: 1px solid #DDD; + background-color: #FEFEFE; +} + +.feedback .face.face-happy { + background-image: url("../img/happy.png"); +} + +.feedback .face.face-sad { + background-image: url("../img/sad.png"); +} + +.feedback button.back { + border-radius: 2px; + border: 1px solid #CCC; + color: #CCC; + font-size: 11px; + cursor: pointer; + padding: 3px 10px; + display: inline; + margin-bottom: 1em; +} + +.feedback label { + display: block; +} + +.feedback form input[type="radio"] { + margin-right: .5em; +} + +.feedback form button[type="submit"] { + width: 100%; +} + +.feedback form input[type="text"] { + width: 100%; +} + +.feedback .info { + display: block; + font-size: 10px; + color: #CCC; + text-align: center; +}
new file mode 100644 index 0000000000000000000000000000000000000000..8e79faf9c6c9408bb6d990659118f1166c721822 GIT binary patch literal 40247 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�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
new file mode 100644 index 0000000000000000000000000000000000000000..13f2dbb88f4df2fb36c943979dac82341cce7678 GIT binary patch literal 40649 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�*f+m9S$Ur;S}!A|}QL_1_Pzbn^LWhAX- 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 KZty3;G2(xKq55F}
new file mode 100644 --- /dev/null +++ b/browser/components/loop/content/shared/js/feedbackApiClient.js @@ -0,0 +1,92 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* global loop:true */ + +var loop = loop || {}; +loop.FeedbackAPIClient = (function($) { + "use strict"; + + /** + * Feedback API client. Sends feedback data to an input.mozilla.com compatible + * API. + * + * Available settings: + * - {String} baseUrl Base API url (required) + * - {String} product Product name (required) + * + * @param {Object} settings Settings. + * @link http://fjord.readthedocs.org/en/latest/api.html + */ + function FeedbackAPIClient(settings) { + settings = settings || {}; + if (!settings.hasOwnProperty("baseUrl")) { + throw new Error("Missing required baseUrl setting."); + } + this._baseUrl = settings.baseUrl; + if (!settings.hasOwnProperty("product")) { + throw new Error("Missing required product setting."); + } + this._product = settings.product; + } + + FeedbackAPIClient.prototype = { + /** + * Formats Feedback data to match the API spec. + * + * @param {Object} fields Feedback form data. + * @return {Object} Formatted data. + */ + _formatData: function(fields) { + var formatted = {}; + + if (typeof fields !== "object") { + throw new Error("Invalid feedback data provided."); + } + + formatted.product = this._product; + formatted.happy = fields.happy; + formatted.category = fields.category; + + // Default description field value + if (!fields.description) { + formatted.description = (fields.happy ? "Happy" : "Sad") + " User"; + } else { + formatted.description = fields.description; + } + + return formatted; + }, + + /** + * Sends feedback data. + * + * @param {Object} fields Feedback form data. + * @param {Function} cb Callback(err, result) + */ + send: function(fields, cb) { + var req = $.ajax({ + url: this._baseUrl, + method: "POST", + contentType: "application/json", + dataType: "json", + data: JSON.stringify(this._formatData(fields)) + }); + + req.done(function(result) { + console.info("User feedback data have been submitted", result); + cb(null, result); + }); + + req.fail(function(jqXHR, textStatus, errorThrown) { + var message = "Error posting user feedback data"; + var httpError = jqXHR.status + " " + errorThrown; + console.error(message, httpError, JSON.stringify(jqXHR.responseJSON)); + cb(new Error(message + ": " + httpError)); + }); + } + }; + + return FeedbackAPIClient; +})(jQuery);
--- a/browser/components/loop/content/shared/js/router.js +++ b/browser/components/loop/content/shared/js/router.js @@ -161,17 +161,16 @@ loop.shared.router = (function(l10n) { _onSessionReady: function() { this.startCall(); }, /** * Session has ended. Notifies the user and ends the call. */ _onSessionEnded: function() { - this._notifier.warnL10n("call_has_ended"); this.endCall(); }, /** * Peer hung up. Notifies the user and ends the call. * * Event properties: * - {String} connectionId: OT session id
--- a/browser/components/loop/content/shared/js/views.js +++ b/browser/components/loop/content/shared/js/views.js @@ -8,16 +8,17 @@ /* global loop:true, React */ var loop = loop || {}; loop.shared = loop.shared || {}; loop.shared.views = (function(_, OT, l10n) { "use strict"; var sharedModels = loop.shared.models; var __ = l10n.get; + var WINDOW_AUTOCLOSE_TIMEOUT_IN_SECONDS = 5; /** * L10n view. Translates resulting view DOM fragment once rendered. */ var L10nView = (function() { var L10nViewImpl = Backbone.View.extend(), // Original View constructor originalExtend = L10nViewImpl.extend; // Original static extend fn @@ -348,16 +349,249 @@ loop.shared.views = (function(_, OT, l10 ) ) ); /* jshint ignore:end */ } }); /** + * Feedback outer layout. + * + * Props: + * - + */ + var FeedbackLayout = React.createClass({displayName: 'FeedbackLayout', + propTypes: { + children: React.PropTypes.component.isRequired, + title: React.PropTypes.string.isRequired, + reset: React.PropTypes.func // if not specified, no Back btn is shown + }, + + render: function() { + var backButton = React.DOM.div(null); + if (this.props.reset) { + backButton = ( + React.DOM.button({className: "back", type: "button", onClick: this.props.reset}, + "« ", __("feedback_back_button") + ) + ); + } + return ( + React.DOM.div({className: "feedback"}, + backButton, + React.DOM.h3(null, this.props.title), + this.props.children + ) + ); + } + }); + + /** + * Detailed feedback form. + */ + var FeedbackForm = React.createClass({displayName: 'FeedbackForm', + propTypes: { + pending: React.PropTypes.bool, + sendFeedback: React.PropTypes.func, + reset: React.PropTypes.func + }, + + getInitialState: function() { + return {category: "", description: ""}; + }, + + getInitialProps: function() { + return {pending: false}; + }, + + _getCategories: function() { + return { + audio_quality: __("feedback_category_audio_quality"), + video_quality: __("feedback_category_video_quality"), + disconnected : __("feedback_category_was_disconnected"), + confusing: __("feedback_category_confusing"), + other: __("feedback_category_other") + }; + }, + + _getCategoryFields: function() { + var categories = this._getCategories(); + return Object.keys(categories).map(function(category, key) { + return ( + React.DOM.label({key: key}, + React.DOM.input({type: "radio", ref: "category", name: "category", + value: category, + onChange: this.handleCategoryChange}), + categories[category] + ) + ); + }, this); + }, + + /** + * Checks if the form is ready for submission: + * - a category (reason) must be chosen + * - no feedback submission should be pending + * + * @return {Boolean} + */ + _isFormReady: function() { + return this.state.category !== "" && !this.props.pending; + }, + + handleCategoryChange: function(event) { + var category = event.target.value; + if (category !== "other") { + // resets description text field + this.setState({description: ""}); + } + this.setState({category: category}); + }, + + handleCustomTextChange: function(event) { + this.setState({description: event.target.value}); + }, + + handleFormSubmit: function(event) { + event.preventDefault(); + this.props.sendFeedback({ + happy: false, + category: this.state.category, + description: this.state.description + }); + }, + + render: function() { + return ( + FeedbackLayout({title: __("feedback_what_makes_you_sad"), + reset: this.props.reset}, + React.DOM.form({onSubmit: this.handleFormSubmit}, + this._getCategoryFields(), + React.DOM.p(null, React.DOM.input({type: "text", ref: "description", name: "description", + disabled: this.state.category !== "other", + onChange: this.handleCustomTextChange, + value: this.state.description})), + React.DOM.button({type: "submit", className: "btn btn-success", + disabled: !this._isFormReady()}, + __("feedback_submit_button") + ) + ) + ) + ); + } + }); + + /** + * Feedback received view. + */ + var FeedbackReceived = React.createClass({displayName: 'FeedbackReceived', + getInitialState: function() { + return {countdown: WINDOW_AUTOCLOSE_TIMEOUT_IN_SECONDS}; + }, + + componentDidMount: function() { + this._timer = setInterval(function() { + this.setState({countdown: this.state.countdown - 1}); + }.bind(this), 1000); + }, + + componentWillUnmount: function() { + if (this._timer) { + clearInterval(this._timer); + } + }, + + render: function() { + if (this.state.countdown < 1) { + clearInterval(this._timer); + window.close(); + } + return ( + FeedbackLayout({title: __("feedback_thank_you_heading")}, + React.DOM.p({className: "info thank-you"}, __("feedback_window_will_close_in", { + countdown: this.state.countdown + })) + ) + ); + } + }); + + /** + * Feedback view. + */ + var FeedbackView = React.createClass({displayName: 'FeedbackView', + propTypes: { + // A loop.FeedbackAPIClient instance + feedbackApiClient: React.PropTypes.object.isRequired, + // The current feedback submission flow step name + step: React.PropTypes.oneOf(["start", "form", "finished"]) + }, + + getInitialState: function() { + return {pending: false, step: this.props.step || "start"}; + }, + + getInitialProps: function() { + return {step: "start"}; + }, + + reset: function() { + this.setState(this.getInitialState()); + }, + + handleHappyClick: function() { + this.sendFeedback({happy: true}, this._onFeedbackSent); + }, + + handleSadClick: function() { + this.setState({step: "form"}); + }, + + sendFeedback: function(fields) { + // Setting state.pending to true will disable the submit button to avoid + // multiple submissions + this.setState({pending: true}); + // Sends feedback data + this.props.feedbackApiClient.send(fields, this._onFeedbackSent); + }, + + _onFeedbackSent: function(err) { + if (err) { + // XXX better end user error reporting, see bug 1046738 + console.error("Unable to send user feedback", err); + } + this.setState({pending: false, step: "finished"}); + }, + + render: function() { + switch(this.state.step) { + case "finished": + return FeedbackReceived(null); + case "form": + return FeedbackForm({feedbackApiClient: this.props.feedbackApiClient, + sendFeedback: this.sendFeedback, + reset: this.reset, + pending: this.state.pending}); + default: + return ( + FeedbackLayout({title: __("feedback_call_experience_heading")}, + React.DOM.div({className: "faces"}, + React.DOM.button({className: "face face-happy", + onClick: this.handleHappyClick}), + React.DOM.button({className: "face face-sad", + onClick: this.handleSadClick}) + ) + ) + ); + } + } + }); + + /** * Notification view. */ var NotificationView = BaseView.extend({ template: _.template([ '<div class="alert alert-<%- level %>">', ' <button class="close"></button>', ' <p class="message"><%- message %></p>', '</div>' @@ -513,15 +747,16 @@ loop.shared.views = (function(_, OT, l10 ].join("")) }); return { L10nView: L10nView, BaseView: BaseView, ConversationView: ConversationView, ConversationToolbar: ConversationToolbar, + FeedbackView: FeedbackView, MediaControlButton: MediaControlButton, NotificationListView: NotificationListView, NotificationView: NotificationView, UnsupportedBrowserView: UnsupportedBrowserView, UnsupportedDeviceView: UnsupportedDeviceView }; })(_, window.OT, document.webL10n || document.mozL10n);
--- a/browser/components/loop/content/shared/js/views.jsx +++ b/browser/components/loop/content/shared/js/views.jsx @@ -8,16 +8,17 @@ /* global loop:true, React */ var loop = loop || {}; loop.shared = loop.shared || {}; loop.shared.views = (function(_, OT, l10n) { "use strict"; var sharedModels = loop.shared.models; var __ = l10n.get; + var WINDOW_AUTOCLOSE_TIMEOUT_IN_SECONDS = 5; /** * L10n view. Translates resulting view DOM fragment once rendered. */ var L10nView = (function() { var L10nViewImpl = Backbone.View.extend(), // Original View constructor originalExtend = L10nViewImpl.extend; // Original static extend fn @@ -348,16 +349,249 @@ loop.shared.views = (function(_, OT, l10 </div> </div> ); /* jshint ignore:end */ } }); /** + * Feedback outer layout. + * + * Props: + * - + */ + var FeedbackLayout = React.createClass({ + propTypes: { + children: React.PropTypes.component.isRequired, + title: React.PropTypes.string.isRequired, + reset: React.PropTypes.func // if not specified, no Back btn is shown + }, + + render: function() { + var backButton = <div />; + if (this.props.reset) { + backButton = ( + <button className="back" type="button" onClick={this.props.reset}> + « {__("feedback_back_button")} + </button> + ); + } + return ( + <div className="feedback"> + {backButton} + <h3>{this.props.title}</h3> + {this.props.children} + </div> + ); + } + }); + + /** + * Detailed feedback form. + */ + var FeedbackForm = React.createClass({ + propTypes: { + pending: React.PropTypes.bool, + sendFeedback: React.PropTypes.func, + reset: React.PropTypes.func + }, + + getInitialState: function() { + return {category: "", description: ""}; + }, + + getInitialProps: function() { + return {pending: false}; + }, + + _getCategories: function() { + return { + audio_quality: __("feedback_category_audio_quality"), + video_quality: __("feedback_category_video_quality"), + disconnected : __("feedback_category_was_disconnected"), + confusing: __("feedback_category_confusing"), + other: __("feedback_category_other") + }; + }, + + _getCategoryFields: function() { + var categories = this._getCategories(); + return Object.keys(categories).map(function(category, key) { + return ( + <label key={key}> + <input type="radio" ref="category" name="category" + value={category} + onChange={this.handleCategoryChange} /> + {categories[category]} + </label> + ); + }, this); + }, + + /** + * Checks if the form is ready for submission: + * - a category (reason) must be chosen + * - no feedback submission should be pending + * + * @return {Boolean} + */ + _isFormReady: function() { + return this.state.category !== "" && !this.props.pending; + }, + + handleCategoryChange: function(event) { + var category = event.target.value; + if (category !== "other") { + // resets description text field + this.setState({description: ""}); + } + this.setState({category: category}); + }, + + handleCustomTextChange: function(event) { + this.setState({description: event.target.value}); + }, + + handleFormSubmit: function(event) { + event.preventDefault(); + this.props.sendFeedback({ + happy: false, + category: this.state.category, + description: this.state.description + }); + }, + + render: function() { + return ( + <FeedbackLayout title={__("feedback_what_makes_you_sad")} + reset={this.props.reset}> + <form onSubmit={this.handleFormSubmit}> + {this._getCategoryFields()} + <p><input type="text" ref="description" name="description" + disabled={this.state.category !== "other"} + onChange={this.handleCustomTextChange} + value={this.state.description} /></p> + <button type="submit" className="btn btn-success" + disabled={!this._isFormReady()}> + {__("feedback_submit_button")} + </button> + </form> + </FeedbackLayout> + ); + } + }); + + /** + * Feedback received view. + */ + var FeedbackReceived = React.createClass({ + getInitialState: function() { + return {countdown: WINDOW_AUTOCLOSE_TIMEOUT_IN_SECONDS}; + }, + + componentDidMount: function() { + this._timer = setInterval(function() { + this.setState({countdown: this.state.countdown - 1}); + }.bind(this), 1000); + }, + + componentWillUnmount: function() { + if (this._timer) { + clearInterval(this._timer); + } + }, + + render: function() { + if (this.state.countdown < 1) { + clearInterval(this._timer); + window.close(); + } + return ( + <FeedbackLayout title={__("feedback_thank_you_heading")}> + <p className="info thank-you">{__("feedback_window_will_close_in", { + countdown: this.state.countdown + })}</p> + </FeedbackLayout> + ); + } + }); + + /** + * Feedback view. + */ + var FeedbackView = React.createClass({ + propTypes: { + // A loop.FeedbackAPIClient instance + feedbackApiClient: React.PropTypes.object.isRequired, + // The current feedback submission flow step name + step: React.PropTypes.oneOf(["start", "form", "finished"]) + }, + + getInitialState: function() { + return {pending: false, step: this.props.step || "start"}; + }, + + getInitialProps: function() { + return {step: "start"}; + }, + + reset: function() { + this.setState(this.getInitialState()); + }, + + handleHappyClick: function() { + this.sendFeedback({happy: true}, this._onFeedbackSent); + }, + + handleSadClick: function() { + this.setState({step: "form"}); + }, + + sendFeedback: function(fields) { + // Setting state.pending to true will disable the submit button to avoid + // multiple submissions + this.setState({pending: true}); + // Sends feedback data + this.props.feedbackApiClient.send(fields, this._onFeedbackSent); + }, + + _onFeedbackSent: function(err) { + if (err) { + // XXX better end user error reporting, see bug 1046738 + console.error("Unable to send user feedback", err); + } + this.setState({pending: false, step: "finished"}); + }, + + render: function() { + switch(this.state.step) { + case "finished": + return <FeedbackReceived />; + case "form": + return <FeedbackForm feedbackApiClient={this.props.feedbackApiClient} + sendFeedback={this.sendFeedback} + reset={this.reset} + pending={this.state.pending} />; + default: + return ( + <FeedbackLayout title={__("feedback_call_experience_heading")}> + <div className="faces"> + <button className="face face-happy" + onClick={this.handleHappyClick}></button> + <button className="face face-sad" + onClick={this.handleSadClick}></button> + </div> + </FeedbackLayout> + ); + } + } + }); + + /** * Notification view. */ var NotificationView = BaseView.extend({ template: _.template([ '<div class="alert alert-<%- level %>">', ' <button class="close"></button>', ' <p class="message"><%- message %></p>', '</div>' @@ -513,15 +747,16 @@ loop.shared.views = (function(_, OT, l10 ].join("")) }); return { L10nView: L10nView, BaseView: BaseView, ConversationView: ConversationView, ConversationToolbar: ConversationToolbar, + FeedbackView: FeedbackView, MediaControlButton: MediaControlButton, NotificationListView: NotificationListView, NotificationView: NotificationView, UnsupportedBrowserView: UnsupportedBrowserView, UnsupportedDeviceView: UnsupportedDeviceView }; })(_, window.OT, document.webL10n || document.mozL10n);
rename from browser/components/loop/content/shared/libs/react-0.10.0.js rename to browser/components/loop/content/shared/libs/react-0.11.1.js --- a/browser/components/loop/content/shared/libs/react-0.10.0.js +++ b/browser/components/loop/content/shared/libs/react-0.11.1.js @@ -1,12 +1,12 @@ /** - * React (with addons) v0.10.0 - */ -!function(e){if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.React=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){ + * React (with addons) v0.11.1 + */ +!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.React=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,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 @@ -30,17 +30,241 @@ var AutoFocusMixin = { if (this.props.autoFocus) { focusNode(this.getDOMNode()); } } }; module.exports = AutoFocusMixin; -},{"./focusNode":113}],2:[function(_dereq_,module,exports){ +},{"./focusNode":120}],2:[function(_dereq_,module,exports){ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule BeforeInputEventPlugin + * @typechecks static-only + */ + +"use strict"; + +var EventConstants = _dereq_("./EventConstants"); +var EventPropagators = _dereq_("./EventPropagators"); +var ExecutionEnvironment = _dereq_("./ExecutionEnvironment"); +var SyntheticInputEvent = _dereq_("./SyntheticInputEvent"); + +var keyOf = _dereq_("./keyOf"); + +var canUseTextInputEvent = ( + ExecutionEnvironment.canUseDOM && + 'TextEvent' in window && + !('documentMode' in document || isPresto()) +); + +/** + * 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 SPACEBAR_CODE = 32; +var SPACEBAR_CHAR = String.fromCharCode(SPACEBAR_CODE); + +var topLevelTypes = EventConstants.topLevelTypes; + +// 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 + ] + } +}; + +// Track characters inserted via keypress and composition events. +var fallbackChars = null; + +/** + * 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) + ); +} + +/** + * Create an `onBeforeInput` event to match + * http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105/#events-inputevents. + * + * 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`. + * + * `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. + */ +var BeforeInputEventPlugin = { + + eventTypes: eventTypes, + + /** + * @param {string} topLevelType Record from `EventConstants`. + * @param {DOMEventTarget} topLevelTarget The listening component root node. + * @param {string} topLevelTargetID ID of `topLevelTarget`. + * @param {object} nativeEvent Native browser event. + * @return {*} An accumulation of synthetic events. + * @see {EventPluginHub.extractEvents} + */ + extractEvents: function( + topLevelType, + topLevelTarget, + topLevelTargetID, + nativeEvent) { + + var chars; + + if (canUseTextInputEvent) { + switch (topLevelType) { + 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; + } + + chars = String.fromCharCode(which); + break; + + case topLevelTypes.topTextInput: + // Record the characters to be added to the DOM. + chars = nativeEvent.data; + + // If it's a spacebar character, assume that we have already handled + // it at the keypress level and bail immediately. + if (chars === SPACEBAR_CHAR) { + return; + } + + // Otherwise, carry on. + break; + + default: + // For other native event types, do nothing. + return; + } + } else { + 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. + fallbackChars = null; + break; + 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)) { + fallbackChars = String.fromCharCode(nativeEvent.which); + } + break; + case topLevelTypes.topCompositionEnd: + fallbackChars = nativeEvent.data; + break; + } + + // If no changes have occurred to the fallback string, no relevant + // event has fired and we're done. + if (fallbackChars === null) { + return; + } + + chars = fallbackChars; + } + + // If no characters are being inserted, no BeforeInput event should + // be fired. + if (!chars) { + return; + } + + var event = SyntheticInputEvent.getPooled( + eventTypes.beforeInput, + topLevelTargetID, + nativeEvent + ); + + event.data = chars; + fallbackChars = null; + EventPropagators.accumulateTwoPhaseDispatches(event); + return event; + } +}; + +module.exports = BeforeInputEventPlugin; + +},{"./EventConstants":16,"./EventPropagators":21,"./ExecutionEnvironment":22,"./SyntheticInputEvent":98,"./keyOf":141}],3:[function(_dereq_,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 @@ -147,17 +371,17 @@ var CSSCore = { } return (' ' + element.className + ' ').indexOf(' ' + className + ' ') > -1; } }; module.exports = CSSCore; -},{"./invariant":125}],3:[function(_dereq_,module,exports){ +},{"./invariant":134}],4:[function(_dereq_,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 @@ -270,17 +494,17 @@ var shorthandPropertyExpansions = { var CSSProperty = { isUnitlessNumber: isUnitlessNumber, shorthandPropertyExpansions: shorthandPropertyExpansions }; module.exports = CSSProperty; -},{}],4:[function(_dereq_,module,exports){ +},{}],5:[function(_dereq_,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 @@ -295,36 +519,36 @@ module.exports = CSSProperty; * @typechecks static-only */ "use strict"; var CSSProperty = _dereq_("./CSSProperty"); var dangerousStyleValue = _dereq_("./dangerousStyleValue"); -var escapeTextForBrowser = _dereq_("./escapeTextForBrowser"); -var hyphenate = _dereq_("./hyphenate"); +var hyphenateStyleName = _dereq_("./hyphenateStyleName"); var memoizeStringOnly = _dereq_("./memoizeStringOnly"); var processStyleName = memoizeStringOnly(function(styleName) { - return escapeTextForBrowser(hyphenate(styleName)); + return hyphenateStyleName(styleName); }); /** * Operations for dealing with CSS properties. */ var CSSPropertyOperations = { /** * Serializes a mapping of style properties for use as inline styles: * * > createMarkupForStyles({width: '200px', height: 0}) * "width:200px;height:0;" * * Undefined values are ignored so that declarative programming is easier. + * The result should be HTML-escaped before insertion into the DOM. * * @param {object} styles * @return {?string} */ createMarkupForStyles: function(styles) { var serialized = ''; for (var styleName in styles) { if (!styles.hasOwnProperty(styleName)) { @@ -369,17 +593,122 @@ var CSSPropertyOperations = { } } } }; module.exports = CSSPropertyOperations; -},{"./CSSProperty":3,"./dangerousStyleValue":108,"./escapeTextForBrowser":111,"./hyphenate":123,"./memoizeStringOnly":133}],5:[function(_dereq_,module,exports){ +},{"./CSSProperty":4,"./dangerousStyleValue":115,"./hyphenateStyleName":132,"./memoizeStringOnly":143}],6:[function(_dereq_,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 CallbackQueue + */ + +"use strict"; + +var PooledClass = _dereq_("./PooledClass"); + +var invariant = _dereq_("./invariant"); +var mixInto = _dereq_("./mixInto"); + +/** + * A specialized pseudo-event module to help keep track of components waiting to + * be notified when their DOM representations are available for use. + * + * This implements `PooledClass`, so you should never need to instantiate this. + * Instead, use `CallbackQueue.getPooled()`. + * + * @class ReactMountReady + * @implements PooledClass + * @internal + */ +function CallbackQueue() { + this._callbacks = null; + this._contexts = null; +} + +mixInto(CallbackQueue, { + + /** + * 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); + }, + + /** + * 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" !== "development" ? 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; + } + }, + + /** + * Resets the internal queue. + * + * @internal + */ + reset: function() { + this._callbacks = null; + this._contexts = null; + }, + + /** + * `PooledClass` looks for this. + */ + destructor: function() { + this.reset(); + } + +}); + +PooledClass.addPoolingTo(CallbackQueue); + +module.exports = CallbackQueue; + +},{"./PooledClass":28,"./invariant":134,"./mixInto":147}],7:[function(_dereq_,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 @@ -457,17 +786,17 @@ function manualDispatchChangeEvent(nativ var event = SyntheticEvent.getPooled( eventTypes.change, activeElementID, nativeEvent ); EventPropagators.accumulateTwoPhaseDispatches(event); // If change and propertychange bubbled, we'd just bind to it like all the - // other events and have it go through ReactEventTopLevelCallback. Since it + // 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 @@ -758,17 +1087,17 @@ var ChangeEventPlugin = { ); } } }; module.exports = ChangeEventPlugin; -},{"./EventConstants":15,"./EventPluginHub":17,"./EventPropagators":20,"./ExecutionEnvironment":21,"./ReactUpdates":81,"./SyntheticEvent":89,"./isEventSupported":126,"./isTextInputElement":128,"./keyOf":132}],6:[function(_dereq_,module,exports){ +},{"./EventConstants":16,"./EventPluginHub":18,"./EventPropagators":21,"./ExecutionEnvironment":22,"./ReactUpdates":87,"./SyntheticEvent":96,"./isEventSupported":135,"./isTextInputElement":137,"./keyOf":141}],8:[function(_dereq_,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 @@ -790,17 +1119,17 @@ var nextReactRootIndex = 0; var ClientReactRootIndex = { createReactRootIndex: function() { return nextReactRootIndex++; } }; module.exports = ClientReactRootIndex; -},{}],7:[function(_dereq_,module,exports){ +},{}],9:[function(_dereq_,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 @@ -837,17 +1166,21 @@ var useCompositionEvent = ( // 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 + ( + 'documentMode' in document && + document.documentMode > 8 && + document.documentMode <= 11 + ) ); var topLevelTypes = EventConstants.topLevelTypes; var currentComposition = null; // Events and their corresponding property names. var eventTypes = { compositionEnd: { @@ -1052,17 +1385,17 @@ var CompositionEventPlugin = { EventPropagators.accumulateTwoPhaseDispatches(event); return event; } } }; module.exports = CompositionEventPlugin; -},{"./EventConstants":15,"./EventPropagators":20,"./ExecutionEnvironment":21,"./ReactInputSelection":56,"./SyntheticCompositionEvent":87,"./getTextContentAccessor":121,"./keyOf":132}],8:[function(_dereq_,module,exports){ +},{"./EventConstants":16,"./EventPropagators":21,"./ExecutionEnvironment":22,"./ReactInputSelection":63,"./SyntheticCompositionEvent":94,"./getTextContentAccessor":129,"./keyOf":141}],10:[function(_dereq_,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 @@ -1078,16 +1411,17 @@ module.exports = CompositionEventPlugin; */ "use strict"; var Danger = _dereq_("./Danger"); var ReactMultiChildUpdateTypes = _dereq_("./ReactMultiChildUpdateTypes"); var getTextContentAccessor = _dereq_("./getTextContentAccessor"); +var invariant = _dereq_("./invariant"); /** * The DOM property to use when setting text content. * * @type {string} * @private */ var textContentAccessor = getTextContentAccessor(); @@ -1096,30 +1430,24 @@ var textContentAccessor = getTextContent * Inserts `childNode` as a child of `parentNode` at the `index`. * * @param {DOMElement} parentNode Parent node in which to insert. * @param {DOMElement} childNode Child node to insert. * @param {number} index Index at which to insert the child. * @internal */ function insertChildAt(parentNode, childNode, index) { - var childNodes = parentNode.childNodes; - if (childNodes[index] === childNode) { - return; - } - // If `childNode` is already a child of `parentNode`, remove it so that - // computing `childNodes[index]` takes into account the removal. - if (childNode.parentNode === parentNode) { - parentNode.removeChild(childNode); - } - if (index >= childNodes.length) { - parentNode.appendChild(childNode); - } else { - parentNode.insertBefore(childNode, childNodes[index]); - } + // 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 + ); } var updateTextContent; if (textContentAccessor === 'textContent') { /** * Sets the text content of `node` to `text`. * * @param {DOMElement} node Node to change @@ -1174,16 +1502,28 @@ var DOMChildrenOperations = { for (var i = 0; update = updates[i]; i++) { if (update.type === ReactMultiChildUpdateTypes.MOVE_EXISTING || update.type === ReactMultiChildUpdateTypes.REMOVE_NODE) { var updatedIndex = update.fromIndex; var updatedChild = update.parentNode.childNodes[updatedIndex]; var parentID = update.parentID; + ("production" !== "development" ? 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 <tbody> when using tables, ' + + 'nesting <p> or <a> tags, or using non-SVG elements in an <svg> '+ + '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; updatedChildren = updatedChildren || []; updatedChildren.push(updatedChild); } } @@ -1225,17 +1565,17 @@ var DOMChildrenOperations = { } } } }; module.exports = DOMChildrenOperations; -},{"./Danger":11,"./ReactMultiChildUpdateTypes":63,"./getTextContentAccessor":121}],9:[function(_dereq_,module,exports){ +},{"./Danger":13,"./ReactMultiChildUpdateTypes":69,"./getTextContentAccessor":129,"./invariant":134}],11:[function(_dereq_,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 @@ -1260,17 +1600,19 @@ var DOMPropertyInjection = { /** * Mapping from normalized, camelcased property names to a configuration that * specifies how the associated DOM property should be accessed or rendered. */ MUST_USE_ATTRIBUTE: 0x1, MUST_USE_PROPERTY: 0x2, HAS_SIDE_EFFECTS: 0x4, HAS_BOOLEAN_VALUE: 0x8, - HAS_POSITIVE_NUMERIC_VALUE: 0x10, + 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 * with the following properties: * * isCustomAttribute: function that given an attribute name will return true * if it can be inserted into the DOM verbatim. Useful for data-* or aria-* * attributes where it's impossible to enumerate all of the possible @@ -1301,77 +1643,88 @@ var DOMPropertyInjection = { if (domPropertyConfig.isCustomAttribute) { DOMProperty._isCustomAttributeFunctions.push( domPropertyConfig.isCustomAttribute ); } for (var propName in Properties) { ("production" !== "development" ? 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 || lowerCased; + DOMProperty.getAttributeName[propName] = attributeName; + } else { + DOMProperty.getAttributeName[propName] = lowerCased; + } DOMProperty.getPropertyName[propName] = - DOMPropertyNames[propName] || propName; - - var mutationMethod = DOMMutationMethods[propName]; - if (mutationMethod) { - DOMProperty.getMutationMethod[propName] = mutationMethod; + DOMPropertyNames.hasOwnProperty(propName) ? + DOMPropertyNames[propName] : + propName; + + 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; DOMProperty.mustUseProperty[propName] = propConfig & DOMPropertyInjection.MUST_USE_PROPERTY; DOMProperty.hasSideEffects[propName] = propConfig & DOMPropertyInjection.HAS_SIDE_EFFECTS; DOMProperty.hasBooleanValue[propName] = propConfig & DOMPropertyInjection.HAS_BOOLEAN_VALUE; + DOMProperty.hasNumericValue[propName] = + propConfig & DOMPropertyInjection.HAS_NUMERIC_VALUE; DOMProperty.hasPositiveNumericValue[propName] = propConfig & DOMPropertyInjection.HAS_POSITIVE_NUMERIC_VALUE; + DOMProperty.hasOverloadedBooleanValue[propName] = + propConfig & DOMPropertyInjection.HAS_OVERLOADED_BOOLEAN_VALUE; ("production" !== "development" ? invariant( !DOMProperty.mustUseAttribute[propName] || !DOMProperty.mustUseProperty[propName], 'DOMProperty: Cannot require using both attribute and property: %s', propName ) : invariant(!DOMProperty.mustUseAttribute[propName] || !DOMProperty.mustUseProperty[propName])); ("production" !== "development" ? invariant( DOMProperty.mustUseProperty[propName] || !DOMProperty.hasSideEffects[propName], 'DOMProperty: Properties that have side effects must use property: %s', propName ) : invariant(DOMProperty.mustUseProperty[propName] || !DOMProperty.hasSideEffects[propName])); ("production" !== "development" ? invariant( - !DOMProperty.hasBooleanValue[propName] || - !DOMProperty.hasPositiveNumericValue[propName], - 'DOMProperty: Cannot have both boolean and positive numeric value: %s', + !!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)); } } }; var defaultValueCache = {}; /** * DOMProperty exports lookup objects that can be used like functions: * @@ -1446,23 +1799,38 @@ var DOMProperty = { /** * Whether the property should be removed when set to a falsey value. * @type {Object} */ 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. * @type {Object} */ 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. */ _isCustomAttributeFunctions: [], /** * Checks whether a property name is a custom attribute. * @method */ @@ -1497,17 +1865,17 @@ var DOMProperty = { return nodeDefaults[prop]; }, injection: DOMPropertyInjection }; module.exports = DOMProperty; -},{"./invariant":125}],10:[function(_dereq_,module,exports){ +},{"./invariant":134}],12:[function(_dereq_,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 @@ -1527,44 +1895,52 @@ module.exports = DOMProperty; var DOMProperty = _dereq_("./DOMProperty"); var escapeTextForBrowser = _dereq_("./escapeTextForBrowser"); var memoizeStringOnly = _dereq_("./memoizeStringOnly"); var warning = _dereq_("./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" !== "development") { var reactProps = { children: true, dangerouslySetInnerHTML: true, key: true, ref: true }; var warnedProperties = {}; var warnUnknownProperty = function(name) { - if (reactProps[name] || warnedProperties[name]) { + if (reactProps.hasOwnProperty(name) && reactProps[name] || + warnedProperties.hasOwnProperty(name) && warnedProperties[name]) { return; } warnedProperties[name] = true; var lowerCasedName = name.toLowerCase(); // data-* attributes should be lowercase; suggest the lowercase version - var standardName = DOMProperty.isCustomAttribute(lowerCasedName) ? - lowerCasedName : DOMProperty.getPossibleStandardName[lowerCasedName]; + 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. ("production" !== "development" ? warning( standardName == null, 'Unknown DOM property ' + name + '. Did you mean ' + standardName + '?' ) : null); @@ -1590,22 +1966,24 @@ var DOMPropertyOperations = { /** * Creates markup for a property. * * @param {string} name * @param {*} value * @return {?string} Markup string, or null if the property was invalid. */ createMarkupForProperty: function(name, value) { - if (DOMProperty.isStandardName[name]) { + if (DOMProperty.isStandardName.hasOwnProperty(name) && + DOMProperty.isStandardName[name]) { if (shouldIgnoreValue(name, value)) { return ''; } var attributeName = DOMProperty.getAttributeName[name]; - if (DOMProperty.hasBooleanValue[name]) { + if (DOMProperty.hasBooleanValue[name] || + (DOMProperty.hasOverloadedBooleanValue[name] && value === true)) { return escapeTextForBrowser(attributeName); } return processAttributeNameAndPrefix(attributeName) + escapeTextForBrowser(value) + '"'; } else if (DOMProperty.isCustomAttribute(name)) { if (value == null) { return ''; } @@ -1620,49 +1998,51 @@ var DOMPropertyOperations = { /** * Sets the value for a property on a node. * * @param {DOMElement} node * @param {string} name * @param {*} value */ setValueForProperty: function(node, name, value) { - if (DOMProperty.isStandardName[name]) { + 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]) { node.setAttribute(DOMProperty.getAttributeName[name], '' + value); } else { var propName = DOMProperty.getPropertyName[name]; if (!DOMProperty.hasSideEffects[name] || node[propName] !== value) { node[propName] = value; } } } else if (DOMProperty.isCustomAttribute(name)) { if (value == null) { - node.removeAttribute(DOMProperty.getAttributeName[name]); + node.removeAttribute(name); } else { node.setAttribute(name, '' + value); } } else if ("production" !== "development") { warnUnknownProperty(name); } }, /** * Deletes the value for a property on a node. * * @param {DOMElement} node * @param {string} name */ deleteValueForProperty: function(node, name) { - if (DOMProperty.isStandardName[name]) { + if (DOMProperty.isStandardName.hasOwnProperty(name) && + DOMProperty.isStandardName[name]) { var mutationMethod = DOMProperty.getMutationMethod[name]; if (mutationMethod) { mutationMethod(node, undefined); } else if (DOMProperty.mustUseAttribute[name]) { node.removeAttribute(DOMProperty.getAttributeName[name]); } else { var propName = DOMProperty.getPropertyName[name]; var defaultValue = DOMProperty.getDefaultValueForProperty( @@ -1680,17 +2060,17 @@ var DOMPropertyOperations = { warnUnknownProperty(name); } } }; module.exports = DOMPropertyOperations; -},{"./DOMProperty":9,"./escapeTextForBrowser":111,"./memoizeStringOnly":133,"./warning":148}],11:[function(_dereq_,module,exports){ +},{"./DOMProperty":11,"./escapeTextForBrowser":118,"./memoizeStringOnly":143,"./warning":158}],13:[function(_dereq_,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 @@ -1869,215 +2249,17 @@ var Danger = { var newChild = createNodesFromMarkup(markup, emptyFunction)[0]; oldChild.parentNode.replaceChild(newChild, oldChild); } }; module.exports = Danger; -},{"./ExecutionEnvironment":21,"./createNodesFromMarkup":105,"./emptyFunction":109,"./getMarkupWrap":118,"./invariant":125}],12:[function(_dereq_,module,exports){ -/** - * Copyright 2013-2014 Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @providesModule DefaultDOMPropertyConfig - */ - -/*jslint bitwise: true*/ - -"use strict"; - -var DOMProperty = _dereq_("./DOMProperty"); - -var MUST_USE_ATTRIBUTE = DOMProperty.injection.MUST_USE_ATTRIBUTE; -var MUST_USE_PROPERTY = DOMProperty.injection.MUST_USE_PROPERTY; -var HAS_BOOLEAN_VALUE = DOMProperty.injection.HAS_BOOLEAN_VALUE; -var HAS_SIDE_EFFECTS = DOMProperty.injection.HAS_SIDE_EFFECTS; -var HAS_POSITIVE_NUMERIC_VALUE = - DOMProperty.injection.HAS_POSITIVE_NUMERIC_VALUE; - -var DefaultDOMPropertyConfig = { - isCustomAttribute: RegExp.prototype.test.bind( - /^(data|aria)-[a-z_][a-z\d_.\-]*$/ - ), - Properties: { - /** - * Standard Properties - */ - accept: null, - accessKey: null, - action: null, - allowFullScreen: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE, - allowTransparency: MUST_USE_ATTRIBUTE, - alt: null, - async: HAS_BOOLEAN_VALUE, - autoComplete: null, - // autoFocus is polyfilled/normalized by AutoFocusMixin - // autoFocus: HAS_BOOLEAN_VALUE, - autoPlay: HAS_BOOLEAN_VALUE, - cellPadding: null, - cellSpacing: null, - charSet: MUST_USE_ATTRIBUTE, - checked: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, - className: MUST_USE_PROPERTY, - cols: MUST_USE_ATTRIBUTE | HAS_POSITIVE_NUMERIC_VALUE, - colSpan: null, - content: null, - contentEditable: null, - contextMenu: MUST_USE_ATTRIBUTE, - controls: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, - crossOrigin: null, - data: null, // For `<object />` acts as `src`. - dateTime: MUST_USE_ATTRIBUTE, - defer: HAS_BOOLEAN_VALUE, - dir: null, - disabled: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE, - download: null, - draggable: null, - encType: null, - form: MUST_USE_ATTRIBUTE, - formNoValidate: HAS_BOOLEAN_VALUE, - frameBorder: MUST_USE_ATTRIBUTE, - height: MUST_USE_ATTRIBUTE, - hidden: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE, - href: null, - hrefLang: null, - htmlFor: null, - httpEquiv: null, - icon: null, - id: MUST_USE_PROPERTY, - label: null, - lang: null, - list: null, - loop: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, - max: null, - maxLength: MUST_USE_ATTRIBUTE, - mediaGroup: null, - method: null, - min: null, - multiple: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, - muted: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, - name: null, - noValidate: HAS_BOOLEAN_VALUE, - pattern: null, - placeholder: null, - poster: null, - preload: null, - radioGroup: null, - readOnly: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, - rel: null, - required: HAS_BOOLEAN_VALUE, - role: MUST_USE_ATTRIBUTE, - rows: MUST_USE_ATTRIBUTE | HAS_POSITIVE_NUMERIC_VALUE, - rowSpan: null, - sandbox: null, - scope: null, - scrollLeft: MUST_USE_PROPERTY, - scrollTop: MUST_USE_PROPERTY, - seamless: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE, - selected: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, - size: MUST_USE_ATTRIBUTE | HAS_POSITIVE_NUMERIC_VALUE, - span: HAS_POSITIVE_NUMERIC_VALUE, - spellCheck: null, - src: null, - srcDoc: MUST_USE_PROPERTY, - srcSet: null, - step: null, - style: null, - tabIndex: null, - target: null, - title: null, - type: null, - value: MUST_USE_PROPERTY | HAS_SIDE_EFFECTS, - width: MUST_USE_ATTRIBUTE, - wmode: MUST_USE_ATTRIBUTE, - - /** - * Non-standard Properties - */ - autoCapitalize: null, // Supported in Mobile Safari for keyboard hints - autoCorrect: null, // Supported in Mobile Safari for keyboard hints - property: null, // Supports OG in meta tags - - /** - * SVG Properties - */ - cx: MUST_USE_ATTRIBUTE, - cy: MUST_USE_ATTRIBUTE, - d: MUST_USE_ATTRIBUTE, - fill: MUST_USE_ATTRIBUTE, - fx: MUST_USE_ATTRIBUTE, - fy: MUST_USE_ATTRIBUTE, - gradientTransform: MUST_USE_ATTRIBUTE, - gradientUnits: MUST_USE_ATTRIBUTE, - offset: MUST_USE_ATTRIBUTE, - points: MUST_USE_ATTRIBUTE, - r: MUST_USE_ATTRIBUTE, - rx: MUST_USE_ATTRIBUTE, - ry: MUST_USE_ATTRIBUTE, - spreadMethod: MUST_USE_ATTRIBUTE, - stopColor: MUST_USE_ATTRIBUTE, - stopOpacity: MUST_USE_ATTRIBUTE, - stroke: MUST_USE_ATTRIBUTE, - strokeLinecap: MUST_USE_ATTRIBUTE, - strokeWidth: MUST_USE_ATTRIBUTE, - textAnchor: MUST_USE_ATTRIBUTE, - transform: MUST_USE_ATTRIBUTE, - version: MUST_USE_ATTRIBUTE, - viewBox: MUST_USE_ATTRIBUTE, - x1: MUST_USE_ATTRIBUTE, - x2: MUST_USE_ATTRIBUTE, - x: MUST_USE_ATTRIBUTE, - y1: MUST_USE_ATTRIBUTE, - y2: MUST_USE_ATTRIBUTE, - y: MUST_USE_ATTRIBUTE - }, - DOMAttributeNames: { - className: 'class', - gradientTransform: 'gradientTransform', - gradientUnits: 'gradientUnits', - htmlFor: 'for', - spreadMethod: 'spreadMethod', - stopColor: 'stop-color', - stopOpacity: 'stop-opacity', - strokeLinecap: 'stroke-linecap', - strokeWidth: 'stroke-width', - textAnchor: 'text-anchor', - viewBox: 'viewBox' - }, - DOMPropertyNames: { - autoCapitalize: 'autocapitalize', - autoComplete: 'autocomplete', - autoCorrect: 'autocorrect', - autoFocus: 'autofocus', - autoPlay: 'autoplay', - encType: 'enctype', - hrefLang: 'hreflang', - radioGroup: 'radiogroup', - spellCheck: 'spellcheck', - srcDoc: 'srcdoc', - srcSet: 'srcset' - } -}; - -module.exports = DefaultDOMPropertyConfig; - -},{"./DOMProperty":9}],13:[function(_dereq_,module,exports){ +},{"./ExecutionEnvironment":22,"./createNodesFromMarkup":113,"./emptyFunction":116,"./getMarkupWrap":126,"./invariant":134}],14:[function(_dereq_,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 @@ -2107,23 +2289,24 @@ module.exports = DefaultDOMPropertyConfi var DefaultEventPluginOrder = [ keyOf({ResponderEventPlugin: null}), keyOf({SimpleEventPlugin: null}), keyOf({TapEventPlugin: null}), keyOf({EnterLeaveEventPlugin: null}), keyOf({ChangeEventPlugin: null}), keyOf({SelectEventPlugin: null}), keyOf({CompositionEventPlugin: null}), + keyOf({BeforeInputEventPlugin: null}), keyOf({AnalyticsEventPlugin: null}), keyOf({MobileSafariClickEventPlugin: null}) ]; module.exports = DefaultEventPluginOrder; -},{"./keyOf":132}],14:[function(_dereq_,module,exports){ +},{"./keyOf":141}],15:[function(_dereq_,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 @@ -2260,17 +2443,17 @@ var EnterLeaveEventPlugin = { return extractedEvents; } }; module.exports = EnterLeaveEventPlugin; -},{"./EventConstants":15,"./EventPropagators":20,"./ReactMount":60,"./SyntheticMouseEvent":92,"./keyOf":132}],15:[function(_dereq_,module,exports){ +},{"./EventConstants":16,"./EventPropagators":21,"./ReactMount":67,"./SyntheticMouseEvent":100,"./keyOf":141}],16:[function(_dereq_,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 @@ -2324,33 +2507,35 @@ var topLevelTypes = keyMirror({ topMouseOut: null, topMouseOver: null, topMouseUp: null, topPaste: null, topReset: null, topScroll: null, topSelectionChange: null, topSubmit: null, + topTextInput: null, topTouchCancel: null, topTouchEnd: null, topTouchMove: null, topTouchStart: null, topWheel: null }); var EventConstants = { topLevelTypes: topLevelTypes, PropagationPhases: PropagationPhases }; module.exports = EventConstants; -},{"./keyMirror":131}],16:[function(_dereq_,module,exports){ +},{"./keyMirror":140}],17:[function(_dereq_,module,exports){ /** * @providesModule EventListener + * @typechecks */ var emptyFunction = _dereq_("./emptyFunction"); /** * Upstream version of event listener. Does not take into account specific * nature of platform. */ @@ -2370,17 +2555,17 @@ var EventListener = { remove: function() { target.removeEventListener(eventType, callback, false); } }; } else if (target.attachEvent) { target.attachEvent('on' + eventType, callback); return { remove: function() { - target.detachEvent(eventType, callback); + target.detachEvent('on' + eventType, callback); } }; } }, /** * Listen to DOM events during the capture phase. * @@ -2404,22 +2589,24 @@ var EventListener = { } else { target.addEventListener(eventType, callback, true); return { remove: function() { target.removeEventListener(eventType, callback, true); } }; } - } + }, + + registerDefault: function() {} }; module.exports = EventListener; -},{"./emptyFunction":109}],17:[function(_dereq_,module,exports){ +},{"./emptyFunction":116}],18:[function(_dereq_,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 @@ -2432,17 +2619,16 @@ module.exports = EventListener; * * @providesModule EventPluginHub */ "use strict"; var EventPluginRegistry = _dereq_("./EventPluginRegistry"); var EventPluginUtils = _dereq_("./EventPluginUtils"); -var ExecutionEnvironment = _dereq_("./ExecutionEnvironment"); var accumulate = _dereq_("./accumulate"); var forEachAccumulated = _dereq_("./forEachAccumulated"); var invariant = _dereq_("./invariant"); var isEventSupported = _dereq_("./isEventSupported"); var monitorCodeUse = _dereq_("./monitorCodeUse"); /** @@ -2567,20 +2753,16 @@ var EventPluginHub = { * Stores `listener` at `listenerBank[registrationName][id]`. Is idempotent. * * @param {string} id ID of the DOM element. * @param {string} registrationName Name of listener (e.g. `onClick`). * @param {?function} listener The callback to store. */ putListener: function(id, registrationName, listener) { ("production" !== "development" ? invariant( - ExecutionEnvironment.canUseDOM, - 'Cannot call putListener() in a non-DOM environment.' - ) : invariant(ExecutionEnvironment.canUseDOM)); - ("production" !== "development" ? invariant( !listener || typeof listener === 'function', 'Expected %s listener to be a function, instead got type %s', registrationName, typeof listener ) : invariant(!listener || typeof listener === 'function')); if ("production" !== "development") { // IE8 has no API for event capturing and the `onScroll` event doesn't // bubble. @@ -2706,17 +2888,17 @@ var EventPluginHub = { __getListenerBank: function() { return listenerBank; } }; module.exports = EventPluginHub; -},{"./EventPluginRegistry":18,"./EventPluginUtils":19,"./ExecutionEnvironment":21,"./accumulate":98,"./forEachAccumulated":114,"./invariant":125,"./isEventSupported":126,"./monitorCodeUse":138}],18:[function(_dereq_,module,exports){ +},{"./EventPluginRegistry":19,"./EventPluginUtils":20,"./accumulate":106,"./forEachAccumulated":121,"./invariant":134,"./isEventSupported":135,"./monitorCodeUse":148}],19:[function(_dereq_,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 @@ -2799,21 +2981,21 @@ function recomputePluginOrdering() { * * @param {object} dispatchConfig Dispatch configuration for the event. * @param {object} PluginModule Plugin publishing the event. * @return {boolean} True if the event was successfully published. * @private */ function publishEventForPlugin(dispatchConfig, PluginModule, eventName) { ("production" !== "development" ? invariant( - !EventPluginRegistry.eventNameDispatchConfigs[eventName], + !EventPluginRegistry.eventNameDispatchConfigs.hasOwnProperty(eventName), 'EventPluginHub: More than one plugin attempted to publish the same ' + 'event name, `%s`.', eventName - ) : invariant(!EventPluginRegistry.eventNameDispatchConfigs[eventName])); + ) : invariant(!EventPluginRegistry.eventNameDispatchConfigs.hasOwnProperty(eventName))); EventPluginRegistry.eventNameDispatchConfigs[eventName] = dispatchConfig; var phasedRegistrationNames = dispatchConfig.phasedRegistrationNames; if (phasedRegistrationNames) { for (var phaseName in phasedRegistrationNames) { if (phasedRegistrationNames.hasOwnProperty(phaseName)) { var phasedRegistrationName = phasedRegistrationNames[phaseName]; publishRegistrationName( @@ -2889,17 +3071,18 @@ var EventPluginRegistry = { * * @param {array} InjectedEventPluginOrder * @internal * @see {EventPluginHub.injection.injectEventPluginOrder} */ injectEventPluginOrder: function(InjectedEventPluginOrder) { ("production" !== "development" ? invariant( !EventPluginOrder, - 'EventPluginRegistry: Cannot inject event plugin ordering more than once.' + 'EventPluginRegistry: Cannot inject event plugin ordering more than ' + + 'once. You are likely trying to load more than one copy of React.' ) : invariant(!EventPluginOrder)); // Clone the ordering so it cannot be dynamically mutated. EventPluginOrder = Array.prototype.slice.call(InjectedEventPluginOrder); recomputePluginOrdering(); }, /** * Injects plugins to be used by `EventPluginHub`. The plugin names must be @@ -2913,17 +3096,18 @@ var EventPluginRegistry = { */ injectEventPluginsByName: function(injectedNamesToPlugins) { var isOrderingDirty = false; for (var pluginName in injectedNamesToPlugins) { if (!injectedNamesToPlugins.hasOwnProperty(pluginName)) { continue; } var PluginModule = injectedNamesToPlugins[pluginName]; - if (namesToPlugins[pluginName] !== PluginModule) { + if (!namesToPlugins.hasOwnProperty(pluginName) || + namesToPlugins[pluginName] !== PluginModule) { ("production" !== "development" ? invariant( !namesToPlugins[pluginName], 'EventPluginRegistry: Cannot inject two different event plugins ' + 'using the same name, `%s`.', pluginName ) : invariant(!namesToPlugins[pluginName])); namesToPlugins[pluginName] = PluginModule; isOrderingDirty = true; @@ -2989,17 +3173,17 @@ var EventPluginRegistry = { } } } }; module.exports = EventPluginRegistry; -},{"./invariant":125}],19:[function(_dereq_,module,exports){ +},{"./invariant":134}],20:[function(_dereq_,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 @@ -3127,17 +3311,17 @@ function executeDispatchesInOrder(event, /** * Standard/simple iteration through an event's collected dispatches, but stops * at the first dispatch execution returning true, and returns that id. * * @return id of the first dispatch execution who's listener returns true, or * null if no listener returned true. */ -function executeDispatchesInOrderStopAtTrue(event) { +function executeDispatchesInOrderStopAtTrueImpl(event) { var dispatchListeners = event._dispatchListeners; var dispatchIDs = event._dispatchIDs; if ("production" !== "development") { validateEventDispatches(event); } if (Array.isArray(dispatchListeners)) { for (var i = 0; i < dispatchListeners.length; i++) { if (event.isPropagationStopped()) { @@ -3152,16 +3336,26 @@ function executeDispatchesInOrderStopAtT if (dispatchListeners(event, dispatchIDs)) { return dispatchIDs; } } return null; } /** + * @see executeDispatchesInOrderStopAtTrueImpl + */ +function executeDispatchesInOrderStopAtTrue(event) { + var ret = executeDispatchesInOrderStopAtTrueImpl(event); + event._dispatchIDs = null; + event._dispatchListeners = null; + return ret; +} + +/** * Execution of a "direct" dispatch - there must be at most one dispatch * accumulated on the event or it is considered an error. It doesn't really make * sense for an event with multiple dispatches (bubbled) to keep track of the * return values at each dispatch execution, but it does tend to make sense when * dealing with "direct" dispatches. * * @return The return value of executing the single dispatch. */ @@ -3205,17 +3399,17 @@ var EventPluginUtils = { executeDispatchesInOrderStopAtTrue: executeDispatchesInOrderStopAtTrue, hasDispatches: hasDispatches, injection: injection, useTouchEvents: false }; module.exports = EventPluginUtils; -},{"./EventConstants":15,"./invariant":125}],20:[function(_dereq_,module,exports){ +},{"./EventConstants":16,"./invariant":134}],21:[function(_dereq_,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 @@ -3350,17 +3544,17 @@ function accumulateDirectDispatches(even var EventPropagators = { accumulateTwoPhaseDispatches: accumulateTwoPhaseDispatches, accumulateDirectDispatches: accumulateDirectDispatches, accumulateEnterLeaveDispatches: accumulateEnterLeaveDispatches }; module.exports = EventPropagators; -},{"./EventConstants":15,"./EventPluginHub":17,"./accumulate":98,"./forEachAccumulated":114}],21:[function(_dereq_,module,exports){ +},{"./EventConstants":16,"./EventPluginHub":18,"./accumulate":106,"./forEachAccumulated":121}],22:[function(_dereq_,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 @@ -3373,40 +3567,234 @@ module.exports = EventPropagators; * * @providesModule ExecutionEnvironment */ /*jslint evil: true */ "use strict"; -var canUseDOM = typeof window !== 'undefined'; +var canUseDOM = !!( + typeof window !== 'undefined' && + window.document && + window.document.createElement +); /** * Simple, lightweight module assisting with the detection and context of * Worker. Helps avoid circular dependencies and allows code to reason about * whether or not they are in a Worker, even if they never include the main * `ReactWorker` dependency. */ var ExecutionEnvironment = { canUseDOM: canUseDOM, canUseWorkers: typeof Worker !== 'undefined', canUseEventListeners: - canUseDOM && (window.addEventListener || window.attachEvent), + canUseDOM && !!(window.addEventListener || window.attachEvent), + + canUseViewport: canUseDOM && !!window.screen, isInWorker: !canUseDOM // For now, this is true - might change in the future. }; module.exports = ExecutionEnvironment; -},{}],22:[function(_dereq_,module,exports){ +},{}],23:[function(_dereq_,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 HTMLDOMPropertyConfig + */ + +/*jslint bitwise: true*/ + +"use strict"; + +var DOMProperty = _dereq_("./DOMProperty"); +var ExecutionEnvironment = _dereq_("./ExecutionEnvironment"); + +var MUST_USE_ATTRIBUTE = DOMProperty.injection.MUST_USE_ATTRIBUTE; +var MUST_USE_PROPERTY = DOMProperty.injection.MUST_USE_PROPERTY; +var HAS_BOOLEAN_VALUE = DOMProperty.injection.HAS_BOOLEAN_VALUE; +var HAS_SIDE_EFFECTS = DOMProperty.injection.HAS_SIDE_EFFECTS; +var HAS_NUMERIC_VALUE = DOMProperty.injection.HAS_NUMERIC_VALUE; +var HAS_POSITIVE_NUMERIC_VALUE = + DOMProperty.injection.HAS_POSITIVE_NUMERIC_VALUE; +var HAS_OVERLOADED_BOOLEAN_VALUE = + DOMProperty.injection.HAS_OVERLOADED_BOOLEAN_VALUE; + +var hasSVG; +if (ExecutionEnvironment.canUseDOM) { + var implementation = document.implementation; + hasSVG = ( + implementation && + implementation.hasFeature && + implementation.hasFeature( + 'http://www.w3.org/TR/SVG11/feature#BasicStructure', + '1.1' + ) + ); +} + + +var HTMLDOMPropertyConfig = { + isCustomAttribute: RegExp.prototype.test.bind( + /^(data|aria)-[a-z_][a-z\d_.\-]*$/ + ), + Properties: { + /** + * Standard Properties + */ + accept: null, + accessKey: null, + action: null, + allowFullScreen: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE, + allowTransparency: MUST_USE_ATTRIBUTE, + alt: null, + async: HAS_BOOLEAN_VALUE, + autoComplete: null, + // autoFocus is polyfilled/normalized by AutoFocusMixin + // autoFocus: HAS_BOOLEAN_VALUE, + autoPlay: HAS_BOOLEAN_VALUE, + cellPadding: null, + cellSpacing: null, + charSet: MUST_USE_ATTRIBUTE, + checked: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, + // To set className on SVG elements, it's necessary to use .setAttribute; + // this works on HTML elements too in all browsers except IE8. Conveniently, + // IE8 doesn't support SVG and so we can simply use the attribute in + // browsers that support SVG and the property in browsers that don't, + // regardless of whether the element is HTML or SVG. + className: hasSVG ? MUST_USE_ATTRIBUTE : MUST_USE_PROPERTY, + cols: MUST_USE_ATTRIBUTE | HAS_POSITIVE_NUMERIC_VALUE, + colSpan: null, + content: null, + contentEditable: null, + contextMenu: MUST_USE_ATTRIBUTE, + controls: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, + coords: null, + crossOrigin: null, + data: null, // For `<object />` acts as `src`. + dateTime: MUST_USE_ATTRIBUTE, + defer: HAS_BOOLEAN_VALUE, + dir: null, + disabled: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE, + download: HAS_OVERLOADED_BOOLEAN_VALUE, + draggable: null, + encType: null, + form: MUST_USE_ATTRIBUTE, + formNoValidate: HAS_BOOLEAN_VALUE, + frameBorder: MUST_USE_ATTRIBUTE, + height: MUST_USE_ATTRIBUTE, + hidden: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE, + href: null, + hrefLang: null, + htmlFor: null, + httpEquiv: null, + icon: null, + id: MUST_USE_PROPERTY, + label: null, + lang: null, + list: null, + loop: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, + max: null, + maxLength: MUST_USE_ATTRIBUTE, + mediaGroup: null, + method: null, + min: null, + multiple: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, + muted: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, + name: null, + noValidate: HAS_BOOLEAN_VALUE, + pattern: null, + placeholder: null, + poster: null, + preload: null, + radioGroup: null, + readOnly: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, + rel: null, + required: HAS_BOOLEAN_VALUE, + role: MUST_USE_ATTRIBUTE, + rows: MUST_USE_ATTRIBUTE | HAS_POSITIVE_NUMERIC_VALUE, + rowSpan: null, + sandbox: null, + scope: null, + scrollLeft: MUST_USE_PROPERTY, + scrolling: null, + scrollTop: MUST_USE_PROPERTY, + seamless: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE, + selected: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, + shape: null, + size: MUST_USE_ATTRIBUTE | HAS_POSITIVE_NUMERIC_VALUE, + span: HAS_POSITIVE_NUMERIC_VALUE, + spellCheck: null, + src: null, + srcDoc: MUST_USE_PROPERTY, + srcSet: null, + start: HAS_NUMERIC_VALUE, + step: null, + style: null, + tabIndex: null, + target: null, + title: null, + type: null, + useMap: null, + value: MUST_USE_PROPERTY | HAS_SIDE_EFFECTS, + width: MUST_USE_ATTRIBUTE, + wmode: MUST_USE_ATTRIBUTE, + + /** + * Non-standard Properties + */ + autoCapitalize: null, // Supported in Mobile Safari for keyboard hints + autoCorrect: null, // Supported in Mobile Safari for keyboard hints + itemProp: MUST_USE_ATTRIBUTE, // Microdata: http://schema.org/docs/gs.html + itemScope: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE, // Microdata: http://schema.org/docs/gs.html + itemType: MUST_USE_ATTRIBUTE, // Microdata: http://schema.org/docs/gs.html + property: null // Supports OG in meta tags + }, + DOMAttributeNames: { + className: 'class', + htmlFor: 'for', + httpEquiv: 'http-equiv' + }, + DOMPropertyNames: { + autoCapitalize: 'autocapitalize', + autoComplete: 'autocomplete', + autoCorrect: 'autocorrect', + autoFocus: 'autofocus', + autoPlay: 'autoplay', + encType: 'enctype', + hrefLang: 'hreflang', + radioGroup: 'radiogroup', + spellCheck: 'spellcheck', + srcDoc: 'srcdoc', + srcSet: 'srcset' + } +}; + +module.exports = HTMLDOMPropertyConfig; + +},{"./DOMProperty":11,"./ExecutionEnvironment":22}],24:[function(_dereq_,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 @@ -3444,17 +3832,17 @@ var LinkedStateMixin = { this.state[key], ReactStateSetters.createStateKeySetter(this, key) ); } }; module.exports = LinkedStateMixin; -},{"./ReactLink":58,"./ReactStateSetters":75}],23:[function(_dereq_,module,exports){ +},{"./ReactLink":65,"./ReactStateSetters":81}],25:[function(_dereq_,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 @@ -3469,33 +3857,32 @@ module.exports = LinkedStateMixin; * @typechecks static-only */ "use strict"; var ReactPropTypes = _dereq_("./ReactPropTypes"); var invariant = _dereq_("./invariant"); -var warning = _dereq_("./warning"); var hasReadOnlyValue = { 'button': true, 'checkbox': true, 'image': true, 'hidden': true, 'radio': true, 'reset': true, 'submit': true }; function _assertSingleLink(input) { ("production" !== "development" ? invariant( - input.props.checkedLink == null || input.props.valueLink == null, - 'Cannot provide a checkedLink and a valueLink. If you want to use ' + - 'checkedLink, you probably don\'t want to use valueLink and vice versa.' + input.props.checkedLink == null || input.props.valueLink == null, + 'Cannot provide a checkedLink and a valueLink. If you want to use ' + + 'checkedLink, you probably don\'t want to use valueLink and vice versa.' ) : invariant(input.props.checkedLink == null || input.props.valueLink == null)); } function _assertValueLink(input) { _assertSingleLink(input); ("production" !== "development" ? invariant( input.props.value == null && input.props.onChange == null, 'Cannot provide a valueLink and a value or onChange event. If you want ' + 'to use value or onChange, you probably don\'t want to use valueLink.' @@ -3531,43 +3918,43 @@ function _handleLinkedCheckChange(e) { /** * Provide a linked `value` attribute for controlled forms. You should not use * this outside of the ReactDOM controlled form components. */ var LinkedValueUtils = { Mixin: { propTypes: { value: function(props, propName, componentName) { - if ("production" !== "development") { - ("production" !== "development" ? warning( - !props[propName] || + if (!props[propName] || hasReadOnlyValue[props.type] || props.onChange || props.readOnly || - props.disabled, - 'You provided a `value` prop to a form field without an ' + - '`onChange` handler. This will render a read-only field. If ' + - 'the field should be mutable use `defaultValue`. Otherwise, ' + - 'set either `onChange` or `readOnly`.' - ) : null); - } + props.disabled) { + return; + } + return new Error( + 'You provided a `value` prop to a form field without an ' + + '`onChange` handler. This will render a read-only field. If ' + + 'the field should be mutable use `defaultValue`. Otherwise, ' + + 'set either `onChange` or `readOnly`.' + ); }, checked: function(props, propName, componentName) { - if ("production" !== "development") { - ("production" !== "development" ? warning( - !props[propName] || + if (!props[propName] || props.onChange || props.readOnly || - props.disabled, - 'You provided a `checked` prop to a form field without an ' + - '`onChange` handler. This will render a read-only field. If ' + - 'the field should be mutable use `defaultChecked`. Otherwise, ' + - 'set either `onChange` or `readOnly`.' - ) : null); - } + props.disabled) { + return; + } + return new Error( + 'You provided a `checked` prop to a form field without an ' + + '`onChange` handler. This will render a read-only field. If ' + + 'the field should be mutable use `defaultChecked`. Otherwise, ' + + 'set either `onChange` or `readOnly`.' + ); }, onChange: ReactPropTypes.func } }, /** * @param {ReactComponent} input Form component * @return {*} current value of the input either from value prop or link. @@ -3606,17 +3993,71 @@ var LinkedValueUtils = { return _handleLinkedCheckChange; } return input.props.onChange; } }; module.exports = LinkedValueUtils; -},{"./ReactPropTypes":69,"./invariant":125,"./warning":148}],24:[function(_dereq_,module,exports){ +},{"./ReactPropTypes":75,"./invariant":134}],26:[function(_dereq_,module,exports){ +/** + * Copyright 2014 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule LocalEventTrapMixin + */ + +"use strict"; + +var ReactBrowserEventEmitter = _dereq_("./ReactBrowserEventEmitter"); + +var accumulate = _dereq_("./accumulate"); +var forEachAccumulated = _dereq_("./forEachAccumulated"); +var invariant = _dereq_("./invariant"); + +function remove(event) { + event.remove(); +} + +var LocalEventTrapMixin = { + trapBubbledEvent:function(topLevelType, handlerBaseName) { + ("production" !== "development" ? invariant(this.isMounted(), 'Must be mounted to trap events') : invariant(this.isMounted())); + var listener = ReactBrowserEventEmitter.trapBubbledEvent( + topLevelType, + handlerBaseName, + this.getDOMNode() + ); + this._localEventListeners = accumulate(this._localEventListeners, listener); + }, + + // trapCapturedEvent would look nearly identical. We don't implement that + // method because it isn't currently needed. + + componentWillUnmount:function() { + if (this._localEventListeners) { + forEachAccumulated(this._localEventListeners, remove); + } + } +}; + +module.exports = LocalEventTrapMixin; + +},{"./ReactBrowserEventEmitter":31,"./accumulate":106,"./forEachAccumulated":121,"./invariant":134}],27:[function(_dereq_,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 @@ -3671,17 +4112,17 @@ var MobileSafariClickEventPlugin = { } } } }; module.exports = MobileSafariClickEventPlugin; -},{"./EventConstants":15,"./emptyFunction":109}],25:[function(_dereq_,module,exports){ +},{"./EventConstants":16,"./emptyFunction":116}],28:[function(_dereq_,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 @@ -3792,17 +4233,17 @@ var PooledClass = { oneArgumentPooler: oneArgumentPooler, twoArgumentPooler: twoArgumentPooler, threeArgumentPooler: threeArgumentPooler, fiveArgumentPooler: fiveArgumentPooler }; module.exports = PooledClass; -},{"./invariant":125}],26:[function(_dereq_,module,exports){ +},{"./invariant":134}],29:[function(_dereq_,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 @@ -3820,16 +4261,17 @@ module.exports = PooledClass; var DOMPropertyOperations = _dereq_("./DOMPropertyOperations"); var EventPluginUtils = _dereq_("./EventPluginUtils"); var ReactChildren = _dereq_("./ReactChildren"); var ReactComponent = _dereq_("./ReactComponent"); var ReactCompositeComponent = _dereq_("./ReactCompositeComponent"); var ReactContext = _dereq_("./ReactContext"); var ReactCurrentOwner = _dereq_("./ReactCurrentOwner"); +var ReactDescriptor = _dereq_("./ReactDescriptor"); var ReactDOM = _dereq_("./ReactDOM"); var ReactDOMComponent = _dereq_("./ReactDOMComponent"); var ReactDefaultInjection = _dereq_("./ReactDefaultInjection"); var ReactInstanceHandles = _dereq_("./ReactInstanceHandles"); var ReactMount = _dereq_("./ReactMount"); var ReactMultiChild = _dereq_("./ReactMultiChild"); var ReactPerf = _dereq_("./ReactPerf"); var ReactPropTypes = _dereq_("./ReactPropTypes"); @@ -3839,37 +4281,42 @@ var ReactTextComponent = _dereq_("./Reac var onlyChild = _dereq_("./onlyChild"); ReactDefaultInjection.inject(); var React = { Children: { map: ReactChildren.map, forEach: ReactChildren.forEach, + count: ReactChildren.count, only: onlyChild }, DOM: ReactDOM, PropTypes: ReactPropTypes, initializeTouchEvents: function(shouldUseTouch) { EventPluginUtils.useTouchEvents = shouldUseTouch; }, createClass: ReactCompositeComponent.createClass, + createDescriptor: function(type, props, children) { + var args = Array.prototype.slice.call(arguments, 1); + return type.apply(null, args); + }, constructAndRenderComponent: ReactMount.constructAndRenderComponent, constructAndRenderComponentByID: ReactMount.constructAndRenderComponentByID, renderComponent: ReactPerf.measure( 'React', 'renderComponent', ReactMount.renderComponent ), renderComponentToString: ReactServerRendering.renderComponentToString, renderComponentToStaticMarkup: ReactServerRendering.renderComponentToStaticMarkup, unmountComponentAtNode: ReactMount.unmountComponentAtNode, - isValidClass: ReactCompositeComponent.isValidClass, - isValidComponent: ReactComponent.isValidComponent, + isValidClass: ReactDescriptor.isValidFactory, + isValidComponent: ReactDescriptor.isValidDescriptor, withContext: ReactContext.withContext, __internals: { Component: ReactComponent, CurrentOwner: ReactCurrentOwner, DOMComponent: ReactDOMComponent, DOMPropertyOperations: DOMPropertyOperations, InstanceHandles: ReactInstanceHandles, Mount: ReactMount, @@ -3882,26 +4329,54 @@ if ("production" !== "development") { var ExecutionEnvironment = _dereq_("./ExecutionEnvironment"); if (ExecutionEnvironment.canUseDOM && window.top === window.self && navigator.userAgent.indexOf('Chrome') > -1) { console.debug( 'Download the React DevTools for a better development experience: ' + 'http://fb.me/react-devtools' ); + + var expectedFeatures = [ + // shims + Array.isArray, + Array.prototype.every, + Array.prototype.forEach, + Array.prototype.indexOf, + Array.prototype.map, + Date.now, + Function.prototype.bind, + Object.keys, + String.prototype.split, + String.prototype.trim, + + // shams + Object.create, + Object.freeze + ]; + + for (var i in expectedFeatures) { + if (!expectedFeatures[i]) { + console.error( + 'One or more ES5 shim/shams expected by React are not available: ' + + 'http://fb.me/react-warning-polyfills' + ); + break; + } + } } } // Version exists only in the open-source version of React, not in Facebook's // internal version. -React.version = '0.10.0'; +React.version = '0.11.1'; module.exports = React; -},{"./DOMPropertyOperations":10,"./EventPluginUtils":19,"./ExecutionEnvironment":21,"./ReactChildren":30,"./ReactComponent":31,"./ReactCompositeComponent":33,"./ReactContext":34,"./ReactCurrentOwner":35,"./ReactDOM":36,"./ReactDOMComponent":38,"./ReactDefaultInjection":48,"./ReactInstanceHandles":57,"./ReactMount":60,"./ReactMultiChild":62,"./ReactPerf":65,"./ReactPropTypes":69,"./ReactServerRendering":73,"./ReactTextComponent":77,"./onlyChild":141}],27:[function(_dereq_,module,exports){ +},{"./DOMPropertyOperations":12,"./EventPluginUtils":20,"./ExecutionEnvironment":22,"./ReactChildren":34,"./ReactComponent":35,"./ReactCompositeComponent":38,"./ReactContext":39,"./ReactCurrentOwner":40,"./ReactDOM":41,"./ReactDOMComponent":43,"./ReactDefaultInjection":53,"./ReactDescriptor":56,"./ReactInstanceHandles":64,"./ReactMount":67,"./ReactMultiChild":68,"./ReactPerf":71,"./ReactPropTypes":75,"./ReactServerRendering":79,"./ReactTextComponent":83,"./onlyChild":149}],30:[function(_dereq_,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 @@ -3912,16 +4387,17 @@ module.exports = React; * See the License for the specific language governing permissions and * limitations under the License. * * @providesModule ReactBrowserComponentMixin */ "use strict"; +var ReactEmptyComponent = _dereq_("./ReactEmptyComponent"); var ReactMount = _dereq_("./ReactMount"); var invariant = _dereq_("./invariant"); var ReactBrowserComponentMixin = { /** * Returns the DOM node rendered by this component. * @@ -3929,23 +4405,388 @@ var ReactBrowserComponentMixin = { * @final * @protected */ getDOMNode: function() { ("production" !== "development" ? invariant( this.isMounted(), 'getDOMNode(): A component must be mounted to have a DOM node.' ) : invariant(this.isMounted())); + if (ReactEmptyComponent.isNullComponentID(this._rootNodeID)) { + return null; + } return ReactMount.getNode(this._rootNodeID); } }; module.exports = ReactBrowserComponentMixin; -},{"./ReactMount":60,"./invariant":125}],28:[function(_dereq_,module,exports){ +},{"./ReactEmptyComponent":58,"./ReactMount":67,"./invariant":134}],31:[function(_dereq_,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 ReactBrowserEventEmitter + * @typechecks static-only + */ + +"use strict"; + +var EventConstants = _dereq_("./EventConstants"); +var EventPluginHub = _dereq_("./EventPluginHub"); +var EventPluginRegistry = _dereq_("./EventPluginRegistry"); +var ReactEventEmitterMixin = _dereq_("./ReactEventEmitterMixin"); +var ViewportMetrics = _dereq_("./ViewportMetrics"); + +var isEventSupported = _dereq_("./isEventSupported"); +var merge = _dereq_("./merge"); + +/** + * Summary of `ReactBrowserEventEmitter` event handling: + * + * - Top-level delegation is used to trap most native browser events. This + * may only occur in the main thread and is the responsibility of + * ReactEventListener, which is injected and can therefore support pluggable + * event sources. This is the only work that occurs in the main thread. + * + * - We normalize and de-duplicate events to account for browser quirks. This + * may be done in the worker thread. + * + * - Forward these native events (with the associated top-level type used to + * trap it) to `EventPluginHub`, which in turn will ask plugins if they want + * to extract any synthetic events. + * + * - The `EventPluginHub` will then process each event by annotating them with + * "dispatches", a sequence of listeners and IDs that care about that event. + * + * - The `EventPluginHub` then dispatches the events. + * + * Overview of React and the event system: + * + * +------------+ . + * | DOM | . + * +------------+ . + * | . + * v . + * +------------+ . + * | ReactEvent | . + * | Listener | . + * +------------+ . +-----------+ + * | . +--------+|SimpleEvent| + * | . | |Plugin | + * +-----|------+ . v +-----------+ + * | | | . +--------------+ +------------+ + * | +-----------.--->|EventPluginHub| | Event | + * | | . | | +-----------+ | Propagators| + * | ReactEvent | . | | |TapEvent | |------------| + * | Emitter | . | |<---+|Plugin | |other plugin| + * | | . | | +-----------+ | utilities | + * | +-----------.--->| | +------------+ + * | | | . +--------------+ + * +-----|------+ . ^ +-----------+ + * | . | |Enter/Leave| + * + . +-------+|Plugin | + * +-------------+ . +-----------+ + * | application | . + * |-------------| . + * | | . + * | | . + * +-------------+ . + * . + * React Core . General Purpose Event Plugin System + */ + +var alreadyListeningTo = {}; +var isMonitoringScrollValue = false; +var reactTopListenersCounter = 0; + +// For events like 'submit' which don't consistently bubble (which we trap at a +// lower node than `document`), binding at `document` would cause duplicate +// events so we don't include them here +var topEventMapping = { + topBlur: 'blur', + topChange: 'change', + topClick: 'click', + topCompositionEnd: 'compositionend', + topCompositionStart: 'compositionstart', + topCompositionUpdate: 'compositionupdate', + topContextMenu: 'contextmenu', + topCopy: 'copy', + topCut: 'cut', + topDoubleClick: 'dblclick', + topDrag: 'drag', + topDragEnd: 'dragend', + topDragEnter: 'dragenter', + topDragExit: 'dragexit', + topDragLeave: 'dragleave', + topDragOver: 'dragover', + topDragStart: 'dragstart', + topDrop: 'drop', + topFocus: 'focus', + topInput: 'input', + topKeyDown: 'keydown', + topKeyPress: 'keypress', + topKeyUp: 'keyup', + topMouseDown: 'mousedown', + topMouseMove: 'mousemove', + topMouseOut: 'mouseout', + topMouseOver: 'mouseover', + topMouseUp: 'mouseup', + topPaste: 'paste', + topScroll: 'scroll', + topSelectionChange: 'selectionchange', + topTextInput: 'textInput', + topTouchCancel: 'touchcancel', + topTouchEnd: 'touchend', + topTouchMove: 'touchmove', + topTouchStart: 'touchstart', + topWheel: 'wheel' +}; + +/** + * To ensure no conflicts with other potential React instances on the page + */ +var topListenersIDKey = "_reactListenersID" + String(Math.random()).slice(2); + +function getListeningForDocument(mountAt) { + // In IE8, `mountAt` is a host object and doesn't have `hasOwnProperty` + // directly. + if (!Object.prototype.hasOwnProperty.call(mountAt, topListenersIDKey)) { + mountAt[topListenersIDKey] = reactTopListenersCounter++; + alreadyListeningTo[mountAt[topListenersIDKey]] = {}; + } + return alreadyListeningTo[mountAt[topListenersIDKey]]; +} + +/** + * `ReactBrowserEventEmitter` is used to attach top-level event listeners. For + * example: + * + * ReactBrowserEventEmitter.putListener('myID', 'onClick', myFunction); + * + * This would allocate a "registration" of `('onClick', myFunction)` on 'myID'. + * + * @internal + */ +var ReactBrowserEventEmitter = merge(ReactEventEmitterMixin, { + + /** + * Injectable event backend + */ + ReactEventListener: null, + + injection: { + /** + * @param {object} ReactEventListener + */ + injectReactEventListener: function(ReactEventListener) { + ReactEventListener.setHandleTopLevel( + ReactBrowserEventEmitter.handleTopLevel + ); + ReactBrowserEventEmitter.ReactEventListener = ReactEventListener; + } + }, + + /** + * Sets whether or not any created callbacks should be enabled. + * + * @param {boolean} enabled True if callbacks should be enabled. + */ + setEnabled: function(enabled) { + if (ReactBrowserEventEmitter.ReactEventListener) { + ReactBrowserEventEmitter.ReactEventListener.setEnabled(enabled); + } + }, + + /** + * @return {boolean} True if callbacks are enabled. + */ + isEnabled: function() { + return !!( + ReactBrowserEventEmitter.ReactEventListener && + ReactBrowserEventEmitter.ReactEventListener.isEnabled() + ); + }, + + /** + * We listen for bubbled touch events on the document object. + * + * Firefox v8.01 (and possibly others) exhibited strange behavior when + * mounting `onmousemove` events at some node that was not the document + * element. The symptoms were that if your mouse is not moving over something + * contained within that mount point (for example on the background) the + * top-level listeners for `onmousemove` won't be called. However, if you + * register the `mousemove` on the document object, then it will of course + * catch all `mousemove`s. This along with iOS quirks, justifies restricting + * top-level listeners to the document object only, at least for these + * movement types of events and possibly all events. + * + * @see http://www.quirksmode.org/blog/archives/2010/09/click_event_del.html + * + * Also, `keyup`/`keypress`/`keydown` do not bubble to the window on IE, but + * they bubble to document. + * + * @param {string} registrationName Name of listener (e.g. `onClick`). + * @param {object} contentDocumentHandle Document which owns the container + */ + listenTo: function(registrationName, contentDocumentHandle) { + var mountAt = contentDocumentHandle; + var isListening = getListeningForDocument(mountAt); + var dependencies = EventPluginRegistry. + registrationNameDependencies[registrationName]; + + var topLevelTypes = EventConstants.topLevelTypes; + for (var i = 0, l = dependencies.length; i < l; i++) { + var dependency = dependencies[i]; + if (!( + isListening.hasOwnProperty(dependency) && + isListening[dependency] + )) { + if (dependency === topLevelTypes.topWheel) { + if (isEventSupported('wheel')) { + ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent( + topLevelTypes.topWheel, + 'wheel', + mountAt + ); + } else if (isEventSupported('mousewheel')) { + ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent( + topLevelTypes.topWheel, + 'mousewheel', + mountAt + ); + } else { + // Firefox needs to capture a different mouse scroll event. + // @see http://www.quirksmode.org/dom/events/tests/scroll.html + ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent( + topLevelTypes.topWheel, + 'DOMMouseScroll', + mountAt + ); + } + } else if (dependency === topLevelTypes.topScroll) { + + if (isEventSupported('scroll', true)) { + ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent( + topLevelTypes.topScroll, + 'scroll', + mountAt + ); + } else { + ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent( + topLevelTypes.topScroll, + 'scroll', + ReactBrowserEventEmitter.ReactEventListener.WINDOW_HANDLE + ); + } + } else if (dependency === topLevelTypes.topFocus || + dependency === topLevelTypes.topBlur) { + + if (isEventSupported('focus', true)) { + ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent( + topLevelTypes.topFocus, + 'focus', + mountAt + ); + ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent( + topLevelTypes.topBlur, + 'blur', + mountAt + ); + } else if (isEventSupported('focusin')) { + // IE has `focusin` and `focusout` events which bubble. + // @see http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html + ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent( + topLevelTypes.topFocus, + 'focusin', + mountAt + ); + ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent( + topLevelTypes.topBlur, + 'focusout', + mountAt + ); + } + + // to make sure blur and focus event listeners are only attached once + isListening[topLevelTypes.topBlur] = true; + isListening[topLevelTypes.topFocus] = true; + } else if (topEventMapping.hasOwnProperty(dependency)) { + ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent( + dependency, + topEventMapping[dependency], + mountAt + ); + } + + isListening[dependency] = true; + } + } + }, + + trapBubbledEvent: function(topLevelType, handlerBaseName, handle) { + return ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent( + topLevelType, + handlerBaseName, + handle + ); + }, + + trapCapturedEvent: function(topLevelType, handlerBaseName, handle) { + return ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent( + topLevelType, + handlerBaseName, + handle + ); + }, + + /** + * Listens to window scroll and resize events. We cache scroll values so that + * application code can access them without triggering reflows. + * + * NOTE: Scroll events do not bubble. + * + * @see http://www.quirksmode.org/dom/events/scroll.html + */ + ensureScrollValueMonitoring: function(){ + if (!isMonitoringScrollValue) { + var refresh = ViewportMetrics.refreshScrollValues; + ReactBrowserEventEmitter.ReactEventListener.monitorScrollValue(refresh); + isMonitoringScrollValue = true; + } + }, + + eventNameDispatchConfigs: EventPluginHub.eventNameDispatchConfigs, + + registrationNameModules: EventPluginHub.registrationNameModules, + + putListener: EventPluginHub.putListener, + + getListener: EventPluginHub.getListener, + + deleteListener: EventPluginHub.deleteListener, + + deleteAllListeners: EventPluginHub.deleteAllListeners + +}); + +module.exports = ReactBrowserEventEmitter; + +},{"./EventConstants":16,"./EventPluginHub":18,"./EventPluginRegistry":19,"./ReactEventEmitterMixin":60,"./ViewportMetrics":105,"./isEventSupported":135,"./merge":144}],32:[function(_dereq_,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 @@ -3963,16 +4804,18 @@ module.exports = ReactBrowserComponentMi "use strict"; var React = _dereq_("./React"); var ReactTransitionGroup = _dereq_("./ReactTransitionGroup"); var ReactCSSTransitionGroupChild = _dereq_("./ReactCSSTransitionGroupChild"); var ReactCSSTransitionGroup = React.createClass({ + displayName: 'ReactCSSTransitionGroup', + propTypes: { transitionName: React.PropTypes.string.isRequired, transitionEnter: React.PropTypes.bool, transitionLeave: React.PropTypes.bool }, getDefaultProps: function() { return { @@ -4002,17 +4845,17 @@ var ReactCSSTransitionGroup = React.crea this.props.children ) ); } }); module.exports = ReactCSSTransitionGroup; -},{"./React":26,"./ReactCSSTransitionGroupChild":29,"./ReactTransitionGroup":80}],29:[function(_dereq_,module,exports){ +},{"./React":29,"./ReactCSSTransitionGroupChild":33,"./ReactTransitionGroup":86}],33:[function(_dereq_,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 @@ -4053,16 +4896,18 @@ if ("production" !== "development") { 'an animationend or transitionend event after timeout (' + NO_EVENT_TIMEOUT + 'ms). You should either disable this ' + 'transition in JS or add a CSS animation/transition.' ); }; } var ReactCSSTransitionGroupChild = React.createClass({ + displayName: 'ReactCSSTransitionGroupChild', + transition: function(animationType, finishCallback) { var node = this.getDOMNode(); var className = this.props.name + '-' + animationType; var activeClassName = className + '-active'; var noEventTimeout = null; var endListener = function() { if ("production" !== "development") { @@ -4089,21 +4934,16 @@ var ReactCSSTransitionGroupChild = React if ("production" !== "development") { noEventTimeout = setTimeout(noEventListener, NO_EVENT_TIMEOUT); } }, queueClass: function(className) { this.classNameQueue.push(className); - if (this.props.runNextTick) { - this.props.runNextTick(this.flushClassNameQueue); - return; - } - if (!this.timeout) { this.timeout = setTimeout(this.flushClassNameQueue, TICK); } }, flushClassNameQueue: function() { if (this.isMounted()) { this.classNameQueue.forEach( @@ -4142,17 +4982,17 @@ var ReactCSSTransitionGroupChild = React render: function() { return onlyChild(this.props.children); } }); module.exports = ReactCSSTransitionGroupChild; -},{"./CSSCore":2,"./React":26,"./ReactTransitionEvents":79,"./onlyChild":141}],30:[function(_dereq_,module,exports){ +},{"./CSSCore":3,"./React":29,"./ReactTransitionEvents":85,"./onlyChild":149}],34:[function(_dereq_,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 @@ -4165,18 +5005,18 @@ module.exports = ReactCSSTransitionGroup * * @providesModule ReactChildren */ "use strict"; var PooledClass = _dereq_("./PooledClass"); -var invariant = _dereq_("./invariant"); var traverseAllChildren = _dereq_("./traverseAllChildren"); +var warning = _dereq_("./warning"); var twoArgumentPooler = PooledClass.twoArgumentPooler; var threeArgumentPooler = PooledClass.threeArgumentPooler; /** * PooledClass representing the bookkeeping associated with performing a child * traversal. Allows avoiding binding callbacks. * @@ -4231,26 +5071,31 @@ function MapBookKeeping(mapResult, mapFu this.mapFunction = mapFunction; this.mapContext = mapContext; } PooledClass.addPoolingTo(MapBookKeeping, threeArgumentPooler); function mapSingleChildIntoContext(traverseContext, child, name, i) { var mapBookKeeping = traverseContext; var mapResult = mapBookKeeping.mapResult; - var mappedChild = - mapBookKeeping.mapFunction.call(mapBookKeeping.mapContext, child, i); - // We found a component instance - ("production" !== "development" ? invariant( - !mapResult.hasOwnProperty(name), + + var keyUnique = !mapResult.hasOwnProperty(name); + ("production" !== "development" ? warning( + keyUnique, 'ReactChildren.map(...): Encountered two children with the same key, ' + - '`%s`. Children keys must be unique.', + '`%s`. Child keys must be unique; when two children share a key, only ' + + 'the first child will be used.', name - ) : invariant(!mapResult.hasOwnProperty(name))); - mapResult[name] = mappedChild; + ) : null); + + if (keyUnique) { + var mappedChild = + mapBookKeeping.mapFunction.call(mapBookKeeping.mapContext, child, i); + mapResult[name] = mappedChild; + } } /** * Maps children that are typically specified as `props.children`. * * The provided mapFunction(child, key, index) will be called for each * leaf child. * @@ -4269,24 +5114,40 @@ function mapChildren(children, func, con var mapResult = {}; var traverseContext = MapBookKeeping.getPooled(mapResult, func, context); traverseAllChildren(children, mapSingleChildIntoContext, traverseContext); MapBookKeeping.release(traverseContext); return mapResult; } +function forEachSingleChildDummy(traverseContext, child, name, i) { + return null; +} + +/** + * Count the number of children that are typically specified as + * `props.children`. + * + * @param {?*} children Children tree container. + * @return {number} The number of children. + */ +function countChildren(children, context) { + return traverseAllChildren(children, forEachSingleChildDummy, null); +} + var ReactChildren = { forEach: forEachChildren, - map: mapChildren + map: mapChildren, + count: countChildren }; module.exports = ReactChildren; -},{"./PooledClass":25,"./invariant":125,"./traverseAllChildren":146}],31:[function(_dereq_,module,exports){ +},{"./PooledClass":28,"./traverseAllChildren":156,"./warning":158}],35:[function(_dereq_,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 @@ -4297,52 +5158,39 @@ module.exports = ReactChildren; * See the License for the specific language governing permissions and * limitations under the License. * * @providesModule ReactComponent */ "use strict"; -var ReactCurrentOwner = _dereq_("./ReactCurrentOwner"); +var ReactDescriptor = _dereq_("./ReactDescriptor"); var ReactOwner = _dereq_("./ReactOwner"); var ReactUpdates = _dereq_("./ReactUpdates"); var invariant = _dereq_("./invariant"); var keyMirror = _dereq_("./keyMirror"); var merge = _dereq_("./merge"); -var monitorCodeUse = _dereq_("./monitorCodeUse"); /** * Every React component is in one of these life cycles. */ var ComponentLifeCycle = keyMirror({ /** * Mounted components have a DOM node representation and are capable of * receiving new props. */ MOUNTED: null, /** * Unmounted components are inactive and cannot receive new props. */ UNMOUNTED: null }); -/** - * Warn if there's no key explicitly set on dynamic arrays of children or - * object keys are not valid. This allows us to keep track of children between - * updates. - */ - -var ownerHasExplicitKeyWarning = {}; -var ownerHasPropertyWarning = {}; -var ownerHasMonitoredObjectMap = {}; - -var NUMERIC_PROPERTY_REGEX = /^\d+$/; - var injected = false; /** * Optionally injectable environment dependent cleanup hook. (server vs. * browser etc). Example: A browser system caches DOM nodes based on component * ID and must remove that cache entry when this instance is unmounted. * * @private @@ -4356,140 +5204,16 @@ var unmountIDFromEnvironment = null; * low level `div` and `span` nodes. Other platforms may have different * encoding of this "image". This must be injected. * * @private */ var mountImageIntoNode = null; /** - * Warn if the component doesn't have an explicit key assigned to it. - * This component is in an array. The array could grow and shrink or be - * reordered. All children that haven't already been validated are required to - * have a "key" property assigned to it. - * - * @internal - * @param {ReactComponent} component Component that requires a key. - */ -function validateExplicitKey(component) { - if (component.__keyValidated__ || component.props.key != null) { - return; - } - component.__keyValidated__ = true; - - // We can't provide friendly warnings for top level components. - if (!ReactCurrentOwner.current) { - return; - } - - // Name of the component whose render method tried to pass children. - var currentName = ReactCurrentOwner.current.constructor.displayName; - if (ownerHasExplicitKeyWarning.hasOwnProperty(currentName)) { - return; - } - ownerHasExplicitKeyWarning[currentName] = true; - - var message = 'Each child in an array should have a unique "key" prop. ' + - 'Check the render method of ' + currentName + '.'; - - var childOwnerName = null; - if (!component.isOwnedBy(ReactCurrentOwner.current)) { - // Name of the component that originally created this child. - childOwnerName = - component._owner && - component._owner.constructor.displayName; - - // Usually the current owner is the offender, but if it accepts - // children as a property, it may be the creator of the child that's - // responsible for assigning it a key. - message += ' It was passed a child from ' + childOwnerName + '.'; - } - - message += ' See http://fb.me/react-warning-keys for more information.'; - monitorCodeUse('react_key_warning', { - component: currentName, - componentOwner: childOwnerName - }); - console.warn(message); -} - -/** - * Warn if the key is being defined as an object property but has an incorrect - * value. - * - * @internal - * @param {string} name Property name of the key. - * @param {ReactComponent} component Component that requires a key. - */ -function validatePropertyKey(name) { - if (NUMERIC_PROPERTY_REGEX.test(name)) { - // Name of the component whose render method tried to pass children. - var currentName = ReactCurrentOwner.current.constructor.displayName; - if (ownerHasPropertyWarning.hasOwnProperty(currentName)) { - return; - } - ownerHasPropertyWarning[currentName] = true; - - monitorCodeUse('react_numeric_key_warning'); - console.warn( - 'Child objects should have non-numeric keys so ordering is preserved. ' + - 'Check the render method of ' + currentName + '. ' + - 'See http://fb.me/react-warning-keys for more information.' - ); - } -} - -/** - * Log that we're using an object map. We're considering deprecating this - * feature and replace it with proper Map and ImmutableMap data structures. - * - * @internal - */ -function monitorUseOfObjectMap() { - // Name of the component whose render method tried to pass children. - // We only use this to avoid spewing the logs. We lose additional - // owner stacks but hopefully one level is enough to trace the source. - var currentName = (ReactCurrentOwner.current && - ReactCurrentOwner.current.constructor.displayName) || ''; - if (ownerHasMonitoredObjectMap.hasOwnProperty(currentName)) { - return; - } - ownerHasMonitoredObjectMap[currentName] = true; - monitorCodeUse('react_object_map_children'); -} - -/** - * Ensure that every component either is passed in a static location, in an - * array with an explicit keys property defined, or in an object literal - * with valid key property. - * - * @internal - * @param {*} component Statically passed child of any type. - * @return {boolean} - */ -function validateChildKeys(component) { - if (Array.isArray(component)) { - for (var i = 0; i < component.length; i++) { - var child = component[i]; - if (ReactComponent.isValidComponent(child)) { - validateExplicitKey(child); - } - } - } else if (ReactComponent.isValidComponent(component)) { - // This component was passed in a valid location. - component.__keyValidated__ = true; - } else if (component && typeof component === 'object') { - monitorUseOfObjectMap(); - for (var name in component) { - validatePropertyKey(name, component); - } - } -} - -/** * Components are the basic units of composition in React. * * Every component accepts a set of keyed input parameters known as "props" that * are initialized by the constructor. Once a component is mounted, the props * can be mutated using `setProps` or `replaceProps`. * * Every component is capable of the following operations: * @@ -4517,65 +5241,35 @@ var ReactComponent = { !injected, 'ReactComponent: injectEnvironment() can only be called once.' ) : invariant(!injected)); mountImageIntoNode = ReactComponentEnvironment.mountImageIntoNode; unmountIDFromEnvironment = ReactComponentEnvironment.unmountIDFromEnvironment; ReactComponent.BackendIDOperations = ReactComponentEnvironment.BackendIDOperations; - ReactComponent.ReactReconcileTransaction = - ReactComponentEnvironment.ReactReconcileTransaction; injected = true; } }, /** - * @param {?object} object - * @return {boolean} True if `object` is a valid component. - * @final - */ - isValidComponent: function(object) { - if (!object || !object.type || !object.type.prototype) { - return false; - } - // This is the safer way of duck checking the type of instance this is. - // The object can be a generic descriptor but the type property refers to - // the constructor and it's prototype can be used to inspect the type that - // will actually get mounted. - var prototype = object.type.prototype; - return ( - typeof prototype.mountComponentIntoNode === 'function' && - typeof prototype.receiveComponent === 'function' - ); - }, - - /** * @internal */ LifeCycle: ComponentLifeCycle, /** * Injected module that provides ability to mutate individual properties. * Injected into the base class because many different subclasses need access * to this. * * @internal */ BackendIDOperations: null, /** - * React references `ReactReconcileTransaction` using this property in order - * to allow dependency injection. - * - * @internal - */ - ReactReconcileTransaction: null, - - /** * Base functionality for every ReactComponent constructor. Mixed into the * `ReactComponent` prototype, but exposed statically for easy access. * * @lends {ReactComponent.prototype} */ Mixin: { /** @@ -4593,19 +5287,21 @@ var ReactComponent = { * Sets a subset of the props. * * @param {object} partialProps Subset of the next props. * @param {?function} callback Called after props are updated. * @final * @public */ setProps: function(partialProps, callback) { - // Merge with `_pendingProps` if it exists, otherwise with existing props. + // Merge with the pending descriptor if it exists, otherwise with existing + // descriptor props. + var descriptor = this._pendingDescriptor || this._descriptor; this.replaceProps( - merge(this._pendingProps || this.props, partialProps), + merge(descriptor.props, partialProps), callback ); }, /** * Replaces all of the props. * * @param {object} props New props. @@ -4621,63 +5317,74 @@ var ReactComponent = { ("production" !== "development" ? invariant( this._mountDepth === 0, 'replaceProps(...): You called `setProps` or `replaceProps` on a ' + 'component with a parent. This is an anti-pattern since props will ' + 'get reactively updated when rendered. Instead, change the owner\'s ' + '`render` method to pass the correct value as props to the component ' + 'where it is created.' ) : invariant(this._mountDepth === 0)); - this._pendingProps = props; + // This is a deoptimized path. We optimize for always having a descriptor. + // This creates an extra internal descriptor. + this._pendingDescriptor = ReactDescriptor.cloneAndReplaceProps( + this._pendingDescriptor || this._descriptor, + props + ); + ReactUpdates.enqueueUpdate(this, callback); + }, + + /** + * Schedule a partial update to the props. Only used for internal testing. + * + * @param {object} partialProps Subset of the next props. + * @param {?function} callback Called after props are updated. + * @final + * @internal + */ + _setPropsInternal: function(partialProps, callback) { + // This is a deoptimized path. We optimize for always having a descriptor. + // This creates an extra internal descriptor. + var descriptor = this._pendingDescriptor || this._descriptor; + this._pendingDescriptor = ReactDescriptor.cloneAndReplaceProps( + descriptor, + merge(descriptor.props, partialProps) + ); ReactUpdates.enqueueUpdate(this, callback); }, /** * Base constructor for all React components. * * Subclasses that override this method should make sure to invoke * `ReactComponent.Mixin.construct.call(this, ...)`. * - * @param {?object} initialProps - * @param {*} children + * @param {ReactDescriptor} descriptor * @internal */ - construct: function(initialProps, children) { - this.props = initialProps || {}; + construct: function(descriptor) { + // This is the public exposed props object after it has been processed + // with default props. The descriptor's props represents the true internal + // state of the props. + this.props = descriptor.props; // Record the component responsible for creating this component. - this._owner = ReactCurrentOwner.current; + // This is accessible through the descriptor but we maintain an extra + // field for compatibility with devtools and as a way to make an + // incremental update. TODO: Consider deprecating this field. + this._owner = descriptor._owner; + // All components start unmounted. this._lifeCycleState = ComponentLifeCycle.UNMOUNTED; - this._pendingProps = null; + // See ReactUpdates. this._pendingCallbacks = null; - // Unlike _pendingProps and _pendingCallbacks, we won't use null to - // indicate that nothing is pending because it's possible for a component - // to have a null owner. Instead, an owner change is pending when - // this._owner !== this._pendingOwner. - this._pendingOwner = this._owner; - - // Children can be more than one argument - var childrenLength = arguments.length - 1; - if (childrenLength === 1) { - if ("production" !== "development") { - validateChildKeys(children); - } - this.props.children = children; - } else if (childrenLength > 1) { - var childArray = Array(childrenLength); - for (var i = 0; i < childrenLength; i++) { - if ("production" !== "development") { - validateChildKeys(arguments[i + 1]); - } - childArray[i] = arguments[i + 1]; - } - this.props.children = childArray; - } + // We keep the old descriptor and a reference to the pending descriptor + // to track updates. + this._descriptor = descriptor; + this._pendingDescriptor = null; }, /** * Initializes the component, renders markup, and registers event listeners. * * NOTE: This does not insert any nodes into the DOM. * * Subclasses that override this method should make sure to invoke @@ -4692,19 +5399,20 @@ var ReactComponent = { mountComponent: function(rootID, transaction, mountDepth) { ("production" !== "development" ? invariant( !this.isMounted(), 'mountComponent(%s, ...): Can only mount an unmounted component. ' + 'Make sure to avoid storing components between renders or reusing a ' + 'single component instance in multiple places.', rootID ) : invariant(!this.isMounted())); - var props = this.props; + var props = this._descriptor.props; if (props.ref != null) { - ReactOwner.addComponentAsRefTo(this, props.ref, this._owner); + var owner = this._descriptor._owner; + ReactOwner.addComponentAsRefTo(this, props.ref, owner); } this._rootNodeID = rootID; this._lifeCycleState = ComponentLifeCycle.MOUNTED; this._mountDepth = mountDepth; // Effectively: return ''; }, /** @@ -4737,101 +5445,105 @@ var ReactComponent = { * * Subclasses that override this method should make sure to invoke * `ReactComponent.Mixin.receiveComponent.call(this, ...)`. * * @param {object} nextComponent Next set of properties. * @param {ReactReconcileTransaction} transaction * @internal */ - receiveComponent: function(nextComponent, transaction) { + receiveComponent: function(nextDescriptor, transaction) { ("production" !== "development" ? invariant( this.isMounted(), 'receiveComponent(...): Can only update a mounted component.' ) : invariant(this.isMounted())); - this._pendingOwner = nextComponent._owner; - this._pendingProps = nextComponent.props; - this._performUpdateIfNecessary(transaction); + this._pendingDescriptor = nextDescriptor; + this.performUpdateIfNecessary(transaction); }, /** - * Call `_performUpdateIfNecessary` within a new transaction. - * - * @internal - */ - performUpdateIfNecessary: function() { - var transaction = ReactComponent.ReactReconcileTransaction.getPooled(); - transaction.perform(this._performUpdateIfNecessary, this, transaction); - ReactComponent.ReactReconcileTransaction.release(transaction); - }, - - /** - * If `_pendingProps` is set, update the component. + * If `_pendingDescriptor` is set, update the component. * * @param {ReactReconcileTransaction} transaction * @internal */ - _performUpdateIfNecessary: function(transaction) { - if (this._pendingProps == null) { + performUpdateIfNecessary: function(transaction) { + if (this._pendingDescriptor == null) { return; } - var prevProps = this.props; - var prevOwner = this._owner; - this.props = this._pendingProps; - this._owner = this._pendingOwner; - this._pendingProps = null; - this.updateComponent(transaction, prevProps, prevOwner); + var prevDescriptor = this._descriptor; + var nextDescriptor = this._pendingDescriptor; + this._descriptor = nextDescriptor; + this.props = nextDescriptor.props; + this._owner = nextDescriptor._owner; + this._pendingDescriptor = null; + this.updateComponent(transaction, prevDescriptor); }, /** * Updates the component's currently mounted representation. * * @param {ReactReconcileTransaction} transaction - * @param {object} prevProps + * @param {object} prevDescriptor * @internal */ - updateComponent: function(transaction, prevProps, prevOwner) { - var props = this.props; + updateComponent: function(transaction, prevDescriptor) { + var nextDescriptor = this._descriptor; + // If either the owner or a `ref` has changed, make sure the newest owner // has stored a reference to `this`, and the previous owner (if different) - // has forgotten the reference to `this`. - if (this._owner !== prevOwner || props.ref !== prevProps.ref) { - if (prevProps.ref != null) { + // has forgotten the reference to `this`. We use the descriptor instead + // of the public this.props because the post processing cannot determine + // a ref. The ref conceptually lives on the descriptor. + + // TODO: Should this even be possible? The owner cannot change because + // it's forbidden by shouldUpdateReactComponent. The ref can change + // if you swap the keys of but not the refs. Reconsider where this check + // is made. It probably belongs where the key checking and + // instantiateReactComponent is done. + + if (nextDescriptor._owner !== prevDescriptor._owner || + nextDescriptor.props.ref !== prevDescriptor.props.ref) { + if (prevDescriptor.props.ref != null) { ReactOwner.removeComponentAsRefFrom( - this, prevProps.ref, prevOwner + this, prevDescriptor.props.ref, prevDescriptor._owner ); } // Correct, even if the owner is the same, and only the ref has changed. - if (props.ref != null) { - ReactOwner.addComponentAsRefTo(this, props.ref, this._owner); + if (nextDescriptor.props.ref != null) { + ReactOwner.addComponentAsRefTo( + this, + nextDescriptor.props.ref, + nextDescriptor._owner + ); } } }, /** * Mounts this component and inserts it into the DOM. * * @param {string} rootID DOM ID of the root node. * @param {DOMElement} container DOM element to mount into. * @param {boolean} shouldReuseMarkup If true, do not insert markup * @final * @internal * @see {ReactMount.renderComponent} */ mountComponentIntoNode: function(rootID, container, shouldReuseMarkup) { - var transaction = ReactComponent.ReactReconcileTransaction.getPooled(); + var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(); transaction.perform( this._mountComponentIntoNode, this, rootID, container, transaction, shouldReuseMarkup ); - ReactComponent.ReactReconcileTransaction.release(transaction); + ReactUpdates.ReactReconcileTransaction.release(transaction); }, /** * @param {string} rootID DOM ID of the root node. * @param {DOMElement} container DOM element to mount into. * @param {ReactReconcileTransaction} transaction * @param {boolean} shouldReuseMarkup If true, do not insert markup * @final @@ -4873,17 +5585,17 @@ var ReactComponent = { } return owner.refs[ref]; } } }; module.exports = ReactComponent; -},{"./ReactCurrentOwner":35,"./ReactOwner":64,"./ReactUpdates":81,"./invariant":125,"./keyMirror":131,"./merge":134,"./monitorCodeUse":138}],32:[function(_dereq_,module,exports){ +},{"./ReactDescriptor":56,"./ReactOwner":70,"./ReactUpdates":87,"./invariant":134,"./keyMirror":140,"./merge":144}],36:[function(_dereq_,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 @@ -4904,16 +5616,17 @@ module.exports = ReactComponent; var ReactDOMIDOperations = _dereq_("./ReactDOMIDOperations"); var ReactMarkupChecksum = _dereq_("./ReactMarkupChecksum"); var ReactMount = _dereq_("./ReactMount"); var ReactPerf = _dereq_("./ReactPerf"); var ReactReconcileTransaction = _dereq_("./ReactReconcileTransaction"); var getReactRootElementInContainer = _dereq_("./getReactRootElementInContainer"); var invariant = _dereq_("./invariant"); +var setInnerHTML = _dereq_("./setInnerHTML"); var ELEMENT_NODE_TYPE = 1; var DOC_NODE_TYPE = 9; /** * Abstracts away all functionality of `ReactComponent` requires knowledge of @@ -4974,17 +5687,17 @@ var ReactComponentBrowserEnvironment = { 'and ensure the props are the same client and server side.' ) : invariant(container.nodeType !== DOC_NODE_TYPE)); if ("production" !== "development") { console.warn( 'React attempted to use reuse markup in a container but the ' + 'checksum was invalid. This generally means that you are ' + 'using server rendering and the markup generated on the ' + - 'server was not what the client was expecting. React injected' + + 'server was not what the client was expecting. React injected ' + 'new markup to compensate which works but you have lost many ' + 'of the benefits of server rendering. Instead, figure out ' + 'why the markup being generated is different on the client ' + 'or server.' ); } } } @@ -4992,24 +5705,80 @@ var ReactComponentBrowserEnvironment = { ("production" !== "development" ? invariant( container.nodeType !== DOC_NODE_TYPE, 'You\'re trying to render a component to the document but ' + 'you didn\'t use server rendering. We can\'t do this ' + 'without using server rendering due to cross-browser quirks. ' + 'See renderComponentToString() for server rendering.' ) : invariant(container.nodeType !== DOC_NODE_TYPE)); - container.innerHTML = markup; + setInnerHTML(container, markup); } ) }; module.exports = ReactComponentBrowserEnvironment; -},{"./ReactDOMIDOperations":40,"./ReactMarkupChecksum":59,"./ReactMount":60,"./ReactPerf":65,"./ReactReconcileTransaction":71,"./getReactRootElementInContainer":120,"./invariant":125}],33:[function(_dereq_,module,exports){ +},{"./ReactDOMIDOperations":45,"./ReactMarkupChecksum":66,"./ReactMount":67,"./ReactPerf":71,"./ReactReconcileTransaction":77,"./getReactRootElementInContainer":128,"./invariant":134,"./setInnerHTML":152}],37:[function(_dereq_,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 ReactComponentWithPureRenderMixin +*/ + +"use strict"; + +var shallowEqual = _dereq_("./shallowEqual"); + +/** + * If your React component's render function is "pure", e.g. it will render the + * same result given the same props and state, provide this Mixin for a + * considerable performance boost. + * + * Most React components have pure render functions. + * + * Example: + * + * var ReactComponentWithPureRenderMixin = + * require('ReactComponentWithPureRenderMixin'); + * React.createClass({ + * mixins: [ReactComponentWithPureRenderMixin], + * + * render: function() { + * return <div className={this.props.className}>foo</div>; + * } + * }); + * + * 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":153}],38:[function(_dereq_,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 @@ -5023,31 +5792,34 @@ module.exports = ReactComponentBrowserEn * @providesModule ReactCompositeComponent */ "use strict"; var ReactComponent = _dereq_("./ReactComponent"); var ReactContext = _dereq_("./ReactContext"); var ReactCurrentOwner = _dereq_("./ReactCurrentOwner"); +var ReactDescriptor = _dereq_("./ReactDescriptor"); +var ReactDescriptorValidator = _dereq_("./ReactDescriptorValidator"); +var ReactEmptyComponent = _dereq_("./ReactEmptyComponent"); var ReactErrorUtils = _dereq_("./ReactErrorUtils"); var ReactOwner = _dereq_("./ReactOwner"); var ReactPerf = _dereq_("./ReactPerf"); var ReactPropTransferer = _dereq_("./ReactPropTransferer"); var ReactPropTypeLocations = _dereq_("./ReactPropTypeLocations"); var ReactPropTypeLocationNames = _dereq_("./ReactPropTypeLocationNames"); var ReactUpdates = _dereq_("./ReactUpdates"); var instantiateReactComponent = _dereq_("./instantiateReactComponent"); var invariant = _dereq_("./invariant"); var keyMirror = _dereq_("./keyMirror"); var merge = _dereq_("./merge"); var mixInto = _dereq_("./mixInto"); var monitorCodeUse = _dereq_("./monitorCodeUse"); -var objMap = _dereq_("./objMap"); +var mapObject = _dereq_("./mapObject"); var shouldUpdateReactComponent = _dereq_("./shouldUpdateReactComponent"); var warning = _dereq_("./warning"); /** * Policies that describe methods in `ReactCompositeComponentInterface`. */ var SpecPolicy = keyMirror({ /** @@ -5328,60 +6100,80 @@ var ReactCompositeComponentInterface = { * * Although these are declared like instance properties in the specification * when defining classes using `React.createClass`, they are actually static * and are accessible on the constructor instead of the prototype. Despite * being static, they must be defined outside of the "statics" key under * which all other static methods are defined. */ var RESERVED_SPEC_KEYS = { - displayName: function(ConvenienceConstructor, displayName) { - ConvenienceConstructor.componentConstructor.displayName = displayName; - }, - mixins: function(ConvenienceConstructor, mixins) { + displayName: function(Constructor, displayName) { + Constructor.displayName = displayName; + }, + mixins: function(Constructor, mixins) { if (mixins) { for (var i = 0; i < mixins.length; i++) { - mixSpecIntoComponent(ConvenienceConstructor, mixins[i]); - } - } - }, - childContextTypes: function(ConvenienceConstructor, childContextTypes) { - var Constructor = ConvenienceConstructor.componentConstructor; + mixSpecIntoComponent(Constructor, mixins[i]); + } + } + }, + childContextTypes: function(Constructor, childContextTypes) { validateTypeDef( Constructor, childContextTypes, ReactPropTypeLocations.childContext ); Constructor.childContextTypes = merge( Constructor.childContextTypes, childContextTypes ); }, - contextTypes: function(ConvenienceConstructor, contextTypes) { - var Constructor = ConvenienceConstructor.componentConstructor; + contextTypes: function(Constructor, contextTypes) { validateTypeDef( Constructor, contextTypes, ReactPropTypeLocations.context ); Constructor.contextTypes = merge(Constructor.contextTypes, contextTypes); }, - propTypes: function(ConvenienceConstructor, propTypes) { - var Constructor = ConvenienceConstructor.componentConstructor; + /** + * Special case getDefaultProps which should move into statics but requires + * automatic merging. + */ + getDefaultProps: function(Constructor, getDefaultProps) { + if (Constructor.getDefaultProps) { + Constructor.getDefaultProps = createMergedResultFunction( + Constructor.getDefaultProps, + getDefaultProps + ); + } else { + Constructor.getDefaultProps = getDefaultProps; + } + }, + propTypes: function(Constructor, propTypes) { validateTypeDef( Constructor, propTypes, ReactPropTypeLocations.prop ); Constructor.propTypes = merge(Constructor.propTypes, propTypes); }, - statics: function(ConvenienceConstructor, statics) { - mixStaticSpecIntoComponent(ConvenienceConstructor, statics); - } -}; + statics: function(Constructor, statics) { + mixStaticSpecIntoComponent(Constructor, statics); + } +}; + +function getDeclarationErrorAddendum(component) { + var owner = component._owner || null; + if (owner && owner.constructor && owner.constructor.displayName) { + return ' Check the render method of `' + owner.constructor.displayName + + '`.'; + } + return ''; +} function validateTypeDef(Constructor, typeDef, location) { for (var propName in typeDef) { if (typeDef.hasOwnProperty(propName)) { ("production" !== "development" ? invariant( typeof typeDef[propName] == 'function', '%s: %s type `%s` is invalid; it must be a function, usually from ' + 'React.PropTypes.', @@ -5389,17 +6181,19 @@ function validateTypeDef(Constructor, ty ReactPropTypeLocationNames[location], propName ) : invariant(typeof typeDef[propName] == 'function')); } } } function validateMethodOverride(proto, name) { - var specPolicy = ReactCompositeComponentInterface[name]; + var specPolicy = ReactCompositeComponentInterface.hasOwnProperty(name) ? + ReactCompositeComponentInterface[name] : + null; // Disallow overriding of base class methods unless explicitly allowed. if (ReactCompositeComponentMixin.hasOwnProperty(name)) { ("production" !== "development" ? invariant( specPolicy === SpecPolicy.OVERRIDE_BASE, 'ReactCompositeComponentInterface: You are attempting to override ' + '`%s` from your class specification. Ensure that your method names ' + 'do not overlap with React methods.', @@ -5439,124 +6233,146 @@ function validateLifeCycleOnReplaceState 'usually means you called setState() on an unmounted component.' ) : invariant(compositeLifeCycleState !== CompositeLifeCycle.UNMOUNTING)); } /** * Custom version of `mixInto` which handles policy validation and reserved * specification keys when building `ReactCompositeComponent` classses. */ -function mixSpecIntoComponent(ConvenienceConstructor, spec) { +function mixSpecIntoComponent(Constructor, spec) { ("production" !== "development" ? invariant( - !isValidClass(spec), + !ReactDescriptor.isValidFactory(spec), 'ReactCompositeComponent: You\'re attempting to ' + 'use a component class as a mixin. Instead, just use a regular object.' - ) : invariant(!isValidClass(spec))); + ) : invariant(!ReactDescriptor.isValidFactory(spec))); ("production" !== "development" ? invariant( - !ReactComponent.isValidComponent(spec), + !ReactDescriptor.isValidDescriptor(spec), 'ReactCompositeComponent: You\'re attempting to ' + 'use a component as a mixin. Instead, just use a regular object.' - ) : invariant(!ReactComponent.isValidComponent(spec))); - - var Constructor = ConvenienceConstructor.componentConstructor; + ) : invariant(!ReactDescriptor.isValidDescriptor(spec))); + var proto = Constructor.prototype; for (var name in spec) { var property = spec[name]; if (!spec.hasOwnProperty(name)) { continue; } validateMethodOverride(proto, name); if (RESERVED_SPEC_KEYS.hasOwnProperty(name)) { - RESERVED_SPEC_KEYS[name](ConvenienceConstructor, property); + RESERVED_SPEC_KEYS[name](Constructor, property); } else { // Setup methods on prototype: // The following member methods should not be automatically bound: // 1. Expected ReactCompositeComponent methods (in the "interface"). // 2. Overridden methods (that were mixed in). - var isCompositeComponentMethod = name in ReactCompositeComponentInterface; - var isInherited = name in proto; + var isCompositeComponentMethod = + ReactCompositeComponentInterface.hasOwnProperty(name); + var isAlreadyDefined = proto.hasOwnProperty(name); var markedDontBind = property && property.__reactDontBind; var isFunction = typeof property === 'function'; var shouldAutoBind = isFunction && !isCompositeComponentMethod && - !isInherited && + !isAlreadyDefined && !markedDontBind; if (shouldAutoBind) { if (!proto.__reactAutoBindMap) { proto.__reactAutoBindMap = {}; } proto.__reactAutoBindMap[name] = property; proto[name] = property; } else { - if (isInherited) { + if (isAlreadyDefined) { + var specPolicy = ReactCompositeComponentInterface[name]; + + // These cases should already be caught by validateMethodOverride + ("production" !== "development" ? invariant( + isCompositeComponentMethod && ( + specPolicy === SpecPolicy.DEFINE_MANY_MERGED || + specPolicy === SpecPolicy.DEFINE_MANY + ), + 'ReactCompositeComponent: Unexpected spec policy %s for key %s ' + + 'when mixing in component specs.', + specPolicy, + name + ) : invariant(isCompositeComponentMethod && ( + specPolicy === SpecPolicy.DEFINE_MANY_MERGED || + specPolicy === SpecPolicy.DEFINE_MANY + ))); + // For methods which are defined more than once, call the existing - // methods before calling the new property. - if (ReactCompositeComponentInterface[name] === - SpecPolicy.DEFINE_MANY_MERGED) { + // methods before calling the new property, merging if appropriate. + if (specPolicy === SpecPolicy.DEFINE_MANY_MERGED) { proto[name] = createMergedResultFunction(proto[name], property); - } else { + } else if (specPolicy === SpecPolicy.DEFINE_MANY) { proto[name] = createChainedFunction(proto[name], property); } } else { proto[name] = property; - } - } - } - } -} - -function mixStaticSpecIntoComponent(ConvenienceConstructor, statics) { + if ("production" !== "development") { + // Add verbose displayName to the function, which helps when looking + // at profiling tools. + if (typeof property === 'function' && spec.displayName) { + proto[name].displayName = spec.displayName + '_' + name; + } + } + } + } + } + } +} + +function mixStaticSpecIntoComponent(Constructor, statics) { if (!statics) { return; } for (var name in statics) { var property = statics[name]; if (!statics.hasOwnProperty(name)) { - return; - } - - var isInherited = name in ConvenienceConstructor; + continue; + } + + var isInherited = name in Constructor; var result = property; if (isInherited) { - var existingProperty = ConvenienceConstructor[name]; + var existingProperty = Constructor[name]; var existingType = typeof existingProperty; var propertyType = typeof property; ("production" !== "development" ? invariant( existingType === 'function' && propertyType === 'function', 'ReactCompositeComponent: You are attempting to define ' + '`%s` on your component more than once, but that is only supported ' + 'for functions, which are chained together. This conflict may be ' + 'due to a mixin.', name ) : invariant(existingType === 'function' && propertyType === 'function')); result = createChainedFunction(existingProperty, property); } - ConvenienceConstructor[name] = result; - ConvenienceConstructor.componentConstructor[name] = result; + Constructor[name] = result; } } /** * Merge two objects, but throw if both contain the same key. * * @param {object} one The first object, which is mutated. * @param {object} two The second object * @return {object} one after it has been mutated to contain everything in two. */ function mergeObjectsWithNoDuplicateKeys(one, two) { ("production" !== "development" ? invariant( one && two && typeof one === 'object' && typeof two === 'object', 'mergeObjectsWithNoDuplicateKeys(): Cannot merge non-objects' ) : invariant(one && two && typeof one === 'object' && typeof two === 'object')); - objMap(two, function(value, key) { + mapObject(two, function(value, key) { ("production" !== "development" ? invariant( one[key] === undefined, 'mergeObjectsWithNoDuplicateKeys(): ' + 'Tried to merge two objects with the same key: %s', key ) : invariant(one[key] === undefined)); one[key] = value; }); @@ -5594,213 +6410,16 @@ function createMergedResultFunction(one, */ function createChainedFunction(one, two) { return function chainedFunction() { one.apply(this, arguments); two.apply(this, arguments); }; } -if ("production" !== "development") { - - var unmountedPropertyWhitelist = { - constructor: true, - construct: true, - isOwnedBy: true, // should be deprecated but can have code mod (internal) - type: true, - props: true, - // currently private but belong on the descriptor and are valid for use - // inside the framework: - __keyValidated__: true, - _owner: true, - _currentContext: true - }; - - var componentInstanceProperties = { - __keyValidated__: true, - __keySetters: true, - _compositeLifeCycleState: true, - _currentContext: true, - _defaultProps: true, - _instance: true, - _lifeCycleState: true, - _mountDepth: true, - _owner: true, - _pendingCallbacks: true, - _pendingContext: true, - _pendingForceUpdate: true, - _pendingOwner: true, - _pendingProps: true, - _pendingState: true, - _renderedComponent: true, - _rootNodeID: true, - context: true, - props: true, - refs: true, - state: true, - - // These are known instance properties coming from other sources - _pendingQueries: true, - _queryPropListeners: true, - queryParams: true - - }; - - var hasWarnedOnComponentType = {}; - - var warningStackCounter = 0; - - var issueMembraneWarning = function(instance, key) { - var isWhitelisted = unmountedPropertyWhitelist.hasOwnProperty(key); - if (warningStackCounter > 0 || isWhitelisted) { - return; - } - var name = instance.constructor.displayName || 'Unknown'; - var owner = ReactCurrentOwner.current; - var ownerName = (owner && owner.constructor.displayName) || 'Unknown'; - var warningKey = key + '|' + name + '|' + ownerName; - if (hasWarnedOnComponentType.hasOwnProperty(warningKey)) { - // We have already warned for this combination. Skip it this time. - return; - } - hasWarnedOnComponentType[warningKey] = true; - - var context = owner ? ' in ' + ownerName + '.' : ' at the top level.'; - var staticMethodExample = '<' + name + ' />.type.' + key + '(...)'; - - monitorCodeUse('react_descriptor_property_access', { component: name }); - console.warn( - 'Invalid access to component property "' + key + '" on ' + name + - context + ' See http://fb.me/react-warning-descriptors .' + - ' Use a static method instead: ' + staticMethodExample - ); - }; - - var wrapInMembraneFunction = function(fn, thisBinding) { - if (fn.__reactMembraneFunction && fn.__reactMembraneSelf === thisBinding) { - return fn.__reactMembraneFunction; - } - return fn.__reactMembraneFunction = function() { - /** - * By getting this function, you've already received a warning. The - * internals of this function will likely cause more warnings. To avoid - * Spamming too much we disable any warning triggered inside of this - * stack. - */ - warningStackCounter++; - try { - // If the this binding is unchanged, we defer to the real component. - // This is important to keep some referential integrity in the - // internals. E.g. owner equality check. - var self = this === thisBinding ? this.__realComponentInstance : this; - return fn.apply(self, arguments); - } finally { - warningStackCounter--; - } - }; - }; - - var defineMembraneProperty = function(membrane, prototype, key) { - Object.defineProperty(membrane, key, { - - configurable: false, - enumerable: true, - - get: function() { - if (this === membrane) { - // We're allowed to access the prototype directly. - return prototype[key]; - } - issueMembraneWarning(this, key); - - var realValue = this.__realComponentInstance[key]; - // If the real value is a function, we need to provide a wrapper that - // disables nested warnings. The properties type and constructors are - // expected to the be constructors and therefore is often use with an - // equality check and we shouldn't try to rebind those. - if (typeof realValue === 'function' && - key !== 'type' && - key !== 'constructor') { - return wrapInMembraneFunction(realValue, this); - } - return realValue; - }, - - set: function(value) { - if (this === membrane) { - // We're allowed to set a value on the prototype directly. - prototype[key] = value; - return; - } - issueMembraneWarning(this, key); - this.__realComponentInstance[key] = value; - } - - }); - }; - - /** - * Creates a membrane prototype which wraps the original prototype. If any - * property is accessed in an unmounted state, a warning is issued. - * - * @param {object} prototype Original prototype. - * @return {object} The membrane prototype. - * @private - */ - var createMountWarningMembrane = function(prototype) { - var membrane = {}; - var key; - for (key in prototype) { - defineMembraneProperty(membrane, prototype, key); - } - // These are properties that goes into the instance but not the prototype. - // We can create the membrane on the prototype even though this will - // result in a faulty hasOwnProperty check it's better perf. - for (key in componentInstanceProperties) { - if (componentInstanceProperties.hasOwnProperty(key) && - !(key in prototype)) { - defineMembraneProperty(membrane, prototype, key); - } - } - return membrane; - }; - - /** - * Creates a membrane constructor which wraps the component that gets mounted. - * - * @param {function} constructor Original constructor. - * @return {function} The membrane constructor. - * @private - */ - var createDescriptorProxy = function(constructor) { - try { - var ProxyConstructor = function() { - this.__realComponentInstance = new constructor(); - - // We can only safely pass through known instance variables. Unknown - // expandos are not safe. Use the real mounted instance to avoid this - // problem if it blows something up. - Object.freeze(this); - }; - - ProxyConstructor.prototype = createMountWarningMembrane( - constructor.prototype - ); - - return ProxyConstructor; - } catch(x) { - // In IE8 define property will fail on non-DOM objects. If anything in - // the membrane creation fails, we'll bail out and just use the plain - // constructor without warnings. - return constructor; - } - }; - -} - /** * `ReactCompositeComponent` maintains an auxiliary life cycle state in * `this._compositeLifeCycleState` (which can be null). * * This is different from the life cycle state maintained by `ReactComponent` in * `this._lifeCycleState`. The following diagram shows how the states overlap in * time. There are times when the CompositeLifeCycle is null - at those times it * is only meaningful to look at ComponentLifeCycle alone. @@ -5848,53 +6467,36 @@ var CompositeLifeCycle = keyMirror({ /** * @lends {ReactCompositeComponent.prototype} */ var ReactCompositeComponentMixin = { /** * Base constructor for all composite component. * - * @param {?object} initialProps - * @param {*} children + * @param {ReactDescriptor} descriptor * @final * @internal */ - construct: function(initialProps, children) { + construct: function(descriptor) { // Children can be either an array or more than one argument ReactComponent.Mixin.construct.apply(this, arguments); ReactOwner.Mixin.construct.apply(this, arguments); this.state = null; this._pendingState = null; + // This is the public post-processed context. The real context and pending + // context lives on the descriptor. this.context = null; - this._currentContext = ReactContext.current; - this._pendingContext = null; - - // The descriptor that was used to instantiate this component. Will be - // set by the instantiator instead of the constructor since this - // constructor is currently used by both instances and descriptors. - this._descriptor = null; this._compositeLifeCycleState = null; }, /** - * Components in the intermediate state now has cyclic references. To avoid - * breaking JSON serialization we expose a custom JSON format. - * @return {object} JSON compatible representation. - * @internal - * @final - */ - toJSON: function() { - return { type: this.type, props: this.props }; - }, - - /** * 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; @@ -5917,24 +6519,23 @@ var ReactCompositeComponentMixin = { ReactComponent.Mixin.mountComponent.call( this, rootID, transaction, mountDepth ); this._compositeLifeCycleState = CompositeLifeCycle.MOUNTING; - this.context = this._processContext(this._currentContext); - this._defaultProps = this.getDefaultProps ? this.getDefaultProps() : null; - this.props = this._processProps(this.props); - if (this.__reactAutoBindMap) { this._bindAutoBindMethods(); } + this.context = this._processContext(this._descriptor._context); + this.props = this._processProps(this.props); + this.state = this.getInitialState ? this.getInitialState() : null; ("production" !== "development" ? invariant( typeof this.state === 'object' && !Array.isArray(this.state), '%s.getInitialState(): must return an object or null', this.constructor.displayName || 'ReactCompositeComponent' ) : invariant(typeof this.state === 'object' && !Array.isArray(this.state))); this._pendingState = null; @@ -5957,17 +6558,17 @@ var ReactCompositeComponentMixin = { // 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); + transaction.getReactMountReady().enqueue(this.componentDidMount, this); } return markup; } ), /** * Releases any resources allocated by `mountComponent`. * @@ -5976,18 +6577,16 @@ var ReactCompositeComponentMixin = { */ unmountComponent: function() { this._compositeLifeCycleState = CompositeLifeCycle.UNMOUNTING; if (this.componentWillUnmount) { this.componentWillUnmount(); } this._compositeLifeCycleState = null; - this._defaultProps = null; - this._renderedComponent.unmountComponent(); this._renderedComponent = null; ReactComponent.Mixin.unmountComponent.call(this); // Some existing components rely on this.props even after they've been // destroyed (in event handlers). // TODO: this.props = null; @@ -6011,17 +6610,17 @@ var ReactCompositeComponentMixin = { * @final * @protected */ setState: function(partialState, callback) { ("production" !== "development" ? invariant( typeof partialState === 'object' || partialState == null, 'setState(...): takes an object of state variables to update.' ) : invariant(typeof partialState === 'object' || partialState == null)); - if ("production" !== "development") { + if ("production" !== "development"){ ("production" !== "development" ? warning( partialState != null, 'setState(...): You passed an undefined or null state object; ' + 'instead, use forceUpdate().' ) : null); } // Merge with `_pendingState` if it exists, otherwise with existing state. this.replaceState( @@ -6040,17 +6639,25 @@ var ReactCompositeComponentMixin = { * @param {object} completeState Next state. * @param {?function} callback Called after state is updated. * @final * @protected */ replaceState: function(completeState, callback) { validateLifeCycleOnReplaceState(this); this._pendingState = completeState; - ReactUpdates.enqueueUpdate(this, callback); + if (this._compositeLifeCycleState !== CompositeLifeCycle.MOUNTING) { + // If we're in a componentWillMount handler, don't enqueue a rerender + // because ReactUpdates assumes we're in a browser context (which is wrong + // for server rendering) and we're about to do a render anyway. + // TODO: The callback here is ignored when setState is called from + // componentWillMount. Either fix it or disallow doing so completely in + // favor of getInitialState. + ReactUpdates.enqueueUpdate(this, callback); + } }, /** * Filters the context object to only contain keys specified in * `contextTypes`, and asserts that they are valid. * * @param {object} context * @return {?object} @@ -6115,22 +6722,27 @@ var ReactCompositeComponentMixin = { * asserting that the props are valid. Does not mutate its argument; returns * a new props object with defaults merged in. * * @param {object} newProps * @return {object} * @private */ _processProps: function(newProps) { - var props = merge(newProps); - var defaultProps = this._defaultProps; - for (var propName in defaultProps) { - if (typeof props[propName] === 'undefined') { - props[propName] = defaultProps[propName]; - } + var defaultProps = this.constructor.defaultProps; + var props; + if (defaultProps) { + props = merge(newProps); + for (var propName in defaultProps) { + if (typeof props[propName] === 'undefined') { + props[propName] = defaultProps[propName]; + } + } + } else { + props = newProps; } if ("production" !== "development") { var propTypes = this.constructor.propTypes; if (propTypes) { this._checkPropTypes(propTypes, props, ReactPropTypeLocations.prop); } } return props; @@ -6140,210 +6752,220 @@ var ReactCompositeComponentMixin = { * Assert that the props are valid * * @param {object} propTypes Map of prop name to a ReactPropType * @param {object} props * @param {string} location e.g. "prop", "context", "child context" * @private */ _checkPropTypes: function(propTypes, props, location) { + // TODO: Stop validating prop types here and only use the descriptor + // validation. var componentName = this.constructor.displayName; for (var propName in propTypes) { if (propTypes.hasOwnProperty(propName)) { - propTypes[propName](props, propName, componentName, location); - } - } - }, - - performUpdateIfNecessary: function() { + var error = + propTypes[propName](props, propName, componentName, location); + if (error instanceof Error) { + // We may want to extend this logic for similar errors in + // renderComponent calls, so I'm abstracting it away into + // a function to minimize refactoring in the future + var addendum = getDeclarationErrorAddendum(this); + ("production" !== "development" ? warning(false, error.message + addendum) : null); + } + } + } + }, + + /** + * If any of `_pendingDescriptor`, `_pendingState`, or `_pendingForceUpdate` + * is set, update the component. + * + * @param {ReactReconcileTransaction} transaction + * @internal + */ + performUpdateIfNecessary: function(transaction) { var compositeLifeCycleState = this._compositeLifeCycleState; // Do not trigger a state transition if we are in the middle of mounting or // receiving props because both of those will already be doing this. if (compositeLifeCycleState === CompositeLifeCycle.MOUNTING || compositeLifeCycleState === CompositeLifeCycle.RECEIVING_PROPS) { return; } - ReactComponent.Mixin.performUpdateIfNecessary.call(this); - }, - - /** - * If any of `_pendingProps`, `_pendingState`, or `_pendingForceUpdate` is - * set, update the component. - * - * @param {ReactReconcileTransaction} transaction - * @internal - */ - _performUpdateIfNecessary: function(transaction) { - if (this._pendingProps == null && + + if (this._pendingDescriptor == null && this._pendingState == null && - this._pendingContext == null && !this._pendingForceUpdate) { return; } - var nextFullContext = this._pendingContext || this._currentContext; - var nextContext = this._processContext(nextFullContext); - this._pendingContext = null; - + var nextContext = this.context; var nextProps = this.props; - if (this._pendingProps != null) { - nextProps = this._processProps(this._pendingProps); - this._pendingProps = null; + var nextDescriptor = this._descriptor; + if (this._pendingDescriptor != null) { + nextDescriptor = this._pendingDescriptor; + nextContext = this._processContext(nextDescriptor._context); + nextProps = this._processProps(nextDescriptor.props); + this._pendingDescriptor = null; this._compositeLifeCycleState = CompositeLifeCycle.RECEIVING_PROPS; if (this.componentWillReceiveProps) { this.componentWillReceiveProps(nextProps, nextContext); } } this._compositeLifeCycleState = CompositeLifeCycle.RECEIVING_STATE; - // Unlike props, state, and context, we specifically don't want to set - // _pendingOwner to null here because it's possible for a component to have - // a null owner, so we instead make `this._owner === this._pendingOwner` - // mean that there's no owner change pending. - var nextOwner = this._pendingOwner; - var nextState = this._pendingState || this.state; this._pendingState = null; try { - if (this._pendingForceUpdate || - !this.shouldComponentUpdate || - this.shouldComponentUpdate(nextProps, nextState, nextContext)) { + var shouldUpdate = + this._pendingForceUpdate || + !this.shouldComponentUpdate || + this.shouldComponentUpdate(nextProps, nextState, nextContext); + + if ("production" !== "development") { + if (typeof shouldUpdate === "undefined") { + console.warn( + (this.constructor.displayName || 'ReactCompositeComponent') + + '.shouldComponentUpdate(): Returned undefined instead of a ' + + 'boolean value. Make sure to return true or false.' + ); + } + } + + if (shouldUpdate) { this._pendingForceUpdate = false; // Will set `this.props`, `this.state` and `this.context`. this._performComponentUpdate( + nextDescriptor, 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._descriptor = nextDescriptor; this.props = nextProps; - this._owner = nextOwner; this.state = nextState; - this._currentContext = nextFullContext; this.context = nextContext; + + // Owner cannot change because shouldUpdateReactComponent doesn't allow + // it. TODO: Remove this._owner completely. + this._owner = nextDescriptor._owner; } } finally { this._compositeLifeCycleState = null; } }, /** * Merges new props and state, notifies delegate methods of update and * performs update. * - * @param {object} nextProps Next object to set as properties. - * @param {?ReactComponent} nextOwner Next component to set as owner + * @param {ReactDescriptor} nextDescriptor Next descriptor + * @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 * @private */ _performComponentUpdate: function( + nextDescriptor, nextProps, - nextOwner, nextState, - nextFullContext, nextContext, transaction ) { + var prevDescriptor = this._descriptor; var prevProps = this.props; - var prevOwner = this._owner; var prevState = this.state; var prevContext = this.context; if (this.componentWillUpdate) { this.componentWillUpdate(nextProps, nextState, nextContext); } + this._descriptor = nextDescriptor; this.props = nextProps; - this._owner = nextOwner; this.state = nextState; - this._currentContext = nextFullContext; this.context = nextContext; + // Owner cannot change because shouldUpdateReactComponent doesn't allow + // it. TODO: Remove this._owner completely. + this._owner = nextDescriptor._owner; + this.updateComponent( transaction, - prevProps, - prevOwner, - prevState, - prevContext + prevDescriptor ); if (this.componentDidUpdate) { transaction.getReactMountReady().enqueue( - this, - this.componentDidUpdate.bind(this, prevProps, prevState, prevContext) + this.componentDidUpdate.bind(this, prevProps, prevState, prevContext), + this ); } }, - receiveComponent: function(nextComponent, transaction) { - if (nextComponent === this._descriptor) { - // 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. + receiveComponent: function(nextDescriptor, transaction) { + if (nextDescriptor === this._descriptor && + nextDescriptor._owner != null) { + // Since descriptors are immutable after the owner is rendered, + // we can do a cheap identity compare here to determine if this is a + // superfluous reconcile. It's possible for state to be mutable but such + // change should trigger an update of the owner which would recreate + // the descriptor. We explicitly check for the existence of an owner since + // it's possible for a descriptor created outside a composite to be + // deeply mutated and reused. return; } - // Update the descriptor that was last used by this component instance - this._descriptor = nextComponent; - - this._pendingContext = nextComponent._currentContext; ReactComponent.Mixin.receiveComponent.call( this, - nextComponent, + nextDescriptor, transaction ); }, /** * Updates the component's currently mounted DOM representation. * * By default, this implements React's rendering and reconciliation algorithm. * Sophisticated clients may wish to override this. * * @param {ReactReconcileTransaction} transaction - * @param {object} prevProps - * @param {?ReactComponent} prevOwner - * @param {?object} prevState - * @param {?object} prevContext + * @param {ReactDescriptor} prevDescriptor * @internal * @overridable */ updateComponent: ReactPerf.measure( 'ReactCompositeComponent', 'updateComponent', - function(transaction, prevProps, prevOwner, prevState, prevContext) { + function(transaction, prevParentDescriptor) { ReactComponent.Mixin.updateComponent.call( this, transaction, - prevProps, - prevOwner + prevParentDescriptor ); - var prevComponentInstance = this._renderedComponent; - var nextComponent = this._renderValidatedComponent(); - if (shouldUpdateReactComponent(prevComponentInstance, nextComponent)) { - prevComponentInstance.receiveComponent(nextComponent, transaction); + var prevDescriptor = prevComponentInstance._descriptor; + var nextDescriptor = this._renderValidatedComponent(); + if (shouldUpdateReactComponent(prevDescriptor, nextDescriptor)) { + prevComponentInstance.receiveComponent(nextDescriptor, transaction); } else { // These two IDs are actually the same! But nothing should rely on that. var thisID = this._rootNodeID; var prevComponentID = prevComponentInstance._rootNodeID; prevComponentInstance.unmountComponent(); - this._renderedComponent = instantiateReactComponent(nextComponent); + this._renderedComponent = instantiateReactComponent(nextDescriptor); var nextMarkup = this._renderedComponent.mountComponent( thisID, transaction, this._mountDepth + 1 ); ReactComponent.BackendIDOperations.dangerouslyReplaceNodeWithMarkupByID( prevComponentID, nextMarkup @@ -6390,30 +7012,38 @@ var ReactCompositeComponentMixin = { * @private */ _renderValidatedComponent: ReactPerf.measure( 'ReactCompositeComponent', '_renderValidatedComponent', function() { var renderedComponent; var previousContext = ReactContext.current; - ReactContext.current = this._processChildContext(this._currentContext); + ReactContext.current = this._processChildContext( + this._descriptor._context + ); ReactCurrentOwner.current = this; try { renderedComponent = this.render(); + if (renderedComponent === null || renderedComponent === false) { + renderedComponent = ReactEmptyComponent.getEmptyComponent(); + ReactEmptyComponent.registerNullComponentID(this._rootNodeID); + } else { + ReactEmptyComponent.deregisterNullComponentID(this._rootNodeID); + } } finally { ReactContext.current = previousContext; ReactCurrentOwner.current = null; } ("production" !== "development" ? invariant( - ReactComponent.isValidComponent(renderedComponent), + ReactDescriptor.isValidDescriptor(renderedComponent), '%s.render(): A valid ReactComponent must be returned. You may have ' + - 'returned null, undefined, an array, or some other invalid object.', + 'returned undefined, an array or some other invalid object.', this.constructor.displayName || 'ReactCompositeComponent' - ) : invariant(ReactComponent.isValidComponent(renderedComponent))); + ) : invariant(ReactDescriptor.isValidDescriptor(renderedComponent))); return renderedComponent; } ), /** * @private */ _bindAutoBindMethods: function() { @@ -6478,28 +7108,16 @@ var ReactCompositeComponentMixin = { 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 = { @@ -6511,36 +7129,32 @@ var ReactCompositeComponent = { /** * Creates a composite component class given a class specification. * * @param {object} spec Class specification (which must define `render`). * @return {function} Component constructor function. * @public */ createClass: function(spec) { - var Constructor = function() {}; + var Constructor = function(props, owner) { + this.construct(props, owner); + }; Constructor.prototype = new ReactCompositeComponentBase(); Constructor.prototype.constructor = Constructor; - var DescriptorConstructor = Constructor; - - var ConvenienceConstructor = function(props, children) { - var descriptor = new DescriptorConstructor(); - descriptor.construct.apply(descriptor, arguments); - return descriptor; - }; - ConvenienceConstructor.componentConstructor = Constructor; - Constructor.ConvenienceConstructor = ConvenienceConstructor; - ConvenienceConstructor.originalSpec = spec; - injectedMixins.forEach( - mixSpecIntoComponent.bind(null, ConvenienceConstructor) - ); - - mixSpecIntoComponent(ConvenienceConstructor, spec); + mixSpecIntoComponent.bind(null, Constructor) + ); + + mixSpecIntoComponent(Constructor, spec); + + // Initialize the defaultProps property after all mixins have been merged + if (Constructor.getDefaultProps) { + Constructor.defaultProps = Constructor.getDefaultProps(); + } ("production" !== "development" ? invariant( Constructor.prototype.render, 'createClass(...): Class specification must implement a `render` method.' ) : invariant(Constructor.prototype.render)); if ("production" !== "development") { if (Constructor.prototype.componentShouldUpdate) { @@ -6552,53 +7166,46 @@ var ReactCompositeComponent = { (spec.displayName || 'A component') + ' has a method called ' + 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' + 'The name is phrased as a question because the function is ' + 'expected to return a value.' ); } } - // Expose the convience constructor on the prototype so that it can be - // easily accessed on descriptors. E.g. <Foo />.type === Foo.type and for - // static methods like <Foo />.type.staticMethod(); - // This should not be named constructor since this may not be the function - // that created the descriptor, and it may not even be a constructor. - ConvenienceConstructor.type = Constructor; - Constructor.prototype.type = Constructor; - // Reduce time spent doing lookups by setting these on the prototype. for (var methodName in ReactCompositeComponentInterface) { if (!Constructor.prototype[methodName]) { Constructor.prototype[methodName] = null; } } + var descriptorFactory = ReactDescriptor.createFactory(Constructor); + if ("production" !== "development") { - // In DEV the convenience constructor generates a proxy to another - // instance around it to warn about access to properties on the - // descriptor. - DescriptorConstructor = createDescriptorProxy(Constructor); - } - - return ConvenienceConstructor; - }, - - isValidClass: isValidClass, + return ReactDescriptorValidator.createFactory( + descriptorFactory, + Constructor.propTypes, + Constructor.contextTypes + ); + } + + return descriptorFactory; + }, injection: { injectMixin: function(mixin) { injectedMixins.push(mixin); } } }; module.exports = ReactCompositeComponent; -},{"./ReactComponent":31,"./ReactContext":34,"./ReactCurrentOwner":35,"./ReactErrorUtils":51,"./ReactOwner":64,"./ReactPerf":65,"./ReactPropTransferer":66,"./ReactPropTypeLocationNames":67,"./ReactPropTypeLocations":68,"./ReactUpdates":81,"./instantiateReactComponent":124,"./invariant":125,"./keyMirror":131,"./merge":134,"./mixInto":137,"./monitorCodeUse":138,"./objMap":139,"./shouldUpdateReactComponent":144,"./warning":148}],34:[function(_dereq_,module,exports){ +},{"./ReactComponent":35,"./ReactContext":39,"./ReactCurrentOwner":40,"./ReactDescriptor":56,"./ReactDescriptorValidator":57,"./ReactEmptyComponent":58,"./ReactErrorUtils":59,"./ReactOwner":70,"./ReactPerf":71,"./ReactPropTransferer":72,"./ReactPropTypeLocationNames":73,"./ReactPropTypeLocations":74,"./ReactUpdates":87,"./instantiateReactComponent":133,"./invariant":134,"./keyMirror":140,"./mapObject":142,"./merge":144,"./mixInto":147,"./monitorCodeUse":148,"./shouldUpdateReactComponent":154,"./warning":158}],39:[function(_dereq_,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 @@ -6657,17 +7264,17 @@ var ReactContext = { } return result; } }; module.exports = ReactContext; -},{"./merge":134}],35:[function(_dereq_,module,exports){ +},{"./merge":144}],40:[function(_dereq_,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 @@ -6698,17 +7305,17 @@ var ReactCurrentOwner = { * @type {ReactComponent} */ current: null }; module.exports = ReactCurrentOwner; -},{}],36:[function(_dereq_,module,exports){ +},{}],41:[function(_dereq_,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 @@ -6720,66 +7327,64 @@ module.exports = ReactCurrentOwner; * limitations under the License. * * @providesModule ReactDOM * @typechecks static-only */ "use strict"; +var ReactDescriptor = _dereq_("./ReactDescriptor"); +var ReactDescriptorValidator = _dereq_("./ReactDescriptorValidator"); var ReactDOMComponent = _dereq_("./ReactDOMComponent"); var mergeInto = _dereq_("./mergeInto"); -var objMapKeyVal = _dereq_("./objMapKeyVal"); +var mapObject = _dereq_("./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. * + * @param {boolean} omitClose True if the close tag should be omitted. * @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() {}; +function createDOMComponentClass(omitClose, tag) { + var Constructor = function(descriptor) { + this.construct(descriptor); + }; Constructor.prototype = new ReactDOMComponent(tag, omitClose); Constructor.prototype.constructor = Constructor; Constructor.displayName = tag; - var ConvenienceConstructor = function(props, children) { - var instance = new Constructor(); - instance.construct.apply(instance, arguments); - return instance; - }; - - // Expose the constructor on the ConvenienceConstructor and prototype so that - // it can be easily easily accessed on descriptors. - // E.g. <div />.type === div.type - ConvenienceConstructor.type = Constructor; - Constructor.prototype.type = Constructor; - - Constructor.ConvenienceConstructor = ConvenienceConstructor; - ConvenienceConstructor.componentConstructor = Constructor; + var ConvenienceConstructor = ReactDescriptor.createFactory(Constructor); + + if ("production" !== "development") { + return ReactDescriptorValidator.createFactory( + ConvenienceConstructor + ); + } + return ConvenienceConstructor; } /** * Creates a mapping from supported HTML tags to `ReactDOMComponent` classes. * This is also accessible via `React.DOM`. * * @public */ -var ReactDOM = objMapKeyVal({ +var ReactDOM = mapObject({ a: false, abbr: false, address: false, area: true, article: false, aside: false, audio: false, b: false, @@ -6884,40 +7489,44 @@ var ReactDOM = objMapKeyVal({ ul: false, 'var': false, video: false, wbr: true, // SVG circle: false, defs: false, + ellipse: false, g: false, line: false, linearGradient: false, + mask: false, path: false, + pattern: false, polygon: false, polyline: false, radialGradient: false, rect: false, stop: false, svg: false, - text: false + text: false, + tspan: false }, createDOMComponentClass); var injection = { injectComponentClasses: function(componentClasses) { mergeInto(ReactDOM, componentClasses); } }; ReactDOM.injection = injection; module.exports = ReactDOM; -},{"./ReactDOMComponent":38,"./mergeInto":136,"./objMapKeyVal":140}],37:[function(_dereq_,module,exports){ +},{"./ReactDOMComponent":43,"./ReactDescriptor":56,"./ReactDescriptorValidator":57,"./mapObject":142,"./mergeInto":146}],42:[function(_dereq_,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 @@ -6978,17 +7587,17 @@ var ReactDOMButton = ReactCompositeCompo return button(props, this.props.children); } }); module.exports = ReactDOMButton; -},{"./AutoFocusMixin":1,"./ReactBrowserComponentMixin":27,"./ReactCompositeComponent":33,"./ReactDOM":36,"./keyMirror":131}],38:[function(_dereq_,module,exports){ +},{"./AutoFocusMixin":1,"./ReactBrowserComponentMixin":30,"./ReactCompositeComponent":38,"./ReactDOM":41,"./keyMirror":140}],43:[function(_dereq_,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 @@ -7005,30 +7614,30 @@ module.exports = ReactDOMButton; "use strict"; var CSSPropertyOperations = _dereq_("./CSSPropertyOperations"); var DOMProperty = _dereq_("./DOMProperty"); var DOMPropertyOperations = _dereq_("./DOMPropertyOperations"); var ReactBrowserComponentMixin = _dereq_("./ReactBrowserComponentMixin"); var ReactComponent = _dereq_("./ReactComponent"); -var ReactEventEmitter = _dereq_("./ReactEventEmitter"); +var ReactBrowserEventEmitter = _dereq_("./ReactBrowserEventEmitter"); var ReactMount = _dereq_("./ReactMount"); var ReactMultiChild = _dereq_("./ReactMultiChild"); var ReactPerf = _dereq_("./ReactPerf"); var escapeTextForBrowser = _dereq_("./escapeTextForBrowser"); var invariant = _dereq_("./invariant"); var keyOf = _dereq_("./keyOf"); var merge = _dereq_("./merge"); var mixInto = _dereq_("./mixInto"); -var deleteListener = ReactEventEmitter.deleteListener; -var listenTo = ReactEventEmitter.listenTo; -var registrationNameModules = ReactEventEmitter.registrationNameModules; +var deleteListener = ReactBrowserEventEmitter.deleteListener; +var listenTo = ReactBrowserEventEmitter.listenTo; +var registrationNameModules = ReactBrowserEventEmitter.registrationNameModules; // For quickly matching children type, to test if can be treated as content. var CONTENT_TYPES = {'string': true, 'number': true}; var STYLE = keyOf({style: null}); var ELEMENT_NODE_TYPE = 1; @@ -7128,17 +7737,17 @@ ReactDOMComponent.Mixin = { for (var propKey in props) { if (!props.hasOwnProperty(propKey)) { continue; } var propValue = props[propKey]; if (propValue == null) { continue; } - if (registrationNameModules[propKey]) { + if (registrationNameModules.hasOwnProperty(propKey)) { putListener(this._rootNodeID, propKey, propValue, transaction); } else { if (propKey === STYLE) { if (propValue) { propValue = props.style = merge(props.style); } propValue = CSSPropertyOperations.createMarkupForStyles(propValue); } @@ -7186,55 +7795,57 @@ ReactDOMComponent.Mixin = { transaction ); return mountImages.join(''); } } return ''; }, - 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. - - // TODO: compare the descriptor + receiveComponent: function(nextDescriptor, transaction) { + if (nextDescriptor === this._descriptor && + nextDescriptor._owner != null) { + // Since descriptors are immutable after the owner is rendered, + // we can do a cheap identity compare here to determine if this is a + // superfluous reconcile. It's possible for state to be mutable but such + // change should trigger an update of the owner which would recreate + // the descriptor. We explicitly check for the existence of an owner since + // it's possible for a descriptor created outside a composite to be + // deeply mutated and reused. return; } - assertValidProps(nextComponent.props); ReactComponent.Mixin.receiveComponent.call( this, - nextComponent, + nextDescriptor, transaction ); }, /** * Updates a native DOM component after it has already been allocated and * attached to the DOM. Reconciles the root DOM node, then recurses. * * @param {ReactReconcileTransaction} transaction - * @param {object} prevProps + * @param {ReactDescriptor} prevDescriptor * @internal * @overridable */ updateComponent: ReactPerf.measure( 'ReactDOMComponent', 'updateComponent', - function(transaction, prevProps, prevOwner) { + function(transaction, prevDescriptor) { + assertValidProps(this._descriptor.props); ReactComponent.Mixin.updateComponent.call( this, transaction, - prevProps, - prevOwner + prevDescriptor ); - this._updateDOMProperties(prevProps, transaction); - this._updateDOMChildren(prevProps, transaction); + this._updateDOMProperties(prevDescriptor.props, transaction); + this._updateDOMChildren(prevDescriptor.props, transaction); } ), /** * Reconciles the properties by detecting differences in property values and * updating the DOM as necessary. This function is probably the single most * critical path for performance optimization. * @@ -7261,17 +7872,17 @@ ReactDOMComponent.Mixin = { if (propKey === STYLE) { var lastStyle = lastProps[propKey]; for (styleName in lastStyle) { if (lastStyle.hasOwnProperty(styleName)) { styleUpdates = styleUpdates || {}; styleUpdates[styleName] = ''; } } - } else if (registrationNameModules[propKey]) { + } else if (registrationNameModules.hasOwnProperty(propKey)) { deleteListener(this._rootNodeID, propKey); } else if ( DOMProperty.isStandardName[propKey] || DOMProperty.isCustomAttribute(propKey)) { ReactComponent.BackendIDOperations.deletePropertyByID( this._rootNodeID, propKey ); @@ -7286,34 +7897,34 @@ ReactDOMComponent.Mixin = { if (propKey === STYLE) { if (nextProp) { nextProp = nextProps.style = merge(nextProp); } if (lastProp) { // Unset styles on `lastProp` but not on `nextProp`. for (styleName in lastProp) { if (lastProp.hasOwnProperty(styleName) && - !nextProp.hasOwnProperty(styleName)) { + (!nextProp || !nextProp.hasOwnProperty(styleName))) { styleUpdates = styleUpdates || {}; styleUpdates[styleName] = ''; } } // Update styles that changed since `lastProp`. for (styleName in nextProp) { if (nextProp.hasOwnProperty(styleName) && lastProp[styleName] !== nextProp[styleName]) { styleUpdates = styleUpdates || {}; styleUpdates[styleName] = nextProp[styleName]; } } } else { // Relies on `updateStylesByID` not mutating `styleUpdates`. styleUpdates = nextProp; } - } else if (registrationNameModules[propKey]) { + } else if (registrationNameModules.hasOwnProperty(propKey)) { putListener(this._rootNodeID, propKey, nextProp, transaction); } else if ( DOMProperty.isStandardName[propKey] || DOMProperty.isCustomAttribute(propKey)) { ReactComponent.BackendIDOperations.updatePropertyByID( this._rootNodeID, propKey, nextProp @@ -7383,30 +7994,30 @@ ReactDOMComponent.Mixin = { /** * Destroys all event registrations for this instance. Does not remove from * the DOM. That must be done by the parent. * * @internal */ unmountComponent: function() { this.unmountChildren(); - ReactEventEmitter.deleteAllListeners(this._rootNodeID); + ReactBrowserEventEmitter.deleteAllListeners(this._rootNodeID); ReactComponent.Mixin.unmountComponent.call(this); } }; mixInto(ReactDOMComponent, ReactComponent.Mixin); mixInto(ReactDOMComponent, ReactDOMComponent.Mixin); mixInto(ReactDOMComponent, ReactMultiChild.Mixin); mixInto(ReactDOMComponent, ReactBrowserComponentMixin); module.exports = ReactDOMComponent; -},{"./CSSPropertyOperations":4,"./DOMProperty":9,"./DOMPropertyOperations":10,"./ReactBrowserComponentMixin":27,"./ReactComponent":31,"./ReactEventEmitter":52,"./ReactMount":60,"./ReactMultiChild":62,"./ReactPerf":65,"./escapeTextForBrowser":111,"./invariant":125,"./keyOf":132,"./merge":134,"./mixInto":137}],39:[function(_dereq_,module,exports){ +},{"./CSSPropertyOperations":5,"./DOMProperty":11,"./DOMPropertyOperations":12,"./ReactBrowserComponentMixin":30,"./ReactBrowserEventEmitter":31,"./ReactComponent":35,"./ReactMount":67,"./ReactMultiChild":68,"./ReactPerf":71,"./escapeTextForBrowser":118,"./invariant":134,"./keyOf":141,"./merge":144,"./mixInto":147}],44:[function(_dereq_,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 @@ -7417,60 +8028,52 @@ module.exports = ReactDOMComponent; * See the License for the specific language governing permissions and * limitations under the License. * * @providesModule ReactDOMForm */ "use strict"; +var EventConstants = _dereq_("./EventConstants"); +var LocalEventTrapMixin = _dereq_("./LocalEventTrapMixin"); var ReactBrowserComponentMixin = _dereq_("./ReactBrowserComponentMixin"); var ReactCompositeComponent = _dereq_("./ReactCompositeComponent"); var ReactDOM = _dereq_("./ReactDOM"); -var ReactEventEmitter = _dereq_("./ReactEventEmitter"); -var EventConstants = _dereq_("./EventConstants"); // Store a reference to the <form> `ReactDOMComponent`. var form = ReactDOM.form; /** * Since onSubmit doesn't bubble OR capture on the top level in IE8, we need * to capture it on the <form> element itself. There are lots of hacks we could * do to accomplish this, but the most reliable is to make <form> a * composite component and use `componentDidMount` to attach the event handlers. */ var ReactDOMForm = ReactCompositeComponent.createClass({ displayName: 'ReactDOMForm', - mixins: [ReactBrowserComponentMixin], + mixins: [ReactBrowserComponentMixin, LocalEventTrapMixin], render: function() { // TODO: Instead of using `ReactDOM` directly, we should use JSX. However, // `jshint` fails to parse JSX so in order for linting to work in the open // source repo, we need to just use `ReactDOM.form`. return this.transferPropsTo(form(null, this.props.children)); }, componentDidMount: function() { - ReactEventEmitter.trapBubbledEvent( - EventConstants.topLevelTypes.topReset, - 'reset', - this.getDOMNode() - ); - ReactEventEmitter.trapBubbledEvent( - EventConstants.topLevelTypes.topSubmit, - 'submit', - this.getDOMNode() - ); + this.trapBubbledEvent(EventConstants.topLevelTypes.topReset, 'reset'); + this.trapBubbledEvent(EventConstants.topLevelTypes.topSubmit, 'submit'); } }); module.exports = ReactDOMForm; -},{"./EventConstants":15,"./ReactBrowserComponentMixin":27,"./ReactCompositeComponent":33,"./ReactDOM":36,"./ReactEventEmitter":52}],40:[function(_dereq_,module,exports){ +},{"./EventConstants":16,"./LocalEventTrapMixin":26,"./ReactBrowserComponentMixin":30,"./ReactCompositeComponent":38,"./ReactDOM":41}],45:[function(_dereq_,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 @@ -7491,31 +8094,30 @@ module.exports = ReactDOMForm; var CSSPropertyOperations = _dereq_("./CSSPropertyOperations"); var DOMChildrenOperations = _dereq_("./DOMChildrenOperations"); var DOMPropertyOperations = _dereq_("./DOMPropertyOperations"); var ReactMount = _dereq_("./ReactMount"); var ReactPerf = _dereq_("./ReactPerf"); var invariant = _dereq_("./invariant"); +var setInnerHTML = _dereq_("./setInnerHTML"); /** * Errors for properties that should not be updated with `updatePropertyById()`. * * @type {object} * @private */ var INVALID_PROPERTY_ERRORS = { dangerouslySetInnerHTML: '`dangerouslySetInnerHTML` must be set using `updateInnerHTMLByID()`.', style: '`style` must be set using `updateStylesByID()`.' }; -var useWhitespaceWorkaround; - /** * Operations used to process updates to DOM nodes. This is made injectable via * `ReactComponent.BackendIDOperations`. */ var ReactDOMIDOperations = { /** * Updates a DOM node with new property values. This should only be used to @@ -7594,45 +8196,17 @@ var ReactDOMIDOperations = { * @param {string} html An HTML string. * @internal */ updateInnerHTMLByID: ReactPerf.measure( 'ReactDOMIDOperations', 'updateInnerHTMLByID', function(id, html) { var node = ReactMount.getNode(id); - - // IE8: When updating a just created node with innerHTML only leading - // whitespace is removed. When updating an existing node with innerHTML - // whitespace in root TextNodes is also collapsed. - // @see quirksmode.org/bugreports/archives/2004/11/innerhtml_and_t.html - - if (useWhitespaceWorkaround === undefined) { - // Feature detection; only IE8 is known to behave improperly like this. - var temp = document.createElement('div'); - temp.innerHTML = ' '; - useWhitespaceWorkaround = temp.innerHTML === ''; - } - - if (useWhitespaceWorkaround) { - // Magic theory: IE8 supposedly differentiates between added and updated - // nodes when processing innerHTML, innerHTML on updated nodes suffers - // from worse whitespace behavior. Re-adding a node like this triggers - // the initial and more favorable whitespace behavior. - node.parentNode.replaceChild(node, node); - } - - if (useWhitespaceWorkaround && html.match(/^[ \r\n\t\f]/)) { - // Recover leading whitespace by temporarily prepending any character. - // \uFEFF has the potential advantage of being zero-width/invisible. - node.innerHTML = '\uFEFF' + html; - node.firstChild.deleteData(0, 1); - } else { - node.innerHTML = html; - } + setInnerHTML(node, html); } ), /** * Updates a DOM node's text content set by `props.content`. * * @param {string} id ID of the node to update. * @param {string} content Text content. @@ -7680,17 +8254,17 @@ var ReactDOMIDOperations = { } DOMChildrenOperations.processUpdates(updates, markup); } ) }; module.exports = ReactDOMIDOperations; -},{"./CSSPropertyOperations":4,"./DOMChildrenOperations":8,"./DOMPropertyOperations":10,"./ReactMount":60,"./ReactPerf":65,"./invariant":125}],41:[function(_dereq_,module,exports){ +},{"./CSSPropertyOperations":5,"./DOMChildrenOperations":10,"./DOMPropertyOperations":12,"./ReactMount":67,"./ReactPerf":71,"./invariant":134,"./setInnerHTML":152}],46:[function(_dereq_,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 @@ -7701,59 +8275,50 @@ module.exports = ReactDOMIDOperations; * See the License for the specific language governing permissions and * limitations under the License. * * @providesModule ReactDOMImg */ "use strict"; +var EventConstants = _dereq_("./EventConstants"); +var LocalEventTrapMixin = _dereq_("./LocalEventTrapMixin"); var ReactBrowserComponentMixin = _dereq_("./ReactBrowserComponentMixin"); var ReactCompositeComponent = _dereq_("./ReactCompositeComponent"); var ReactDOM = _dereq_("./ReactDOM"); -var ReactEventEmitter = _dereq_("./ReactEventEmitter"); -var EventConstants = _dereq_("./EventConstants"); // Store a reference to the <img> `ReactDOMComponent`. var img = ReactDOM.img; /** * Since onLoad doesn't bubble OR capture on the top level in IE8, we need to * capture it on the <img> element itself. There are lots of hacks we could do * to accomplish this, but the most reliable is to make <img> a composite * component and use `componentDidMount` to attach the event handlers. */ var ReactDOMImg = ReactCompositeComponent.createClass({ displayName: 'ReactDOMImg', tagName: 'IMG', - mixins: [ReactBrowserComponentMixin], + mixins: [ReactBrowserComponentMixin, LocalEventTrapMixin], render: function() { return img(this.props); }, componentDidMount: function() { - var node = this.getDOMNode(); - ReactEventEmitter.trapBubbledEvent( - EventConstants.topLevelTypes.topLoad, - 'load', - node - ); - ReactEventEmitter.trapBubbledEvent( - EventConstants.topLevelTypes.topError, - 'error', - node - ); + this.trapBubbledEvent(EventConstants.topLevelTypes.topLoad, 'load'); + this.trapBubbledEvent(EventConstants.topLevelTypes.topError, 'error'); } }); module.exports = ReactDOMImg; -},{"./EventConstants":15,"./ReactBrowserComponentMixin":27,"./ReactCompositeComponent":33,"./ReactDOM":36,"./ReactEventEmitter":52}],42:[function(_dereq_,module,exports){ +},{"./EventConstants":16,"./LocalEventTrapMixin":26,"./ReactBrowserComponentMixin":30,"./ReactCompositeComponent":38,"./ReactDOM":41}],47:[function(_dereq_,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 @@ -7927,17 +8492,17 @@ var ReactDOMInput = ReactCompositeCompon return returnValue; } }); module.exports = ReactDOMInput; -},{"./AutoFocusMixin":1,"./DOMPropertyOperations":10,"./LinkedValueUtils":23,"./ReactBrowserComponentMixin":27,"./ReactCompositeComponent":33,"./ReactDOM":36,"./ReactMount":60,"./invariant":125,"./merge":134}],43:[function(_dereq_,module,exports){ +},{"./AutoFocusMixin":1,"./DOMPropertyOperations":12,"./LinkedValueUtils":25,"./ReactBrowserComponentMixin":30,"./ReactCompositeComponent":38,"./ReactDOM":41,"./ReactMount":67,"./invariant":134,"./merge":144}],48:[function(_dereq_,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 @@ -7984,17 +8549,17 @@ var ReactDOMOption = ReactCompositeCompo render: function() { return option(this.props, this.props.children); } }); module.exports = ReactDOMOption; -},{"./ReactBrowserComponentMixin":27,"./ReactCompositeComponent":33,"./ReactDOM":36,"./warning":148}],44:[function(_dereq_,module,exports){ +},{"./ReactBrowserComponentMixin":30,"./ReactCompositeComponent":38,"./ReactDOM":41,"./warning":158}],49:[function(_dereq_,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 @@ -8011,44 +8576,43 @@ module.exports = ReactDOMOption; "use strict"; var AutoFocusMixin = _dereq_("./AutoFocusMixin"); var LinkedValueUtils = _dereq_("./LinkedValueUtils"); var ReactBrowserComponentMixin = _dereq_("./ReactBrowserComponentMixin"); var ReactCompositeComponent = _dereq_("./ReactCompositeComponent"); var ReactDOM = _dereq_("./ReactDOM"); -var invariant = _dereq_("./invariant"); var merge = _dereq_("./merge"); // Store a reference to the <select> `ReactDOMComponent`. var select = ReactDOM.select; /** * Validation function for `value` and `defaultValue`. * @private */ function selectValueType(props, propName, componentName) { if (props[propName] == null) { return; } if (props.multiple) { - ("production" !== "development" ? invariant( - Array.isArray(props[propName]), - 'The `%s` prop supplied to <select> must be an array if `multiple` is ' + - 'true.', - propName - ) : invariant(Array.isArray(props[propName]))); + if (!Array.isArray(props[propName])) { + return new Error( + ("The `" + propName + "` prop supplied to <select> must be an array if ") + + ("`multiple` is true.") + ); + } } else { - ("production" !== "development" ? invariant( - !Array.isArray(props[propName]), - 'The `%s` prop supplied to <select> must be a scalar value if ' + - '`multiple` is false.', - propName - ) : invariant(!Array.isArray(props[propName]))); + if (Array.isArray(props[propName])) { + return new Error( + ("The `" + propName + "` prop supplied to <select> must be a scalar ") + + ("value if `multiple` is false.") + ); + } } } /** * If `value` is supplied, updates <option> elements on mount and update. * @param {ReactComponent} component Instance of ReactDOMSelect * @param {?*} propValue For uncontrolled components, null/undefined. For * controlled components, a string (or with `multiple`, a list of strings). @@ -8129,19 +8693,21 @@ var ReactDOMSelect = ReactCompositeCompo return select(props, this.props.children); }, componentDidMount: function() { updateOptions(this, LinkedValueUtils.getValue(this)); }, - componentDidUpdate: function() { + componentDidUpdate: function(prevProps) { var value = LinkedValueUtils.getValue(this); - if (value != null) { + var prevMultiple = !!prevProps.multiple; + var multiple = !!this.props.multiple; + if (value != null || prevMultiple !== multiple) { updateOptions(this, value); } }, _handleChange: function(event) { var returnValue; var onChange = LinkedValueUtils.getOnChange(this); if (onChange) { @@ -8166,17 +8732,17 @@ var ReactDOMSelect = ReactCompositeCompo this.setState({value: selectedValue}); return returnValue; } }); module.exports = ReactDOMSelect; -},{"./AutoFocusMixin":1,"./LinkedValueUtils":23,"./ReactBrowserComponentMixin":27,"./ReactCompositeComponent":33,"./ReactDOM":36,"./invariant":125,"./merge":134}],45:[function(_dereq_,module,exports){ +},{"./AutoFocusMixin":1,"./LinkedValueUtils":25,"./ReactBrowserComponentMixin":30,"./ReactCompositeComponent":38,"./ReactDOM":41,"./merge":144}],50:[function(_dereq_,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 @@ -8187,20 +8753,31 @@ module.exports = ReactDOMSelect; * See the License for the specific language governing permissions and * limitations under the License. * * @providesModule ReactDOMSelection */ "use strict"; +var ExecutionEnvironment = _dereq_("./ExecutionEnvironment"); + var getNodeForCharacterOffset = _dereq_("./getNodeForCharacterOffset"); var getTextContentAccessor = _dereq_("./getTextContentAccessor"); /** + * While `isCollapsed` is available on the Selection object and `collapsed` + * is available on the Range object, IE11 sometimes gets them wrong. + * If the anchor/focus nodes and offsets are the same, the range is collapsed. + */ +function isCollapsed(anchorNode, anchorOffset, focusNode, focusOffset) { + return anchorNode === focusNode && anchorOffset === focusOffset; +} + +/** * Get the appropriate anchor and focus node/offset pairs for IE. * * The catch here is that IE's selection API doesn't provide information * about whether the selection is forward or backward, so we have to * behave as though it's always forward. * * IE text differs from modern selection in that it behaves as though * block elements end with a new line. This means character offsets will @@ -8240,23 +8817,41 @@ function getModernOffsets(node) { } var anchorNode = selection.anchorNode; var anchorOffset = selection.anchorOffset; var focusNode = selection.focusNode; var focusOffset = selection.focusOffset; var currentRange = selection.getRangeAt(0); - var rangeLength = currentRange.toString().length; + + // If the node and offset values are the same, the selection is collapsed. + // `Selection.isCollapsed` is available natively, but IE sometimes gets + // this value wrong. + var isSelectionCollapsed = isCollapsed( + selection.anchorNode, + selection.anchorOffset, + selection.focusNode, + selection.focusOffset + ); + + var rangeLength = isSelectionCollapsed ? 0 : currentRange.toString().length; var tempRange = currentRange.cloneRange(); tempRange.selectNodeContents(node); tempRange.setEnd(currentRange.startContainer, currentRange.startOffset); - var start = tempRange.toString().length; + var isTempRangeCollapsed = isCollapsed( + tempRange.startContainer, + tempRange.startOffset, + tempRange.endContainer, + tempRange.endOffset + ); + + var start = isTempRangeCollapsed ? 0 : tempRange.toString().length; var end = start + rangeLength; // Detect whether the selection is backward. var detectionRange = document.createRange(); detectionRange.setStart(anchorNode, anchorOffset); detectionRange.setEnd(focusNode, focusOffset); var isBackward = detectionRange.collapsed; detectionRange.detach(); @@ -8336,38 +8931,34 @@ function setModernOffsets(node, offsets) range.setEnd(endMarker.node, endMarker.offset); selection.addRange(range); } range.detach(); } } +var useIEOffsets = ExecutionEnvironment.canUseDOM && document.selection; + var ReactDOMSelection = { /** * @param {DOMElement} node */ - getOffsets: function(node) { - var getOffsets = document.selection ? getIEOffsets : getModernOffsets; - return getOffsets(node); - }, + getOffsets: useIEOffsets ? getIEOffsets : getModernOffsets, /** * @param {DOMElement|DOMTextNode} node * @param {object} offsets */ - setOffsets: function(node, offsets) { - var setOffsets = document.selection ? setIEOffsets : setModernOffsets; - setOffsets(node, offsets); - } + setOffsets: useIEOffsets ? setIEOffsets : setModernOffsets }; module.exports = ReactDOMSelection; -},{"./getNodeForCharacterOffset":119,"./getTextContentAccessor":121}],46:[function(_dereq_,module,exports){ +},{"./ExecutionEnvironment":22,"./getNodeForCharacterOffset":127,"./getTextContentAccessor":129}],51:[function(_dereq_,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 @@ -8448,38 +9039,36 @@ var ReactDOMTextarea = ReactCompositeCom defaultValue = ''; } var value = LinkedValueUtils.getValue(this); return { // We save the initial value so that `ReactDOMComponent` doesn't update // `textContent` (unnecessary since we update value). // The initial value can be a boolean or object so that's why it's // forced to be a string. - initialValue: '' + (value != null ? value : defaultValue), - value: defaultValue + initialValue: '' + (value != null ? value : defaultValue) }; }, shouldComponentUpdate: function() { // Defer any updates to this component during the `onChange` handler. return !this._isChanging; }, render: function() { // Clone `this.props` so we don't mutate the input. var props = merge(this.props); - var value = LinkedValueUtils.getValue(this); ("production" !== "development" ? invariant( props.dangerouslySetInnerHTML == null, '`dangerouslySetInnerHTML` does not make sense on <textarea>.' ) : invariant(props.dangerouslySetInnerHTML == null)); props.defaultValue = null; - props.value = value != null ? value : this.state.value; + props.value = null; props.onChange = this._handleChange; // Always set children to the same thing. In IE9, the selection range will // get reset if `textContent` is mutated. return textarea(props, this.state.initialValue); }, componentDidUpdate: function(prevProps, prevState, prevContext) { @@ -8503,17 +9092,17 @@ var ReactDOMTextarea = ReactCompositeCom this.setState({value: event.target.value}); return returnValue; } }); module.exports = ReactDOMTextarea; -},{"./AutoFocusMixin":1,"./DOMPropertyOperations":10,"./LinkedValueUtils":23,"./ReactBrowserComponentMixin":27,"./ReactCompositeComponent":33,"./ReactDOM":36,"./invariant":125,"./merge":134,"./warning":148}],47:[function(_dereq_,module,exports){ +},{"./AutoFocusMixin":1,"./DOMPropertyOperations":12,"./LinkedValueUtils":25,"./ReactBrowserComponentMixin":30,"./ReactCompositeComponent":38,"./ReactDOM":41,"./invariant":134,"./merge":144,"./warning":158}],52:[function(_dereq_,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 @@ -8564,33 +9153,33 @@ var transaction = new ReactDefaultBatchi var ReactDefaultBatchingStrategy = { isBatchingUpdates: false, /** * Call the provided function in a context within which calls to `setState` * and friends are batched such that components aren't updated unnecessarily. */ - batchedUpdates: function(callback, param) { + batchedUpdates: function(callback, a, b) { var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates; ReactDefaultBatchingStrategy.isBatchingUpdates = true; // The code is written this way to avoid extra allocations if (alreadyBatchingUpdates) { - callback(param); + callback(a, b); } else { - transaction.perform(callback, null, param); + transaction.perform(callback, null, a, b); } } }; module.exports = ReactDefaultBatchingStrategy; -},{"./ReactUpdates":81,"./Transaction":96,"./emptyFunction":109,"./mixInto":137}],48:[function(_dereq_,module,exports){ +},{"./ReactUpdates":87,"./Transaction":104,"./emptyFunction":116,"./mixInto":147}],53:[function(_dereq_,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 @@ -8601,53 +9190,51 @@ module.exports = ReactDefaultBatchingStr * See the License for the specific language governing permissions and * limitations under the License. * * @providesModule ReactDefaultInjection */ "use strict"; -var ReactInjection = _dereq_("./ReactInjection"); - -var ExecutionEnvironment = _dereq_("./ExecutionEnvironment"); - -var DefaultDOMPropertyConfig = _dereq_("./DefaultDOMPropertyConfig"); - +var BeforeInputEventPlugin = _dereq_("./BeforeInputEventPlugin"); var ChangeEventPlugin = _dereq_("./ChangeEventPlugin"); var ClientReactRootIndex = _dereq_("./ClientReactRootIndex"); var CompositionEventPlugin = _dereq_("./CompositionEventPlugin"); var DefaultEventPluginOrder = _dereq_("./DefaultEventPluginOrder"); var EnterLeaveEventPlugin = _dereq_("./EnterLeaveEventPlugin"); +var ExecutionEnvironment = _dereq_("./ExecutionEnvironment"); +var HTMLDOMPropertyConfig = _dereq_("./HTMLDOMPropertyConfig"); var MobileSafariClickEventPlugin = _dereq_("./MobileSafariClickEventPlugin"); var ReactBrowserComponentMixin = _dereq_("./ReactBrowserComponentMixin"); var ReactComponentBrowserEnvironment = _dereq_("./ReactComponentBrowserEnvironment"); -var ReactEventTopLevelCallback = _dereq_("./ReactEventTopLevelCallback"); +var ReactDefaultBatchingStrategy = _dereq_("./ReactDefaultBatchingStrategy"); var ReactDOM = _dereq_("./ReactDOM"); var ReactDOMButton = _dereq_("./ReactDOMButton"); var ReactDOMForm = _dereq_("./ReactDOMForm"); var ReactDOMImg = _dereq_("./ReactDOMImg"); var ReactDOMInput = _dereq_("./ReactDOMInput"); var ReactDOMOption = _dereq_("./ReactDOMOption"); var ReactDOMSelect = _dereq_("./ReactDOMSelect"); var ReactDOMTextarea = _dereq_("./ReactDOMTextarea"); +var ReactEventListener = _dereq_("./ReactEventListener"); +var ReactInjection = _dereq_("./ReactInjection"); var ReactInstanceHandles = _dereq_("./ReactInstanceHandles"); var ReactMount = _dereq_("./ReactMount"); var SelectEventPlugin = _dereq_("./SelectEventPlugin"); var ServerReactRootIndex = _dereq_("./ServerReactRootIndex"); var SimpleEventPlugin = _dereq_("./SimpleEventPlugin"); - -var ReactDefaultBatchingStrategy = _dereq_("./ReactDefaultBatchingStrategy"); +var SVGDOMPropertyConfig = _dereq_("./SVGDOMPropertyConfig"); var createFullPageComponent = _dereq_("./createFullPageComponent"); function inject() { - ReactInjection.EventEmitter.injectTopLevelCallbackCreator( - ReactEventTopLevelCallback + ReactInjection.EventEmitter.injectReactEventListener( + ReactEventListener ); /** * Inject modules for resolving DOM hierarchy and plugin ordering. */ ReactInjection.EventPluginHub.injectEventPluginOrder(DefaultEventPluginOrder); ReactInjection.EventPluginHub.injectInstanceHandle(ReactInstanceHandles); ReactInjection.EventPluginHub.injectMount(ReactMount); @@ -8657,41 +9244,46 @@ function inject() { * them). */ ReactInjection.EventPluginHub.injectEventPluginsByName({ SimpleEventPlugin: SimpleEventPlugin, EnterLeaveEventPlugin: EnterLeaveEventPlugin, ChangeEventPlugin: ChangeEventPlugin, CompositionEventPlugin: CompositionEventPlugin, MobileSafariClickEventPlugin: MobileSafariClickEventPlugin, - SelectEventPlugin: SelectEventPlugin + SelectEventPlugin: SelectEventPlugin, + BeforeInputEventPlugin: BeforeInputEventPlugin }); ReactInjection.DOM.injectComponentClasses({ button: ReactDOMButton, form: ReactDOMForm, img: ReactDOMImg, input: ReactDOMInput, option: ReactDOMOption, select: ReactDOMSelect, textarea: ReactDOMTextarea, html: createFullPageComponent(ReactDOM.html), head: createFullPageComponent(ReactDOM.head), - title: createFullPageComponent(ReactDOM.title), body: createFullPageComponent(ReactDOM.body) }); - // This needs to happen after createFullPageComponent() otherwise the mixin // gets double injected. ReactInjection.CompositeComponent.injectMixin(ReactBrowserComponentMixin); - ReactInjection.DOMProperty.injectDOMPropertyConfig(DefaultDOMPropertyConfig); - + ReactInjection.DOMProperty.injectDOMPropertyConfig(HTMLDOMPropertyConfig); + ReactInjection.DOMProperty.injectDOMPropertyConfig(SVGDOMPropertyConfig); + + ReactInjection.EmptyComponent.injectEmptyComponent(ReactDOM.noscript); + + ReactInjection.Updates.injectReconcileTransaction( + ReactComponentBrowserEnvironment.ReactReconcileTransaction + ); ReactInjection.Updates.injectBatchingStrategy( ReactDefaultBatchingStrategy ); ReactInjection.RootIndex.injectCreateReactRootIndex( ExecutionEnvironment.canUseDOM ? ClientReactRootIndex.createReactRootIndex : ServerReactRootIndex.createReactRootIndex @@ -8707,17 +9299,17 @@ function inject() { } } } module.exports = { inject: inject }; -},{"./ChangeEventPlugin":5,"./ClientReactRootIndex":6,"./CompositionEventPlugin":7,"./DefaultDOMPropertyConfig":12,"./DefaultEventPluginOrder":13,"./EnterLeaveEventPlugin":14,"./ExecutionEnvironment":21,"./MobileSafariClickEventPlugin":24,"./ReactBrowserComponentMixin":27,"./ReactComponentBrowserEnvironment":32,"./ReactDOM":36,"./ReactDOMButton":37,"./ReactDOMForm":39,"./ReactDOMImg":41,"./ReactDOMInput":42,"./ReactDOMOption":43,"./ReactDOMSelect":44,"./ReactDOMTextarea":46,"./ReactDefaultBatchingStrategy":47,"./ReactDefaultPerf":49,"./ReactEventTopLevelCallback":54,"./ReactInjection":55,"./ReactInstanceHandles":57,"./ReactMount":60,"./SelectEventPlugin":83,"./ServerReactRootIndex":84,"./SimpleEventPlugin":85,"./createFullPageComponent":104}],49:[function(_dereq_,module,exports){ +},{"./BeforeInputEventPlugin":2,"./ChangeEventPlugin":7,"./ClientReactRootIndex":8,"./CompositionEventPlugin":9,"./DefaultEventPluginOrder":14,"./EnterLeaveEventPlugin":15,"./ExecutionEnvironment":22,"./HTMLDOMPropertyConfig":23,"./MobileSafariClickEventPlugin":27,"./ReactBrowserComponentMixin":30,"./ReactComponentBrowserEnvironment":36,"./ReactDOM":41,"./ReactDOMButton":42,"./ReactDOMForm":44,"./ReactDOMImg":46,"./ReactDOMInput":47,"./ReactDOMOption":48,"./ReactDOMSelect":49,"./ReactDOMTextarea":51,"./ReactDefaultBatchingStrategy":52,"./ReactDefaultPerf":54,"./ReactEventListener":61,"./ReactInjection":62,"./ReactInstanceHandles":64,"./ReactMount":67,"./SVGDOMPropertyConfig":89,"./SelectEventPlugin":90,"./ServerReactRootIndex":91,"./SimpleEventPlugin":92,"./createFullPageComponent":112}],54:[function(_dereq_,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 @@ -8740,18 +9332,23 @@ var ReactMount = _dereq_("./ReactMount") var ReactPerf = _dereq_("./ReactPerf"); var performanceNow = _dereq_("./performanceNow"); function roundFloat(val) { return Math.floor(val * 100) / 100; } +function addValue(obj, key, val) { + obj[key] = (obj[key] || 0) + val; +} + var ReactDefaultPerf = { _allMeasurements: [], // last item in the list is the current one + _mountStack: [0], _injected: false, start: function() { if (!ReactDefaultPerf._injected) { ReactPerf.injection.injectMeasure(ReactDefaultPerf.measure); } ReactDefaultPerf._allMeasurements.length = 0; @@ -8768,25 +9365,25 @@ var ReactDefaultPerf = { printExclusive: function(measurements) { measurements = measurements || ReactDefaultPerf._allMeasurements; var summary = ReactDefaultPerfAnalysis.getExclusiveSummary(measurements); console.table(summary.map(function(item) { return { 'Component class name': item.componentName, 'Total inclusive time (ms)': roundFloat(item.inclusive), - 'Total exclusive time (ms)': roundFloat(item.exclusive), - 'Exclusive time per instance (ms)': roundFloat(item.exclusive / item.count), + 'Exclusive mount time (ms)': roundFloat(item.exclusive), + 'Exclusive render time (ms)': roundFloat(item.render), + 'Mount time per instance (ms)': roundFloat(item.exclusive / item.count), + 'Render time per instance (ms)': roundFloat(item.render / item.count), 'Instances': item.count }; })); - console.log( - 'Total time:', - ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms' - ); + // TODO: ReactDefaultPerfAnalysis.getTotalTime() does not return the correct + // number. }, printInclusive: function(measurements) { measurements = measurements || ReactDefaultPerf._allMeasurements; var summary = ReactDefaultPerfAnalysis.getInclusiveSummary(measurements); console.table(summary.map(function(item) { return { 'Owner > component': item.componentName, @@ -8859,16 +9456,17 @@ var ReactDefaultPerf = { fnName === 'flushBatchedUpdates') { // A "measurement" is a set of metrics recorded for each flush. We want // to group the metrics for a given flush together so we can look at the // components that rendered and the DOM operations that actually // happened to determine the amount of "wasted work" performed. ReactDefaultPerf._allMeasurements.push({ exclusive: {}, inclusive: {}, + render: {}, counts: {}, writes: {}, displayNames: {}, totalTime: 0 }); start = performanceNow(); rv = func.apply(this, args); ReactDefaultPerf._allMeasurements[ @@ -8921,49 +9519,60 @@ var ReactDefaultPerf = { fnName === 'mountComponent' || fnName === 'updateComponent' || // TODO: receiveComponent()? fnName === '_renderValidatedComponent')) { var rootNodeID = fnName === 'mountComponent' ? args[0] : this._rootNodeID; var isRender = fnName === '_renderValidatedComponent'; + var isMount = fnName === 'mountComponent'; + + var mountStack = ReactDefaultPerf._mountStack; var entry = ReactDefaultPerf._allMeasurements[ ReactDefaultPerf._allMeasurements.length - 1 ]; if (isRender) { - entry.counts[rootNodeID] = entry.counts[rootNodeID] || 0; - entry.counts[rootNodeID] += 1; + addValue(entry.counts, rootNodeID, 1); + } else if (isMount) { + mountStack.push(0); } start = performanceNow(); rv = func.apply(this, args); totalTime = performanceNow() - start; - var typeOfLog = isRender ? entry.exclusive : entry.inclusive; - typeOfLog[rootNodeID] = typeOfLog[rootNodeID] || 0; - typeOfLog[rootNodeID] += totalTime; + if (isRender) { + addValue(entry.render, rootNodeID, totalTime); + } else if (isMount) { + var subMountTime = mountStack.pop(); + mountStack[mountStack.length - 1] += totalTime; + addValue(entry.exclusive, rootNodeID, totalTime - subMountTime); + addValue(entry.inclusive, rootNodeID, totalTime); + } else { + addValue(entry.inclusive, rootNodeID, totalTime); + } entry.displayNames[rootNodeID] = { current: this.constructor.displayName, owner: this._owner ? this._owner.constructor.displayName : '<root>' }; return rv; } else { return func.apply(this, args); } }; } }; module.exports = ReactDefaultPerf; -},{"./DOMProperty":9,"./ReactDefaultPerfAnalysis":50,"./ReactMount":60,"./ReactPerf":65,"./performanceNow":142}],50:[function(_dereq_,module,exports){ +},{"./DOMProperty":11,"./ReactDefaultPerfAnalysis":55,"./ReactMount":67,"./ReactPerf":71,"./performanceNow":151}],55:[function(_dereq_,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 @@ -9036,18 +9645,22 @@ function getExclusiveSummary(measurement for (var id in allIDs) { displayName = measurement.displayNames[id].current; candidates[displayName] = candidates[displayName] || { componentName: displayName, inclusive: 0, exclusive: 0, + render: 0, count: 0 }; + if (measurement.render[id]) { + candidates[displayName].render += measurement.render[id]; + } if (measurement.exclusive[id]) { candidates[displayName].exclusive += measurement.exclusive[id]; } if (measurement.inclusive[id]) { candidates[displayName].inclusive += measurement.inclusive[id]; } if (measurement.counts[id]) { candidates[displayName].count += measurement.counts[id]; @@ -9154,17 +9767,635 @@ var ReactDefaultPerfAnalysis = { getExclusiveSummary: getExclusiveSummary, getInclusiveSummary: getInclusiveSummary, getDOMSummary: getDOMSummary, getTotalTime: getTotalTime }; module.exports = ReactDefaultPerfAnalysis; -},{"./merge":134}],51:[function(_dereq_,module,exports){ +},{"./merge":144}],56:[function(_dereq_,module,exports){ +/** + * Copyright 2014 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule ReactDescriptor + */ + +"use strict"; + +var ReactContext = _dereq_("./ReactContext"); +var ReactCurrentOwner = _dereq_("./ReactCurrentOwner"); + +var merge = _dereq_("./merge"); +var warning = _dereq_("./warning"); + +/** + * Warn for mutations. + * + * @internal + * @param {object} object + * @param {string} key + */ +function defineWarningProperty(object, key) { + Object.defineProperty(object, key, { + + configurable: false, + enumerable: true, + + get: function() { + if (!this._store) { + return null; + } + return this._store[key]; + }, + + set: function(value) { + ("production" !== "development" ? warning( + false, + 'Don\'t set the ' + key + ' property of the component. ' + + 'Mutate the existing props object instead.' + ) : null); + this._store[key] = value; + } + + }); +} + +/** + * This is updated to true if the membrane is successfully created. + */ +var useMutationMembrane = false; + +/** + * Warn for mutations. + * + * @internal + * @param {object} descriptor + */ +function defineMutationMembrane(prototype) { + try { + var pseudoFrozenProperties = { + props: true + }; + for (var key in pseudoFrozenProperties) { + defineWarningProperty(prototype, key); + } + useMutationMembrane = true; + } catch (x) { + // IE will fail on defineProperty + } +} + +/** + * Transfer static properties from the source to the target. Functions are + * rebound to have this reflect the original source. + */ +function proxyStaticMethods(target, source) { + if (typeof source !== 'function') { + return; + } + for (var key in source) { + if (source.hasOwnProperty(key)) { + var value = source[key]; + if (typeof value === 'function') { + var bound = value.bind(source); + // Copy any properties defined on the function, such as `isRequired` on + // a PropTypes validator. (mergeInto refuses to work on functions.) + for (var k in value) { + if (value.hasOwnProperty(k)) { + bound[k] = value[k]; + } + } + target[key] = bound; + } else { + target[key] = value; + } + } + } +} + +/** + * Base constructor for all React descriptors. This is only used to make this + * work with a dynamic instanceof check. Nothing should live on this prototype. + * + * @param {*} type + * @internal + */ +var ReactDescriptor = function() {}; + +if ("production" !== "development") { + defineMutationMembrane(ReactDescriptor.prototype); +} + +ReactDescriptor.createFactory = function(type) { + + var descriptorPrototype = Object.create(ReactDescriptor.prototype); + + var factory = function(props, children) { + // For consistency we currently allocate a new object for every descriptor. + // This protects the descriptor from being mutated by the original props + // object being mutated. It also protects the original props object from + // being mutated by children arguments and default props. This behavior + // comes with a performance cost and could be deprecated in the future. + // It could also be optimized with a smarter JSX transform. + if (props == null) { + props = {}; + } else if (typeof props === 'object') { + props = merge(props); + } + + // Children can be more than one argument, and those are transferred onto + // the newly allocated props object. + var childrenLength = arguments.length - 1; + if (childrenLength === 1) { + props.children = children; + } else if (childrenLength > 1) { + var childArray = Array(childrenLength); + for (var i = 0; i < childrenLength; i++) { + childArray[i] = arguments[i + 1]; + } + props.children = childArray; + } + + // Initialize the descriptor object + var descriptor = Object.create(descriptorPrototype); + + // Record the component responsible for creating this descriptor. + descriptor._owner = ReactCurrentOwner.current; + + // TODO: Deprecate withContext, and then the context becomes accessible + // through the owner. + descriptor._context = ReactContext.current; + + if ("production" !== "development") { + // The validation flag and props are currently mutative. We put them on + // an external backing store so that we can freeze the whole object. + // This can be replaced with a WeakMap once they are implemented in + // commonly used development environments. + descriptor._store = { validated: false, props: props }; + + // We're not allowed to set props directly on the object so we early + // return and rely on the prototype membrane to forward to the backing + // store. + if (useMutationMembrane) { + Object.freeze(descriptor); + return descriptor; + } + } + + descriptor.props = props; + return descriptor; + }; + + // Currently we expose the prototype of the descriptor so that + // <Foo /> instanceof Foo works. This is controversial pattern. + factory.prototype = descriptorPrototype; + + // Expose the type on the factory and the prototype so that it can be + // easily accessed on descriptors. E.g. <Foo />.type === Foo.type and for + // static methods like <Foo />.type.staticMethod(); + // This should not be named constructor since this may not be the function + // that created the descriptor, and it may not even be a constructor. + factory.type = type; + descriptorPrototype.type = type; + + proxyStaticMethods(factory, type); + + // Expose a unique constructor on the prototype is that this works with type + // systems that compare constructor properties: <Foo />.constructor === Foo + // This may be controversial since it requires a known factory function. + descriptorPrototype.constructor = factory; + + return factory; + +}; + +ReactDescriptor.cloneAndReplaceProps = function(oldDescriptor, newProps) { + var newDescriptor = Object.create(oldDescriptor.constructor.prototype); + // It's important that this property order matches the hidden class of the + // original descriptor to maintain perf. + newDescriptor._owner = oldDescriptor._owner; + newDescriptor._context = oldDescriptor._context; + + if ("production" !== "development") { + newDescriptor._store = { + validated: oldDescriptor._store.validated, + props: newProps + }; + if (useMutationMembrane) { + Object.freeze(newDescriptor); + return newDescriptor; + } + } + + newDescriptor.props = newProps; + return newDescriptor; +}; + +/** + * Checks if a value is a valid descriptor constructor. + * + * @param {*} + * @return {boolean} + * @public + */ +ReactDescriptor.isValidFactory = function(factory) { + return typeof factory === 'function' && + factory.prototype instanceof ReactDescriptor; +}; + +/** + * @param {?object} object + * @return {boolean} True if `object` is a valid component. + * @final + */ +ReactDescriptor.isValidDescriptor = function(object) { + return object instanceof ReactDescriptor; +}; + +module.exports = ReactDescriptor; + +},{"./ReactContext":39,"./ReactCurrentOwner":40,"./merge":144,"./warning":158}],57:[function(_dereq_,module,exports){ +/** + * Copyright 2014 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule ReactDescriptorValidator + */ + +/** + * ReactDescriptorValidator provides a wrapper around a descriptor factory + * which validates the props passed to the descriptor. This is intended to be + * used only in DEV and could be replaced by a static type checker for languages + * that support it. + */ + +"use strict"; + +var ReactDescriptor = _dereq_("./ReactDescriptor"); +var ReactPropTypeLocations = _dereq_("./ReactPropTypeLocations"); +var ReactCurrentOwner = _dereq_("./ReactCurrentOwner"); + +var monitorCodeUse = _dereq_("./monitorCodeUse"); + +/** + * Warn if there's no key explicitly set on dynamic arrays of children or + * object keys are not valid. This allows us to keep track of children between + * updates. + */ +var ownerHasKeyUseWarning = { + 'react_key_warning': {}, + 'react_numeric_key_warning': {} +}; +var ownerHasMonitoredObjectMap = {}; + +var loggedTypeFailures = {}; + +var NUMERIC_PROPERTY_REGEX = /^\d+$/; + +/** + * Gets the current owner's displayName for use in warnings. + * + * @internal + * @return {?string} Display name or undefined + */ +function getCurrentOwnerDisplayName() { + var current = ReactCurrentOwner.current; + return current && current.constructor.displayName || undefined; +} + +/** + * Warn if the component doesn't have an explicit key assigned to it. + * This component is in an array. The array could grow and shrink or be + * reordered. All children that haven't already been validated are required to + * have a "key" property assigned to it. + * + * @internal + * @param {ReactComponent} component Component that requires a key. + * @param {*} parentType component's parent's type. + */ +function validateExplicitKey(component, parentType) { + if (component._store.validated || component.props.key != null) { + return; + } + component._store.validated = true; + + warnAndMonitorForKeyUse( + 'react_key_warning', + 'Each child in an array should have a unique "key" prop.', + component, + parentType + ); +} + +/** + * Warn if the key is being defined as an object property but has an incorrect + * value. + * + * @internal + * @param {string} name Property name of the key. + * @param {ReactComponent} component Component that requires a key. + * @param {*} parentType component's parent's type. + */ +function validatePropertyKey(name, component, parentType) { + if (!NUMERIC_PROPERTY_REGEX.test(name)) { + return; + } + warnAndMonitorForKeyUse( + 'react_numeric_key_warning', + 'Child objects should have non-numeric keys so ordering is preserved.', + component, + parentType + ); +} + +/** + * Shared warning and monitoring code for the key warnings. + * + * @internal + * @param {string} warningID The id used when logging. + * @param {string} message The base warning that gets output. + * @param {ReactComponent} component Component that requires a key. + * @param {*} parentType component's parent's type. + */ +function warnAndMonitorForKeyUse(warningID, message, component, parentType) { + var ownerName = getCurrentOwnerDisplayName(); + var parentName = parentType.displayName; + + var useName = ownerName || parentName; + var memoizer = ownerHasKeyUseWarning[warningID]; + if (memoizer.hasOwnProperty(useName)) { + return; + } + memoizer[useName] = true; + + message += ownerName ? + (" Check the render method of " + ownerName + ".") : + (" Check the renderComponent call using <" + parentName + ">."); + + // Usually the current owner is the offender, but if it accepts children as a + // property, it may be the creator of the child that's responsible for + // assigning it a key. + var childOwnerName = null; + if (component._owner && component._owner !== ReactCurrentOwner.current) { + // Name of the component that originally created this child. + childOwnerName = component._owner.constructor.displayName; + + message += (" It was passed a child from " + childOwnerName + "."); + } + + message += ' See http://fb.me/react-warning-keys for more information.'; + monitorCodeUse(warningID, { + component: useName, + componentOwner: childOwnerName + }); + console.warn(message); +} + +/** + * Log that we're using an object map. We're considering deprecating this + * feature and replace it with proper Map and ImmutableMap data structures. + * + * @internal + */ +function monitorUseOfObjectMap() { + var currentName = getCurrentOwnerDisplayName() || ''; + if (ownerHasMonitoredObjectMap.hasOwnProperty(currentName)) { + return; + } + ownerHasMonitoredObjectMap[currentName] = true; + monitorCodeUse('react_object_map_children'); +} + +/** + * Ensure that every component either is passed in a static location, in an + * array with an explicit keys property defined, or in an object literal + * with valid key property. + * + * @internal + * @param {*} component Statically passed child of any type. + * @param {*} parentType component's parent's type. + * @return {boolean} + */ +function validateChildKeys(component, parentType) { + if (Array.isArray(component)) { + for (var i = 0; i < component.length; i++) { + var child = component[i]; + if (ReactDescriptor.isValidDescriptor(child)) { + validateExplicitKey(child, parentType); + } + } + } else if (ReactDescriptor.isValidDescriptor(component)) { + // This component was passed in a valid location. + component._store.validated = true; + } else if (component && typeof component === 'object') { + monitorUseOfObjectMap(); + for (var name in component) { + validatePropertyKey(name, component[name], parentType); + } + } +} + +/** + * Assert that the props are valid + * + * @param {string} componentName Name of the component for error messages. + * @param {object} propTypes Map of prop name to a ReactPropType + * @param {object} props + * @param {string} location e.g. "prop", "context", "child context" + * @private + */ +function checkPropTypes(componentName, propTypes, props, location) { + for (var propName in propTypes) { + if (propTypes.hasOwnProperty(propName)) { + var error; + // Prop type validation may throw. In case they do, we don't want to + // fail the render phase where it didn't fail before. So we log it. + // After these have been cleaned up, we'll let them throw. + try { + error = propTypes[propName](props, propName, componentName, location); + } catch (ex) { + error = ex; + } + if (error instanceof Error && !(error.message in loggedTypeFailures)) { + // Only monitor this failure once because there tends to be a lot of the + // same error. + loggedTypeFailures[error.message] = true; + // This will soon use the warning module + monitorCodeUse( + 'react_failed_descriptor_type_check', + { message: error.message } + ); + } + } + } +} + +var ReactDescriptorValidator = { + + /** + * Wraps a descriptor factory function in another function which validates + * the props and context of the descriptor and warns about any failed type + * checks. + * + * @param {function} factory The original descriptor factory + * @param {object?} propTypes A prop type definition set + * @param {object?} contextTypes A context type definition set + * @return {object} The component descriptor, which may be invalid. + * @private + */ + createFactory: function(factory, propTypes, contextTypes) { + var validatedFactory = function(props, children) { + var descriptor = factory.apply(this, arguments); + + for (var i = 1; i < arguments.length; i++) { + validateChildKeys(arguments[i], descriptor.type); + } + + var name = descriptor.type.displayName; + if (propTypes) { + checkPropTypes( + name, + propTypes, + descriptor.props, + ReactPropTypeLocations.prop + ); + } + if (contextTypes) { + checkPropTypes( + name, + contextTypes, + descriptor._context, + ReactPropTypeLocations.context + ); + } + return descriptor; + }; + + validatedFactory.prototype = factory.prototype; + validatedFactory.type = factory.type; + + // Copy static properties + for (var key in factory) { + if (factory.hasOwnProperty(key)) { + validatedFactory[key] = factory[key]; + } + } + + return validatedFactory; + } + +}; + +module.exports = ReactDescriptorValidator; + +},{"./ReactCurrentOwner":40,"./ReactDescriptor":56,"./ReactPropTypeLocations":74,"./monitorCodeUse":148}],58:[function(_dereq_,module,exports){ +/** + * Copyright 2014 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule ReactEmptyComponent + */ + +"use strict"; + +var invariant = _dereq_("./invariant"); + +var component; +// This registry keeps track of the React IDs of the components that rendered to +// `null` (in reality a placeholder such as `noscript`) +var nullComponentIdsRegistry = {}; + +var ReactEmptyComponentInjection = { + injectEmptyComponent: function(emptyComponent) { + component = emptyComponent; + } +}; + +/** + * @return {ReactComponent} component The injected empty component. + */ +function getEmptyComponent() { + ("production" !== "development" ? invariant( + component, + 'Trying to return null from a render, but no null placeholder component ' + + 'was injected.' + ) : invariant(component)); + return component(); +} + +/** + * Mark the component as having rendered to null. + * @param {string} id Component's `_rootNodeID`. + */ +function registerNullComponentID(id) { + nullComponentIdsRegistry[id] = true; +} + +/** + * Unmark the component as having rendered to null: it renders to something now. + * @param {string} id Component's `_rootNodeID`. + */ +function deregisterNullComponentID(id) { + delete nullComponentIdsRegistry[id]; +} + +/** + * @param {string} id Component's `_rootNodeID`. + * @return {boolean} True if the component is rendered to null. + */ +function isNullComponentID(id) { + return nullComponentIdsRegistry[id]; +} + +var ReactEmptyComponent = { + deregisterNullComponentID: deregisterNullComponentID, + getEmptyComponent: getEmptyComponent, + injection: ReactEmptyComponentInjection, + isNullComponentID: isNullComponentID, + registerNullComponentID: registerNullComponentID +}; + +module.exports = ReactEmptyComponent; + +},{"./invariant":134}],59:[function(_dereq_,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 @@ -9193,358 +10424,17 @@ var ReactErrorUtils = { */ guard: function(func, name) { return func; } }; module.exports = ReactErrorUtils; -},{}],52:[function(_dereq_,module,exports){ -/** - * Copyright 2013-2014 Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @providesModule ReactEventEmitter - * @typechecks static-only - */ - -"use strict"; - -var EventConstants = _dereq_("./EventConstants"); -var EventListener = _dereq_("./EventListener"); -var EventPluginHub = _dereq_("./EventPluginHub"); -var EventPluginRegistry = _dereq_("./EventPluginRegistry"); -var ExecutionEnvironment = _dereq_("./ExecutionEnvironment"); -var ReactEventEmitterMixin = _dereq_("./ReactEventEmitterMixin"); -var ViewportMetrics = _dereq_("./ViewportMetrics"); - -var invariant = _dereq_("./invariant"); -var isEventSupported = _dereq_("./isEventSupported"); -var merge = _dereq_("./merge"); - -/** - * Summary of `ReactEventEmitter` event handling: - * - * - Top-level delegation is used to trap native browser events. We normalize - * and de-duplicate events to account for browser quirks. - * - * - Forward these native events (with the associated top-level type used to - * trap it) to `EventPluginHub`, which in turn will ask plugins if they want - * to extract any synthetic events. - * - * - The `EventPluginHub` will then process each event by annotating them with - * "dispatches", a sequence of listeners and IDs that care about that event. - * - * - The `EventPluginHub` then dispatches the events. - * - * Overview of React and the event system: - * - * . - * +------------+ . - * | DOM | . - * +------------+ . +-----------+ - * + . +--------+|SimpleEvent| - * | . | |Plugin | - * +-----|------+ . v +-----------+ - * | | | . +--------------+ +------------+ - * | +-----------.--->|EventPluginHub| | Event | - * | | . | | +-----------+ | Propagators| - * | ReactEvent | . | | |TapEvent | |------------| - * | Emitter | . | |<---+|Plugin | |other plugin| - * | | . | | +-----------+ | utilities | - * | +-----------.--->| | +------------+ - * | | | . +--------------+ - * +-----|------+ . ^ +-----------+ - * | . | |Enter/Leave| - * + . +-------+|Plugin | - * +-------------+ . +-----------+ - * | application | . - * |-------------| . - * | | . - * | | . - * +-------------+ . - * . - * React Core . General Purpose Event Plugin System - */ - -var alreadyListeningTo = {}; -var isMonitoringScrollValue = false; -var reactTopListenersCounter = 0; - -// For events like 'submit' which don't consistently bubble (which we trap at a -// lower node than `document`), binding at `document` would cause duplicate -// events so we don't include them here -var topEventMapping = { - topBlur: 'blur', - topChange: 'change', - topClick: 'click', - topCompositionEnd: 'compositionend', - topCompositionStart: 'compositionstart', - topCompositionUpdate: 'compositionupdate', - topContextMenu: 'contextmenu', - topCopy: 'copy', - topCut: 'cut', - topDoubleClick: 'dblclick', - topDrag: 'drag', - topDragEnd: 'dragend', - topDragEnter: 'dragenter', - topDragExit: 'dragexit', - topDragLeave: 'dragleave', - topDragOver: 'dragover', - topDragStart: 'dragstart', - topDrop: 'drop', - topFocus: 'focus', - topInput: 'input', - topKeyDown: 'keydown', - topKeyPress: 'keypress', - topKeyUp: 'keyup', - topMouseDown: 'mousedown', - topMouseMove: 'mousemove', - topMouseOut: 'mouseout', - topMouseOver: 'mouseover', - topMouseUp: 'mouseup', - topPaste: 'paste', - topScroll: 'scroll', - topSelectionChange: 'selectionchange', - topTouchCancel: 'touchcancel', - topTouchEnd: 'touchend', - topTouchMove: 'touchmove', - topTouchStart: 'touchstart', - topWheel: 'wheel' -}; - -/** - * To ensure no conflicts with other potential React instances on the page - */ -var topListenersIDKey = "_reactListenersID" + String(Math.random()).slice(2); - -function getListeningForDocument(mountAt) { - if (mountAt[topListenersIDKey] == null) { - mountAt[topListenersIDKey] = reactTopListenersCounter++; - alreadyListeningTo[mountAt[topListenersIDKey]] = {}; - } - return alreadyListeningTo[mountAt[topListenersIDKey]]; -} - -/** - * Traps top-level events by using event bubbling. - * - * @param {string} topLevelType Record from `EventConstants`. - * @param {string} handlerBaseName Event name (e.g. "click"). - * @param {DOMEventTarget} element Element on which to attach listener. - * @internal - */ -function trapBubbledEvent(topLevelType, handlerBaseName, element) { - EventListener.listen( - element, - handlerBaseName, - ReactEventEmitter.TopLevelCallbackCreator.createTopLevelCallback( - topLevelType - ) - ); -} - -/** - * Traps a top-level event by using event capturing. - * - * @param {string} topLevelType Record from `EventConstants`. - * @param {string} handlerBaseName Event name (e.g. "click"). - * @param {DOMEventTarget} element Element on which to attach listener. - * @internal - */ -function trapCapturedEvent(topLevelType, handlerBaseName, element) { - EventListener.capture( - element, - handlerBaseName, - ReactEventEmitter.TopLevelCallbackCreator.createTopLevelCallback( - topLevelType - ) - ); -} - -/** - * `ReactEventEmitter` is used to attach top-level event listeners. For example: - * - * ReactEventEmitter.putListener('myID', 'onClick', myFunction); - * - * This would allocate a "registration" of `('onClick', myFunction)` on 'myID'. - * - * @internal - */ -var ReactEventEmitter = merge(ReactEventEmitterMixin, { - - /** - * React references `ReactEventTopLevelCallback` using this property in order - * to allow dependency injection. - */ - TopLevelCallbackCreator: null, - - injection: { - /** - * @param {function} TopLevelCallbackCreator - */ - injectTopLevelCallbackCreator: function(TopLevelCallbackCreator) { - ReactEventEmitter.TopLevelCallbackCreator = TopLevelCallbackCreator; - } - }, - - /** - * Sets whether or not any created callbacks should be enabled. - * - * @param {boolean} enabled True if callbacks should be enabled. - */ - setEnabled: function(enabled) { - ("production" !== "development" ? invariant( - ExecutionEnvironment.canUseDOM, - 'setEnabled(...): Cannot toggle event listening in a Worker thread. ' + - 'This is likely a bug in the framework. Please report immediately.' - ) : invariant(ExecutionEnvironment.canUseDOM)); - if (ReactEventEmitter.TopLevelCallbackCreator) { - ReactEventEmitter.TopLevelCallbackCreator.setEnabled(enabled); - } - }, - - /** - * @return {boolean} True if callbacks are enabled. - */ - isEnabled: function() { - return !!( - ReactEventEmitter.TopLevelCallbackCreator && - ReactEventEmitter.TopLevelCallbackCreator.isEnabled() - ); - }, - - /** - * We listen for bubbled touch events on the document object. - * - * Firefox v8.01 (and possibly others) exhibited strange behavior when - * mounting `onmousemove` events at some node that was not the document - * element. The symptoms were that if your mouse is not moving over something - * contained within that mount point (for example on the background) the - * top-level listeners for `onmousemove` won't be called. However, if you - * register the `mousemove` on the document object, then it will of course - * catch all `mousemove`s. This along with iOS quirks, justifies restricting - * top-level listeners to the document object only, at least for these - * movement types of events and possibly all events. - * - * @see http://www.quirksmode.org/blog/archives/2010/09/click_event_del.html - * - * Also, `keyup`/`keypress`/`keydown` do not bubble to the window on IE, but - * they bubble to document. - * - * @param {string} registrationName Name of listener (e.g. `onClick`). - * @param {DOMDocument} contentDocument Document which owns the container - */ - listenTo: function(registrationName, contentDocument) { - var mountAt = contentDocument; - var isListening = getListeningForDocument(mountAt); - var dependencies = EventPluginRegistry. - registrationNameDependencies[registrationName]; - - var topLevelTypes = EventConstants.topLevelTypes; - for (var i = 0, l = dependencies.length; i < l; i++) { - var dependency = dependencies[i]; - if (!isListening[dependency]) { - var topLevelType = topLevelTypes[dependency]; - - if (topLevelType === topLevelTypes.topWheel) { - if (isEventSupported('wheel')) { - trapBubbledEvent(topLevelTypes.topWheel, 'wheel', mountAt); - } else if (isEventSupported('mousewheel')) { - trapBubbledEvent(topLevelTypes.topWheel, 'mousewheel', mountAt); - } else { - // Firefox needs to capture a different mouse scroll event. - // @see http://www.quirksmode.org/dom/events/tests/scroll.html - trapBubbledEvent( - topLevelTypes.topWheel, - 'DOMMouseScroll', - mountAt); - } - } else if (topLevelType === topLevelTypes.topScroll) { - - if (isEventSupported('scroll', true)) { - trapCapturedEvent(topLevelTypes.topScroll, 'scroll', mountAt); - } else { - trapBubbledEvent(topLevelTypes.topScroll, 'scroll', window); - } - } else if (topLevelType === topLevelTypes.topFocus || - topLevelType === topLevelTypes.topBlur) { - - if (isEventSupported('focus', true)) { - trapCapturedEvent(topLevelTypes.topFocus, 'focus', mountAt); - trapCapturedEvent(topLevelTypes.topBlur, 'blur', mountAt); - } else if (isEventSupported('focusin')) { - // IE has `focusin` and `focusout` events which bubble. - // @see http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html - trapBubbledEvent(topLevelTypes.topFocus, 'focusin', mountAt); - trapBubbledEvent(topLevelTypes.topBlur, 'focusout', mountAt); - } - - // to make sure blur and focus event listeners are only attached once - isListening[topLevelTypes.topBlur] = true; - isListening[topLevelTypes.topFocus] = true; - } else if (topEventMapping[dependency]) { - trapBubbledEvent(topLevelType, topEventMapping[dependency], mountAt); - } - - isListening[dependency] = true; - } - } - }, - - /** - * Listens to window scroll and resize events. We cache scroll values so that - * application code can access them without triggering reflows. - * - * NOTE: Scroll events do not bubble. - * - * @see http://www.quirksmode.org/dom/events/scroll.html - */ - ensureScrollValueMonitoring: function(){ - if (!isMonitoringScrollValue) { - var refresh = ViewportMetrics.refreshScrollValues; - EventListener.listen(window, 'scroll', refresh); - EventListener.listen(window, 'resize', refresh); - isMonitoringScrollValue = true; - } - }, - - eventNameDispatchConfigs: EventPluginHub.eventNameDispatchConfigs, - - registrationNameModules: EventPluginHub.registrationNameModules, - - putListener: EventPluginHub.putListener, - - getListener: EventPluginHub.getListener, - - deleteListener: EventPluginHub.deleteListener, - - deleteAllListeners: EventPluginHub.deleteAllListeners, - - trapBubbledEvent: trapBubbledEvent, - - trapCapturedEvent: trapCapturedEvent - -}); - -module.exports = ReactEventEmitter; - -},{"./EventConstants":15,"./EventListener":16,"./EventPluginHub":17,"./EventPluginRegistry":18,"./ExecutionEnvironment":21,"./ReactEventEmitterMixin":53,"./ViewportMetrics":97,"./invariant":125,"./isEventSupported":126,"./merge":134}],53:[function(_dereq_,module,exports){ +},{}],60:[function(_dereq_,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 @@ -9556,17 +10446,16 @@ module.exports = ReactEventEmitter; * limitations under the License. * * @providesModule ReactEventEmitterMixin */ "use strict"; var EventPluginHub = _dereq_("./EventPluginHub"); -var ReactUpdates = _dereq_("./ReactUpdates"); function runEventQueueInBatch(events) { EventPluginHub.enqueueEvents(events); EventPluginHub.processEventQueue(); } var ReactEventEmitterMixin = { @@ -9586,60 +10475,56 @@ var ReactEventEmitterMixin = { nativeEvent) { var events = EventPluginHub.extractEvents( topLevelType, topLevelTarget, topLevelTargetID, nativeEvent ); - // Event queue being processed in the same cycle allows `preventDefault`. - ReactUpdates.batchedUpdates(runEventQueueInBatch, events); + runEventQueueInBatch(events); } }; module.exports = ReactEventEmitterMixin; -},{"./EventPluginHub":17,"./ReactUpdates":81}],54:[function(_dereq_,module,exports){ -/** - * Copyright 2013-2014 Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @providesModule ReactEventTopLevelCallback +},{"./EventPluginHub":18}],61:[function(_dereq_,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 ReactEventListener * @typechecks static-only */ "use strict"; +var EventListener = _dereq_("./EventListener"); +var ExecutionEnvironment = _dereq_("./ExecutionEnvironment"); var PooledClass = _dereq_("./PooledClass"); -var ReactEventEmitter = _dereq_("./ReactEventEmitter"); var ReactInstanceHandles = _dereq_("./ReactInstanceHandles"); var ReactMount = _dereq_("./ReactMount"); +var ReactUpdates = _dereq_("./ReactUpdates"); var getEventTarget = _dereq_("./getEventTarget"); +var getUnboundedScrollPosition = _dereq_("./getUnboundedScrollPosition"); var mixInto = _dereq_("./mixInto"); /** - * @type {boolean} - * @private - */ -var _topLevelListenersEnabled = true; - -/** * Finds the parent React component of `node`. * * @param {*} node * @return {?DOMEventTarget} Parent container, or `null` if the specified node * is not nested. */ function findParent(node) { // TODO: It may be a good idea to cache this to prevent unnecessary DOM @@ -9647,114 +10532,157 @@ function findParent(node) { // mutation observer to listen for all DOM changes. var nodeID = ReactMount.getID(node); var rootID = ReactInstanceHandles.getReactRootIDFromNodeID(nodeID); var container = ReactMount.findReactContainerForID(rootID); var parent = ReactMount.getFirstReactDOM(container); return parent; } -/** - * Calls ReactEventEmitter.handleTopLevel for each node stored in bookKeeping's - * ancestor list. Separated from createTopLevelCallback to avoid try/finally - * deoptimization. - * - * @param {string} topLevelType - * @param {DOMEvent} nativeEvent - * @param {TopLevelCallbackBookKeeping} bookKeeping - */ -function handleTopLevelImpl(topLevelType, nativeEvent, bookKeeping) { +// Used to store ancestor hierarchy in top level callback +function TopLevelCallbackBookKeeping(topLevelType, nativeEvent) { + this.topLevelType = topLevelType; + this.nativeEvent = nativeEvent; + this.ancestors = []; +} +mixInto(TopLevelCallbackBookKeeping, { + destructor: function() { + this.topLevelType = null; + this.nativeEvent = null; + this.ancestors.length = 0; + } +}); +PooledClass.addPoolingTo( + TopLevelCallbackBookKeeping, + PooledClass.twoArgumentPooler +); + +function handleTopLevelImpl(bookKeeping) { var topLevelTarget = ReactMount.getFirstReactDOM( - getEventTarget(nativeEvent) + getEventTarget(bookKeeping.nativeEvent) ) || window; // Loop through the hierarchy, in case there's any nested components. // It's important that we build the array of ancestors before calling any // event handlers, because event handlers can modify the DOM, leading to // inconsistencies with ReactMount's node cache. See #1105. var ancestor = topLevelTarget; while (ancestor) { bookKeeping.ancestors.push(ancestor); ancestor = findParent(ancestor); } for (var i = 0, l = bookKeeping.ancestors.length; i < l; i++) { topLevelTarget = bookKeeping.ancestors[i]; var topLevelTargetID = ReactMount.getID(topLevelTarget) || ''; - ReactEventEmitter.handleTopLevel( - topLevelType, + ReactEventListener._handleTopLevel( + bookKeeping.topLevelType, topLevelTarget, topLevelTargetID, - nativeEvent - ); - } -} - -// Used to store ancestor hierarchy in top level callback -function TopLevelCallbackBookKeeping() { - this.ancestors = []; -} -mixInto(TopLevelCallbackBookKeeping, { - destructor: function() { - this.ancestors.length = 0; - } -}); -PooledClass.addPoolingTo(TopLevelCallbackBookKeeping); - -/** - * Top-level callback creator used to implement event handling using delegation. - * This is used via dependency injection. - */ -var ReactEventTopLevelCallback = { - - /** - * Sets whether or not any created callbacks should be enabled. - * - * @param {boolean} enabled True if callbacks should be enabled. - */ + bookKeeping.nativeEvent + ); + } +} + +function scrollValueMonitor(cb) { + var scrollPosition = getUnboundedScrollPosition(window); + cb(scrollPosition); +} + +var ReactEventListener = { + _enabled: true, + _handleTopLevel: null, + + WINDOW_HANDLE: ExecutionEnvironment.canUseDOM ? window : null, + + setHandleTopLevel: function(handleTopLevel) { + ReactEventListener._handleTopLevel = handleTopLevel; + }, + setEnabled: function(enabled) { - _topLevelListenersEnabled = !!enabled; - }, - - /** - * @return {boolean} True if callbacks are enabled. - */ + ReactEventListener._enabled = !!enabled; + }, + isEnabled: function() { - return _topLevelListenersEnabled; - }, - - /** - * Creates a callback for the supplied `topLevelType` that could be added as - * a listener to the document. The callback computes a `topLevelTarget` which - * should be the root node of a mounted React component where the listener - * is attached. + return ReactEventListener._enabled; + }, + + + /** + * Traps top-level events by using event bubbling. * * @param {string} topLevelType Record from `EventConstants`. - * @return {function} Callback for handling top-level events. - */ - createTopLevelCallback: function(topLevelType) { - return function(nativeEvent) { - if (!_topLevelListenersEnabled) { - return; - } - - var bookKeeping = TopLevelCallbackBookKeeping.getPooled(); - try { - handleTopLevelImpl(topLevelType, nativeEvent, bookKeeping); - } finally { - TopLevelCallbackBookKeeping.release(bookKeeping); - } - }; - } - -}; - -module.exports = ReactEventTopLevelCallback; - -},{"./PooledClass":25,"./ReactEventEmitter":52,"./ReactInstanceHandles":57,"./ReactMount":60,"./getEventTarget":117,"./mixInto":137}],55:[function(_dereq_,module,exports){ + * @param {string} handlerBaseName Event name (e.g. "click"). + * @param {object} handle Element on which to attach listener. + * @return {object} An object with a remove function which will forcefully + * remove the listener. + * @internal + */ + trapBubbledEvent: function(topLevelType, handlerBaseName, handle) { + var element = handle; + if (!element) { + return; + } + return EventListener.listen( + element, + handlerBaseName, + ReactEventListener.dispatchEvent.bind(null, topLevelType) + ); + }, + + /** + * Traps a top-level event by using event capturing. + * + * @param {string} topLevelType Record from `EventConstants`. + * @param {string} handlerBaseName Event name (e.g. "click"). + * @param {object} handle Element on which to attach listener. + * @return {object} An object with a remove function which will forcefully + * remove the listener. + * @internal + */ + trapCapturedEvent: function(topLevelType, handlerBaseName, handle) { + var element = handle; + if (!element) { + return; + } + return EventListener.capture( + element, + handlerBaseName, + ReactEventListener.dispatchEvent.bind(null, topLevelType) + ); + }, + + monitorScrollValue: function(refresh) { + var callback = scrollValueMonitor.bind(null, refresh); + EventListener.listen(window, 'scroll', callback); + EventListener.listen(window, 'resize', callback); + }, + + dispatchEvent: function(topLevelType, nativeEvent) { + if (!ReactEventListener._enabled) { + return; + } + + var bookKeeping = TopLevelCallbackBookKeeping.getPooled( + topLevelType, + nativeEvent + ); + try { + // Event queue being processed in the same cycle allows + // `preventDefault`. + ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping); + } finally { + TopLevelCallbackBookKeeping.release(bookKeeping); + } + } +}; + +module.exports = ReactEventListener; + +},{"./EventListener":17,"./ExecutionEnvironment":22,"./PooledClass":28,"./ReactInstanceHandles":64,"./ReactMount":67,"./ReactUpdates":87,"./getEventTarget":125,"./getUnboundedScrollPosition":130,"./mixInto":147}],62:[function(_dereq_,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 @@ -9770,36 +10698,38 @@ module.exports = ReactEventTopLevelCallb "use strict"; var DOMProperty = _dereq_("./DOMProperty"); var EventPluginHub = _dereq_("./EventPluginHub"); var ReactComponent = _dereq_("./ReactComponent"); var ReactCompositeComponent = _dereq_("./ReactCompositeComponent"); var ReactDOM = _dereq_("./ReactDOM"); -var ReactEventEmitter = _dereq_("./ReactEventEmitter"); +var ReactEmptyComponent = _dereq_("./ReactEmptyComponent"); +var ReactBrowserEventEmitter = _dereq_("./ReactBrowserEventEmitter"); var ReactPerf = _dereq_("./ReactPerf"); var ReactRootIndex = _dereq_("./ReactRootIndex"); var ReactUpdates = _dereq_("./ReactUpdates"); var ReactInjection = { Component: ReactComponent.injection, CompositeComponent: ReactCompositeComponent.injection, DOMProperty: DOMProperty.injection, + EmptyComponent: ReactEmptyComponent.injection, EventPluginHub: EventPluginHub.injection, DOM: ReactDOM.injection, - EventEmitter: ReactEventEmitter.injection, + EventEmitter: ReactBrowserEventEmitter.injection, Perf: ReactPerf.injection, RootIndex: ReactRootIndex.injection, Updates: ReactUpdates.injection }; module.exports = ReactInjection; -},{"./DOMProperty":9,"./EventPluginHub":17,"./ReactComponent":31,"./ReactCompositeComponent":33,"./ReactDOM":36,"./ReactEventEmitter":52,"./ReactPerf":65,"./ReactRootIndex":72,"./ReactUpdates":81}],56:[function(_dereq_,module,exports){ +},{"./DOMProperty":11,"./EventPluginHub":18,"./ReactBrowserEventEmitter":31,"./ReactComponent":35,"./ReactCompositeComponent":38,"./ReactDOM":41,"./ReactEmptyComponent":58,"./ReactPerf":71,"./ReactRootIndex":78,"./ReactUpdates":87}],63:[function(_dereq_,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 @@ -9932,17 +10862,17 @@ var ReactInputSelection = { } else { ReactDOMSelection.setOffsets(input, offsets); } } }; module.exports = ReactInputSelection; -},{"./ReactDOMSelection":45,"./containsNode":101,"./focusNode":113,"./getActiveElement":115}],57:[function(_dereq_,module,exports){ +},{"./ReactDOMSelection":50,"./containsNode":109,"./focusNode":120,"./getActiveElement":122}],64:[function(_dereq_,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 @@ -10272,17 +11202,17 @@ var ReactInstanceHandles = { isAncestorIDOf: isAncestorIDOf, SEPARATOR: SEPARATOR }; module.exports = ReactInstanceHandles; -},{"./ReactRootIndex":72,"./invariant":125}],58:[function(_dereq_,module,exports){ +},{"./ReactRootIndex":78,"./invariant":134}],65:[function(_dereq_,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 @@ -10317,28 +11247,52 @@ module.exports = ReactInstanceHandles; * this.setState({value: newValue}); * } * }); * * We have provided some sugary mixins to make the creation and * consumption of ReactLink easier; see LinkedValueUtils and LinkedStateMixin. */ +var React = _dereq_("./React"); + /** * @param {*} value current value of the link * @param {function} requestChange callback to request a change */ function ReactLink(value, requestChange) { this.value = value; this.requestChange = requestChange; } +/** + * Creates a PropType that enforces the ReactLink API and optionally checks the + * type of the value being passed inside the link. Example: + * + * MyComponent.propTypes = { + * tabIndexLink: ReactLink.PropTypes.link(React.PropTypes.number) + * } + */ +function createLinkTypeChecker(linkType) { + var shapes = { + value: typeof linkType === 'undefined' ? + React.PropTypes.any.isRequired : + linkType.isRequired, + requestChange: React.PropTypes.func.isRequired + }; + return React.PropTypes.shape(shapes); +} + +ReactLink.PropTypes = { + link: createLinkTypeChecker +}; + module.exports = ReactLink; -},{}],59:[function(_dereq_,module,exports){ +},{"./React":29}],66:[function(_dereq_,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 @@ -10383,17 +11337,17 @@ var ReactMarkupChecksum = { existingChecksum = existingChecksum && parseInt(existingChecksum, 10); var markupChecksum = adler32(markup); return markupChecksum === existingChecksum; } }; module.exports = ReactMarkupChecksum; -},{"./adler32":99}],60:[function(_dereq_,module,exports){ +},{"./adler32":107}],67:[function(_dereq_,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 @@ -10405,25 +11359,28 @@ module.exports = ReactMarkupChecksum; * limitations under the License. * * @providesModule ReactMount */ "use strict"; var DOMProperty = _dereq_("./DOMProperty"); -var ReactEventEmitter = _dereq_("./ReactEventEmitter"); +var ReactBrowserEventEmitter = _dereq_("./ReactBrowserEventEmitter"); +var ReactCurrentOwner = _dereq_("./ReactCurrentOwner"); +var ReactDescriptor = _dereq_("./ReactDescriptor"); var ReactInstanceHandles = _dereq_("./ReactInstanceHandles"); var ReactPerf = _dereq_("./ReactPerf"); var containsNode = _dereq_("./containsNode"); var getReactRootElementInContainer = _dereq_("./getReactRootElementInContainer"); var instantiateReactComponent = _dereq_("./instantiateReactComponent"); var invariant = _dereq_("./invariant"); var shouldUpdateReactComponent = _dereq_("./shouldUpdateReactComponent"); +var warning = _dereq_("./warning"); var SEPARATOR = ReactInstanceHandles.SEPARATOR; var ATTR_NAME = DOMProperty.ID_ATTRIBUTE_NAME; var nodeCache = {}; var ELEMENT_NODE_TYPE = 1; var DOC_NODE_TYPE = 9; @@ -10596,25 +11553,16 @@ function findDeepestCachedAncestor(targe * <div data-reactid=".3"> <-- Rendered reactRoot of React * // ... component. * </div> * </div> * * Inside of `container`, the first element rendered is the "reactRoot". */ var ReactMount = { - /** Time spent generating markup. */ - totalInstantiationTime: 0, - - /** Time spent inserting markup into the DOM. */ - totalInjectionTime: 0, - - /** Whether support for touch events should be initialized. */ - useTouchEvents: false, - /** Exposed for debugging purposes **/ _instancesByReactRootID: instancesByReactRootID, /** * This is a hook provided to support rendering React components while * ensuring that the apparent scroll position of its `container` does not * change. * @@ -10665,17 +11613,17 @@ var ReactMount = { container.nodeType === DOC_NODE_TYPE ), '_registerComponent(...): Target container is not a DOM element.' ) : invariant(container && ( container.nodeType === ELEMENT_NODE_TYPE || container.nodeType === DOC_NODE_TYPE ))); - ReactEventEmitter.ensureScrollValueMonitoring(); + ReactBrowserEventEmitter.ensureScrollValueMonitoring(); var reactRootID = ReactMount.registerContainer(container); instancesByReactRootID[reactRootID] = nextComponent; return reactRootID; }, /** * Render a new component into the DOM. @@ -10686,16 +11634,26 @@ var ReactMount = { */ _renderNewRootComponent: ReactPerf.measure( 'ReactMount', '_renderNewRootComponent', function( nextComponent, container, shouldReuseMarkup) { + // Various parts of our code (such as ReactCompositeComponent's + // _renderValidatedComponent) assume that calls to render aren't nested; + // verify that that's the case. + ("production" !== "development" ? warning( + ReactCurrentOwner.current == null, + '_renderNewRootComponent(): Render methods should be a pure function ' + + 'of props and state; triggering nested component updates from ' + + 'render is not allowed. If necessary, trigger nested updates in ' + + 'componentDidUpdate.' + ) : null); var componentInstance = instantiateReactComponent(nextComponent); var reactRootID = ReactMount._registerComponent( componentInstance, container ); componentInstance.mountComponentIntoNode( reactRootID, @@ -10715,45 +11673,61 @@ var ReactMount = { /** * Renders a React component into the DOM in the supplied `container`. * * If the React component was previously rendered into `container`, this will * perform an update on it and only mutate the DOM as necessary to reflect the * latest React component. * - * @param {ReactComponent} nextComponent Component instance to render. + * @param {ReactDescriptor} nextDescriptor Component descriptor to render. * @param {DOMElement} container DOM element to render into. * @param {?function} callback function triggered on completion * @return {ReactComponent} Component instance rendered in `container`. */ - renderComponent: function(nextComponent, container, callback) { + renderComponent: function(nextDescriptor, container, callback) { + ("production" !== "development" ? invariant( + ReactDescriptor.isValidDescriptor(nextDescriptor), + 'renderComponent(): Invalid component descriptor.%s', + ( + ReactDescriptor.isValidFactory(nextDescriptor) ? + ' Instead of passing a component class, make sure to instantiate ' + + 'it first by calling it with props.' : + // Check if it quacks like a descriptor + typeof nextDescriptor.props !== "undefined" ? + ' This may be caused by unintentionally loading two independent ' + + 'copies of React.' : + '' + ) + ) : invariant(ReactDescriptor.isValidDescriptor(nextDescriptor))); + var prevComponent = instancesByReactRootID[getReactRootID(container)]; if (prevComponent) { - if (shouldUpdateReactComponent(prevComponent, nextComponent)) { + var prevDescriptor = prevComponent._descriptor; + if (shouldUpdateReactComponent(prevDescriptor, nextDescriptor)) { return ReactMount._updateRootComponent( prevComponent, - nextComponent, + nextDescriptor, container, callback ); } else { ReactMount.unmountComponentAtNode(container); } } var reactRootElement = getReactRootElementInContainer(container); var containerHasReactMarkup = reactRootElement && ReactMount.isRenderedByReact(reactRootElement); var shouldReuseMarkup = containerHasReactMarkup && !prevComponent; var component = ReactMount._renderNewRootComponent( - nextComponent, + nextDescriptor, container, shouldReuseMarkup ); callback && callback.call(component); return component; }, /** @@ -10813,16 +11787,28 @@ var ReactMount = { /** * Unmounts and destroys the React component rendered in the `container`. * * @param {DOMElement} container DOM element containing a React component. * @return {boolean} True if a component was found in and unmounted from * `container` */ unmountComponentAtNode: function(container) { + // Various parts of our code (such as ReactCompositeComponent's + // _renderValidatedComponent) assume that calls to render aren't nested; + // verify that that's the case. (Strictly speaking, unmounting won't cause a + // render but we still don't expect to be in a render call here.) + ("production" !== "development" ? warning( + ReactCurrentOwner.current == null, + 'unmountComponentAtNode(): Render methods should be a pure function of ' + + 'props and state; triggering nested component updates from render is ' + + 'not allowed. If necessary, trigger nested updates in ' + + 'componentDidUpdate.' + ) : null); + var reactRootID = getReactRootID(container); var component = instancesByReactRootID[reactRootID]; if (!component) { return false; } ReactMount.unmountComponentFromNode(component, container); delete instancesByReactRootID[reactRootID]; delete containersByReactRootID[reactRootID]; @@ -11008,19 +11994,19 @@ var ReactMount = { } firstChildren.length = 0; ("production" !== "development" ? invariant( false, 'findComponentRoot(..., %s): Unable to find element. This probably ' + 'means the DOM was unexpectedly mutated (e.g., by the browser), ' + - 'usually due to forgetting a <tbody> when using tables or nesting <p> ' + - 'or <a> tags. Try inspecting the child nodes of the element with React ' + - 'ID `%s`.', + 'usually due to forgetting a <tbody> when using tables, nesting <p> ' + + 'or <a> tags, or using non-SVG elements in an <svg> parent. Try ' + + 'inspecting the child nodes of the element with React ID `%s`.', targetID, ReactMount.getID(ancestorNode) ) : invariant(false)); }, /** * React ID utilities. @@ -11034,114 +12020,17 @@ var ReactMount = { getNode: getNode, purgeID: purgeID }; module.exports = ReactMount; -},{"./DOMProperty":9,"./ReactEventEmitter":52,"./ReactInstanceHandles":57,"./ReactPerf":65,"./containsNode":101,"./getReactRootElementInContainer":120,"./instantiateReactComponent":124,"./invariant":125,"./shouldUpdateReactComponent":144}],61:[function(_dereq_,module,exports){ -/** - * Copyright 2013-2014 Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @providesModule ReactMountReady - */ - -"use strict"; - -var PooledClass = _dereq_("./PooledClass"); - -var mixInto = _dereq_("./mixInto"); - -/** - * A specialized pseudo-event module to help keep track of components waiting to - * be notified when their DOM representations are available for use. - * - * This implements `PooledClass`, so you should never need to instantiate this. - * Instead, use `ReactMountReady.getPooled()`. - * - * @param {?array<function>} initialCollection - * @class ReactMountReady - * @implements PooledClass - * @internal - */ -function ReactMountReady(initialCollection) { - this._queue = initialCollection || null; -} - -mixInto(ReactMountReady, { - - /** - * Enqueues a callback to be invoked when `notifyAll` is invoked. This is used - * to enqueue calls to `componentDidMount` and `componentDidUpdate`. - * - * @param {ReactComponent} component Component being rendered. - * @param {function(DOMElement)} callback Invoked when `notifyAll` is invoked. - * @internal - */ - enqueue: function(component, callback) { - this._queue = this._queue || []; - this._queue.push({component: component, callback: callback}); - }, - - /** - * Invokes all enqueued callbacks and clears the queue. This is invoked after - * the DOM representation of a component has been created or updated. - * - * @internal - */ - notifyAll: function() { - var queue = this._queue; - if (queue) { - this._queue = null; - for (var i = 0, l = queue.length; i < l; i++) { - var component = queue[i].component; - var callback = queue[i].callback; - callback.call(component); - } - queue.length = 0; - } - }, - - /** - * Resets the internal queue. - * - * @internal - */ - reset: function() { - this._queue = null; - }, - - /** - * `PooledClass` looks for this. - */ - destructor: function() { - this.reset(); - } - -}); - -PooledClass.addPoolingTo(ReactMountReady); - -module.exports = ReactMountReady; - -},{"./PooledClass":25,"./mixInto":137}],62:[function(_dereq_,module,exports){ +},{"./DOMProperty":11,"./ReactBrowserEventEmitter":31,"./ReactCurrentOwner":40,"./ReactDescriptor":56,"./ReactInstanceHandles":64,"./ReactPerf":71,"./containsNode":109,"./getReactRootElementInContainer":128,"./instantiateReactComponent":133,"./invariant":134,"./shouldUpdateReactComponent":154,"./warning":158}],68:[function(_dereq_,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 @@ -11420,30 +12309,31 @@ var ReactMultiChild = { // `lastIndex` will be the last index visited in `prevChildren`. var lastIndex = 0; var nextIndex = 0; for (name in nextChildren) { if (!nextChildren.hasOwnProperty(name)) { continue; } var prevChild = prevChildren && prevChildren[name]; - var nextChild = nextChildren[name]; - if (shouldUpdateReactComponent(prevChild, nextChild)) { + var prevDescriptor = prevChild && prevChild._descriptor; + var nextDescriptor = nextChildren[name]; + if (shouldUpdateReactComponent(prevDescriptor, nextDescriptor)) { this.moveChild(prevChild, nextIndex, lastIndex); lastIndex = Math.max(prevChild._mountIndex, lastIndex); - prevChild.receiveComponent(nextChild, transaction); + prevChild.receiveComponent(nextDescriptor, transaction); prevChild._mountIndex = nextIndex; } else { if (prevChild) { // Update `lastIndex` before `_mountIndex` gets unset by unmounting. lastIndex = Math.max(prevChild._mountIndex, lastIndex); this._unmountChildByName(prevChild, name); } // The child must be instantiated before it's mounted. - var nextChildInstance = instantiateReactComponent(nextChild); + var nextChildInstance = instantiateReactComponent(nextDescriptor); this._mountChildByNameAtIndex( nextChildInstance, name, nextIndex, transaction ); } nextIndex++; } // Remove children that are no longer present. for (name in prevChildren) { @@ -11550,32 +12440,29 @@ var ReactMultiChild = { * * NOTE: This is part of `updateChildren` and is here for readability. * * @param {ReactComponent} child Component to unmount. * @param {string} name Name of the child in `this._renderedChildren`. * @private */ _unmountChildByName: function(child, name) { - // TODO: When is this not true? - if (ReactComponent.isValidComponent(child)) { - this.removeChild(child); - child._mountIndex = null; - child.unmountComponent(); - delete this._renderedChildren[name]; - } + this.removeChild(child); + child._mountIndex = null; + child.unmountComponent(); + delete this._renderedChildren[name]; } } }; module.exports = ReactMultiChild; -},{"./ReactComponent":31,"./ReactMultiChildUpdateTypes":63,"./flattenChildren":112,"./instantiateReactComponent":124,"./shouldUpdateReactComponent":144}],63:[function(_dereq_,module,exports){ +},{"./ReactComponent":35,"./ReactMultiChildUpdateTypes":69,"./flattenChildren":119,"./instantiateReactComponent":133,"./shouldUpdateReactComponent":154}],69:[function(_dereq_,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 @@ -11605,17 +12492,17 @@ var ReactMultiChildUpdateTypes = keyMirr INSERT_MARKUP: null, MOVE_EXISTING: null, REMOVE_NODE: null, TEXT_CONTENT: null }); module.exports = ReactMultiChildUpdateTypes; -},{"./keyMirror":131}],64:[function(_dereq_,module,exports){ +},{"./keyMirror":140}],70:[function(_dereq_,module,exports){ /** * Copyright 2013-2014 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License");