src/cm/media/js/lib/yui/yui3-3.15.0/build/router/router-debug.js
changeset 602 e16a97fb364a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cm/media/js/lib/yui/yui3-3.15.0/build/router/router-debug.js	Mon Mar 10 15:19:48 2014 +0100
@@ -0,0 +1,1762 @@
+YUI.add('router', function (Y, NAME) {
+
+/**
+Provides URL-based routing using HTML5 `pushState()` or the location hash.
+
+@module app
+@submodule router
+@since 3.4.0
+**/
+
+var HistoryHash = Y.HistoryHash,
+    QS          = Y.QueryString,
+    YArray      = Y.Array,
+    YLang       = Y.Lang,
+    YObject     = Y.Object,
+
+    win = Y.config.win,
+
+    // Holds all the active router instances. This supports the static
+    // `dispatch()` method which causes all routers to dispatch.
+    instances = [],
+
+    // We have to queue up pushState calls to avoid race conditions, since the
+    // popstate event doesn't actually provide any info on what URL it's
+    // associated with.
+    saveQueue = [],
+
+    /**
+    Fired when the router is ready to begin dispatching to route handlers.
+
+    You shouldn't need to wait for this event unless you plan to implement some
+    kind of custom dispatching logic. It's used internally in order to avoid
+    dispatching to an initial route if a browser history change occurs first.
+
+    @event ready
+    @param {Boolean} dispatched `true` if routes have already been dispatched
+      (most likely due to a history change).
+    @fireOnce
+    **/
+    EVT_READY = 'ready';
+
+/**
+Provides URL-based routing using HTML5 `pushState()` or the location hash.
+
+This makes it easy to wire up route handlers for different application states
+while providing full back/forward navigation support and bookmarkable, shareable
+URLs.
+
+@class Router
+@param {Object} [config] Config properties.
+    @param {Boolean} [config.html5] Overrides the default capability detection
+        and forces this router to use (`true`) or not use (`false`) HTML5
+        history.
+    @param {String} [config.root=''] Root path from which all routes should be
+        evaluated.
+    @param {Array} [config.routes=[]] Array of route definition objects.
+@constructor
+@extends Base
+@since 3.4.0
+**/
+function Router() {
+    Router.superclass.constructor.apply(this, arguments);
+}
+
+Y.Router = Y.extend(Router, Y.Base, {
+    // -- Protected Properties -------------------------------------------------
+
+    /**
+    Whether or not `_dispatch()` has been called since this router was
+    instantiated.
+
+    @property _dispatched
+    @type Boolean
+    @default undefined
+    @protected
+    **/
+
+    /**
+    Whether or not we're currently in the process of dispatching to routes.
+
+    @property _dispatching
+    @type Boolean
+    @default undefined
+    @protected
+    **/
+
+    /**
+    History event handle for the `history:change` or `hashchange` event
+    subscription.
+
+    @property _historyEvents
+    @type EventHandle
+    @protected
+    **/
+
+    /**
+    Cached copy of the `html5` attribute for internal use.
+
+    @property _html5
+    @type Boolean
+    @protected
+    **/
+
+    /**
+    Map which holds the registered param handlers in the form:
+    `name` -> RegExp | Function.
+
+    @property _params
+    @type Object
+    @protected
+    @since 3.12.0
+    **/
+
+    /**
+    Whether or not the `ready` event has fired yet.
+
+    @property _ready
+    @type Boolean
+    @default undefined
+    @protected
+    **/
+
+    /**
+    Regex used to break up a URL string around the URL's path.
+
+    Subpattern captures:
+
+      1. Origin, everything before the URL's path-part.
+      2. The URL's path-part.
+      3. The URL's query.
+      4. The URL's hash fragment.
+
+    @property _regexURL
+    @type RegExp
+    @protected
+    @since 3.5.0
+    **/
+    _regexURL: /^((?:[^\/#?:]+:\/\/|\/\/)[^\/]*)?([^?#]*)(\?[^#]*)?(#.*)?$/,
+
+    /**
+    Regex used to match parameter placeholders in route paths.
+
+    Subpattern captures:
+
+      1. Parameter prefix character. Either a `:` for subpath parameters that
+         should only match a single level of a path, or `*` for splat parameters
+         that should match any number of path levels.
+
+      2. Parameter name, if specified, otherwise it is a wildcard match.
+
+    @property _regexPathParam
+    @type RegExp
+    @protected
+    **/
+    _regexPathParam: /([:*])([\w\-]+)?/g,
+
+    /**
+    Regex that matches and captures the query portion of a URL, minus the
+    preceding `?` character, and discarding the hash portion of the URL if any.
+
+    @property _regexUrlQuery
+    @type RegExp
+    @protected
+    **/
+    _regexUrlQuery: /\?([^#]*).*$/,
+
+    /**
+    Regex that matches everything before the path portion of a URL (the origin).
+    This will be used to strip this part of the URL from a string when we
+    only want the path.
+
+    @property _regexUrlOrigin
+    @type RegExp
+    @protected
+    **/
+    _regexUrlOrigin: /^(?:[^\/#?:]+:\/\/|\/\/)[^\/]*/,
+
+    /**
+    Collection of registered routes.
+
+    @property _routes
+    @type Array
+    @protected
+    **/
+
+    // -- Lifecycle Methods ----------------------------------------------------
+    initializer: function (config) {
+        var self = this;
+
+        self._html5  = self.get('html5');
+        self._params = {};
+        self._routes = [];
+        self._url    = self._getURL();
+
+        // Necessary because setters don't run on init.
+        self._setRoutes(config && config.routes ? config.routes :
+                self.get('routes'));
+
+        // Set up a history instance or hashchange listener.
+        if (self._html5) {
+            self._history       = new Y.HistoryHTML5({force: true});
+            self._historyEvents =
+                    Y.after('history:change', self._afterHistoryChange, self);
+        } else {
+            self._historyEvents =
+                    Y.on('hashchange', self._afterHistoryChange, win, self);
+        }
+
+        // Fire a `ready` event once we're ready to route. We wait first for all
+        // subclass initializers to finish, then for window.onload, and then an
+        // additional 20ms to allow the browser to fire a useless initial
+        // `popstate` event if it wants to (and Chrome always wants to).
+        self.publish(EVT_READY, {
+            defaultFn  : self._defReadyFn,
+            fireOnce   : true,
+            preventable: false
+        });
+
+        self.once('initializedChange', function () {
+            Y.once('load', function () {
+                setTimeout(function () {
+                    self.fire(EVT_READY, {dispatched: !!self._dispatched});
+                }, 20);
+            });
+        });
+
+        // Store this router in the collection of all active router instances.
+        instances.push(this);
+    },
+
+    destructor: function () {
+        var instanceIndex = YArray.indexOf(instances, this);
+
+        // Remove this router from the collection of active router instances.
+        if (instanceIndex > -1) {
+            instances.splice(instanceIndex, 1);
+        }
+
+        if (this._historyEvents) {
+            this._historyEvents.detach();
+        }
+    },
+
+    // -- Public Methods -------------------------------------------------------
+
+    /**
+    Dispatches to the first route handler that matches the current URL, if any.
+
+    If `dispatch()` is called before the `ready` event has fired, it will
+    automatically wait for the `ready` event before dispatching. Otherwise it
+    will dispatch immediately.
+
+    @method dispatch
+    @chainable
+    **/
+    dispatch: function () {
+        this.once(EVT_READY, function () {
+            var req, res;
+
+            this._ready = true;
+
+            if (!this.upgrade()) {
+                req = this._getRequest('dispatch');
+                res = this._getResponse(req);
+
+                this._dispatch(req, res);
+            }
+        });
+
+        return this;
+    },
+
+    /**
+    Gets the current route path.
+
+    @method getPath
+    @return {String} Current route path.
+    **/
+    getPath: function () {
+        return this._getPath();
+    },
+
+    /**
+    Returns `true` if this router has at least one route that matches the
+    specified URL, `false` otherwise.
+
+    This method enforces the same-origin security constraint on the specified
+    `url`; any URL which is not from the same origin as the current URL will
+    always return `false`.
+
+    @method hasRoute
+    @param {String} url URL to match.
+    @return {Boolean} `true` if there's at least one matching route, `false`
+      otherwise.
+    **/
+    hasRoute: function (url) {
+        var path;
+
+        if (!this._hasSameOrigin(url)) {
+            return false;
+        }
+
+        if (!this._html5) {
+            url = this._upgradeURL(url);
+        }
+
+        // Get just the path portion of the specified `url`.
+        path = this.removeQuery(url.replace(this._regexUrlOrigin, ''));
+
+        return !!this.match(path).length;
+    },
+
+    /**
+    Returns an array of route objects that match the specified URL path.
+
+    If this router has a `root`, then the specified `path` _must_ be
+    semantically within the `root` path to match any routes.
+
+    This method is called internally to determine which routes match the current
+    path whenever the URL changes. You may override it if you want to customize
+    the route matching logic, although this usually shouldn't be necessary.
+
+    Each returned route object has the following properties:
+
+      * `callback`: A function or a string representing the name of a function
+        this router that should be executed when the route is triggered.
+
+      * `keys`: An array of strings representing the named parameters defined in
+        the route's path specification, if any.
+
+      * `path`: The route's path specification, which may be either a string or
+        a regex.
+
+      * `regex`: A regular expression version of the route's path specification.
+        This regex is used to determine whether the route matches a given path.
+
+    @example
+        router.route('/foo', function () {});
+        router.match('/foo');
+        // => [{callback: ..., keys: [], path: '/foo', regex: ...}]
+
+    @method match
+    @param {String} path URL path to match. This should be an absolute path that
+        starts with a slash: "/".
+    @return {Object[]} Array of route objects that match the specified path.
+    **/
+    match: function (path) {
+        var root = this.get('root');
+
+        if (root) {
+            // The `path` must be semantically within this router's `root` path
+            // or mount point, if it's not then no routes should be considered a
+            // match.
+            if (!this._pathHasRoot(root, path)) {
+                return [];
+            }
+
+            // Remove this router's `root` from the `path` before checking the
+            // routes for any matches.
+            path = this.removeRoot(path);
+        }
+
+        return YArray.filter(this._routes, function (route) {
+            return path.search(route.regex) > -1;
+        });
+    },
+
+    /**
+    Adds a handler for a route param specified by _name_.
+
+    Param handlers can be registered via this method and are used to
+    validate/format values of named params in routes before dispatching to the
+    route's handler functions. Using param handlers allows routes to defined
+    using string paths which allows for `req.params` to use named params, but
+    still applying extra validation or formatting to the param values parsed
+    from the URL.
+
+    If a param handler regex or function returns a value of `false`, `null`,
+    `undefined`, or `NaN`, the current route will not match and be skipped. All
+    other return values will be used in place of the original param value parsed
+    from the URL.
+
+    @example
+        router.param('postId', function (value) {
+            return parseInt(value, 10);
+        });
+
+        router.param('username', /^\w+$/);
+
+        router.route('/posts/:postId', function (req) {
+            Y.log('Post: ' + req.params.id);
+        });
+
+        router.route('/users/:username', function (req) {
+            // `req.params.username` is an array because the result of calling
+            // `exec()` on the regex is assigned as the param's value.
+            Y.log('User: ' + req.params.username[0]);
+        });
+
+        router.route('*', function () {
+            Y.log('Catch-all no routes matched!');
+        });
+
+        // URLs which match routes:
+        router.save('/posts/1');     // => "Post: 1"
+        router.save('/users/ericf'); // => "User: ericf"
+
+        // URLs which do not match routes because params fail validation:
+        router.save('/posts/a');            // => "Catch-all no routes matched!"
+        router.save('/users/ericf,rgrove'); // => "Catch-all no routes matched!"
+
+    @method param
+    @param {String} name Name of the param used in route paths.
+    @param {Function|RegExp} handler Function to invoke or regular expression to
+        `exec()` during route dispatching whose return value is used as the new
+        param value. Values of `false`, `null`, `undefined`, or `NaN` will cause
+        the current route to not match and be skipped. When a function is
+        specified, it will be invoked in the context of this instance with the
+        following parameters:
+      @param {String} handler.value The current param value parsed from the URL.
+      @param {String} handler.name The name of the param.
+    @chainable
+    @since 3.12.0
+    **/
+    param: function (name, handler) {
+        this._params[name] = handler;
+        return this;
+    },
+
+    /**
+    Removes the `root` URL from the front of _url_ (if it's there) and returns
+    the result. The returned path will always have a leading `/`.
+
+    @method removeRoot
+    @param {String} url URL.
+    @return {String} Rootless path.
+    **/
+    removeRoot: function (url) {
+        var root = this.get('root'),
+            path;
+
+        // Strip out the non-path part of the URL, if any (e.g.
+        // "http://foo.com"), so that we're left with just the path.
+        url = url.replace(this._regexUrlOrigin, '');
+
+        // Return the host-less URL if there's no `root` path to further remove.
+        if (!root) {
+            return url;
+        }
+
+        path = this.removeQuery(url);
+
+        // Remove the `root` from the `url` if it's the same or its path is
+        // semantically within the root path.
+        if (path === root || this._pathHasRoot(root, path)) {
+            url = url.substring(root.length);
+        }
+
+        return url.charAt(0) === '/' ? url : '/' + url;
+    },
+
+    /**
+    Removes a query string from the end of the _url_ (if one exists) and returns
+    the result.
+
+    @method removeQuery
+    @param {String} url URL.
+    @return {String} Queryless path.
+    **/
+    removeQuery: function (url) {
+        return url.replace(/\?.*$/, '');
+    },
+
+    /**
+    Replaces the current browser history entry with a new one, and dispatches to
+    the first matching route handler, if any.
+
+    Behind the scenes, this method uses HTML5 `pushState()` in browsers that
+    support it (or the location hash in older browsers and IE) to change the
+    URL.
+
+    The specified URL must share the same origin (i.e., protocol, host, and
+    port) as the current page, or an error will occur.
+
+    @example
+        // Starting URL: http://example.com/
+
+        router.replace('/path/');
+        // New URL: http://example.com/path/
+
+        router.replace('/path?foo=bar');
+        // New URL: http://example.com/path?foo=bar
+
+        router.replace('/');
+        // New URL: http://example.com/
+
+    @method replace
+    @param {String} [url] URL to set. This URL needs to be of the same origin as
+      the current URL. This can be a URL relative to the router's `root`
+      attribute. If no URL is specified, the page's current URL will be used.
+    @chainable
+    @see save()
+    **/
+    replace: function (url) {
+        return this._queue(url, true);
+    },
+
+    /**
+    Adds a route handler for the specified `route`.
+
+    The `route` parameter may be a string or regular expression to represent a
+    URL path, or a route object. If it's a string (which is most common), it may
+    contain named parameters: `:param` will match any single part of a URL path
+    (not including `/` characters), and `*param` will match any number of parts
+    of a URL path (including `/` characters). These named parameters will be
+    made available as keys on the `req.params` object that's passed to route
+    handlers.
+
+    If the `route` parameter is a regex, all pattern matches will be made
+    available as numbered keys on `req.params`, starting with `0` for the full
+    match, then `1` for the first subpattern match, and so on.
+
+    Alternatively, an object can be provided to represent the route and it may
+    contain a `path` property which is a string or regular expression which
+    causes the route to be process as described above. If the route object
+    already contains a `regex` or `regexp` property, the route will be
+    considered fully-processed and will be associated with any `callacks`
+    specified on the object and those specified as parameters to this method.
+    **Note:** Any additional data contained on the route object will be
+    preserved.
+
+    Here's a set of sample routes along with URL paths that they match:
+
+      * Route: `/photos/:tag/:page`
+        * URL: `/photos/kittens/1`, params: `{tag: 'kittens', page: '1'}`
+        * URL: `/photos/puppies/2`, params: `{tag: 'puppies', page: '2'}`
+
+      * Route: `/file/*path`
+        * URL: `/file/foo/bar/baz.txt`, params: `{path: 'foo/bar/baz.txt'}`
+        * URL: `/file/foo`, params: `{path: 'foo'}`
+
+    **Middleware**: Routes also support an arbitrary number of callback
+    functions. This allows you to easily reuse parts of your route-handling code
+    with different route. This method is liberal in how it processes the
+    specified `callbacks`, you can specify them as separate arguments, or as
+    arrays, or both.
+
+    If multiple route match a given URL, they will be executed in the order they
+    were added. The first route that was added will be the first to be executed.
+
+    **Passing Control**: Invoking the `next()` function within a route callback
+    will pass control to the next callback function (if any) or route handler
+    (if any). If a value is passed to `next()`, it's assumed to be an error,
+    therefore stopping the dispatch chain, unless that value is: `"route"`,
+    which is special case and dispatching will skip to the next route handler.
+    This allows middleware to skip any remaining middleware for a particular
+    route.
+
+    @example
+        router.route('/photos/:tag/:page', function (req, res, next) {
+            Y.log('Current tag: ' + req.params.tag);
+            Y.log('Current page number: ' + req.params.page);
+        });
+
+        // Using middleware.
+
+        router.findUser = function (req, res, next) {
+            req.user = this.get('users').findById(req.params.user);
+            next();
+        };
+
+        router.route('/users/:user', 'findUser', function (req, res, next) {
+            // The `findUser` middleware puts the `user` object on the `req`.
+            Y.log('Current user:' req.user.get('name'));
+        });
+
+    @method route
+    @param {String|RegExp|Object} route Route to match. May be a string or a
+      regular expression, or a route object.
+    @param {Array|Function|String} callbacks* Callback functions to call
+        whenever this route is triggered. These can be specified as separate
+        arguments, or in arrays, or both. If a callback is specified as a
+        string, the named function will be called on this router instance.
+
+      @param {Object} callbacks.req Request object containing information about
+          the request. It contains the following properties.
+
+        @param {Array|Object} callbacks.req.params Captured parameters matched
+          by the route path specification. If a string path was used and
+          contained named parameters, then this will be a key/value hash mapping
+          parameter names to their matched values. If a regex path was used,
+          this will be an array of subpattern matches starting at index 0 for
+          the full match, then 1 for the first subpattern match, and so on.
+        @param {String} callbacks.req.path The current URL path.
+        @param {Number} callbacks.req.pendingCallbacks Number of remaining
+          callbacks the route handler has after this one in the dispatch chain.
+        @param {Number} callbacks.req.pendingRoutes Number of matching routes
+          after this one in the dispatch chain.
+        @param {Object} callbacks.req.query Query hash representing the URL
+          query string, if any. Parameter names are keys, and are mapped to
+          parameter values.
+        @param {Object} callbacks.req.route Reference to the current route
+          object whose callbacks are being dispatched.
+        @param {Object} callbacks.req.router Reference to this router instance.
+        @param {String} callbacks.req.src What initiated the dispatch. In an
+          HTML5 browser, when the back/forward buttons are used, this property
+          will have a value of "popstate". When the `dispath()` method is
+          called, the `src` will be `"dispatch"`.
+        @param {String} callbacks.req.url The full URL.
+
+      @param {Object} callbacks.res Response object containing methods and
+          information that relate to responding to a request. It contains the
+          following properties.
+        @param {Object} callbacks.res.req Reference to the request object.
+
+      @param {Function} callbacks.next Function to pass control to the next
+          callback or the next matching route if no more callbacks (middleware)
+          exist for the current route handler. If you don't call this function,
+          then no further callbacks or route handlers will be executed, even if
+          there are more that match. If you do call this function, then the next
+          callback (if any) or matching route handler (if any) will be called.
+          All of these functions will receive the same `req` and `res` objects
+          that were passed to this route (so you can use these objects to pass
+          data along to subsequent callbacks and routes).
+        @param {String} [callbacks.next.err] Optional error which will stop the
+          dispatch chaining for this `req`, unless the value is `"route"`, which
+          is special cased to jump skip past any callbacks for the current route
+          and pass control the next route handler.
+    @chainable
+    **/
+    route: function (route, callbacks) {
+        // Grab callback functions from var-args.
+        callbacks = YArray(arguments, 1, true);
+
+        var keys, regex;
+
+        // Supports both the `route(path, callbacks)` and `route(config)` call
+        // signatures, allowing for fully-processed route configs to be passed.
+        if (typeof route === 'string' || YLang.isRegExp(route)) {
+            // Flatten `callbacks` into a single dimension array.
+            callbacks = YArray.flatten(callbacks);
+
+            keys  = [];
+            regex = this._getRegex(route, keys);
+
+            route = {
+                callbacks: callbacks,
+                keys     : keys,
+                path     : route,
+                regex    : regex
+            };
+        } else {
+            // Look for any configured `route.callbacks` and fallback to
+            // `route.callback` for back-compat, append var-arg `callbacks`,
+            // then flatten the entire collection to a single dimension array.
+            callbacks = YArray.flatten(
+                [route.callbacks || route.callback || []].concat(callbacks)
+            );
+
+            // Check for previously generated regex, also fallback to `regexp`
+            // for greater interop.
+            keys  = route.keys;
+            regex = route.regex || route.regexp;
+
+            // Generates the route's regex if it doesn't already have one.
+            if (!regex) {
+                keys  = [];
+                regex = this._getRegex(route.path, keys);
+            }
+
+            // Merge specified `route` config object with processed data.
+            route = Y.merge(route, {
+                callbacks: callbacks,
+                keys     : keys,
+                path     : route.path || regex,
+                regex    : regex
+            });
+        }
+
+        this._routes.push(route);
+        return this;
+    },
+
+    /**
+    Saves a new browser history entry and dispatches to the first matching route
+    handler, if any.
+
+    Behind the scenes, this method uses HTML5 `pushState()` in browsers that
+    support it (or the location hash in older browsers and IE) to change the
+    URL and create a history entry.
+
+    The specified URL must share the same origin (i.e., protocol, host, and
+    port) as the current page, or an error will occur.
+
+    @example
+        // Starting URL: http://example.com/
+
+        router.save('/path/');
+        // New URL: http://example.com/path/
+
+        router.save('/path?foo=bar');
+        // New URL: http://example.com/path?foo=bar
+
+        router.save('/');
+        // New URL: http://example.com/
+
+    @method save
+    @param {String} [url] URL to set. This URL needs to be of the same origin as
+      the current URL. This can be a URL relative to the router's `root`
+      attribute. If no URL is specified, the page's current URL will be used.
+    @chainable
+    @see replace()
+    **/
+    save: function (url) {
+        return this._queue(url);
+    },
+
+    /**
+    Upgrades a hash-based URL to an HTML5 URL if necessary. In non-HTML5
+    browsers, this method is a noop.
+
+    @method upgrade
+    @return {Boolean} `true` if the URL was upgraded, `false` otherwise.
+    **/
+    upgrade: function () {
+        if (!this._html5) {
+            return false;
+        }
+
+        // Get the resolve hash path.
+        var hashPath = this._getHashPath();
+
+        if (hashPath) {
+            // This is an HTML5 browser and we have a hash-based path in the
+            // URL, so we need to upgrade the URL to a non-hash URL. This
+            // will trigger a `history:change` event, which will in turn
+            // trigger a dispatch.
+            this.once(EVT_READY, function () {
+                this.replace(hashPath);
+            });
+
+            return true;
+        }
+
+        return false;
+    },
+
+    // -- Protected Methods ----------------------------------------------------
+
+    /**
+    Wrapper around `decodeURIComponent` that also converts `+` chars into
+    spaces.
+
+    @method _decode
+    @param {String} string String to decode.
+    @return {String} Decoded string.
+    @protected
+    **/
+    _decode: function (string) {
+        return decodeURIComponent(string.replace(/\+/g, ' '));
+    },
+
+    /**
+    Shifts the topmost `_save()` call off the queue and executes it. Does
+    nothing if the queue is empty.
+
+    @method _dequeue
+    @chainable
+    @see _queue
+    @protected
+    **/
+    _dequeue: function () {
+        var self = this,
+            fn;
+
+        // If window.onload hasn't yet fired, wait until it has before
+        // dequeueing. This will ensure that we don't call pushState() before an
+        // initial popstate event has fired.
+        if (!YUI.Env.windowLoaded) {
+            Y.once('load', function () {
+                self._dequeue();
+            });
+
+            return this;
+        }
+
+        fn = saveQueue.shift();
+        return fn ? fn() : this;
+    },
+
+    /**
+    Dispatches to the first route handler that matches the specified _path_.
+
+    If called before the `ready` event has fired, the dispatch will be aborted.
+    This ensures normalized behavior between Chrome (which fires a `popstate`
+    event on every pageview) and other browsers (which do not).
+
+    @method _dispatch
+    @param {object} req Request object.
+    @param {String} res Response object.
+    @chainable
+    @protected
+    **/
+    _dispatch: function (req, res) {
+        var self      = this,
+            decode    = self._decode,
+            routes    = self.match(req.path),
+            callbacks = [],
+            matches, paramsMatch, routePath;
+
+        self._dispatching = self._dispatched = true;
+
+        if (!routes || !routes.length) {
+            self._dispatching = false;
+            return self;
+        }
+
+        routePath = self.removeRoot(req.path);
+
+        function next(err) {
+            var callback, name, route;
+
+            if (err) {
+                // Special case "route" to skip to the next route handler
+                // avoiding any additional callbacks for the current route.
+                if (err === 'route') {
+                    callbacks = [];
+                    next();
+                } else {
+                    Y.error(err);
+                }
+
+            } else if ((callback = callbacks.shift())) {
+                if (typeof callback === 'string') {
+                    name     = callback;
+                    callback = self[name];
+
+                    if (!callback) {
+                        Y.error('Router: Callback not found: ' + name, null, 'router');
+                    }
+                }
+
+                // Allow access to the number of remaining callbacks for the
+                // route.
+                req.pendingCallbacks = callbacks.length;
+
+                callback.call(self, req, res, next);
+
+            } else if ((route = routes.shift())) {
+                // Make a copy of this route's `callbacks` so the original array
+                // is preserved.
+                callbacks = route.callbacks.concat();
+
+                // Decode each of the path matches so that the any URL-encoded
+                // path segments are decoded in the `req.params` object.
+                matches = YArray.map(route.regex.exec(routePath) || [],
+                        function (match) {
+
+                    // Decode matches, or coerce `undefined` matches to an empty
+                    // string to match expectations of working with `req.params`
+                    // in the content of route dispatching, and normalize
+                    // browser differences in their handling of regex NPCGs:
+                    // https://github.com/yui/yui3/issues/1076
+                    return (match && decode(match)) || '';
+                });
+
+                paramsMatch = true;
+
+                // Use named keys for parameter names if the route path contains
+                // named keys. Otherwise, use numerical match indices.
+                if (matches.length === route.keys.length + 1) {
+                    matches    = matches.slice(1);
+                    req.params = YArray.hash(route.keys, matches);
+
+                    paramsMatch = YArray.every(route.keys, function (key, i) {
+                        var paramHandler = self._params[key],
+                            value        = matches[i];
+
+                        if (paramHandler && value && typeof value === 'string') {
+                            // Check if `paramHandler` is a RegExp, becuase this
+                            // is true in Android 2.3 and other browsers!
+                            // `typeof /.*/ === 'function'`
+                            value = YLang.isRegExp(paramHandler) ?
+                                    paramHandler.exec(value) :
+                                    paramHandler.call(self, value, key);
+
+                            if (value !== false && YLang.isValue(value)) {
+                                req.params[key] = value;
+                                return true;
+                            }
+
+                            return false;
+                        }
+
+                        return true;
+                    });
+                } else {
+                    req.params = matches.concat();
+                }
+
+                // Allow access to current route and the number of remaining
+                // routes for this request.
+                req.route         = route;
+                req.pendingRoutes = routes.length;
+
+                // Execute this route's `callbacks` or skip this route because
+                // some of the param regexps don't match.
+                if (paramsMatch) {
+                    next();
+                } else {
+                    next('route');
+                }
+            }
+        }
+
+        next();
+
+        self._dispatching = false;
+        return self._dequeue();
+    },
+
+    /**
+    Returns the resolved path from the hash fragment, or an empty string if the
+    hash is not path-like.
+
+    @method _getHashPath
+    @param {String} [hash] Hash fragment to resolve into a path. By default this
+        will be the hash from the current URL.
+    @return {String} Current hash path, or an empty string if the hash is empty.
+    @protected
+    **/
+    _getHashPath: function (hash) {
+        hash || (hash = HistoryHash.getHash());
+
+        // Make sure the `hash` is path-like.
+        if (hash && hash.charAt(0) === '/') {
+            return this._joinURL(hash);
+        }
+
+        return '';
+    },
+
+    /**
+    Gets the location origin (i.e., protocol, host, and port) as a URL.
+
+    @example
+        http://example.com
+
+    @method _getOrigin
+    @return {String} Location origin (i.e., protocol, host, and port).
+    @protected
+    **/
+    _getOrigin: function () {
+        var location = Y.getLocation();
+        return location.origin || (location.protocol + '//' + location.host);
+    },
+
+    /**
+    Getter for the `params` attribute.
+
+    @method _getParams
+    @return {Object} Mapping of param handlers: `name` -> RegExp | Function.
+    @protected
+    @since 3.12.0
+    **/
+    _getParams: function () {
+        return Y.merge(this._params);
+    },
+
+    /**
+    Gets the current route path.
+
+    @method _getPath
+    @return {String} Current route path.
+    @protected
+    **/
+    _getPath: function () {
+        var path = (!this._html5 && this._getHashPath()) ||
+                Y.getLocation().pathname;
+
+        return this.removeQuery(path);
+    },
+
+    /**
+    Returns the current path root after popping off the last path segment,
+    making it useful for resolving other URL paths against.
+
+    The path root will always begin and end with a '/'.
+
+    @method _getPathRoot
+    @return {String} The URL's path root.
+    @protected
+    @since 3.5.0
+    **/
+    _getPathRoot: function () {
+        var slash = '/',
+            path  = Y.getLocation().pathname,
+            segments;
+
+        if (path.charAt(path.length - 1) === slash) {
+            return path;
+        }
+
+        segments = path.split(slash);
+        segments.pop();
+
+        return segments.join(slash) + slash;
+    },
+
+    /**
+    Gets the current route query string.
+
+    @method _getQuery
+    @return {String} Current route query string.
+    @protected
+    **/
+    _getQuery: function () {
+        var location = Y.getLocation(),
+            hash, matches;
+
+        if (this._html5) {
+            return location.search.substring(1);
+        }
+
+        hash    = HistoryHash.getHash();
+        matches = hash.match(this._regexUrlQuery);
+
+        return hash && matches ? matches[1] : location.search.substring(1);
+    },
+
+    /**
+    Creates a regular expression from the given route specification. If _path_
+    is already a regex, it will be returned unmodified.
+
+    @method _getRegex
+    @param {String|RegExp} path Route path specification.
+    @param {Array} keys Array reference to which route parameter names will be
+      added.
+    @return {RegExp} Route regex.
+    @protected
+    **/
+    _getRegex: function (path, keys) {
+        if (YLang.isRegExp(path)) {
+            return path;
+        }
+
+        // Special case for catchall paths.
+        if (path === '*') {
+            return (/.*/);
+        }
+
+        path = path.replace(this._regexPathParam, function (match, operator, key) {
+            // Only `*` operators are supported for key-less matches to allowing
+            // in-path wildcards like: '/foo/*'.
+            if (!key) {
+                return operator === '*' ? '.*' : match;
+            }
+
+            keys.push(key);
+            return operator === '*' ? '(.*?)' : '([^/#?]*)';
+        });
+
+        return new RegExp('^' + path + '$');
+    },
+
+    /**
+    Gets a request object that can be passed to a route handler.
+
+    @method _getRequest
+    @param {String} src What initiated the URL change and need for the request.
+    @return {Object} Request object.
+    @protected
+    **/
+    _getRequest: function (src) {
+        return {
+            path  : this._getPath(),
+            query : this._parseQuery(this._getQuery()),
+            url   : this._getURL(),
+            router: this,
+            src   : src
+        };
+    },
+
+    /**
+    Gets a response object that can be passed to a route handler.
+
+    @method _getResponse
+    @param {Object} req Request object.
+    @return {Object} Response Object.
+    @protected
+    **/
+    _getResponse: function (req) {
+        return {req: req};
+    },
+
+    /**
+    Getter for the `routes` attribute.
+
+    @method _getRoutes
+    @return {Object[]} Array of route objects.
+    @protected
+    **/
+    _getRoutes: function () {
+        return this._routes.concat();
+    },
+
+    /**
+    Gets the current full URL.
+
+    @method _getURL
+    @return {String} URL.
+    @protected
+    **/
+    _getURL: function () {
+        var url = Y.getLocation().toString();
+
+        if (!this._html5) {
+            url = this._upgradeURL(url);
+        }
+
+        return url;
+    },
+
+    /**
+    Returns `true` when the specified `url` is from the same origin as the
+    current URL; i.e., the protocol, host, and port of the URLs are the same.
+
+    All host or path relative URLs are of the same origin. A scheme-relative URL
+    is first prefixed with the current scheme before being evaluated.
+
+    @method _hasSameOrigin
+    @param {String} url URL to compare origin with the current URL.
+    @return {Boolean} Whether the URL has the same origin of the current URL.
+    @protected
+    **/
+    _hasSameOrigin: function (url) {
+        var origin = ((url && url.match(this._regexUrlOrigin)) || [])[0];
+
+        // Prepend current scheme to scheme-relative URLs.
+        if (origin && origin.indexOf('//') === 0) {
+            origin = Y.getLocation().protocol + origin;
+        }
+
+        return !origin || origin === this._getOrigin();
+    },
+
+    /**
+    Joins the `root` URL to the specified _url_, normalizing leading/trailing
+    `/` characters.
+
+    @example
+        router.set('root', '/foo');
+        router._joinURL('bar');  // => '/foo/bar'
+        router._joinURL('/bar'); // => '/foo/bar'
+
+        router.set('root', '/foo/');
+        router._joinURL('bar');  // => '/foo/bar'
+        router._joinURL('/bar'); // => '/foo/bar'
+
+    @method _joinURL
+    @param {String} url URL to append to the `root` URL.
+    @return {String} Joined URL.
+    @protected
+    **/
+    _joinURL: function (url) {
+        var root = this.get('root');
+
+        // Causes `url` to _always_ begin with a "/".
+        url = this.removeRoot(url);
+
+        if (url.charAt(0) === '/') {
+            url = url.substring(1);
+        }
+
+        return root && root.charAt(root.length - 1) === '/' ?
+                root + url :
+                root + '/' + url;
+    },
+
+    /**
+    Returns a normalized path, ridding it of any '..' segments and properly
+    handling leading and trailing slashes.
+
+    @method _normalizePath
+    @param {String} path URL path to normalize.
+    @return {String} Normalized path.
+    @protected
+    @since 3.5.0
+    **/
+    _normalizePath: function (path) {
+        var dots  = '..',
+            slash = '/',
+            i, len, normalized, segments, segment, stack;
+
+        if (!path || path === slash) {
+            return slash;
+        }
+
+        segments = path.split(slash);
+        stack    = [];
+
+        for (i = 0, len = segments.length; i < len; ++i) {
+            segment = segments[i];
+
+            if (segment === dots) {
+                stack.pop();
+            } else if (segment) {
+                stack.push(segment);
+            }
+        }
+
+        normalized = slash + stack.join(slash);
+
+        // Append trailing slash if necessary.
+        if (normalized !== slash && path.charAt(path.length - 1) === slash) {
+            normalized += slash;
+        }
+
+        return normalized;
+    },
+
+    /**
+    Parses a URL query string into a key/value hash. If `Y.QueryString.parse` is
+    available, this method will be an alias to that.
+
+    @method _parseQuery
+    @param {String} query Query string to parse.
+    @return {Object} Hash of key/value pairs for query parameters.
+    @protected
+    **/
+    _parseQuery: QS && QS.parse ? QS.parse : function (query) {
+        var decode = this._decode,
+            params = query.split('&'),
+            i      = 0,
+            len    = params.length,
+            result = {},
+            param;
+
+        for (; i < len; ++i) {
+            param = params[i].split('=');
+
+            if (param[0]) {
+                result[decode(param[0])] = decode(param[1] || '');
+            }
+        }
+
+        return result;
+    },
+
+    /**
+    Returns `true` when the specified `path` is semantically within the
+    specified `root` path.
+
+    If the `root` does not end with a trailing slash ("/"), one will be added
+    before the `path` is evaluated against the root path.
+
+    @example
+        this._pathHasRoot('/app',  '/app/foo'); // => true
+        this._pathHasRoot('/app/', '/app/foo'); // => true
+        this._pathHasRoot('/app/', '/app/');    // => true
+
+        this._pathHasRoot('/app',  '/foo/bar'); // => false
+        this._pathHasRoot('/app/', '/foo/bar'); // => false
+        this._pathHasRoot('/app/', '/app');     // => false
+        this._pathHasRoot('/app',  '/app');     // => false
+
+    @method _pathHasRoot
+    @param {String} root Root path used to evaluate whether the specificed
+        `path` is semantically within. A trailing slash ("/") will be added if
+        it does not already end with one.
+    @param {String} path Path to evaluate for containing the specified `root`.
+    @return {Boolean} Whether or not the `path` is semantically within the
+        `root` path.
+    @protected
+    @since 3.13.0
+    **/
+    _pathHasRoot: function (root, path) {
+        var rootPath = root.charAt(root.length - 1) === '/' ? root : root + '/';
+        return path.indexOf(rootPath) === 0;
+    },
+
+    /**
+    Queues up a `_save()` call to run after all previously-queued calls have
+    finished.
+
+    This is necessary because if we make multiple `_save()` calls before the
+    first call gets dispatched, then both calls will dispatch to the last call's
+    URL.
+
+    All arguments passed to `_queue()` will be passed on to `_save()` when the
+    queued function is executed.
+
+    @method _queue
+    @chainable
+    @see _dequeue
+    @protected
+    **/
+    _queue: function () {
+        var args = arguments,
+            self = this;
+
+        saveQueue.push(function () {
+            if (self._html5) {
+                if (Y.UA.ios && Y.UA.ios < 5) {
+                    // iOS <5 has buggy HTML5 history support, and needs to be
+                    // synchronous.
+                    self._save.apply(self, args);
+                } else {
+                    // Wrapped in a timeout to ensure that _save() calls are
+                    // always processed asynchronously. This ensures consistency
+                    // between HTML5- and hash-based history.
+                    setTimeout(function () {
+                        self._save.apply(self, args);
+                    }, 1);
+                }
+            } else {
+                self._dispatching = true; // otherwise we'll dequeue too quickly
+                self._save.apply(self, args);
+            }
+
+            return self;
+        });
+
+        return !this._dispatching ? this._dequeue() : this;
+    },
+
+    /**
+    Returns the normalized result of resolving the `path` against the current
+    path. Falsy values for `path` will return just the current path.
+
+    @method _resolvePath
+    @param {String} path URL path to resolve.
+    @return {String} Resolved path.
+    @protected
+    @since 3.5.0
+    **/
+    _resolvePath: function (path) {
+        if (!path) {
+            return Y.getLocation().pathname;
+        }
+
+        if (path.charAt(0) !== '/') {
+            path = this._getPathRoot() + path;
+        }
+
+        return this._normalizePath(path);
+    },
+
+    /**
+    Resolves the specified URL against the current URL.
+
+    This method resolves URLs like a browser does and will always return an
+    absolute URL. When the specified URL is already absolute, it is assumed to
+    be fully resolved and is simply returned as is. Scheme-relative URLs are
+    prefixed with the current protocol. Relative URLs are giving the current
+    URL's origin and are resolved and normalized against the current path root.
+
+    @method _resolveURL
+    @param {String} url URL to resolve.
+    @return {String} Resolved URL.
+    @protected
+    @since 3.5.0
+    **/
+    _resolveURL: function (url) {
+        var parts    = url && url.match(this._regexURL),
+            origin, path, query, hash, resolved;
+
+        if (!parts) {
+            return Y.getLocation().toString();
+        }
+
+        origin = parts[1];
+        path   = parts[2];
+        query  = parts[3];
+        hash   = parts[4];
+
+        // Absolute and scheme-relative URLs are assumed to be fully-resolved.
+        if (origin) {
+            // Prepend the current scheme for scheme-relative URLs.
+            if (origin.indexOf('//') === 0) {
+                origin = Y.getLocation().protocol + origin;
+            }
+
+            return origin + (path || '/') + (query || '') + (hash || '');
+        }
+
+        // Will default to the current origin and current path.
+        resolved = this._getOrigin() + this._resolvePath(path);
+
+        // A path or query for the specified URL trumps the current URL's.
+        if (path || query) {
+            return resolved + (query || '') + (hash || '');
+        }
+
+        query = this._getQuery();
+
+        return resolved + (query ? ('?' + query) : '') + (hash || '');
+    },
+
+    /**
+    Saves a history entry using either `pushState()` or the location hash.
+
+    This method enforces the same-origin security constraint; attempting to save
+    a `url` that is not from the same origin as the current URL will result in
+    an error.
+
+    @method _save
+    @param {String} [url] URL for the history entry.
+    @param {Boolean} [replace=false] If `true`, the current history entry will
+      be replaced instead of a new one being added.
+    @chainable
+    @protected
+    **/
+    _save: function (url, replace) {
+        var urlIsString = typeof url === 'string',
+            currentPath, root, hash;
+
+        // Perform same-origin check on the specified URL.
+        if (urlIsString && !this._hasSameOrigin(url)) {
+            Y.error('Security error: The new URL must be of the same origin as the current URL.');
+            return this;
+        }
+
+        // Joins the `url` with the `root`.
+        if (urlIsString) {
+            url = this._joinURL(url);
+        }
+
+        // Force _ready to true to ensure that the history change is handled
+        // even if _save is called before the `ready` event fires.
+        this._ready = true;
+
+        if (this._html5) {
+            this._history[replace ? 'replace' : 'add'](null, {url: url});
+        } else {
+            currentPath = Y.getLocation().pathname;
+            root        = this.get('root');
+            hash        = HistoryHash.getHash();
+
+            if (!urlIsString) {
+                url = hash;
+            }
+
+            // Determine if the `root` already exists in the current location's
+            // `pathname`, and if it does then we can exclude it from the
+            // hash-based path. No need to duplicate the info in the URL.
+            if (root === currentPath || root === this._getPathRoot()) {
+                url = this.removeRoot(url);
+            }
+
+            // The `hashchange` event only fires when the new hash is actually
+            // different. This makes sure we'll always dequeue and dispatch
+            // _all_ router instances, mimicking the HTML5 behavior.
+            if (url === hash) {
+                Y.Router.dispatch();
+            } else {
+                HistoryHash[replace ? 'replaceHash' : 'setHash'](url);
+            }
+        }
+
+        return this;
+    },
+
+    /**
+    Setter for the `params` attribute.
+
+    @method _setParams
+    @param {Object} params Map in the form: `name` -> RegExp | Function.
+    @return {Object} The map of params: `name` -> RegExp | Function.
+    @protected
+    @since 3.12.0
+    **/
+    _setParams: function (params) {
+        this._params = {};
+
+        YObject.each(params, function (regex, name) {
+            this.param(name, regex);
+        }, this);
+
+        return Y.merge(this._params);
+    },
+
+    /**
+    Setter for the `routes` attribute.
+
+    @method _setRoutes
+    @param {Object[]} routes Array of route objects.
+    @return {Object[]} Array of route objects.
+    @protected
+    **/
+    _setRoutes: function (routes) {
+        this._routes = [];
+
+        YArray.each(routes, function (route) {
+            this.route(route);
+        }, this);
+
+        return this._routes.concat();
+    },
+
+    /**
+    Upgrades a hash-based URL to a full-path URL, if necessary.
+
+    The specified `url` will be upgraded if its of the same origin as the
+    current URL and has a path-like hash. URLs that don't need upgrading will be
+    returned as-is.
+
+    @example
+        app._upgradeURL('http://example.com/#/foo/'); // => 'http://example.com/foo/';
+
+    @method _upgradeURL
+    @param {String} url The URL to upgrade from hash-based to full-path.
+    @return {String} The upgraded URL, or the specified URL untouched.
+    @protected
+    @since 3.5.0
+    **/
+    _upgradeURL: function (url) {
+        // We should not try to upgrade paths for external URLs.
+        if (!this._hasSameOrigin(url)) {
+            return url;
+        }
+
+        var hash       = (url.match(/#(.*)$/) || [])[1] || '',
+            hashPrefix = Y.HistoryHash.hashPrefix,
+            hashPath;
+
+        // Strip any hash prefix, like hash-bangs.
+        if (hashPrefix && hash.indexOf(hashPrefix) === 0) {
+            hash = hash.replace(hashPrefix, '');
+        }
+
+        // If the hash looks like a URL path, assume it is, and upgrade it!
+        if (hash) {
+            hashPath = this._getHashPath(hash);
+
+            if (hashPath) {
+                return this._resolveURL(hashPath);
+            }
+        }
+
+        return url;
+    },
+
+    // -- Protected Event Handlers ---------------------------------------------
+
+    /**
+    Handles `history:change` and `hashchange` events.
+
+    @method _afterHistoryChange
+    @param {EventFacade} e
+    @protected
+    **/
+    _afterHistoryChange: function (e) {
+        var self       = this,
+            src        = e.src,
+            prevURL    = self._url,
+            currentURL = self._getURL(),
+            req, res;
+
+        self._url = currentURL;
+
+        // Handles the awkwardness that is the `popstate` event. HTML5 browsers
+        // fire `popstate` right before they fire `hashchange`, and Chrome fires
+        // `popstate` on page load. If this router is not ready or the previous
+        // and current URLs only differ by their hash, then we want to ignore
+        // this `popstate` event.
+        if (src === 'popstate' &&
+                (!self._ready || prevURL.replace(/#.*$/, '') === currentURL.replace(/#.*$/, ''))) {
+
+            return;
+        }
+
+        req = self._getRequest(src);
+        res = self._getResponse(req);
+
+        self._dispatch(req, res);
+    },
+
+    // -- Default Event Handlers -----------------------------------------------
+
+    /**
+    Default handler for the `ready` event.
+
+    @method _defReadyFn
+    @param {EventFacade} e
+    @protected
+    **/
+    _defReadyFn: function (e) {
+        this._ready = true;
+    }
+}, {
+    // -- Static Properties ----------------------------------------------------
+    NAME: 'router',
+
+    ATTRS: {
+        /**
+        Whether or not this browser is capable of using HTML5 history.
+
+        Setting this to `false` will force the use of hash-based history even on
+        HTML5 browsers, but please don't do this unless you understand the
+        consequences.
+
+        @attribute html5
+        @type Boolean
+        @initOnly
+        **/
+        html5: {
+            // Android versions lower than 3.0 are buggy and don't update
+            // window.location after a pushState() call, so we fall back to
+            // hash-based history for them.
+            //
+            // See http://code.google.com/p/android/issues/detail?id=17471
+            valueFn: function () { return Y.Router.html5; },
+            writeOnce: 'initOnly'
+        },
+
+        /**
+        Map of params handlers in the form: `name` -> RegExp | Function.
+
+        If a param handler regex or function returns a value of `false`, `null`,
+        `undefined`, or `NaN`, the current route will not match and be skipped.
+        All other return values will be used in place of the original param
+        value parsed from the URL.
+
+        This attribute is intended to be used to set params at init time, or to
+        completely reset all params after init. To add params after init without
+        resetting all existing params, use the `param()` method.
+
+        @attribute params
+        @type Object
+        @default `{}`
+        @see param
+        @since 3.12.0
+        **/
+        params: {
+            value : {},
+            getter: '_getParams',
+            setter: '_setParams'
+        },
+
+        /**
+        Absolute root path from which all routes should be evaluated.
+
+        For example, if your router is running on a page at
+        `http://example.com/myapp/` and you add a route with the path `/`, your
+        route will never execute, because the path will always be preceded by
+        `/myapp`. Setting `root` to `/myapp` would cause all routes to be
+        evaluated relative to that root URL, so the `/` route would then execute
+        when the user browses to `http://example.com/myapp/`.
+
+        @example
+            router.set('root', '/myapp');
+            router.route('/foo', function () { ... });
+
+            Y.log(router.hasRoute('/foo'));       // => false
+            Y.log(router.hasRoute('/myapp/foo')); // => true
+
+            // Updates the URL to: "/myapp/foo"
+            router.save('/foo');
+
+        @attribute root
+        @type String
+        @default `''`
+        **/
+        root: {
+            value: ''
+        },
+
+        /**
+        Array of route objects.
+
+        Each item in the array must be an object with the following properties
+        in order to be processed by the router:
+
+          * `path`: String or regex representing the path to match. See the docs
+            for the `route()` method for more details.
+
+          * `callbacks`: Function or a string representing the name of a
+            function on this router instance that should be called when the
+            route is triggered. An array of functions and/or strings may also be
+            provided. See the docs for the `route()` method for more details.
+
+        If a route object contains a `regex` or `regexp` property, or if its
+        `path` is a regular express, then the route will be considered to be
+        fully-processed. Any fully-processed routes may contain the following
+        properties:
+
+          * `regex`: The regular expression representing the path to match, this
+            property may also be named `regexp` for greater compatibility.
+
+          * `keys`: Array of named path parameters used to populate `req.params`
+            objects when dispatching to route handlers.
+
+        Any additional data contained on these route objects will be retained.
+        This is useful to store extra metadata about a route; e.g., a `name` to
+        give routes logical names.
+
+        This attribute is intended to be used to set routes at init time, or to
+        completely reset all routes after init. To add routes after init without
+        resetting all existing routes, use the `route()` method.
+
+        @attribute routes
+        @type Object[]
+        @default `[]`
+        @see route
+        **/
+        routes: {
+            value : [],
+            getter: '_getRoutes',
+            setter: '_setRoutes'
+        }
+    },
+
+    // Used as the default value for the `html5` attribute, and for testing.
+    html5: Y.HistoryBase.html5 && (!Y.UA.android || Y.UA.android >= 3),
+
+    // To make this testable.
+    _instances: instances,
+
+    /**
+    Dispatches to the first route handler that matches the specified `path` for
+    all active router instances.
+
+    This provides a mechanism to cause all active router instances to dispatch
+    to their route handlers without needing to change the URL or fire the
+    `history:change` or `hashchange` event.
+
+    @method dispatch
+    @static
+    @since 3.6.0
+    **/
+    dispatch: function () {
+        var i, len, router, req, res;
+
+        for (i = 0, len = instances.length; i < len; i += 1) {
+            router = instances[i];
+
+            if (router) {
+                req = router._getRequest('dispatch');
+                res = router._getResponse(req);
+
+                router._dispatch(req, res);
+            }
+        }
+    }
+});
+
+/**
+The `Controller` class was deprecated in YUI 3.5.0 and is now an alias for the
+`Router` class. Use that class instead. This alias will be removed in a future
+version of YUI.
+
+@class Controller
+@constructor
+@extends Base
+@deprecated Use `Router` instead.
+@see Router
+**/
+Y.Controller = Y.Router;
+
+
+}, '@VERSION@', {"optional": ["querystring-parse"], "requires": ["array-extras", "base-build", "history"]});