| Index: third_party/google_input_tools/third_party/closure_library/closure/goog/uri/utils.js
|
| diff --git a/third_party/google_input_tools/third_party/closure_library/closure/goog/uri/utils.js b/third_party/google_input_tools/third_party/closure_library/closure/goog/uri/utils.js
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..425661e26b6672883d866f6770a91da83264a64e
|
| --- /dev/null
|
| +++ b/third_party/google_input_tools/third_party/closure_library/closure/goog/uri/utils.js
|
| @@ -0,0 +1,1076 @@
|
| +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
|
| +//
|
| +// 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 Simple utilities for dealing with URI strings.
|
| + *
|
| + * This is intended to be a lightweight alternative to constructing goog.Uri
|
| + * objects. Whereas goog.Uri adds several kilobytes to the binary regardless
|
| + * of how much of its functionality you use, this is designed to be a set of
|
| + * mostly-independent utilities so that the compiler includes only what is
|
| + * necessary for the task. Estimated savings of porting is 5k pre-gzip and
|
| + * 1.5k post-gzip. To ensure the savings remain, future developers should
|
| + * avoid adding new functionality to existing functions, but instead create
|
| + * new ones and factor out shared code.
|
| + *
|
| + * Many of these utilities have limited functionality, tailored to common
|
| + * cases. The query parameter utilities assume that the parameter keys are
|
| + * already encoded, since most keys are compile-time alphanumeric strings. The
|
| + * query parameter mutation utilities also do not tolerate fragment identifiers.
|
| + *
|
| + * By design, these functions can be slower than goog.Uri equivalents.
|
| + * Repeated calls to some of functions may be quadratic in behavior for IE,
|
| + * although the effect is somewhat limited given the 2kb limit.
|
| + *
|
| + * One advantage of the limited functionality here is that this approach is
|
| + * less sensitive to differences in URI encodings than goog.Uri, since these
|
| + * functions modify the strings in place, rather than decoding and
|
| + * re-encoding.
|
| + *
|
| + * Uses features of RFC 3986 for parsing/formatting URIs:
|
| + * http://www.ietf.org/rfc/rfc3986.txt
|
| + *
|
| + * @author gboyer@google.com (Garrett Boyer) - The "lightened" design.
|
| + * @author msamuel@google.com (Mike Samuel) - Domain knowledge and regexes.
|
| + */
|
| +
|
| +goog.provide('goog.uri.utils');
|
| +goog.provide('goog.uri.utils.ComponentIndex');
|
| +goog.provide('goog.uri.utils.QueryArray');
|
| +goog.provide('goog.uri.utils.QueryValue');
|
| +goog.provide('goog.uri.utils.StandardQueryParam');
|
| +
|
| +goog.require('goog.asserts');
|
| +goog.require('goog.string');
|
| +goog.require('goog.userAgent');
|
| +
|
| +
|
| +/**
|
| + * Character codes inlined to avoid object allocations due to charCode.
|
| + * @enum {number}
|
| + * @private
|
| + */
|
| +goog.uri.utils.CharCode_ = {
|
| + AMPERSAND: 38,
|
| + EQUAL: 61,
|
| + HASH: 35,
|
| + QUESTION: 63
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Builds a URI string from already-encoded parts.
|
| + *
|
| + * No encoding is performed. Any component may be omitted as either null or
|
| + * undefined.
|
| + *
|
| + * @param {?string=} opt_scheme The scheme such as 'http'.
|
| + * @param {?string=} opt_userInfo The user name before the '@'.
|
| + * @param {?string=} opt_domain The domain such as 'www.google.com', already
|
| + * URI-encoded.
|
| + * @param {(string|number|null)=} opt_port The port number.
|
| + * @param {?string=} opt_path The path, already URI-encoded. If it is not
|
| + * empty, it must begin with a slash.
|
| + * @param {?string=} opt_queryData The URI-encoded query data.
|
| + * @param {?string=} opt_fragment The URI-encoded fragment identifier.
|
| + * @return {string} The fully combined URI.
|
| + */
|
| +goog.uri.utils.buildFromEncodedParts = function(opt_scheme, opt_userInfo,
|
| + opt_domain, opt_port, opt_path, opt_queryData, opt_fragment) {
|
| + var out = '';
|
| +
|
| + if (opt_scheme) {
|
| + out += opt_scheme + ':';
|
| + }
|
| +
|
| + if (opt_domain) {
|
| + out += '//';
|
| +
|
| + if (opt_userInfo) {
|
| + out += opt_userInfo + '@';
|
| + }
|
| +
|
| + out += opt_domain;
|
| +
|
| + if (opt_port) {
|
| + out += ':' + opt_port;
|
| + }
|
| + }
|
| +
|
| + if (opt_path) {
|
| + out += opt_path;
|
| + }
|
| +
|
| + if (opt_queryData) {
|
| + out += '?' + opt_queryData;
|
| + }
|
| +
|
| + if (opt_fragment) {
|
| + out += '#' + opt_fragment;
|
| + }
|
| +
|
| + return out;
|
| +};
|
| +
|
| +
|
| +/**
|
| + * A regular expression for breaking a URI into its component parts.
|
| + *
|
| + * {@link http://www.ietf.org/rfc/rfc3986.txt} says in Appendix B
|
| + * 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.
|
| + *
|
| + * 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>
|
| + *
|
| + * 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>
|
| + *
|
| + * The regular expression has been modified slightly to expose the
|
| + * userInfo, domain, and port separately from the authority.
|
| + * The modified version yields
|
| + * <pre>
|
| + * $1 = http scheme
|
| + * $2 = <undefined> userInfo -\
|
| + * $3 = www.ics.uci.edu domain | authority
|
| + * $4 = <undefined> port -/
|
| + * $5 = /pub/ietf/uri/ path
|
| + * $6 = <undefined> query without ?
|
| + * $7 = Related fragment without #
|
| + * </pre>
|
| + * @type {!RegExp}
|
| + * @private
|
| + */
|
| +goog.uri.utils.splitRe_ = new RegExp(
|
| + '^' +
|
| + '(?:' +
|
| + '([^:/?#.]+)' + // scheme - ignore special characters
|
| + // used by other URL parts such as :,
|
| + // ?, /, #, and .
|
| + ':)?' +
|
| + '(?://' +
|
| + '(?:([^/?#]*)@)?' + // userInfo
|
| + '([^/#?]*?)' + // domain
|
| + '(?::([0-9]+))?' + // port
|
| + '(?=[/#?]|$)' + // authority-terminating character
|
| + ')?' +
|
| + '([^?#]+)?' + // path
|
| + '(?:\\?([^#]*))?' + // query
|
| + '(?:#(.*))?' + // fragment
|
| + '$');
|
| +
|
| +
|
| +/**
|
| + * The index of each URI component in the return value of goog.uri.utils.split.
|
| + * @enum {number}
|
| + */
|
| +goog.uri.utils.ComponentIndex = {
|
| + SCHEME: 1,
|
| + USER_INFO: 2,
|
| + DOMAIN: 3,
|
| + PORT: 4,
|
| + PATH: 5,
|
| + QUERY_DATA: 6,
|
| + FRAGMENT: 7
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Splits a URI into its component parts.
|
| + *
|
| + * Each component can be accessed via the component indices; for example:
|
| + * <pre>
|
| + * goog.uri.utils.split(someStr)[goog.uri.utils.CompontentIndex.QUERY_DATA];
|
| + * </pre>
|
| + *
|
| + * @param {string} uri The URI string to examine.
|
| + * @return {!Array.<string|undefined>} Each component still URI-encoded.
|
| + * Each component that is present will contain the encoded value, whereas
|
| + * components that are not present will be undefined or empty, depending
|
| + * on the browser's regular expression implementation. Never null, since
|
| + * arbitrary strings may still look like path names.
|
| + */
|
| +goog.uri.utils.split = function(uri) {
|
| + goog.uri.utils.phishingProtection_();
|
| +
|
| + // See @return comment -- never null.
|
| + return /** @type {!Array.<string|undefined>} */ (
|
| + uri.match(goog.uri.utils.splitRe_));
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Safari has a nasty bug where if you have an http URL with a username, e.g.,
|
| + * http://evil.com%2F@google.com/
|
| + * Safari will report that window.location.href is
|
| + * http://evil.com/google.com/
|
| + * so that anyone who tries to parse the domain of that URL will get
|
| + * the wrong domain. We've seen exploits where people use this to trick
|
| + * Safari into loading resources from evil domains.
|
| + *
|
| + * To work around this, we run a little "Safari phishing check", and throw
|
| + * an exception if we see this happening.
|
| + *
|
| + * There is no convenient place to put this check. We apply it to
|
| + * anyone doing URI parsing on Webkit. We're not happy about this, but
|
| + * it fixes the problem.
|
| + *
|
| + * This should be removed once Safari fixes their bug.
|
| + *
|
| + * Exploit reported by Masato Kinugawa.
|
| + *
|
| + * @type {boolean}
|
| + * @private
|
| + */
|
| +goog.uri.utils.needsPhishingProtection_ = goog.userAgent.WEBKIT;
|
| +
|
| +
|
| +/**
|
| + * Check to see if the user is being phished.
|
| + * @private
|
| + */
|
| +goog.uri.utils.phishingProtection_ = function() {
|
| + if (goog.uri.utils.needsPhishingProtection_) {
|
| + // Turn protection off, so that we don't recurse.
|
| + goog.uri.utils.needsPhishingProtection_ = false;
|
| +
|
| + // Use quoted access, just in case the user isn't using location externs.
|
| + var location = goog.global['location'];
|
| + if (location) {
|
| + var href = location['href'];
|
| + if (href) {
|
| + var domain = goog.uri.utils.getDomain(href);
|
| + if (domain && domain != location['hostname']) {
|
| + // Phishing attack
|
| + goog.uri.utils.needsPhishingProtection_ = true;
|
| + throw Error();
|
| + }
|
| + }
|
| + }
|
| + }
|
| +};
|
| +
|
| +
|
| +/**
|
| + * @param {?string} uri A possibly null string.
|
| + * @return {?string} The string URI-decoded, or null if uri is null.
|
| + * @private
|
| + */
|
| +goog.uri.utils.decodeIfPossible_ = function(uri) {
|
| + return uri && decodeURIComponent(uri);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Gets a URI component by index.
|
| + *
|
| + * It is preferred to use the getPathEncoded() variety of functions ahead,
|
| + * since they are more readable.
|
| + *
|
| + * @param {goog.uri.utils.ComponentIndex} componentIndex The component index.
|
| + * @param {string} uri The URI to examine.
|
| + * @return {?string} The still-encoded component, or null if the component
|
| + * is not present.
|
| + * @private
|
| + */
|
| +goog.uri.utils.getComponentByIndex_ = function(componentIndex, uri) {
|
| + // Convert undefined, null, and empty string into null.
|
| + return goog.uri.utils.split(uri)[componentIndex] || null;
|
| +};
|
| +
|
| +
|
| +/**
|
| + * @param {string} uri The URI to examine.
|
| + * @return {?string} The protocol or scheme, or null if none. Does not
|
| + * include trailing colons or slashes.
|
| + */
|
| +goog.uri.utils.getScheme = function(uri) {
|
| + return goog.uri.utils.getComponentByIndex_(
|
| + goog.uri.utils.ComponentIndex.SCHEME, uri);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Gets the effective scheme for the URL. If the URL is relative then the
|
| + * scheme is derived from the page's location.
|
| + * @param {string} uri The URI to examine.
|
| + * @return {string} The protocol or scheme, always lower case.
|
| + */
|
| +goog.uri.utils.getEffectiveScheme = function(uri) {
|
| + var scheme = goog.uri.utils.getScheme(uri);
|
| + if (!scheme && self.location) {
|
| + var protocol = self.location.protocol;
|
| + scheme = protocol.substr(0, protocol.length - 1);
|
| + }
|
| + // NOTE: When called from a web worker in Firefox 3.5, location maybe null.
|
| + // All other browsers with web workers support self.location from the worker.
|
| + return scheme ? scheme.toLowerCase() : '';
|
| +};
|
| +
|
| +
|
| +/**
|
| + * @param {string} uri The URI to examine.
|
| + * @return {?string} The user name still encoded, or null if none.
|
| + */
|
| +goog.uri.utils.getUserInfoEncoded = function(uri) {
|
| + return goog.uri.utils.getComponentByIndex_(
|
| + goog.uri.utils.ComponentIndex.USER_INFO, uri);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * @param {string} uri The URI to examine.
|
| + * @return {?string} The decoded user info, or null if none.
|
| + */
|
| +goog.uri.utils.getUserInfo = function(uri) {
|
| + return goog.uri.utils.decodeIfPossible_(
|
| + goog.uri.utils.getUserInfoEncoded(uri));
|
| +};
|
| +
|
| +
|
| +/**
|
| + * @param {string} uri The URI to examine.
|
| + * @return {?string} The domain name still encoded, or null if none.
|
| + */
|
| +goog.uri.utils.getDomainEncoded = function(uri) {
|
| + return goog.uri.utils.getComponentByIndex_(
|
| + goog.uri.utils.ComponentIndex.DOMAIN, uri);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * @param {string} uri The URI to examine.
|
| + * @return {?string} The decoded domain, or null if none.
|
| + */
|
| +goog.uri.utils.getDomain = function(uri) {
|
| + return goog.uri.utils.decodeIfPossible_(goog.uri.utils.getDomainEncoded(uri));
|
| +};
|
| +
|
| +
|
| +/**
|
| + * @param {string} uri The URI to examine.
|
| + * @return {?number} The port number, or null if none.
|
| + */
|
| +goog.uri.utils.getPort = function(uri) {
|
| + // Coerce to a number. If the result of getComponentByIndex_ is null or
|
| + // non-numeric, the number coersion yields NaN. This will then return
|
| + // null for all non-numeric cases (though also zero, which isn't a relevant
|
| + // port number).
|
| + return Number(goog.uri.utils.getComponentByIndex_(
|
| + goog.uri.utils.ComponentIndex.PORT, uri)) || null;
|
| +};
|
| +
|
| +
|
| +/**
|
| + * @param {string} uri The URI to examine.
|
| + * @return {?string} The path still encoded, or null if none. Includes the
|
| + * leading slash, if any.
|
| + */
|
| +goog.uri.utils.getPathEncoded = function(uri) {
|
| + return goog.uri.utils.getComponentByIndex_(
|
| + goog.uri.utils.ComponentIndex.PATH, uri);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * @param {string} uri The URI to examine.
|
| + * @return {?string} The decoded path, or null if none. Includes the leading
|
| + * slash, if any.
|
| + */
|
| +goog.uri.utils.getPath = function(uri) {
|
| + return goog.uri.utils.decodeIfPossible_(goog.uri.utils.getPathEncoded(uri));
|
| +};
|
| +
|
| +
|
| +/**
|
| + * @param {string} uri The URI to examine.
|
| + * @return {?string} The query data still encoded, or null if none. Does not
|
| + * include the question mark itself.
|
| + */
|
| +goog.uri.utils.getQueryData = function(uri) {
|
| + return goog.uri.utils.getComponentByIndex_(
|
| + goog.uri.utils.ComponentIndex.QUERY_DATA, uri);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * @param {string} uri The URI to examine.
|
| + * @return {?string} The fragment identifier, or null if none. Does not
|
| + * include the hash mark itself.
|
| + */
|
| +goog.uri.utils.getFragmentEncoded = function(uri) {
|
| + // The hash mark may not appear in any other part of the URL.
|
| + var hashIndex = uri.indexOf('#');
|
| + return hashIndex < 0 ? null : uri.substr(hashIndex + 1);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * @param {string} uri The URI to examine.
|
| + * @param {?string} fragment The encoded fragment identifier, or null if none.
|
| + * Does not include the hash mark itself.
|
| + * @return {string} The URI with the fragment set.
|
| + */
|
| +goog.uri.utils.setFragmentEncoded = function(uri, fragment) {
|
| + return goog.uri.utils.removeFragment(uri) + (fragment ? '#' + fragment : '');
|
| +};
|
| +
|
| +
|
| +/**
|
| + * @param {string} uri The URI to examine.
|
| + * @return {?string} The decoded fragment identifier, or null if none. Does
|
| + * not include the hash mark.
|
| + */
|
| +goog.uri.utils.getFragment = function(uri) {
|
| + return goog.uri.utils.decodeIfPossible_(
|
| + goog.uri.utils.getFragmentEncoded(uri));
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Extracts everything up to the port of the URI.
|
| + * @param {string} uri The URI string.
|
| + * @return {string} Everything up to and including the port.
|
| + */
|
| +goog.uri.utils.getHost = function(uri) {
|
| + var pieces = goog.uri.utils.split(uri);
|
| + return goog.uri.utils.buildFromEncodedParts(
|
| + pieces[goog.uri.utils.ComponentIndex.SCHEME],
|
| + pieces[goog.uri.utils.ComponentIndex.USER_INFO],
|
| + pieces[goog.uri.utils.ComponentIndex.DOMAIN],
|
| + pieces[goog.uri.utils.ComponentIndex.PORT]);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Extracts the path of the URL and everything after.
|
| + * @param {string} uri The URI string.
|
| + * @return {string} The URI, starting at the path and including the query
|
| + * parameters and fragment identifier.
|
| + */
|
| +goog.uri.utils.getPathAndAfter = function(uri) {
|
| + var pieces = goog.uri.utils.split(uri);
|
| + return goog.uri.utils.buildFromEncodedParts(null, null, null, null,
|
| + pieces[goog.uri.utils.ComponentIndex.PATH],
|
| + pieces[goog.uri.utils.ComponentIndex.QUERY_DATA],
|
| + pieces[goog.uri.utils.ComponentIndex.FRAGMENT]);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Gets the URI with the fragment identifier removed.
|
| + * @param {string} uri The URI to examine.
|
| + * @return {string} Everything preceding the hash mark.
|
| + */
|
| +goog.uri.utils.removeFragment = function(uri) {
|
| + // The hash mark may not appear in any other part of the URL.
|
| + var hashIndex = uri.indexOf('#');
|
| + return hashIndex < 0 ? uri : uri.substr(0, hashIndex);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Ensures that two URI's have the exact same domain, scheme, and port.
|
| + *
|
| + * Unlike the version in goog.Uri, this checks protocol, and therefore is
|
| + * suitable for checking against the browser's same-origin policy.
|
| + *
|
| + * @param {string} uri1 The first URI.
|
| + * @param {string} uri2 The second URI.
|
| + * @return {boolean} Whether they have the same domain and port.
|
| + */
|
| +goog.uri.utils.haveSameDomain = function(uri1, uri2) {
|
| + var pieces1 = goog.uri.utils.split(uri1);
|
| + var pieces2 = goog.uri.utils.split(uri2);
|
| + return pieces1[goog.uri.utils.ComponentIndex.DOMAIN] ==
|
| + pieces2[goog.uri.utils.ComponentIndex.DOMAIN] &&
|
| + pieces1[goog.uri.utils.ComponentIndex.SCHEME] ==
|
| + pieces2[goog.uri.utils.ComponentIndex.SCHEME] &&
|
| + pieces1[goog.uri.utils.ComponentIndex.PORT] ==
|
| + pieces2[goog.uri.utils.ComponentIndex.PORT];
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Asserts that there are no fragment or query identifiers, only in uncompiled
|
| + * mode.
|
| + * @param {string} uri The URI to examine.
|
| + * @private
|
| + */
|
| +goog.uri.utils.assertNoFragmentsOrQueries_ = function(uri) {
|
| + // NOTE: would use goog.asserts here, but jscompiler doesn't know that
|
| + // indexOf has no side effects.
|
| + if (goog.DEBUG && (uri.indexOf('#') >= 0 || uri.indexOf('?') >= 0)) {
|
| + throw Error('goog.uri.utils: Fragment or query identifiers are not ' +
|
| + 'supported: [' + uri + ']');
|
| + }
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Supported query parameter values by the parameter serializing utilities.
|
| + *
|
| + * If a value is null or undefined, the key-value pair is skipped, as an easy
|
| + * way to omit parameters conditionally. Non-array parameters are converted
|
| + * to a string and URI encoded. Array values are expanded into multiple
|
| + * &key=value pairs, with each element stringized and URI-encoded.
|
| + *
|
| + * @typedef {*}
|
| + */
|
| +goog.uri.utils.QueryValue;
|
| +
|
| +
|
| +/**
|
| + * An array representing a set of query parameters with alternating keys
|
| + * and values.
|
| + *
|
| + * Keys are assumed to be URI encoded already and live at even indices. See
|
| + * goog.uri.utils.QueryValue for details on how parameter values are encoded.
|
| + *
|
| + * Example:
|
| + * <pre>
|
| + * var data = [
|
| + * // Simple param: ?name=BobBarker
|
| + * 'name', 'BobBarker',
|
| + * // Conditional param -- may be omitted entirely.
|
| + * 'specialDietaryNeeds', hasDietaryNeeds() ? getDietaryNeeds() : null,
|
| + * // Multi-valued param: &house=LosAngeles&house=NewYork&house=null
|
| + * 'house', ['LosAngeles', 'NewYork', null]
|
| + * ];
|
| + * </pre>
|
| + *
|
| + * @typedef {!Array.<string|goog.uri.utils.QueryValue>}
|
| + */
|
| +goog.uri.utils.QueryArray;
|
| +
|
| +
|
| +/**
|
| + * Appends a URI and query data in a string buffer with special preconditions.
|
| + *
|
| + * Internal implementation utility, performing very few object allocations.
|
| + *
|
| + * @param {!Array.<string|undefined>} buffer A string buffer. The first element
|
| + * must be the base URI, and may have a fragment identifier. If the array
|
| + * contains more than one element, the second element must be an ampersand,
|
| + * and may be overwritten, depending on the base URI. Undefined elements
|
| + * are treated as empty-string.
|
| + * @return {string} The concatenated URI and query data.
|
| + * @private
|
| + */
|
| +goog.uri.utils.appendQueryData_ = function(buffer) {
|
| + if (buffer[1]) {
|
| + // At least one query parameter was added. We need to check the
|
| + // punctuation mark, which is currently an ampersand, and also make sure
|
| + // there aren't any interfering fragment identifiers.
|
| + var baseUri = /** @type {string} */ (buffer[0]);
|
| + var hashIndex = baseUri.indexOf('#');
|
| + if (hashIndex >= 0) {
|
| + // Move the fragment off the base part of the URI into the end.
|
| + buffer.push(baseUri.substr(hashIndex));
|
| + buffer[0] = baseUri = baseUri.substr(0, hashIndex);
|
| + }
|
| + var questionIndex = baseUri.indexOf('?');
|
| + if (questionIndex < 0) {
|
| + // No question mark, so we need a question mark instead of an ampersand.
|
| + buffer[1] = '?';
|
| + } else if (questionIndex == baseUri.length - 1) {
|
| + // Question mark is the very last character of the existing URI, so don't
|
| + // append an additional delimiter.
|
| + buffer[1] = undefined;
|
| + }
|
| + }
|
| +
|
| + return buffer.join('');
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Appends key=value pairs to an array, supporting multi-valued objects.
|
| + * @param {string} key The key prefix.
|
| + * @param {goog.uri.utils.QueryValue} value The value to serialize.
|
| + * @param {!Array.<string>} pairs The array to which the 'key=value' strings
|
| + * should be appended.
|
| + * @private
|
| + */
|
| +goog.uri.utils.appendKeyValuePairs_ = function(key, value, pairs) {
|
| + if (goog.isArray(value)) {
|
| + // Convince the compiler it's an array.
|
| + goog.asserts.assertArray(value);
|
| + for (var j = 0; j < value.length; j++) {
|
| + // Convert to string explicitly, to short circuit the null and array
|
| + // logic in this function -- this ensures that null and undefined get
|
| + // written as literal 'null' and 'undefined', and arrays don't get
|
| + // expanded out but instead encoded in the default way.
|
| + goog.uri.utils.appendKeyValuePairs_(key, String(value[j]), pairs);
|
| + }
|
| + } else if (value != null) {
|
| + // Skip a top-level null or undefined entirely.
|
| + pairs.push('&', key,
|
| + // Check for empty string. Zero gets encoded into the url as literal
|
| + // strings. For empty string, skip the equal sign, to be consistent
|
| + // with UriBuilder.java.
|
| + value === '' ? '' : '=',
|
| + goog.string.urlEncode(value));
|
| + }
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Builds a buffer of query data from a sequence of alternating keys and values.
|
| + *
|
| + * @param {!Array.<string|undefined>} buffer A string buffer to append to. The
|
| + * first element appended will be an '&', and may be replaced by the caller.
|
| + * @param {goog.uri.utils.QueryArray|Arguments} keysAndValues An array with
|
| + * alternating keys and values -- see the typedef.
|
| + * @param {number=} opt_startIndex A start offset into the arary, defaults to 0.
|
| + * @return {!Array.<string|undefined>} The buffer argument.
|
| + * @private
|
| + */
|
| +goog.uri.utils.buildQueryDataBuffer_ = function(
|
| + buffer, keysAndValues, opt_startIndex) {
|
| + goog.asserts.assert(Math.max(keysAndValues.length - (opt_startIndex || 0),
|
| + 0) % 2 == 0, 'goog.uri.utils: Key/value lists must be even in length.');
|
| +
|
| + for (var i = opt_startIndex || 0; i < keysAndValues.length; i += 2) {
|
| + goog.uri.utils.appendKeyValuePairs_(
|
| + keysAndValues[i], keysAndValues[i + 1], buffer);
|
| + }
|
| +
|
| + return buffer;
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Builds a query data string from a sequence of alternating keys and values.
|
| + * Currently generates "&key&" for empty args.
|
| + *
|
| + * @param {goog.uri.utils.QueryArray} keysAndValues Alternating keys and
|
| + * values. See the typedef.
|
| + * @param {number=} opt_startIndex A start offset into the arary, defaults to 0.
|
| + * @return {string} The encoded query string, in the form 'a=1&b=2'.
|
| + */
|
| +goog.uri.utils.buildQueryData = function(keysAndValues, opt_startIndex) {
|
| + var buffer = goog.uri.utils.buildQueryDataBuffer_(
|
| + [], keysAndValues, opt_startIndex);
|
| + buffer[0] = ''; // Remove the leading ampersand.
|
| + return buffer.join('');
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Builds a buffer of query data from a map.
|
| + *
|
| + * @param {!Array.<string|undefined>} buffer A string buffer to append to. The
|
| + * first element appended will be an '&', and may be replaced by the caller.
|
| + * @param {Object.<goog.uri.utils.QueryValue>} map An object where keys are
|
| + * URI-encoded parameter keys, and the values conform to the contract
|
| + * specified in the goog.uri.utils.QueryValue typedef.
|
| + * @return {!Array.<string|undefined>} The buffer argument.
|
| + * @private
|
| + */
|
| +goog.uri.utils.buildQueryDataBufferFromMap_ = function(buffer, map) {
|
| + for (var key in map) {
|
| + goog.uri.utils.appendKeyValuePairs_(key, map[key], buffer);
|
| + }
|
| +
|
| + return buffer;
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Builds a query data string from a map.
|
| + * Currently generates "&key&" for empty args.
|
| + *
|
| + * @param {Object} map An object where keys are URI-encoded parameter keys,
|
| + * and the values are arbitrary types or arrays. Keys with a null value
|
| + * are dropped.
|
| + * @return {string} The encoded query string, in the form 'a=1&b=2'.
|
| + */
|
| +goog.uri.utils.buildQueryDataFromMap = function(map) {
|
| + var buffer = goog.uri.utils.buildQueryDataBufferFromMap_([], map);
|
| + buffer[0] = '';
|
| + return buffer.join('');
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Appends URI parameters to an existing URI.
|
| + *
|
| + * The variable arguments may contain alternating keys and values. Keys are
|
| + * assumed to be already URI encoded. The values should not be URI-encoded,
|
| + * and will instead be encoded by this function.
|
| + * <pre>
|
| + * appendParams('http://www.foo.com?existing=true',
|
| + * 'key1', 'value1',
|
| + * 'key2', 'value?willBeEncoded',
|
| + * 'key3', ['valueA', 'valueB', 'valueC'],
|
| + * 'key4', null);
|
| + * result: 'http://www.foo.com?existing=true&' +
|
| + * 'key1=value1&' +
|
| + * 'key2=value%3FwillBeEncoded&' +
|
| + * 'key3=valueA&key3=valueB&key3=valueC'
|
| + * </pre>
|
| + *
|
| + * A single call to this function will not exhibit quadratic behavior in IE,
|
| + * whereas multiple repeated calls may, although the effect is limited by
|
| + * fact that URL's generally can't exceed 2kb.
|
| + *
|
| + * @param {string} uri The original URI, which may already have query data.
|
| + * @param {...(goog.uri.utils.QueryArray|string|goog.uri.utils.QueryValue)} var_args
|
| + * An array or argument list conforming to goog.uri.utils.QueryArray.
|
| + * @return {string} The URI with all query parameters added.
|
| + */
|
| +goog.uri.utils.appendParams = function(uri, var_args) {
|
| + return goog.uri.utils.appendQueryData_(
|
| + arguments.length == 2 ?
|
| + goog.uri.utils.buildQueryDataBuffer_([uri], arguments[1], 0) :
|
| + goog.uri.utils.buildQueryDataBuffer_([uri], arguments, 1));
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Appends query parameters from a map.
|
| + *
|
| + * @param {string} uri The original URI, which may already have query data.
|
| + * @param {Object} map An object where keys are URI-encoded parameter keys,
|
| + * and the values are arbitrary types or arrays. Keys with a null value
|
| + * are dropped.
|
| + * @return {string} The new parameters.
|
| + */
|
| +goog.uri.utils.appendParamsFromMap = function(uri, map) {
|
| + return goog.uri.utils.appendQueryData_(
|
| + goog.uri.utils.buildQueryDataBufferFromMap_([uri], map));
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Appends a single URI parameter.
|
| + *
|
| + * Repeated calls to this can exhibit quadratic behavior in IE6 due to the
|
| + * way string append works, though it should be limited given the 2kb limit.
|
| + *
|
| + * @param {string} uri The original URI, which may already have query data.
|
| + * @param {string} key The key, which must already be URI encoded.
|
| + * @param {*=} opt_value The value, which will be stringized and encoded
|
| + * (assumed not already to be encoded). If omitted, undefined, or null, the
|
| + * key will be added as a valueless parameter.
|
| + * @return {string} The URI with the query parameter added.
|
| + */
|
| +goog.uri.utils.appendParam = function(uri, key, opt_value) {
|
| + var paramArr = [uri, '&', key];
|
| + if (goog.isDefAndNotNull(opt_value)) {
|
| + paramArr.push('=', goog.string.urlEncode(opt_value));
|
| + }
|
| + return goog.uri.utils.appendQueryData_(paramArr);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Finds the next instance of a query parameter with the specified name.
|
| + *
|
| + * Does not instantiate any objects.
|
| + *
|
| + * @param {string} uri The URI to search. May contain a fragment identifier
|
| + * if opt_hashIndex is specified.
|
| + * @param {number} startIndex The index to begin searching for the key at. A
|
| + * match may be found even if this is one character after the ampersand.
|
| + * @param {string} keyEncoded The URI-encoded key.
|
| + * @param {number} hashOrEndIndex Index to stop looking at. If a hash
|
| + * mark is present, it should be its index, otherwise it should be the
|
| + * length of the string.
|
| + * @return {number} The position of the first character in the key's name,
|
| + * immediately after either a question mark or a dot.
|
| + * @private
|
| + */
|
| +goog.uri.utils.findParam_ = function(
|
| + uri, startIndex, keyEncoded, hashOrEndIndex) {
|
| + var index = startIndex;
|
| + var keyLength = keyEncoded.length;
|
| +
|
| + // Search for the key itself and post-filter for surronuding punctuation,
|
| + // rather than expensively building a regexp.
|
| + while ((index = uri.indexOf(keyEncoded, index)) >= 0 &&
|
| + index < hashOrEndIndex) {
|
| + var precedingChar = uri.charCodeAt(index - 1);
|
| + // Ensure that the preceding character is '&' or '?'.
|
| + if (precedingChar == goog.uri.utils.CharCode_.AMPERSAND ||
|
| + precedingChar == goog.uri.utils.CharCode_.QUESTION) {
|
| + // Ensure the following character is '&', '=', '#', or NaN
|
| + // (end of string).
|
| + var followingChar = uri.charCodeAt(index + keyLength);
|
| + if (!followingChar ||
|
| + followingChar == goog.uri.utils.CharCode_.EQUAL ||
|
| + followingChar == goog.uri.utils.CharCode_.AMPERSAND ||
|
| + followingChar == goog.uri.utils.CharCode_.HASH) {
|
| + return index;
|
| + }
|
| + }
|
| + index += keyLength + 1;
|
| + }
|
| +
|
| + return -1;
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Regular expression for finding a hash mark or end of string.
|
| + * @type {RegExp}
|
| + * @private
|
| + */
|
| +goog.uri.utils.hashOrEndRe_ = /#|$/;
|
| +
|
| +
|
| +/**
|
| + * Determines if the URI contains a specific key.
|
| + *
|
| + * Performs no object instantiations.
|
| + *
|
| + * @param {string} uri The URI to process. May contain a fragment
|
| + * identifier.
|
| + * @param {string} keyEncoded The URI-encoded key. Case-sensitive.
|
| + * @return {boolean} Whether the key is present.
|
| + */
|
| +goog.uri.utils.hasParam = function(uri, keyEncoded) {
|
| + return goog.uri.utils.findParam_(uri, 0, keyEncoded,
|
| + uri.search(goog.uri.utils.hashOrEndRe_)) >= 0;
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Gets the first value of a query parameter.
|
| + * @param {string} uri The URI to process. May contain a fragment.
|
| + * @param {string} keyEncoded The URI-encoded key. Case-sensitive.
|
| + * @return {?string} The first value of the parameter (URI-decoded), or null
|
| + * if the parameter is not found.
|
| + */
|
| +goog.uri.utils.getParamValue = function(uri, keyEncoded) {
|
| + var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_);
|
| + var foundIndex = goog.uri.utils.findParam_(
|
| + uri, 0, keyEncoded, hashOrEndIndex);
|
| +
|
| + if (foundIndex < 0) {
|
| + return null;
|
| + } else {
|
| + var endPosition = uri.indexOf('&', foundIndex);
|
| + if (endPosition < 0 || endPosition > hashOrEndIndex) {
|
| + endPosition = hashOrEndIndex;
|
| + }
|
| + // Progress forth to the end of the "key=" or "key&" substring.
|
| + foundIndex += keyEncoded.length + 1;
|
| + // Use substr, because it (unlike substring) will return empty string
|
| + // if foundIndex > endPosition.
|
| + return goog.string.urlDecode(
|
| + uri.substr(foundIndex, endPosition - foundIndex));
|
| + }
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Gets all values of a query parameter.
|
| + * @param {string} uri The URI to process. May contain a framgnet.
|
| + * @param {string} keyEncoded The URI-encoded key. Case-snsitive.
|
| + * @return {!Array.<string>} All URI-decoded values with the given key.
|
| + * If the key is not found, this will have length 0, but never be null.
|
| + */
|
| +goog.uri.utils.getParamValues = function(uri, keyEncoded) {
|
| + var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_);
|
| + var position = 0;
|
| + var foundIndex;
|
| + var result = [];
|
| +
|
| + while ((foundIndex = goog.uri.utils.findParam_(
|
| + uri, position, keyEncoded, hashOrEndIndex)) >= 0) {
|
| + // Find where this parameter ends, either the '&' or the end of the
|
| + // query parameters.
|
| + position = uri.indexOf('&', foundIndex);
|
| + if (position < 0 || position > hashOrEndIndex) {
|
| + position = hashOrEndIndex;
|
| + }
|
| +
|
| + // Progress forth to the end of the "key=" or "key&" substring.
|
| + foundIndex += keyEncoded.length + 1;
|
| + // Use substr, because it (unlike substring) will return empty string
|
| + // if foundIndex > position.
|
| + result.push(goog.string.urlDecode(uri.substr(
|
| + foundIndex, position - foundIndex)));
|
| + }
|
| +
|
| + return result;
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Regexp to find trailing question marks and ampersands.
|
| + * @type {RegExp}
|
| + * @private
|
| + */
|
| +goog.uri.utils.trailingQueryPunctuationRe_ = /[?&]($|#)/;
|
| +
|
| +
|
| +/**
|
| + * Removes all instances of a query parameter.
|
| + * @param {string} uri The URI to process. Must not contain a fragment.
|
| + * @param {string} keyEncoded The URI-encoded key.
|
| + * @return {string} The URI with all instances of the parameter removed.
|
| + */
|
| +goog.uri.utils.removeParam = function(uri, keyEncoded) {
|
| + var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_);
|
| + var position = 0;
|
| + var foundIndex;
|
| + var buffer = [];
|
| +
|
| + // Look for a query parameter.
|
| + while ((foundIndex = goog.uri.utils.findParam_(
|
| + uri, position, keyEncoded, hashOrEndIndex)) >= 0) {
|
| + // Get the portion of the query string up to, but not including, the ?
|
| + // or & starting the parameter.
|
| + buffer.push(uri.substring(position, foundIndex));
|
| + // Progress to immediately after the '&'. If not found, go to the end.
|
| + // Avoid including the hash mark.
|
| + position = Math.min((uri.indexOf('&', foundIndex) + 1) || hashOrEndIndex,
|
| + hashOrEndIndex);
|
| + }
|
| +
|
| + // Append everything that is remaining.
|
| + buffer.push(uri.substr(position));
|
| +
|
| + // Join the buffer, and remove trailing punctuation that remains.
|
| + return buffer.join('').replace(
|
| + goog.uri.utils.trailingQueryPunctuationRe_, '$1');
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Replaces all existing definitions of a parameter with a single definition.
|
| + *
|
| + * Repeated calls to this can exhibit quadratic behavior due to the need to
|
| + * find existing instances and reconstruct the string, though it should be
|
| + * limited given the 2kb limit. Consider using appendParams to append multiple
|
| + * parameters in bulk.
|
| + *
|
| + * @param {string} uri The original URI, which may already have query data.
|
| + * @param {string} keyEncoded The key, which must already be URI encoded.
|
| + * @param {*} value The value, which will be stringized and encoded (assumed
|
| + * not already to be encoded).
|
| + * @return {string} The URI with the query parameter added.
|
| + */
|
| +goog.uri.utils.setParam = function(uri, keyEncoded, value) {
|
| + return goog.uri.utils.appendParam(
|
| + goog.uri.utils.removeParam(uri, keyEncoded), keyEncoded, value);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Generates a URI path using a given URI and a path with checks to
|
| + * prevent consecutive "//". The baseUri passed in must not contain
|
| + * query or fragment identifiers. The path to append may not contain query or
|
| + * fragment identifiers.
|
| + *
|
| + * @param {string} baseUri URI to use as the base.
|
| + * @param {string} path Path to append.
|
| + * @return {string} Updated URI.
|
| + */
|
| +goog.uri.utils.appendPath = function(baseUri, path) {
|
| + goog.uri.utils.assertNoFragmentsOrQueries_(baseUri);
|
| +
|
| + // Remove any trailing '/'
|
| + if (goog.string.endsWith(baseUri, '/')) {
|
| + baseUri = baseUri.substr(0, baseUri.length - 1);
|
| + }
|
| + // Remove any leading '/'
|
| + if (goog.string.startsWith(path, '/')) {
|
| + path = path.substr(1);
|
| + }
|
| + return goog.string.buildString(baseUri, '/', path);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Replaces the path.
|
| + * @param {string} uri URI to use as the base.
|
| + * @param {string} path New path.
|
| + * @return {string} Updated URI.
|
| + */
|
| +goog.uri.utils.setPath = function(uri, path) {
|
| + // Add any missing '/'.
|
| + if (!goog.string.startsWith(path, '/')) {
|
| + path = '/' + path;
|
| + }
|
| + var parts = goog.uri.utils.split(uri);
|
| + return goog.uri.utils.buildFromEncodedParts(
|
| + parts[goog.uri.utils.ComponentIndex.SCHEME],
|
| + parts[goog.uri.utils.ComponentIndex.USER_INFO],
|
| + parts[goog.uri.utils.ComponentIndex.DOMAIN],
|
| + parts[goog.uri.utils.ComponentIndex.PORT],
|
| + path,
|
| + parts[goog.uri.utils.ComponentIndex.QUERY_DATA],
|
| + parts[goog.uri.utils.ComponentIndex.FRAGMENT]);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Standard supported query parameters.
|
| + * @enum {string}
|
| + */
|
| +goog.uri.utils.StandardQueryParam = {
|
| +
|
| + /** Unused parameter for unique-ifying. */
|
| + RANDOM: 'zx'
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Sets the zx parameter of a URI to a random value.
|
| + * @param {string} uri Any URI.
|
| + * @return {string} That URI with the "zx" parameter added or replaced to
|
| + * contain a random string.
|
| + */
|
| +goog.uri.utils.makeUnique = function(uri) {
|
| + return goog.uri.utils.setParam(uri,
|
| + goog.uri.utils.StandardQueryParam.RANDOM, goog.string.getRandomString());
|
| +};
|
|
|