UNPKG

dcp-client

Version:

Core libraries for accessing DCP network

432 lines (368 loc) 14 kB
/* Credit to https://gist.github.com/ryangoree/def0a520ed43c6c465d9a6518161bc7c#file-url-api-polyfill-js for the polyfill. Minor modifications made for the DCP Worker environment. */ self.wrapScriptLoading({ scriptName: 'url' }, function url$$fn(protectedStorage, ring2PostMessage) { (function() { if (typeof URL != 'function') { rewriteURL(); } else if (!('searchParams' in new URL(globalThis.location))) { rewriteURL(); } function rewriteURL() { // Overwrite URL if no searchParams property exists. globalThis.URL = function(url, base) { var _hash; var _hostname; var _password; var _pathname; var _port; var _protocol; var _search; var _username; // In the spec it seems like 'base' must be a string, but when empirically testing all platforms accept it as a object as well (and treat it as toString to a URL) if (base) base = base.toString() Object.defineProperty(this, 'hash', { get: function() { return _hash; }, set: function(value) { _hash = value.length > 0 ? '#' + value.match(/^#*(.*)/)[1] : ''; return value; } }); Object.defineProperty(this, 'host', { get: function() { return _port.length > 0 ? _hostname + ':' + _port : _hostname; }, set: function(value) { var parts = value.split(':'); this.hostname = parts[0]; this.port = parts[1]; return value; } }); Object.defineProperty(this, 'hostname', { get: function() { return _hostname; }, set: function(value) { _hostname = value.length > 0 ? encodeURIComponent(value) : _hostname; return value; } }); function removeUsername(match, username, password) { if (password === '@') { return ''; } else { return password; } } Object.defineProperty(this, 'href', { get: function() { var hrefStr = _protocol + '//'; if (_username.length > 0 || _password.length > 0) { if (_username.length > 0) { hrefStr += _username; } if (_password.length > 0) { hrefStr += ':' + _password; } hrefStr += '@' } hrefStr += _hostname; if (_port.length > 0) { hrefStr += ':' + _port; } hrefStr += _pathname + _search + _hash; return hrefStr; }, set: function(value) { this.protocol = value; value = value.replace(/.*?:\/*/, ''); var usernameMatch = value.match(/([^:]*).*@/); this.username = usernameMatch ? usernameMatch[1] : ''; value = value.replace(/([^:]*):?(.*@)/, removeUsername); var passwordMatch = value.match(/.*(?=@)/); this.password = passwordMatch ? passwordMatch[0] : ''; value = value.replace(/.*@/, ''); this.hostname = value.match(/[^:/?]*/); var portMatch = value.match(/:(\d+)/); this.port = portMatch ? portMatch[1] : ''; var pathnameMatch = value.match(/\/([^?#]*)/); this.pathname = pathnameMatch ? pathnameMatch[1] : ''; var searchMatch = value.match(/\?[^#]*/); this.search = searchMatch ? searchMatch[0] : ''; var hashMatch = value.match(/\#.*/); this.hash = hashMatch ? hashMatch[0] : ''; } }); this.toString = () => this.href; Object.defineProperty(this, 'origin', { get: function() { var originStr = _protocol + '//' + _hostname; if (_port.length > 0) { originStr += ':' + _port; } return originStr; }, set: function(value) { this.protocol = value; value = value.replace(/.*?:\/*/, ''); this.hostname = value.match(/[^:/?]*/); var portMatch = value.match(/:(\d+)/); this.port = portMatch ? portMatch[1] : ''; } }); Object.defineProperty(this, 'password', { get: function() { return _password; }, set: function(value) { _password = encodeURIComponent(value); return value; } }); Object.defineProperty(this, 'pathname', { get: function() { return _pathname; }, set: function(value) { _pathname = '/' + value.match(/\/?(.*)/)[1]; return value; } }); Object.defineProperty(this, 'port', { get: function() { return _port; }, set: function(value) { if (isNaN(value) || value === '') { _port = ''; } else { _port = Math.min(65535, value).toString(); } return value; } }); Object.defineProperty(this, 'protocol', { get: function() { return _protocol; }, set: function(value) { _protocol = value.match(/[^/:]*/)[0] + ':'; return value; } }); Object.defineProperty(this, 'search', { get: function() { return _search; }, set: function(value) { _search = value.length > 0 ? '?' + value.match(/\??(.*)/)[1] : ''; return value; } }); Object.defineProperty(this, 'username', { get: function() { return _username; }, set: function(value) { _username = value; } }); // If a string is passed for url instead of location or link, then set the if (typeof url === 'string') { var urlIsValid = /^[a-zA-z]+:\/\/.*/.test(url); var baseIsValid = /^[a-zA-z]+:\/\/.*/.test(base); if (urlIsValid) { this.href = url; } // If the url isn't valid, but the base is, then prepend the base to the url. else if (baseIsValid) { // Parse the base URL // Improved handling URL(url, base) based on // https://gist.github.com/hu9o/66d273aeced748c127cc7b317bcad577#file-url-api-polyfill-js-L11 this.href = base; // Authority = username + password + hostname + protocol let authority = ''; if (_username.length > 0 || _password.length > 0) { if (_username.length > 0) { authority += _username; } if (_password.length > 0) { authority += ':' + _password; } authority += '@' } authority += _hostname if (_port.length > 0) { authority += ':' + _port; } // Full URL combining `base` and `url` let full_url; if (url.startsWith('//')) { full_url = _protocol + url } else if (url.startsWith('/')) { full_url = _protocol + '//' + authority + url } else if (url.startsWith('?')) { full_url = _protocol + '//' + authority + _pathname + url } else if (url.startsWith('#')) { full_url = _protocol + '//' + authority + _pathname + _search + url } else { // drop file name and keep dir, without the trailing slash let pathdir = _pathname.substr(0, _pathname.lastIndexOf('/')) // resolve any leading ./ and ../ in the relative path while (url.startsWith('./') || url.startsWith('../')) { if (url.startsWith('../')) pathdir = pathdir.substr(0, pathdir.lastIndexOf('/')) // pop last dir of pathdir, if any url = url.substr(url.indexOf('/') + 1) // consume the leading ./ or ../ in url } // restore trailing slash (which is also the leading slash, if pathdir == '') pathdir += '/' full_url = _protocol + '//' + authority + pathdir + url } this.href = full_url } // If no valid url or base is given, then throw a type error. else { throw new TypeError('URL string is not valid. If using a relative url, a second argument needs to be passed representing the base URL. Example: new URL("relative/path", "http://www.example.com");'); } } else { // Copy all of the location or link properties to the // new URL instance. _hash = url.hash; _hostname = url.hostname; _password = url.password ? url.password : ''; _pathname = url.pathname; _port = url.port; _protocol = url.protocol; _search = url.search; _username = url.username ? url.username: ''; } // Use IIFE to capture the URL instance and encapsulate the params instead of finding them // each time a searchParam method is called this.searchParams = (function(url) { // Create 2 seperate arrays for the params and values to make management and lookup easier. var params = []; var values = []; if (url.search.length > 0) { var pairs = url.search.slice(1).split('&'); pairs.forEach(function(pair) { var parts = pair.split('='); params.push(parts[0]); values.push(parts[1]); }); } // Update the search property of the URL instance with the new params and values. function updateSearchString() { if (params.length === 0) { url.search = ''; } else { url.search = params.map(function(param, index) { return param + '=' + values[index]; }).join('&'); } } // Expose functions to mimic the behavior of the native searchParams methods. return { // Add a given param with a given value to the end. append: function(param, value) { params.push(param); values.push(value); updateSearchString(); }, // Remove all occurances of a given param delete: function(param) { while(params.indexOf(param) > -1) { // Continue until the param is not found. values.splice(params.indexOf(param), 1); params.splice(params.indexOf(param), 1); } updateSearchString(); }, // Return an array to be structured in this way: [[param1, value1], [param2, value2]] to // mimic the native method's ES6 iterator. entries: function() { return params.map(function(param, index) { return [param, values[index]]; }); }, // Return the value matched to the first occurance of a given param. get: function(param) { return values[params.indexOf(param)]; }, // Return all values matched to all occurances of a given param. getAll: function(param) { return values.filter(function(value, index) { return params[index] === param; }); }, // Return a boolean to indicate whether a given param exists. has: function(param) { return params.indexOf(param) > -1; }, // Return an array of the param names to mimic the native method's ES6 iterator. keys: function() { return params; }, // Set a given param to a given value. set: function set(param, value) { if (params.indexOf(param) === -1) { this.append(param,value); // If the given param doesn't already exist, append it. } else { var first = true; var newValues = []; // If the param already exists, change the value of the first occurance and remove any // remaining occurances. params = params.filter(function(currentParam, index) { // If the currentParam isn't the one being changed keep the param and it's current value. if (currentParam !== param) { newValues.push(values[index]); return true; } // If the currentParam matches the one being changed and it's the first one, keep the // param and change its value to the given one. else if (first) { first = false; newValues.push(value); return true; } // If the currentParam matches the one being changed, but it's not the first, remove it. return false; }); values = newValues; updateSearchString(); } }, // Sort all key/value pairs, if any, by their keys then by their values. sort: function() { // Call entries to make sorting easier, then rewrite the params and values in the new order. var sortedPairs = this.entries().sort(); params = []; values = []; sortedPairs.forEach(function(pair) { params.push(pair[0]); values.push(pair[1]); }) updateSearchString(); }, // Return the search string without the '?'. toString: function() { return url.search.slice(1); }, // Return and array of the param values to mimic the native method's ES6 iterator.. values: function() { return values; } }; })(this); } } })() });