|
1 | 1 | 'use strict';
|
| 2 | + |
| 3 | +// ABNF info for non-encoded characters of path entries, query and fragment |
| 4 | +// https://tools.ietf.org/html/rfc3986#section-6 |
| 5 | +var sub_delims = '!$&\'()*+,;='; |
| 6 | +var alpha = 'abcdefghijklmnopqrstuvwxyz'; |
| 7 | +var digit = '0123456789' |
| 8 | +var unreserved = alpha + digit + '-._~'; |
| 9 | +var pchar = unreserved + sub_delims + ':' + '@'; //pct-encoded excluded |
| 10 | +var query = (pchar + '/' + '?').replace(/[&=]/g, ''); //&= excluded |
| 11 | +var fragment = pchar + '/' + '?'; |
| 12 | + |
| 13 | +// Map of the encoded version of all characters not requiring encoding |
| 14 | +var PATH_NON_ENCODED = charsToEncodedMap(pchar); |
| 15 | +var QUERY_NON_ENCODED = charsToEncodedMap(query); |
| 16 | +var FRAGMENT_NON_ENCODED = charsToEncodedMap(fragment); |
| 17 | + |
| 18 | +// Util for generating a map of %XX (in upper case) to the represented character |
| 19 | +function charsToEncodedMap(chars) { |
| 20 | + return chars.split('').reduce(function(o, c) { |
| 21 | + o[ '%' + c.charCodeAt(0).toString(16).toUpperCase() ] = c; |
| 22 | + return o; |
| 23 | + }, {}); |
| 24 | +} |
| 25 | + |
| 26 | +function decodeUnnecesary(s, nonEncoded) { |
| 27 | + return s.replace(/%[0-9][0-9a-f]/gi, function(c) { |
| 28 | + // Uppercase and lowercase hexadecimal digits are equivelent, but RFC3986 specifies |
| 29 | + // "For consistency, URI producers and normalizers should use uppercase hexadecimal |
| 30 | + // digits for all percent-encodings" |
| 31 | + c = uppercase(c); |
| 32 | + |
| 33 | + return nonEncoded[c] || c; |
| 34 | + }); |
| 35 | +} |
| 36 | + |
| 37 | +function normalizeUriPathSegment(pct_encoded) { |
| 38 | + return decodeUnnecesary(pct_encoded, PATH_NON_ENCODED); |
| 39 | +} |
| 40 | +function normalizeUriPath(path) { |
| 41 | + return path.split('/').map(normalizeUriPathSegment).join('/'); |
| 42 | +} |
| 43 | +function normalizeUriQuery(query) { |
| 44 | + return decodeUnnecesary(query, QUERY_NON_ENCODED); |
| 45 | +} |
| 46 | +function normalizeUriFragment(fragment) { |
| 47 | + return decodeUnnecesary(fragment, FRAGMENT_NON_ENCODED); |
| 48 | +} |
| 49 | + |
| 50 | + |
2 | 51 | // NOTE: The usage of window and document instead of $window and $document here is
|
3 | 52 | // deliberate. This service depends on the specific behavior of anchor nodes created by the
|
4 | 53 | // browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
|
|
8 | 57 | // service.
|
9 | 58 | var urlParsingNode = window.document.createElement('a');
|
10 | 59 | var originUrl = urlResolve(window.location.href);
|
11 |
| -var baseUrlParsingNode; |
12 | 60 |
|
13 | 61 |
|
14 | 62 | /**
|
@@ -72,12 +120,21 @@ function urlResolve(url) {
|
72 | 120 |
|
73 | 121 | urlParsingNode.setAttribute('href', href);
|
74 | 122 |
|
| 123 | + // Support: everything |
| 124 | + // |
| 125 | + // No browser normalizes all of the optionally encoded characters consistently. |
| 126 | + // Various browsers normalize a subsets of the unreserved characters within the |
| 127 | + // path, search and hash portions of the URL. |
| 128 | + urlParsingNode.pathname = normalizeUriPath(urlParsingNode.pathname); |
| 129 | + urlParsingNode.search = normalizeUriQuery(urlParsingNode.search.replace(/^\?/, '')); |
| 130 | + urlParsingNode.hash = normalizeUriFragment(urlParsingNode.hash.replace(/^\#/, '')); |
| 131 | + |
75 | 132 | return {
|
76 | 133 | href: urlParsingNode.href,
|
77 | 134 | protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
|
78 | 135 | host: urlParsingNode.host,
|
79 |
| - search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '', |
80 |
| - hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '', |
| 136 | + search: urlParsingNode.search.replace(/^\?/, ''), |
| 137 | + hash: urlParsingNode.hash.replace(/^#/, ''), |
81 | 138 | hostname: urlParsingNode.hostname,
|
82 | 139 | port: urlParsingNode.port,
|
83 | 140 | pathname: (urlParsingNode.pathname.charAt(0) === '/')
|
@@ -178,3 +235,4 @@ function getBaseUrl() {
|
178 | 235 | }
|
179 | 236 | return baseUrlParsingNode.href;
|
180 | 237 | }
|
| 238 | +var baseUrlParsingNode; |
0 commit comments