aboutsummaryrefslogtreecommitdiff
path: root/node_modules/sanitizer/lib/uri.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/sanitizer/lib/uri.js')
-rw-r--r--node_modules/sanitizer/lib/uri.js752
1 files changed, 752 insertions, 0 deletions
diff --git a/node_modules/sanitizer/lib/uri.js b/node_modules/sanitizer/lib/uri.js
new file mode 100644
index 0000000..8b64dc6
--- /dev/null
+++ b/node_modules/sanitizer/lib/uri.js
@@ -0,0 +1,752 @@
+// Copyright (C) 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * @fileoverview
+ * Implements RFC 3986 for parsing/formatting URIs.
+ *
+ * @author mikesamuel@gmail.com
+ * \@provides URI
+ * \@overrides window
+ */
+
+var URI = (function () {
+
+ /**
+ * creates a uri from the string form. The parser is relaxed, so special
+ * characters that aren't escaped but don't cause ambiguities will not cause
+ * parse failures.
+ *
+ * @return {URI|null}
+ */
+ function parse(uriStr) {
+ var m = ('' + uriStr).match(URI_RE_);
+ if (!m) { return null; }
+ return new URI(
+ nullIfAbsent(m[1]),
+ nullIfAbsent(m[2]),
+ nullIfAbsent(m[3]),
+ nullIfAbsent(m[4]),
+ nullIfAbsent(m[5]),
+ nullIfAbsent(m[6]),
+ nullIfAbsent(m[7]));
+ }
+
+
+ /**
+ * creates a uri from the given parts.
+ *
+ * @param scheme {string} an unencoded scheme such as "http" or null
+ * @param credentials {string} unencoded user credentials or null
+ * @param domain {string} an unencoded domain name or null
+ * @param port {number} a port number in [1, 32768].
+ * -1 indicates no port, as does null.
+ * @param path {string} an unencoded path
+ * @param query {Array.<string>|string|null} a list of unencoded cgi
+ * parameters where even values are keys and odds the corresponding values
+ * or an unencoded query.
+ * @param fragment {string} an unencoded fragment without the "#" or null.
+ * @return {URI}
+ */
+ function create(scheme, credentials, domain, port, path, query, fragment) {
+ var uri = new URI(
+ encodeIfExists2(scheme, URI_DISALLOWED_IN_SCHEME_OR_CREDENTIALS_),
+ encodeIfExists2(
+ credentials, URI_DISALLOWED_IN_SCHEME_OR_CREDENTIALS_),
+ encodeIfExists(domain),
+ port > 0 ? port.toString() : null,
+ encodeIfExists2(path, URI_DISALLOWED_IN_PATH_),
+ null,
+ encodeIfExists(fragment));
+ if (query) {
+ if ('string' === typeof query) {
+ uri.setRawQuery(query.replace(/[^?&=0-9A-Za-z_\-~.%]/g, encodeOne));
+ } else {
+ uri.setAllParameters(query);
+ }
+ }
+ return uri;
+ }
+ function encodeIfExists(unescapedPart) {
+ if ('string' == typeof unescapedPart) {
+ return encodeURIComponent(unescapedPart);
+ }
+ return null;
+ };
+ /**
+ * if unescapedPart is non null, then escapes any characters in it that aren't
+ * valid characters in a url and also escapes any special characters that
+ * appear in extra.
+ *
+ * @param unescapedPart {string}
+ * @param extra {RegExp} a character set of characters in [\01-\177].
+ * @return {string|null} null iff unescapedPart == null.
+ */
+ function encodeIfExists2(unescapedPart, extra) {
+ if ('string' == typeof unescapedPart) {
+ return encodeURI(unescapedPart).replace(extra, encodeOne);
+ }
+ return null;
+ };
+ /** converts a character in [\01-\177] to its url encoded equivalent. */
+ function encodeOne(ch) {
+ var n = ch.charCodeAt(0);
+ return '%' + '0123456789ABCDEF'.charAt((n >> 4) & 0xf) +
+ '0123456789ABCDEF'.charAt(n & 0xf);
+ }
+
+ /**
+ * {@updoc
+ * $ normPath('foo/./bar')
+ * # 'foo/bar'
+ * $ normPath('./foo')
+ * # 'foo'
+ * $ normPath('foo/.')
+ * # 'foo'
+ * $ normPath('foo//bar')
+ * # 'foo/bar'
+ * }
+ */
+ function normPath(path) {
+ return path.replace(/(^|\/)\.(?:\/|$)/g, '$1').replace(/\/{2,}/g, '/');
+ }
+
+ var PARENT_DIRECTORY_HANDLER = new RegExp(
+ ''
+ // A path break
+ + '(/|^)'
+ // followed by a non .. path element
+ // (cannot be . because normPath is used prior to this RegExp)
+ + '(?:[^./][^/]*|\\.{2,}(?:[^./][^/]*)|\\.{3,}[^/]*)'
+ // followed by .. followed by a path break.
+ + '/\\.\\.(?:/|$)');
+
+ var PARENT_DIRECTORY_HANDLER_RE = new RegExp(PARENT_DIRECTORY_HANDLER);
+
+ var EXTRA_PARENT_PATHS_RE = /^(?:\.\.\/)*(?:\.\.$)?/;
+
+ /**
+ * Normalizes its input path and collapses all . and .. sequences except for
+ * .. sequences that would take it above the root of the current parent
+ * directory.
+ * {@updoc
+ * $ collapse_dots('foo/../bar')
+ * # 'bar'
+ * $ collapse_dots('foo/./bar')
+ * # 'foo/bar'
+ * $ collapse_dots('foo/../bar/./../../baz')
+ * # 'baz'
+ * $ collapse_dots('../foo')
+ * # '../foo'
+ * $ collapse_dots('../foo').replace(EXTRA_PARENT_PATHS_RE, '')
+ * # 'foo'
+ * }
+ */
+ function collapse_dots(path) {
+ if (path === null) { return null; }
+ var p = normPath(path);
+ // Only /../ left to flatten
+ var r = PARENT_DIRECTORY_HANDLER_RE;
+ // We replace with $1 which matches a / before the .. because this
+ // guarantees that:
+ // (1) we have at most 1 / between the adjacent place,
+ // (2) always have a slash if there is a preceding path section, and
+ // (3) we never turn a relative path into an absolute path.
+ for (var q; (q = p.replace(r, '$1')) != p; p = q) {};
+ return p;
+ }
+
+ /**
+ * resolves a relative url string to a base uri.
+ * @return {URI}
+ */
+ function resolve(baseUri, relativeUri) {
+ // there are several kinds of relative urls:
+ // 1. //foo - replaces everything from the domain on. foo is a domain name
+ // 2. foo - replaces the last part of the path, the whole query and fragment
+ // 3. /foo - replaces the the path, the query and fragment
+ // 4. ?foo - replace the query and fragment
+ // 5. #foo - replace the fragment only
+
+ var absoluteUri = baseUri.clone();
+ // we satisfy these conditions by looking for the first part of relativeUri
+ // that is not blank and applying defaults to the rest
+
+ var overridden = relativeUri.hasScheme();
+
+ if (overridden) {
+ absoluteUri.setRawScheme(relativeUri.getRawScheme());
+ } else {
+ overridden = relativeUri.hasCredentials();
+ }
+
+ if (overridden) {
+ absoluteUri.setRawCredentials(relativeUri.getRawCredentials());
+ } else {
+ overridden = relativeUri.hasDomain();
+ }
+
+ if (overridden) {
+ absoluteUri.setRawDomain(relativeUri.getRawDomain());
+ } else {
+ overridden = relativeUri.hasPort();
+ }
+
+ var rawPath = relativeUri.getRawPath();
+ var simplifiedPath = collapse_dots(rawPath);
+ if (overridden) {
+ absoluteUri.setPort(relativeUri.getPort());
+ simplifiedPath = simplifiedPath
+ && simplifiedPath.replace(EXTRA_PARENT_PATHS_RE, '');
+ } else {
+ overridden = !!rawPath;
+ if (overridden) {
+ // resolve path properly
+ if (simplifiedPath.charCodeAt(0) !== 0x2f /* / */) { // path is relative
+ var absRawPath = collapse_dots(absoluteUri.getRawPath() || '')
+ .replace(EXTRA_PARENT_PATHS_RE, '');
+ var slash = absRawPath.lastIndexOf('/') + 1;
+ simplifiedPath = collapse_dots(
+ (slash ? absRawPath.substring(0, slash) : '')
+ + collapse_dots(rawPath))
+ .replace(EXTRA_PARENT_PATHS_RE, '');
+ }
+ } else {
+ simplifiedPath = simplifiedPath
+ && simplifiedPath.replace(EXTRA_PARENT_PATHS_RE, '');
+ if (simplifiedPath !== rawPath) {
+ absoluteUri.setRawPath(simplifiedPath);
+ }
+ }
+ }
+
+ if (overridden) {
+ absoluteUri.setRawPath(simplifiedPath);
+ } else {
+ overridden = relativeUri.hasQuery();
+ }
+
+ if (overridden) {
+ absoluteUri.setRawQuery(relativeUri.getRawQuery());
+ } else {
+ overridden = relativeUri.hasFragment();
+ }
+
+ if (overridden) {
+ absoluteUri.setRawFragment(relativeUri.getRawFragment());
+ }
+
+ return absoluteUri;
+ }
+
+ /**
+ * a mutable URI.
+ *
+ * This class contains setters and getters for the parts of the URI.
+ * The <tt>getXYZ</tt>/<tt>setXYZ</tt> methods return the decoded part -- so
+ * <code>uri.parse('/foo%20bar').getPath()</code> will return the decoded path,
+ * <tt>/foo bar</tt>.
+ *
+ * <p>The raw versions of fields are available too.
+ * <code>uri.parse('/foo%20bar').getRawPath()</code> will return the raw path,
+ * <tt>/foo%20bar</tt>. Use the raw setters with care, since
+ * <code>URI::toString</code> is not guaranteed to return a valid url if a
+ * raw setter was used.
+ *
+ * <p>All setters return <tt>this</tt> and so may be chained, a la
+ * <code>uri.parse('/foo').setFragment('part').toString()</code>.
+ *
+ * <p>You should not use this constructor directly -- please prefer the factory
+ * functions {@link uri.parse}, {@link uri.create}, {@link uri.resolve}
+ * instead.</p>
+ *
+ * <p>The parameters are all raw (assumed to be properly escaped) parts, and
+ * any (but not all) may be null. Undefined is not allowed.</p>
+ *
+ * @constructor
+ */
+ function URI(
+ rawScheme,
+ rawCredentials, rawDomain, port,
+ rawPath, rawQuery, rawFragment) {
+ this.scheme_ = rawScheme;
+ this.credentials_ = rawCredentials;
+ this.domain_ = rawDomain;
+ this.port_ = port;
+ this.path_ = rawPath;
+ this.query_ = rawQuery;
+ this.fragment_ = rawFragment;
+ /**
+ * @type {Array|null}
+ */
+ this.paramCache_ = null;
+ }
+
+ /** returns the string form of the url. */
+ URI.prototype.toString = function () {
+ var out = [];
+ if (null !== this.scheme_) { out.push(this.scheme_, ':'); }
+ if (null !== this.domain_) {
+ out.push('//');
+ if (null !== this.credentials_) { out.push(this.credentials_, '@'); }
+ out.push(this.domain_);
+ if (null !== this.port_) { out.push(':', this.port_.toString()); }
+ }
+ if (null !== this.path_) { out.push(this.path_); }
+ if (null !== this.query_) { out.push('?', this.query_); }
+ if (null !== this.fragment_) { out.push('#', this.fragment_); }
+ return out.join('');
+ };
+
+ URI.prototype.clone = function () {
+ return new URI(this.scheme_, this.credentials_, this.domain_, this.port_,
+ this.path_, this.query_, this.fragment_);
+ };
+
+ URI.prototype.getScheme = function () {
+ // HTML5 spec does not require the scheme to be lowercased but
+ // all common browsers except Safari lowercase the scheme.
+ return this.scheme_ && decodeURIComponent(this.scheme_).toLowerCase();
+ };
+ URI.prototype.getRawScheme = function () {
+ return this.scheme_;
+ };
+ URI.prototype.setScheme = function (newScheme) {
+ this.scheme_ = encodeIfExists2(
+ newScheme, URI_DISALLOWED_IN_SCHEME_OR_CREDENTIALS_);
+ return this;
+ };
+ URI.prototype.setRawScheme = function (newScheme) {
+ this.scheme_ = newScheme ? newScheme : null;
+ return this;
+ };
+ URI.prototype.hasScheme = function () {
+ return null !== this.scheme_;
+ };
+
+
+ URI.prototype.getCredentials = function () {
+ return this.credentials_ && decodeURIComponent(this.credentials_);
+ };
+ URI.prototype.getRawCredentials = function () {
+ return this.credentials_;
+ };
+ URI.prototype.setCredentials = function (newCredentials) {
+ this.credentials_ = encodeIfExists2(
+ newCredentials, URI_DISALLOWED_IN_SCHEME_OR_CREDENTIALS_);
+
+ return this;
+ };
+ URI.prototype.setRawCredentials = function (newCredentials) {
+ this.credentials_ = newCredentials ? newCredentials : null;
+ return this;
+ };
+ URI.prototype.hasCredentials = function () {
+ return null !== this.credentials_;
+ };
+
+
+ URI.prototype.getDomain = function () {
+ return this.domain_ && decodeURIComponent(this.domain_);
+ };
+ URI.prototype.getRawDomain = function () {
+ return this.domain_;
+ };
+ URI.prototype.setDomain = function (newDomain) {
+ return this.setRawDomain(newDomain && encodeURIComponent(newDomain));
+ };
+ URI.prototype.setRawDomain = function (newDomain) {
+ this.domain_ = newDomain ? newDomain : null;
+ // Maintain the invariant that paths must start with a slash when the URI
+ // is not path-relative.
+ return this.setRawPath(this.path_);
+ };
+ URI.prototype.hasDomain = function () {
+ return null !== this.domain_;
+ };
+
+
+ URI.prototype.getPort = function () {
+ return this.port_ && decodeURIComponent(this.port_);
+ };
+ URI.prototype.setPort = function (newPort) {
+ if (newPort) {
+ newPort = Number(newPort);
+ if (newPort !== (newPort & 0xffff)) {
+ throw new Error('Bad port number ' + newPort);
+ }
+ this.port_ = '' + newPort;
+ } else {
+ this.port_ = null;
+ }
+ return this;
+ };
+ URI.prototype.hasPort = function () {
+ return null !== this.port_;
+ };
+
+
+ URI.prototype.getPath = function () {
+ return this.path_ && decodeURIComponent(this.path_);
+ };
+ URI.prototype.getRawPath = function () {
+ return this.path_;
+ };
+ URI.prototype.setPath = function (newPath) {
+ return this.setRawPath(encodeIfExists2(newPath, URI_DISALLOWED_IN_PATH_));
+ };
+ URI.prototype.setRawPath = function (newPath) {
+ if (newPath) {
+ newPath = String(newPath);
+ this.path_ =
+ // Paths must start with '/' unless this is a path-relative URL.
+ (!this.domain_ || /^\//.test(newPath)) ? newPath : '/' + newPath;
+ } else {
+ this.path_ = null;
+ }
+ return this;
+ };
+ URI.prototype.hasPath = function () {
+ return null !== this.path_;
+ };
+
+
+ URI.prototype.getQuery = function () {
+ // From http://www.w3.org/Addressing/URL/4_URI_Recommentations.html
+ // Within the query string, the plus sign is reserved as shorthand notation
+ // for a space.
+ return this.query_ && decodeURIComponent(this.query_).replace(/\+/g, ' ');
+ };
+ URI.prototype.getRawQuery = function () {
+ return this.query_;
+ };
+ URI.prototype.setQuery = function (newQuery) {
+ this.paramCache_ = null;
+ this.query_ = encodeIfExists(newQuery);
+ return this;
+ };
+ URI.prototype.setRawQuery = function (newQuery) {
+ this.paramCache_ = null;
+ this.query_ = newQuery ? newQuery : null;
+ return this;
+ };
+ URI.prototype.hasQuery = function () {
+ return null !== this.query_;
+ };
+
+ /**
+ * sets the query given a list of strings of the form
+ * [ key0, value0, key1, value1, ... ].
+ *
+ * <p><code>uri.setAllParameters(['a', 'b', 'c', 'd']).getQuery()</code>
+ * will yield <code>'a=b&c=d'</code>.
+ */
+ URI.prototype.setAllParameters = function (params) {
+ if (typeof params === 'object') {
+ if (!(params instanceof Array)
+ && (params instanceof Object
+ || Object.prototype.toString.call(params) !== '[object Array]')) {
+ var newParams = [];
+ var i = -1;
+ for (var k in params) {
+ var v = params[k];
+ if ('string' === typeof v) {
+ newParams[++i] = k;
+ newParams[++i] = v;
+ }
+ }
+ params = newParams;
+ }
+ }
+ this.paramCache_ = null;
+ var queryBuf = [];
+ var separator = '';
+ for (var j = 0; j < params.length;) {
+ var k = params[j++];
+ var v = params[j++];
+ queryBuf.push(separator, encodeURIComponent(k.toString()));
+ separator = '&';
+ if (v) {
+ queryBuf.push('=', encodeURIComponent(v.toString()));
+ }
+ }
+ this.query_ = queryBuf.join('');
+ return this;
+ };
+ URI.prototype.checkParameterCache_ = function () {
+ if (!this.paramCache_) {
+ var q = this.query_;
+ if (!q) {
+ this.paramCache_ = [];
+ } else {
+ var cgiParams = q.split(/[&\?]/);
+ var out = [];
+ var k = -1;
+ for (var i = 0; i < cgiParams.length; ++i) {
+ var m = cgiParams[i].match(/^([^=]*)(?:=(.*))?$/);
+ // From http://www.w3.org/Addressing/URL/4_URI_Recommentations.html
+ // Within the query string, the plus sign is reserved as shorthand
+ // notation for a space.
+ out[++k] = decodeURIComponent(m[1]).replace(/\+/g, ' ');
+ out[++k] = decodeURIComponent(m[2] || '').replace(/\+/g, ' ');
+ }
+ this.paramCache_ = out;
+ }
+ }
+ };
+ /**
+ * sets the values of the named cgi parameters.
+ *
+ * <p>So, <code>uri.parse('foo?a=b&c=d&e=f').setParameterValues('c', ['new'])
+ * </code> yields <tt>foo?a=b&c=new&e=f</tt>.</p>
+ *
+ * @param key {string}
+ * @param values {Array.<string>} the new values. If values is a single string
+ * then it will be treated as the sole value.
+ */
+ URI.prototype.setParameterValues = function (key, values) {
+ // be nice and avoid subtle bugs where [] operator on string performs charAt
+ // on some browsers and crashes on IE
+ if (typeof values === 'string') {
+ values = [ values ];
+ }
+
+ this.checkParameterCache_();
+ var newValueIndex = 0;
+ var pc = this.paramCache_;
+ var params = [];
+ for (var i = 0, k = 0; i < pc.length; i += 2) {
+ if (key === pc[i]) {
+ if (newValueIndex < values.length) {
+ params.push(key, values[newValueIndex++]);
+ }
+ } else {
+ params.push(pc[i], pc[i + 1]);
+ }
+ }
+ while (newValueIndex < values.length) {
+ params.push(key, values[newValueIndex++]);
+ }
+ this.setAllParameters(params);
+ return this;
+ };
+ URI.prototype.removeParameter = function (key) {
+ return this.setParameterValues(key, []);
+ };
+ /**
+ * returns the parameters specified in the query part of the uri as a list of
+ * keys and values like [ key0, value0, key1, value1, ... ].
+ *
+ * @return {Array.<string>}
+ */
+ URI.prototype.getAllParameters = function () {
+ this.checkParameterCache_();
+ return this.paramCache_.slice(0, this.paramCache_.length);
+ };
+ /**
+ * returns the value<b>s</b> for a given cgi parameter as a list of decoded
+ * query parameter values.
+ * @return {Array.<string>}
+ */
+ URI.prototype.getParameterValues = function (paramNameUnescaped) {
+ this.checkParameterCache_();
+ var values = [];
+ for (var i = 0; i < this.paramCache_.length; i += 2) {
+ if (paramNameUnescaped === this.paramCache_[i]) {
+ values.push(this.paramCache_[i + 1]);
+ }
+ }
+ return values;
+ };
+ /**
+ * returns a map of cgi parameter names to (non-empty) lists of values.
+ * @return {Object.<string,Array.<string>>}
+ */
+ URI.prototype.getParameterMap = function (paramNameUnescaped) {
+ this.checkParameterCache_();
+ var paramMap = {};
+ for (var i = 0; i < this.paramCache_.length; i += 2) {
+ var key = this.paramCache_[i++],
+ value = this.paramCache_[i++];
+ if (!(key in paramMap)) {
+ paramMap[key] = [value];
+ } else {
+ paramMap[key].push(value);
+ }
+ }
+ return paramMap;
+ };
+ /**
+ * returns the first value for a given cgi parameter or null if the given
+ * parameter name does not appear in the query string.
+ * If the given parameter name does appear, but has no '<tt>=</tt>' following
+ * it, then the empty string will be returned.
+ * @return {string|null}
+ */
+ URI.prototype.getParameterValue = function (paramNameUnescaped) {
+ this.checkParameterCache_();
+ for (var i = 0; i < this.paramCache_.length; i += 2) {
+ if (paramNameUnescaped === this.paramCache_[i]) {
+ return this.paramCache_[i + 1];
+ }
+ }
+ return null;
+ };
+
+ URI.prototype.getFragment = function () {
+ return this.fragment_ && decodeURIComponent(this.fragment_);
+ };
+ URI.prototype.getRawFragment = function () {
+ return this.fragment_;
+ };
+ URI.prototype.setFragment = function (newFragment) {
+ this.fragment_ = newFragment ? encodeURIComponent(newFragment) : null;
+ return this;
+ };
+ URI.prototype.setRawFragment = function (newFragment) {
+ this.fragment_ = newFragment ? newFragment : null;
+ return this;
+ };
+ URI.prototype.hasFragment = function () {
+ return null !== this.fragment_;
+ };
+
+ function nullIfAbsent(matchPart) {
+ return ('string' == typeof matchPart) && (matchPart.length > 0)
+ ? matchPart
+ : null;
+ }
+
+
+
+
+ /**
+ * a regular expression for breaking a URI into its component parts.
+ *
+ * <p>http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#RFC2234 says
+ * As the "first-match-wins" algorithm is identical to the "greedy"
+ * disambiguation method used by POSIX regular expressions, it is natural and
+ * commonplace to use a regular expression for parsing the potential five
+ * components of a URI reference.
+ *
+ * <p>The following line is the regular expression for breaking-down a
+ * well-formed URI reference into its components.
+ *
+ * <pre>
+ * ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
+ * 12 3 4 5 6 7 8 9
+ * </pre>
+ *
+ * <p>The numbers in the second line above are only to assist readability; they
+ * indicate the reference points for each subexpression (i.e., each paired
+ * parenthesis). We refer to the value matched for subexpression <n> as $<n>.
+ * For example, matching the above expression to
+ * <pre>
+ * http://www.ics.uci.edu/pub/ietf/uri/#Related
+ * </pre>
+ * results in the following subexpression matches:
+ * <pre>
+ * $1 = http:
+ * $2 = http
+ * $3 = //www.ics.uci.edu
+ * $4 = www.ics.uci.edu
+ * $5 = /pub/ietf/uri/
+ * $6 = <undefined>
+ * $7 = <undefined>
+ * $8 = #Related
+ * $9 = Related
+ * </pre>
+ * where <undefined> indicates that the component is not present, as is the
+ * case for the query component in the above example. Therefore, we can
+ * determine the value of the five components as
+ * <pre>
+ * scheme = $2
+ * authority = $4
+ * path = $5
+ * query = $7
+ * fragment = $9
+ * </pre>
+ *
+ * <p>msamuel: I have modified the regular expression slightly to expose the
+ * credentials, domain, and port separately from the authority.
+ * The modified version yields
+ * <pre>
+ * $1 = http scheme
+ * $2 = <undefined> credentials -\
+ * $3 = www.ics.uci.edu domain | authority
+ * $4 = <undefined> port -/
+ * $5 = /pub/ietf/uri/ path
+ * $6 = <undefined> query without ?
+ * $7 = Related fragment without #
+ * </pre>
+ */
+ var URI_RE_ = new RegExp(
+ "^" +
+ "(?:" +
+ "([^:/?#]+)" + // scheme
+ ":)?" +
+ "(?://" +
+ "(?:([^/?#]*)@)?" + // credentials
+ "([^/?#:@]*)" + // domain
+ "(?::([0-9]+))?" + // port
+ ")?" +
+ "([^?#]+)?" + // path
+ "(?:\\?([^#]*))?" + // query
+ "(?:#(.*))?" + // fragment
+ "$"
+ );
+
+ var URI_DISALLOWED_IN_SCHEME_OR_CREDENTIALS_ = /[#\/\?@]/g;
+ var URI_DISALLOWED_IN_PATH_ = /[\#\?]/g;
+
+ URI.parse = parse;
+ URI.create = create;
+ URI.resolve = resolve;
+ URI.collapse_dots = collapse_dots; // Visible for testing.
+
+// lightweight string-based api for loadModuleMaker
+ URI.utils = {
+ mimeTypeOf: function (uri) {
+ var uriObj = parse(uri);
+ if (/\.html$/.test(uriObj.getPath())) {
+ return 'text/html';
+ } else {
+ return 'application/javascript';
+ }
+ },
+ resolve: function (base, uri) {
+ if (base) {
+ return resolve(parse(base), parse(uri)).toString();
+ } else {
+ return '' + uri;
+ }
+ }
+ };
+
+
+ return URI;
+})();
+
+if (typeof exports !== 'undefined') {
+ if (typeof module !== 'undefined' && module.exports) {
+ exports = module.exports = URI;
+ }
+ exports.URI = URI;
+} else {
+
+ // Exports for closure compiler.
+ if (typeof window !== 'undefined') {
+ window['URI'] = URI;
+ }
+}