Hi, I am developing my own javascript api router. It performs routing based on #FregmentIdentifiers (document.location.hash).
Api almos is finished, but I'm still working on the backbuttom event. Whenever the backbuttom is clicked and the hash has changed and been seen before, the old content will be restored.
Do you know a way to save and restore all content?
My problem is that if I save and restore document.body.innerHTML, then only the markup will be restored, but not events, i.e. googlemaps stops working. I tried to clone document.body or document.documentElement, but javascript either told me that there is no setter in the field or that my clone is invalid.
EDIT:
To clearly understand what I'm working on, I decided to publish my current code. The question is about parts marked by comment // TODO.
function Router(){ var that = this; var router = this; var executionObservers = []; that.routes = []; this.registerRoute = function(route){ that.routes.push(route); }; var history = null; this.init = function(){ var i; var identifier = document.location.hash; history = new History(); history.start(); if(identifier.length > 0){ identifier = identifier.substring(1,identifier.length); for(i = 0; i< that.routes.length; i++){ var route = that.routes[i]; if(route.contains(identifier)){ route.getAction(identifier)(route.getParams(identifier)); return true; } } } return false; }; this.executed = function (identifier){ var i; for(i=0; i<executionObservers.length; i++){ executionObservers[i](identifier); } document.location.hash = identifier; }; this.addExecutionObserver = function(observer){ executionObservers.push(observer); }; function History(){ var history = []; var timeout = 200; var lastAddedHash = null; var loop = function(callback){ var hash = window.location.hash; window.setTimeout( function(){ if(window.location.hash!=hash){ hash = window.location.hash; callback(hash); } loop(callback); }, timeout ); }; this.add = function(hash){ lastAddedHash = hash; window.setTimeout(addCallback(hash), timeout); }; addCallback = function(hash){ return function(){ var i; var found = false; for(i =0; i< history.length&&!found; i++){ if(history[i][1] == hash){ found = true; //TODO create backup //history[i][0] = } } if(!found){history.push(new Array(document.documentElement.cloneNode(true),hash));} } } this.setTimeout = function(micoseconds){ timeout = microseconds; }; started = false; this.start = function(){ if(!started){ started = true; loop(function(hash){ var i; if(lastAddedHash!=null&&hash!=lastAddedHash){ for(i =0; i<history.length; i++){ if(history[i][1] == hash){ //TODO restore from backup document.location.reload(); } } } }); } }; router.addExecutionObserver(this.add); } } Router.instance = null; Router.getInstance = function(){ if(Router.instance === null ){ Router.instance = new Router(); } return Router.instance; }; /** * @param getParams = function(identifier) * @param getIdentifier = function(params) * @param contains = function(identifier) */ function Route(action, getParams, getIdentifier, contains){ var that = this; var router = Router.getInstance(); this.contains = contains; this.getParams = getParams; this.getAction = function(){ return action; } this.reExecute = function(identifier){ action(getParams(identifier)); }; this.execute = function(params){ action(params); this.executed(params); } this.executed = function(params){ router.executed('#' + getIdentifier(params)); }; this.register = function(){ router.registerRoute(this); }; } function PrefixedRouterConfig(prefix,paramRegexes){ this.contains = function(identifier){ var regex = "^" + prefix; for(var i=0;i<paramRegexes.length;i++){ regex+="_"+paramRegexes[i]; } regex +="$"; var match = identifier.match(regex); return match != null && (typeof match) == 'object' && (match[0] == identifier); }; this.getIdentifier = function(params){ ret = prefix; for(var i=0;i<params.length;i++){ ret+="_"+params[i]; } return ret; }; this.getParams = function(identifier){ var regex = "^" + prefix; for(var i=0;i<paramRegexes.length;i++){ regex+="_("+paramRegexes[i]+")"; } regex +="$"; var matches = identifier.match(regex); var ret = []; for(var i=1;i<matches.length;i++){ ret.push(matches[i]); } return ret; }; }
An example of using my api might look like this:
config = new PrefixedRouterConfig('show_map',new Array("\\d+", "-?\\d+(?:\\.\\d+)?", "-?\\d+(?:\\.\\d+)?")); var ROUTE_SHOW_MAP = new Route( function(params){ var zoom = params[0]; var lat = params[1]; var lng = params[2]; MyGmapInterface.preparePage(-1); addTabSelectedCallback(MyGmapInterface.tabLoaded); addTabClosedCallback(MyGmapInterface.tabClosed); MyGmapInterface.tabsLoaded = true; MyGmapInterface.myMap = new MyMap(lat,lng,zoom,MyGmapInterface.getMapContainer(),MyGmapInterface.notCompatible); MyGmapInterface.addNewCamMarkers(MyGmapInterface.loadCams()); MyGmapInterface.initListeners(); tabSelected(TAB_LEFT); }, config.getParams, config.getIdentifier, config.contains ); ROUTE_SHOW_MAP.register();
After including all the Javascript files (which can register routes) I call Router.getInstance (). init ();
When I make an ajax request somewhere (manually) for which a route exists, I call ROUTE_NAME.executed () to set the identifier and register it with the history.
In addition, I have an observer that updates some links that are used for direct translations, whenever the location hash is changed using executed ()