1 (function( $, window ) {
2 // For now, let's Monkeypatch this onto the end of $.mobile._registerInternalEvents
3 // Scope self to pushStateHandler so we can reference it sanely within the
4 // methods handed off as event handlers
5 var pushStateHandler = {},
6 self = pushStateHandler,
7 $win = $.mobile.$window,
8 url = $.mobile.path.parseLocation(),
9 mobileinitDeferred = $.Deferred(),
10 domreadyDeferred = $.Deferred();
12 $.mobile.$document.ready( $.proxy( domreadyDeferred, "resolve" ) );
14 $.mobile.$document.one( "mobileinit", $.proxy( mobileinitDeferred, "resolve" ) );
16 $.extend( pushStateHandler, {
17 // TODO move to a path helper, this is rather common functionality
18 initialFilePath: (function() {
19 return url.pathname + url.search;
22 hashChangeTimeout: 200,
24 hashChangeEnableTimer: undefined,
26 initialHref: url.hrefNoHash,
30 // firefox auto decodes the url when using location.hash but not href
31 hash: $.mobile.path.parseLocation().hash || "#" + self.initialFilePath,
32 title: document.title,
34 // persist across refresh
35 initialHref: self.initialHref
39 resetUIKeys: function( url ) {
40 var dialog = $.mobile.dialogHashKey,
41 subkey = "&" + $.mobile.subPageUrlKey,
42 dialogIndex = url.indexOf( dialog );
44 if ( dialogIndex > -1 ) {
45 url = url.slice( 0, dialogIndex ) + "#" + url.slice( dialogIndex );
46 } else if ( url.indexOf( subkey ) > -1 ) {
47 url = url.split( subkey ).join( "#" + subkey );
53 // TODO sort out a single barrier to hashchange functionality
54 nextHashChangePrevented: function( value ) {
55 $.mobile.urlHistory.ignoreNextHashChange = value;
56 self.onHashChangeDisabled = value;
59 // on hash change we want to clean up the url
60 // NOTE this takes place *after* the vanilla navigation hash change
61 // handling has taken place and set the state of the DOM
62 onHashChange: function( e ) {
63 // disable this hash change
64 if ( self.onHashChangeDisabled ) {
69 // firefox auto decodes the url when using location.hash but not href
70 hash = $.mobile.path.parseLocation().hash,
71 isPath = $.mobile.path.isPath( hash ),
72 resolutionUrl = isPath ? $.mobile.path.getLocation() : $.mobile.getDocumentUrl();
74 hash = isPath ? hash.replace( "#", "" ) : hash;
77 // propulate the hash when its not available
80 // make the hash abolute with the current href
81 href = $.mobile.path.makeUrlAbsolute( hash, resolutionUrl );
84 href = self.resetUIKeys( href );
87 // replace the current url with the new href and store the state
88 // Note that in some cases we might be replacing an url with the
89 // same url. We do this anyways because we need to make sure that
90 // all of our history entries have a state object associated with
91 // them. This allows us to work around the case where $.mobile.back()
92 // is called to transition from an external page to an embedded page.
93 // In that particular case, a hashchange event is *NOT* generated by the browser.
94 // Ensuring each history entry has a state object means that onPopState()
95 // will always trigger our hashchange callback even when a hashchange event
97 history.replaceState( state, document.title, href );
100 // on popstate (ie back or forward) we need to replace the hash that was there previously
101 // cleaned up by the additional hash handling
102 onPopState: function( e ) {
103 var poppedState = e.originalEvent.state,
104 fromHash, toHash, hashChanged;
106 // if there's no state its not a popstate we care about, eg chrome's initial popstate
108 // if we get two pop states in under this.hashChangeTimeout
109 // make sure to clear any timer set for the previous change
110 clearTimeout( self.hashChangeEnableTimer );
112 // make sure to enable hash handling for the the _handleHashChange call
113 self.nextHashChangePrevented( false );
115 // change the page based on the hash in the popped state
116 $.mobile._handleHashChange( poppedState.hash );
118 // prevent any hashchange in the next self.hashChangeTimeout
119 self.nextHashChangePrevented( true );
121 // re-enable hash change handling after swallowing a possible hash
122 // change event that comes on all popstates courtesy of browsers like Android
123 self.hashChangeEnableTimer = setTimeout( function() {
124 self.nextHashChangePrevented( false );
125 }, self.hashChangeTimeout );
130 $win.bind( "hashchange", self.onHashChange );
132 // Handle popstate events the occur through history changes
133 $win.bind( "popstate", self.onPopState );
135 // if there's no hash, we need to replacestate for returning to home
136 if ( location.hash === "" ) {
137 history.replaceState( self.state(), document.title, $.mobile.path.getLocation() );
142 // We need to init when "mobileinit", "domready", and "navready" have all happened
143 $.when( domreadyDeferred, mobileinitDeferred, $.mobile.navreadyDeferred ).done(function() {
144 if ( $.mobile.pushStateEnabled && $.support.pushState ) {
145 pushStateHandler.init();