This repository has been archived by the owner on Oct 8, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
/
jquery.mobile.navigation.pushstate.js
144 lines (116 loc) · 5.13 KB
/
jquery.mobile.navigation.pushstate.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
//>>description: history.pushState support, layered on top of hashchange.
//>>label: Pushstate Support
//>>group: Navigation
define( [ "jquery", "./jquery.mobile.navigation", "depend!./jquery.hashchange[jquery]" ], function( $ ) {
//>>excludeEnd("jqmBuildExclude");
(function( $, window ) {
// For now, let's Monkeypatch this onto the end of $.mobile._registerInternalEvents
// Scope self to pushStateHandler so we can reference it sanely within the
// methods handed off as event handlers
var pushStateHandler = {},
self = pushStateHandler,
$win = $( window ),
url = $.mobile.path.parseLocation(),
mobileinitDeferred = $.Deferred(),
domreadyDeferred = $.Deferred();
$( document ).ready( $.proxy( domreadyDeferred, "resolve" ) );
$( document ).one( "mobileinit", $.proxy( mobileinitDeferred, "resolve" ) );
$.extend( pushStateHandler, {
// TODO move to a path helper, this is rather common functionality
initialFilePath: (function() {
return url.pathname + url.search;
})(),
hashChangeTimeout: 200,
hashChangeEnableTimer: undefined,
initialHref: url.hrefNoHash,
state: function() {
return {
// firefox auto decodes the url when using location.hash but not href
hash: $.mobile.path.parseLocation().hash || "#" + self.initialFilePath,
title: document.title,
// persist across refresh
initialHref: self.initialHref
};
},
// TODO sort out a single barrier to hashchange functionality
nextHashChangePrevented: function( value ) {
$.mobile.urlHistory.ignoreNextHashChange = value;
self.onHashChangeDisabled = value;
},
// on hash change we want to clean up the url
// NOTE this takes place *after* the vanilla navigation hash change
// handling has taken place and set the state of the DOM
onHashChange: function( e ) {
// disable this hash change
if ( self.onHashChangeDisabled ) {
return;
}
var href, state,
// firefox auto decodes the url when using location.hash but not href
hash = $.mobile.path.parseLocation().hash,
isPath = $.mobile.path.isPath( hash ),
resolutionUrl = isPath ? $.mobile.path.getLocation() : $.mobile.getDocumentUrl();
hash = isPath ? hash.replace( "#", "" ) : hash;
// propulate the hash when its not available
state = self.state();
// make the hash abolute with the current href
href = $.mobile.path.makeUrlAbsolute( hash, resolutionUrl );
if ( isPath ) {
href = $.mobile.path.resetUIKeys( href );
}
// 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.
history.replaceState( state, document.title, href );
},
// on popstate (ie back or forward) we need to replace the hash that was there previously
// cleaned up by the additional hash handling
onPopState: function( e ) {
var poppedState = e.originalEvent.state,
fromHash, toHash, hashChanged;
// if there's no state its not a popstate we care about, eg chrome's initial popstate
if ( poppedState ) {
// if we get two pop states in under this.hashChangeTimeout
// make sure to clear any timer set for the previous change
clearTimeout( self.hashChangeEnableTimer );
// make sure to enable hash handling for the the _handleHashChange call
self.nextHashChangePrevented( false );
// change the page based on the hash in the popped state
$.mobile._handleHashChange( poppedState.hash );
// prevent any hashchange in the next self.hashChangeTimeout
self.nextHashChangePrevented( true );
// re-enable hash change handling after swallowing a possible hash
// change event that comes on all popstates courtesy of browsers like Android
self.hashChangeEnableTimer = setTimeout( function() {
self.nextHashChangePrevented( false );
}, self.hashChangeTimeout );
}
},
init: function() {
$win.bind( "hashchange", self.onHashChange );
// Handle popstate events the occur through history changes
$win.bind( "popstate", self.onPopState );
// if there's no hash, we need to replacestate for returning to home
if ( location.hash === "" ) {
history.replaceState( self.state(), document.title, $.mobile.path.getLocation() );
}
}
});
// We need to init when "mobileinit", "domready", and "navready" have all happened
$.when( domreadyDeferred, mobileinitDeferred, $.mobile.navreadyDeferred ).done(function() {
if ( $.mobile.pushStateEnabled && $.support.pushState ) {
pushStateHandler.init();
}
});
})( jQuery, this );
//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
});
//>>excludeEnd("jqmBuildExclude");