github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/docs/themes/hugo-theme-relearn/static/js/url.js (about) 1 // https://github.com/inexorabletash/polyfill/blob/v0.1.25/url.js 2 3 // URL Polyfill 4 // Draft specification: https://url.spec.whatwg.org 5 6 // Notes: 7 // - Primarily useful for parsing URLs and modifying query parameters 8 // - Should work in IE8+ and everything more modern 9 10 (function (global) { 11 'use strict'; 12 13 // Browsers may have: 14 // * No global URL object 15 // * URL with static methods only - may have a dummy constructor 16 // * URL with members except searchParams 17 // * Full URL API support 18 var origURL = global.URL; 19 var nativeURL; 20 try{ 21 if( origURL ){ 22 nativeURL = new global.URL('http://example.com'); 23 if( 'searchParams' in nativeURL ) 24 return; 25 if( !('href' in nativeURL) ) 26 nativeURL = undefined; 27 } 28 } 29 catch( _ ){} 30 31 // NOTE: Doesn't do the encoding/decoding dance 32 function urlencoded_serialize(pairs) { 33 var output = '', first = true; 34 pairs.forEach(function (pair) { 35 var name = encodeURIComponent(pair.name); 36 var value = encodeURIComponent(pair.value); 37 if( !first ) output += '&'; 38 output += name + '=' + value; 39 first = false; 40 }); 41 return output.replace(/%20/g, '+'); 42 } 43 44 // NOTE: Doesn't do the encoding/decoding dance 45 function urlencoded_parse(input, isindex) { 46 var sequences = input.split('&'); 47 if( isindex && sequences[0].indexOf('=') === -1 ) 48 sequences[0] = '=' + sequences[0]; 49 var pairs = []; 50 sequences.forEach(function (bytes) { 51 if( bytes.length === 0 ) return; 52 var index = bytes.indexOf('='); 53 if( index !== -1 ){ 54 var name = bytes.substring(0, index); 55 var value = bytes.substring(index + 1); 56 } 57 else{ 58 name = bytes; 59 value = ''; 60 } 61 name = name.replace(/\+/g, ' '); 62 value = value.replace(/\+/g, ' '); 63 pairs.push({ name: name, value: value }); 64 }); 65 var output = []; 66 pairs.forEach(function (pair) { 67 output.push({ 68 name: decodeURIComponent(pair.name), 69 value: decodeURIComponent(pair.value) 70 }); 71 }); 72 return output; 73 } 74 75 function URLUtils(url) { 76 if( nativeURL ) 77 return new origURL(url); 78 var anchor = document.createElement('a'); 79 anchor.href = url; 80 return anchor; 81 } 82 83 function URLSearchParams(init) { 84 var $this = this; 85 this._list = []; 86 87 if( init === undefined || init === null ) 88 init = ''; 89 90 if( Object(init) !== init || !(init instanceof URLSearchParams) ) 91 init = String(init); 92 93 if( typeof init === 'string' && init.substring(0, 1) === '?' ) 94 init = init.substring(1); 95 96 if( typeof init === 'string' ) 97 this._list = urlencoded_parse(init); 98 else 99 this._list = init._list.slice(); 100 101 this._url_object = null; 102 this._setList = function (list) { if( !updating ) $this._list = list; }; 103 104 var updating = false; 105 this._update_steps = function() { 106 if( updating ) return; 107 updating = true; 108 109 if( !$this._url_object ) return; 110 111 // Partial workaround for IE issue with 'about:' 112 if( $this._url_object.protocol === 'about:' && 113 $this._url_object.pathname.indexOf('?') !== -1 ){ 114 $this._url_object.pathname = $this._url_object.pathname.split('?')[0]; 115 } 116 117 $this._url_object.search = urlencoded_serialize($this._list); 118 119 updating = false; 120 }; 121 } 122 123 Object.defineProperties(URLSearchParams.prototype, { 124 append: { 125 value: function (name, value) { 126 this._list.push({ name: name, value: value }); 127 this._update_steps(); 128 }, writable: true, enumerable: true, configurable: true 129 }, 130 131 'delete': { 132 value: function (name) { 133 for( var i = 0; i < this._list.length; ){ 134 if( this._list[i].name === name ) 135 this._list.splice(i, 1); 136 else 137 ++i; 138 } 139 this._update_steps(); 140 }, writable: true, enumerable: true, configurable: true 141 }, 142 143 get: { 144 value: function (name) { 145 for( var i = 0; i < this._list.length; ++i ){ 146 if( this._list[i].name === name ) 147 return this._list[i].value; 148 } 149 return null; 150 }, writable: true, enumerable: true, configurable: true 151 }, 152 153 getAll: { 154 value: function (name) { 155 var result = []; 156 for( var i = 0; i < this._list.length; ++i ){ 157 if( this._list[i].name === name ) 158 result.push(this._list[i].value); 159 } 160 return result; 161 }, writable: true, enumerable: true, configurable: true 162 }, 163 164 has: { 165 value: function (name) { 166 for( var i = 0; i < this._list.length; ++i ){ 167 if( this._list[i].name === name ) 168 return true; 169 } 170 return false; 171 }, writable: true, enumerable: true, configurable: true 172 }, 173 174 set: { 175 value: function (name, value) { 176 var found = false; 177 for( var i = 0; i < this._list.length; ){ 178 if( this._list[i].name === name ){ 179 if( !found ){ 180 this._list[i].value = value; 181 found = true; 182 ++i; 183 } 184 else{ 185 this._list.splice(i, 1); 186 } 187 } 188 else{ 189 ++i; 190 } 191 } 192 193 if( !found ) 194 this._list.push({ name: name, value: value }); 195 196 this._update_steps(); 197 }, writable: true, enumerable: true, configurable: true 198 }, 199 200 entries: { 201 value: function() { 202 var $this = this, index = 0; 203 return { next: function() { 204 if( index >= $this._list.length ) 205 return {done: true, value: undefined}; 206 var pair = $this._list[index++]; 207 return {done: false, value: [pair.name, pair.value]}; 208 }}; 209 }, writable: true, enumerable: true, configurable: true 210 }, 211 212 keys: { 213 value: function() { 214 var $this = this, index = 0; 215 return { next: function() { 216 if( index >= $this._list.length ) 217 return {done: true, value: undefined}; 218 var pair = $this._list[index++]; 219 return {done: false, value: pair.name}; 220 }}; 221 }, writable: true, enumerable: true, configurable: true 222 }, 223 224 values: { 225 value: function() { 226 var $this = this, index = 0; 227 return { next: function() { 228 if( index >= $this._list.length ) 229 return {done: true, value: undefined}; 230 var pair = $this._list[index++]; 231 return {done: false, value: pair.value}; 232 }}; 233 }, writable: true, enumerable: true, configurable: true 234 }, 235 236 forEach: { 237 value: function(callback) { 238 var thisArg = (arguments.length > 1) ? arguments[1] : undefined; 239 this._list.forEach(function(pair, _index) { 240 callback.call(thisArg, pair.name, pair.value); 241 }); 242 243 }, writable: true, enumerable: true, configurable: true 244 }, 245 246 toString: { 247 value: function () { 248 return urlencoded_serialize(this._list); 249 }, writable: true, enumerable: false, configurable: true 250 } 251 }); 252 253 if( 'Symbol' in global && 'iterator' in global.Symbol ){ 254 Object.defineProperty(URLSearchParams.prototype, global.Symbol.iterator, { 255 value: URLSearchParams.prototype.entries, 256 writable: true, enumerable: true, configurable: true}); 257 } 258 259 function URL(url, base) { 260 if( !(this instanceof global.URL) ) 261 throw new TypeError('Failed to construct "URL": Please use the "new" operator.'); 262 263 if( base ){ 264 url = (function () { 265 if( nativeURL ) return new origURL(url, base).href; 266 267 var doc; 268 // Use another document/base tag/anchor for relative URL resolution, if possible 269 if( document.implementation && document.implementation.createHTMLDocument ){ 270 doc = document.implementation.createHTMLDocument(''); 271 } 272 else if( document.implementation && document.implementation.createDocument ){ 273 doc = document.implementation.createDocument('http://www.w3.org/1999/xhtml', 'html', null); 274 doc.documentElement.appendChild(doc.createElement('head')); 275 doc.documentElement.appendChild(doc.createElement('body')); 276 } 277 else if( window.ActiveXObject ){ 278 doc = new window.ActiveXObject('htmlfile'); 279 doc.write('<head></head><body></body>'); 280 doc.close(); 281 } 282 283 if( !doc ) throw Error('base not supported'); 284 285 var baseTag = doc.createElement('base'); 286 baseTag.href = base; 287 doc.getElementsByTagName('head')[0].appendChild(baseTag); 288 var anchor = doc.createElement('a'); 289 anchor.href = url; 290 return anchor.href; 291 }()); 292 } 293 294 // An inner object implementing URLUtils (either a native URL 295 // object or an HTMLAnchorElement instance) is used to perform the 296 // URL algorithms. With full ES5 getter/setter support, return a 297 // regular object For IE8's limited getter/setter support, a 298 // different HTMLAnchorElement is returned with properties 299 // overridden 300 301 var instance = URLUtils(url || ''); 302 303 // Detect for ES5 getter/setter support 304 // (an Object.defineProperties polyfill that doesn't support getters/setters may throw) 305 var ES5_GET_SET = (function() { 306 if( !('defineProperties' in Object) ) return false; 307 try{ 308 var obj = {}; 309 Object.defineProperties(obj, { prop: { 'get': function () { return true; } } }); 310 return obj.prop; 311 } 312 catch( _ ){ 313 return false; 314 } 315 })(); 316 317 var self = ES5_GET_SET ? this : document.createElement('a'); 318 319 var query_object = new URLSearchParams( 320 instance.search ? instance.search.substring(1) : null); 321 query_object._url_object = self; 322 323 Object.defineProperties(self, { 324 href: { 325 get: function () { return instance.href; }, 326 set: function (v) { instance.href = v; tidy_instance(); update_steps(); }, 327 enumerable: true, configurable: true 328 }, 329 origin: { 330 get: function () { 331 if( 'origin' in instance ) return instance.origin; 332 return this.protocol + '//' + this.host; 333 }, 334 enumerable: true, configurable: true 335 }, 336 protocol: { 337 get: function () { return instance.protocol; }, 338 set: function (v) { instance.protocol = v; }, 339 enumerable: true, configurable: true 340 }, 341 username: { 342 get: function () { return instance.username; }, 343 set: function (v) { instance.username = v; }, 344 enumerable: true, configurable: true 345 }, 346 password: { 347 get: function () { return instance.password; }, 348 set: function (v) { instance.password = v; }, 349 enumerable: true, configurable: true 350 }, 351 host: { 352 get: function () { 353 // IE returns default port in |host| 354 var re = {'http:': /:80$/, 'https:': /:443$/, 'ftp:': /:21$/}[instance.protocol]; 355 return re ? instance.host.replace(re, '') : instance.host; 356 }, 357 set: function (v) { instance.host = v; }, 358 enumerable: true, configurable: true 359 }, 360 hostname: { 361 get: function () { return instance.hostname; }, 362 set: function (v) { instance.hostname = v; }, 363 enumerable: true, configurable: true 364 }, 365 port: { 366 get: function () { return instance.port; }, 367 set: function (v) { instance.port = v; }, 368 enumerable: true, configurable: true 369 }, 370 pathname: { 371 get: function () { 372 // IE does not include leading '/' in |pathname| 373 if( instance.pathname.charAt(0) !== '/' ) return '/' + instance.pathname; 374 return instance.pathname; 375 }, 376 set: function (v) { instance.pathname = v; }, 377 enumerable: true, configurable: true 378 }, 379 search: { 380 get: function () { return instance.search; }, 381 set: function (v) { 382 if( instance.search === v ) return; 383 instance.search = v; tidy_instance(); update_steps(); 384 }, 385 enumerable: true, configurable: true 386 }, 387 searchParams: { 388 get: function () { return query_object; }, 389 enumerable: true, configurable: true 390 }, 391 hash: { 392 get: function () { return instance.hash; }, 393 set: function (v) { instance.hash = v; tidy_instance(); }, 394 enumerable: true, configurable: true 395 }, 396 toString: { 397 value: function() { return instance.toString(); }, 398 enumerable: false, configurable: true 399 }, 400 valueOf: { 401 value: function() { return instance.valueOf(); }, 402 enumerable: false, configurable: true 403 } 404 }); 405 406 function tidy_instance() { 407 var href = instance.href.replace(/#$|\?$|\?(?=#)/g, ''); 408 if( instance.href !== href ) 409 instance.href = href; 410 } 411 412 function update_steps() { 413 query_object._setList(instance.search ? urlencoded_parse(instance.search.substring(1)) : []); 414 query_object._update_steps(); 415 } 416 417 return self; 418 } 419 420 if( origURL ){ 421 for( var i in origURL ){ 422 if( Object.prototype.hasOwnProperty.call(origURL, i) && typeof origURL[i] === 'function' ) 423 URL[i] = origURL[i]; 424 } 425 } 426 427 global.URL = URL; 428 global.URLSearchParams = URLSearchParams; 429 430 }( typeof self !== 'undefined' ? self : this ));