From 76663b0c91b5f81516f864dfb5481c4f7b929656 Mon Sep 17 00:00:00 2001 From: John Bender Date: Mon, 17 Dec 2012 14:29:16 -0800 Subject: [PATCH] fix initial state for alternate init case --- js/jquery.mobile.init.js | 10 +- js/navigation/navigate.js | 228 +++++++++++++++++++------------------- 2 files changed, 121 insertions(+), 117 deletions(-) diff --git a/js/jquery.mobile.init.js b/js/jquery.mobile.init.js index 08752f865c2..ac64175acb3 100644 --- a/js/jquery.mobile.init.js +++ b/js/jquery.mobile.init.js @@ -104,8 +104,12 @@ define([ $.mobile.urlHistory.initialDst = hash.replace( "#", "" ); } - var loc = path.parseLocation(); - // $.navigate.history.add( loc.href, {hash: loc.hash} ); + // make sure to set initial popstate state if it exists + // so that navigation back to the initial page works properly + if( $.event.special.navigate.isPushStateEnabled() ) { + $.navigate.navigator.squash( path.parseLocation().href ); + } + $.mobile.changePage( $.mobile.firstPage, { transition: "none", reverse: true, @@ -115,7 +119,7 @@ define([ } else { // trigger hashchange or navigate to squash and record the correct // history entry for an initial hash path - if( $.event.special.navigate.originalEventName === "hashchange" ) { + if( !$.event.special.navigate.isPushStateEnabled() ) { $window.trigger( "hashchange", [true] ); } else { // TODO figure out how to simplify this interaction with the initial history entry diff --git a/js/navigation/navigate.js b/js/navigation/navigate.js index d1c52e405d3..68d94b78c58 100644 --- a/js/navigation/navigate.js +++ b/js/navigation/navigate.js @@ -17,121 +17,121 @@ define([ this.history = history; }; - $.extend($.Navigator.prototype, { - squash: function( url, data ) { - var state, href, - hash = path.isPath(url) ? path.stripHash(url) : url; - - href = path.squash( url ); - - // make sure to provide this information when it isn't explicitly set in the - // data object that was passed to the squash method - state = $.extend({ - hash: hash, - url: href - }, data); - - // replace the current url with the new href and store the state - // Note that in some cases we might be replacing an url with the - // same url. We do this anyways because we need to make sure that - // all of our history entries have a state object associated with - // them. This allows us to work around the case where $.mobile.back() - // is called to transition from an external page to an embedded page. - // In that particular case, a hashchange event is *NOT* generated by the browser. - // Ensuring each history entry has a state object means that onPopState() - // will always trigger our hashchange callback even when a hashchange event - // is not fired. - window.history.replaceState( state, state.title || document.title, href ); - - return state; - }, - - go: function( url, data, noEvents ) { - var state, href, parsed, loc, hash, popstateEvent, - isPopStateEvent = $.event.special.navigate.isPushStateEnabled(), - resolutionUrl = path.isPath( url ) ? path.getLocation() : $.mobile.getDocumentUrl(); - - // Get the url as it would look squashed on to the current resolution url - href = path.squash( url ); - - // Grab the hash for recording. If the passed url is a path - // we used the parsed version of the squashed url to reconstruct, - // otherwise we assume it's a hash and store it directly - parsed = path.parseUrl( url ); - loc = path.parseLocation(); - - if( loc.pathname + loc.search === parsed.pathname + parsed.search ) { - // If the pathname and search of the passed url is identical to the current loc - // then we must use the hash. Otherwise there will be no event - // eg, url = "/foo/bar?baz#bang", location.href = "http://example.com/foo/bar?baz" - hash = parsed.hash ? parsed.hash : parsed.pathname + parsed.search; - } else if ( path.isPath(url) ) { - var resolved = path.parseUrl( href ); - // If the passed url is a path, make it domain relative and remove any trailing hash - hash = resolved.pathname + resolved.search + (path.isPreservableHash( resolved.hash )? resolved.hash.replace( "#", "" ) : ""); - } else { - hash = url; - } - - // Here we prevent the next hash change or popstate event from doing any - // history management. In the case of hashchange we don't swallow it - // if there will be no hashchange fired (since that won't reset the value) - // and will swallow the following hashchange - history.ignoreNextHashChange = true; - if( noEvents && hash !== path.stripHash(path.parseLocation().hash) ) { - history.preventNextHashChange = noEvents; - } - - // IMPORTANT in the case where popstate is supported the event will be triggered - // directly, stopping further execution - ie, interupting the flow of this - // method call to fire bindings at this expression. Below the navigate method - // there is a binding to catch this event and stop its propagation. - // - // We then trigger a new popstate event on the window with a null state - // so that the navigate events can conclude their work properly - // - // if the url is a path we want to preserve the query params that are available on - // the current url. - window.location.hash = hash; - - state = $.extend({ - url: href, - hash: hash, - title: document.title - }, data); - - if( isPopStateEvent ) { - popstateEvent = new $.Event( "popstate" ); - popstateEvent.originalEvent = { - type: "popstate", - state: null - }; - - this.squash( url, state ); - - // Trigger a new faux popstate event to replace the one that we - // caught that was triggered by the hash setting above. - if( !noEvents ) { - history.ignoreNextPopState = true; - $( window ).trigger( popstateEvent ); - } - } - - // record the history entry so that the information can be included - // in hashchange event driven navigate events in a similar fashion to - // the state that's provided by popstate - history.add( state.url, state ); - } - }); - - // TODO replace singleton history object - $.History = function() { - this.stack = []; - this.activeIndex = 0; - this.initialDst = path.parseLocation().hash.replace( /^#/, "" ); + $.extend($.Navigator.prototype, { + squash: function( url, data ) { + var state, href, + hash = path.isPath(url) ? path.stripHash(url) : url; + + href = path.squash( url ); + + // make sure to provide this information when it isn't explicitly set in the + // data object that was passed to the squash method + state = $.extend({ + hash: hash, + url: href + }, data); + + // replace the current url with the new href and store the state + // Note that in some cases we might be replacing an url with the + // same url. We do this anyways because we need to make sure that + // all of our history entries have a state object associated with + // them. This allows us to work around the case where $.mobile.back() + // is called to transition from an external page to an embedded page. + // In that particular case, a hashchange event is *NOT* generated by the browser. + // Ensuring each history entry has a state object means that onPopState() + // will always trigger our hashchange callback even when a hashchange event + // is not fired. + window.history.replaceState( state, state.title || document.title, href ); + + return state; + }, + + // TODO reconsider name + go: function( url, data, noEvents ) { + var state, href, parsed, loc, hash, popstateEvent, + isPopStateEvent = $.event.special.navigate.isPushStateEnabled(), + resolutionUrl = path.isPath( url ) ? path.getLocation() : $.mobile.getDocumentUrl(); + + // Get the url as it would look squashed on to the current resolution url + href = path.squash( url ); + + // Grab the hash for recording. If the passed url is a path + // we used the parsed version of the squashed url to reconstruct, + // otherwise we assume it's a hash and store it directly + parsed = path.parseUrl( url ); + loc = path.parseLocation(); + + if( loc.pathname + loc.search === parsed.pathname + parsed.search ) { + // If the pathname and search of the passed url is identical to the current loc + // then we must use the hash. Otherwise there will be no event + // eg, url = "/foo/bar?baz#bang", location.href = "http://example.com/foo/bar?baz" + hash = parsed.hash ? parsed.hash : parsed.pathname + parsed.search; + } else if ( path.isPath(url) ) { + var resolved = path.parseUrl( href ); + // If the passed url is a path, make it domain relative and remove any trailing hash + hash = resolved.pathname + resolved.search + (path.isPreservableHash( resolved.hash )? resolved.hash.replace( "#", "" ) : ""); + } else { + hash = url; + } + + // Here we prevent the next hash change or popstate event from doing any + // history management. In the case of hashchange we don't swallow it + // if there will be no hashchange fired (since that won't reset the value) + // and will swallow the following hashchange + history.ignoreNextHashChange = true; + if( noEvents && hash !== path.stripHash(path.parseLocation().hash) ) { + history.preventNextHashChange = noEvents; + } + + // IMPORTANT in the case where popstate is supported the event will be triggered + // directly, stopping further execution - ie, interupting the flow of this + // method call to fire bindings at this expression. Below the navigate method + // there is a binding to catch this event and stop its propagation. + // + // We then trigger a new popstate event on the window with a null state + // so that the navigate events can conclude their work properly + // + // if the url is a path we want to preserve the query params that are available on + // the current url. + window.location.hash = hash; + + state = $.extend({ + url: href, + hash: hash, + title: document.title + }, data); + + if( isPopStateEvent ) { + popstateEvent = new $.Event( "popstate" ); + popstateEvent.originalEvent = { + type: "popstate", + state: null + }; + + this.squash( url, state ); + + // Trigger a new faux popstate event to replace the one that we + // caught that was triggered by the hash setting above. + if( !noEvents ) { + history.ignoreNextPopState = true; + $( window ).trigger( popstateEvent ); + } + } + + // record the history entry so that the information can be included + // in hashchange event driven navigate events in a similar fashion to + // the state that's provided by popstate + history.add( state.url, state ); + } + }); + + $.History = function( stack, index, initialDestination ) { + this.stack = stack || []; + this.activeIndex = index || 0; + this.initialDst = initialDestination || path.parseLocation().hash.replace( /^#/, "" ); }; - $.extend($.History.prototype, { + $.extend($.History.prototype, { getActive: function() { return this.stack[ this.activeIndex ]; }, @@ -253,7 +253,7 @@ define([ // existing navigation functionalty that is tightly coupled to the history information $.navigate.history = history = new $.History(); - // instantiate an instance of the navigator for use within the $.navigate method + // instantiate an instance of the navigator for use within the $.navigate method $.navigate.navigator = new $.Navigator( history ); // This binding is intended to catch the popstate events that are fired