1 //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
2 //>>description: history.pushState support, layered on top of hashchange.
3 //>>label: Pushstate Support
6 define( [ "jquery", "./jquery.mobile.navigation", "../external/requirejs/depend!./jquery.mobile.hashchange[jquery]" ], function( $ ) {
7 //>>excludeEnd("jqmBuildExclude");
8 ( function( $, window ) {
9 // For now, let's Monkeypatch this onto the end of $.mobile._registerInternalEvents
10 // Scope self to pushStateHandler so we can reference it sanely within the
11 // methods handed off as event handlers
12 var pushStateHandler = {},
13 self = pushStateHandler,
15 url = $.mobile.path.parseUrl( location.href );
17 $.extend( pushStateHandler, {
18 // TODO move to a path helper, this is rather common functionality
19 initialFilePath: (function() {
20 return url.pathname + url.search;
23 initialHref: url.hrefNoHash,
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 hashValueAfterReset: function( url ) {
50 var resetUrl = self.resetUIKeys( url );
51 return $.mobile.path.parseUrl( resetUrl ).hash;
54 // TODO sort out a single barrier to hashchange functionality
55 nextHashChangePrevented: function( value ) {
56 $.mobile.urlHistory.ignoreNextHashChange = value;
57 self.onHashChangeDisabled = value;
60 // on hash change we want to clean up the url
61 // NOTE this takes place *after* the vanilla navigation hash change
62 // handling has taken place and set the state of the DOM
63 onHashChange: function( e ) {
64 // disable this hash change
65 if( self.onHashChangeDisabled ){
71 isPath = $.mobile.path.isPath( hash ),
72 resolutionUrl = isPath ? location.href : $.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 window.history.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 timeout, fromHash, toHash, hashChanged;
106 // if there's no state its not a popstate we care about, eg chrome's initial popstate
108 // the active url in the history stack will still be from the previous state
109 // so we can use it to verify if a hashchange will be fired from the popstate
110 fromHash = self.hashValueAfterReset( $.mobile.urlHistory.getActive().url );
112 // the hash stored in the state popped off the stack will be our currenturl or
113 // the url to which we wish to navigate
114 toHash = self.hashValueAfterReset( poppedState.hash.replace("#", "") );
116 // if the hashes of the urls are different we must assume that the browser
117 // will fire a hashchange
118 hashChanged = fromHash !== toHash;
120 // unlock hash handling once the hashchange caused be the popstate has fired
122 $win.one( "hashchange.pushstate", function() {
123 self.nextHashChangePrevented( false );
127 // enable hash handling for the the _handleHashChange call
128 self.nextHashChangePrevented( false );
130 // change the page based on the hash
131 $.mobile._handleHashChange( poppedState.hash );
133 // only prevent another hash change handling if a hash change will be fired
136 // disable hash handling until one of the above timers fires
137 self.nextHashChangePrevented( true );
143 $win.bind( "hashchange", self.onHashChange );
145 // Handle popstate events the occur through history changes
146 $win.bind( "popstate", self.onPopState );
148 // if there's no hash, we need to replacestate for returning to home
149 if ( location.hash === "" ) {
150 history.replaceState( self.state(), document.title, location.href );
156 if( $.mobile.pushStateEnabled && $.support.pushState ){
157 pushStateHandler.init();
161 //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
163 //>>excludeEnd("jqmBuildExclude");