diff --git a/lib/cloak/router.js b/lib/cloak/router.js index aecd94c..2f7c42a 100644 --- a/lib/cloak/router.js +++ b/lib/cloak/router.js @@ -4,22 +4,34 @@ var History = require('history'); var AppObject = require('cloak/app-object'); var _ = require('cloak/underscore'); +var defaults = { + autoStart: true, + isTopLevel: true +}; + // // Router class // var Router = module.exports = AppObject.extend({ routes: null, - _routes: [ ], + _opts: null, + _routes: null, + _subRouters: null, _isOn: false, _variablePattern: /:([^\/]+)/g, _currentUrl: null, _currentRoute: null, _isAnchor: false, - init: function() { + init: function(opts) { this._super(); this._routes = [ ]; + this._opts = _.defaults(opts || { }, Router.defaults); + + if (this._opts.isTopLevel) { + this.topLevel = this; + } _.forEach(_.keys(this.routes), _.bind( @@ -29,11 +41,13 @@ var Router = module.exports = AppObject.extend({ this) ); + this._subRouters = [ ]; + this.bind('handleAnchor'); // Listen for history.statechange events this.bind('_onstatechange'); - if (History.enabled) { + if (History.enabled && this._opts.autoStart) { this.start(); } @@ -41,11 +55,28 @@ var Router = module.exports = AppObject.extend({ if (typeof this.initialize === 'function') { this.initialize.apply(this, arguments); } + }, + + // + // Add another router's routes to this one + // + // @param {router} the router to use + // @return this + // + use: function(router) { + // If given a router constructor, create an instance + if (typeof router === 'function') { + router = new router({ + autoStart: false, + isTopLevel: false + }); + } + + router.parent = this; + router.topLevel = this.topLevel; + this._subRouters.push(router); - // When we initialize a new router, we trigger a statechange event. This shouldn't - // cause any issues, though, as we ignore statechanges that have the same url as - // the current one - cloak.$win.trigger('statechange'); + return this; }, // @@ -57,6 +88,11 @@ var Router = module.exports = AppObject.extend({ if (! this._isOn) { this._isOn = true; cloak.$win.on('statechange', this._onstatechange); + + // When we start a router, we trigger a statechange event. This shouldn't + // cause any issues, though, as we ignore statechanges that have the same url as + // the current one + cloak.$win.trigger('statechange'); } }, @@ -228,20 +264,41 @@ var Router = module.exports = AppObject.extend({ // If the currently tracked url is the one we're already on, emit an event and move on if (state.hash === this._currentUrl) { this.emit('reload', this._currentRoute.params, this._currentRoute.href, data); - return; + return true; } - cloak.log('State Change: ' + state.hash); + if (this._opts.isTopLevel) { + cloak.log('State Change: ' + state.hash); + } this._currentUrl = state.hash; this._currentRoute = this._find(state.hash); // Handle unrecognized routes if (! this._currentRoute) { - return this.emit('notfound', state); + this.emit('notfound', state); + return this._deferToSubRouters(); } + this._currentRoute.func(this._currentRoute.params, this._currentRoute.href, data); + return true; + }, + + // + // Checks any listed sub-routers for a match + // + // @return void + // + _deferToSubRouters: function() { + return !! _.find(this._subRouters, function(subRouter) { + return subRouter._onstatechange(); + }); } }); + +// +// Expose the default config object +// +Router.defaults = defaults;