UNPKG

@qiniu/miku-delivery-mp-ks

Version:

Kuaishou Mini Program SDK for Miku Delivery

734 lines (733 loc) 26.3 kB
/** * class URL 实现(小程序环境没有提供内置的 class URL) * 参考 https://github.com/webcomponents/polyfills/blob/master/packages/url/url.js */ const relative = Object.create(null); relative['ftp'] = 21; relative['file'] = 0; relative['gopher'] = 70; relative['http'] = 80; relative['https'] = 443; relative['ws'] = 80; relative['wss'] = 443; const relativePathDotMapping = Object.create(null); relativePathDotMapping['%2e'] = '.'; relativePathDotMapping['.%2e'] = '..'; relativePathDotMapping['%2e.'] = '..'; relativePathDotMapping['%2e%2e'] = '..'; function isRelativeScheme(scheme) { return relative[scheme] !== undefined; } function percentEscape(c) { var unicode = c.charCodeAt(0); if (unicode > 0x20 && unicode < 0x7f && // " # < > ? ` [0x22, 0x23, 0x3c, 0x3e, 0x3f, 0x60].indexOf(unicode) == -1) { return c; } return encodeURIComponent(c); } function percentEscapeQuery(c) { // XXX This actually needs to encode c using encoding and then // convert the bytes one-by-one. var unicode = c.charCodeAt(0); if (unicode > 0x20 && unicode < 0x7f && // " # < > ` (do not escape '?') [0x22, 0x23, 0x3c, 0x3e, 0x60].indexOf(unicode) == -1) { return c; } return encodeURIComponent(c); } const EOF = undefined; const ALPHA = /[a-zA-Z]/; const ALPHANUMERIC = /[a-zA-Z0-9+\-.]/; /** * 同 Web 中的 class URL * 如果命名为 `URL`,小程序开发工具中会报 `SyntaxError: Identifier 'URL' has already been declared`,因此命名为 `Polyfilled_URL` */ export class Polyfilled_URL { constructor(url, base /* , encoding */) { Object.defineProperty(this, "_url", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_scheme", { enumerable: true, configurable: true, writable: true, value: '' }); Object.defineProperty(this, "_schemeData", { enumerable: true, configurable: true, writable: true, value: '' }); Object.defineProperty(this, "_username", { enumerable: true, configurable: true, writable: true, value: '' }); Object.defineProperty(this, "_password", { enumerable: true, configurable: true, writable: true, value: null }); Object.defineProperty(this, "_host", { enumerable: true, configurable: true, writable: true, value: '' }); Object.defineProperty(this, "_port", { enumerable: true, configurable: true, writable: true, value: '' }); Object.defineProperty(this, "_path", { enumerable: true, configurable: true, writable: true, value: [] }); Object.defineProperty(this, "_query", { enumerable: true, configurable: true, writable: true, value: '' }); Object.defineProperty(this, "_fragment", { enumerable: true, configurable: true, writable: true, value: '' }); Object.defineProperty(this, "_isInvalid", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "_isRelative", { enumerable: true, configurable: true, writable: true, value: false }); if (base !== undefined && !(base instanceof Polyfilled_URL)) { base = new Polyfilled_URL(String(base)); } this._url = '' + url; this.clear(); const input = this._url.replace(/^[ \t\r\n\f]+|[ \t\r\n\f]+$/g, ''); // const encoding = encoding || 'utf-8' this.parse(input, undefined, base); } toString() { return this.href; } get href() { if (this._isInvalid) { return this._url; } var authority = ''; if ('' != this._username || null != this._password) { authority = this._username + (null != this._password ? ':' + this._password : '') + '@'; } return (this.protocol + (this._isRelative ? '//' + authority + this.host : '') + this.pathname + this._query + this._fragment); } set href(href) { this.clear(); this.parse(href); } get protocol() { return this._scheme + ':'; } set protocol(protocol) { if (this._isInvalid) { return; } this.parse(protocol + ':', 'scheme start'); } get username() { return this._isInvalid ? '' : this._username; } set username(username) { if (this._isInvalid || !this._isRelative) { return; } throw new Error('Not supported'); } get password() { return this._isInvalid ? '' : this._password; } set password(password) { if (this._isInvalid || !this._isRelative) { return; } throw new Error('Not supported'); } get host() { return this._isInvalid ? '' : this._port ? this._host + ':' + this._port : this._host; } set host(host) { if (this._isInvalid || !this._isRelative) { return; } this.parse(host, 'host'); } get hostname() { return this._host; } set hostname(hostname) { if (this._isInvalid || !this._isRelative) { return; } this.parse(hostname, 'hostname'); } get port() { return this._port; } set port(port) { if (this._isInvalid || !this._isRelative) { return; } this.parse(port, 'port'); } get pathname() { return this._isInvalid ? '' : this._isRelative ? '/' + this._path.join('/') : this._schemeData; } set pathname(pathname) { if (this._isInvalid || !this._isRelative) { return; } this._path = []; this.parse(pathname, 'relative path start'); } get search() { return this._isInvalid || !this._query || '?' == this._query ? '' : this._query; } set search(search) { if (this._isInvalid || !this._isRelative) { return; } if ('?' == search[0]) { search = search.slice(1); } this._query = search !== '' ? '?' : ''; this.parse(search, 'query'); } get hash() { return this._isInvalid || !this._fragment || '#' == this._fragment ? '' : this._fragment; } set hash(hash) { if (this._isInvalid) { return; } if (!hash) { this._fragment = ''; return; } this._fragment = '#'; if ('#' == hash[0]) { hash = hash.slice(1); } this.parse(hash, 'fragment'); } get origin() { var host; if (this._isInvalid || !this._scheme) { return ''; } // javascript: Gecko returns String(""), WebKit/Blink String("null") // Gecko throws error for "data://" // data: Gecko returns "", Blink returns "data://", WebKit returns "null" // Gecko returns String("") for file: mailto: // WebKit/Blink returns String("SCHEME://") for file: mailto: switch (this._scheme) { case 'data': case 'file': case 'javascript': case 'mailto': return 'null'; } host = this.host; if (!host) { return ''; } return this._scheme + '://' + host; } invalid() { this.clear(); this._isInvalid = true; } IDNAToASCII(h) { if ('' == h) { this.invalid(); } // XXX return h.toLowerCase(); } clear() { this._scheme = ''; this._schemeData = ''; this._username = ''; this._password = null; this._host = ''; this._port = ''; this._path = []; this._query = ''; this._fragment = ''; this._isInvalid = false; this._isRelative = false; } parse(input, stateOverride, base) { function err(message) { errors.push(message); } var state = stateOverride || 'scheme start', cursor = 0, buffer = '', seenAt = false, seenBracket = false, errors = []; loop: while ((input[cursor - 1] != EOF || cursor == 0) && !this._isInvalid) { var c = input[cursor]; switch (state) { case 'scheme start': if (c && ALPHA.test(c)) { buffer += c.toLowerCase(); // ASCII-safe state = 'scheme'; } else if (!stateOverride) { buffer = ''; state = 'no scheme'; continue; } else { err('Invalid scheme.'); break loop; } break; case 'scheme': if (c && ALPHANUMERIC.test(c)) { buffer += c.toLowerCase(); // ASCII-safe } else if (':' == c) { this._scheme = buffer; buffer = ''; if (stateOverride) { break loop; } if (isRelativeScheme(this._scheme)) { this._isRelative = true; } if ('file' == this._scheme) { state = 'relative'; } else if (this._isRelative && base && base._scheme == this._scheme) { state = 'relative or authority'; } else if (this._isRelative) { state = 'authority first slash'; } else { state = 'scheme data'; } } else if (!stateOverride) { buffer = ''; cursor = 0; state = 'no scheme'; continue; } else if (EOF == c) { break loop; } else { err('Code point not allowed in scheme: ' + c); break loop; } break; case 'scheme data': if ('?' == c) { this._query = '?'; state = 'query'; } else if ('#' == c) { this._fragment = '#'; state = 'fragment'; } else { // XXX error handling if (EOF != c && '\t' != c && '\n' != c && '\r' != c) { this._schemeData += percentEscape(c); } } break; case 'no scheme': if (!base || !isRelativeScheme(base._scheme)) { err('Missing scheme.'); this.invalid(); } else { state = 'relative'; continue; } break; case 'relative or authority': if ('/' == c && '/' == input[cursor + 1]) { state = 'authority ignore slashes'; } else { err('Expected /, got: ' + c); state = 'relative'; continue; } break; case 'relative': if (base == null) { err('Missing base.'); break loop; } this._isRelative = true; if ('file' != this._scheme) { this._scheme = base._scheme; } if (EOF == c) { this._host = base._host; this._port = base._port; this._path = base._path.slice(); this._query = base._query; this._username = base._username; this._password = base._password; break loop; } else if ('/' == c || '\\' == c) { if ('\\' == c) { err('\\ is an invalid code point.'); } state = 'relative slash'; } else if ('?' == c) { this._host = base._host; this._port = base._port; this._path = base._path.slice(); this._query = '?'; this._username = base._username; this._password = base._password; state = 'query'; } else if ('#' == c) { this._host = base._host; this._port = base._port; this._path = base._path.slice(); this._query = base._query; this._fragment = '#'; this._username = base._username; this._password = base._password; state = 'fragment'; } else { var nextC = input[cursor + 1]; var nextNextC = input[cursor + 2]; if ('file' != this._scheme || !ALPHA.test(c) || (nextC != ':' && nextC != '|') || (EOF != nextNextC && '/' != nextNextC && '\\' != nextNextC && '?' != nextNextC && '#' != nextNextC)) { this._host = base._host; this._port = base._port; this._username = base._username; this._password = base._password; this._path = base._path.slice(); this._path.pop(); } state = 'relative path'; continue; } break; case 'relative slash': if (base == null) { err('Missing base.'); break loop; } if ('/' == c || '\\' == c) { if ('\\' == c) { err('\\ is an invalid code point.'); } if ('file' == this._scheme) { state = 'file host'; } else { state = 'authority ignore slashes'; } } else { if ('file' != this._scheme) { this._host = base._host; this._port = base._port; this._username = base._username; this._password = base._password; } state = 'relative path'; continue; } break; case 'authority first slash': if ('/' == c) { state = 'authority second slash'; } else { err("Expected '/', got: " + c); state = 'authority ignore slashes'; continue; } break; case 'authority second slash': state = 'authority ignore slashes'; if ('/' != c) { err("Expected '/', got: " + c); continue; } break; case 'authority ignore slashes': if ('/' != c && '\\' != c) { state = 'authority'; continue; } else { err('Expected authority, got: ' + c); } break; case 'authority': if ('@' == c) { if (seenAt) { err('@ already seen.'); buffer += '%40'; } seenAt = true; for (var i = 0; i < buffer.length; i++) { var cp = buffer[i]; if ('\t' == cp || '\n' == cp || '\r' == cp) { err('Invalid whitespace in authority.'); continue; } // XXX check URL code points if (':' == cp && null === this._password) { this._password = ''; continue; } var tempC = percentEscape(cp); null !== this._password ? (this._password += tempC) : (this._username += tempC); } buffer = ''; } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) { cursor -= buffer.length; buffer = ''; state = 'host'; continue; } else { buffer += c; } break; case 'file host': if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) { if (buffer.length == 2 && ALPHA.test(buffer[0]) && (buffer[1] == ':' || buffer[1] == '|')) { state = 'relative path'; } else if (buffer.length == 0) { state = 'relative path start'; } else { this._host = this.IDNAToASCII(buffer); buffer = ''; state = 'relative path start'; } continue; } else if ('\t' == c || '\n' == c || '\r' == c) { err('Invalid whitespace in file host.'); } else { buffer += c; } break; case 'host': case 'hostname': if (':' == c && !seenBracket) { // XXX host parsing this._host = this.IDNAToASCII(buffer); buffer = ''; state = 'port'; if ('hostname' == stateOverride) { break loop; } } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) { this._host = this.IDNAToASCII(buffer); buffer = ''; state = 'relative path start'; if (stateOverride) { break loop; } continue; } else if ('\t' != c && '\n' != c && '\r' != c) { if ('[' == c) { seenBracket = true; } else if (']' == c) { seenBracket = false; } buffer += c; } else { err('Invalid code point in host/hostname: ' + c); } break; case 'port': if (/[0-9]/.test(c)) { buffer += c; } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c || stateOverride) { if ('' != buffer) { var temp = parseInt(buffer, 10); if (temp != relative[this._scheme]) { this._port = temp + ''; } buffer = ''; } if (stateOverride) { break loop; } state = 'relative path start'; continue; } else if ('\t' == c || '\n' == c || '\r' == c) { err('Invalid code point in port: ' + c); } else { this.invalid(); } break; case 'relative path start': if ('\\' == c) { err("'\\' not allowed in path."); } state = 'relative path'; if ('/' != c && '\\' != c) { continue; } break; case 'relative path': if (EOF == c || '/' == c || '\\' == c || (!stateOverride && ('?' == c || '#' == c))) { if ('\\' == c) { err('\\ not allowed in relative path.'); } var tmp; // eslint-disable-next-line no-cond-assign if ((tmp = relativePathDotMapping[buffer.toLowerCase()])) { buffer = tmp; } if ('..' == buffer) { this._path.pop(); if ('/' != c && '\\' != c) { this._path.push(''); } } else if ('.' == buffer && '/' != c && '\\' != c) { this._path.push(''); } else if ('.' != buffer) { if ('file' == this._scheme && this._path.length == 0 && buffer.length == 2 && ALPHA.test(buffer[0]) && buffer[1] == '|') { buffer = buffer[0] + ':'; } this._path.push(buffer); } buffer = ''; if ('?' == c) { this._query = '?'; state = 'query'; } else if ('#' == c) { this._fragment = '#'; state = 'fragment'; } } else if ('\t' != c && '\n' != c && '\r' != c) { buffer += percentEscape(c); } break; case 'query': if (!stateOverride && '#' == c) { this._fragment = '#'; state = 'fragment'; } else if (EOF != c && '\t' != c && '\n' != c && '\r' != c) { this._query += percentEscapeQuery(c); } break; case 'fragment': if (EOF != c && '\t' != c && '\n' != c && '\r' != c) { this._fragment += c; } break; } cursor++; } } }