2 * history.pushState support, layered on top of hashchange
5 ( function( $, window ) {
6 // For now, let's Monkeypatch this onto the end of $.mobile._registerInternalEvents
7 // Scope self to pushStateHandler so we can reference it sanely within the
8 // methods handed off as event handlers
9 var pushStateHandler = {},
10 self = pushStateHandler,
12 url = $.mobile.path.parseUrl( location.href );
14 $.extend( pushStateHandler, {
15 // TODO move to a path helper, this is rather common functionality
16 initialFilePath: (function() {
17 return url.pathname + url.search;
20 initialHref: url.hrefNoHash,
22 // Flag for tracking if a Hashchange naturally occurs after each popstate + replace
23 hashchangeFired: false,
27 hash: location.hash || "#" + self.initialFilePath,
28 title: document.title,
30 // persist across refresh
31 initialHref: self.initialHref
35 resetUIKeys: function( url ) {
36 var dialog = $.mobile.dialogHashKey,
37 subkey = "&" + $.mobile.subPageUrlKey,
38 dialogIndex = url.indexOf( dialog );
40 if( dialogIndex > -1 ) {
41 url = url.slice( 0, dialogIndex ) + "#" + url.slice( dialogIndex );
42 } else if( url.indexOf( subkey ) > -1 ) {
43 url = url.split( subkey ).join( "#" + subkey );
49 // TODO sort out a single barrier to hashchange functionality
50 nextHashChangePrevented: function( value ) {
51 $.mobile.urlHistory.ignoreNextHashChange = value;
52 self.onHashChangeDisabled = value;
55 // on hash change we want to clean up the url
56 // NOTE this takes place *after* the vanilla navigation hash change
57 // handling has taken place and set the state of the DOM
58 onHashChange: function( e ) {
59 // disable this hash change
60 if( self.onHashChangeDisabled ){
66 isPath = $.mobile.path.isPath( hash ),
67 resolutionUrl = isPath ? location.href : $.mobile.getDocumentUrl();
68 hash = isPath ? hash.replace( "#", "" ) : hash;
70 // propulate the hash when its not available
73 // make the hash abolute with the current href
74 href = $.mobile.path.makeUrlAbsolute( hash, resolutionUrl );
77 href = self.resetUIKeys( href );
80 // replace the current url with the new href and store the state
81 // Note that in some cases we might be replacing an url with the
82 // same url. We do this anyways because we need to make sure that
83 // all of our history entries have a state object associated with
84 // them. This allows us to work around the case where window.history.back()
85 // is called to transition from an external page to an embedded page.
86 // In that particular case, a hashchange event is *NOT* generated by the browser.
87 // Ensuring each history entry has a state object means that onPopState()
88 // will always trigger our hashchange callback even when a hashchange event
90 history.replaceState( state, document.title, href );
93 // on popstate (ie back or forward) we need to replace the hash that was there previously
94 // cleaned up by the additional hash handling
95 onPopState: function( e ) {
96 var poppedState = e.originalEvent.state, holdnexthashchange = false;
98 // if there's no state its not a popstate we care about, ie chrome's initial popstate
99 // or forward popstate
101 // disable any hashchange triggered by the browser
102 self.nextHashChangePrevented( true );
104 // defer our manual hashchange until after the browser fired
105 // version has come and gone
106 setTimeout(function() {
107 // make sure that the manual hash handling takes place
108 self.nextHashChangePrevented( false );
110 // change the page based on the hash
111 $.mobile._handleHashChange( poppedState.hash );
117 $win.bind( "hashchange", self.onHashChange );
119 // Handle popstate events the occur through history changes
120 $win.bind( "popstate", self.onPopState );
122 // if there's no hash, we need to replacestate for returning to home
123 if ( location.hash === "" ) {
124 history.replaceState( self.state(), document.title, location.href );
130 if( $.mobile.pushStateEnabled && $.support.pushState ){
131 pushStateHandler.init();