define([ "./Content", "./Path", "dojo/_base/declare", "dojo/_base/event", "dojo/_base/lang", "dojo/hash", "dojo/on", "dojo/topic", "require" ], function(Content, Path, declare, event, lang, hash, on, topic, require) { var Router = declare(null,{ _started: false, _routes: null, _previousHash: null, _beforePreviousHash: null, constructor: function() { this._routes = []; }, startup: function() { if ( this._started ) { return; } Content.startup(); this._started = true; if ( hash() === "" ) { hash(Path.getDefault()); } this._handleHashChange(hash()); window.onbeforeunload = function(evt) { var msg; if ( Content.isDirty() ) { msg = "Unsaved changes, leave anyway?"; evt.returnValue = msg; } return msg; }; topic.subscribe("/dojo/hashchange", lang.hitch(this,'_handleHashChange')); }, register: function(route) { if ( this._started ) { console.warn('Registering routes after startup() is called is discouraged.'); } var callback = this._normalizeCallback(route); if ( callback ) { if ( route.path ) { route.callback = callback; route.path = new Path(route.path); this._routes.push(route); } else { this._defaultCallback = callback; } } else { console.warn("Route "+(route.path||"default")+" has no action."); } }, _normalizeCallback: function(route) { var self = this; var callback = null; if ( route.callback ) { callback = function(params){ Content.set(route.callback(params)); }; } else if ( route.redirect ) { callback = function(params){ self.go(route.redirect); }; } else if ( route.constructor ) { callback = function(params){ Content.set( new route.constructor(params) ); }; } return callback; }, _handleHashChange: function(newHash) { if ( this._previousHash === newHash ) { return false; } if ( Content.isDirty() ) { if ( !confirm("Unsaved changes, leave anyway?") ) { var probablyBack = this._beforePreviousHash === newHash; // if we think we go backwards, we re-add the history // entry, otherwise we reset the current one, // resulting in minor annoyance of double back // behaviour. hash(this._previousHash,!probablyBack); return false; } else { Content.markClean(); } } this._beforePreviousHash = this._previousHash; this._previousHash = newHash; for (var i = this._routes.length-1; i >= 0; i--) { var route = this._routes[i]; var params = null; if ((params = route.path.match(newHash)) !== null) { try { route.callback(params); } catch(err) { console.error("Page change failed with",err,err.stack,err.toString()); } return true; } } try { this._defaultCallback(); } catch(err) { console.error("Default page failed.",err); } return true; }, go: function(path,args,replace) { if ( !this._started ) { return; } var newHash = Path.format(path,args); if ( replace === true ) { this._beforePreviousHash = this._previousHash; this._previousHash = newHash; } hash(newHash,replace); }, _defaultCallback: function() { var Page = require("./Page"); Content.set(new Page({ templateString: "