Skip to content
This repository was archived by the owner on Oct 8, 2021. It is now read-only.

Commit f0ce8c7

Browse files
committed
split out path for future direct reference
1 parent cd1053d commit f0ce8c7

File tree

1 file changed

+307
-0
lines changed

1 file changed

+307
-0
lines changed

js/navigation/path.js

Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
2+
//>>description: Path parsing and manipulation helpers
3+
//>>label: AJAX Navigation System
4+
//>>group: Navigation
5+
define([
6+
"jquery",
7+
"./jquery.mobile.core" ], function( $ ) {
8+
//>>excludeEnd("jqmBuildExclude");
9+
10+
var path, documentBase, $base, dialogHashKey = "&ui-state=dialog";
11+
12+
$.mobile.path = path = {
13+
// This scary looking regular expression parses an absolute URL or its relative
14+
// variants (protocol, site, document, query, and hash), into the various
15+
// components (protocol, host, path, query, fragment, etc that make up the
16+
// URL as well as some other commonly used sub-parts. When used with RegExp.exec()
17+
// or String.match, it parses the URL into a results array that looks like this:
18+
//
19+
// [0]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread#msg-content
20+
// [1]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread
21+
// [2]: http://jblas:password@mycompany.com:8080/mail/inbox
22+
// [3]: http://jblas:password@mycompany.com:8080
23+
// [4]: http:
24+
// [5]: //
25+
// [6]: jblas:password@mycompany.com:8080
26+
// [7]: jblas:password
27+
// [8]: jblas
28+
// [9]: password
29+
// [10]: mycompany.com:8080
30+
// [11]: mycompany.com
31+
// [12]: 8080
32+
// [13]: /mail/inbox
33+
// [14]: /mail/
34+
// [15]: inbox
35+
// [16]: ?msg=1234&type=unread
36+
// [17]: #msg-content
37+
//
38+
urlParseRE: /^(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/,
39+
40+
// Abstraction to address xss (Issue #4787) by removing the authority in
41+
// browsers that auto decode it. All references to location.href should be
42+
// replaced with a call to this method so that it can be dealt with properly here
43+
getLocation: function( url ) {
44+
var uri = url ? this.parseUrl( url ) : location,
45+
hash = this.parseUrl( url || location.href ).hash;
46+
47+
// mimic the browser with an empty string when the hash is empty
48+
hash = hash === "#" ? "" : hash;
49+
50+
// Make sure to parse the url or the location object for the hash because using location.hash
51+
// is autodecoded in firefox, the rest of the url should be from the object (location unless
52+
// we're testing) to avoid the inclusion of the authority
53+
return uri.protocol + "//" + uri.host + uri.pathname + uri.search + hash;
54+
},
55+
56+
parseLocation: function() {
57+
return this.parseUrl( this.getLocation() );
58+
},
59+
60+
//Parse a URL into a structure that allows easy access to
61+
//all of the URL components by name.
62+
parseUrl: function( url ) {
63+
// If we're passed an object, we'll assume that it is
64+
// a parsed url object and just return it back to the caller.
65+
if ( $.type( url ) === "object" ) {
66+
return url;
67+
}
68+
69+
var matches = path.urlParseRE.exec( url || "" ) || [];
70+
71+
// Create an object that allows the caller to access the sub-matches
72+
// by name. Note that IE returns an empty string instead of undefined,
73+
// like all other browsers do, so we normalize everything so its consistent
74+
// no matter what browser we're running on.
75+
return {
76+
href: matches[ 0 ] || "",
77+
hrefNoHash: matches[ 1 ] || "",
78+
hrefNoSearch: matches[ 2 ] || "",
79+
domain: matches[ 3 ] || "",
80+
protocol: matches[ 4 ] || "",
81+
doubleSlash: matches[ 5 ] || "",
82+
authority: matches[ 6 ] || "",
83+
username: matches[ 8 ] || "",
84+
password: matches[ 9 ] || "",
85+
host: matches[ 10 ] || "",
86+
hostname: matches[ 11 ] || "",
87+
port: matches[ 12 ] || "",
88+
pathname: matches[ 13 ] || "",
89+
directory: matches[ 14 ] || "",
90+
filename: matches[ 15 ] || "",
91+
search: matches[ 16 ] || "",
92+
hash: matches[ 17 ] || ""
93+
};
94+
},
95+
96+
//Turn relPath into an asbolute path. absPath is
97+
//an optional absolute path which describes what
98+
//relPath is relative to.
99+
makePathAbsolute: function( relPath, absPath ) {
100+
if ( relPath && relPath.charAt( 0 ) === "/" ) {
101+
return relPath;
102+
}
103+
104+
relPath = relPath || "";
105+
absPath = absPath ? absPath.replace( /^\/|(\/[^\/]*|[^\/]+)$/g, "" ) : "";
106+
107+
var absStack = absPath ? absPath.split( "/" ) : [],
108+
relStack = relPath.split( "/" );
109+
for ( var i = 0; i < relStack.length; i++ ) {
110+
var d = relStack[ i ];
111+
switch ( d ) {
112+
case ".":
113+
break;
114+
case "..":
115+
if ( absStack.length ) {
116+
absStack.pop();
117+
}
118+
break;
119+
default:
120+
absStack.push( d );
121+
break;
122+
}
123+
}
124+
return "/" + absStack.join( "/" );
125+
},
126+
127+
//Returns true if both urls have the same domain.
128+
isSameDomain: function( absUrl1, absUrl2 ) {
129+
return path.parseUrl( absUrl1 ).domain === path.parseUrl( absUrl2 ).domain;
130+
},
131+
132+
//Returns true for any relative variant.
133+
isRelativeUrl: function( url ) {
134+
// All relative Url variants have one thing in common, no protocol.
135+
return path.parseUrl( url ).protocol === "";
136+
},
137+
138+
//Returns true for an absolute url.
139+
isAbsoluteUrl: function( url ) {
140+
return path.parseUrl( url ).protocol !== "";
141+
},
142+
143+
//Turn the specified realtive URL into an absolute one. This function
144+
//can handle all relative variants (protocol, site, document, query, fragment).
145+
makeUrlAbsolute: function( relUrl, absUrl ) {
146+
if ( !path.isRelativeUrl( relUrl ) ) {
147+
return relUrl;
148+
}
149+
150+
if ( absUrl === undefined ) {
151+
absUrl = path.documentBase;
152+
}
153+
154+
var relObj = path.parseUrl( relUrl ),
155+
absObj = path.parseUrl( absUrl ),
156+
protocol = relObj.protocol || absObj.protocol,
157+
doubleSlash = relObj.protocol ? relObj.doubleSlash : ( relObj.doubleSlash || absObj.doubleSlash ),
158+
authority = relObj.authority || absObj.authority,
159+
hasPath = relObj.pathname !== "",
160+
pathname = path.makePathAbsolute( relObj.pathname || absObj.filename, absObj.pathname ),
161+
search = relObj.search || ( !hasPath && absObj.search ) || "",
162+
hash = relObj.hash;
163+
164+
return protocol + doubleSlash + authority + pathname + search + hash;
165+
},
166+
167+
//Add search (aka query) params to the specified url.
168+
addSearchParams: function( url, params ) {
169+
var u = path.parseUrl( url ),
170+
p = ( typeof params === "object" ) ? $.param( params ) : params,
171+
s = u.search || "?";
172+
return u.hrefNoSearch + s + ( s.charAt( s.length - 1 ) !== "?" ? "&" : "" ) + p + ( u.hash || "" );
173+
},
174+
175+
convertUrlToDataUrl: function( absUrl ) {
176+
var u = path.parseUrl( absUrl );
177+
if ( path.isEmbeddedPage( u ) ) {
178+
// For embedded pages, remove the dialog hash key as in getFilePath(),
179+
// and remove otherwise the Data Url won't match the id of the embedded Page.
180+
return u.hash
181+
.split( dialogHashKey )[0]
182+
.replace( /^#/, "" )
183+
.replace( /\?.*$/, "" );
184+
} else if ( path.isSameDomain( u, path.documentBase ) ) {
185+
return u.hrefNoHash.replace( path.documentBase.domain, "" ).split( dialogHashKey )[0];
186+
}
187+
188+
return window.decodeURIComponent(absUrl);
189+
},
190+
191+
//get path from current hash, or from a file path
192+
get: function( newPath ) {
193+
if ( newPath === undefined ) {
194+
newPath = path.parseLocation().hash;
195+
}
196+
return path.stripHash( newPath ).replace( /[^\/]*\.[^\/*]+$/, '' );
197+
},
198+
199+
//return the substring of a filepath before the sub-page key, for making a server request
200+
getFilePath: function( path ) {
201+
var splitkey = '&' + $.mobile.subPageUrlKey;
202+
return path && path.split( splitkey )[0].split( dialogHashKey )[0];
203+
},
204+
205+
//set location hash to path
206+
set: function( path ) {
207+
location.hash = path;
208+
},
209+
210+
//test if a given url (string) is a path
211+
//NOTE might be exceptionally naive
212+
isPath: function( url ) {
213+
return ( /\// ).test( url );
214+
},
215+
216+
//return a url path with the window's location protocol/hostname/pathname removed
217+
clean: function( url ) {
218+
return url.replace( path.documentBase.domain, "" );
219+
},
220+
221+
//just return the url without an initial #
222+
stripHash: function( url ) {
223+
return url.replace( /^#/, "" );
224+
},
225+
226+
// TODO leave the dialog hashkey cleaning in nav core
227+
//remove the preceding hash, any query params, and dialog notations
228+
cleanHash: function( hash ) {
229+
return path.stripHash( hash.replace( /\?.*$/, "" ).replace( dialogHashKey, "" ) );
230+
},
231+
232+
isHashValid: function( hash ) {
233+
return ( /^#[^#]+$/ ).test( hash );
234+
},
235+
236+
//check whether a url is referencing the same domain, or an external domain or different protocol
237+
//could be mailto, etc
238+
isExternal: function( url ) {
239+
var u = path.parseUrl( url );
240+
return u.protocol && u.domain !== path.documentUrl.domain ? true : false;
241+
},
242+
243+
hasProtocol: function( url ) {
244+
return ( /^(:?\w+:)/ ).test( url );
245+
},
246+
247+
//check if the specified url refers to the first page in the main application document.
248+
isFirstPageUrl: function( url ) {
249+
// We only deal with absolute paths.
250+
var u = path.parseUrl( path.makeUrlAbsolute( url, path.documentBase ) ),
251+
252+
// Does the url have the same path as the document?
253+
samePath = u.hrefNoHash === path.documentUrl.hrefNoHash || ( path.documentBaseDiffers && u.hrefNoHash === documentBase.hrefNoHash ),
254+
255+
// Get the first page element.
256+
fp = $.mobile.firstPage,
257+
258+
// Get the id of the first page element if it has one.
259+
fpId = fp && fp[0] ? fp[0].id : undefined;
260+
261+
// The url refers to the first page if the path matches the document and
262+
// it either has no hash value, or the hash is exactly equal to the id of the
263+
// first page element.
264+
return samePath && ( !u.hash || u.hash === "#" || ( fpId && u.hash.replace( /^#/, "" ) === fpId ) );
265+
},
266+
267+
isEmbeddedPage: function( url ) {
268+
var u = path.parseUrl( url );
269+
270+
//if the path is absolute, then we need to compare the url against
271+
//both the documentUrl and the documentBase. The main reason for this
272+
//is that links embedded within external documents will refer to the
273+
//application document, whereas links embedded within the application
274+
//document will be resolved against the document base.
275+
if ( u.protocol !== "" ) {
276+
return ( u.hash && ( u.hrefNoHash === path.documentUrl.hrefNoHash || ( path.documentBaseDiffers && u.hrefNoHash === path.documentBase.hrefNoHash ) ) );
277+
}
278+
return ( /^#/ ).test( u.href );
279+
},
280+
281+
282+
// Some embedded browsers, like the web view in Phone Gap, allow cross-domain XHR
283+
// requests if the document doing the request was loaded via the file:// protocol.
284+
// This is usually to allow the application to "phone home" and fetch app specific
285+
// data. We normally let the browser handle external/cross-domain urls, but if the
286+
// allowCrossDomainPages option is true, we will allow cross-domain http/https
287+
// requests to go through our page loading logic.
288+
isPermittedCrossDomainRequest: function( docUrl, reqUrl ) {
289+
return $.mobile.allowCrossDomainPages &&
290+
docUrl.protocol === "file:" &&
291+
reqUrl.search( /^https?:/ ) !== -1;
292+
}
293+
};
294+
295+
path.documentUrl = path.parseLocation();
296+
297+
$base = $( "head" ).find( "base" );
298+
299+
path.documentBase = $base.length ?
300+
path.parseUrl( path.makeUrlAbsolute( $base.attr( "href" ), path.documentUrl.href ) ) :
301+
path.documentUrl;
302+
303+
path.documentBaseDiffers = (path.documentUrl.hrefNoHash !== path.documentBase.hrefNoHash);
304+
305+
//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
306+
});
307+
//>>excludeEnd("jqmBuildExclude");

0 commit comments

Comments
 (0)