1/*! 2 * Lightweight URL manipulation with JavaScript 3 * This library is independent of any other libraries and has pretty simple interface 4 * and lightweight code-base. 5 * Some ideas of query string parsing had been taken from Jan Wolter 6 * @see http://unixpapa.com/js/querystring.html 7 * 8 * @license MIT 9 * @author Mykhailo Stadnyk <[email protected]> 10 */ 11; var Url = (function() { 12 "use strict"; 13 14 var 15 // mapping between what we want and <a> element properties 16 map = { 17 protocol : 'protocol', 18 host : 'hostname', 19 port : 'port', 20 path : 'pathname', 21 query : 'search', 22 hash : 'hash' 23 }, 24 25 /** 26 * default ports as defined by http://url.spec.whatwg.org/#default-port 27 * We need them to fix IE behavior, @see https://github.com/Mikhus/jsurl/issues/2 28 */ 29 defaultPorts = { 30 "ftp" : 21, 31 "gopher" : 70, 32 "http" : 80, 33 "https" : 443, 34 "ws" : 80, 35 "wss" : 443 36 }, 37 38 parse = function( self, url) { 39 var 40 d = document, 41 link = d.createElement( 'a'), 42 url = url || d.location.href, 43 auth = url.match( /\/\/(.*?)(?::(.*?))?@/) || [] 44 ; 45 46 link.href = url; 47 48 for (var i in map) { 49 self[i] = link[map[i]] || ''; 50 } 51 52 // fix-up some parts 53 self.protocol = self.protocol.replace( /:$/, ''); 54 self.query = self.query.replace( /^\?/, ''); 55 self.hash = self.hash.replace( /^#/, ''); 56 self.user = auth[1] || ''; 57 self.pass = auth[2] || ''; 58 self.port = (defaultPorts[self.protocol] == self.port || self.port == 0) ? '' : self.port; // IE fix, Android browser fix 59 60 if (!self.protocol && !/^([a-z]+:)?\/\//.test( url)) { // is IE and path is relative 61 var 62 base = new Url( d.location.href.match(/(.*\/)/)[0]), 63 basePath = base.path.split( '/'), 64 selfPath = self.path.split( '/') 65 ; 66 67 basePath.pop(); 68 69 for (var i = 0, props = ['protocol','user','pass','host','port'], s = props.length; i < s; i++) { 70 self[props[i]] = base[props[i]]; 71 } 72 73 while (selfPath[0] == '..') { // skip all "../ 74 basePath.pop(); 75 selfPath.shift(); 76 } 77 78 self.path = (url.substring(0, 1) != '/' ? basePath.join( '/') : '') + '/' + selfPath.join( '/'); 79 } 80 81 else { 82 // fix absolute URL's path in IE 83 self.path = self.path.replace( /^\/?/, '/'); 84 } 85 86 parseQs( self); 87 }, 88 89 decode = function(s) { 90 s = s.replace( /\+/g, ' '); 91 92 s = s.replace( /%([ef][0-9a-f])%([89ab][0-9a-f])%([89ab][0-9a-f])/gi, 93 function( code, hex1, hex2, hex3) { 94 var 95 n1 = parseInt( hex1, 16) - 0xE0, 96 n2 = parseInt( hex2, 16) - 0x80 97 ; 98 99 if (n1 == 0 && n2 < 32) { 100 return code; 101 } 102 103 var 104 n3 = parseInt( hex3, 16) - 0x80, 105 n = (n1 << 12) + (n2 << 6) + n3 106 ; 107 108 if (n > 0xFFFF) { 109 return code; 110 } 111 112 return String.fromCharCode( n); 113 } 114 ); 115 116 s = s.replace( /%([cd][0-9a-f])%([89ab][0-9a-f])/gi, 117 function( code, hex1, hex2) { 118 var n1 = parseInt(hex1, 16) - 0xC0; 119 120 if (n1 < 2) { 121 return code; 122 } 123 124 var n2 = parseInt(hex2, 16) - 0x80; 125 126 return String.fromCharCode( (n1 << 6) + n2); 127 } 128 ); 129 130 s = s.replace( /%([0-7][0-9a-f])/gi, 131 function( code, hex) { 132 return String.fromCharCode( parseInt(hex, 16)); 133 } 134 ); 135 136 return s; 137 }, 138 139 parseQs = function( self) { 140 var qs = self.query; 141 142 self.query = new (function( qs) { 143 var re = /([^=&]+)(=([^&]*))?/g, match; 144 145 while ((match = re.exec( qs))) { 146 var 147 key = decodeURIComponent(match[1].replace(/\+/g, ' ')), 148 value = match[3] ? decode(match[3]) : '' 149 ; 150 151 if (this[key] != null) { 152 if (!(this[key] instanceof Array)) { 153 this[key] = [this[key]]; 154 } 155 156 this[key].push( value); 157 } 158 159 else { 160 this[key] = value; 161 } 162 } 163 164 this.clear = function() { 165 for (key in this) { 166 if (!(this[key] instanceof Function)) { 167 delete this[key]; 168 } 169 } 170 }; 171 172 this.count = function() { 173 var count = 0; 174 for (key in this) { 175 if (!(this[key] instanceof Function)) { 176 count++; 177 } 178 } 179 return count; 180 }; 181 182 this.isEmpty = function() { 183 return this.count() === 0; 184 }; 185 186 this.toString = function() { 187 var 188 s = '', 189 e = encodeURIComponent 190 ; 191 192 for (var i in this) { 193 if (this[i] instanceof Function) { 194 continue; 195 } 196 197 if (this[i] instanceof Array) { 198 var len = this[i].length; 199 200 if (len) { 201 for (var ii = 0; ii < len; ii++) { 202 s += s ? '&' : ''; 203 s += e( i) + '=' + e( this[i][ii]); 204 } 205 } 206 207 else { // parameter is an empty array, so treat as an empty argument 208 s += (s ? '&' : '') + e( i) + '='; 209 } 210 } 211 212 else { 213 s += s ? '&' : ''; 214 s += e( i) + '=' + e( this[i]); 215 } 216 } 217 218 return s; 219 }; 220 })( qs); 221 } 222 ; 223 224 return function( url) { 225 this.toString = function() { 226 return ( 227 (this.protocol && (this.protocol + '://')) + 228 (this.user && (this.user + (this.pass && (':' + this.pass)) + '@')) + 229 (this.host && this.host) + 230 (this.port && (':' + this.port)) + 231 (this.path && this.path) + 232 (this.query.toString() && ('?' + this.query)) + 233 (this.hash && ('#' + this.hash)) 234 ); 235 }; 236 237 parse( this, url); 238 }; 239}());
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.