Skip to content

Commit c94c310

Browse files
committed
Initial checkin of files.
0 parents  commit c94c310

File tree

3 files changed

+412
-0
lines changed

3 files changed

+412
-0
lines changed

path-utils.js

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
// path-utils.js - version 0.1
2+
// Copyright (c) 2011, Kin Blas
3+
// All rights reserved.
4+
//
5+
// Redistribution and use in source and binary forms, with or without
6+
// modification, are permitted provided that the following conditions are met:
7+
// * Redistributions of source code must retain the above copyright
8+
// notice, this list of conditions and the following disclaimer.
9+
// * Redistributions in binary form must reproduce the above copyright
10+
// notice, this list of conditions and the following disclaimer in the
11+
// documentation and/or other materials provided with the distribution.
12+
// * Neither the name of the <organization> nor the
13+
// names of its contributors may be used to endorse or promote products
14+
// derived from this software without specific prior written permission.
15+
//
16+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17+
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18+
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+
// DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
20+
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23+
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
27+
(function( window ){
28+
29+
var pathUtils = {
30+
// This scary looking regular expression parses an absolute URL or its relative
31+
// variants (protocol, site, document, query, and hash), into the various
32+
// components (protocol, host, path, query, fragment, etc that make up the
33+
// URL as well as some other commonly used sub-parts. When used with RegExp.exec()
34+
// or String.match, it parses the URL into a results array that looks like this:
35+
//
36+
// [0]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread#msg-content
37+
// [1]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread
38+
// [2]: http://jblas:password@mycompany.com:8080/mail/inbox
39+
// [3]: http://jblas:password@mycompany.com:8080
40+
// [4]: http:
41+
// [5]: //
42+
// [6]: jblas:password@mycompany.com:8080
43+
// [7]: jblas:password
44+
// [8]: jblas
45+
// [9]: password
46+
// [10]: mycompany.com:8080
47+
// [11]: mycompany.com
48+
// [12]: 8080
49+
// [13]: /mail/inbox
50+
// [14]: /mail/
51+
// [15]: inbox
52+
// [16]: ?msg=1234&type=unread
53+
// [17]: #msg-content
54+
//
55+
urlParseRE: /^(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/,
56+
57+
// These are the property names we set on the parsed url object. The order of the names
58+
// in this array must match the order of the sub-matches in urlParseRE!
59+
parsedUrlPropNames: [
60+
"href",
61+
"hrefNoHash",
62+
"hrefNoSearch",
63+
"domain",
64+
"protocol",
65+
"doubleSlash",
66+
"authority",
67+
"userinfo",
68+
"username",
69+
"password",
70+
"host",
71+
"hostname",
72+
"port",
73+
"pathname",
74+
"directory",
75+
"filename",
76+
"search",
77+
"hash"
78+
],
79+
80+
defaultPorts: { http: "80", https: "443", ftp: "20", ftps: "990" },
81+
82+
parseUrl: function( url ) {
83+
84+
// If we're passed an object, we'll assume that it is
85+
// a parsed url object and just return it back to the caller.
86+
if ( url && typeof url === "object" ) {
87+
return url;
88+
}
89+
90+
var matches = pathUtils.urlParseRE.exec( url || "" ) || [],
91+
props = pathUtils.parsedUrlPropNames,
92+
cnt = props.length,
93+
result = {},
94+
i;
95+
96+
for ( i = 0; i < cnt; i++ ) {
97+
// Most browsers returns an empty string for empty sub-matches, but
98+
// IE returns undefined, so we need to make sure we normalize empty
99+
// sub-matches so results are consistent across all browsers.
100+
101+
result[ props[ i ] ] = matches[ i ] || "";
102+
}
103+
104+
return result;
105+
},
106+
107+
port: function( url ) {
108+
var u = pathUtils.parseUrl( url );
109+
return u.port || pathUtils.defaultPorts[u.protocol];
110+
},
111+
112+
isSameDomain: function( absUrl1, absUrl2 ) {
113+
return pathUtils.parseUrl( absUrl1 ).domain === pathUtils.parseUrl( absUrl2 ).domain;
114+
},
115+
116+
//Returns true for any relative variant.
117+
isRelativeUrl: function( url ) {
118+
// All relative Url variants have one thing in common, no protocol.
119+
return pathUtils.parseUrl( url ).protocol === "";
120+
},
121+
122+
//Returns true for an absolute url.
123+
isAbsoluteUrl: function( url ) {
124+
return pathUtils.parseUrl( url ).protocol !== "";
125+
},
126+
127+
// Turn relPath into an asbolute path. absPath is
128+
// an optional absolute path which describes what
129+
// relPath is relative to.
130+
131+
makePathAbsolute: function( relPath, absPath ) {
132+
if ( relPath && relPath.charAt( 0 ) === "/" ) {
133+
return relPath;
134+
}
135+
136+
relPath = relPath || "";
137+
absPath = absPath ? absPath.replace( /^\/|(\/[^\/]*|[^\/]+)$/g, "" ) : "";
138+
139+
var absStack = absPath ? absPath.split( "/" ) : [],
140+
relStack = relPath.split( "/" );
141+
for ( var i = 0; i < relStack.length; i++ ) {
142+
var d = relStack[ i ];
143+
switch ( d ) {
144+
case ".":
145+
break;
146+
case "..":
147+
if ( absStack.length ) {
148+
absStack.pop();
149+
}
150+
break;
151+
default:
152+
absStack.push( d );
153+
break;
154+
}
155+
}
156+
return "/" + absStack.join( "/" );
157+
},
158+
159+
// Turn absolute pathA into a path that is
160+
// relative to absolute pathB.
161+
162+
makePathRelative: function( pathA, pathB ) {
163+
// Remove any file reference in the path.
164+
pathB = pathB ? pathB.replace( /^\/|\/?[^\/]*$/g, "" ) : "";
165+
pathA = pathA ? pathA.replace( /^\//, "" ) : "";
166+
167+
var stackB = pathB ? pathB.split( "/" ) : [],
168+
stackA = pathA.split( "/" ),
169+
stackC = [],
170+
len = stackB.length,
171+
upLevel = false,
172+
startIndex = 0;
173+
174+
for ( var i = 0; i < len; i++ ) {
175+
upLevel = upLevel || stackA[ 0 ] !== stackB[ i ];
176+
if ( upLevel ) {
177+
stackC.push( ".." );
178+
} else {
179+
stackA.shift();
180+
}
181+
}
182+
return stackC.concat( stackA ).join( "/" );
183+
},
184+
185+
// Turn any relative URL variant into an absolute URL.
186+
187+
makeUrlAbsolute: function( relUrl, absUrl ) {
188+
if ( !pathUtils.isRelativeUrl( relUrl ) ) {
189+
return relUrl;
190+
}
191+
192+
var relObj = pathUtils.parseUrl( relUrl ),
193+
absObj = pathUtils.parseUrl( absUrl ),
194+
protocol = relObj.protocol || absObj.protocol,
195+
doubleSlash = relObj.protocol ? relObj.doubleSlash : ( relObj.doubleSlash || absObj.doubleSlash ),
196+
authority = relObj.authority || absObj.authority,
197+
hasPath = relObj.pathname !== "",
198+
pathname = pathUtils.makePathAbsolute( relObj.pathname || absObj.filename, absObj.pathname ),
199+
search = relObj.search || ( !hasPath && absObj.search ) || "",
200+
hash = relObj.hash;
201+
202+
return protocol + doubleSlash + authority + pathname + search + hash;
203+
}
204+
};
205+
206+
// For every parsedUrlPropName, make sure there is a getter function defined on the pathUtils object.
207+
208+
function getterFunc( propName )
209+
{
210+
return function( url ){
211+
return pathUtils.parseUrl( url )[ propName ];
212+
}
213+
}
214+
215+
var i, prop, props = pathUtils.parsedUrlPropNames, cnt = props.length;
216+
for ( i = 0; i < cnt; i++ ) {
217+
prop = props[ i ];
218+
if ( !pathUtils[ prop ] ) {
219+
pathUtils[ prop ] = getterFunc( prop );
220+
}
221+
}
222+
223+
// Expose pathUtils to the world.
224+
window.pathUtils = pathUtils;
225+
226+
})( window );

tests/parse.html

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<!DOCTYPE HTML>
2+
<html>
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>Parse Tests</title>
6+
<script src="../path-utils.js"></script>
7+
</head>
8+
9+
<body>
10+
11+
<pre>
12+
<script>
13+
14+
function entityEncode( str )
15+
{
16+
return ( str || "" ).replace(/&/, "&amp;").replace(/</, "&lt;").replace(/>/, "&gt;");
17+
}
18+
19+
function getMaxLength( arr )
20+
{
21+
var i, max = 0;
22+
for ( i = 0; i < arr.length; i++ ) {
23+
max = Math.max( max, ( arr[ i ] || "" ).length );
24+
}
25+
return max;
26+
}
27+
28+
function getSpaces( num )
29+
{
30+
var i, result = "";
31+
num = num < 1 ? 0 : num;
32+
for ( i = 0; i < num; i++) {
33+
result += " ";
34+
}
35+
return result;
36+
}
37+
38+
var testUrls = [
39+
"http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread#msg-content",
40+
"//jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread#msg-content",
41+
"//mycompany.com:8080/mail/inbox?msg=1234&type=unread#msg-content",
42+
"/mail/inbox?msg=1234&type=unread#msg-content",
43+
"mail/inbox?msg=1234&type=unread#msg-content",
44+
"inbox?msg=1234&type=unread#msg-content",
45+
"?msg=1234&type=unread#msg-content",
46+
"#msg-content",
47+
"http://mycompany.com/mail/inbox?msg=1234&type=unread#msg-content",
48+
"//mycompany.com/mail/inbox?msg=1234&type=unread#msg-content",
49+
"/mail/inbox?msg=1234&type=unread#msg-content",
50+
"mail/inbox?msg=1234&type=unread#msg-content",
51+
"inbox?msg=1234&type=unread#msg-content",
52+
"http://jblas:password@123.456.78.9:8080/mail/inbox?msg=1234&type=unread#msg-content",
53+
"//jblas:password@123.456.78.9:8080/mail/inbox?msg=1234&type=unread#msg-content",
54+
"//123.456.78.9:8080/mail/inbox?msg=1234&type=unread#msg-content",
55+
],
56+
props = pathUtils.parsedUrlPropNames,
57+
maxLen = getMaxLength( props ),
58+
obj;
59+
60+
for ( var i = 0; i < testUrls.length; i++ ) {
61+
document.write("<strong>Url:</strong> " + entityEncode( testUrls[ i ] ) + "\n\n");
62+
obj = pathUtils.parseUrl( testUrls[ i ] );
63+
for ( var j = 0; j < props.length; j++ ) {
64+
document.write("\t<strong>" + getSpaces( maxLen - props[ j ].length ) + props[ j ] + ":</strong> " + entityEncode( obj[ props[ j ] ] ) + "\n");
65+
}
66+
document.write("\n\n");
67+
}
68+
69+
</script>
70+
</pre>
71+
</body>
72+
</html>

0 commit comments

Comments
 (0)