Action
HTTP URL
This is a utility action for those creating other JavaScript actions. It does nothing on its own.
This library allows one to take an HTTP/S URL, split it into its component parts, substitute any of them, and get a reassembled URL back out again.
This is a stopgap until Drafts/JavaScript Core supports the URL class described at https://url.spec.whatwg.org/. It defines JavaScript HTTPURL, HTTPURLError, and HTTPURLSearchParams classes that implement a subset of the ones in the parallel classes from the standard without the “HTTP” prefix.
- HTTP and HTTPS only.
- No support for relative URLs.
- ASCII codepoints only.
- No IP addresses in the host section, yet.
- No username/password support, yet.
- No alternate query parameter syntax: ye olde ampersand and equals signs.
Steps
-
script
// Goals and non-goals of this implementation: // // * This is a stop-gap until Drafts/JavaScript Core supports // https://url.spec.whatwg.org/. If you're looking for more readable // documentation, look at https://nodejs.org/api/url.html. // * HTTP/HTTPS only. // * Parsing and serialization is necessary. // * No support for relative URLs. // * Due to limitations of JavaScript regular expressions, no support for // non-ASCII characters. // * No IP addresses, yet. // * No support for username & password, yet. // * No support for alternate query syntax. var HTTPURL; var HTTPURLError; var HTTPURLSearchParams; ( function () { const protocolPattern = '(https?)'; const preOriginPattern = '://'; const domainComponentPattern = '[a-zA-Z](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?'; const domainPattern = `(${domainComponentPattern}(?:[.]${domainComponentPattern})*)`; const portPattern = '(?:[:]([1-9][0-9]{0,4}))?'; const pathPattern = '([^?#]*)?'; const queryPattern = '([?](?:[^#]*))?'; const fragmentPattern = '(#(?:.*\\S)?)?'; const regex = new RegExp( '^\\s*' + protocolPattern + preOriginPattern + domainPattern + portPattern + pathPattern + queryPattern + fragmentPattern + '\\s*$', 'i', ); HTTPURLError = class HTTPURLError extends Error {}; function parseQuery(self, string) { self.storage = []; if (! string || string.length < 1) { return; } if (string[0] === '?') { string = string.substr(1); } for (const param of string.split('&')) { const equals = param.indexOf('='); let key; let value; if (equals < 0) { key = param; value = ''; } else { key = param.substring(0, equals); value = param.substr(equals + 1); } self.storage.push( [ decodeURIComponent(key), decodeURIComponent(value) ] ); } } HTTPURLSearchParams = class HTTPURLSearchParams { constructor(string) { parseQuery(this, string); } replaceContent(string) { parseQuery(this, string); } append(name, value) { this.storage.push( [name, value] ); } delete(name) { this.storage = this.storage.filter(entry => entry[0] !== name); } entries() { return this.storage; } [Symbol.iterator]() { let nextIndex = 0; return { next: () => { if (nextIndex < this.storage.length) { return { value: this.storage[nextIndex++], done: false, }; } return {done: true}; } }; } forEach(callback, callbackArgument) { for (const entry of this.storage) { callback(entry[0], entry[1], callbackArgument); } } get(name) { let found = this.storage.find( entry => entry[0] === name ); if (found) { return found[1]; } return null; } getAll(name) { return this.storage .filter( entry => entry[0] === name ) .map( entry => entry[1] ); } has(name) { return !! this.storage.find( entry => entry[0] === name ); } keys() { return this.storage.map(entry => entry[0]); } set(name, value) { const newStorage = []; let found = false; for (const entry of this.storage) { if (entry[0] === name) { if (! found) { found = true; newStorage.push( [name, value] ); } } else { newStorage.push(entry); } } if (! found) { newStorage.push( [name, value] ); } this.storage = newStorage; } sort() { this.storage.sort( function (a, b) { const a_key = a[0]; const b_key = b[0]; if (a_key < b_key) { return -1; } if (a_key > b_key) { return 1; } return 0; } ); } values() { return this.storage.map(entry => entry[1]); } toString() { let string = ''; let separator = '?'; for (const param of this.storage) { string += separator; string += encodeURIComponent( param[0] ); if ( param[1] ) { string += '=' string += encodeURIComponent( param[1] ); } separator = '&'; } return string; } }; function parseURL(self, string) { let match = regex.exec(string); if (! match) { throw new HTTPURLError( `"${string}" does not look like an HTTP URL.` ); } self.protocol = match[1]; self.hostname = match[2]; self.port = match[3]; self.pathname = match[4]; self.search = match[5]; self.hash = match[6]; } HTTPURL = class HTTPURL { constructor(string) { this.searchParams = new HTTPURLSearchParams(''); parseURL(this, string); } get href() { return this.toString(); } set href(string) { parseURL(this, string); } get search() { return this.searchParams.toString(); } set search(string) { this.searchParams.replaceContent(string); } toString() { let string = `${this.protocol}://${this.hostname}`; if (this.port) { string += ':'; string += this.port; } if (this.pathname) { string += this.pathname; } const search = this.search; if (search) { string += search; } if (this.hash) { string += this.hash; } return string } } } )(); // let url = new HTTPURL('https://hostname:443#'); // console.log('Parse of simple URL:'); // console.log(url); // console.log(url.href); // // url.href = 'http://hostname.tld/foo/bar?foo=bar&baz=quuz&eggs&bacon&foo%20bar=bar%20foo&eggs&bacon#fragment'; // console.log(''); // console.log('Parse of complicated URL:'); // console.log(url); // console.log(url.href); // // url.search = '?alpha=beta&gamma=delta'; // console.log(''); // console.log('Assignment to .search:'); // console.log(url); // console.log(url.href); // // url.searchParams.append('null'); // url.searchParams.append('null', ''); // url.searchParams.append('epsilon', 'zeta'); // console.log(''); // console.log('Several calls to .searchParams.append():'); // console.log(url); // console.log(url.href); // // url.searchParams.delete('null'); // console.log(''); // console.log('.searchParams.delete():'); // console.log(url); // console.log(url.href); // // // Test of iterable interface: // console.log(''); // console.log('Iterating over .searchParams:'); // for (const queryParameter of url.searchParams) { // console.log(queryParameter); // } // // console.log(''); // console.log('Iterating over .searchParams.entries():'); // for (const queryParameter of url.searchParams.entries()) { // console.log(queryParameter); // } // // console.log(''); // console.log('Iterating over .searchParams.keys():'); // for (const key of url.searchParams.keys()) { // console.log(key); // } // // console.log(''); // console.log('Iterating over .searchParams.values():'); // for (const value of url.searchParams.values()) { // console.log(value); // } // // console.log(''); // console.log('Internal iteration via .searchParams.forEach():'); // url.searchParams.forEach( // (name, value, extra) => { console.log( `${extra} ${name} ${value}` ); }, // 'Here ->', // ); // // console.log(''); // console.log('.searchParams.get():'); // console.log( url.searchParams.get('gamma') ); // console.log( url.searchParams.get('no such parameter') ); // // console.log(''); // console.log('.searchParams.has():'); // console.log( url.searchParams.has('gamma') ); // console.log( url.searchParams.has('no such parameter') ); // // url.search = 'a=b&b=c&d=e&f=g&a=b&b=c&d=e&f=g' // console.log(''); // console.log('.searchParams.getAll():'); // console.log( url.searchParams.getAll('b') ); // console.log( url.searchParams.getAll('no such parameter') ); // // url.searchParams.set('a', 'XXX') // url.searchParams.set('h', 'OOO') // console.log(''); // console.log('.searchParams.set():'); // console.log(url); // console.log(url.href); // // url.search = 'a=b&b=c&d=e&f=g&a=b&b=c&d=e&f=g' // url.searchParams.sort(); // console.log(''); // console.log('.searchParams.sort():'); // console.log(url); // console.log(url.href);
Options
-
After Success Default Notification Error Log Level Error
Items available in the Drafts Directory are uploaded by community members. Use appropriate caution reviewing downloaded items before use.