aboutsummaryrefslogtreecommitdiffstats
path: root/web/src/vendor/react-router/react-router.js
diff options
context:
space:
mode:
authorMaximilian Hils <git@maximilianhils.com>2014-11-27 01:37:36 +0100
committerMaximilian Hils <git@maximilianhils.com>2014-11-27 01:37:36 +0100
commit021e209ce0fbfa5fa993ad43e8167a29a759d120 (patch)
treee0afdac43998757fea5ef2dd29de38968f2b4cce /web/src/vendor/react-router/react-router.js
parented8249023fb7c0d429b9278c63b51ac071700987 (diff)
downloadmitmproxy-021e209ce0fbfa5fa993ad43e8167a29a759d120.tar.gz
mitmproxy-021e209ce0fbfa5fa993ad43e8167a29a759d120.tar.bz2
mitmproxy-021e209ce0fbfa5fa993ad43e8167a29a759d120.zip
web: update dependencies
Diffstat (limited to 'web/src/vendor/react-router/react-router.js')
-rw-r--r--web/src/vendor/react-router/react-router.js3744
1 files changed, 1387 insertions, 2357 deletions
diff --git a/web/src/vendor/react-router/react-router.js b/web/src/vendor/react-router/react-router.js
index 942a2fc7..e2836135 100644
--- a/web/src/vendor/react-router/react-router.js
+++ b/web/src/vendor/react-router/react-router.js
@@ -1,65 +1,61 @@
!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.ReactRouter=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){
-var LocationDispatcher = _dereq_('../dispatchers/LocationDispatcher');
-var makePath = _dereq_('../utils/makePath');
-
/**
* Actions that modify the URL.
*/
var LocationActions = {
- PUSH: 'push',
- REPLACE: 'replace',
- POP: 'pop',
- UPDATE_SCROLL: 'update-scroll',
-
/**
- * Transitions to the URL specified in the arguments by pushing
- * a new URL onto the history stack.
+ * Indicates a new location is being pushed to the history stack.
*/
- transitionTo: function (to, params, query) {
- LocationDispatcher.handleViewAction({
- type: LocationActions.PUSH,
- path: makePath(to, params, query)
- });
- },
+ PUSH: 'push',
/**
- * Transitions to the URL specified in the arguments by replacing
- * the current URL in the history stack.
+ * Indicates the current location should be replaced.
*/
- replaceWith: function (to, params, query) {
- LocationDispatcher.handleViewAction({
- type: LocationActions.REPLACE,
- path: makePath(to, params, query)
- });
- },
+ REPLACE: 'replace',
/**
- * Transitions to the previous URL.
+ * Indicates the most recent entry should be removed from the history stack.
*/
- goBack: function () {
- LocationDispatcher.handleViewAction({
- type: LocationActions.POP
- });
- },
+ POP: 'pop'
- /**
- * Updates the window's scroll position to the last known position
- * for the current URL path.
- */
- updateScroll: function () {
- LocationDispatcher.handleViewAction({
- type: LocationActions.UPDATE_SCROLL
- });
+};
+
+module.exports = LocationActions;
+
+},{}],2:[function(_dereq_,module,exports){
+var LocationActions = _dereq_('../actions/LocationActions');
+
+/**
+ * A scroll behavior that attempts to imitate the default behavior
+ * of modern browsers.
+ */
+var ImitateBrowserBehavior = {
+
+ updateScrollPosition: function (position, actionType) {
+ switch (actionType) {
+ case LocationActions.PUSH:
+ case LocationActions.REPLACE:
+ window.scrollTo(0, 0);
+ break;
+ case LocationActions.POP:
+ if (position) {
+ window.scrollTo(position.x, position.y);
+ } else {
+ window.scrollTo(0, 0);
+ }
+ break;
+ }
}
};
-module.exports = LocationActions;
+module.exports = ImitateBrowserBehavior;
-},{"../dispatchers/LocationDispatcher":8,"../utils/makePath":26}],2:[function(_dereq_,module,exports){
-var merge = _dereq_('react/lib/merge');
-var Route = _dereq_('./Route');
+},{"../actions/LocationActions":1}],3:[function(_dereq_,module,exports){
+var React = (typeof window !== "undefined" ? window.React : typeof global !== "undefined" ? global.React : null);
+var FakeNode = _dereq_('../mixins/FakeNode');
+var PropTypes = _dereq_('../utils/PropTypes');
/**
* A <DefaultRoute> component is a special kind of <Route> that
@@ -67,25 +63,28 @@ var Route = _dereq_('./Route');
* Only one such route may be used at any given level in the
* route hierarchy.
*/
-function DefaultRoute(props) {
- return Route(
- merge(props, {
- path: null,
- isDefault: true
- })
- );
-}
+var DefaultRoute = React.createClass({
+
+ displayName: 'DefaultRoute',
+
+ mixins: [ FakeNode ],
+
+ propTypes: {
+ name: React.PropTypes.string,
+ path: PropTypes.falsy,
+ handler: React.PropTypes.func.isRequired
+ }
+
+});
module.exports = DefaultRoute;
-},{"./Route":6,"react/lib/merge":44}],3:[function(_dereq_,module,exports){
+},{"../mixins/FakeNode":13,"../utils/PropTypes":21}],4:[function(_dereq_,module,exports){
var React = (typeof window !== "undefined" ? window.React : typeof global !== "undefined" ? global.React : null);
-var ActiveState = _dereq_('../mixins/ActiveState');
-var transitionTo = _dereq_('../actions/LocationActions').transitionTo;
-var withoutProperties = _dereq_('../utils/withoutProperties');
-var hasOwnProperty = _dereq_('../utils/hasOwnProperty');
-var makeHref = _dereq_('../utils/makeHref');
-var warning = _dereq_('react/lib/warning');
+var classSet = _dereq_('react/lib/cx');
+var assign = _dereq_('react/lib/Object.assign');
+var Navigation = _dereq_('../mixins/Navigation');
+var State = _dereq_('../mixins/State');
function isLeftClickEvent(event) {
return event.button === 0;
@@ -96,69 +95,32 @@ function isModifiedEvent(event) {
}
/**
- * DEPRECATED: A map of <Link> component props that are reserved for use by the
- * router and/or React. All other props are used as params that are
- * interpolated into the link's path.
- */
-var RESERVED_PROPS = {
- to: true,
- key: true,
- className: true,
- activeClassName: true,
- query: true,
- onClick:true,
- children: true // ReactChildren
-};
-
-/**
* <Link> components are used to create an <a> element that links to a route.
* When that route is active, the link gets an "active" class name (or the
* value of its `activeClassName` prop).
*
* For example, assuming you have the following route:
*
- * <Route name="showPost" path="/posts/:postId" handler={Post}/>
+ * <Route name="showPost" path="/posts/:postID" handler={Post}/>
*
* You could use the following component to link to that route:
*
- * <Link to="showPost" params={{postId: "123"}} />
+ * <Link to="showPost" params={{ postID: "123" }} />
*
* In addition to params, links may pass along query string parameters
* using the `query` prop.
*
- * <Link to="showPost" params={{postId: "123"}} query={{show:true}}/>
+ * <Link to="showPost" params={{ postID: "123" }} query={{ show:true }}/>
*/
var Link = React.createClass({
displayName: 'Link',
- mixins: [ ActiveState ],
-
- statics: {
-
- // TODO: Deprecate passing props as params in v1.0
- getUnreservedProps: function (props) {
- var props = withoutProperties(props, RESERVED_PROPS);
- warning(
- Object.keys(props).length === 0,
- 'Passing props for params on <Link>s is deprecated, '+
- 'please use the `params` property.'
- );
- return props;
- },
-
- /**
- * Returns a hash of URL parameters to use in this <Link>'s path.
- */
- getParams: function (props) {
- return props.params || Link.getUnreservedProps(props);
- }
-
- },
+ mixins: [ Navigation, State ],
propTypes: {
- to: React.PropTypes.string.isRequired,
activeClassName: React.PropTypes.string.isRequired,
+ to: React.PropTypes.string.isRequired,
params: React.PropTypes.object,
query: React.PropTypes.object,
onClick: React.PropTypes.func
@@ -170,17 +132,30 @@ var Link = React.createClass({
};
},
- getInitialState: function () {
- return {
- isActive: false
- };
+ handleClick: function (event) {
+ var allowTransition = true;
+ var clickResult;
+
+ if (this.props.onClick)
+ clickResult = this.props.onClick(event);
+
+ if (isModifiedEvent(event) || !isLeftClickEvent(event))
+ return;
+
+ if (clickResult === false || event.defaultPrevented === true)
+ allowTransition = false;
+
+ event.preventDefault();
+
+ if (allowTransition)
+ this.transitionTo(this.props.to, this.props.params, this.props.query);
},
/**
* Returns the value of the "href" attribute to use on the DOM element.
*/
getHref: function () {
- return makeHref(this.props.to, Link.getParams(this.props), this.props.query);
+ return this.makeHref(this.props.to, this.props.params, this.props.query);
},
/**
@@ -188,59 +163,23 @@ var Link = React.createClass({
* the value of the activeClassName property when this <Link> is active.
*/
getClassName: function () {
- var className = this.props.className || '';
-
- if (this.state.isActive)
- return className + ' ' + this.props.activeClassName;
+ var classNames = {};
- return className;
- },
-
- componentWillReceiveProps: function (nextProps) {
- var params = Link.getParams(nextProps);
+ if (this.props.className)
+ classNames[this.props.className] = true;
- this.setState({
- isActive: Link.isActive(nextProps.to, params, nextProps.query)
- });
- },
+ if (this.isActive(this.props.to, this.props.params, this.props.query))
+ classNames[this.props.activeClassName] = true;
- updateActiveState: function () {
- this.setState({
- isActive: Link.isActive(this.props.to, Link.getParams(this.props), this.props.query)
- });
- },
-
- handleClick: function (event) {
- var allowTransition = true;
- var ret;
-
- if (this.props.onClick)
- ret = this.props.onClick(event);
-
- if (isModifiedEvent(event) || !isLeftClickEvent(event))
- return;
-
- if (ret === false || event.defaultPrevented === true)
- allowTransition = false;
-
- event.preventDefault();
-
- if (allowTransition)
- transitionTo(this.props.to, Link.getParams(this.props), this.props.query);
+ return classSet(classNames);
},
render: function () {
- var props = {
+ var props = assign({}, this.props, {
href: this.getHref(),
className: this.getClassName(),
onClick: this.handleClick
- };
-
- // pull in props without overriding
- for (var propName in this.props) {
- if (hasOwnProperty(this.props, propName) && hasOwnProperty(props, propName) === false)
- props[propName] = this.props[propName];
- }
+ });
return React.DOM.a(props, this.props.children);
}
@@ -249,9 +188,10 @@ var Link = React.createClass({
module.exports = Link;
-},{"../actions/LocationActions":1,"../mixins/ActiveState":15,"../utils/hasOwnProperty":24,"../utils/makeHref":25,"../utils/withoutProperties":29,"react/lib/warning":48}],4:[function(_dereq_,module,exports){
-var merge = _dereq_('react/lib/merge');
-var Route = _dereq_('./Route');
+},{"../mixins/Navigation":14,"../mixins/State":17,"react/lib/Object.assign":36,"react/lib/cx":37}],5:[function(_dereq_,module,exports){
+var React = (typeof window !== "undefined" ? window.React : typeof global !== "undefined" ? global.React : null);
+var FakeNode = _dereq_('../mixins/FakeNode');
+var PropTypes = _dereq_('../utils/PropTypes');
/**
* A <NotFoundRoute> is a special kind of <Route> that
@@ -260,65 +200,51 @@ var Route = _dereq_('./Route');
* Only one such route may be used at any given level in the
* route hierarchy.
*/
-function NotFoundRoute(props) {
- return Route(
- merge(props, {
- path: null,
- catchAll: true
- })
- );
-}
+var NotFoundRoute = React.createClass({
-module.exports = NotFoundRoute;
+ displayName: 'NotFoundRoute',
-},{"./Route":6,"react/lib/merge":44}],5:[function(_dereq_,module,exports){
-var React = (typeof window !== "undefined" ? window.React : typeof global !== "undefined" ? global.React : null);
-var Route = _dereq_('./Route');
+ mixins: [ FakeNode ],
-function createRedirectHandler(to) {
- return React.createClass({
- statics: {
- willTransitionTo: function (transition, params, query) {
- transition.redirect(to, params, query);
- }
- },
+ propTypes: {
+ name: React.PropTypes.string,
+ path: PropTypes.falsy,
+ handler: React.PropTypes.func.isRequired
+ }
- render: function () {
- return null;
- }
- });
-}
+});
+
+module.exports = NotFoundRoute;
+
+},{"../mixins/FakeNode":13,"../utils/PropTypes":21}],6:[function(_dereq_,module,exports){
+var React = (typeof window !== "undefined" ? window.React : typeof global !== "undefined" ? global.React : null);
+var FakeNode = _dereq_('../mixins/FakeNode');
+var PropTypes = _dereq_('../utils/PropTypes');
/**
* A <Redirect> component is a special kind of <Route> that always
* redirects to another route when it matches.
*/
-function Redirect(props) {
- return Route({
- name: props.name,
- path: props.from || props.path || '*',
- handler: createRedirectHandler(props.to)
- });
-}
+var Redirect = React.createClass({
+
+ displayName: 'Redirect',
+
+ mixins: [ FakeNode ],
+
+ propTypes: {
+ path: React.PropTypes.string,
+ from: React.PropTypes.string, // Alias for path.
+ to: React.PropTypes.string,
+ handler: PropTypes.falsy
+ }
+
+});
module.exports = Redirect;
-},{"./Route":6}],6:[function(_dereq_,module,exports){
+},{"../mixins/FakeNode":13,"../utils/PropTypes":21}],7:[function(_dereq_,module,exports){
var React = (typeof window !== "undefined" ? window.React : typeof global !== "undefined" ? global.React : null);
-var withoutProperties = _dereq_('../utils/withoutProperties');
-
-/**
- * A map of <Route> component props that are reserved for use by the
- * router and/or React. All other props are considered "static" and
- * are passed through to the route handler.
- */
-var RESERVED_PROPS = {
- handler: true,
- path: true,
- defaultRoute: true,
- paramNames: true,
- children: true // ReactChildren
-};
+var FakeNode = _dereq_('../mixins/FakeNode');
/**
* <Route> components specify components that are rendered to the page when the
@@ -330,40 +256,29 @@ var RESERVED_PROPS = {
* "active" and their components are rendered into the DOM, nested in the same
* order as they are in the tree.
*
- * Unlike Ember, a nested route's path does not build upon that of its parents.
- * This may seem like it creates more work up front in specifying URLs, but it
- * has the nice benefit of decoupling nested UI from "nested" URLs.
- *
* The preferred way to configure a router is using JSX. The XML-like syntax is
* a great way to visualize how routes are laid out in an application.
*
- * React.renderComponent((
- * <Routes handler={App}>
+ * var routes = [
+ * <Route handler={App}>
* <Route name="login" handler={Login}/>
* <Route name="logout" handler={Logout}/>
* <Route name="about" handler={About}/>
- * </Routes>
- * ), document.body);
- *
- * If you don't use JSX, you can also assemble a Router programmatically using
- * the standard React component JavaScript API.
- *
- * React.renderComponent((
- * Routes({ handler: App },
- * Route({ name: 'login', handler: Login }),
- * Route({ name: 'logout', handler: Logout }),
- * Route({ name: 'about', handler: About })
- * )
- * ), document.body);
+ * </Route>
+ * ];
+ *
+ * Router.run(routes, function (Handler) {
+ * React.render(<Handler/>, document.body);
+ * });
*
* Handlers for Route components that contain children can render their active
- * child route using the activeRouteHandler prop.
+ * child route using a <RouteHandler> element.
*
* var App = React.createClass({
* render: function () {
* return (
* <div class="application">
- * {this.props.activeRouteHandler()}
+ * <RouteHandler/>
* </div>
* );
* }
@@ -373,542 +288,119 @@ var Route = React.createClass({
displayName: 'Route',
- statics: {
-
- getUnreservedProps: function (props) {
- return withoutProperties(props, RESERVED_PROPS);
- },
-
- },
+ mixins: [ FakeNode ],
propTypes: {
- preserveScrollPosition: React.PropTypes.bool.isRequired,
- handler: React.PropTypes.any.isRequired,
+ name: React.PropTypes.string,
path: React.PropTypes.string,
- name: React.PropTypes.string
- },
-
- getDefaultProps: function () {
- return {
- preserveScrollPosition: false
- };
- },
-
- render: function () {
- throw new Error(
- 'The <Route> component should not be rendered directly. You may be ' +
- 'missing a <Routes> wrapper around your list of routes.'
- );
+ handler: React.PropTypes.func.isRequired,
+ ignoreScrollBehavior: React.PropTypes.bool
}
});
module.exports = Route;
-},{"../utils/withoutProperties":29}],7:[function(_dereq_,module,exports){
+},{"../mixins/FakeNode":13}],8:[function(_dereq_,module,exports){
var React = (typeof window !== "undefined" ? window.React : typeof global !== "undefined" ? global.React : null);
-var warning = _dereq_('react/lib/warning');
-var copyProperties = _dereq_('react/lib/copyProperties');
-var Promise = _dereq_('when/lib/Promise');
-var LocationActions = _dereq_('../actions/LocationActions');
-var Route = _dereq_('../components/Route');
-var Path = _dereq_('../utils/Path');
-var Redirect = _dereq_('../utils/Redirect');
-var Transition = _dereq_('../utils/Transition');
-var DefaultLocation = _dereq_('../locations/DefaultLocation');
-var HashLocation = _dereq_('../locations/HashLocation');
-var HistoryLocation = _dereq_('../locations/HistoryLocation');
-var RefreshLocation = _dereq_('../locations/RefreshLocation');
-var ActiveStore = _dereq_('../stores/ActiveStore');
-var PathStore = _dereq_('../stores/PathStore');
-var RouteStore = _dereq_('../stores/RouteStore');
/**
- * The ref name that can be used to reference the active route component.
+ * A <RouteHandler> component renders the active child route handler
+ * when routes are nested.
*/
-var REF_NAME = '__activeRoute__';
+var RouteHandler = React.createClass({
-/**
- * A hash of { name, location } pairs of all locations.
- */
-var NAMED_LOCATIONS = {
- hash: HashLocation,
- history: HistoryLocation,
- refresh: RefreshLocation
-};
-
-/**
- * The default handler for aborted transitions. Redirects replace
- * the current URL and all others roll it back.
- */
-function defaultAbortedTransitionHandler(transition) {
- var reason = transition.abortReason;
-
- if (reason instanceof Redirect) {
- LocationActions.replaceWith(reason.to, reason.params, reason.query);
- } else {
- LocationActions.goBack();
- }
-}
-
-/**
- * The default handler for active state updates.
- */
-function defaultActiveStateChangeHandler(state) {
- ActiveStore.updateState(state);
-}
-
-/**
- * The default handler for errors that were thrown asynchronously
- * while transitioning. The default behavior is to re-throw the
- * error so that it isn't silently swallowed.
- */
-function defaultTransitionErrorHandler(error) {
- throw error; // This error probably originated in a transition hook.
-}
-
-function maybeUpdateScroll(routes, rootRoute) {
- if (!routes.props.preserveScrollPosition && !rootRoute.props.preserveScrollPosition)
- LocationActions.updateScroll();
-}
-
-/**
- * The <Routes> component configures the route hierarchy and renders the
- * route matching the current location when rendered into a document.
- *
- * See the <Route> component for more details.
- */
-var Routes = React.createClass({
-
- displayName: 'Routes',
-
- propTypes: {
- onAbortedTransition: React.PropTypes.func.isRequired,
- onActiveStateChange: React.PropTypes.func.isRequired,
- onTransitionError: React.PropTypes.func.isRequired,
- preserveScrollPosition: React.PropTypes.bool,
- location: function (props, propName, componentName) {
- var location = props[propName];
-
- if (typeof location === 'string' && !(location in NAMED_LOCATIONS))
- return new Error('Unknown location "' + location + '", see ' + componentName);
- }
- },
+ displayName: 'RouteHandler',
getDefaultProps: function () {
return {
- onAbortedTransition: defaultAbortedTransitionHandler,
- onActiveStateChange: defaultActiveStateChangeHandler,
- onTransitionError: defaultTransitionErrorHandler,
- preserveScrollPosition: false,
- location: DefaultLocation
+ ref: '__routeHandler__'
};
},
- getInitialState: function () {
- return {
- routes: RouteStore.registerChildren(this.props.children, this)
- };
+ contextTypes: {
+ getRouteAtDepth: React.PropTypes.func.isRequired,
+ getRouteComponents: React.PropTypes.func.isRequired,
+ routeHandlers: React.PropTypes.array.isRequired
},
- getLocation: function () {
- var location = this.props.location;
-
- if (typeof location === 'string')
- return NAMED_LOCATIONS[location];
-
- return location;
+ childContextTypes: {
+ routeHandlers: React.PropTypes.array.isRequired
},
- componentWillMount: function () {
- PathStore.setup(this.getLocation());
- PathStore.addChangeListener(this.handlePathChange);
- },
-
- componentDidMount: function () {
- this.handlePathChange();
+ getChildContext: function () {
+ return {
+ routeHandlers: this.context.routeHandlers.concat([ this ])
+ };
},
- componentWillUnmount: function () {
- PathStore.removeChangeListener(this.handlePathChange);
+ getRouteDepth: function () {
+ return this.context.routeHandlers.length - 1;
},
- handlePathChange: function () {
- this.dispatch(PathStore.getCurrentPath());
+ componentDidMount: function () {
+ this._updateRouteComponent();
},
- /**
- * Performs a depth-first search for the first route in the tree that matches
- * on the given path. Returns an array of all routes in the tree leading to
- * the one that matched in the format { route, params } where params is an
- * object that contains the URL parameters relevant to that route. Returns
- * null if no route in the tree matches the path.
- *
- * React.renderComponent(
- * <Routes>
- * <Route handler={App}>
- * <Route name="posts" handler={Posts}/>
- * <Route name="post" path="/posts/:id" handler={Post}/>
- * </Route>
- * </Routes>
- * ).match('/posts/123'); => [ { route: <AppRoute>, params: {} },
- * { route: <PostRoute>, params: { id: '123' } } ]
- */
- match: function (path) {
- return findMatches(Path.withoutQuery(path), this.state.routes, this.props.defaultRoute, this.props.notFoundRoute);
+ componentDidUpdate: function () {
+ this._updateRouteComponent();
},
- /**
- * Performs a transition to the given path and returns a promise for the
- * Transition object that was used.
- *
- * In order to do this, the router first determines which routes are involved
- * in the transition beginning with the current route, up the route tree to
- * the first parent route that is shared with the destination route, and back
- * down the tree to the destination route. The willTransitionFrom static
- * method is invoked on all route handlers we're transitioning away from, in
- * reverse nesting order. Likewise, the willTransitionTo static method
- * is invoked on all route handlers we're transitioning to.
- *
- * Both willTransitionFrom and willTransitionTo hooks may either abort or
- * redirect the transition. If they need to resolve asynchronously, they may
- * return a promise.
- *
- * Any error that occurs asynchronously during the transition is re-thrown in
- * the top-level scope unless returnRejectedPromise is true, in which case a
- * rejected promise is returned so the caller may handle the error.
- *
- * Note: This function does not update the URL in a browser's location bar.
- * If you want to keep the URL in sync with transitions, use Router.transitionTo,
- * Router.replaceWith, or Router.goBack instead.
- */
- dispatch: function (path, returnRejectedPromise) {
- var transition = new Transition(path);
- var routes = this;
-
- var promise = runTransitionHooks(routes, transition).then(function (nextState) {
- if (transition.isAborted) {
- routes.props.onAbortedTransition(transition);
- } else if (nextState) {
- routes.setState(nextState);
- routes.props.onActiveStateChange(nextState);
-
- // TODO: add functional test
- var rootMatch = getRootMatch(nextState.matches);
-
- if (rootMatch)
- maybeUpdateScroll(routes, rootMatch.route);
- }
-
- return transition;
- });
-
- if (!returnRejectedPromise) {
- promise = promise.then(undefined, function (error) {
- // Use setTimeout to break the promise chain.
- setTimeout(function () {
- routes.props.onTransitionError(error);
- });
- });
- }
-
- return promise;
+ _updateRouteComponent: function () {
+ var depth = this.getRouteDepth();
+ var components = this.context.getRouteComponents();
+ components[depth] = this.refs[this.props.ref];
},
render: function () {
- if (!this.state.path)
- return null;
-
- var matches = this.state.matches;
- if (matches.length) {
- // matches[0] corresponds to the top-most match
- return matches[0].route.props.handler(computeHandlerProps(matches, this.state.activeQuery));
- } else {
- return null;
- }
+ var route = this.context.getRouteAtDepth(this.getRouteDepth());
+ return route ? React.createElement(route.handler, this.props) : null;
}
});
-function findMatches(path, routes, defaultRoute, notFoundRoute) {
- var matches = null, route, params;
-
- for (var i = 0, len = routes.length; i < len; ++i) {
- route = routes[i];
-
- // Check the subtree first to find the most deeply-nested match.
- matches = findMatches(path, route.props.children, route.props.defaultRoute, route.props.notFoundRoute);
-
- if (matches != null) {
- var rootParams = getRootMatch(matches).params;
-
- params = route.props.paramNames.reduce(function (params, paramName) {
- params[paramName] = rootParams[paramName];
- return params;
- }, {});
-
- matches.unshift(makeMatch(route, params));
-
- return matches;
- }
-
- // No routes in the subtree matched, so check this route.
- params = Path.extractParams(route.props.path, path);
-
- if (params)
- return [ makeMatch(route, params) ];
- }
-
- // No routes matched, so try the default route if there is one.
- if (defaultRoute && (params = Path.extractParams(defaultRoute.props.path, path)))
- return [ makeMatch(defaultRoute, params) ];
-
- // Last attempt: does the "not found" route match?
- if (notFoundRoute && (params = Path.extractParams(notFoundRoute.props.path, path)))
- return [ makeMatch(notFoundRoute, params) ];
-
- return matches;
-}
-
-function makeMatch(route, params) {
- return { route: route, params: params };
-}
-
-function hasMatch(matches, match) {
- return matches.some(function (m) {
- if (m.route !== match.route)
- return false;
-
- for (var property in m.params) {
- if (m.params[property] !== match.params[property])
- return false;
- }
-
- return true;
- });
-}
-
-function getRootMatch(matches) {
- return matches[matches.length - 1];
-}
-
-function updateMatchComponents(matches, refs) {
- var i = 0, component;
- while (component = refs[REF_NAME]) {
- matches[i++].component = component;
- refs = component.refs;
- }
-}
-
-/**
- * Runs all transition hooks that are required to get from the current state
- * to the state specified by the given transition and updates the current state
- * if they all pass successfully. Returns a promise that resolves to the new
- * state if it needs to be updated, or undefined if not.
- */
-function runTransitionHooks(routes, transition) {
- if (routes.state.path === transition.path)
- return Promise.resolve(); // Nothing to do!
-
- var currentMatches = routes.state.matches;
- var nextMatches = routes.match(transition.path);
-
- warning(
- nextMatches,
- 'No route matches path "' + transition.path + '". Make sure you have ' +
- '<Route path="' + transition.path + '"> somewhere in your routes'
- );
-
- if (!nextMatches)
- nextMatches = [];
-
- var fromMatches, toMatches;
- if (currentMatches) {
- updateMatchComponents(currentMatches, routes.refs);
-
- fromMatches = currentMatches.filter(function (match) {
- return !hasMatch(nextMatches, match);
- });
-
- toMatches = nextMatches.filter(function (match) {
- return !hasMatch(currentMatches, match);
- });
- } else {
- fromMatches = [];
- toMatches = nextMatches;
- }
-
- var query = Path.extractQuery(transition.path) || {};
-
- return runTransitionFromHooks(fromMatches, transition).then(function () {
- if (transition.isAborted)
- return; // No need to continue.
-
- return runTransitionToHooks(toMatches, transition, query).then(function () {
- if (transition.isAborted)
- return; // No need to continue.
-
- var rootMatch = getRootMatch(nextMatches);
- var params = (rootMatch && rootMatch.params) || {};
-
- return {
- path: transition.path,
- matches: nextMatches,
- activeParams: params,
- activeQuery: query,
- activeRoutes: nextMatches.map(function (match) {
- return match.route;
- })
- };
- });
- });
-}
-
-/**
- * Calls the willTransitionFrom hook of all handlers in the given matches
- * serially in reverse with the transition object and the current instance of
- * the route's handler, so that the deepest nested handlers are called first.
- * Returns a promise that resolves after the last handler.
- */
-function runTransitionFromHooks(matches, transition) {
- var promise = Promise.resolve();
-
- reversedArray(matches).forEach(function (match) {
- promise = promise.then(function () {
- var handler = match.route.props.handler;
-
- if (!transition.isAborted && handler.willTransitionFrom)
- return handler.willTransitionFrom(transition, match.component);
- });
- });
-
- return promise;
-}
-
-/**
- * Calls the willTransitionTo hook of all handlers in the given matches serially
- * with the transition object and any params that apply to that handler. Returns
- * a promise that resolves after the last handler.
- */
-function runTransitionToHooks(matches, transition, query) {
- var promise = Promise.resolve();
-
- matches.forEach(function (match) {
- promise = promise.then(function () {
- var handler = match.route.props.handler;
-
- if (!transition.isAborted && handler.willTransitionTo)
- return handler.willTransitionTo(transition, match.params, query);
- });
- });
-
- return promise;
-}
-
-/**
- * Given an array of matches as returned by findMatches, return a descriptor for
- * the handler hierarchy specified by the route.
- */
-function computeHandlerProps(matches, query) {
- var props = {
- ref: null,
- key: null,
- params: null,
- query: null,
- activeRouteHandler: returnNull
- };
-
- var childHandler;
- reversedArray(matches).forEach(function (match) {
- var route = match.route;
-
- props = Route.getUnreservedProps(route.props);
-
- props.ref = REF_NAME;
- props.params = match.params;
- props.query = query;
-
- if (route.props.addHandlerKey)
- props.key = Path.injectParams(route.props.path, match.params);
-
- if (childHandler) {
- props.activeRouteHandler = childHandler;
- } else {
- props.activeRouteHandler = returnNull;
- }
-
- childHandler = function (props, addedProps) {
- if (arguments.length > 2 && typeof arguments[2] !== 'undefined')
- throw new Error('Passing children to a route handler is not supported');
-
- return route.props.handler(copyProperties(props, addedProps));
- }.bind(this, props);
- });
-
- return props;
-}
-
-function returnNull() {
- return null;
-}
-
-function reversedArray(array) {
- return array.slice(0).reverse();
-}
-
-module.exports = Routes;
-
-},{"../actions/LocationActions":1,"../components/Route":6,"../locations/DefaultLocation":10,"../locations/HashLocation":11,"../locations/HistoryLocation":12,"../locations/RefreshLocation":14,"../stores/ActiveStore":17,"../stores/PathStore":18,"../stores/RouteStore":19,"../utils/Path":20,"../utils/Redirect":21,"../utils/Transition":22,"react/lib/copyProperties":40,"react/lib/warning":48,"when/lib/Promise":49}],8:[function(_dereq_,module,exports){
-var copyProperties = _dereq_('react/lib/copyProperties');
-var Dispatcher = _dereq_('flux').Dispatcher;
-
-/**
- * Dispatches actions that modify the URL.
- */
-var LocationDispatcher = copyProperties(new Dispatcher, {
-
- handleViewAction: function (action) {
- this.dispatch({
- source: 'VIEW_ACTION',
- action: action
- });
- }
-
-});
-
-module.exports = LocationDispatcher;
-
-},{"flux":31,"react/lib/copyProperties":40}],9:[function(_dereq_,module,exports){
-exports.goBack = _dereq_('./actions/LocationActions').goBack;
-exports.replaceWith = _dereq_('./actions/LocationActions').replaceWith;
-exports.transitionTo = _dereq_('./actions/LocationActions').transitionTo;
+module.exports = RouteHandler;
+},{}],9:[function(_dereq_,module,exports){
exports.DefaultRoute = _dereq_('./components/DefaultRoute');
exports.Link = _dereq_('./components/Link');
exports.NotFoundRoute = _dereq_('./components/NotFoundRoute');
exports.Redirect = _dereq_('./components/Redirect');
exports.Route = _dereq_('./components/Route');
-exports.Routes = _dereq_('./components/Routes');
+exports.RouteHandler = _dereq_('./components/RouteHandler');
-exports.ActiveState = _dereq_('./mixins/ActiveState');
-exports.AsyncState = _dereq_('./mixins/AsyncState');
+exports.HashLocation = _dereq_('./locations/HashLocation');
+exports.HistoryLocation = _dereq_('./locations/HistoryLocation');
+exports.RefreshLocation = _dereq_('./locations/RefreshLocation');
-exports.makeHref = _dereq_('./utils/makeHref');
+exports.Navigation = _dereq_('./mixins/Navigation');
+exports.State = _dereq_('./mixins/State');
-},{"./actions/LocationActions":1,"./components/DefaultRoute":2,"./components/Link":3,"./components/NotFoundRoute":4,"./components/Redirect":5,"./components/Route":6,"./components/Routes":7,"./mixins/ActiveState":15,"./mixins/AsyncState":16,"./utils/makeHref":25}],10:[function(_dereq_,module,exports){
-module.exports = "production" === 'test'
- ? _dereq_('./MemoryLocation')
- : _dereq_('./HashLocation');
+exports.create = _dereq_('./utils/createRouter');
+exports.run = _dereq_('./utils/runRouter');
-},{"./HashLocation":11,"./MemoryLocation":13}],11:[function(_dereq_,module,exports){
+},{"./components/DefaultRoute":3,"./components/Link":4,"./components/NotFoundRoute":5,"./components/Redirect":6,"./components/Route":7,"./components/RouteHandler":8,"./locations/HashLocation":10,"./locations/HistoryLocation":11,"./locations/RefreshLocation":12,"./mixins/Navigation":14,"./mixins/State":17,"./utils/createRouter":24,"./utils/runRouter":28}],10:[function(_dereq_,module,exports){
var invariant = _dereq_('react/lib/invariant');
-var ExecutionEnvironment = _dereq_('react/lib/ExecutionEnvironment');
-var getWindowPath = _dereq_('../utils/getWindowPath');
+var canUseDOM = _dereq_('react/lib/ExecutionEnvironment').canUseDOM;
+var LocationActions = _dereq_('../actions/LocationActions');
+var Path = _dereq_('../utils/Path');
+/**
+ * Returns the current URL path from `window.location.hash`, including query string
+ */
function getHashPath() {
- return window.location.hash.substr(1);
+ invariant(
+ canUseDOM,
+ 'getHashPath needs a DOM'
+ );
+
+ return Path.decode(
+ window.location.hash.substr(1)
+ );
}
+var _actionType;
+
function ensureSlash() {
var path = getHashPath();
@@ -920,11 +412,30 @@ function ensureSlash() {
return false;
}
-var _onChange;
+var _changeListeners = [];
+
+function notifyChange(type) {
+ var change = {
+ path: getHashPath(),
+ type: type
+ };
+
+ _changeListeners.forEach(function (listener) {
+ listener(change);
+ });
+}
+
+var _isListening = false;
-function handleHashChange() {
- if (ensureSlash())
- _onChange();
+function onHashChange() {
+ if (ensureSlash()) {
+ // If we don't have an _actionType then all we know is the hash
+ // changed. It was probably caused by the user clicking the Back
+ // button, but may have also been the Forward button or manual
+ // manipulation. So just guess 'pop'.
+ notifyChange(_actionType || LocationActions.POP);
+ _actionType = null;
+ }
}
/**
@@ -932,40 +443,36 @@ function handleHashChange() {
*/
var HashLocation = {
- setup: function (onChange) {
- invariant(
- ExecutionEnvironment.canUseDOM,
- 'You cannot use HashLocation in an environment with no DOM'
- );
-
- _onChange = onChange;
+ addChangeListener: function (listener) {
+ _changeListeners.push(listener);
+ // Do this BEFORE listening for hashchange.
ensureSlash();
+ if (_isListening)
+ return;
+
if (window.addEventListener) {
- window.addEventListener('hashchange', handleHashChange, false);
+ window.addEventListener('hashchange', onHashChange, false);
} else {
- window.attachEvent('onhashchange', handleHashChange);
+ window.attachEvent('onhashchange', onHashChange);
}
- },
- teardown: function () {
- if (window.removeEventListener) {
- window.removeEventListener('hashchange', handleHashChange, false);
- } else {
- window.detachEvent('onhashchange', handleHashChange);
- }
+ _isListening = true;
},
push: function (path) {
- window.location.hash = path;
+ _actionType = LocationActions.PUSH;
+ window.location.hash = Path.encode(path);
},
replace: function (path) {
- window.location.replace(getWindowPath() + '#' + path);
+ _actionType = LocationActions.REPLACE;
+ window.location.replace(window.location.pathname + '#' + Path.encode(path));
},
pop: function () {
+ _actionType = LocationActions.POP;
window.history.back();
},
@@ -979,49 +486,73 @@ var HashLocation = {
module.exports = HashLocation;
-},{"../utils/getWindowPath":23,"react/lib/ExecutionEnvironment":39,"react/lib/invariant":42}],12:[function(_dereq_,module,exports){
+},{"../actions/LocationActions":1,"../utils/Path":19,"react/lib/ExecutionEnvironment":35,"react/lib/invariant":39}],11:[function(_dereq_,module,exports){
var invariant = _dereq_('react/lib/invariant');
-var ExecutionEnvironment = _dereq_('react/lib/ExecutionEnvironment');
-var getWindowPath = _dereq_('../utils/getWindowPath');
+var canUseDOM = _dereq_('react/lib/ExecutionEnvironment').canUseDOM;
+var LocationActions = _dereq_('../actions/LocationActions');
+var Path = _dereq_('../utils/Path');
+
+/**
+ * Returns the current URL path from `window.location`, including query string
+ */
+function getWindowPath() {
+ invariant(
+ canUseDOM,
+ 'getWindowPath needs a DOM'
+ );
+
+ return Path.decode(
+ window.location.pathname + window.location.search
+ );
+}
+
+var _changeListeners = [];
+
+function notifyChange(type) {
+ var change = {
+ path: getWindowPath(),
+ type: type
+ };
+
+ _changeListeners.forEach(function (listener) {
+ listener(change);
+ });
+}
+
+var _isListening = false;
-var _onChange;
+function onPopState() {
+ notifyChange(LocationActions.POP);
+}
/**
* A Location that uses HTML5 history.
*/
var HistoryLocation = {
- setup: function (onChange) {
- invariant(
- ExecutionEnvironment.canUseDOM,
- 'You cannot use HistoryLocation in an environment with no DOM'
- );
+ addChangeListener: function (listener) {
+ _changeListeners.push(listener);
- _onChange = onChange;
+ if (_isListening)
+ return;
if (window.addEventListener) {
- window.addEventListener('popstate', _onChange, false);
+ window.addEventListener('popstate', onPopState, false);
} else {
- window.attachEvent('popstate', _onChange);
+ window.attachEvent('popstate', onPopState);
}
- },
- teardown: function () {
- if (window.removeEventListener) {
- window.removeEventListener('popstate', _onChange, false);
- } else {
- window.detachEvent('popstate', _onChange);
- }
+ _isListening = true;
},
push: function (path) {
- window.history.pushState({ path: path }, '', path);
- _onChange();
+ window.history.pushState({ path: path }, '', Path.encode(path));
+ notifyChange(LocationActions.PUSH);
},
replace: function (path) {
- window.history.replaceState({ path: path }, '', path);
- _onChange();
+ window.history.replaceState({ path: path }, '', Path.encode(path));
+ notifyChange(LocationActions.REPLACE);
},
pop: function () {
@@ -1038,60 +569,9 @@ var HistoryLocation = {
module.exports = HistoryLocation;
-},{"../utils/getWindowPath":23,"react/lib/ExecutionEnvironment":39,"react/lib/invariant":42}],13:[function(_dereq_,module,exports){
-var warning = _dereq_('react/lib/warning');
-
-var _lastPath = null;
-var _currentPath = null;
-var _onChange;
-
-/**
- * A Location that does not require a DOM.
- */
-var MemoryLocation = {
-
- setup: function (onChange) {
- _onChange = onChange;
- },
-
- push: function (path) {
- _lastPath = _currentPath;
- _currentPath = path;
- _onChange();
- },
-
- replace: function (path) {
- _currentPath = path;
- _onChange();
- },
-
- pop: function () {
- warning(
- _lastPath != null,
- 'You cannot use MemoryLocation to go back more than once'
- );
-
- _currentPath = _lastPath;
- _lastPath = null;
- _onChange();
- },
-
- getCurrentPath: function () {
- return _currentPath || '/';
- },
-
- toString: function () {
- return '<MemoryLocation>';
- }
-
-};
-
-module.exports = MemoryLocation;
-
-},{"react/lib/warning":48}],14:[function(_dereq_,module,exports){
-var invariant = _dereq_('react/lib/invariant');
-var ExecutionEnvironment = _dereq_('react/lib/ExecutionEnvironment');
-var getWindowPath = _dereq_('../utils/getWindowPath');
+},{"../actions/LocationActions":1,"../utils/Path":19,"react/lib/ExecutionEnvironment":35,"react/lib/invariant":39}],12:[function(_dereq_,module,exports){
+var HistoryLocation = _dereq_('./HistoryLocation');
+var Path = _dereq_('../utils/Path');
/**
* A Location that uses full page refreshes. This is used as
@@ -1100,26 +580,19 @@ var getWindowPath = _dereq_('../utils/getWindowPath');
*/
var RefreshLocation = {
- setup: function () {
- invariant(
- ExecutionEnvironment.canUseDOM,
- 'You cannot use RefreshLocation in an environment with no DOM'
- );
- },
-
push: function (path) {
- window.location = path;
+ window.location = Path.encode(path);
},
replace: function (path) {
- window.location.replace(path);
+ window.location.replace(Path.encode(path));
},
pop: function () {
window.history.back();
},
- getCurrentPath: getWindowPath,
+ getCurrentPath: HistoryLocation.getCurrentPath,
toString: function () {
return '<RefreshLocation>';
@@ -1129,579 +602,357 @@ var RefreshLocation = {
module.exports = RefreshLocation;
-},{"../utils/getWindowPath":23,"react/lib/ExecutionEnvironment":39,"react/lib/invariant":42}],15:[function(_dereq_,module,exports){
-var ActiveStore = _dereq_('../stores/ActiveStore');
-
-/**
- * A mixin for components that need to know about the routes, params,
- * and query that are currently active. Components that use it get two
- * things:
- *
- * 1. An `isActive` static method they can use to check if a route,
- * params, and query are active.
- * 2. An `updateActiveState` instance method that is called when the
- * active state changes.
- *
- * Example:
- *
- * var Tab = React.createClass({
- *
- * mixins: [ Router.ActiveState ],
- *
- * getInitialState: function () {
- * return {
- * isActive: false
- * };
- * },
- *
- * updateActiveState: function () {
- * this.setState({
- * isActive: Tab.isActive(routeName, params, query)
- * })
- * }
- *
- * });
- */
-var ActiveState = {
-
- statics: {
-
- /**
- * Returns true if the route with the given name, URL parameters, and query
- * are all currently active.
- */
- isActive: ActiveStore.isActive
-
- },
-
- componentWillMount: function () {
- ActiveStore.addChangeListener(this.handleActiveStateChange);
- },
-
- componentDidMount: function () {
- if (this.updateActiveState)
- this.updateActiveState();
- },
+},{"../utils/Path":19,"./HistoryLocation":11}],13:[function(_dereq_,module,exports){
+var invariant = _dereq_('react/lib/invariant');
- componentWillUnmount: function () {
- ActiveStore.removeChangeListener(this.handleActiveStateChange);
- },
+var FakeNode = {
- handleActiveStateChange: function () {
- if (this.isMounted() && typeof this.updateActiveState === 'function')
- this.updateActiveState();
+ render: function () {
+ invariant(
+ false,
+ '%s elements should not be rendered',
+ this.constructor.displayName
+ );
}
};
-module.exports = ActiveState;
+module.exports = FakeNode;
-},{"../stores/ActiveStore":17}],16:[function(_dereq_,module,exports){
+},{"react/lib/invariant":39}],14:[function(_dereq_,module,exports){
var React = (typeof window !== "undefined" ? window.React : typeof global !== "undefined" ? global.React : null);
-var resolveAsyncState = _dereq_('../utils/resolveAsyncState');
/**
- * A mixin for route handler component classes that fetch at least
- * part of their state asynchronously. Classes that use it should
- * declare a static `getInitialAsyncState` method that fetches state
- * for a component after it mounts. This function is given three
- * arguments: 1) the current route params, 2) the current query and
- * 3) a function that can be used to set state as it is received.
- *
- * Much like the familiar `getInitialState` method, `getInitialAsyncState`
- * should return a hash of key/value pairs to use in the component's
- * state. The difference is that the values may be promises. As these
- * values resolve, the component's state is updated. You should only
- * ever need to use the setState function for doing things like
- * streaming data and/or updating progress.
+ * A mixin for components that modify the URL.
*
* Example:
*
- * var User = React.createClass({
- *
- * statics: {
- *
- * getInitialAsyncState: function (params, query, setState) {
- * // Return a hash with keys named after the state variables
- * // you want to set, as you normally do in getInitialState,
- * // except the values may be immediate values or promises.
- * // The state is automatically updated as promises resolve.
- * return {
- * user: getUserByID(params.userID) // may be a promise
- * };
- *
- * // Or, use the setState function to stream data!
- * var buffer = '';
- *
- * return {
- *
- * // Same as above, the stream state variable is set to the
- * // value returned by this promise when it resolves.
- * stream: getStreamingData(params.userID, function (chunk) {
- * buffer += chunk;
- *
- * // Notify of progress.
- * setState({
- * streamBuffer: buffer
- * });
- * })
- *
- * };
- * }
- *
+ * var MyLink = React.createClass({
+ * mixins: [ Router.Navigation ],
+ * handleClick: function (event) {
+ * event.preventDefault();
+ * this.transitionTo('aRoute', { the: 'params' }, { the: 'query' });
* },
- *
- * getInitialState: function () {
- * return {
- * user: null, // Receives a value when getUserByID resolves.
- * stream: null, // Receives a value when getStreamingData resolves.
- * streamBuffer: '' // Used to track data as it loads.
- * };
- * },
- *
* render: function () {
- * if (!this.state.user)
- * return <LoadingUser/>;
- *
* return (
- * <div>
- * <p>Welcome {this.state.user.name}!</p>
- * <p>So far, you've received {this.state.streamBuffer.length} data!</p>
- * </div>
+ * <a onClick={this.handleClick}>Click me!</a>
* );
* }
- *
* });
- *
- * When testing, use the `initialAsyncState` prop to simulate asynchronous
- * data fetching. When this prop is present, no attempt is made to retrieve
- * additional state via `getInitialAsyncState`.
*/
-var AsyncState = {
+var Navigation = {
- propTypes: {
- initialAsyncState: React.PropTypes.object
+ contextTypes: {
+ makePath: React.PropTypes.func.isRequired,
+ makeHref: React.PropTypes.func.isRequired,
+ transitionTo: React.PropTypes.func.isRequired,
+ replaceWith: React.PropTypes.func.isRequired,
+ goBack: React.PropTypes.func.isRequired
},
- getInitialState: function () {
- return this.props.initialAsyncState || null;
+ /**
+ * Returns an absolute URL path created from the given route
+ * name, URL parameters, and query values.
+ */
+ makePath: function (to, params, query) {
+ return this.context.makePath(to, params, query);
},
- updateAsyncState: function (state) {
- if (this.isMounted())
- this.setState(state);
+ /**
+ * Returns a string that may safely be used as the href of a
+ * link to the route with the given name.
+ */
+ makeHref: function (to, params, query) {
+ return this.context.makeHref(to, params, query);
},
- componentDidMount: function () {
- if (this.props.initialAsyncState || typeof this.constructor.getInitialAsyncState !== 'function')
- return;
+ /**
+ * Transitions to the URL specified in the arguments by pushing
+ * a new URL onto the history stack.
+ */
+ transitionTo: function (to, params, query) {
+ this.context.transitionTo(to, params, query);
+ },
- resolveAsyncState(
- this.constructor.getInitialAsyncState(this.props.params, this.props.query, this.updateAsyncState),
- this.updateAsyncState
- );
+ /**
+ * Transitions to the URL specified in the arguments by replacing
+ * the current URL in the history stack.
+ */
+ replaceWith: function (to, params, query) {
+ this.context.replaceWith(to, params, query);
+ },
+
+ /**
+ * Transitions to the previous URL.
+ */
+ goBack: function () {
+ this.context.goBack();
}
};
-module.exports = AsyncState;
-
-},{"../utils/resolveAsyncState":27}],17:[function(_dereq_,module,exports){
-var EventEmitter = _dereq_('events').EventEmitter;
-
-var CHANGE_EVENT = 'change';
-var _events = new EventEmitter;
-
-_events.setMaxListeners(0);
+module.exports = Navigation;
-function notifyChange() {
- _events.emit(CHANGE_EVENT);
-}
+},{}],15:[function(_dereq_,module,exports){
+var React = (typeof window !== "undefined" ? window.React : typeof global !== "undefined" ? global.React : null);
-var _activeRoutes = [];
-var _activeParams = {};
-var _activeQuery = {};
+/**
+ * Provides the router with context for Router.Navigation.
+ */
+var NavigationContext = {
-function routeIsActive(routeName) {
- return _activeRoutes.some(function (route) {
- return route.props.name === routeName;
- });
-}
+ childContextTypes: {
+ makePath: React.PropTypes.func.isRequired,
+ makeHref: React.PropTypes.func.isRequired,
+ transitionTo: React.PropTypes.func.isRequired,
+ replaceWith: React.PropTypes.func.isRequired,
+ goBack: React.PropTypes.func.isRequired
+ },
-function paramsAreActive(params) {
- for (var property in params) {
- if (_activeParams[property] !== String(params[property]))
- return false;
+ getChildContext: function () {
+ return {
+ makePath: this.constructor.makePath,
+ makeHref: this.constructor.makeHref,
+ transitionTo: this.constructor.transitionTo,
+ replaceWith: this.constructor.replaceWith,
+ goBack: this.constructor.goBack
+ };
}
- return true;
-}
+};
-function queryIsActive(query) {
- for (var property in query) {
- if (_activeQuery[property] !== String(query[property]))
- return false;
- }
+module.exports = NavigationContext;
- return true;
-}
+},{}],16:[function(_dereq_,module,exports){
+var invariant = _dereq_('react/lib/invariant');
+var canUseDOM = _dereq_('react/lib/ExecutionEnvironment').canUseDOM;
+var getWindowScrollPosition = _dereq_('../utils/getWindowScrollPosition');
/**
- * The ActiveStore keeps track of which routes, URL and query parameters are
- * currently active on a page. <Link>s subscribe to the ActiveStore to know
- * whether or not they are active.
+ * Provides the router with the ability to manage window scroll position
+ * according to its scroll behavior.
*/
-var ActiveStore = {
+var Scrolling = {
- addChangeListener: function (listener) {
- _events.on(CHANGE_EVENT, listener);
+ componentWillMount: function () {
+ invariant(
+ this.getScrollBehavior() == null || canUseDOM,
+ 'Cannot use scroll behavior without a DOM'
+ );
+
+ this._scrollHistory = {};
},
- removeChangeListener: function (listener) {
- _events.removeListener(CHANGE_EVENT, listener);
+ componentDidMount: function () {
+ this._updateScroll();
},
- /**
- * Updates the currently active state and notifies all listeners.
- * This is automatically called by routes as they become active.
- */
- updateState: function (state) {
- state = state || {};
+ componentWillUpdate: function () {
+ this._scrollHistory[this.state.path] = getWindowScrollPosition();
+ },
- _activeRoutes = state.activeRoutes || [];
- _activeParams = state.activeParams || {};
- _activeQuery = state.activeQuery || {};
+ componentDidUpdate: function () {
+ this._updateScroll();
+ },
- notifyChange();
+ componentWillUnmount: function () {
+ delete this._scrollHistory;
},
/**
- * Returns true if the route with the given name, URL parameters, and query
- * are all currently active.
+ * Returns the last known scroll position for the given URL path.
*/
- isActive: function (routeName, params, query) {
- var isActive = routeIsActive(routeName) && paramsAreActive(params);
+ getScrollPosition: function (path) {
+ return this._scrollHistory[path] || null;
+ },
- if (query)
- return isActive && queryIsActive(query);
+ _updateScroll: function () {
+ var scrollBehavior = this.getScrollBehavior();
- return isActive;
+ if (scrollBehavior)
+ scrollBehavior.updateScrollPosition(
+ this.getScrollPosition(this.state.path),
+ this.state.action
+ );
}
};
-module.exports = ActiveStore;
-
-},{"events":30}],18:[function(_dereq_,module,exports){
-var warning = _dereq_('react/lib/warning');
-var EventEmitter = _dereq_('events').EventEmitter;
-var LocationActions = _dereq_('../actions/LocationActions');
-var LocationDispatcher = _dereq_('../dispatchers/LocationDispatcher');
-var supportsHistory = _dereq_('../utils/supportsHistory');
-var HistoryLocation = _dereq_('../locations/HistoryLocation');
-var RefreshLocation = _dereq_('../locations/RefreshLocation');
-
-var CHANGE_EVENT = 'change';
-var _events = new EventEmitter;
-
-function notifyChange() {
- _events.emit(CHANGE_EVENT);
-}
-
-var _scrollPositions = {};
-
-function recordScrollPosition(path) {
- _scrollPositions[path] = {
- x: window.scrollX,
- y: window.scrollY
- };
-}
-
-function updateScrollPosition(path) {
- var p = PathStore.getScrollPosition(path);
- window.scrollTo(p.x, p.y);
-}
+module.exports = Scrolling;
-var _location;
+},{"../utils/getWindowScrollPosition":26,"react/lib/ExecutionEnvironment":35,"react/lib/invariant":39}],17:[function(_dereq_,module,exports){
+var React = (typeof window !== "undefined" ? window.React : typeof global !== "undefined" ? global.React : null);
/**
- * The PathStore keeps track of the current URL path and manages
- * the location strategy that is used to update the URL.
+ * A mixin for components that need to know the path, routes, URL
+ * params and query that are currently active.
+ *
+ * Example:
+ *
+ * var AboutLink = React.createClass({
+ * mixins: [ Router.State ],
+ * render: function () {
+ * var className = this.props.className;
+ *
+ * if (this.isActive('about'))
+ * className += ' is-active';
+ *
+ * return React.DOM.a({ className: className }, this.props.children);
+ * }
+ * });
*/
-var PathStore = {
-
- addChangeListener: function (listener) {
- _events.on(CHANGE_EVENT, listener);
- },
+var State = {
- removeChangeListener: function (listener) {
- _events.removeListener(CHANGE_EVENT, listener);
-
- // Automatically teardown when the last listener is removed.
- if (EventEmitter.listenerCount(_events, CHANGE_EVENT) === 0)
- PathStore.teardown();
+ contextTypes: {
+ getCurrentPath: React.PropTypes.func.isRequired,
+ getCurrentRoutes: React.PropTypes.func.isRequired,
+ getCurrentParams: React.PropTypes.func.isRequired,
+ getCurrentQuery: React.PropTypes.func.isRequired,
+ isActive: React.PropTypes.func.isRequired
},
- setup: function (location) {
- // When using HistoryLocation, automatically fallback
- // to RefreshLocation in browsers that do not support
- // the HTML5 history API.
- if (location === HistoryLocation && !supportsHistory())
- location = RefreshLocation;
-
- if (_location == null) {
- _location = location;
-
- if (_location && typeof _location.setup === 'function')
- _location.setup(notifyChange);
- } else {
- warning(
- _location === location,
- 'Cannot use location %s, already using %s', location, _location
- );
- }
- },
-
- teardown: function () {
- _events.removeAllListeners(CHANGE_EVENT);
-
- if (_location && typeof _location.teardown === 'function')
- _location.teardown();
-
- _location = null;
+ /**
+ * Returns the current URL path.
+ */
+ getPath: function () {
+ return this.context.getCurrentPath();
},
/**
- * Returns the location object currently in use.
+ * Returns an array of the routes that are currently active.
*/
- getLocation: function () {
- return _location;
+ getRoutes: function () {
+ return this.context.getCurrentRoutes();
},
/**
- * Returns the current URL path.
+ * Returns an object of the URL params that are currently active.
*/
- getCurrentPath: function () {
- return _location.getCurrentPath();
+ getParams: function () {
+ return this.context.getCurrentParams();
},
/**
- * Returns the last known scroll position for the given path.
+ * Returns an object of the query params that are currently active.
*/
- getScrollPosition: function (path) {
- return _scrollPositions[path] || { x: 0, y: 0 };
+ getQuery: function () {
+ return this.context.getCurrentQuery();
},
- dispatchToken: LocationDispatcher.register(function (payload) {
- var action = payload.action;
- var currentPath = _location.getCurrentPath();
+ /**
+ * A helper method to determine if a given route, params, and query
+ * are active.
+ */
+ isActive: function (to, params, query) {
+ return this.context.isActive(to, params, query);
+ }
- switch (action.type) {
- case LocationActions.PUSH:
- if (currentPath !== action.path) {
- recordScrollPosition(currentPath);
- _location.push(action.path);
- }
- break;
+};
- case LocationActions.REPLACE:
- if (currentPath !== action.path) {
- recordScrollPosition(currentPath);
- _location.replace(action.path);
- }
- break;
+module.exports = State;
- case LocationActions.POP:
- recordScrollPosition(currentPath);
- _location.pop();
- break;
+},{}],18:[function(_dereq_,module,exports){
+var React = (typeof window !== "undefined" ? window.React : typeof global !== "undefined" ? global.React : null);
+var assign = _dereq_('react/lib/Object.assign');
+var Path = _dereq_('../utils/Path');
- case LocationActions.UPDATE_SCROLL:
- updateScrollPosition(currentPath);
- break;
- }
- })
+function routeIsActive(activeRoutes, routeName) {
+ return activeRoutes.some(function (route) {
+ return route.name === routeName;
+ });
+}
-};
+function paramsAreActive(activeParams, params) {
+ for (var property in params)
+ if (String(activeParams[property]) !== String(params[property]))
+ return false;
-module.exports = PathStore;
+ return true;
+}
-},{"../actions/LocationActions":1,"../dispatchers/LocationDispatcher":8,"../locations/HistoryLocation":12,"../locations/RefreshLocation":14,"../utils/supportsHistory":28,"events":30,"react/lib/warning":48}],19:[function(_dereq_,module,exports){
-var React = (typeof window !== "undefined" ? window.React : typeof global !== "undefined" ? global.React : null);
-var invariant = _dereq_('react/lib/invariant');
-var warning = _dereq_('react/lib/warning');
-var Path = _dereq_('../utils/Path');
+function queryIsActive(activeQuery, query) {
+ for (var property in query)
+ if (String(activeQuery[property]) !== String(query[property]))
+ return false;
-var _namedRoutes = {};
+ return true;
+}
/**
- * The RouteStore contains a directory of all <Route>s in the system. It is
- * used primarily for looking up routes by name so that <Link>s can use a
- * route name in the "to" prop and users can use route names in `Router.transitionTo`
- * and other high-level utility methods.
+ * Provides the router with context for Router.State.
*/
-var RouteStore = {
+var StateContext = {
/**
- * Removes all references to <Route>s from the store. Should only ever
- * really be used in tests to clear the store between test runs.
+ * Returns the current URL path + query string.
*/
- unregisterAllRoutes: function () {
- _namedRoutes = {};
+ getCurrentPath: function () {
+ return this.state.path;
},
/**
- * Removes the reference to the given <Route> and all of its children
- * from the store.
+ * Returns a read-only array of the currently active routes.
*/
- unregisterRoute: function (route) {
- var props = route.props;
-
- if (props.name)
- delete _namedRoutes[props.name];
-
- React.Children.forEach(props.children, RouteStore.unregisterRoute);
+ getCurrentRoutes: function () {
+ return this.state.routes.slice(0);
},
/**
- * Registers a <Route> and all of its children with the store. Also,
- * does some normalization and validation on route props.
+ * Returns a read-only object of the currently active URL parameters.
*/
- registerRoute: function (route, parentRoute) {
- // Note: parentRoute may be a <Route> _or_ a <Routes>.
- var props = route.props;
-
- invariant(
- React.isValidClass(props.handler),
- 'The handler for the "%s" route must be a valid React class',
- props.name || props.path
- );
-
- var parentPath = (parentRoute && parentRoute.props.path) || '/';
-
- if ((props.path || props.name) && !props.isDefault && !props.catchAll) {
- var path = props.path || props.name;
-
- // Relative paths extend their parent.
- if (!Path.isAbsolute(path))
- path = Path.join(parentPath, path);
-
- props.path = Path.normalize(path);
- } else {
- props.path = parentPath;
-
- if (props.catchAll)
- props.path += '*';
- }
-
- props.paramNames = Path.extractParamNames(props.path);
-
- // Make sure the route's path has all params its parent needs.
- if (parentRoute && Array.isArray(parentRoute.props.paramNames)) {
- parentRoute.props.paramNames.forEach(function (paramName) {
- invariant(
- props.paramNames.indexOf(paramName) !== -1,
- 'The nested route path "%s" is missing the "%s" parameter of its parent path "%s"',
- props.path, paramName, parentRoute.props.path
- );
- });
- }
-
- // Make sure the route can be looked up by <Link>s.
- if (props.name) {
- var existingRoute = _namedRoutes[props.name];
-
- invariant(
- !existingRoute || route === existingRoute,
- 'You cannot use the name "%s" for more than one route',
- props.name
- );
-
- _namedRoutes[props.name] = route;
- }
-
- if (props.catchAll) {
- invariant(
- parentRoute,
- '<NotFoundRoute> must have a parent <Route>'
- );
-
- invariant(
- parentRoute.props.notFoundRoute == null,
- 'You may not have more than one <NotFoundRoute> per <Route>'
- );
-
- parentRoute.props.notFoundRoute = route;
-
- return null;
- }
-
- if (props.isDefault) {
- invariant(
- parentRoute,
- '<DefaultRoute> must have a parent <Route>'
- );
-
- invariant(
- parentRoute.props.defaultRoute == null,
- 'You may not have more than one <DefaultRoute> per <Route>'
- );
-
- parentRoute.props.defaultRoute = route;
-
- return null;
- }
-
- // Make sure children is an array.
- props.children = RouteStore.registerChildren(props.children, route);
+ getCurrentParams: function () {
+ return assign({}, this.state.params);
+ },
- return route;
+ /**
+ * Returns a read-only object of the currently active query parameters.
+ */
+ getCurrentQuery: function () {
+ return assign({}, this.state.query);
},
/**
- * Registers many children routes at once, always returning an array.
+ * Returns true if the given route, params, and query are active.
*/
- registerChildren: function (children, parentRoute) {
- var routes = [];
+ isActive: function (to, params, query) {
+ if (Path.isAbsolute(to))
+ return to === this.state.path;
- React.Children.forEach(children, function (child) {
- // Exclude <DefaultRoute>s.
- if (child = RouteStore.registerRoute(child, parentRoute))
- routes.push(child);
- });
+ return routeIsActive(this.state.routes, to) &&
+ paramsAreActive(this.state.params, params) &&
+ (query == null || queryIsActive(this.state.query, query));
+ },
- return routes;
+ childContextTypes: {
+ getCurrentPath: React.PropTypes.func.isRequired,
+ getCurrentRoutes: React.PropTypes.func.isRequired,
+ getCurrentParams: React.PropTypes.func.isRequired,
+ getCurrentQuery: React.PropTypes.func.isRequired,
+ isActive: React.PropTypes.func.isRequired
},
- /**
- * Returns the Route object with the given name, if one exists.
- */
- getRouteByName: function (routeName) {
- return _namedRoutes[routeName] || null;
+ getChildContext: function () {
+ return {
+ getCurrentPath: this.getCurrentPath,
+ getCurrentRoutes: this.getCurrentRoutes,
+ getCurrentParams: this.getCurrentParams,
+ getCurrentQuery: this.getCurrentQuery,
+ isActive: this.isActive
+ };
}
};
-module.exports = RouteStore;
+module.exports = StateContext;
-},{"../utils/Path":20,"react/lib/invariant":42,"react/lib/warning":48}],20:[function(_dereq_,module,exports){
+},{"../utils/Path":19,"react/lib/Object.assign":36}],19:[function(_dereq_,module,exports){
var invariant = _dereq_('react/lib/invariant');
var merge = _dereq_('qs/lib/utils').merge;
var qs = _dereq_('qs');
-function encodeURL(url) {
- return encodeURIComponent(url).replace(/%20/g, '+');
-}
-
-function decodeURL(url) {
- return decodeURIComponent(url.replace(/\+/g, ' '));
-}
-
-function encodeURLPath(path) {
- return String(path).split('/').map(encodeURL).join('/');
-}
-
-var paramMatcher = /:([a-zA-Z_$][a-zA-Z0-9_$]*)|[*.()\[\]\\+|{}^$]/g;
+var paramCompileMatcher = /:([a-zA-Z_$][a-zA-Z0-9_$]*)|[*.()\[\]\\+|{}^$]/g;
+var paramInjectMatcher = /:([a-zA-Z_$][a-zA-Z0-9_$?]*[?]?)|[*]/g;
+var paramInjectTrailingSlashMatcher = /\/\/\?|\/\?/g;
var queryMatcher = /\?(.+)/;
var _compiledPatterns = {};
@@ -1709,10 +960,10 @@ var _compiledPatterns = {};
function compilePattern(pattern) {
if (!(pattern in _compiledPatterns)) {
var paramNames = [];
- var source = pattern.replace(paramMatcher, function (match, paramName) {
+ var source = pattern.replace(paramCompileMatcher, function (match, paramName) {
if (paramName) {
paramNames.push(paramName);
- return '([^./?#]+)';
+ return '([^/?#]+)';
} else if (match === '*') {
paramNames.push('splat');
return '(.*?)';
@@ -1733,6 +984,20 @@ function compilePattern(pattern) {
var Path = {
/**
+ * Safely decodes special characters in the given URL path.
+ */
+ decode: function (path) {
+ return decodeURI(path.replace(/\+/g, ' '));
+ },
+
+ /**
+ * Safely encodes special characters in the given URL path.
+ */
+ encode: function (path) {
+ return encodeURI(path).replace(/%20/g, '+');
+ },
+
+ /**
* Returns an array of the names of all parameters in the given pattern.
*/
extractParamNames: function (pattern) {
@@ -1746,7 +1011,7 @@ var Path = {
*/
extractParams: function (pattern, path) {
var object = compilePattern(pattern);
- var match = decodeURL(path).match(object.matcher);
+ var match = path.match(object.matcher);
if (!match)
return null;
@@ -1769,13 +1034,21 @@ var Path = {
var splatIndex = 0;
- return pattern.replace(paramMatcher, function (match, paramName) {
+ return pattern.replace(paramInjectMatcher, function (match, paramName) {
paramName = paramName || 'splat';
- invariant(
- params[paramName] != null,
- 'Missing "' + paramName + '" parameter for path "' + pattern + '"'
- );
+ // If param is optional don't check for existence
+ if (paramName.slice(-1) !== '?') {
+ invariant(
+ params[paramName] != null,
+ 'Missing "' + paramName + '" parameter for path "' + pattern + '"'
+ );
+ } else {
+ paramName = paramName.slice(0, -1);
+
+ if (params[paramName] == null)
+ return '';
+ }
var segment;
if (paramName === 'splat' && Array.isArray(params[paramName])) {
@@ -1789,8 +1062,8 @@ var Path = {
segment = params[paramName];
}
- return encodeURLPath(segment);
- });
+ return segment;
+ }).replace(paramInjectTrailingSlashMatcher, '/');
},
/**
@@ -1798,7 +1071,7 @@ var Path = {
* in the given path, null if the path contains no query string.
*/
extractQuery: function (path) {
- var match = decodeURL(path).match(queryMatcher);
+ var match = path.match(queryMatcher);
return match && qs.parse(match[1]);
},
@@ -1852,7 +1125,30 @@ var Path = {
module.exports = Path;
-},{"qs":34,"qs/lib/utils":38,"react/lib/invariant":42}],21:[function(_dereq_,module,exports){
+},{"qs":30,"qs/lib/utils":34,"react/lib/invariant":39}],20:[function(_dereq_,module,exports){
+var Promise = _dereq_('when/lib/Promise');
+
+// TODO: Use process.env.NODE_ENV check + envify to enable
+// when's promise monitor here when in dev.
+
+module.exports = Promise;
+
+},{"when/lib/Promise":41}],21:[function(_dereq_,module,exports){
+var PropTypes = {
+
+ /**
+ * Requires that the value of a prop be falsy.
+ */
+ falsy: function (props, propName, elementName) {
+ if (props[propName])
+ return new Error('<' + elementName + '> may not have a "' + propName + '" prop');
+ }
+
+};
+
+module.exports = PropTypes;
+
+},{}],22:[function(_dereq_,module,exports){
/**
* Encapsulates a redirect to the given route.
*/
@@ -1864,10 +1160,89 @@ function Redirect(to, params, query) {
module.exports = Redirect;
-},{}],22:[function(_dereq_,module,exports){
-var mixInto = _dereq_('react/lib/mixInto');
-var transitionTo = _dereq_('../actions/LocationActions').transitionTo;
+},{}],23:[function(_dereq_,module,exports){
+var assign = _dereq_('react/lib/Object.assign');
+var reversedArray = _dereq_('./reversedArray');
var Redirect = _dereq_('./Redirect');
+var Promise = _dereq_('./Promise');
+
+/**
+ * Runs all hook functions serially and calls callback(error) when finished.
+ * A hook may return a promise if it needs to execute asynchronously.
+ */
+function runHooks(hooks, callback) {
+ try {
+ var promise = hooks.reduce(function (promise, hook) {
+ // The first hook to use transition.wait makes the rest
+ // of the transition async from that point forward.
+ return promise ? promise.then(hook) : hook();
+ }, null);
+ } catch (error) {
+ return callback(error); // Sync error.
+ }
+
+ if (promise) {
+ // Use setTimeout to break the promise chain.
+ promise.then(function () {
+ setTimeout(callback);
+ }, function (error) {
+ setTimeout(function () {
+ callback(error);
+ });
+ });
+ } else {
+ callback();
+ }
+}
+
+/**
+ * Calls the willTransitionFrom hook of all handlers in the given matches
+ * serially in reverse with the transition object and the current instance of
+ * the route's handler, so that the deepest nested handlers are called first.
+ * Calls callback(error) when finished.
+ */
+function runTransitionFromHooks(transition, routes, components, callback) {
+ components = reversedArray(components);
+
+ var hooks = reversedArray(routes).map(function (route, index) {
+ return function () {
+ var handler = route.handler;
+
+ if (!transition.isAborted && handler.willTransitionFrom)
+ return handler.willTransitionFrom(transition, components[index]);
+
+ var promise = transition._promise;
+ transition._promise = null;
+
+ return promise;
+ };
+ });
+
+ runHooks(hooks, callback);
+}
+
+/**
+ * Calls the willTransitionTo hook of all handlers in the given matches
+ * serially with the transition object and any params that apply to that
+ * handler. Calls callback(error) when finished.
+ */
+function runTransitionToHooks(transition, routes, params, query, callback) {
+ var hooks = routes.map(function (route) {
+ return function () {
+ var handler = route.handler;
+
+ if (!transition.isAborted && handler.willTransitionTo)
+ handler.willTransitionTo(transition, params, query);
+
+ var promise = transition._promise;
+ transition._promise = null;
+
+ return promise;
+ };
+ });
+
+ runHooks(hooks, callback);
+}
/**
* Encapsulates a transition to a given path.
@@ -1875,13 +1250,15 @@ var Redirect = _dereq_('./Redirect');
* The willTransitionTo and willTransitionFrom handlers receive
* an instance of this class as their first argument.
*/
-function Transition(path) {
+function Transition(path, retry) {
this.path = path;
this.abortReason = null;
this.isAborted = false;
+ this.retry = retry.bind(this);
+ this._promise = null;
}
-mixInto(Transition, {
+assign(Transition.prototype, {
abort: function (reason) {
this.abortReason = reason;
@@ -1892,763 +1269,715 @@ mixInto(Transition, {
this.abort(new Redirect(to, params, query));
},
- retry: function () {
- transitionTo(this.path);
+ wait: function (value) {
+ this._promise = Promise.resolve(value);
+ },
+
+ from: function (routes, components, callback) {
+ return runTransitionFromHooks(this, routes, components, callback);
+ },
+
+ to: function (routes, params, query, callback) {
+ return runTransitionToHooks(this, routes, params, query, callback);
}
});
module.exports = Transition;
-},{"../actions/LocationActions":1,"./Redirect":21,"react/lib/mixInto":47}],23:[function(_dereq_,module,exports){
+},{"./Promise":20,"./Redirect":22,"./reversedArray":27,"react/lib/Object.assign":36}],24:[function(_dereq_,module,exports){
+var React = (typeof window !== "undefined" ? window.React : typeof global !== "undefined" ? global.React : null);
+var warning = _dereq_('react/lib/warning');
+var invariant = _dereq_('react/lib/invariant');
+var canUseDOM = _dereq_('react/lib/ExecutionEnvironment').canUseDOM;
+var ImitateBrowserBehavior = _dereq_('../behaviors/ImitateBrowserBehavior');
+var RouteHandler = _dereq_('../components/RouteHandler');
+var HashLocation = _dereq_('../locations/HashLocation');
+var HistoryLocation = _dereq_('../locations/HistoryLocation');
+var RefreshLocation = _dereq_('../locations/RefreshLocation');
+var NavigationContext = _dereq_('../mixins/NavigationContext');
+var StateContext = _dereq_('../mixins/StateContext');
+var Scrolling = _dereq_('../mixins/Scrolling');
+var createRoutesFromChildren = _dereq_('./createRoutesFromChildren');
+var supportsHistory = _dereq_('./supportsHistory');
+var Transition = _dereq_('./Transition');
+var PropTypes = _dereq_('./PropTypes');
+var Redirect = _dereq_('./Redirect');
+var Path = _dereq_('./Path');
+
/**
- * Returns the current URL path from `window.location`, including query string
+ * The default location for new routers.
*/
-function getWindowPath() {
- return window.location.pathname + window.location.search;
-}
-
-module.exports = getWindowPath;
-
+var DEFAULT_LOCATION = canUseDOM ? HashLocation : '/';
-},{}],24:[function(_dereq_,module,exports){
-module.exports = Function.prototype.call.bind(Object.prototype.hasOwnProperty);
-
-},{}],25:[function(_dereq_,module,exports){
-var HashLocation = _dereq_('../locations/HashLocation');
-var PathStore = _dereq_('../stores/PathStore');
-var makePath = _dereq_('./makePath');
+/**
+ * The default scroll behavior for new routers.
+ */
+var DEFAULT_SCROLL_BEHAVIOR = canUseDOM ? ImitateBrowserBehavior : null;
/**
- * Returns a string that may safely be used as the href of a
- * link to the route with the given name.
+ * The default error handler for new routers.
*/
-function makeHref(to, params, query) {
- var path = makePath(to, params, query);
+function defaultErrorHandler(error) {
+ // Throw so we don't silently swallow async errors.
+ throw error; // This error probably originated in a transition hook.
+}
- if (PathStore.getLocation() === HashLocation)
- return '#' + path;
+/**
+ * The default aborted transition handler for new routers.
+ */
+function defaultAbortHandler(abortReason, location) {
+ if (typeof location === 'string')
+ throw new Error('Unhandled aborted transition! Reason: ' + abortReason);
- return path;
+ if (abortReason instanceof Redirect) {
+ location.replace(this.makePath(abortReason.to, abortReason.params, abortReason.query));
+ } else {
+ location.pop();
+ }
}
-module.exports = makeHref;
+function findMatch(pathname, routes, defaultRoute, notFoundRoute) {
+ var match, route, params;
-},{"../locations/HashLocation":11,"../stores/PathStore":18,"./makePath":26}],26:[function(_dereq_,module,exports){
-var invariant = _dereq_('react/lib/invariant');
-var RouteStore = _dereq_('../stores/RouteStore');
-var Path = _dereq_('./Path');
+ for (var i = 0, len = routes.length; i < len; ++i) {
+ route = routes[i];
-/**
- * Returns an absolute URL path created from the given route name, URL
- * parameters, and query values.
- */
-function makePath(to, params, query) {
- var path;
- if (Path.isAbsolute(to)) {
- path = Path.normalize(to);
- } else {
- var route = RouteStore.getRouteByName(to);
+ // Check the subtree first to find the most deeply-nested match.
+ match = findMatch(pathname, route.childRoutes, route.defaultRoute, route.notFoundRoute);
- invariant(
- route,
- 'Unable to find a route named "' + to + '". Make sure you have ' +
- 'a <Route name="' + to + '"> defined somewhere in your routes'
- );
+ if (match != null) {
+ match.routes.unshift(route);
+ return match;
+ }
+
+ // No routes in the subtree matched, so check this route.
+ params = Path.extractParams(route.path, pathname);
- path = route.props.path;
+ if (params)
+ return createMatch(route, params);
}
- return Path.withQuery(Path.injectParams(path, params), query);
+ // No routes matched, so try the default route if there is one.
+ if (defaultRoute && (params = Path.extractParams(defaultRoute.path, pathname)))
+ return createMatch(defaultRoute, params);
+
+ // Last attempt: does the "not found" route match?
+ if (notFoundRoute && (params = Path.extractParams(notFoundRoute.path, pathname)))
+ return createMatch(notFoundRoute, params);
+
+ return match;
}
-module.exports = makePath;
+function createMatch(route, params) {
+ return { routes: [ route ], params: params };
+}
-},{"../stores/RouteStore":19,"./Path":20,"react/lib/invariant":42}],27:[function(_dereq_,module,exports){
-var Promise = _dereq_('when/lib/Promise');
+function hasMatch(routes, route, prevParams, nextParams) {
+ return routes.some(function (r) {
+ if (r !== route)
+ return false;
+
+ var paramNames = route.paramNames;
+ var paramName;
+
+ for (var i = 0, len = paramNames.length; i < len; ++i) {
+ paramName = paramNames[i];
+
+ if (nextParams[paramName] !== prevParams[paramName])
+ return false;
+ }
+
+ return true;
+ });
+}
/**
- * Resolves all values in asyncState and calls the setState
- * function with new state as they resolve. Returns a promise
- * that resolves after all values are resolved.
+ * Creates and returns a new router using the given options. A router
+ * is a ReactComponent class that knows how to react to changes in the
+ * URL and keep the contents of the page in sync.
+ *
+ * Options may be any of the following:
+ *
+ * - routes (required) The route config
+ * - location The location to use. Defaults to HashLocation when
+ * the DOM is available, "/" otherwise
+ * - scrollBehavior The scroll behavior to use. Defaults to ImitateBrowserBehavior
+ * when the DOM is available, null otherwise
+ * - onError A function that is used to handle errors
+ * - onAbort A function that is used to handle aborted transitions
+ *
+ * When rendering in a server-side environment, the location should simply
+ * be the URL path that was used in the request, including the query string.
*/
-function resolveAsyncState(asyncState, setState) {
- if (asyncState == null)
- return Promise.resolve();
-
- var keys = Object.keys(asyncState);
-
- return Promise.all(
- keys.map(function (key) {
- return Promise.resolve(asyncState[key]).then(function (value) {
- var newState = {};
- newState[key] = value;
- setState(newState);
- });
- })
- );
-}
+function createRouter(options) {
+ options = options || {};
-module.exports = resolveAsyncState;
+ if (typeof options === 'function') {
+ options = { routes: options }; // Router.create(<Route>)
+ } else if (Array.isArray(options)) {
+ options = { routes: options }; // Router.create([ <Route>, <Route> ])
+ }
-},{"when/lib/Promise":49}],28:[function(_dereq_,module,exports){
-function supportsHistory() {
- /*! taken from modernizr
- * https://github.com/Modernizr/Modernizr/blob/master/LICENSE
- * https://github.com/Modernizr/Modernizr/blob/master/feature-detects/history.js
- */
- var ua = navigator.userAgent;
- if ((ua.indexOf('Android 2.') !== -1 ||
- (ua.indexOf('Android 4.0') !== -1)) &&
- ua.indexOf('Mobile Safari') !== -1 &&
- ua.indexOf('Chrome') === -1) {
- return false;
+ var routes = [];
+ var namedRoutes = {};
+ var components = [];
+ var location = options.location || DEFAULT_LOCATION;
+ var scrollBehavior = options.scrollBehavior || DEFAULT_SCROLL_BEHAVIOR;
+ var onError = options.onError || defaultErrorHandler;
+ var onAbort = options.onAbort || defaultAbortHandler;
+ var state = {};
+ var nextState = {};
+
+ function updateState() {
+ state = nextState;
+ nextState = {};
}
- return (window.history && 'pushState' in window.history);
-}
-module.exports = supportsHistory;
+ // Automatically fall back to full page refreshes in
+ // browsers that don't support the HTML history API.
+ if (location === HistoryLocation && !supportsHistory())
+ location = RefreshLocation;
-},{}],29:[function(_dereq_,module,exports){
-function withoutProperties(object, properties) {
- var result = {};
+ var router = React.createClass({
- for (var property in object) {
- if (object.hasOwnProperty(property) && !properties[property])
- result[property] = object[property];
- }
+ displayName: 'Router',
- return result;
-}
+ mixins: [ NavigationContext, StateContext, Scrolling ],
-module.exports = withoutProperties;
+ statics: {
-},{}],30:[function(_dereq_,module,exports){
-// Copyright Joyent, Inc. and other Node contributors.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to permit
-// persons to whom the Software is furnished to do so, subject to the
-// following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
-// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
-// USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-function EventEmitter() {
- this._events = this._events || {};
- this._maxListeners = this._maxListeners || undefined;
-}
-module.exports = EventEmitter;
+ defaultRoute: null,
+ notFoundRoute: null,
+
+ /**
+ * Adds routes to this router from the given children object (see ReactChildren).
+ */
+ addRoutes: function (children) {
+ routes.push.apply(routes, createRoutesFromChildren(children, this, namedRoutes));
+ },
+
+ /**
+ * Returns an absolute URL path created from the given route
+ * name, URL parameters, and query.
+ */
+ makePath: function (to, params, query) {
+ var path;
+ if (Path.isAbsolute(to)) {
+ path = Path.normalize(to);
+ } else {
+ var route = namedRoutes[to];
+
+ invariant(
+ route,
+ 'Unable to find <Route name="%s">',
+ to
+ );
+
+ path = route.path;
+ }
-// Backwards-compat with node 0.10.x
-EventEmitter.EventEmitter = EventEmitter;
+ return Path.withQuery(Path.injectParams(path, params), query);
+ },
+
+ /**
+ * Returns a string that may safely be used as the href of a link
+ * to the route with the given name, URL parameters, and query.
+ */
+ makeHref: function (to, params, query) {
+ var path = this.makePath(to, params, query);
+ return (location === HashLocation) ? '#' + path : path;
+ },
+
+ /**
+ * Transitions to the URL specified in the arguments by pushing
+ * a new URL onto the history stack.
+ */
+ transitionTo: function (to, params, query) {
+ invariant(
+ typeof location !== 'string',
+ 'You cannot use transitionTo with a static location'
+ );
-EventEmitter.prototype._events = undefined;
-EventEmitter.prototype._maxListeners = undefined;
+ location.push(this.makePath(to, params, query));
+ },
-// By default EventEmitters will print a warning if more than 10 listeners are
-// added to it. This is a useful default which helps finding memory leaks.
-EventEmitter.defaultMaxListeners = 10;
+ /**
+ * Transitions to the URL specified in the arguments by replacing
+ * the current URL in the history stack.
+ */
+ replaceWith: function (to, params, query) {
+ invariant(
+ typeof location !== 'string',
+ 'You cannot use replaceWith with a static location'
+ );
-// Obviously not all Emitters should be limited to 10. This function allows
-// that to be increased. Set to zero for unlimited.
-EventEmitter.prototype.setMaxListeners = function(n) {
- if (!isNumber(n) || n < 0 || isNaN(n))
- throw TypeError('n must be a positive number');
- this._maxListeners = n;
- return this;
-};
+ location.replace(this.makePath(to, params, query));
+ },
-EventEmitter.prototype.emit = function(type) {
- var er, handler, len, args, i, listeners;
+ /**
+ * Transitions to the previous URL.
+ */
+ goBack: function () {
+ invariant(
+ typeof location !== 'string',
+ 'You cannot use goBack with a static location'
+ );
- if (!this._events)
- this._events = {};
+ location.pop();
+ },
+
+ /**
+ * Performs a match of the given path against this router and returns an object with
+ * the { path, routes, params, query } that match. Returns null if no match can be made.
+ */
+ match: function (path) {
+ return findMatch(Path.withoutQuery(path), routes, this.defaultRoute, this.notFoundRoute) || null;
+ },
+
+ /**
+ * Performs a transition to the given path and calls callback(error, abortReason)
+ * when the transition is finished. If both arguments are null the router's state
+ * was updated. Otherwise the transition did not complete.
+ *
+ * In a transition, a router first determines which routes are involved by beginning
+ * with the current route, up the route tree to the first parent route that is shared
+ * with the destination route, and back down the tree to the destination route. The
+ * willTransitionFrom hook is invoked on all route handlers we're transitioning away
+ * from, in reverse nesting order. Likewise, the willTransitionTo hook is invoked on
+ * all route handlers we're transitioning to.
+ *
+ * Both willTransitionFrom and willTransitionTo hooks may either abort or redirect the
+ * transition. To resolve asynchronously, they may use transition.wait(promise). If no
+ * hooks wait, the transition is fully synchronous.
+ */
+ dispatch: function (path, action, callback) {
+ if (state.path === path)
+ return; // Nothing to do!
+
+ var match = this.match(path);
+
+ warning(
+ match != null,
+ 'No route matches path "%s". Make sure you have <Route path="%s"> somewhere in your routes',
+ path, path
+ );
- // If there is no 'error' event listener then throw.
- if (type === 'error') {
- if (!this._events.error ||
- (isObject(this._events.error) && !this._events.error.length)) {
- er = arguments[1];
- if (er instanceof Error) {
- throw er; // Unhandled 'error' event
- } else {
- throw TypeError('Uncaught, unspecified "error" event.');
- }
- return false;
- }
- }
+ if (match == null)
+ match = {};
- handler = this._events[type];
+ var prevRoutes = state.routes || [];
+ var prevParams = state.params || {};
- if (isUndefined(handler))
- return false;
+ var nextRoutes = match.routes || [];
+ var nextParams = match.params || {};
+ var nextQuery = Path.extractQuery(path) || {};
- if (isFunction(handler)) {
- switch (arguments.length) {
- // fast cases
- case 1:
- handler.call(this);
- break;
- case 2:
- handler.call(this, arguments[1]);
- break;
- case 3:
- handler.call(this, arguments[1], arguments[2]);
- break;
- // slower
- default:
- len = arguments.length;
- args = new Array(len - 1);
- for (i = 1; i < len; i++)
- args[i - 1] = arguments[i];
- handler.apply(this, args);
- }
- } else if (isObject(handler)) {
- len = arguments.length;
- args = new Array(len - 1);
- for (i = 1; i < len; i++)
- args[i - 1] = arguments[i];
-
- listeners = handler.slice();
- len = listeners.length;
- for (i = 0; i < len; i++)
- listeners[i].apply(this, args);
- }
+ var fromRoutes, toRoutes;
+ if (prevRoutes.length) {
+ fromRoutes = prevRoutes.filter(function (route) {
+ return !hasMatch(nextRoutes, route, prevParams, nextParams);
+ });
- return true;
-};
+ toRoutes = nextRoutes.filter(function (route) {
+ return !hasMatch(prevRoutes, route, prevParams, nextParams);
+ });
+ } else {
+ fromRoutes = [];
+ toRoutes = nextRoutes;
+ }
-EventEmitter.prototype.addListener = function(type, listener) {
- var m;
-
- if (!isFunction(listener))
- throw TypeError('listener must be a function');
-
- if (!this._events)
- this._events = {};
-
- // To avoid recursion in the case that type === "newListener"! Before
- // adding it to the listeners, first emit "newListener".
- if (this._events.newListener)
- this.emit('newListener', type,
- isFunction(listener.listener) ?
- listener.listener : listener);
-
- if (!this._events[type])
- // Optimize the case of one listener. Don't need the extra array object.
- this._events[type] = listener;
- else if (isObject(this._events[type]))
- // If we've already got an array, just append.
- this._events[type].push(listener);
- else
- // Adding the second element, need to change to array.
- this._events[type] = [this._events[type], listener];
-
- // Check for listener leak
- if (isObject(this._events[type]) && !this._events[type].warned) {
- var m;
- if (!isUndefined(this._maxListeners)) {
- m = this._maxListeners;
- } else {
- m = EventEmitter.defaultMaxListeners;
- }
+ var transition = new Transition(path, this.replaceWith.bind(this, path));
- if (m && m > 0 && this._events[type].length > m) {
- this._events[type].warned = true;
- console.error('(node) warning: possible EventEmitter memory ' +
- 'leak detected. %d listeners added. ' +
- 'Use emitter.setMaxListeners() to increase limit.',
- this._events[type].length);
- if (typeof console.trace === 'function') {
- // not supported in IE 10
- console.trace();
- }
- }
- }
+ transition.from(fromRoutes, components, function (error) {
+ if (error || transition.isAborted)
+ return callback.call(router, error, transition);
- return this;
-};
+ transition.to(toRoutes, nextParams, nextQuery, function (error) {
+ if (error || transition.isAborted)
+ return callback.call(router, error, transition);
-EventEmitter.prototype.on = EventEmitter.prototype.addListener;
+ nextState.path = path;
+ nextState.action = action;
+ nextState.routes = nextRoutes;
+ nextState.params = nextParams;
+ nextState.query = nextQuery;
-EventEmitter.prototype.once = function(type, listener) {
- if (!isFunction(listener))
- throw TypeError('listener must be a function');
+ callback.call(router, null, transition);
+ });
+ });
+ },
+
+ /**
+ * Starts this router and calls callback(router, state) when the route changes.
+ *
+ * If the router's location is static (i.e. a URL path in a server environment)
+ * the callback is called only once. Otherwise, the location should be one of the
+ * Router.*Location objects (e.g. Router.HashLocation or Router.HistoryLocation).
+ */
+ run: function (callback) {
+ function dispatchHandler(error, transition) {
+ if (error) {
+ onError.call(router, error);
+ } else if (transition.isAborted) {
+ onAbort.call(router, transition.abortReason, location);
+ } else {
+ callback.call(router, router, nextState);
+ }
+ }
- var fired = false;
+ if (typeof location === 'string') {
+ warning(
+ !canUseDOM || "production" === 'test',
+ 'You should not use a static location in a DOM environment because ' +
+ 'the router will not be kept in sync with the current URL'
+ );
+
+ // Dispatch the location.
+ router.dispatch(location, null, dispatchHandler);
+ } else {
+ invariant(
+ canUseDOM,
+ 'You cannot use %s in a non-DOM environment',
+ location
+ );
+
+ // Listen for changes to the location.
+ function changeListener(change) {
+ router.dispatch(change.path, change.type, dispatchHandler);
+ }
+
+ if (location.addChangeListener)
+ location.addChangeListener(changeListener);
+
+ // Bootstrap using the current path.
+ router.dispatch(location.getCurrentPath(), null, dispatchHandler);
+ }
+ }
- function g() {
- this.removeListener(type, g);
+ },
- if (!fired) {
- fired = true;
- listener.apply(this, arguments);
- }
- }
+ propTypes: {
+ children: PropTypes.falsy
+ },
- g.listener = listener;
- this.on(type, g);
+ getLocation: function () {
+ return location;
+ },
- return this;
-};
+ getScrollBehavior: function () {
+ return scrollBehavior;
+ },
-// emits a 'removeListener' event iff the listener was removed
-EventEmitter.prototype.removeListener = function(type, listener) {
- var list, position, length, i;
+ getRouteAtDepth: function (depth) {
+ var routes = this.state.routes;
+ return routes && routes[depth];
+ },
- if (!isFunction(listener))
- throw TypeError('listener must be a function');
+ getRouteComponents: function () {
+ return components;
+ },
- if (!this._events || !this._events[type])
- return this;
+ getInitialState: function () {
+ updateState();
+ return state;
+ },
+
+ componentWillReceiveProps: function () {
+ updateState();
+ this.setState(state);
+ },
- list = this._events[type];
- length = list.length;
- position = -1;
+ render: function () {
+ return this.getRouteAtDepth(0) ? React.createElement(RouteHandler, this.props) : null;
+ },
- if (list === listener ||
- (isFunction(list.listener) && list.listener === listener)) {
- delete this._events[type];
- if (this._events.removeListener)
- this.emit('removeListener', type, listener);
+ childContextTypes: {
+ getRouteAtDepth: React.PropTypes.func.isRequired,
+ getRouteComponents: React.PropTypes.func.isRequired,
+ routeHandlers: React.PropTypes.array.isRequired
+ },
- } else if (isObject(list)) {
- for (i = length; i-- > 0;) {
- if (list[i] === listener ||
- (list[i].listener && list[i].listener === listener)) {
- position = i;
- break;
- }
+ getChildContext: function () {
+ return {
+ getRouteComponents: this.getRouteComponents,
+ getRouteAtDepth: this.getRouteAtDepth,
+ routeHandlers: [ this ]
+ };
}
- if (position < 0)
- return this;
+ });
- if (list.length === 1) {
- list.length = 0;
- delete this._events[type];
- } else {
- list.splice(position, 1);
- }
+ if (options.routes)
+ router.addRoutes(options.routes);
- if (this._events.removeListener)
- this.emit('removeListener', type, listener);
- }
+ return router;
+}
- return this;
-};
+module.exports = createRouter;
-EventEmitter.prototype.removeAllListeners = function(type) {
- var key, listeners;
+},{"../behaviors/ImitateBrowserBehavior":2,"../components/RouteHandler":8,"../locations/HashLocation":10,"../locations/HistoryLocation":11,"../locations/RefreshLocation":12,"../mixins/NavigationContext":15,"../mixins/Scrolling":16,"../mixins/StateContext":18,"./Path":19,"./PropTypes":21,"./Redirect":22,"./Transition":23,"./createRoutesFromChildren":25,"./supportsHistory":29,"react/lib/ExecutionEnvironment":35,"react/lib/invariant":39,"react/lib/warning":40}],25:[function(_dereq_,module,exports){
+var React = (typeof window !== "undefined" ? window.React : typeof global !== "undefined" ? global.React : null);
+var warning = _dereq_('react/lib/warning');
+var invariant = _dereq_('react/lib/invariant');
+var DefaultRoute = _dereq_('../components/DefaultRoute');
+var NotFoundRoute = _dereq_('../components/NotFoundRoute');
+var Redirect = _dereq_('../components/Redirect');
+var Route = _dereq_('../components/Route');
+var Path = _dereq_('./Path');
- if (!this._events)
- return this;
+var CONFIG_ELEMENT_TYPES = [
+ DefaultRoute.type,
+ NotFoundRoute.type,
+ Redirect.type,
+ Route.type
+];
- // not listening for removeListener, no need to emit
- if (!this._events.removeListener) {
- if (arguments.length === 0)
- this._events = {};
- else if (this._events[type])
- delete this._events[type];
- return this;
- }
+function createRedirectHandler(to, _params, _query) {
+ return React.createClass({
+ statics: {
+ willTransitionTo: function (transition, params, query) {
+ transition.redirect(to, _params || params, _query || query);
+ }
+ },
- // emit removeListener for all listeners on all events
- if (arguments.length === 0) {
- for (key in this._events) {
- if (key === 'removeListener') continue;
- this.removeAllListeners(key);
+ render: function () {
+ return null;
}
- this.removeAllListeners('removeListener');
- this._events = {};
- return this;
- }
+ });
+}
- listeners = this._events[type];
+function checkPropTypes(componentName, propTypes, props) {
+ for (var propName in propTypes) {
+ if (propTypes.hasOwnProperty(propName)) {
+ var error = propTypes[propName](props, propName, componentName);
- if (isFunction(listeners)) {
- this.removeListener(type, listeners);
- } else {
- // LIFO order
- while (listeners.length)
- this.removeListener(type, listeners[listeners.length - 1]);
+ if (error instanceof Error)
+ warning(false, error.message);
+ }
}
- delete this._events[type];
+}
- return this;
-};
+function createRoute(element, parentRoute, namedRoutes) {
+ var type = element.type;
+ var props = element.props;
+ var componentName = (type && type.displayName) || 'UnknownComponent';
-EventEmitter.prototype.listeners = function(type) {
- var ret;
- if (!this._events || !this._events[type])
- ret = [];
- else if (isFunction(this._events[type]))
- ret = [this._events[type]];
- else
- ret = this._events[type].slice();
- return ret;
-};
+ invariant(
+ CONFIG_ELEMENT_TYPES.indexOf(type) !== -1,
+ 'Unrecognized route configuration element "<%s>"',
+ componentName
+ );
-EventEmitter.listenerCount = function(emitter, type) {
- var ret;
- if (!emitter._events || !emitter._events[type])
- ret = 0;
- else if (isFunction(emitter._events[type]))
- ret = 1;
- else
- ret = emitter._events[type].length;
- return ret;
-};
+ if (type.propTypes)
+ checkPropTypes(componentName, type.propTypes, props);
-function isFunction(arg) {
- return typeof arg === 'function';
-}
+ var route = { name: props.name };
-function isNumber(arg) {
- return typeof arg === 'number';
-}
+ if (type === Redirect.type) {
+ route.handler = createRedirectHandler(props.to, props.params, props.query);
+ props.path = props.path || props.from || '*';
+ } else {
+ route.handler = props.handler;
+ }
-function isObject(arg) {
- return typeof arg === 'object' && arg !== null;
-}
+ var parentPath = (parentRoute && parentRoute.path) || '/';
-function isUndefined(arg) {
- return arg === void 0;
-}
+ if ((props.path || props.name) && type !== DefaultRoute.type && type !== NotFoundRoute.type) {
+ var path = props.path || props.name;
-},{}],31:[function(_dereq_,module,exports){
-/**
- * Copyright (c) 2014, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- */
+ // Relative paths extend their parent.
+ if (!Path.isAbsolute(path))
+ path = Path.join(parentPath, path);
-module.exports.Dispatcher = _dereq_('./lib/Dispatcher')
+ route.path = Path.normalize(path);
+ } else {
+ route.path = parentPath;
-},{"./lib/Dispatcher":32}],32:[function(_dereq_,module,exports){
-/*
- * Copyright (c) 2014, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- * @providesModule Dispatcher
- * @typechecks
- */
+ if (type === NotFoundRoute.type)
+ route.path += '*';
+ }
-var invariant = _dereq_('./invariant');
+ route.paramNames = Path.extractParamNames(route.path);
-var _lastID = 1;
-var _prefix = 'ID_';
+ // Make sure the route's path has all params its parent needs.
+ if (parentRoute && Array.isArray(parentRoute.paramNames)) {
+ parentRoute.paramNames.forEach(function (paramName) {
+ invariant(
+ route.paramNames.indexOf(paramName) !== -1,
+ 'The nested route path "%s" is missing the "%s" parameter of its parent path "%s"',
+ route.path, paramName, parentRoute.path
+ );
+ });
+ }
-/**
- * Dispatcher is used to broadcast payloads to registered callbacks. This is
- * different from generic pub-sub systems in two ways:
- *
- * 1) Callbacks are not subscribed to particular events. Every payload is
- * dispatched to every registered callback.
- * 2) Callbacks can be deferred in whole or part until other callbacks have
- * been executed.
- *
- * For example, consider this hypothetical flight destination form, which
- * selects a default city when a country is selected:
- *
- * var flightDispatcher = new Dispatcher();
- *
- * // Keeps track of which country is selected
- * var CountryStore = {country: null};
- *
- * // Keeps track of which city is selected
- * var CityStore = {city: null};
- *
- * // Keeps track of the base flight price of the selected city
- * var FlightPriceStore = {price: null}
- *
- * When a user changes the selected city, we dispatch the payload:
- *
- * flightDispatcher.dispatch({
- * actionType: 'city-update',
- * selectedCity: 'paris'
- * });
- *
- * This payload is digested by `CityStore`:
- *
- * flightDispatcher.register(function(payload) {
- * if (payload.actionType === 'city-update') {
- * CityStore.city = payload.selectedCity;
- * }
- * });
- *
- * When the user selects a country, we dispatch the payload:
- *
- * flightDispatcher.dispatch({
- * actionType: 'country-update',
- * selectedCountry: 'australia'
- * });
- *
- * This payload is digested by both stores:
- *
- * CountryStore.dispatchToken = flightDispatcher.register(function(payload) {
- * if (payload.actionType === 'country-update') {
- * CountryStore.country = payload.selectedCountry;
- * }
- * });
- *
- * When the callback to update `CountryStore` is registered, we save a reference
- * to the returned token. Using this token with `waitFor()`, we can guarantee
- * that `CountryStore` is updated before the callback that updates `CityStore`
- * needs to query its data.
- *
- * CityStore.dispatchToken = flightDispatcher.register(function(payload) {
- * if (payload.actionType === 'country-update') {
- * // `CountryStore.country` may not be updated.
- * flightDispatcher.waitFor([CountryStore.dispatchToken]);
- * // `CountryStore.country` is now guaranteed to be updated.
- *
- * // Select the default city for the new country
- * CityStore.city = getDefaultCityForCountry(CountryStore.country);
- * }
- * });
- *
- * The usage of `waitFor()` can be chained, for example:
- *
- * FlightPriceStore.dispatchToken =
- * flightDispatcher.register(function(payload) {
- * switch (payload.actionType) {
- * case 'country-update':
- * flightDispatcher.waitFor([CityStore.dispatchToken]);
- * FlightPriceStore.price =
- * getFlightPriceStore(CountryStore.country, CityStore.city);
- * break;
- *
- * case 'city-update':
- * FlightPriceStore.price =
- * FlightPriceStore(CountryStore.country, CityStore.city);
- * break;
- * }
- * });
- *
- * The `country-update` payload will be guaranteed to invoke the stores'
- * registered callbacks in order: `CountryStore`, `CityStore`, then
- * `FlightPriceStore`.
- */
+ // Make sure the route can be looked up by <Link>s.
+ if (props.name) {
+ invariant(
+ namedRoutes[props.name] == null,
+ 'You cannot use the name "%s" for more than one route',
+ props.name
+ );
- function Dispatcher() {"use strict";
- this.$Dispatcher_callbacks = {};
- this.$Dispatcher_isPending = {};
- this.$Dispatcher_isHandled = {};
- this.$Dispatcher_isDispatching = false;
- this.$Dispatcher_pendingPayload = null;
+ namedRoutes[props.name] = route;
}
- /**
- * Registers a callback to be invoked with every dispatched payload. Returns
- * a token that can be used with `waitFor()`.
- *
- * @param {function} callback
- * @return {string}
- */
- Dispatcher.prototype.register=function(callback) {"use strict";
- var id = _prefix + _lastID++;
- this.$Dispatcher_callbacks[id] = callback;
- return id;
- };
+ // Handle <NotFoundRoute>.
+ if (type === NotFoundRoute.type) {
+ invariant(
+ parentRoute,
+ '<NotFoundRoute> must have a parent <Route>'
+ );
- /**
- * Removes a callback based on its token.
- *
- * @param {string} id
- */
- Dispatcher.prototype.unregister=function(id) {"use strict";
invariant(
- this.$Dispatcher_callbacks[id],
- 'Dispatcher.unregister(...): `%s` does not map to a registered callback.',
- id
+ parentRoute.notFoundRoute == null,
+ 'You may not have more than one <NotFoundRoute> per <Route>'
);
- delete this.$Dispatcher_callbacks[id];
- };
- /**
- * Waits for the callbacks specified to be invoked before continuing execution
- * of the current callback. This method should only be used by a callback in
- * response to a dispatched payload.
- *
- * @param {array<string>} ids
- */
- Dispatcher.prototype.waitFor=function(ids) {"use strict";
+ parentRoute.notFoundRoute = route;
+
+ return null;
+ }
+
+ // Handle <DefaultRoute>.
+ if (type === DefaultRoute.type) {
invariant(
- this.$Dispatcher_isDispatching,
- 'Dispatcher.waitFor(...): Must be invoked while dispatching.'
+ parentRoute,
+ '<DefaultRoute> must have a parent <Route>'
);
- for (var ii = 0; ii < ids.length; ii++) {
- var id = ids[ii];
- if (this.$Dispatcher_isPending[id]) {
- invariant(
- this.$Dispatcher_isHandled[id],
- 'Dispatcher.waitFor(...): Circular dependency detected while ' +
- 'waiting for `%s`.',
- id
- );
- continue;
- }
- invariant(
- this.$Dispatcher_callbacks[id],
- 'Dispatcher.waitFor(...): `%s` does not map to a registered callback.',
- id
- );
- this.$Dispatcher_invokeCallback(id);
- }
- };
- /**
- * Dispatches a payload to all registered callbacks.
- *
- * @param {object} payload
- */
- Dispatcher.prototype.dispatch=function(payload) {"use strict";
invariant(
- !this.$Dispatcher_isDispatching,
- 'Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch.'
+ parentRoute.defaultRoute == null,
+ 'You may not have more than one <DefaultRoute> per <Route>'
);
- this.$Dispatcher_startDispatching(payload);
- try {
- for (var id in this.$Dispatcher_callbacks) {
- if (this.$Dispatcher_isPending[id]) {
- continue;
- }
- this.$Dispatcher_invokeCallback(id);
- }
- } finally {
- this.$Dispatcher_stopDispatching();
- }
- };
- /**
- * Is this Dispatcher currently dispatching.
- *
- * @return {boolean}
- */
- Dispatcher.prototype.isDispatching=function() {"use strict";
- return this.$Dispatcher_isDispatching;
- };
+ parentRoute.defaultRoute = route;
- /**
- * Call the callback stored with the given id. Also do some internal
- * bookkeeping.
- *
- * @param {string} id
- * @internal
- */
- Dispatcher.prototype.$Dispatcher_invokeCallback=function(id) {"use strict";
- this.$Dispatcher_isPending[id] = true;
- this.$Dispatcher_callbacks[id](this.$Dispatcher_pendingPayload);
- this.$Dispatcher_isHandled[id] = true;
- };
+ return null;
+ }
- /**
- * Set up bookkeeping needed when dispatching.
- *
- * @param {object} payload
- * @internal
- */
- Dispatcher.prototype.$Dispatcher_startDispatching=function(payload) {"use strict";
- for (var id in this.$Dispatcher_callbacks) {
- this.$Dispatcher_isPending[id] = false;
- this.$Dispatcher_isHandled[id] = false;
- }
- this.$Dispatcher_pendingPayload = payload;
- this.$Dispatcher_isDispatching = true;
- };
+ route.childRoutes = createRoutesFromChildren(props.children, route, namedRoutes);
- /**
- * Clear bookkeeping used for dispatching.
- *
- * @internal
- */
- Dispatcher.prototype.$Dispatcher_stopDispatching=function() {"use strict";
- this.$Dispatcher_pendingPayload = null;
- this.$Dispatcher_isDispatching = false;
- };
+ return route;
+}
+
+/**
+ * Creates and returns an array of route objects from the given ReactChildren.
+ */
+function createRoutesFromChildren(children, parentRoute, namedRoutes) {
+ var routes = [];
+
+ React.Children.forEach(children, function (child) {
+ // Exclude <DefaultRoute>s and <NotFoundRoute>s.
+ if (child = createRoute(child, parentRoute, namedRoutes))
+ routes.push(child);
+ });
+
+ return routes;
+}
+module.exports = createRoutesFromChildren;
-module.exports = Dispatcher;
+},{"../components/DefaultRoute":3,"../components/NotFoundRoute":5,"../components/Redirect":6,"../components/Route":7,"./Path":19,"react/lib/invariant":39,"react/lib/warning":40}],26:[function(_dereq_,module,exports){
+var invariant = _dereq_('react/lib/invariant');
+var canUseDOM = _dereq_('react/lib/ExecutionEnvironment').canUseDOM;
-},{"./invariant":33}],33:[function(_dereq_,module,exports){
/**
- * Copyright (c) 2014, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- * @providesModule invariant
+ * Returns the current scroll position of the window as { x, y }.
*/
+function getWindowScrollPosition() {
+ invariant(
+ canUseDOM,
+ 'Cannot get current scroll position without a DOM'
+ );
-"use strict";
+ return {
+ x: window.scrollX,
+ y: window.scrollY
+ };
+}
+
+module.exports = getWindowScrollPosition;
+
+},{"react/lib/ExecutionEnvironment":35,"react/lib/invariant":39}],27:[function(_dereq_,module,exports){
+function reversedArray(array) {
+ return array.slice(0).reverse();
+}
+
+module.exports = reversedArray;
+
+},{}],28:[function(_dereq_,module,exports){
+var createRouter = _dereq_('./createRouter');
/**
- * Use invariant() to assert state which your program assumes to be true.
+ * A high-level convenience method that creates, configures, and
+ * runs a router in one shot. The method signature is:
*
- * Provide sprintf-style format (only %s is supported) and arguments
- * to provide information about what broke and what you were
- * expecting.
+ * Router.run(routes[, location ], callback);
*
- * The invariant message will be stripped in production, but the invariant
- * will remain to ensure logic does not differ in production.
+ * Using `window.location.hash` to manage the URL, you could do:
+ *
+ * Router.run(routes, function (Handler) {
+ * React.render(<Handler/>, document.body);
+ * });
+ *
+ * Using HTML5 history and a custom "cursor" prop:
+ *
+ * Router.run(routes, Router.HistoryLocation, function (Handler) {
+ * React.render(<Handler cursor={cursor}/>, document.body);
+ * });
+ *
+ * Returns the newly created router.
+ *
+ * Note: If you need to specify further options for your router such
+ * as error/abort handling or custom scroll behavior, use Router.create
+ * instead.
+ *
+ * var router = Router.create(options);
+ * router.run(function (Handler) {
+ * // ...
+ * });
*/
-
-var invariant = function(condition, format, a, b, c, d, e, f) {
- if (false) {
- if (format === undefined) {
- throw new Error('invariant requires an error message argument');
- }
+function runRouter(routes, location, callback) {
+ if (typeof location === 'function') {
+ callback = location;
+ location = null;
}
- if (!condition) {
- var error;
- if (format === undefined) {
- error = new Error(
- 'Minified exception occurred; use the non-minified dev environment ' +
- 'for the full error message and additional helpful warnings.'
- );
- } else {
- var args = [a, b, c, d, e, f];
- var argIndex = 0;
- error = new Error(
- 'Invariant Violation: ' +
- format.replace(/%s/g, function() { return args[argIndex++]; })
- );
- }
+ var router = createRouter({
+ routes: routes,
+ location: location
+ });
- error.framesToPop = 1; // we don't care about invariant's own frame
- throw error;
+ router.run(callback);
+
+ return router;
+}
+
+module.exports = runRouter;
+
+},{"./createRouter":24}],29:[function(_dereq_,module,exports){
+function supportsHistory() {
+ /*! taken from modernizr
+ * https://github.com/Modernizr/Modernizr/blob/master/LICENSE
+ * https://github.com/Modernizr/Modernizr/blob/master/feature-detects/history.js
+ */
+ var ua = navigator.userAgent;
+ if ((ua.indexOf('Android 2.') !== -1 ||
+ (ua.indexOf('Android 4.0') !== -1)) &&
+ ua.indexOf('Mobile Safari') !== -1 &&
+ ua.indexOf('Chrome') === -1) {
+ return false;
}
-};
+ return (window.history && 'pushState' in window.history);
+}
-module.exports = invariant;
+module.exports = supportsHistory;
-},{}],34:[function(_dereq_,module,exports){
+},{}],30:[function(_dereq_,module,exports){
module.exports = _dereq_('./lib');
-},{"./lib":35}],35:[function(_dereq_,module,exports){
+},{"./lib":31}],31:[function(_dereq_,module,exports){
// Load modules
var Stringify = _dereq_('./stringify');
@@ -2665,7 +1994,7 @@ module.exports = {
parse: Parse
};
-},{"./parse":36,"./stringify":37}],36:[function(_dereq_,module,exports){
+},{"./parse":32,"./stringify":33}],32:[function(_dereq_,module,exports){
// Load modules
var Utils = _dereq_('./utils');
@@ -2821,7 +2150,7 @@ module.exports = function (str, options) {
return Utils.compact(obj);
};
-},{"./utils":38}],37:[function(_dereq_,module,exports){
+},{"./utils":34}],33:[function(_dereq_,module,exports){
// Load modules
var Utils = _dereq_('./utils');
@@ -2881,7 +2210,7 @@ module.exports = function (obj, options) {
return keys.join(delimiter);
};
-},{"./utils":38}],38:[function(_dereq_,module,exports){
+},{"./utils":34}],34:[function(_dereq_,module,exports){
// Load modules
@@ -3022,21 +2351,14 @@ exports.isBuffer = function (obj) {
}
};
-},{}],39:[function(_dereq_,module,exports){
+},{}],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
+ * Copyright 2013-2014, Facebook, Inc.
+ * All rights reserved.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ExecutionEnvironment
*/
@@ -3074,83 +2396,104 @@ var ExecutionEnvironment = {
module.exports = ExecutionEnvironment;
-},{}],40:[function(_dereq_,module,exports){
+},{}],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
+ * Copyright 2014, Facebook, Inc.
+ * All rights reserved.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
*
- * @providesModule copyProperties
+ * @providesModule Object.assign
*/
-/**
- * Copy properties from one or more objects (up to 5) into the first object.
- * This is a shallow copy. It mutates the first object and also returns it.
- *
- * NOTE: `arguments` has a very significant performance penalty, which is why
- * we don't support unlimited arguments.
- */
-function copyProperties(obj, a, b, c, d, e, f) {
- obj = obj || {};
+// https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.assign
- if ("production" !== "production") {
- if (f) {
- throw new Error('Too many arguments passed to copyProperties');
- }
+function assign(target, sources) {
+ if (target == null) {
+ throw new TypeError('Object.assign target cannot be null or undefined');
}
- var args = [a, b, c, d, e];
- var ii = 0, v;
- while (args[ii]) {
- v = args[ii++];
- for (var k in v) {
- obj[k] = v[k];
+ var to = Object(target);
+ var hasOwnProperty = Object.prototype.hasOwnProperty;
+
+ for (var nextIndex = 1; nextIndex < arguments.length; nextIndex++) {
+ var nextSource = arguments[nextIndex];
+ if (nextSource == null) {
+ continue;
}
- // IE ignores toString in object iteration.. See:
- // webreflection.blogspot.com/2007/07/quick-fix-internet-explorer-and.html
- if (v.hasOwnProperty && v.hasOwnProperty('toString') &&
- (typeof v.toString != 'undefined') && (obj.toString !== v.toString)) {
- obj.toString = v.toString;
+ var from = Object(nextSource);
+
+ // We don't currently support accessors nor proxies. Therefore this
+ // copy cannot throw. If we ever supported this then we must handle
+ // exceptions and side-effects. We don't support symbols so they won't
+ // be transferred.
+
+ for (var key in from) {
+ if (hasOwnProperty.call(from, key)) {
+ to[key] = from[key];
+ }
}
}
- return obj;
-}
+ return to;
+};
-module.exports = copyProperties;
+module.exports = assign;
-},{}],41:[function(_dereq_,module,exports){
+},{}],37:[function(_dereq_,module,exports){
/**
- * Copyright 2013-2014 Facebook, Inc.
+ * Copyright 2013-2014, Facebook, Inc.
+ * All rights reserved.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * @providesModule cx
+ */
+
+/**
+ * This function is used to mark string literals representing CSS class names
+ * so that they can be transformed statically. This allows for modularization
+ * and minification of CSS class names.
+ *
+ * In static_upstream, this function is actually implemented, but it should
+ * eventually be replaced with something more descriptive, and the transform
+ * that is used in the main stack should be ported for use elsewhere.
+ *
+ * @param string|object className to modularize, or an object of key/values.
+ * In the object case, the values are conditions that
+ * determine if the className keys should be included.
+ * @param [string ...] Variable list of classNames in the string case.
+ * @return string Renderable space-separated CSS className.
+ */
+function cx(classNames) {
+ if (typeof classNames == 'object') {
+ return Object.keys(classNames).filter(function(className) {
+ return classNames[className];
+ }).join(' ');
+ } else {
+ return Array.prototype.join.call(arguments, ' ');
+ }
+}
+
+module.exports = cx;
+
+},{}],38:[function(_dereq_,module,exports){
+/**
+ * Copyright 2013-2014, Facebook, Inc.
+ * All rights reserved.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule emptyFunction
*/
-var copyProperties = _dereq_("./copyProperties");
-
function makeEmptyFunction(arg) {
return function() {
return arg;
@@ -3164,32 +2507,23 @@ function makeEmptyFunction(arg) {
*/
function emptyFunction() {}
-copyProperties(emptyFunction, {
- thatReturns: makeEmptyFunction,
- thatReturnsFalse: makeEmptyFunction(false),
- thatReturnsTrue: makeEmptyFunction(true),
- thatReturnsNull: makeEmptyFunction(null),
- thatReturnsThis: function() { return this; },
- thatReturnsArgument: function(arg) { return arg; }
-});
+emptyFunction.thatReturns = makeEmptyFunction;
+emptyFunction.thatReturnsFalse = makeEmptyFunction(false);
+emptyFunction.thatReturnsTrue = makeEmptyFunction(true);
+emptyFunction.thatReturnsNull = makeEmptyFunction(null);
+emptyFunction.thatReturnsThis = function() { return this; };
+emptyFunction.thatReturnsArgument = function(arg) { return arg; };
module.exports = emptyFunction;
-},{"./copyProperties":40}],42:[function(_dereq_,module,exports){
+},{}],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
+ * Copyright 2013-2014, Facebook, Inc.
+ * All rights reserved.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule invariant
*/
@@ -3237,353 +2571,14 @@ var invariant = function(condition, format, a, b, c, d, e, f) {
module.exports = invariant;
-},{}],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
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * @providesModule keyMirror
- * @typechecks static-only
- */
-
-"use strict";
-
-var invariant = _dereq_("./invariant");
-
-/**
- * Constructs an enumeration with keys equal to their value.
- *
- * For example:
- *
- * var COLORS = keyMirror({blue: null, red: null});
- * var myColor = COLORS.blue;
- * var isColorValid = !!COLORS[myColor];
- *
- * The last line could not be performed if the values of the generated enum were
- * not equal to their keys.
- *
- * Input: {key1: val1, key2: val2}
- * Output: {key1: key1, key2: key2}
- *
- * @param {object} obj
- * @return {object}
- */
-var keyMirror = function(obj) {
- var ret = {};
- var key;
- ("production" !== "production" ? invariant(
- obj instanceof Object && !Array.isArray(obj),
- 'keyMirror(...): Argument must be an object.'
- ) : invariant(obj instanceof Object && !Array.isArray(obj)));
- for (key in obj) {
- if (!obj.hasOwnProperty(key)) {
- continue;
- }
- ret[key] = key;
- }
- return ret;
-};
-
-module.exports = keyMirror;
-
-},{"./invariant":42}],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
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * @providesModule merge
- */
-
-"use strict";
-
-var mergeInto = _dereq_("./mergeInto");
-
-/**
- * Shallow merges two structures into a return value, without mutating either.
- *
- * @param {?object} one Optional object with properties to merge from.
- * @param {?object} two Optional object with properties to merge from.
- * @return {object} The shallow extension of one by two.
- */
-var merge = function(one, two) {
- var result = {};
- mergeInto(result, one);
- mergeInto(result, two);
- return result;
-};
-
-module.exports = merge;
-
-},{"./mergeInto":46}],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
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * @providesModule mergeHelpers
- *
- * requiresPolyfills: Array.isArray
- */
-
-"use strict";
-
-var invariant = _dereq_("./invariant");
-var keyMirror = _dereq_("./keyMirror");
-
-/**
- * Maximum number of levels to traverse. Will catch circular structures.
- * @const
- */
-var MAX_MERGE_DEPTH = 36;
-
-/**
- * We won't worry about edge cases like new String('x') or new Boolean(true).
- * Functions are considered terminals, and arrays are not.
- * @param {*} o The item/object/value to test.
- * @return {boolean} true iff the argument is a terminal.
- */
-var isTerminal = function(o) {
- return typeof o !== 'object' || o === null;
-};
-
-var mergeHelpers = {
-
- MAX_MERGE_DEPTH: MAX_MERGE_DEPTH,
-
- isTerminal: isTerminal,
-
- /**
- * Converts null/undefined values into empty object.
- *
- * @param {?Object=} arg Argument to be normalized (nullable optional)
- * @return {!Object}
- */
- normalizeMergeArg: function(arg) {
- return arg === undefined || arg === null ? {} : arg;
- },
-
- /**
- * If merging Arrays, a merge strategy *must* be supplied. If not, it is
- * likely the caller's fault. If this function is ever called with anything
- * but `one` and `two` being `Array`s, it is the fault of the merge utilities.
- *
- * @param {*} one Array to merge into.
- * @param {*} two Array to merge from.
- */
- checkMergeArrayArgs: function(one, two) {
- ("production" !== "production" ? invariant(
- Array.isArray(one) && Array.isArray(two),
- 'Tried to merge arrays, instead got %s and %s.',
- one,
- two
- ) : invariant(Array.isArray(one) && Array.isArray(two)));
- },
-
- /**
- * @param {*} one Object to merge into.
- * @param {*} two Object to merge from.
- */
- checkMergeObjectArgs: function(one, two) {
- mergeHelpers.checkMergeObjectArg(one);
- mergeHelpers.checkMergeObjectArg(two);
- },
-
- /**
- * @param {*} arg
- */
- checkMergeObjectArg: function(arg) {
- ("production" !== "production" ? invariant(
- !isTerminal(arg) && !Array.isArray(arg),
- 'Tried to merge an object, instead got %s.',
- arg
- ) : invariant(!isTerminal(arg) && !Array.isArray(arg)));
- },
-
- /**
- * @param {*} arg
- */
- checkMergeIntoObjectArg: function(arg) {
- ("production" !== "production" ? invariant(
- (!isTerminal(arg) || typeof arg === 'function') && !Array.isArray(arg),
- 'Tried to merge into an object, instead got %s.',
- arg
- ) : invariant((!isTerminal(arg) || typeof arg === 'function') && !Array.isArray(arg)));
- },
-
- /**
- * Checks that a merge was not given a circular object or an object that had
- * too great of depth.
- *
- * @param {number} Level of recursion to validate against maximum.
- */
- checkMergeLevel: function(level) {
- ("production" !== "production" ? invariant(
- level < MAX_MERGE_DEPTH,
- 'Maximum deep merge depth exceeded. You may be attempting to merge ' +
- 'circular structures in an unsupported way.'
- ) : invariant(level < MAX_MERGE_DEPTH));
- },
-
- /**
- * Checks that the supplied merge strategy is valid.
- *
- * @param {string} Array merge strategy.
- */
- checkArrayStrategy: function(strategy) {
- ("production" !== "production" ? invariant(
- strategy === undefined || strategy in mergeHelpers.ArrayStrategies,
- 'You must provide an array strategy to deep merge functions to ' +
- 'instruct the deep merge how to resolve merging two arrays.'
- ) : invariant(strategy === undefined || strategy in mergeHelpers.ArrayStrategies));
- },
-
- /**
- * Set of possible behaviors of merge algorithms when encountering two Arrays
- * that must be merged together.
- * - `clobber`: The left `Array` is ignored.
- * - `indexByIndex`: The result is achieved by recursively deep merging at
- * each index. (not yet supported.)
- */
- ArrayStrategies: keyMirror({
- Clobber: true,
- IndexByIndex: true
- })
-
-};
-
-module.exports = mergeHelpers;
-
-},{"./invariant":42,"./keyMirror":43}],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
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * @providesModule mergeInto
- * @typechecks static-only
- */
-
-"use strict";
-
-var mergeHelpers = _dereq_("./mergeHelpers");
-
-var checkMergeObjectArg = mergeHelpers.checkMergeObjectArg;
-var checkMergeIntoObjectArg = mergeHelpers.checkMergeIntoObjectArg;
-
-/**
- * Shallow merges two structures by mutating the first parameter.
- *
- * @param {object|function} one Object to be merged into.
- * @param {?object} two Optional object with properties to merge from.
- */
-function mergeInto(one, two) {
- checkMergeIntoObjectArg(one);
- if (two != null) {
- checkMergeObjectArg(two);
- for (var key in two) {
- if (!two.hasOwnProperty(key)) {
- continue;
- }
- one[key] = two[key];
- }
- }
-}
-
-module.exports = mergeInto;
-
-},{"./mergeHelpers":45}],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
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * @providesModule mixInto
- */
-
-"use strict";
-
-/**
- * Simply copies properties to the prototype.
- */
-var mixInto = function(constructor, methodBag) {
- var methodName;
- for (methodName in methodBag) {
- if (!methodBag.hasOwnProperty(methodName)) {
- continue;
- }
- constructor.prototype[methodName] = methodBag[methodName];
- }
-};
-
-module.exports = mixInto;
-
-},{}],48:[function(_dereq_,module,exports){
+},{}],40:[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
+ * Copyright 2014, Facebook, Inc.
+ * All rights reserved.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule warning
*/
@@ -3619,7 +2614,7 @@ if ("production" !== "production") {
module.exports = warning;
-},{"./emptyFunction":41}],49:[function(_dereq_,module,exports){
+},{"./emptyFunction":38}],41:[function(_dereq_,module,exports){
/** @license MIT License (c) copyright 2010-2014 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */
@@ -3638,7 +2633,7 @@ define(function (_dereq_) {
});
})(typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(_dereq_); });
-},{"./Scheduler":51,"./async":52,"./makePromise":53}],50:[function(_dereq_,module,exports){
+},{"./Scheduler":43,"./async":44,"./makePromise":45}],42:[function(_dereq_,module,exports){
/** @license MIT License (c) copyright 2010-2014 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */
@@ -3710,7 +2705,7 @@ define(function() {
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); }));
-},{}],51:[function(_dereq_,module,exports){
+},{}],43:[function(_dereq_,module,exports){
/** @license MIT License (c) copyright 2010-2014 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */
@@ -3794,7 +2789,7 @@ define(function(_dereq_) {
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(_dereq_); }));
-},{"./Queue":50}],52:[function(_dereq_,module,exports){
+},{"./Queue":42}],44:[function(_dereq_,module,exports){
/** @license MIT License (c) copyright 2010-2014 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */
@@ -3839,11 +2834,21 @@ define(function(_dereq_) {
} else {
nextTick = (function(cjsRequire) {
+ var vertx;
try {
// vert.x 1.x || 2.x
- return cjsRequire('vertx').runOnLoop || cjsRequire('vertx').runOnContext;
+ vertx = cjsRequire('vertx');
} catch (ignore) {}
+ if (vertx) {
+ if (typeof vertx.runOnLoop === 'function') {
+ return vertx.runOnLoop;
+ }
+ if (typeof vertx.runOnContext === 'function') {
+ return vertx.runOnContext;
+ }
+ }
+
// capture setTimeout to avoid being caught by fake timers
// used in time based tests
var capturedSetTimeout = setTimeout;
@@ -3857,7 +2862,7 @@ define(function(_dereq_) {
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(_dereq_); }));
-},{}],53:[function(_dereq_,module,exports){
+},{}],45:[function(_dereq_,module,exports){
/** @license MIT License (c) copyright 2010-2014 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */
@@ -3988,10 +2993,12 @@ define(function() {
*/
Promise.prototype.then = function(onFulfilled, onRejected) {
var parent = this._handler;
+ var state = parent.join().state();
- if (typeof onFulfilled !== 'function' && parent.join().state() > 0) {
+ if ((typeof onFulfilled !== 'function' && state > 0) ||
+ (typeof onRejected !== 'function' && state < 0)) {
// Short circuit: value will not change, simply share handler
- return new Promise(Handler, parent);
+ return new this.constructor(Handler, parent);
}
var p = this._beget();
@@ -4052,9 +3059,7 @@ define(function() {
}
if (maybeThenable(x)) {
- h = isPromise(x)
- ? x._handler.join()
- : getHandlerUntrusted(x);
+ h = getHandlerMaybeThenable(x);
s = h.state();
if (s === 0) {
@@ -4063,6 +3068,7 @@ define(function() {
results[i] = h.value;
--pending;
} else {
+ unreportRemaining(promises, i+1, h);
resolver.become(h);
break;
}
@@ -4088,6 +3094,20 @@ define(function() {
}
}
+ function unreportRemaining(promises, start, rejectedHandler) {
+ var i, h, x;
+ for(i=start; i<promises.length; ++i) {
+ x = promises[i];
+ if(maybeThenable(x)) {
+ h = getHandlerMaybeThenable(x);
+
+ if(h !== rejectedHandler) {
+ h.visit(h, void 0, h._unreport);
+ }
+ }
+ }
+ }
+
/**
* Fulfill-reject competitive race. Return a promise that will settle
* to the same state as the earliest input promise to settle.
@@ -4136,6 +3156,16 @@ define(function() {
}
/**
+ * Get a handler for thenable x.
+ * NOTE: You must only call this if maybeThenable(x) == true
+ * @param {object|function|Promise} x
+ * @returns {object} handler
+ */
+ function getHandlerMaybeThenable(x) {
+ return isPromise(x) ? x._handler.join() : getHandlerUntrusted(x);
+ }
+
+ /**
* Get a handler for potentially untrusted thenable x
* @param {*} x
* @returns {object} handler