Chromium Code Reviews| Index: sdk/lib/core/uri.dart |
| diff --git a/sdk/lib/core/uri.dart b/sdk/lib/core/uri.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..86f37fb1ef31bee3cd6b7e7997e5d51b8d7f74a6 |
| --- /dev/null |
| +++ b/sdk/lib/core/uri.dart |
| @@ -0,0 +1,787 @@ |
| +// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| +// for details. All rights reserved. Use of this source code is governed by a |
| +// BSD-style license that can be found in the LICENSE file. |
| + |
| +part of dart.core; |
| + |
| +/** |
| + * A parsed URI, as specified by RFC-3986, http://tools.ietf.org/html/rfc3986. |
| + */ |
| +class Uri { |
| + int _port; |
| + |
| + /* |
| + * Returns the URI scheme. If there is no scheme the empty string |
|
floitsch
2013/05/24 20:37:27
All these comments are dartdocs. -> /** instead of
Søren Gjesse
2013/05/27 10:47:21
Of cause - thanks.
|
| + * will be returned. |
|
floitsch
2013/05/24 20:37:27
No need for future.
If there is no scheme the empt
Søren Gjesse
2013/05/27 10:47:21
Done.
Similar change for all Uri component proper
|
| + */ |
| + final String scheme; |
| + |
| + /* |
| + * Returns the user info part of the URI authority. If there is no |
| + * user info in the authority the empty string will be returned. |
| + */ |
| + final String userInfo; |
| + |
| + /* |
| + * Returns the host part of the URI authority. If there is no |
| + * authority and hence no host the empty string will be returned. |
| + */ |
| + final String host; |
| + |
| + /* |
| + * Returns the port part of the URI authority. If there is no |
| + * port in the authority 0 will be returned. |
| + */ |
| + int get port => _port; |
| + |
| + /* |
| + * Returns the URI path. The returned path is encoded. To get direct |
| + * access to the decoded path use [pathSegement]. If there is no |
|
floitsch
2013/05/24 20:37:27
pathSegments
Søren Gjesse
2013/05/27 10:47:21
Done.
|
| + * path the empty string will be returned. |
| + */ |
| + final String path; |
| + |
| + /* |
| + * Returns the URI query. The returned query is encoded. To get |
| + * direct access to the decoded query use [queryParameters]. If |
| + * there is no query the empty string will be returned. |
| + */ |
| + final String query; |
| + |
| + /* |
| + * Returns the URI fragment. If there is no fragment the empty string |
| + * will be returned. |
| + */ |
| + final String fragment; |
| + |
| + /** |
| + * Creates a new URI object by parsing a URI string. |
| + */ |
| + static Uri parse(String uri) => new Uri._fromMatch(_splitRe.firstMatch(uri)); |
| + |
| + Uri._fromMatch(Match m) : |
| + this(scheme: _emptyIfNull(m[_COMPONENT_SCHEME]), |
| + userInfo: _emptyIfNull(m[_COMPONENT_USER_INFO]), |
| + host: _eitherOf( |
| + m[_COMPONENT_HOST], m[_COMPONENT_HOST_IPV6]), |
| + port: _parseIntOrZero(m[_COMPONENT_PORT]), |
| + path: _emptyIfNull(m[_COMPONENT_PATH]), |
| + query: _emptyIfNull(m[_COMPONENT_QUERY_DATA]), |
| + fragment: _emptyIfNull(m[_COMPONENT_FRAGMENT])); |
| + |
| + /* |
| + * Create a new URI from its components. |
| + * |
| + * The URI scheme is set through [scheme]. The scheme will be |
| + * normalized to all lowercase letters. |
| + * |
| + * The URI user info of the domain is set through [userInfo]. |
| + * |
| + * The URI host part of the domain is set through [host]. The host |
| + * can either be a hostname, a IPv4 address or an IPv6 address, |
| + * contained in '[' and ']'. If the host contains a ':' character, |
| + * the host will have the '[' and ']' added. |
| + * |
| + * The URI port is set through [port]. The port is normalized for |
| + * scheme http and https where port 80 and port 443 respectively |
| + * will be set as 0. |
|
floitsch
2013/05/24 20:37:27
is set to 0.
Søren Gjesse
2013/05/27 10:47:21
This was a broken sentence. Removed the "as 0" fro
|
| + * |
| + * The path component can be specified using either [path] or |
| + * [pathSegments]. When [path] is used the provided string is |
| + * expected to be fully percent-encoded and will be used in its |
|
floitsch
2013/05/24 20:37:27
is used
Søren Gjesse
2013/05/27 10:47:21
Done.
|
| + * literal form for the URI path. When [pathSegments] is used each |
| + * of the provided segments will be percent encoded and joined using |
| + * the forward slash separator. |
| + * |
| + * The query component can be specified using either [query] or |
| + * [queryParameters]. When [query] is used the provided string is |
| + * expected to be fully percent-encoded and will be used in its |
| + * literal form for the URI query. When [queryParameters] is used xxx |
|
floitsch
2013/05/24 20:37:27
xxx
Søren Gjesse
2013/05/27 10:47:21
My mark of unfinished comment - sorry.
|
| + * |
| + * The URI fragment is set through [fragment]. |
| + */ |
| + Uri({scheme, |
| + this.userInfo: "", |
| + this.host: "", |
| + port: 0, |
| + String path, |
| + List<String> pathSegments, |
| + String query, |
| + Map<String, String> queryParameters, |
| + fragment: ""}) : |
| + scheme = _makeScheme(scheme), |
| + path = _makePath(path, pathSegments), |
| + query = _makeQuery(query, queryParameters), |
| + fragment = _makeFragment(fragment) { |
| + // Perform scheme specific normalization. |
| + if (scheme == "http" && port == 80) { |
| + _port = 0; |
| + } else if (scheme == "https" && port == 443) { |
| + _port = 0; |
| + } else { |
| + _port = port; |
| + } |
| + } |
| + |
| + /* |
| + * Returns the URI path split into its segments. Each of the |
| + * segments in the returned list have been decoded. If the path is |
| + * empty the empty list will be returned. |
| + */ |
| + List<String> get pathSegments { |
| + if (path == "") return []; |
|
floitsch
2013/05/24 20:37:27
const <String>[];
?
Søren Gjesse
2013/05/27 10:47:21
Done.
|
| + return path.split("/").map(Uri.decodeComponent).toList(growable: false); |
| + } |
| + |
| + /* |
| + * Returns the URI query split into a map according to the rules |
| + * specified for FORM post in the HTML 4.01 specification. Each key |
| + * and value in the returned map have been decoded. If there is no |
| + * query the empty map will be returned. |
| + */ |
| + Map<String, String> get queryParameters { |
| + return query.split("&").fold({}, (map, element) { |
| + int index = element.indexOf("="); |
| + if (index == -1) { |
| + if (!element.isEmpty) map[element] = ""; |
| + } else if (index != 0) { |
| + var key = element.substring(0, index); |
| + var value = element.substring(index + 1, element.length); |
|
floitsch
2013/05/24 20:37:27
substring doesn't need the second argument if it i
Søren Gjesse
2013/05/27 10:47:21
Done.
|
| + map[Uri.decodeQueryComponent(key)] = decodeQueryComponent(value); |
| + } |
| + return map; |
| + }); |
| + } |
| + |
| + static String _makeScheme(String scheme) { |
| + bool isSchemeLowerCharacter(int ch) { |
| + return ch < 128 && |
| + ((_schemeLowerTable[ch >> 4] & (1 << (ch & 0x0f))) != 0); |
| + } |
| + |
| + bool isSchemeCharacter(int ch) { |
| + return ch < 128 && ((_schemeTable[ch >> 4] & (1 << (ch & 0x0f))) != 0); |
| + } |
| + |
| + if (scheme == null) return ""; |
| + bool allLowercase = true; |
| + int length = scheme.length; |
| + for (int i = 0; i < length; i++) { |
| + int codeUnit = scheme.codeUnitAt(i); |
| + if (!isSchemeLowerCharacter(codeUnit)) { |
| + if (isSchemeCharacter(codeUnit)) { |
| + allLowercase = false; |
| + } else { |
| + throw new ArgumentError('Illegal scheme'); |
|
floitsch
2013/05/24 20:37:27
Add the scheme: 'Illegal scheme: $scheme'.
Søren Gjesse
2013/05/27 10:47:21
Done.
|
| + } |
| + } |
| + } |
| + |
| + return allLowercase ? scheme : scheme.toLowerCase(); |
| + } |
| + |
| + static String _makePath(String path, List<String> pathSegments) { |
| + if (path == null && pathSegments == null) return ""; |
| + if (path != null && pathSegments != null) { |
| + throw new ArgumentError('Both path and pathSegments specified'); |
| + } |
| + if (path != null) return _normalize(path); |
| + |
| + return pathSegments.map((s) => _uriEncode(_pathCharTable, s)).join("/"); |
| + } |
| + |
| + static String _makeQuery(String query, Map<String, String> queryParameters) { |
| + if (query == null && queryParameters == null) return ""; |
| + if (query != null && queryParameters != null) { |
| + throw new ArgumentError('Both query and queryParameters specified'); |
| + } |
| + if (query != null) return _normalize(query); |
| + |
| + var result = new StringBuffer(); |
| + var first = true; |
| + queryParameters.forEach((key, value) { |
| + if (!first) { |
| + result.write("&"); |
| + } |
| + first = false; |
| + result.write(Uri.encodeQueryComponent(key)); |
| + if (value != null && !value.isEmpty) { |
| + result.write("="); |
| + result.write(Uri.encodeQueryComponent(value)); |
| + } |
| + }); |
| + return result.toString(); |
| + } |
| + |
| + static String _makeFragment(String fragment) { |
| + if (fragment == null) return ""; |
| + return _normalize(fragment); |
| + } |
| + |
| + static String _normalize(String component) { |
| + bool isNormalizedHexDigit(int digit) { |
| + return (_CharCode.ZERO <= digit && digit <= _CharCode.NINE) || |
| + (_CharCode.UPPER_CASE_A <= digit && digit <= _CharCode.UPPER_CASE_F); |
| + } |
| + |
| + bool isLowerCaseHexDigit(int digit) { |
| + return _CharCode.LOWER_CASE_A <= digit && digit <= _CharCode.LOWER_CASE_F; |
| + } |
| + |
| + bool isUnreserved(int ch) { |
| + return ch < 128 && |
| + ((_unreservedTable[ch >> 4] & (1 << (ch & 0x0f))) != 0); |
| + } |
| + |
| + int normalizeHexDigit(int index) { |
| + var codeUnit = component.codeUnitAt(index); |
| + if (isLowerCaseHexDigit(codeUnit)) { |
| + return codeUnit - 0x20; |
| + } else if (!isNormalizedHexDigit(codeUnit)) { |
| + throw new ArgumentError("Invalid URI component"); |
| + } else { |
| + return codeUnit; |
| + } |
| + } |
| + |
| + int decodeHexDigitPair(int index) { |
| + int byte = 0; |
| + for (int i = 0; i < 2; i++) { |
| + var codeUnit = component.codeUnitAt(index + i); |
| + if (_CharCode.ZERO <= codeUnit && codeUnit <= _CharCode.NINE) { |
| + byte = byte * 16 + codeUnit - _CharCode.ZERO; |
| + } else { |
| + // Check ranges A-F (0x41-0x46) and a-f (0x61-0x66). |
| + codeUnit |= 0x20; |
| + if (_CharCode.LOWER_CASE_A <= codeUnit && |
| + codeUnit <= _CharCode.LOWER_CASE_F) { |
| + byte = byte * 16 + codeUnit - 0x57; |
|
floitsch
2013/05/24 20:37:27
what's 0x57?
Søren Gjesse
2013/05/27 10:47:21
LOWERCASE_A - 10
Change the code to which should
|
| + } else { |
| + throw new ArgumentError("Invalid URI encoding"); |
|
floitsch
2013/05/24 20:37:27
add reason.
Søren Gjesse
2013/05/27 10:47:21
Done.
|
| + } |
| + } |
| + } |
| + return byte; |
| + } |
| + |
| + // Start building the normalized component string. |
| + StringBuffer result; |
| + int length = component.length; |
| + int index = 0; |
| + int prevIndex = 0; |
| + while (index < length) { |
| + |
| + // Copy a part of the component string to the result. |
| + fillResult() { |
| + if (result == null) { |
| + assert(prevIndex == 0); |
| + result = new StringBuffer(component.substring(prevIndex, index)); |
| + } else { |
| + result.write(component.substring(prevIndex, index)); |
| + } |
| + } |
| + |
| + // Normalize percent encoding to uppercase and don't encode |
| + // unreserved characters. |
| + if (component.codeUnitAt(index) == _CharCode.PERCENT) { |
| + if (length < index + 2) { |
| + throw new ArgumentError("Invalid Uri component"); |
|
floitsch
2013/05/24 20:37:27
add reason.
Søren Gjesse
2013/05/27 10:47:21
Done.
|
| + } |
| + |
| + var codeUnit1 = component.codeUnitAt(index + 1); |
| + var codeUnit2 = component.codeUnitAt(index + 2); |
| + var decodedCodeUnit = decodeHexDigitPair(index + 1); |
| + if (isNormalizedHexDigit(codeUnit1) && |
| + isNormalizedHexDigit(codeUnit2) && |
| + !isUnreserved(decodedCodeUnit)) { |
| + index += 3; |
| + } else { |
| + fillResult(); |
| + if (isUnreserved(decodedCodeUnit)) { |
| + result.writeCharCode(decodedCodeUnit); |
| + } else { |
| + result.write("%"); |
| + result.writeCharCode(normalizeHexDigit(index + 1)); |
| + result.writeCharCode(normalizeHexDigit(index + 2)); |
| + } |
| + index += 3; |
| + prevIndex = index; |
| + } |
| + } else { |
| + index++; |
| + } |
| + } |
| + assert(index == length); |
| + |
| + if (result == null) return component; |
| + return result.toString(); |
| + } |
| + |
| + static String _emptyIfNull(String val) => val != null ? val : ''; |
| + |
| + static int _parseIntOrZero(String val) { |
| + if (val != null && val != '') { |
| + return int.parse(val); |
| + } else { |
| + return 0; |
| + } |
| + } |
| + |
| + static String _eitherOf(String val1, String val2) { |
| + if (val1 != null) return val1; |
| + if (val2 != null) return val2; |
| + return ''; |
| + } |
| + |
| + // NOTE: This code was ported from: closure-library/closure/goog/uri/utils.js |
| + static final RegExp _splitRe = new RegExp( |
| + '^' |
| + '(?:' |
| + '([^:/?#.]+)' // scheme - ignore special characters |
| + // used by other URL parts such as :, |
| + // ?, /, #, and . |
| + ':)?' |
| + '(?://' |
| + '(?:([^/?#]*)@)?' // userInfo |
| + '(?:' |
| + r'([\w\d\-\u0100-\uffff.%]*)' |
| + // host - restrict to letters, |
| + // digits, dashes, dots, percent |
| + // escapes, and unicode characters. |
| + '|' |
| + // TODO(ajohnsen): Only allow a max number of parts? |
| + r'\[([A-Fa-f0-9:.]*)\])' |
| + // IPv6 host - restrict to hex, |
| + // dot and colon. |
| + '(?::([0-9]+))?' // port |
| + ')?' |
| + r'([^?#[]+)?' // path |
| + r'(?:\?([^#]*))?' // query |
| + '(?:#(.*))?' // fragment |
| + r'$'); |
| + |
| + static const _COMPONENT_SCHEME = 1; |
| + static const _COMPONENT_USER_INFO = 2; |
| + static const _COMPONENT_HOST = 3; |
| + static const _COMPONENT_HOST_IPV6 = 4; |
| + static const _COMPONENT_PORT = 5; |
| + static const _COMPONENT_PATH = 6; |
| + static const _COMPONENT_QUERY_DATA = 7; |
| + static const _COMPONENT_FRAGMENT = 8; |
| + |
| + /** |
| + * Returns `true` if the URI is absolute. |
| + */ |
| + bool get isAbsolute { |
| + if ("" == scheme) return false; |
| + if ("" != fragment) return false; |
| + return true; |
| + |
| + /* absolute-URI = scheme ":" hier-part [ "?" query ] |
|
floitsch
2013/05/24 20:37:27
why is this comment here?
Søren Gjesse
2013/05/27 10:47:21
It was there in the dart:uri library. Removed it.
|
| + * hier-part = "//" authority path-abempty |
| + * / path-absolute |
| + * / path-rootless |
| + * / path-empty |
| + * |
| + * path = path-abempty ; begins with "/" or is empty |
| + * / path-absolute ; begins with "/" but not "//" |
| + * / path-noscheme ; begins with a non-colon segment |
| + * / path-rootless ; begins with a segment |
| + * / path-empty ; zero characters |
| + * |
| + * path-abempty = *( "/" segment ) |
| + * path-absolute = "/" [ segment-nz *( "/" segment ) ] |
| + * path-noscheme = segment-nz-nc *( "/" segment ) |
| + * path-rootless = segment-nz *( "/" segment ) |
| + * path-empty = 0<pchar> |
| + * segment = *pchar |
| + * segment-nz = 1*pchar |
| + * segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) |
| + * ; non-zero-length segment without any colon ":" |
| + * |
| + * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" |
| + */ |
| + } |
| + |
| + Uri resolve(String uri) { |
| + return resolveUri(Uri.parse(uri)); |
| + } |
| + |
| + Uri resolveUri(Uri reference) { |
| + // From RFC 3986. |
| + String targetScheme; |
| + String targetUserInfo; |
| + String targetHost; |
| + int targetPort; |
| + String targetPath; |
| + String targetQuery; |
| + if (reference.scheme != "") { |
| + targetScheme = reference.scheme; |
| + targetUserInfo = reference.userInfo; |
| + targetHost = reference.host; |
| + targetPort = reference.port; |
| + targetPath = removeDotSegments(reference.path); |
| + targetQuery = reference.query; |
| + } else { |
| + if (reference.hasAuthority) { |
| + targetUserInfo = reference.userInfo; |
| + targetHost = reference.host; |
| + targetPort = reference.port; |
| + targetPath = removeDotSegments(reference.path); |
| + targetQuery = reference.query; |
| + } else { |
| + if (reference.path == "") { |
| + targetPath = this.path; |
| + if (reference.query != "") { |
| + targetQuery = reference.query; |
| + } else { |
| + targetQuery = this.query; |
| + } |
| + } else { |
| + if (reference.path.startsWith("/")) { |
| + targetPath = removeDotSegments(reference.path); |
| + } else { |
| + targetPath = removeDotSegments(merge(this.path, reference.path)); |
| + } |
| + targetQuery = reference.query; |
| + } |
| + targetUserInfo = this.userInfo; |
| + targetHost = this.host; |
| + targetPort = this.port; |
| + } |
| + targetScheme = this.scheme; |
| + } |
| + return new Uri(scheme: targetScheme, |
| + userInfo: targetUserInfo, |
|
floitsch
2013/05/24 20:37:27
indentation.
Søren Gjesse
2013/05/27 10:47:21
Done.
|
| + host: targetHost, |
| + port: targetPort, |
| + path: targetPath, |
| + query: targetQuery, |
| + fragment: reference.fragment); |
| + } |
| + |
| + bool get hasAuthority { |
| + return (userInfo != "") || (host != "") || (port != 0); |
|
floitsch
2013/05/24 20:37:27
why is the authority linked to a non-zery port?
Søren Gjesse
2013/05/27 10:47:21
Good catch!
An authority always have a host. Chan
|
| + } |
| + |
| + /** |
| + * For http/https schemes returns URI's [origin][] - scheme://host:port. |
|
floitsch
2013/05/24 20:37:27
Returns the URI's [origin][] for http/https scheme
Søren Gjesse
2013/05/27 10:47:21
Done.
|
| + * For all other schemes throws StateError. |
| + * [origin]: http://www.w3.org/TR/2011/WD-html5-20110405/origin-0.html#origin |
| + */ |
| + String get origin { |
| + if (scheme == "") { |
| + throw new StateError("Cannot use origin without a scheme"); |
|
floitsch
2013/05/24 20:37:27
give information: "Cannot use origin without a sch
Søren Gjesse
2013/05/27 10:47:21
Done.
|
| + } |
| + if (scheme != "http" && scheme != "https") { |
| + throw new StateError( |
| + "origin is applicable to http/https schemes only. Not \'$scheme\'"); |
| + } |
| + StringBuffer sb = new StringBuffer(); |
| + sb.write(scheme); |
| + sb.write(":"); |
| + if (host == null || host == "") { |
|
floitsch
2013/05/24 20:37:27
Move check up to the checks.
Søren Gjesse
2013/05/27 10:47:21
Done.
|
| + throw new StateError("Cannot use origin without a host"); |
| + } |
| + |
| + sb.write("//"); |
| + sb.write(host); |
| + if (port != 0) { |
| + sb.write(":"); |
| + sb.write(port); |
| + } |
| + return sb.toString(); |
|
floitsch
2013/05/24 20:37:27
No need for StringBuffers.
if (port == 0) return "
Søren Gjesse
2013/05/27 10:47:21
Good point. Changed.
|
| + } |
| + |
| + String toString() { |
| + StringBuffer sb = new StringBuffer(); |
| + _addIfNonEmpty(sb, scheme, scheme, ':'); |
| + if (hasAuthority || (scheme == "file")) { |
| + sb.write("//"); |
| + _addIfNonEmpty(sb, userInfo, userInfo, "@"); |
| + sb.write(host == null ? "null" : |
| + host.contains(':') ? '[$host]' : host); |
| + if (port != 0) { |
| + sb.write(":"); |
| + sb.write(port.toString()); |
| + } |
| + } |
| + sb.write(path == null ? "null" : path); |
|
floitsch
2013/05/24 20:37:27
isn't the path null-check redundant?
Søren Gjesse
2013/05/27 10:47:21
It is - removed.
|
| + _addIfNonEmpty(sb, query, "?", query); |
| + _addIfNonEmpty(sb, fragment, "#", fragment); |
| + return sb.toString(); |
| + } |
| + |
| + bool operator==(other) { |
| + if (other is! Uri) return false; |
| + Uri uri = other; |
| + return scheme == uri.scheme && |
| + userInfo == uri.userInfo && |
| + host == uri.host && |
| + port == uri.port && |
| + path == uri.path && |
| + query == uri.query && |
| + fragment == uri.fragment; |
| + } |
| + |
| + int get hashCode { |
| + int combine(part, current) { |
| + // The sum is truncated to 30 bits to make sure it fits into a Smi. |
| + return (current * 31 + part.hashCode) & 0x3FFFFFFF; |
| + } |
| + return combine(scheme, combine(userInfo, combine(host, combine(port, |
| + combine(path, combine(query, combine(fragment, 1))))))); |
| + } |
| + |
| + static void _addIfNonEmpty(StringBuffer sb, String test, |
| + String first, String second) { |
| + if ("" != test) { |
| + sb.write(first == null ? "null" : first); |
| + sb.write(second == null ? "null" : second); |
| + } |
| + } |
| + |
| + /** |
| + * A javaScript-like URI component encoder, this encodes a URI |
| + * [component] by replacing each instance of certain characters by |
|
floitsch
2013/05/24 20:37:27
If the set of characters is fixed, add it at the e
Søren Gjesse
2013/05/27 10:47:21
Done.
|
| + * one, two, three, or four escape sequences representing the UTF-8 |
| + * encoding of the character (will only be four escape sequences for |
| + * characters composed of two "surrogate" characters). To avoid |
| + * unexpected requests to the server, you should call |
| + * Uri.encodeComponent on any user-entered parameters that will be |
| + * passed as part of a URI. |
| + * |
| + * For encoding the query part consider using |
| + * [encodeQueryComponent]. |
| + * |
| + * When manually encoding path segments or query components remember |
| + * to encode each part separately before building the path or query |
| + * string. |
| + * |
| + * To avoid the need for explicitly encoding use the [pathSegments] |
| + * and [queryParameters] optional named arguments when constructing |
| + * a Uri. |
| + */ |
| + static String encodeComponent(String component) { |
| + return _uriEncode(_unreserved2396Table, component); |
| + } |
| + |
| + /* |
| + * Encode [component] according to the HTML 4.01 rules for encoding |
| + * the posting of a HTML form as a query string component. Spaces |
| + * will be replaced with plus and all characters except for |
| + * uppercase and lowercase letters, decimal digits, hyphen, period, |
| + * underscore and tilde will be encoded. Note that the set of |
| + * characters encoded is a superset of what HTML 4.01 says as it |
| + * refers to RFC 1738 for reserved characters. |
| + * |
| + * When manually encoding query components remember to encode each |
| + * part separately before building the query string. |
| + * |
| + * See http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2 for more |
|
floitsch
2013/05/24 20:37:27
I think there is a way to make this a link.
Søren Gjesse
2013/05/27 10:47:21
I will postpone the adding of proper links to a se
|
| + * details. |
| + */ |
| + static String encodeQueryComponent(String component) { |
| + return _uriEncode(_unreservedTable, component, spaceToPlus: true); |
| + } |
| + |
| + /** |
| + * A javaScript-like URI component decoder. |
|
floitsch
2013/05/24 20:37:27
Is it JavaScript like, or more general? In other w
Søren Gjesse
2013/05/27 10:47:21
Rephrased the comment and explained more precisely
|
| + */ |
| + static String decodeComponent(String encodedComponent) { |
| + return _uriDecode(encodedComponent); |
| + } |
| + |
| + static String decodeQueryComponent(String encodedComponent) { |
| + return _uriDecode(encodedComponent, plusToSpace: true); |
| + } |
| + |
| + /** |
| + * A JavaScript-like URI encoder. Encodes a full URI string by |
| + * replacing each instance of certain characters by one, two, three, |
| + * or four escape sequences representing the UTF-8 encoding of the |
| + * character (will only be four escape sequences for characters |
| + * composed of two "surrogate" characters). This assumes that [uri] |
| + * is a complete URI, so does not encode reserved characters that |
| + * have special meaning in the URI: [:#;,/?:@&=+\$:] It returns the |
|
floitsch
2013/05/24 20:37:27
nit (personal preference): `#;,/?:@&=+\$`.
Søren Gjesse
2013/05/27 10:47:21
Done.
|
| + * escaped URI. |
| + */ |
| + static String encodeFull(String uri) { |
| + return _uriEncode(_encodeFullTable, uri); |
| + } |
| + |
| + /** |
| + * A JavaScript-like URI decoder. Decodes a full URI string. This is |
|
floitsch
2013/05/24 20:37:27
Start with what it does: "Decodes ...".
Make one s
Søren Gjesse
2013/05/27 10:47:21
Rephrased the comment and explained more precisely
|
| + * an implementation of JavaScript's decodeURIComponent function. |
| + * Decodes a Uniform Resource Identifier [uri] previously created by |
| + * encodeURI or by a similar routine. It replaces each escape sequence |
| + * in [uri] with the character that it represents. |
| + */ |
| + static String decodeFull(String uri) { |
| + return _uriDecode(uri); |
| + } |
| + |
| + // Tables of char-codes organized as a bit vector of 128 bits where |
| + // each bit indicate whether a character code on the 0-127 needs to |
| + // be escaped or not. |
| + |
| + // The unreserved characters of RFC 3986. |
| + static const _unreservedTable = const [ |
| + // LSB MSB |
| + // | | |
| + 0x0000, // 0x00 - 0x0f 0000000000000000 |
| + 0x0000, // 0x10 - 0x1f 0000000000000000 |
| + // -. |
| + 0x6000, // 0x20 - 0x2f 0000000000000110 |
| + // 0123456789 |
| + 0x03ff, // 0x30 - 0x3f 1111111111000000 |
| + // ABCDEFGHIJKLMNO |
| + 0xfffe, // 0x40 - 0x4f 0111111111111111 |
| + // PQRSTUVWXYZ _ |
| + 0x87ff, // 0x50 - 0x5f 1111111111100001 |
| + // abcdefghijklmno |
| + 0xfffe, // 0x60 - 0x6f 0111111111111111 |
| + // pqrstuvwxyz ~ |
| + 0x47ff]; // 0x70 - 0x7f 1111111111100010 |
| + |
| + // The unreserved characters of RFC 2396. |
| + static const _unreserved2396Table = const [ |
| + // LSB MSB |
| + // | | |
| + 0x0000, // 0x00 - 0x0f 0000000000000000 |
| + 0x0000, // 0x10 - 0x1f 0000000000000000 |
| + // ! '()* -. |
| + 0x6782, // 0x20 - 0x2f 0100000111100110 |
| + // 0123456789 |
| + 0x03ff, // 0x30 - 0x3f 1111111111000000 |
| + // ABCDEFGHIJKLMNO |
| + 0xfffe, // 0x40 - 0x4f 0111111111111111 |
| + // PQRSTUVWXYZ _ |
| + 0x87ff, // 0x50 - 0x5f 1111111111100001 |
| + // abcdefghijklmno |
| + 0xfffe, // 0x60 - 0x6f 0111111111111111 |
| + // pqrstuvwxyz ~ |
| + 0x47ff]; // 0x70 - 0x7f 1111111111100010 |
| + |
| + // Table of reserved characters specified by ECMAScript 5. |
| + static const _encodeFullTable = const [ |
| + // LSB MSB |
| + // | | |
| + 0x0000, // 0x00 - 0x0f 0000000000000000 |
| + 0x0000, // 0x10 - 0x1f 0000000000000000 |
| + // ! #$ &'()*+,-./ |
| + 0xf7da, // 0x20 - 0x2f 0101101111101111 |
| + // 0123456789:; = ? |
| + 0xafff, // 0x30 - 0x3f 1111111111110101 |
| + // @ABCDEFGHIJKLMNO |
| + 0xffff, // 0x40 - 0x4f 1111111111111111 |
| + // PQRSTUVWXYZ _ |
| + 0x87ff, // 0x50 - 0x5f 1111111111100001 |
| + // abcdefghijklmno |
| + 0xfffe, // 0x60 - 0x6f 0111111111111111 |
| + // pqrstuvwxyz ~ |
| + 0x47ff]; // 0x70 - 0x7f 1111111111100010 |
| + |
| + // Characters allowed in the scheme. |
| + static const _schemeTable = const [ |
| + // LSB MSB |
| + // | | |
| + 0x0000, // 0x00 - 0x0f 0000000000000000 |
| + 0x0000, // 0x10 - 0x1f 0000000000000000 |
| + // + -. |
| + 0x6800, // 0x20 - 0x2f 0000000000010110 |
| + // 0123456789 |
| + 0x03ff, // 0x30 - 0x3f 1111111111000000 |
| + // ABCDEFGHIJKLMNO |
| + 0xfffe, // 0x40 - 0x4f 0111111111111111 |
| + // PQRSTUVWXYZ |
| + 0x07ff, // 0x50 - 0x5f 1111111111100001 |
| + // abcdefghijklmno |
| + 0xfffe, // 0x60 - 0x6f 0111111111111111 |
| + // pqrstuvwxyz |
| + 0x07ff]; // 0x70 - 0x7f 1111111111100010 |
| + |
| + // Characters allowed in scheme except for upper case letters. |
| + static const _schemeLowerTable = const [ |
| + // LSB MSB |
| + // | | |
| + 0x0000, // 0x00 - 0x0f 0000000000000000 |
| + 0x0000, // 0x10 - 0x1f 0000000000000000 |
| + // + -. |
| + 0x6800, // 0x20 - 0x2f 0000000000010110 |
| + // 0123456789 |
| + 0x03ff, // 0x30 - 0x3f 1111111111000000 |
| + // |
| + 0x0000, // 0x40 - 0x4f 0111111111111111 |
| + // |
| + 0x0000, // 0x50 - 0x5f 1111111111100001 |
| + // abcdefghijklmno |
| + 0xfffe, // 0x60 - 0x6f 0111111111111111 |
| + // pqrstuvwxyz |
| + 0x07ff]; // 0x70 - 0x7f 1111111111100010 |
| + |
| + // Sub delimiter characters combined with unreserved as of 3986. |
| + // sub-delims = "!" / "$" / "&" / "'" / "(" / ")" |
| + // / "*" / "+" / "," / ";" / "=" |
| + // RFC 3986 section 2.3. |
| + // unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" |
| + static const _subDelimitersTable = const [ |
| + // LSB MSB |
| + // | | |
| + 0x0000, // 0x00 - 0x0f 0000000000000000 |
| + 0x0000, // 0x10 - 0x1f 0000000000000000 |
| + // ! $ &'()*+,-. |
| + 0x7fd2, // 0x20 - 0x2f 0100101111111110 |
| + // 0123456789 ; = |
| + 0x2bff, // 0x30 - 0x3f 1111111111010100 |
| + // ABCDEFGHIJKLMNO |
| + 0xfffe, // 0x40 - 0x4f 0111111111111111 |
| + // PQRSTUVWXYZ _ |
| + 0x87ff, // 0x50 - 0x5f 1111111111100001 |
| + // abcdefghijklmno |
| + 0xfffe, // 0x60 - 0x6f 0111111111111111 |
| + // pqrstuvwxyz ~ |
| + 0x47ff]; // 0x70 - 0x7f 1111111111100010 |
| + |
| + // Characters allowed in the path as of RFC 3986. |
| + // RFC 3986 section 3.3. |
| + // pchar = unreserved / pct-encoded / sub-delims / ":" / "@" |
| + static const _pathCharTable = const [ |
| + // LSB MSB |
| + // | | |
| + 0x0000, // 0x00 - 0x0f 0000000000000000 |
| + 0x0000, // 0x10 - 0x1f 0000000000000000 |
| + // ! $ &'()*+,-. |
| + 0x7fd2, // 0x20 - 0x2f 0100101111111110 |
| + // 0123456789:; = |
| + 0x2fff, // 0x30 - 0x3f 1111111111110100 |
| + // @ABCDEFGHIJKLMNO |
| + 0xffff, // 0x40 - 0x4f 1111111111111111 |
| + // PQRSTUVWXYZ _ |
| + 0x87ff, // 0x50 - 0x5f 1111111111100001 |
| + // abcdefghijklmno |
| + 0xfffe, // 0x60 - 0x6f 0111111111111111 |
| + // pqrstuvwxyz ~ |
| + 0x47ff]; // 0x70 - 0x7f 1111111111100010 |
| + |
| + // Characters allowed in the query as of RFC 3986. |
| + // RFC 3986 section 3.4. |
| + // query = *( pchar / "/" / "?" ) |
| + static const _queryCharTable = const [ |
| + // LSB MSB |
| + // | | |
| + 0x0000, // 0x00 - 0x0f 0000000000000000 |
| + 0x0000, // 0x10 - 0x1f 0000000000000000 |
| + // ! $ &'()*+,-./ |
| + 0xffd2, // 0x20 - 0x2f 0100101111111111 |
| + // 0123456789:; = ? |
| + 0xafff, // 0x30 - 0x3f 1111111111110101 |
| + // @ABCDEFGHIJKLMNO |
| + 0xffff, // 0x40 - 0x4f 1111111111111111 |
| + // PQRSTUVWXYZ _ |
| + 0x87ff, // 0x50 - 0x5f 1111111111100001 |
| + // abcdefghijklmno |
| + 0xfffe, // 0x60 - 0x6f 0111111111111111 |
| + // pqrstuvwxyz ~ |
| + 0x47ff]; // 0x70 - 0x7f 1111111111100010 |
| +} |