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 ) ;
0 commit comments