//javascript/closure/base.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Bootstrap for the Google JS Library (Closure).
 *
 * In uncompiled mode base.js will attempt to load Closure's deps file, unless
 * the global <code>CLOSURE_NO_DEPS</code> is set to true.  This allows projects
 * to include their own deps file(s) from different locations.
 *
 * Avoid including base.js more than once. This is strictly discouraged and not
 * supported. goog.require(...) won't work properly in that case.
 *
 * @provideGoog
 */


/**
 * @define {boolean} Overridden to true by the compiler.
 */
var COMPILED = false;


/**
 * Base namespace for the Closure library.  Checks to see goog is already
 * defined in the current scope before assigning to prevent clobbering if
 * base.js is loaded more than once.
 *
 * @const
 */
var goog = goog || {};

/**
 * Reference to the global object.
 * https://www.ecma-international.org/ecma-262/9.0/index.html#sec-global-object
 *
 * More info on this implementation here:
 * https://docs.google.com/document/d/1NAeW4Wk7I7FV0Y2tcUFvQdGMc89k2vdgSXInw8_nvCI/edit
 *
 * @const
 * @suppress {undefinedVars} self won't be referenced unless `this` is falsy.
 * @type {!Global}
 */
goog.global =
    // Check `this` first for backwards compatibility.
    // Valid unless running as an ES module or in a function wrapper called
    //   without setting `this` properly.
    // Note that base.js can't usefully be imported as an ES module, but it may
    // be compiled into bundles that are loadable as ES modules.
    this ||
    // https://developer.mozilla.org/en-US/docs/Web/API/Window/self
    // For in-page browser environments and workers.
    self;


/**
 * A hook for overriding the define values in uncompiled mode.
 *
 * In uncompiled mode, `CLOSURE_UNCOMPILED_DEFINES` may be defined before
 * loading base.js.  If a key is defined in `CLOSURE_UNCOMPILED_DEFINES`,
 * `goog.define` will use the value instead of the default value.  This
 * allows flags to be overwritten without compilation (this is normally
 * accomplished with the compiler's "define" flag).
 *
 * Example:
 * <pre>
 *   var CLOSURE_UNCOMPILED_DEFINES = {'goog.DEBUG': false};
 * </pre>
 *
 * @type {Object<string, (string|number|boolean)>|undefined}
 */
goog.global.CLOSURE_UNCOMPILED_DEFINES;


/**
 * A hook for overriding the define values in uncompiled or compiled mode,
 * like CLOSURE_UNCOMPILED_DEFINES but effective in compiled code.  In
 * uncompiled code CLOSURE_UNCOMPILED_DEFINES takes precedence.
 *
 * Also unlike CLOSURE_UNCOMPILED_DEFINES the values must be number, boolean or
 * string literals or the compiler will emit an error.
 *
 * While any @define value may be set, only those set with goog.define will be
 * effective for uncompiled code.
 *
 * Example:
 * <pre>
 *   var CLOSURE_DEFINES = {'goog.DEBUG': false} ;
 * </pre>
 *
 * @type {Object<string, (string|number|boolean)>|undefined}
 */
goog.global.CLOSURE_DEFINES;


/**
 * Builds an object structure for the provided namespace path, ensuring that
 * names that already exist are not overwritten. For example:
 * "a.b.c" -> a = {};a.b={};a.b.c={};
 * Used by goog.provide and goog.exportSymbol.
 * @param {string} name name of the object that this file defines.
 * @param {*=} opt_object the object to expose at the end of the path.
 * @param {Object=} opt_objectToExportTo The object to add the path to; default
 *     is `goog.global`.
 * @private
 */
goog.exportPath_ = function(name, opt_object, opt_objectToExportTo) {
  var parts = name.split('.');
  var cur = opt_objectToExportTo || goog.global;

  // Internet Explorer exhibits strange behavior when throwing errors from
  // methods externed in this manner.  See the testExportSymbolExceptions in
  // base_test.html for an example.
  if (!(parts[0] in cur) && typeof cur.execScript != 'undefined') {
    cur.execScript('var ' + parts[0]);
  }

  for (var part; parts.length && (part = parts.shift());) {
    if (!parts.length && opt_object !== undefined) {
      // last part and we have an object; use it
      cur[part] = opt_object;
    } else if (cur[part] && cur[part] !== Object.prototype[part]) {
      cur = cur[part];
    } else {
      cur = cur[part] = {};
    }
  }
};


/**
 * Defines a named value. In uncompiled mode, the value is retrieved from
 * CLOSURE_DEFINES or CLOSURE_UNCOMPILED_DEFINES if the object is defined and
 * has the property specified, and otherwise used the defined defaultValue.
 * When compiled the default can be overridden using the compiler options or the
 * value set in the CLOSURE_DEFINES object. Returns the defined value so that it
 * can be used safely in modules. Note that the value type MUST be either
 * boolean, number, or string.
 *
 * @param {string} name The distinguished name to provide.
 * @param {T} defaultValue
 * @return {T} The defined value.
 * @template T
 */
goog.define = function(name, defaultValue) {
  var value = defaultValue;
  if (!COMPILED) {
    var uncompiledDefines = goog.global.CLOSURE_UNCOMPILED_DEFINES;
    var defines = goog.global.CLOSURE_DEFINES;
    if (uncompiledDefines &&
        // Anti DOM-clobbering runtime check (b/37736576).
        /** @type {?} */ (uncompiledDefines).nodeType === undefined &&
        Object.prototype.hasOwnProperty.call(uncompiledDefines, name)) {
      value = uncompiledDefines[name];
    } else if (
        defines &&
        // Anti DOM-clobbering runtime check (b/37736576).
        /** @type {?} */ (defines).nodeType === undefined &&
        Object.prototype.hasOwnProperty.call(defines, name)) {
      value = defines[name];
    }
  }
  return value;
};


/**
 * @define {number} Integer year indicating the set of browser features that are
 * guaranteed to be present.  This is defined to include exactly features that
 * work correctly on all "modern" browsers that are stable on January 1 of the
 * specified year.  For example,
 * ```js
 * if (goog.FEATURESET_YEAR >= 2019) {
 *   // use APIs known to be available on all major stable browsers Jan 1, 2019
 * } else {
 *   // polyfill for older browsers
 * }
 * ```
 * This is intended to be the primary define for removing
 * unnecessary browser compatibility code (such as ponyfills and workarounds),
 * and should inform the default value for most other defines:
 * ```js
 * const ASSUME_NATIVE_PROMISE =
 *     goog.define('ASSUME_NATIVE_PROMISE', goog.FEATURESET_YEAR >= 2016);
 * ```
 *
 * The default assumption is that IE9 is the lowest supported browser, which was
 * first available Jan 1, 2012.
 *
 * TODO(mathiasb): Reference more thorough documentation when it's available.
 */
goog.FEATURESET_YEAR = goog.define('goog.FEATURESET_YEAR', 2012);


/**
 * @define {boolean} DEBUG is provided as a convenience so that debugging code
 * that should not be included in a production. It can be easily stripped
 * by specifying --define goog.DEBUG=false to the Closure Compiler aka
 * JSCompiler. For example, most toString() methods should be declared inside an
 * "if (goog.DEBUG)" conditional because they are generally used for debugging
 * purposes and it is difficult for the JSCompiler to statically determine
 * whether they are used.
 */
goog.DEBUG = goog.define('goog.DEBUG', true);


/**
 * @define {string} LOCALE defines the locale being used for compilation. It is
 * used to select locale specific data to be compiled in js binary. BUILD rule
 * can specify this value by "--define goog.LOCALE=<locale_name>" as a compiler
 * option.
 *
 * Take into account that the locale code format is important. You should use
 * the canonical Unicode format with hyphen as a delimiter. Language must be
 * lowercase, Language Script - Capitalized, Region - UPPERCASE.
 * There are few examples: pt-BR, en, en-US, sr-Latin-BO, zh-Hans-CN.
 *
 * See more info about locale codes here:
 * http://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers
 *
 * For language codes you should use values defined by ISO 693-1. See it here
 * http://www.w3.org/WAI/ER/IG/ert/iso639.htm. There is only one exception from
 * this rule: the Hebrew language. For legacy reasons the old code (iw) should
 * be used instead of the new code (he).
 *
 * MOE:begin_intracomment_strip
 * See http://g3doc/i18n/identifiers/g3doc/synonyms.
 * MOE:end_intracomment_strip
 */
goog.LOCALE = goog.define('goog.LOCALE', 'en');  // default to en


/**
 * @define {boolean} Whether this code is running on trusted sites.
 *
 * On untrusted sites, several native functions can be defined or overridden by
 * external libraries like Prototype, Datejs, and JQuery and setting this flag
 * to false forces closure to use its own implementations when possible.
 *
 * If your JavaScript can be loaded by a third party site and you are wary about
 * relying on non-standard implementations, specify
 * "--define goog.TRUSTED_SITE=false" to the compiler.
 */
goog.TRUSTED_SITE = goog.define('goog.TRUSTED_SITE', true);


/**
 * @define {boolean} Whether a project is expected to be running in strict mode.
 *
 * This define can be used to trigger alternate implementations compatible with
 * running in EcmaScript Strict mode or warn about unavailable functionality.
 * @see https://goo.gl/PudQ4y
 *
 */
goog.STRICT_MODE_COMPATIBLE = goog.define('goog.STRICT_MODE_COMPATIBLE', false);


/**
 * @define {boolean} Whether code that calls {@link goog.setTestOnly} should
 *     be disallowed in the compilation unit.
 */
goog.DISALLOW_TEST_ONLY_CODE =
    goog.define('goog.DISALLOW_TEST_ONLY_CODE', COMPILED && !goog.DEBUG);


/**
 * @define {boolean} Whether to use a Chrome app CSP-compliant method for
 *     loading scripts via goog.require. @see appendScriptSrcNode_.
 */
goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING =
    goog.define('goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING', false);


/**
 * Defines a namespace in Closure.
 *
 * A namespace may only be defined once in a codebase. It may be defined using
 * goog.provide() or goog.module().
 *
 * The presence of one or more goog.provide() calls in a file indicates
 * that the file defines the given objects/namespaces.
 * Provided symbols must not be null or undefined.
 *
 * In addition, goog.provide() creates the object stubs for a namespace
 * (for example, goog.provide("goog.foo.bar") will create the object
 * goog.foo.bar if it does not already exist).
 *
 * Build tools also scan for provide/require/module statements
 * to discern dependencies, build dependency files (see deps.js), etc.
 *
 * @see goog.require
 * @see goog.module
 * @param {string} name Namespace provided by this file in the form
 *     "goog.package.part".
 */
goog.provide = function(name) {
  if (goog.isInModuleLoader_()) {
    throw new Error('goog.provide cannot be used within a module.');
  }
  if (!COMPILED) {
    // Ensure that the same namespace isn't provided twice.
    // A goog.module/goog.provide maps a goog.require to a specific file
    if (goog.isProvided_(name)) {
      throw new Error('Namespace "' + name + '" already declared.');
    }
  }

  goog.constructNamespace_(name);
};


/**
 * @param {string} name Namespace provided by this file in the form
 *     "goog.package.part".
 * @param {Object=} opt_obj The object to embed in the namespace.
 * @private
 */
goog.constructNamespace_ = function(name, opt_obj) {
  if (!COMPILED) {
    delete goog.implicitNamespaces_[name];

    var namespace = name;
    while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) {
      if (goog.getObjectByName(namespace)) {
        break;
      }
      goog.implicitNamespaces_[namespace] = true;
    }
  }

  goog.exportPath_(name, opt_obj);
};


/**
 * Returns CSP nonce, if set for any script tag.
 * @param {?Window=} opt_window The window context used to retrieve the nonce.
 *     Defaults to global context.
 * @return {string} CSP nonce or empty string if no nonce is present.
 */
goog.getScriptNonce = function(opt_window) {
  if (opt_window && opt_window != goog.global) {
    return goog.getScriptNonce_(opt_window.document);
  }
  if (goog.cspNonce_ === null) {
    goog.cspNonce_ = goog.getScriptNonce_(goog.global.document);
  }
  return goog.cspNonce_;
};


/**
 * According to the CSP3 spec a nonce must be a valid base64 string.
 * @see https://www.w3.org/TR/CSP3/#grammardef-base64-value
 * @private @const
 */
goog.NONCE_PATTERN_ = /^[\w+/_-]+[=]{0,2}$/;


/**
 * @private {?string}
 */
goog.cspNonce_ = null;


/**
 * Returns CSP nonce, if set for any script tag.
 * @param {!Document} doc
 * @return {string} CSP nonce or empty string if no nonce is present.
 * @private
 */
goog.getScriptNonce_ = function(doc) {
  var script = doc.querySelector && doc.querySelector('script[nonce]');
  if (script) {
    // Try to get the nonce from the IDL property first, because browsers that
    // implement additional nonce protection features (currently only Chrome) to
    // prevent nonce stealing via CSS do not expose the nonce via attributes.
    // See https://github.com/whatwg/html/issues/2369
    var nonce = script['nonce'] || script.getAttribute('nonce');
    if (nonce && goog.NONCE_PATTERN_.test(nonce)) {
      return nonce;
    }
  }
  return '';
};


/**
 * Module identifier validation regexp.
 * Note: This is a conservative check, it is very possible to be more lenient,
 *   the primary exclusion here is "/" and "\" and a leading ".", these
 *   restrictions are intended to leave the door open for using goog.require
 *   with relative file paths rather than module identifiers.
 * @private
 */
goog.VALID_MODULE_RE_ = /^[a-zA-Z_$][a-zA-Z0-9._$]*$/;


/**
 * Defines a module in Closure.
 *
 * Marks that this file must be loaded as a module and claims the namespace.
 *
 * A namespace may only be defined once in a codebase. It may be defined using
 * goog.provide() or goog.module().
 *
 * goog.module() has three requirements:
 * - goog.module may not be used in the same file as goog.provide.
 * - goog.module must be the first statement in the file.
 * - only one goog.module is allowed per file.
 *
 * When a goog.module annotated file is loaded, it is enclosed in
 * a strict function closure. This means that:
 * - any variables declared in a goog.module file are private to the file
 * (not global), though the compiler is expected to inline the module.
 * - The code must obey all the rules of "strict" JavaScript.
 * - the file will be marked as "use strict"
 *
 * NOTE: unlike goog.provide, goog.module does not declare any symbols by
 * itself. If declared symbols are desired, use
 * goog.module.declareLegacyNamespace().
 *
 * MOE:begin_intracomment_strip
 * See the goog.module announcement at http://go/goog.module-announce
 * MOE:end_intracomment_strip
 *
 * See the public goog.module proposal: http://goo.gl/Va1hin
 *
 * @param {string} name Namespace provided by this file in the form
 *     "goog.package.part", is expected but not required.
 * @return {void}
 */
goog.module = function(name) {
  if (typeof name !== 'string' || !name ||
      name.search(goog.VALID_MODULE_RE_) == -1) {
    throw new Error('Invalid module identifier');
  }
  if (!goog.isInGoogModuleLoader_()) {
    throw new Error(
        'Module ' + name + ' has been loaded incorrectly. Note, ' +
        'modules cannot be loaded as normal scripts. They require some kind of ' +
        'pre-processing step. You\'re likely trying to load a module via a ' +
        'script tag or as a part of a concatenated bundle without rewriting the ' +
        'module. For more info see: ' +
        'https://github.com/google/closure-library/wiki/goog.module:-an-ES6-module-like-alternative-to-goog.provide.');
  }
  if (goog.moduleLoaderState_.moduleName) {
    throw new Error('goog.module may only be called once per module.');
  }

  // Store the module name for the loader.
  goog.moduleLoaderState_.moduleName = name;
  if (!COMPILED) {
    // Ensure that the same namespace isn't provided twice.
    // A goog.module/goog.provide maps a goog.require to a specific file
    if (goog.isProvided_(name)) {
      throw new Error('Namespace "' + name + '" already declared.');
    }
    delete goog.implicitNamespaces_[name];
  }
};


/**
 * @param {string} name The module identifier.
 * @return {?} The module exports for an already loaded module or null.
 *
 * Note: This is not an alternative to goog.require, it does not
 * indicate a hard dependency, instead it is used to indicate
 * an optional dependency or to access the exports of a module
 * that has already been loaded.
 * @suppress {missingProvide}
 */
goog.module.get = function(name) {
  return goog.module.getInternal_(name);
};


/**
 * @param {string} name The module identifier.
 * @return {?} The module exports for an already loaded module or null.
 * @private
 */
goog.module.getInternal_ = function(name) {
  if (!COMPILED) {
    if (name in goog.loadedModules_) {
      return goog.loadedModules_[name].exports;
    } else if (!goog.implicitNamespaces_[name]) {
      var ns = goog.getObjectByName(name);
      return ns != null ? ns : null;
    }
  }
  return null;
};


/**
 * Types of modules the debug loader can load.
 * @enum {string}
 */
goog.ModuleType = {
  ES6: 'es6',
  GOOG: 'goog'
};


/**
 * @private {?{
 *   moduleName: (string|undefined),
 *   declareLegacyNamespace:boolean,
 *   type: ?goog.ModuleType
 * }}
 */
goog.moduleLoaderState_ = null;


/**
 * @private
 * @return {boolean} Whether a goog.module or an es6 module is currently being
 *     initialized.
 */
goog.isInModuleLoader_ = function() {
  return goog.isInGoogModuleLoader_() || goog.isInEs6ModuleLoader_();
};


/**
 * @private
 * @return {boolean} Whether a goog.module is currently being initialized.
 */
goog.isInGoogModuleLoader_ = function() {
  return !!goog.moduleLoaderState_ &&
      goog.moduleLoaderState_.type == goog.ModuleType.GOOG;
};


/**
 * @private
 * @return {boolean} Whether an es6 module is currently being initialized.
 */
goog.isInEs6ModuleLoader_ = function() {
  var inLoader = !!goog.moduleLoaderState_ &&
      goog.moduleLoaderState_.type == goog.ModuleType.ES6;

  if (inLoader) {
    return true;
  }

  var jscomp = goog.global['$jscomp'];

  if (jscomp) {
    // jscomp may not have getCurrentModulePath if this is a compiled bundle
    // that has some of the runtime, but not all of it. This can happen if
    // optimizations are turned on so the unused runtime is removed but renaming
    // and Closure pass are off (so $jscomp is still named $jscomp and the
    // goog.provide/require calls still exist).
    if (typeof jscomp.getCurrentModulePath != 'function') {
      return false;
    }

    // Bundled ES6 module.
    return !!jscomp.getCurrentModulePath();
  }

  return false;
};


/**
 * Provide the module's exports as a globally accessible object under the
 * module's declared name.  This is intended to ease migration to goog.module
 * for files that have existing usages.
 * @suppress {missingProvide}
 */
goog.module.declareLegacyNamespace = function() {
  if (!COMPILED && !goog.isInGoogModuleLoader_()) {
    throw new Error(
        'goog.module.declareLegacyNamespace must be called from ' +
        'within a goog.module');
  }
  if (!COMPILED && !goog.moduleLoaderState_.moduleName) {
    throw new Error(
        'goog.module must be called prior to ' +
        'goog.module.declareLegacyNamespace.');
  }
  goog.moduleLoaderState_.declareLegacyNamespace = true;
};


/**
 * Associates an ES6 module with a Closure module ID so that is available via
 * goog.require. The associated ID  acts like a goog.module ID - it does not
 * create any global names, it is merely available via goog.require /
 * goog.module.get / goog.forwardDeclare / goog.requireType. goog.require and
 * goog.module.get will return the entire module as if it was import *'d. This
 * allows Closure files to reference ES6 modules for the sake of migration.
 *
 * @param {string} namespace
 * @suppress {missingProvide}
 */
goog.declareModuleId = function(namespace) {
  if (!COMPILED) {
    if (!goog.isInEs6ModuleLoader_()) {
      throw new Error(
          'goog.declareModuleId may only be called from ' +
          'within an ES6 module');
    }
    if (goog.moduleLoaderState_ && goog.moduleLoaderState_.moduleName) {
      throw new Error(
          'goog.declareModuleId may only be called once per module.');
    }
    if (namespace in goog.loadedModules_) {
      throw new Error(
          'Module with namespace "' + namespace + '" already exists.');
    }
  }
  if (goog.moduleLoaderState_) {
    // Not bundled - debug loading.
    goog.moduleLoaderState_.moduleName = namespace;
  } else {
    // Bundled - not debug loading, no module loader state.
    var jscomp = goog.global['$jscomp'];
    if (!jscomp || typeof jscomp.getCurrentModulePath != 'function') {
      throw new Error(
          'Module with namespace "' + namespace +
          '" has been loaded incorrectly.');
    }
    var exports = jscomp.require(jscomp.getCurrentModulePath());
    goog.loadedModules_[namespace] = {
      exports: exports,
      type: goog.ModuleType.ES6,
      moduleId: namespace
    };
  }
};


/**
 * Marks that the current file should only be used for testing, and never for
 * live code in production.
 *
 * In the case of unit tests, the message may optionally be an exact namespace
 * for the test (e.g. 'goog.stringTest'). The linter will then ignore the extra
 * provide (if not explicitly defined in the code).
 *
 * @param {string=} opt_message Optional message to add to the error that's
 *     raised when used in production code.
 */
goog.setTestOnly = function(opt_message) {
  if (goog.DISALLOW_TEST_ONLY_CODE) {
    opt_message = opt_message || '';
    throw new Error(
        'Importing test-only code into non-debug environment' +
        (opt_message ? ': ' + opt_message : '.'));
  }
};


/**
 * Forward declares a symbol. This is an indication to the compiler that the
 * symbol may be used in the source yet is not required and may not be provided
 * in compilation.
 *
 * The most common usage of forward declaration is code that takes a type as a
 * function parameter but does not need to require it. By forward declaring
 * instead of requiring, no hard dependency is made, and (if not required
 * elsewhere) the namespace may never be required and thus, not be pulled
 * into the JavaScript binary. If it is required elsewhere, it will be type
 * checked as normal.
 *
 * Before using goog.forwardDeclare, please read the documentation at
 * https://github.com/google/closure-compiler/wiki/Bad-Type-Annotation to
 * understand the options and tradeoffs when working with forward declarations.
 *
 * @param {string} name The namespace to forward declare in the form of
 *     "goog.package.part".
 * @deprecated See go/noforwarddeclaration, Use `goog.requireType` instead.
 */
goog.forwardDeclare = function(name) {};


/**
 * Forward declare type information. Used to assign types to goog.global
 * referenced object that would otherwise result in unknown type references
 * and thus block property disambiguation.
 */
goog.forwardDeclare('Document');
goog.forwardDeclare('HTMLScriptElement');
goog.forwardDeclare('XMLHttpRequest');


if (!COMPILED) {
  /**
   * Check if the given name has been goog.provided. This will return false for
   * names that are available only as implicit namespaces.
   * @param {string} name name of the object to look for.
   * @return {boolean} Whether the name has been provided.
   * @private
   */
  goog.isProvided_ = function(name) {
    return (name in goog.loadedModules_) ||
        (!goog.implicitNamespaces_[name] && goog.getObjectByName(name) != null);
  };

  /**
   * Namespaces implicitly defined by goog.provide. For example,
   * goog.provide('goog.events.Event') implicitly declares that 'goog' and
   * 'goog.events' must be namespaces.
   *
   * @type {!Object<string, (boolean|undefined)>}
   * @private
   */
  goog.implicitNamespaces_ = {'goog.module': true};

  // NOTE: We add goog.module as an implicit namespace as goog.module is defined
  // here and because the existing module package has not been moved yet out of
  // the goog.module namespace. This satisifies both the debug loader and
  // ahead-of-time dependency management.
}


/**
 * Returns an object based on its fully qualified external name.  The object
 * is not found if null or undefined.  If you are using a compilation pass that
 * renames property names beware that using this function will not find renamed
 * properties.
 *
 * @param {string} name The fully qualified name.
 * @param {Object=} opt_obj The object within which to look; default is
 *     |goog.global|.
 * @return {?} The value (object or primitive) or, if not found, null.
 */
goog.getObjectByName = function(name, opt_obj) {
  var parts = name.split('.');
  var cur = opt_obj || goog.global;
  for (var i = 0; i < parts.length; i++) {
    cur = cur[parts[i]];
    if (cur == null) {
      return null;
    }
  }
  return cur;
};


/**
 * Globalizes a whole namespace, such as goog or goog.lang.
 *
 * @param {!Object} obj The namespace to globalize.
 * @param {Object=} opt_global The object to add the properties to.
 * @deprecated Properties may be explicitly exported to the global scope, but
 *     this should no longer be done in bulk.
 */
goog.globalize = function(obj, opt_global) {
  var global = opt_global || goog.global;
  for (var x in obj) {
    global[x] = obj[x];
  }
};


/**
 * Adds a dependency from a file to the files it requires.
 * @param {string} relPath The path to the js file.
 * @param {!Array<string>} provides An array of strings with
 *     the names of the objects this file provides.
 * @param {!Array<string>} requires An array of strings with
 *     the names of the objects this file requires.
 * @param {boolean|!Object<string>=} opt_loadFlags Parameters indicating
 *     how the file must be loaded.  The boolean 'true' is equivalent
 *     to {'module': 'goog'} for backwards-compatibility.  Valid properties
 *     and values include {'module': 'goog'} and {'lang': 'es6'}.
 */
goog.addDependency = function(relPath, provides, requires, opt_loadFlags) {
  if (!COMPILED && goog.DEPENDENCIES_ENABLED) {
    goog.debugLoader_.addDependency(relPath, provides, requires, opt_loadFlags);
  }
};


// MOE:begin_strip
/**
 * Whether goog.require should throw an exception if it fails.
 * @type {boolean}
 */
goog.useStrictRequires = false;


// MOE:end_strip


// NOTE(nnaze): The debug DOM loader was included in base.js as an original way
// to do "debug-mode" development.  The dependency system can sometimes be
// confusing, as can the debug DOM loader's asynchronous nature.
//
// With the DOM loader, a call to goog.require() is not blocking -- the script
// will not load until some point after the current script.  If a namespace is
// needed at runtime, it needs to be defined in a previous script, or loaded via
// require() with its registered dependencies.
//
// User-defined namespaces may need their own deps file. For a reference on
// creating a deps file, see:
// MOE:begin_strip
// Internally: http://go/deps-files and http://go/be#js_deps
// MOE:end_strip
// Externally: https://developers.google.com/closure/library/docs/depswriter
//
// Because of legacy clients, the DOM loader can't be easily removed from
// base.js.  Work was done to make it disableable or replaceable for
// different environments (DOM-less JavaScript interpreters like Rhino or V8,
// for example). See bootstrap/ for more information.


/**
 * @define {boolean} Whether to enable the debug loader.
 *
 * If enabled, a call to goog.require() will attempt to load the namespace by
 * appending a script tag to the DOM (if the namespace has been registered).
 *
 * If disabled, goog.require() will simply assert that the namespace has been
 * provided (and depend on the fact that some outside tool correctly ordered
 * the script).
 */
goog.ENABLE_DEBUG_LOADER = goog.define('goog.ENABLE_DEBUG_LOADER', true);


/**
 * @param {string} msg
 * @private
 */
goog.logToConsole_ = function(msg) {
  if (goog.global.console) {
    goog.global.console['error'](msg);
  }
};


/**
 * Implements a system for the dynamic resolution of dependencies that works in
 * parallel with the BUILD system.
 *
 * Note that all calls to goog.require will be stripped by the compiler.
 *
 * @see goog.provide
 * @param {string} namespace Namespace (as was given in goog.provide,
 *     goog.module, or goog.declareModuleId) in the form
 *     "goog.package.part".
 * @return {?} If called within a goog.module or ES6 module file, the associated
 *     namespace or module otherwise null.
 */
goog.require = function(namespace) {
  if (!COMPILED) {
    // Might need to lazy load on old IE.
    if (goog.ENABLE_DEBUG_LOADER) {
      goog.debugLoader_.requested(namespace);
    }

    // If the object already exists we do not need to do anything.
    if (goog.isProvided_(namespace)) {
      if (goog.isInModuleLoader_()) {
        return goog.module.getInternal_(namespace);
      }
    } else if (goog.ENABLE_DEBUG_LOADER) {
      var moduleLoaderState = goog.moduleLoaderState_;
      goog.moduleLoaderState_ = null;
      try {
        goog.debugLoader_.load_(namespace);
      } finally {
        goog.moduleLoaderState_ = moduleLoaderState;
      }
    }

    return null;
  }
};


/**
 * Requires a symbol for its type information. This is an indication to the
 * compiler that the symbol may appear in type annotations, yet it is not
 * referenced at runtime.
 *
 * When called within a goog.module or ES6 module file, the return value may be
 * assigned to or destructured into a variable, but it may not be otherwise used
 * in code outside of a type annotation.
 *
 * Note that all calls to goog.requireType will be stripped by the compiler.
 *
 * @param {string} namespace Namespace (as was given in goog.provide,
 *     goog.module, or goog.declareModuleId) in the form
 *     "goog.package.part".
 * @return {?}
 */
goog.requireType = function(namespace) {
  // Return an empty object so that single-level destructuring of the return
  // value doesn't crash at runtime when using the debug loader. Multi-level
  // destructuring isn't supported.
  return {};
};


/**
 * Path for included scripts.
 * @type {string}
 */
goog.basePath = '';


/**
 * A hook for overriding the base path.
 * @type {string|undefined}
 */
goog.global.CLOSURE_BASE_PATH;


/**
 * Whether to attempt to load Closure's deps file. By default, when uncompiled,
 * deps files will attempt to be loaded.
 * @type {boolean|undefined}
 */
goog.global.CLOSURE_NO_DEPS;


/**
 * A function to import a single script. This is meant to be overridden when
 * Closure is being run in non-HTML contexts, such as web workers. It's defined
 * in the global scope so that it can be set before base.js is loaded, which
 * allows deps.js to be imported properly.
 *
 * The first parameter the script source, which is a relative URI. The second,
 * optional parameter is the script contents, in the event the script needed
 * transformation. It should return true if the script was imported, false
 * otherwise.
 * @type {(function(string, string=): boolean)|undefined}
 */
goog.global.CLOSURE_IMPORT_SCRIPT;


/**
 * Null function used for default values of callbacks, etc.
 * @return {void} Nothing.
 */
goog.nullFunction = function() {};


/**
 * When defining a class Foo with an abstract method bar(), you can do:
 * Foo.prototype.bar = goog.abstractMethod
 *
 * Now if a subclass of Foo fails to override bar(), an error will be thrown
 * when bar() is invoked.
 *
 * @type {!Function}
 * @throws {Error} when invoked to indicate the method should be overridden.
 * @deprecated Use "@abstract" annotation instead of goog.abstractMethod in new
 *     code. See
 *     https://github.com/google/closure-compiler/wiki/@abstract-classes-and-methods
 */
goog.abstractMethod = function() {
  throw new Error('unimplemented abstract method');
};


/**
 * Adds a `getInstance` static method that always returns the same
 * instance object.
 * @param {!Function} ctor The constructor for the class to add the static
 *     method to.
 * @suppress {missingProperties} 'instance_' isn't a property on 'Function'
 *     but we don't have a better type to use here.
 */
goog.addSingletonGetter = function(ctor) {
  // instance_ is immediately set to prevent issues with sealed constructors
  // such as are encountered when a constructor is returned as the export object
  // of a goog.module in unoptimized code.
  // Delcare type to avoid conformance violations that ctor.instance_ is unknown
  /** @type {undefined|!Object} @suppress {underscore} */
  ctor.instance_ = undefined;
  ctor.getInstance = function() {
    if (ctor.instance_) {
      return ctor.instance_;
    }
    if (goog.DEBUG) {
      // NOTE: JSCompiler can't optimize away Array#push.
      goog.instantiatedSingletons_[goog.instantiatedSingletons_.length] = ctor;
    }
    // Cast to avoid conformance violations that ctor.instance_ is unknown
    return /** @type {!Object|undefined} */ (ctor.instance_) = new ctor;
  };
};


/**
 * All singleton classes that have been instantiated, for testing. Don't read
 * it directly, use the `goog.testing.singleton` module. The compiler
 * removes this variable if unused.
 * @type {!Array<!Function>}
 * @private
 */
goog.instantiatedSingletons_ = [];


/**
 * @define {boolean} Whether to load goog.modules using `eval` when using
 * the debug loader.  This provides a better debugging experience as the
 * source is unmodified and can be edited using Chrome Workspaces or similar.
 * However in some environments the use of `eval` is banned
 * so we provide an alternative.
 */
goog.LOAD_MODULE_USING_EVAL = goog.define('goog.LOAD_MODULE_USING_EVAL', true);


/**
 * @define {boolean} Whether the exports of goog.modules should be sealed when
 * possible.
 */
goog.SEAL_MODULE_EXPORTS = goog.define('goog.SEAL_MODULE_EXPORTS', goog.DEBUG);


/**
 * The registry of initialized modules:
 * The module identifier or path to module exports map.
 * @private @const {!Object<string, {exports:?,type:string,moduleId:string}>}
 */
goog.loadedModules_ = {};


/**
 * True if the debug loader enabled and used.
 * @const {boolean}
 */
goog.DEPENDENCIES_ENABLED = !COMPILED && goog.ENABLE_DEBUG_LOADER;


/**
 * @define {string} How to decide whether to transpile.  Valid values
 * are 'always', 'never', and 'detect'.  The default ('detect') is to
 * use feature detection to determine which language levels need
 * transpilation.
 */
// NOTE(sdh): we could expand this to accept a language level to bypass
// detection: e.g. goog.TRANSPILE == 'es5' would transpile ES6 files but
// would leave ES3 and ES5 files alone.
goog.TRANSPILE = goog.define('goog.TRANSPILE', 'detect');

/**
 * @define {boolean} If true assume that ES modules have already been
 * transpiled by the jscompiler (in the same way that transpile.js would
 * transpile them - to jscomp modules). Useful only for servers that wish to use
 * the debug loader and transpile server side. Thus this is only respected if
 * goog.TRANSPILE is "never".
 */
goog.ASSUME_ES_MODULES_TRANSPILED =
    goog.define('goog.ASSUME_ES_MODULES_TRANSPILED', false);


/**
 * @define {string} If a file needs to be transpiled what the output language
 * should be. By default this is the highest language level this file detects
 * the current environment supports. Generally this flag should not be set, but
 * it could be useful to override. Example: If the current environment supports
 * ES6 then by default ES7+ files will be transpiled to ES6, unless this is
 * overridden.
 *
 * Valid values include: es3, es5, es6, es7, and es8. Anything not recognized
 * is treated as es3.
 *
 * Note that setting this value does not force transpilation. Just if
 * transpilation occurs this will be the output. So this is most useful when
 * goog.TRANSPILE is set to 'always' and then forcing the language level to be
 * something lower than what the environment detects.
 */
goog.TRANSPILE_TO_LANGUAGE = goog.define('goog.TRANSPILE_TO_LANGUAGE', '');


/**
 * @define {string} Path to the transpiler.  Executing the script at this
 * path (relative to base.js) should define a function $jscomp.transpile.
 */
goog.TRANSPILER = goog.define('goog.TRANSPILER', 'transpile.js');


/**
 * @package {?boolean}
 * Visible for testing.
 */
goog.hasBadLetScoping = null;


/**
 * @return {boolean}
 * @package Visible for testing.
 */
goog.useSafari10Workaround = function() {
  if (goog.hasBadLetScoping == null) {
    var hasBadLetScoping;
    try {
      hasBadLetScoping = !eval(
          '"use strict";' +
          'let x = 1; function f() { return typeof x; };' +
          'f() == "number";');
    } catch (e) {
      // Assume that ES6 syntax isn't supported.
      hasBadLetScoping = false;
    }
    goog.hasBadLetScoping = hasBadLetScoping;
  }
  return goog.hasBadLetScoping;
};


/**
 * @param {string} moduleDef
 * @return {string}
 * @package Visible for testing.
 */
goog.workaroundSafari10EvalBug = function(moduleDef) {
  return '(function(){' + moduleDef +
      '\n' +  // Terminate any trailing single line comment.
      ';' +   // Terminate any trailing expression.
      '})();\n';
};


/**
 * @param {function(?):?|string} moduleDef The module definition.
 */
goog.loadModule = function(moduleDef) {
  // NOTE: we allow function definitions to be either in the from
  // of a string to eval (which keeps the original source intact) or
  // in a eval forbidden environment (CSP) we allow a function definition
  // which in its body must call `goog.module`, and return the exports
  // of the module.
  var previousState = goog.moduleLoaderState_;
  try {
    goog.moduleLoaderState_ = {
      moduleName: '',
      declareLegacyNamespace: false,
      type: goog.ModuleType.GOOG
    };
    var exports;
    if (goog.isFunction(moduleDef)) {
      exports = moduleDef.call(undefined, {});
    } else if (typeof moduleDef === 'string') {
      if (goog.useSafari10Workaround()) {
        moduleDef = goog.workaroundSafari10EvalBug(moduleDef);
      }

      exports = goog.loadModuleFromSource_.call(undefined, moduleDef);
    } else {
      throw new Error('Invalid module definition');
    }

    var moduleName = goog.moduleLoaderState_.moduleName;
    if (typeof moduleName === 'string' && moduleName) {
      // Don't seal legacy namespaces as they may be used as a parent of
      // another namespace
      if (goog.moduleLoaderState_.declareLegacyNamespace) {
        goog.constructNamespace_(moduleName, exports);
      } else if (
          goog.SEAL_MODULE_EXPORTS && Object.seal &&
          typeof exports == 'object' && exports != null) {
        Object.seal(exports);
      }

      var data = {
        exports: exports,
        type: goog.ModuleType.GOOG,
        moduleId: goog.moduleLoaderState_.moduleName
      };
      goog.loadedModules_[moduleName] = data;
    } else {
      throw new Error('Invalid module name \"' + moduleName + '\"');
    }
  } finally {
    goog.moduleLoaderState_ = previousState;
  }
};


/**
 * @private @const
 */
goog.loadModuleFromSource_ = /** @type {function(string):?} */ (function() {
  // NOTE: we avoid declaring parameters or local variables here to avoid
  // masking globals or leaking values into the module definition.
  'use strict';
  var exports = {};
  eval(arguments[0]);
  return exports;
});


/**
 * Normalize a file path by removing redundant ".." and extraneous "." file
 * path components.
 * @param {string} path
 * @return {string}
 * @private
 */
goog.normalizePath_ = function(path) {
  var components = path.split('/');
  var i = 0;
  while (i < components.length) {
    if (components[i] == '.') {
      components.splice(i, 1);
    } else if (
        i && components[i] == '..' && components[i - 1] &&
        components[i - 1] != '..') {
      components.splice(--i, 2);
    } else {
      i++;
    }
  }
  return components.join('/');
};


/**
 * Provides a hook for loading a file when using Closure's goog.require() API
 * with goog.modules.  In particular this hook is provided to support Node.js.
 *
 * @type {(function(string):string)|undefined}
 */
goog.global.CLOSURE_LOAD_FILE_SYNC;


/**
 * Loads file by synchronous XHR. Should not be used in production environments.
 * @param {string} src Source URL.
 * @return {?string} File contents, or null if load failed.
 * @private
 */
goog.loadFileSync_ = function(src) {
  if (goog.global.CLOSURE_LOAD_FILE_SYNC) {
    return goog.global.CLOSURE_LOAD_FILE_SYNC(src);
  } else {
    try {
      /** @type {XMLHttpRequest} */
      var xhr = new goog.global['XMLHttpRequest']();
      xhr.open('get', src, false);
      xhr.send();
      // NOTE: Successful http: requests have a status of 200, but successful
      // file: requests may have a status of zero.  Any other status, or a
      // thrown exception (particularly in case of file: requests) indicates
      // some sort of error, which we treat as a missing or unavailable file.
      return xhr.status == 0 || xhr.status == 200 ? xhr.responseText : null;
    } catch (err) {
      // No need to rethrow or log, since errors should show up on their own.
      return null;
    }
  }
};


/**
 * Lazily retrieves the transpiler and applies it to the source.
 * @param {string} code JS code.
 * @param {string} path Path to the code.
 * @param {string} target Language level output.
 * @return {string} The transpiled code.
 * @private
 */
goog.transpile_ = function(code, path, target) {
  var jscomp = goog.global['$jscomp'];
  if (!jscomp) {
    goog.global['$jscomp'] = jscomp = {};
  }
  var transpile = jscomp.transpile;
  if (!transpile) {
    var transpilerPath = goog.basePath + goog.TRANSPILER;
    var transpilerCode = goog.loadFileSync_(transpilerPath);
    if (transpilerCode) {
      // This must be executed synchronously, since by the time we know we
      // need it, we're about to load and write the ES6 code synchronously,
      // so a normal script-tag load will be too slow. Wrapped in a function
      // so that code is eval'd in the global scope.
      (function() {
        (0, eval)(transpilerCode + '\n//# sourceURL=' + transpilerPath);
      }).call(goog.global);
      // Even though the transpiler is optional, if $gwtExport is found, it's
      // a sign the transpiler was loaded and the $jscomp.transpile *should*
      // be there.
      if (goog.global['$gwtExport'] && goog.global['$gwtExport']['$jscomp'] &&
          !goog.global['$gwtExport']['$jscomp']['transpile']) {
        throw new Error(
            'The transpiler did not properly export the "transpile" ' +
            'method. $gwtExport: ' + JSON.stringify(goog.global['$gwtExport']));
      }
      // transpile.js only exports a single $jscomp function, transpile. We
      // grab just that and add it to the existing definition of $jscomp which
      // contains the polyfills.
      goog.global['$jscomp'].transpile =
          goog.global['$gwtExport']['$jscomp']['transpile'];
      jscomp = goog.global['$jscomp'];
      transpile = jscomp.transpile;
    }
  }
  if (!transpile) {
    // The transpiler is an optional component.  If it's not available then
    // replace it with a pass-through function that simply logs.
    var suffix = ' requires transpilation but no transpiler was found.';
    // MOE:begin_strip
    suffix +=  // Provide a more appropriate message internally.
        ' Please add "//javascript/closure:transpiler" as a data ' +
        'dependency to ensure it is included.';
    // MOE:end_strip
    transpile = jscomp.transpile = function(code, path) {
      // TODO(sdh): figure out some way to get this error to show up
      // in test results, noting that the failure may occur in many
      // different ways, including in loadModule() before the test
      // runner even comes up.
      goog.logToConsole_(path + suffix);
      return code;
    };
  }
  // Note: any transpilation errors/warnings will be logged to the console.
  return transpile(code, path, target);
};

//==============================================================================
// Language Enhancements
//==============================================================================


/**
 * This is a "fixed" version of the typeof operator.  It differs from the typeof
 * operator in such a way that null returns 'null' and arrays return 'array'.
 * @param {?} value The value to get the type of.
 * @return {string} The name of the type.
 */
goog.typeOf = function(value) {
  var s = typeof value;
  if (s == 'object') {
    if (value) {
      // Check these first, so we can avoid calling Object.prototype.toString if
      // possible.
      //
      // IE9 and below improperly marshals typeof across execution contexts, but
      // a cross-context object will still return false for "instanceof Object".
      if (value instanceof Array) {
        return 'array';
      } else if (value instanceof Object) {
        return s;
      }

      // HACK: In order to use an Object prototype method on the arbitrary
      //   value, the compiler requires the value be cast to type Object,
      //   even though the ECMA spec explicitly allows it.
      var className = Object.prototype.toString.call(
          /** @type {!Object} */ (value));
      // In Firefox 3.6, attempting to access iframe window objects' length
      // property throws an NS_ERROR_FAILURE, so we need to special-case it
      // here.
      if (className == '[object Window]') {
        return 'object';
      }

      // We cannot always use constructor == Array or instanceof Array because
      // different frames have different Array objects. In IE6, if the iframe
      // where the array was created is destroyed, the array loses its
      // prototype. Then dereferencing val.splice here throws an exception, so
      // we can't use goog.isFunction. Calling typeof directly returns 'unknown'
      // so that will work. In this case, this function will return false and
      // most array functions will still work because the array is still
      // array-like (supports length and []) even though it has lost its
      // prototype.
      // Mark Miller noticed that Object.prototype.toString
      // allows access to the unforgeable [[Class]] property.
      //  15.2.4.2 Object.prototype.toString ( )
      //  When the toString method is called, the following steps are taken:
      //      1. Get the [[Class]] property of this object.
      //      2. Compute a string value by concatenating the three strings
      //         "[object ", Result(1), and "]".
      //      3. Return Result(2).
      // and this behavior survives the destruction of the execution context.
      if ((className == '[object Array]' ||
           // In IE all non value types are wrapped as objects across window
           // boundaries (not iframe though) so we have to do object detection
           // for this edge case.
           typeof value.length == 'number' &&
               typeof value.splice != 'undefined' &&
               typeof value.propertyIsEnumerable != 'undefined' &&
               !value.propertyIsEnumerable('splice')

               )) {
        return 'array';
      }
      // HACK: There is still an array case that fails.
      //     function ArrayImpostor() {}
      //     ArrayImpostor.prototype = [];
      //     var impostor = new ArrayImpostor;
      // this can be fixed by getting rid of the fast path
      // (value instanceof Array) and solely relying on
      // (value && Object.prototype.toString.vall(value) === '[object Array]')
      // but that would require many more function calls and is not warranted
      // unless closure code is receiving objects from untrusted sources.

      // IE in cross-window calls does not correctly marshal the function type
      // (it appears just as an object) so we cannot use just typeof val ==
      // 'function'. However, if the object has a call property, it is a
      // function.
      if ((className == '[object Function]' ||
           typeof value.call != 'undefined' &&
               typeof value.propertyIsEnumerable != 'undefined' &&
               !value.propertyIsEnumerable('call'))) {
        return 'function';
      }

    } else {
      return 'null';
    }

  } else if (s == 'function' && typeof value.call == 'undefined') {
    // In Safari typeof nodeList returns 'function', and on Firefox typeof
    // behaves similarly for HTML{Applet,Embed,Object}, Elements and RegExps. We
    // would like to return object for those and we can detect an invalid
    // function by making sure that the function object has a call method.
    return 'object';
  }
  return s;
};


/**
 * Returns true if the specified value is an array.
 * @param {?} val Variable to test.
 * @return {boolean} Whether variable is an array.
 * @deprecated Use Array.isArray instead.
 */
goog.isArray = function(val) {
  return goog.typeOf(val) == 'array';
};


/**
 * Returns true if the object looks like an array. To qualify as array like
 * the value needs to be either a NodeList or an object with a Number length
 * property. Note that for this function neither strings nor functions are
 * considered "array-like".
 *
 * @param {?} val Variable to test.
 * @return {boolean} Whether variable is an array.
 */
goog.isArrayLike = function(val) {
  var type = goog.typeOf(val);
  // We do not use goog.isObject here in order to exclude function values.
  return type == 'array' || type == 'object' && typeof val.length == 'number';
};


/**
 * Returns true if the object looks like a Date. To qualify as Date-like the
 * value needs to be an object and have a getFullYear() function.
 * @param {?} val Variable to test.
 * @return {boolean} Whether variable is a like a Date.
 */
goog.isDateLike = function(val) {
  return goog.isObject(val) && typeof val.getFullYear == 'function';
};


/**
 * Returns true if the specified value is a function.
 * @param {?} val Variable to test.
 * @return {boolean} Whether variable is a function.
 */
goog.isFunction = function(val) {
  return goog.typeOf(val) == 'function';
};


/**
 * Returns true if the specified value is an object.  This includes arrays and
 * functions.
 * @param {?} val Variable to test.
 * @return {boolean} Whether variable is an object.
 */
goog.isObject = function(val) {
  var type = typeof val;
  return type == 'object' && val != null || type == 'function';
  // return Object(val) === val also works, but is slower, especially if val is
  // not an object.
};


/**
 * Gets a unique ID for an object. This mutates the object so that further calls
 * with the same object as a parameter returns the same value. The unique ID is
 * guaranteed to be unique across the current session amongst objects that are
 * passed into `getUid`. There is no guarantee that the ID is unique or
 * consistent across sessions. It is unsafe to generate unique ID for function
 * prototypes.
 *
 * @param {Object} obj The object to get the unique ID for.
 * @return {number} The unique ID for the object.
 */
goog.getUid = function(obj) {
  // TODO(arv): Make the type stricter, do not accept null.
  return Object.prototype.hasOwnProperty.call(obj, goog.UID_PROPERTY_) &&
      obj[goog.UID_PROPERTY_] ||
      (obj[goog.UID_PROPERTY_] = ++goog.uidCounter_);
};


/**
 * Whether the given object is already assigned a unique ID.
 *
 * This does not modify the object.
 *
 * @param {!Object} obj The object to check.
 * @return {boolean} Whether there is an assigned unique id for the object.
 */
goog.hasUid = function(obj) {
  return !!obj[goog.UID_PROPERTY_];
};


/**
 * Removes the unique ID from an object. This is useful if the object was
 * previously mutated using `goog.getUid` in which case the mutation is
 * undone.
 * @param {Object} obj The object to remove the unique ID field from.
 */
goog.removeUid = function(obj) {
  // TODO(arv): Make the type stricter, do not accept null.

  // In IE, DOM nodes are not instances of Object and throw an exception if we
  // try to delete.  Instead we try to use removeAttribute.
  if (obj !== null && 'removeAttribute' in obj) {
    obj.removeAttribute(goog.UID_PROPERTY_);
  }

  try {
    delete obj[goog.UID_PROPERTY_];
  } catch (ex) {
  }
};


/**
 * Name for unique ID property. Initialized in a way to help avoid collisions
 * with other closure JavaScript on the same page.
 * @type {string}
 * @private
 */
goog.UID_PROPERTY_ = 'closure_uid_' + ((Math.random() * 1e9) >>> 0);


/**
 * Counter for UID.
 * @type {number}
 * @private
 */
goog.uidCounter_ = 0;


/**
 * Adds a hash code field to an object. The hash code is unique for the
 * given object.
 * @param {Object} obj The object to get the hash code for.
 * @return {number} The hash code for the object.
 * @deprecated Use goog.getUid instead.
 */
goog.getHashCode = goog.getUid;


/**
 * Removes the hash code field from an object.
 * @param {Object} obj The object to remove the field from.
 * @deprecated Use goog.removeUid instead.
 */
goog.removeHashCode = goog.removeUid;


/**
 * Clones a value. The input may be an Object, Array, or basic type. Objects and
 * arrays will be cloned recursively.
 *
 * WARNINGS:
 * <code>goog.cloneObject</code> does not detect reference loops. Objects that
 * refer to themselves will cause infinite recursion.
 *
 * <code>goog.cloneObject</code> is unaware of unique identifiers, and copies
 * UIDs created by <code>getUid</code> into cloned results.
 *
 * @param {*} obj The value to clone.
 * @return {*} A clone of the input value.
 * @deprecated goog.cloneObject is unsafe. Prefer the goog.object methods.
 */
goog.cloneObject = function(obj) {
  var type = goog.typeOf(obj);
  if (type == 'object' || type == 'array') {
    if (typeof obj.clone === 'function') {
      return obj.clone();
    }
    var clone = type == 'array' ? [] : {};
    for (var key in obj) {
      clone[key] = goog.cloneObject(obj[key]);
    }
    return clone;
  }

  return obj;
};


/**
 * A native implementation of goog.bind.
 * @param {?function(this:T, ...)} fn A function to partially apply.
 * @param {T} selfObj Specifies the object which this should point to when the
 *     function is run.
 * @param {...*} var_args Additional arguments that are partially applied to the
 *     function.
 * @return {!Function} A partially-applied form of the function goog.bind() was
 *     invoked as a method of.
 * @template T
 * @private
 */
goog.bindNative_ = function(fn, selfObj, var_args) {
  return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments));
};


/**
 * A pure-JS implementation of goog.bind.
 * @param {?function(this:T, ...)} fn A function to partially apply.
 * @param {T} selfObj Specifies the object which this should point to when the
 *     function is run.
 * @param {...*} var_args Additional arguments that are partially applied to the
 *     function.
 * @return {!Function} A partially-applied form of the function goog.bind() was
 *     invoked as a method of.
 * @template T
 * @private
 */
goog.bindJs_ = function(fn, selfObj, var_args) {
  if (!fn) {
    throw new Error();
  }

  if (arguments.length > 2) {
    var boundArgs = Array.prototype.slice.call(arguments, 2);
    return function() {
      // Prepend the bound arguments to the current arguments.
      var newArgs = Array.prototype.slice.call(arguments);
      Array.prototype.unshift.apply(newArgs, boundArgs);
      return fn.apply(selfObj, newArgs);
    };

  } else {
    return function() {
      return fn.apply(selfObj, arguments);
    };
  }
};


/**
 * Partially applies this function to a particular 'this object' and zero or
 * more arguments. The result is a new function with some arguments of the first
 * function pre-filled and the value of this 'pre-specified'.
 *
 * Remaining arguments specified at call-time are appended to the pre-specified
 * ones.
 *
 * Also see: {@link #partial}.
 *
 * Usage:
 * <pre>var barMethBound = goog.bind(myFunction, myObj, 'arg1', 'arg2');
 * barMethBound('arg3', 'arg4');</pre>
 *
 * @param {?function(this:T, ...)} fn A function to partially apply.
 * @param {T} selfObj Specifies the object which this should point to when the
 *     function is run.
 * @param {...*} var_args Additional arguments that are partially applied to the
 *     function.
 * @return {!Function} A partially-applied form of the function goog.bind() was
 *     invoked as a method of.
 * @template T
 * @suppress {deprecated} See above.
 */
goog.bind = function(fn, selfObj, var_args) {
  // TODO(nicksantos): narrow the type signature.
  if (Function.prototype.bind &&
      // NOTE(nicksantos): Somebody pulled base.js into the default Chrome
      // extension environment. This means that for Chrome extensions, they get
      // the implementation of Function.prototype.bind that calls goog.bind
      // instead of the native one. Even worse, we don't want to introduce a
      // circular dependency between goog.bind and Function.prototype.bind, so
      // we have to hack this to make sure it works correctly.
      Function.prototype.bind.toString().indexOf('native code') != -1) {
    goog.bind = goog.bindNative_;
  } else {
    goog.bind = goog.bindJs_;
  }
  return goog.bind.apply(null, arguments);
};


/**
 * Like goog.bind(), except that a 'this object' is not required. Useful when
 * the target function is already bound.
 *
 * Usage:
 * var g = goog.partial(f, arg1, arg2);
 * g(arg3, arg4);
 *
 * @param {Function} fn A function to partially apply.
 * @param {...*} var_args Additional arguments that are partially applied to fn.
 * @return {!Function} A partially-applied form of the function goog.partial()
 *     was invoked as a method of.
 */
goog.partial = function(fn, var_args) {
  var args = Array.prototype.slice.call(arguments, 1);
  return function() {
    // Clone the array (with slice()) and append additional arguments
    // to the existing arguments.
    var newArgs = args.slice();
    newArgs.push.apply(newArgs, arguments);
    return fn.apply(/** @type {?} */ (this), newArgs);
  };
};


/**
 * Copies all the members of a source object to a target object. This method
 * does not work on all browsers for all objects that contain keys such as
 * toString or hasOwnProperty. Use goog.object.extend for this purpose.
 *
 * NOTE: Some have advocated for the use of goog.mixin to setup classes
 * with multiple inheritence (traits, mixins, etc).  However, as it simply
 * uses "for in", this is not compatible with ES6 classes whose methods are
 * non-enumerable.  Changing this, would break cases where non-enumerable
 * properties are not expected.
 *
 * @param {Object} target Target.
 * @param {Object} source Source.
 * @deprecated Prefer Object.assign
 */
goog.mixin = function(target, source) {
  for (var x in source) {
    target[x] = source[x];
  }

  // For IE7 or lower, the for-in-loop does not contain any properties that are
  // not enumerable on the prototype object (for example, isPrototypeOf from
  // Object.prototype) but also it will not include 'replace' on objects that
  // extend String and change 'replace' (not that it is common for anyone to
  // extend anything except Object).
};


/**
 * @return {number} An integer value representing the number of milliseconds
 *     between midnight, January 1, 1970 and the current time.
 * @deprecated Use Date.now
 */
goog.now = (goog.TRUSTED_SITE && Date.now) || (function() {
             // Unary plus operator converts its operand to a number which in
             // the case of
             // a date is done by calling getTime().
             return +new Date();
           });


/**
 * Evals JavaScript in the global scope.  In IE this uses execScript, other
 * browsers use goog.global.eval. If goog.global.eval does not evaluate,
 * appends a script tag instead.
 * Throws an exception if neither execScript or eval is defined.
 * @param {string} script JavaScript string.
 */
goog.globalEval = function(script) {
  if (goog.global.execScript) {
    goog.global.execScript(script, 'JavaScript');
  } else if (goog.global.eval) {
    // Test to see if eval works
    if (goog.evalWorks_ == null) {
      try {
        goog.global.eval('');
        goog.evalWorks_ = true;
      } catch (ignore) {
        goog.evalWorks_ = false;
      }
    }

    if (goog.evalWorks_) {
      goog.global.eval(script);
    } else {
      /** @type {!Document} */
      var doc = goog.global.document;
      var scriptElt =
          /** @type {!HTMLScriptElement} */ (doc.createElement('script'));
      scriptElt.type = 'text/javascript';
      scriptElt.defer = false;
      // Note(pupius): can't use .innerHTML since "t('<test>')" will fail and
      // .text doesn't work in Safari 2.  Therefore we append a text node.
      scriptElt.appendChild(doc.createTextNode(script));
      doc.head.appendChild(scriptElt);
      doc.head.removeChild(scriptElt);
    }
  } else {
    throw new Error('goog.globalEval not available');
  }
};


/**
 * Indicates whether or not we can call 'eval' directly to eval code in the
 * global scope. Set to a Boolean by the first call to goog.globalEval (which
 * empirically tests whether eval works for globals). @see goog.globalEval
 * @type {?boolean}
 * @private
 */
goog.evalWorks_ = null;


/**
 * Optional map of CSS class names to obfuscated names used with
 * goog.getCssName().
 * @private {!Object<string, string>|undefined}
 * @see goog.setCssNameMapping
 */
goog.cssNameMapping_;


/**
 * Optional obfuscation style for CSS class names. Should be set to either
 * 'BY_WHOLE' or 'BY_PART' if defined.
 * @type {string|undefined}
 * @private
 * @see goog.setCssNameMapping
 */
goog.cssNameMappingStyle_;



/**
 * A hook for modifying the default behavior goog.getCssName. The function
 * if present, will receive the standard output of the goog.getCssName as
 * its input.
 *
 * @type {(function(string):string)|undefined}
 */
goog.global.CLOSURE_CSS_NAME_MAP_FN;


/**
 * Handles strings that are intended to be used as CSS class names.
 *
 * This function works in tandem with @see goog.setCssNameMapping.
 *
 * Without any mapping set, the arguments are simple joined with a hyphen and
 * passed through unaltered.
 *
 * When there is a mapping, there are two possible styles in which these
 * mappings are used. In the BY_PART style, each part (i.e. in between hyphens)
 * of the passed in css name is rewritten according to the map. In the BY_WHOLE
 * style, the full css name is looked up in the map directly. If a rewrite is
 * not specified by the map, the compiler will output a warning.
 *
 * When the mapping is passed to the compiler, it will replace calls to
 * goog.getCssName with the strings from the mapping, e.g.
 *     var x = goog.getCssName('foo');
 *     var y = goog.getCssName(this.baseClass, 'active');
 *  becomes:
 *     var x = 'foo';
 *     var y = this.baseClass + '-active';
 *
 * If one argument is passed it will be processed, if two are passed only the
 * modifier will be processed, as it is assumed the first argument was generated
 * as a result of calling goog.getCssName.
 *
 * @param {string} className The class name.
 * @param {string=} opt_modifier A modifier to be appended to the class name.
 * @return {string} The class name or the concatenation of the class name and
 *     the modifier.
 */
goog.getCssName = function(className, opt_modifier) {
  // String() is used for compatibility with compiled soy where the passed
  // className can be non-string objects.
  if (String(className).charAt(0) == '.') {
    throw new Error(
        'className passed in goog.getCssName must not start with ".".' +
        ' You passed: ' + className);
  }

  var getMapping = function(cssName) {
    return goog.cssNameMapping_[cssName] || cssName;
  };

  var renameByParts = function(cssName) {
    // Remap all the parts individually.
    var parts = cssName.split('-');
    var mapped = [];
    for (var i = 0; i < parts.length; i++) {
      mapped.push(getMapping(parts[i]));
    }
    return mapped.join('-');
  };

  var rename;
  if (goog.cssNameMapping_) {
    rename =
        goog.cssNameMappingStyle_ == 'BY_WHOLE' ? getMapping : renameByParts;
  } else {
    rename = function(a) {
      return a;
    };
  }

  var result =
      opt_modifier ? className + '-' + rename(opt_modifier) : rename(className);

  // The special CLOSURE_CSS_NAME_MAP_FN allows users to specify further
  // processing of the class name.
  if (goog.global.CLOSURE_CSS_NAME_MAP_FN) {
    return goog.global.CLOSURE_CSS_NAME_MAP_FN(result);
  }

  return result;
};


/**
 * Sets the map to check when returning a value from goog.getCssName(). Example:
 * <pre>
 * goog.setCssNameMapping({
 *   "goog": "a",
 *   "disabled": "b",
 * });
 *
 * var x = goog.getCssName('goog');
 * // The following evaluates to: "a a-b".
 * goog.getCssName('goog') + ' ' + goog.getCssName(x, 'disabled')
 * </pre>
 * When declared as a map of string literals to string literals, the JSCompiler
 * will replace all calls to goog.getCssName() using the supplied map if the
 * --process_closure_primitives flag is set.
 *
 * @param {!Object} mapping A map of strings to strings where keys are possible
 *     arguments to goog.getCssName() and values are the corresponding values
 *     that should be returned.
 * @param {string=} opt_style The style of css name mapping. There are two valid
 *     options: 'BY_PART', and 'BY_WHOLE'.
 * @see goog.getCssName for a description.
 */
goog.setCssNameMapping = function(mapping, opt_style) {
  goog.cssNameMapping_ = mapping;
  goog.cssNameMappingStyle_ = opt_style;
};


/**
 * To use CSS renaming in compiled mode, one of the input files should have a
 * call to goog.setCssNameMapping() with an object literal that the JSCompiler
 * can extract and use to replace all calls to goog.getCssName(). In uncompiled
 * mode, JavaScript code should be loaded before this base.js file that declares
 * a global variable, CLOSURE_CSS_NAME_MAPPING, which is used below. This is
 * to ensure that the mapping is loaded before any calls to goog.getCssName()
 * are made in uncompiled mode.
 *
 * A hook for overriding the CSS name mapping.
 * @type {!Object<string, string>|undefined}
 */
goog.global.CLOSURE_CSS_NAME_MAPPING;


if (!COMPILED && goog.global.CLOSURE_CSS_NAME_MAPPING) {
  // This does not call goog.setCssNameMapping() because the JSCompiler
  // requires that goog.setCssNameMapping() be called with an object literal.
  goog.cssNameMapping_ = goog.global.CLOSURE_CSS_NAME_MAPPING;
}


/**
 * Gets a localized message.
 *
 * This function is a compiler primitive. If you give the compiler a localized
 * message bundle, it will replace the string at compile-time with a localized
 * version, and expand goog.getMsg call to a concatenated string.
 *
 * Messages must be initialized in the form:
 * <code>
 * var MSG_NAME = goog.getMsg('Hello {$placeholder}', {'placeholder': 'world'});
 * </code>
 *
 * This function produces a string which should be treated as plain text. Use
 * {@link goog.html.SafeHtmlFormatter} in conjunction with goog.getMsg to
 * produce SafeHtml.
 *
 * @param {string} str Translatable string, places holders in the form {$foo}.
 * @param {Object<string, string>=} opt_values Maps place holder name to value.
 * @param {{html: boolean}=} opt_options Options:
 *     html: Escape '<' in str to '&lt;'. Used by Closure Templates where the
 *     generated code size and performance is critical which is why {@link
 *     goog.html.SafeHtmlFormatter} is not used. The value must be literal true
 *     or false.
 * @return {string} message with placeholders filled.
 */
goog.getMsg = function(str, opt_values, opt_options) {
  if (opt_options && opt_options.html) {
    // Note that '&' is not replaced because the translation can contain HTML
    // entities.
    str = str.replace(/</g, '&lt;');
  }
  if (opt_values) {
    str = str.replace(/\{\$([^}]+)}/g, function(match, key) {
      return (opt_values != null && key in opt_values) ? opt_values[key] :
                                                         match;
    });
  }
  return str;
};


/**
 * Gets a localized message. If the message does not have a translation, gives a
 * fallback message.
 *
 * This is useful when introducing a new message that has not yet been
 * translated into all languages.
 *
 * This function is a compiler primitive. Must be used in the form:
 * <code>var x = goog.getMsgWithFallback(MSG_A, MSG_B);</code>
 * where MSG_A and MSG_B were initialized with goog.getMsg.
 *
 * @param {string} a The preferred message.
 * @param {string} b The fallback message.
 * @return {string} The best translated message.
 */
goog.getMsgWithFallback = function(a, b) {
  return a;
};


/**
 * Exposes an unobfuscated global namespace path for the given object.
 * Note that fields of the exported object *will* be obfuscated, unless they are
 * exported in turn via this function or goog.exportProperty.
 *
 * Also handy for making public items that are defined in anonymous closures.
 *
 * ex. goog.exportSymbol('public.path.Foo', Foo);
 *
 * ex. goog.exportSymbol('public.path.Foo.staticFunction', Foo.staticFunction);
 *     public.path.Foo.staticFunction();
 *
 * ex. goog.exportSymbol('public.path.Foo.prototype.myMethod',
 *                       Foo.prototype.myMethod);
 *     new public.path.Foo().myMethod();
 *
 * @param {string} publicPath Unobfuscated name to export.
 * @param {*} object Object the name should point to.
 * @param {Object=} opt_objectToExportTo The object to add the path to; default
 *     is goog.global.
 */
goog.exportSymbol = function(publicPath, object, opt_objectToExportTo) {
  goog.exportPath_(publicPath, object, opt_objectToExportTo);
};


/**
 * Exports a property unobfuscated into the object's namespace.
 * ex. goog.exportProperty(Foo, 'staticFunction', Foo.staticFunction);
 * ex. goog.exportProperty(Foo.prototype, 'myMethod', Foo.prototype.myMethod);
 * @param {Object} object Object whose static property is being exported.
 * @param {string} publicName Unobfuscated name to export.
 * @param {*} symbol Object the name should point to.
 */
goog.exportProperty = function(object, publicName, symbol) {
  object[publicName] = symbol;
};


/**
 * Inherit the prototype methods from one constructor into another.
 *
 * Usage:
 * <pre>
 * function ParentClass(a, b) { }
 * ParentClass.prototype.foo = function(a) { };
 *
 * function ChildClass(a, b, c) {
 *   ChildClass.base(this, 'constructor', a, b);
 * }
 * goog.inherits(ChildClass, ParentClass);
 *
 * var child = new ChildClass('a', 'b', 'see');
 * child.foo(); // This works.
 * </pre>
 *
 * @param {!Function} childCtor Child class.
 * @param {!Function} parentCtor Parent class.
 * @suppress {strictMissingProperties} superClass_ and base is not defined on
 *    Function.
 */
goog.inherits = function(childCtor, parentCtor) {
  /** @constructor */
  function tempCtor() {}
  tempCtor.prototype = parentCtor.prototype;
  childCtor.superClass_ = parentCtor.prototype;
  childCtor.prototype = new tempCtor();
  /** @override */
  childCtor.prototype.constructor = childCtor;

  /**
   * Calls superclass constructor/method.
   *
   * This function is only available if you use goog.inherits to
   * express inheritance relationships between classes.
   *
   * NOTE: This is a replacement for goog.base and for superClass_
   * property defined in childCtor.
   *
   * @param {!Object} me Should always be "this".
   * @param {string} methodName The method name to call. Calling
   *     superclass constructor can be done with the special string
   *     'constructor'.
   * @param {...*} var_args The arguments to pass to superclass
   *     method/constructor.
   * @return {*} The return value of the superclass method/constructor.
   */
  childCtor.base = function(me, methodName, var_args) {
    // Copying using loop to avoid deop due to passing arguments object to
    // function. This is faster in many JS engines as of late 2014.
    var args = new Array(arguments.length - 2);
    for (var i = 2; i < arguments.length; i++) {
      args[i - 2] = arguments[i];
    }
    return parentCtor.prototype[methodName].apply(me, args);
  };
};


/**
 * Allow for aliasing within scope functions.  This function exists for
 * uncompiled code - in compiled code the calls will be inlined and the aliases
 * applied.  In uncompiled code the function is simply run since the aliases as
 * written are valid JavaScript.
 *
 * MOE:begin_intracomment_strip
 * See the goog.scope document at http://go/goog.scope
 *
 * For more on goog.scope deprecation, see the style guide entry:
 * http://go/jsstyle#appendices-legacy-exceptions-goog-scope
 * MOE:end_intracomment_strip
 *
 * @param {function()} fn Function to call.  This function can contain aliases
 *     to namespaces (e.g. "var dom = goog.dom") or classes
 *     (e.g. "var Timer = goog.Timer").
 * @deprecated Use goog.module instead.
 */
goog.scope = function(fn) {
  if (goog.isInModuleLoader_()) {
    throw new Error('goog.scope is not supported within a module.');
  }
  fn.call(goog.global);
};


/*
 * To support uncompiled, strict mode bundles that use eval to divide source
 * like so:
 *    eval('someSource;//# sourceUrl sourcefile.js');
 * We need to export the globally defined symbols "goog" and "COMPILED".
 * Exporting "goog" breaks the compiler optimizations, so we required that
 * be defined externally.
 * NOTE: We don't use goog.exportSymbol here because we don't want to trigger
 * extern generation when that compiler option is enabled.
 */
if (!COMPILED) {
  goog.global['COMPILED'] = COMPILED;
}


//==============================================================================
// goog.defineClass implementation
//==============================================================================


/**
 * Creates a restricted form of a Closure "class":
 *   - from the compiler's perspective, the instance returned from the
 *     constructor is sealed (no new properties may be added).  This enables
 *     better checks.
 *   - the compiler will rewrite this definition to a form that is optimal
 *     for type checking and optimization (initially this will be a more
 *     traditional form).
 *
 * @param {Function} superClass The superclass, Object or null.
 * @param {goog.defineClass.ClassDescriptor} def
 *     An object literal describing
 *     the class.  It may have the following properties:
 *     "constructor": the constructor function
 *     "statics": an object literal containing methods to add to the constructor
 *        as "static" methods or a function that will receive the constructor
 *        function as its only parameter to which static properties can
 *        be added.
 *     all other properties are added to the prototype.
 * @return {!Function} The class constructor.
 * @deprecated Use ES6 class syntax instead.
 */
goog.defineClass = function(superClass, def) {
  // TODO(johnlenz): consider making the superClass an optional parameter.
  var constructor = def.constructor;
  var statics = def.statics;
  // Wrap the constructor prior to setting up the prototype and static methods.
  if (!constructor || constructor == Object.prototype.constructor) {
    constructor = function() {
      throw new Error(
          'cannot instantiate an interface (no constructor defined).');
    };
  }

  var cls = goog.defineClass.createSealingConstructor_(constructor, superClass);
  if (superClass) {
    goog.inherits(cls, superClass);
  }

  // Remove all the properties that should not be copied to the prototype.
  delete def.constructor;
  delete def.statics;

  goog.defineClass.applyProperties_(cls.prototype, def);
  if (statics != null) {
    if (statics instanceof Function) {
      statics(cls);
    } else {
      goog.defineClass.applyProperties_(cls, statics);
    }
  }

  return cls;
};


/**
 * @typedef {{
 *   constructor: (!Function|undefined),
 *   statics: (Object|undefined|function(Function):void)
 * }}
 */
goog.defineClass.ClassDescriptor;


/**
 * @define {boolean} Whether the instances returned by goog.defineClass should
 *     be sealed when possible.
 *
 * When sealing is disabled the constructor function will not be wrapped by
 * goog.defineClass, making it incompatible with ES6 class methods.
 */
goog.defineClass.SEAL_CLASS_INSTANCES =
    goog.define('goog.defineClass.SEAL_CLASS_INSTANCES', goog.DEBUG);


/**
 * If goog.defineClass.SEAL_CLASS_INSTANCES is enabled and Object.seal is
 * defined, this function will wrap the constructor in a function that seals the
 * results of the provided constructor function.
 *
 * @param {!Function} ctr The constructor whose results maybe be sealed.
 * @param {Function} superClass The superclass constructor.
 * @return {!Function} The replacement constructor.
 * @private
 */
goog.defineClass.createSealingConstructor_ = function(ctr, superClass) {
  if (!goog.defineClass.SEAL_CLASS_INSTANCES) {
    // Do now wrap the constructor when sealing is disabled. Angular code
    // depends on this for injection to work properly.
    return ctr;
  }

  // NOTE: The sealing behavior has been removed

  /**
   * @this {Object}
   * @return {?}
   */
  var wrappedCtr = function() {
    // Don't seal an instance of a subclass when it calls the constructor of
    // its super class as there is most likely still setup to do.
    var instance = ctr.apply(this, arguments) || this;
    instance[goog.UID_PROPERTY_] = instance[goog.UID_PROPERTY_];

    return instance;
  };

  return wrappedCtr;
};



// TODO(johnlenz): share these values with the goog.object
/**
 * The names of the fields that are defined on Object.prototype.
 * @type {!Array<string>}
 * @private
 * @const
 */
goog.defineClass.OBJECT_PROTOTYPE_FIELDS_ = [
  'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
  'toLocaleString', 'toString', 'valueOf'
];


// TODO(johnlenz): share this function with the goog.object
/**
 * @param {!Object} target The object to add properties to.
 * @param {!Object} source The object to copy properties from.
 * @private
 */
goog.defineClass.applyProperties_ = function(target, source) {
  // TODO(johnlenz): update this to support ES5 getters/setters

  var key;
  for (key in source) {
    if (Object.prototype.hasOwnProperty.call(source, key)) {
      target[key] = source[key];
    }
  }

  // For IE the for-in-loop does not contain any properties that are not
  // enumerable on the prototype object (for example isPrototypeOf from
  // Object.prototype) and it will also not include 'replace' on objects that
  // extend String and change 'replace' (not that it is common for anyone to
  // extend anything except Object).
  for (var i = 0; i < goog.defineClass.OBJECT_PROTOTYPE_FIELDS_.length; i++) {
    key = goog.defineClass.OBJECT_PROTOTYPE_FIELDS_[i];
    if (Object.prototype.hasOwnProperty.call(source, key)) {
      target[key] = source[key];
    }
  }
};


/**
 * Sealing classes breaks the older idiom of assigning properties on the
 * prototype rather than in the constructor. As such, goog.defineClass
 * must not seal subclasses of these old-style classes until they are fixed.
 * Until then, this marks a class as "broken", instructing defineClass
 * not to seal subclasses.
 * @param {!Function} ctr The legacy constructor to tag as unsealable.
 */
goog.tagUnsealableClass = function(ctr) {

};



// There's a bug in the compiler where without collapse properties the
// Closure namespace defines do not guard code correctly. To help reduce code
// size also check for !COMPILED even though it redundant until this is fixed.
if (!COMPILED && goog.DEPENDENCIES_ENABLED) {
  // MOE:begin_strip
  // TODO(b/67050526) This object is obsolete but some people are relying on
  // it internally. Keep it around until we migrate them.
  /**
   * @private
   * @type {{
   *   loadFlags: !Object<string, !Object<string, string>>,
   *   nameToPath: !Object<string, string>,
   *   requires: !Object<string, !Object<string, boolean>>,
   *   visited: !Object<string, boolean>,
   *   written: !Object<string, boolean>,
   *   deferred: !Object<string, string>
   * }}
   */
  goog.dependencies_ = {
    loadFlags: {},  // 1 to 1

    nameToPath: {},  // 1 to 1

    requires: {},  // 1 to many

    // Used when resolving dependencies to prevent us from visiting file
    // twice.
    visited: {},

    written: {},  // Used to keep track of script files we have written.

    deferred: {}  // Used to track deferred module evaluations in old IEs
  };

  /**
   * @return {!Object}
   * @private
   */
  goog.getLoader_ = function() {
    return {
      dependencies_: goog.dependencies_,
      writeScriptTag_: goog.writeScriptTag_
    };
  };


  /**
   * @param {string} src The script url.
   * @param {string=} opt_sourceText The optionally source text to evaluate
   * @return {boolean} True if the script was imported, false otherwise.
   * @private
   */
  goog.writeScriptTag_ = function(src, opt_sourceText) {
    if (goog.inHtmlDocument_()) {
      /** @type {!HTMLDocument} */
      var doc = goog.global.document;

      // If the user tries to require a new symbol after document load,
      // something has gone terribly wrong. Doing a document.write would
      // wipe out the page. This does not apply to the CSP-compliant method
      // of writing script tags.
      if (!goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING &&
          doc.readyState == 'complete') {
        // Certain test frameworks load base.js multiple times, which tries
        // to write deps.js each time. If that happens, just fail silently.
        // These frameworks wipe the page between each load of base.js, so this
        // is OK.
        var isDeps = /\bdeps.js$/.test(src);
        if (isDeps) {
          return false;
        } else {
          throw Error('Cannot write "' + src + '" after document load');
        }
      }

      if (opt_sourceText === undefined) {
        var script = '<script type="text/javascript" src="' + src + '"></' +
            'script>';
        doc.write(
            goog.TRUSTED_TYPES_POLICY_ ?
                goog.TRUSTED_TYPES_POLICY_.createHTML(script) :
                script);
      } else {
        var script = '<script type="text/javascript">' +
            goog.protectScriptTag_(opt_sourceText) + '</' +
            'script>';
        doc.write(
            goog.TRUSTED_TYPES_POLICY_ ?
                goog.TRUSTED_TYPES_POLICY_.createHTML(script) :
                script);
      }
      return true;
    } else {
      return false;
    }
  };
  // MOE:end_strip

  /**
   * Tries to detect whether is in the context of an HTML document.
   * @return {boolean} True if it looks like HTML document.
   * @private
   */
  goog.inHtmlDocument_ = function() {
    /** @type {!Document} */
    var doc = goog.global.document;
    return doc != null && 'write' in doc;  // XULDocument misses write.
  };


  /**
   * We'd like to check for if the document readyState is 'loading'; however
   * there are bugs on IE 10 and below where the readyState being anything other
   * than 'complete' is not reliable.
   * @return {boolean}
   * @private
   */
  goog.isDocumentLoading_ = function() {
    // attachEvent is available on IE 6 thru 10 only, and thus can be used to
    // detect those browsers.
    /** @type {!HTMLDocument} */
    var doc = goog.global.document;
    return doc.attachEvent ? doc.readyState != 'complete' :
                             doc.readyState == 'loading';
  };


  /**
   * Tries to detect the base path of base.js script that bootstraps Closure.
   * @private
   */
  goog.findBasePath_ = function() {
    if (goog.global.CLOSURE_BASE_PATH != undefined &&
        // Anti DOM-clobbering runtime check (b/37736576).
        typeof goog.global.CLOSURE_BASE_PATH === 'string') {
      goog.basePath = goog.global.CLOSURE_BASE_PATH;
      return;
    } else if (!goog.inHtmlDocument_()) {
      return;
    }
    /** @type {!Document} */
    var doc = goog.global.document;
    // If we have a currentScript available, use it exclusively.
    var currentScript = doc.currentScript;
    if (currentScript) {
      var scripts = [currentScript];
    } else {
      var scripts = doc.getElementsByTagName('SCRIPT');
    }
    // Search backwards since the current script is in almost all cases the one
    // that has base.js.
    for (var i = scripts.length - 1; i >= 0; --i) {
      var script = /** @type {!HTMLScriptElement} */ (scripts[i]);
      var src = script.src;
      var qmark = src.lastIndexOf('?');
      var l = qmark == -1 ? src.length : qmark;
      if (src.substr(l - 7, 7) == 'base.js') {
        goog.basePath = src.substr(0, l - 7);
        return;
      }
    }
  };

  goog.findBasePath_();

  /** @struct @constructor @final */
  goog.Transpiler = function() {
    /** @private {?Object<string, boolean>} */
    this.requiresTranspilation_ = null;
    /** @private {string} */
    this.transpilationTarget_ = goog.TRANSPILE_TO_LANGUAGE;
  };


  // MOE:begin_strip
  // LINT.IfChange
  // MOE:end_strip
  /**
   * Returns a newly created map from language mode string to a boolean
   * indicating whether transpilation should be done for that mode as well as
   * the highest level language that this environment supports.
   *
   * Guaranteed invariant:
   * For any two modes, l1 and l2 where l2 is a newer mode than l1,
   * `map[l1] == true` implies that `map[l2] == true`.
   *
   * Note this method is extracted and used elsewhere, so it cannot rely on
   * anything external (it should easily be able to be transformed into a
   * standalone, top level function).
   *
   * @private
   * @return {{
   *   target: string,
   *   map: !Object<string, boolean>
   * }}
   */
  goog.Transpiler.prototype.createRequiresTranspilation_ = function() {
    var transpilationTarget = 'es3';
    var /** !Object<string, boolean> */ requiresTranspilation = {'es3': false};
    var transpilationRequiredForAllLaterModes = false;

    /**
     * Adds an entry to requiresTranspliation for the given language mode.
     *
     * IMPORTANT: Calls must be made in order from oldest to newest language
     * mode.
     * @param {string} modeName
     * @param {function(): boolean} isSupported Returns true if the JS engine
     *     supports the given mode.
     */
    function addNewerLanguageTranspilationCheck(modeName, isSupported) {
      if (transpilationRequiredForAllLaterModes) {
        requiresTranspilation[modeName] = true;
      } else if (isSupported()) {
        transpilationTarget = modeName;
        requiresTranspilation[modeName] = false;
      } else {
        requiresTranspilation[modeName] = true;
        transpilationRequiredForAllLaterModes = true;
      }
    }

    /**
     * Does the given code evaluate without syntax errors and return a truthy
     * result?
     */
    function /** boolean */ evalCheck(/** string */ code) {
      try {
        return !!eval(code);
      } catch (ignored) {
        return false;
      }
    }

    var userAgent = goog.global.navigator && goog.global.navigator.userAgent ?
        goog.global.navigator.userAgent :
        '';

    // Identify ES3-only browsers by their incorrect treatment of commas.
    addNewerLanguageTranspilationCheck('es5', function() {
      return evalCheck('[1,].length==1');
    });
    addNewerLanguageTranspilationCheck('es6', function() {
      // Edge has a non-deterministic (i.e., not reproducible) bug with ES6:
      // https://github.com/Microsoft/ChakraCore/issues/1496.
      // MOE:begin_strip
      // TODO(joeltine): Our internal web-testing version of Edge will need to
      // be updated before we can remove this check. See http://b/34945376.
      // MOE:end_strip
      var re = /Edge\/(\d+)(\.\d)*/i;
      var edgeUserAgent = userAgent.match(re);
      if (edgeUserAgent) {
        // The Reflect.construct test below is flaky on Edge. It can sometimes
        // pass or fail on 40 15.15063, so just exit early for Edge and treat
        // it as ES5. Until we're on a more up to date version just always use
        // ES5. See https://github.com/Microsoft/ChakraCore/issues/3217.
        return false;
      }
      // Test es6: [FF50 (?), Edge 14 (?), Chrome 50]
      //   (a) default params (specifically shadowing locals),
      //   (b) destructuring, (c) block-scoped functions,
      //   (d) for-of (const), (e) new.target/Reflect.construct
      var es6fullTest =
          'class X{constructor(){if(new.target!=String)throw 1;this.x=42}}' +
          'let q=Reflect.construct(X,[],String);if(q.x!=42||!(q instanceof ' +
          'String))throw 1;for(const a of[2,3]){if(a==2)continue;function ' +
          'f(z={a}){let a=0;return z.a}{function f(){return 0;}}return f()' +
          '==3}';

      return evalCheck('(()=>{"use strict";' + es6fullTest + '})()');
    });
    // ** and **= are the only new features in 'es7'
    addNewerLanguageTranspilationCheck('es7', function() {
      return evalCheck('2 ** 2 == 4');
    });
    // async functions are the only new features in 'es8'
    addNewerLanguageTranspilationCheck('es8', function() {
      return evalCheck('async () => 1, true');
    });
    addNewerLanguageTranspilationCheck('es9', function() {
      return evalCheck('({...rest} = {}), true');
    });
    addNewerLanguageTranspilationCheck('es_next', function() {
      return false;  // assume it always need to transpile
    });
    return {target: transpilationTarget, map: requiresTranspilation};
  };
  // MOE:begin_strip
  // LINT.ThenChange(//depot/google3/java/com/google/testing/web/devtools/updatebrowserinfo/requires_transpilation.js)
  // MOE:end_strip


  /**
   * Determines whether the given language needs to be transpiled.
   * @param {string} lang
   * @param {string|undefined} module
   * @return {boolean}
   */
  goog.Transpiler.prototype.needsTranspile = function(lang, module) {
    if (goog.TRANSPILE == 'always') {
      return true;
    } else if (goog.TRANSPILE == 'never') {
      return false;
    } else if (!this.requiresTranspilation_) {
      var obj = this.createRequiresTranspilation_();
      this.requiresTranspilation_ = obj.map;
      this.transpilationTarget_ = this.transpilationTarget_ || obj.target;
    }
    if (lang in this.requiresTranspilation_) {
      if (this.requiresTranspilation_[lang]) {
        return true;
      } else if (
          goog.inHtmlDocument_() && module == 'es6' &&
          !('noModule' in goog.global.document.createElement('script'))) {
        return true;
      } else {
        return false;
      }
    } else {
      throw new Error('Unknown language mode: ' + lang);
    }
  };


  /**
   * Lazily retrieves the transpiler and applies it to the source.
   * @param {string} code JS code.
   * @param {string} path Path to the code.
   * @return {string} The transpiled code.
   */
  goog.Transpiler.prototype.transpile = function(code, path) {
    // TODO(johnplaisted): We should delete goog.transpile_ and just have this
    // function. But there's some compile error atm where goog.global is being
    // stripped incorrectly without this.
    return goog.transpile_(code, path, this.transpilationTarget_);
  };


  /** @private @final {!goog.Transpiler} */
  goog.transpiler_ = new goog.Transpiler();

  /**
   * Rewrites closing script tags in input to avoid ending an enclosing script
   * tag.
   *
   * @param {string} str
   * @return {string}
   * @private
   */
  goog.protectScriptTag_ = function(str) {
    return str.replace(/<\/(SCRIPT)/ig, '\\x3c/$1');
  };


  /**
   * A debug loader is responsible for downloading and executing javascript
   * files in an unbundled, uncompiled environment.
   *
   * This can be custimized via the setDependencyFactory method, or by
   * CLOSURE_IMPORT_SCRIPT/CLOSURE_LOAD_FILE_SYNC.
   *
   * @struct @constructor @final @private
   */
  goog.DebugLoader_ = function() {
    /** @private @const {!Object<string, !goog.Dependency>} */
    this.dependencies_ = {};
    /** @private @const {!Object<string, string>} */
    this.idToPath_ = {};
    /** @private @const {!Object<string, boolean>} */
    this.written_ = {};
    /** @private @const {!Array<!goog.Dependency>} */
    this.loadingDeps_ = [];
    /** @private {!Array<!goog.Dependency>} */
    this.depsToLoad_ = [];
    /** @private {boolean} */
    this.paused_ = false;
    /** @private {!goog.DependencyFactory} */
    this.factory_ = new goog.DependencyFactory(goog.transpiler_);
    /** @private @const {!Object<string, !Function>} */
    this.deferredCallbacks_ = {};
    /** @private @const {!Array<string>} */
    this.deferredQueue_ = [];
  };

  /**
   * @param {!Array<string>} namespaces
   * @param {function(): undefined} callback Function to call once all the
   *     namespaces have loaded.
   */
  goog.DebugLoader_.prototype.bootstrap = function(namespaces, callback) {
    var cb = callback;
    function resolve() {
      if (cb) {
        goog.global.setTimeout(cb, 0);
        cb = null;
      }
    }

    if (!namespaces.length) {
      resolve();
      return;
    }

    var deps = [];
    for (var i = 0; i < namespaces.length; i++) {
      var path = this.getPathFromDeps_(namespaces[i]);
      if (!path) {
        throw new Error('Unregonized namespace: ' + namespaces[i]);
      }
      deps.push(this.dependencies_[path]);
    }

    var require = goog.require;
    var loaded = 0;
    for (var i = 0; i < namespaces.length; i++) {
      require(namespaces[i]);
      deps[i].onLoad(function() {
        if (++loaded == namespaces.length) {
          resolve();
        }
      });
    }
  };


  /**
   * Loads the Closure Dependency file.
   *
   * Exposed a public function so CLOSURE_NO_DEPS can be set to false, base
   * loaded, setDependencyFactory called, and then this called. i.e. allows
   * custom loading of the deps file.
   */
  goog.DebugLoader_.prototype.loadClosureDeps = function() {
    // Circumvent addDependency, which would try to transpile deps.js if
    // transpile is set to always.
    var relPath = 'deps.js';
    this.depsToLoad_.push(this.factory_.createDependency(
        goog.normalizePath_(goog.basePath + relPath), relPath, [], [], {},
        false));
    this.loadDeps_();
  };


  /**
   * Notifies the debug loader when a dependency has been requested.
   *
   * @param {string} absPathOrId Path of the dependency or goog id.
   * @param {boolean=} opt_force
   */
  goog.DebugLoader_.prototype.requested = function(absPathOrId, opt_force) {
    var path = this.getPathFromDeps_(absPathOrId);
    if (path &&
        (opt_force || this.areDepsLoaded_(this.dependencies_[path].requires))) {
      var callback = this.deferredCallbacks_[path];
      if (callback) {
        delete this.deferredCallbacks_[path];
        callback();
      }
    }
  };


  /**
   * Sets the dependency factory, which can be used to create custom
   * goog.Dependency implementations to control how dependencies are loaded.
   *
   * @param {!goog.DependencyFactory} factory
   */
  goog.DebugLoader_.prototype.setDependencyFactory = function(factory) {
    this.factory_ = factory;
  };


  /**
   * Travserses the dependency graph and queues the given dependency, and all of
   * its transitive dependencies, for loading and then starts loading if not
   * paused.
   *
   * @param {string} namespace
   * @private
   */
  goog.DebugLoader_.prototype.load_ = function(namespace) {
    if (!this.getPathFromDeps_(namespace)) {
      var errorMessage = 'goog.require could not find: ' + namespace;

      goog.logToConsole_(errorMessage);
      // MOE:begin_strip

      // NOTE(nicksantos): We could always throw an error, but this would
      // break legacy users that depended on this failing silently. Instead,
      // the compiler should warn us when there are invalid goog.require
      // calls. For now, we simply give clients a way to turn strict mode on.
      if (goog.useStrictRequires) {
        throw Error(errorMessage);
      }

      // In external Closure, always error.
      // MOE:end_strip
      // MOE:insert throw Error(errorMessage);
    } else {
      var loader = this;

      var deps = [];

      /** @param {string} namespace */
      var visit = function(namespace) {
        var path = loader.getPathFromDeps_(namespace);

        if (!path) {
          throw new Error('Bad dependency path or symbol: ' + namespace);
        }

        if (loader.written_[path]) {
          return;
        }

        loader.written_[path] = true;

        var dep = loader.dependencies_[path];
        // MOE:begin_strip
        if (goog.dependencies_.written[dep.relativePath]) {
          return;
        }
        // MOE:end_strip
        for (var i = 0; i < dep.requires.length; i++) {
          if (!goog.isProvided_(dep.requires[i])) {
            visit(dep.requires[i]);
          }
        }

        deps.push(dep);
      };

      visit(namespace);

      var wasLoading = !!this.depsToLoad_.length;
      this.depsToLoad_ = this.depsToLoad_.concat(deps);

      if (!this.paused_ && !wasLoading) {
        this.loadDeps_();
      }
    }
  };


  /**
   * Loads any queued dependencies until they are all loaded or paused.
   *
   * @private
   */
  goog.DebugLoader_.prototype.loadDeps_ = function() {
    var loader = this;
    var paused = this.paused_;

    while (this.depsToLoad_.length && !paused) {
      (function() {
        var loadCallDone = false;
        var dep = loader.depsToLoad_.shift();

        var loaded = false;
        loader.loading_(dep);

        var controller = {
          pause: function() {
            if (loadCallDone) {
              throw new Error('Cannot call pause after the call to load.');
            } else {
              paused = true;
            }
          },
          resume: function() {
            if (loadCallDone) {
              loader.resume_();
            } else {
              // Some dep called pause and then resume in the same load call.
              // Just keep running this same loop.
              paused = false;
            }
          },
          loaded: function() {
            if (loaded) {
              throw new Error('Double call to loaded.');
            }

            loaded = true;
            loader.loaded_(dep);
          },
          pending: function() {
            // Defensive copy.
            var pending = [];
            for (var i = 0; i < loader.loadingDeps_.length; i++) {
              pending.push(loader.loadingDeps_[i]);
            }
            return pending;
          },
          /**
           * @param {goog.ModuleType} type
           */
          setModuleState: function(type) {
            goog.moduleLoaderState_ = {
              type: type,
              moduleName: '',
              declareLegacyNamespace: false
            };
          },
          /** @type {function(string, string, string=)} */
          registerEs6ModuleExports: function(
              path, exports, opt_closureNamespace) {
            if (opt_closureNamespace) {
              goog.loadedModules_[opt_closureNamespace] = {
                exports: exports,
                type: goog.ModuleType.ES6,
                moduleId: opt_closureNamespace || ''
              };
            }
          },
          /** @type {function(string, ?)} */
          registerGoogModuleExports: function(moduleId, exports) {
            goog.loadedModules_[moduleId] = {
              exports: exports,
              type: goog.ModuleType.GOOG,
              moduleId: moduleId
            };
          },
          clearModuleState: function() {
            goog.moduleLoaderState_ = null;
          },
          defer: function(callback) {
            if (loadCallDone) {
              throw new Error(
                  'Cannot register with defer after the call to load.');
            }
            loader.defer_(dep, callback);
          },
          areDepsLoaded: function() {
            return loader.areDepsLoaded_(dep.requires);
          }
        };

        try {
          dep.load(controller);
        } finally {
          loadCallDone = true;
        }
      })();
    }

    if (paused) {
      this.pause_();
    }
  };


  /** @private */
  goog.DebugLoader_.prototype.pause_ = function() {
    this.paused_ = true;
  };


  /** @private */
  goog.DebugLoader_.prototype.resume_ = function() {
    if (this.paused_) {
      this.paused_ = false;
      this.loadDeps_();
    }
  };


  /**
   * Marks the given dependency as loading (load has been called but it has not
   * yet marked itself as finished). Useful for dependencies that want to know
   * what else is loading. Example: goog.modules cannot eval if there are
   * loading dependencies.
   *
   * @param {!goog.Dependency} dep
   * @private
   */
  goog.DebugLoader_.prototype.loading_ = function(dep) {
    this.loadingDeps_.push(dep);
  };


  /**
   * Marks the given dependency as having finished loading and being available
   * for require.
   *
   * @param {!goog.Dependency} dep
   * @private
   */
  goog.DebugLoader_.prototype.loaded_ = function(dep) {
    for (var i = 0; i < this.loadingDeps_.length; i++) {
      if (this.loadingDeps_[i] == dep) {
        this.loadingDeps_.splice(i, 1);
        break;
      }
    }

    for (var i = 0; i < this.deferredQueue_.length; i++) {
      if (this.deferredQueue_[i] == dep.path) {
        this.deferredQueue_.splice(i, 1);
        break;
      }
    }

    if (this.loadingDeps_.length == this.deferredQueue_.length &&
        !this.depsToLoad_.length) {
      // Something has asked to load these, but they may not be directly
      // required again later, so load them now that we know we're done loading
      // everything else. e.g. a goog module entry point.
      while (this.deferredQueue_.length) {
        this.requested(this.deferredQueue_.shift(), true);
      }
    }

    dep.loaded();
  };


  /**
   * @param {!Array<string>} pathsOrIds
   * @return {boolean}
   * @private
   */
  goog.DebugLoader_.prototype.areDepsLoaded_ = function(pathsOrIds) {
    for (var i = 0; i < pathsOrIds.length; i++) {
      var path = this.getPathFromDeps_(pathsOrIds[i]);
      if (!path ||
          (!(path in this.deferredCallbacks_) &&
           !goog.isProvided_(pathsOrIds[i]))) {
        return false;
      }
    }

    return true;
  };


  /**
   * @param {string} absPathOrId
   * @return {?string}
   * @private
   */
  goog.DebugLoader_.prototype.getPathFromDeps_ = function(absPathOrId) {
    if (absPathOrId in this.idToPath_) {
      return this.idToPath_[absPathOrId];
    } else if (absPathOrId in this.dependencies_) {
      return absPathOrId;
    } else {
      return null;
    }
  };


  /**
   * @param {!goog.Dependency} dependency
   * @param {!Function} callback
   * @private
   */
  goog.DebugLoader_.prototype.defer_ = function(dependency, callback) {
    this.deferredCallbacks_[dependency.path] = callback;
    this.deferredQueue_.push(dependency.path);
  };


  /**
   * Interface for goog.Dependency implementations to have some control over
   * loading of dependencies.
   *
   * @record
   */
  goog.LoadController = function() {};


  /**
   * Tells the controller to halt loading of more dependencies.
   */
  goog.LoadController.prototype.pause = function() {};


  /**
   * Tells the controller to resume loading of more dependencies if paused.
   */
  goog.LoadController.prototype.resume = function() {};


  /**
   * Tells the controller that this dependency has finished loading.
   *
   * This causes this to be removed from pending() and any load callbacks to
   * fire.
   */
  goog.LoadController.prototype.loaded = function() {};


  /**
   * List of dependencies on which load has been called but which have not
   * called loaded on their controller. This includes the current dependency.
   *
   * @return {!Array<!goog.Dependency>}
   */
  goog.LoadController.prototype.pending = function() {};


  /**
   * Registers an object as an ES6 module's exports so that goog.modules may
   * require it by path.
   *
   * @param {string} path Full path of the module.
   * @param {?} exports
   * @param {string=} opt_closureNamespace Closure namespace to associate with
   *     this module.
   */
  goog.LoadController.prototype.registerEs6ModuleExports = function(
      path, exports, opt_closureNamespace) {};


  /**
   * Sets the current module state.
   *
   * @param {goog.ModuleType} type Type of module.
   */
  goog.LoadController.prototype.setModuleState = function(type) {};


  /**
   * Clears the current module state.
   */
  goog.LoadController.prototype.clearModuleState = function() {};


  /**
   * Registers a callback to call once the dependency is actually requested
   * via goog.require + all of the immediate dependencies have been loaded or
   * all other files have been loaded. Allows for lazy loading until
   * require'd without pausing dependency loading, which is needed on old IE.
   *
   * @param {!Function} callback
   */
  goog.LoadController.prototype.defer = function(callback) {};


  /**
   * @return {boolean}
   */
  goog.LoadController.prototype.areDepsLoaded = function() {};


  /**
   * Basic super class for all dependencies Closure Library can load.
   *
   * This default implementation is designed to load untranspiled, non-module
   * scripts in a web broswer.
   *
   * For transpiled non-goog.module files {@see goog.TranspiledDependency}.
   * For goog.modules see {@see goog.GoogModuleDependency}.
   * For untranspiled ES6 modules {@see goog.Es6ModuleDependency}.
   *
   * @param {string} path Absolute path of this script.
   * @param {string} relativePath Path of this script relative to goog.basePath.
   * @param {!Array<string>} provides goog.provided or goog.module symbols
   *     in this file.
   * @param {!Array<string>} requires goog symbols or relative paths to Closure
   *     this depends on.
   * @param {!Object<string, string>} loadFlags
   * @struct @constructor
   */
  goog.Dependency = function(
      path, relativePath, provides, requires, loadFlags) {
    /** @const */
    this.path = path;
    /** @const */
    this.relativePath = relativePath;
    /** @const */
    this.provides = provides;
    /** @const */
    this.requires = requires;
    /** @const */
    this.loadFlags = loadFlags;
    /** @private {boolean} */
    this.loaded_ = false;
    /** @private {!Array<function()>} */
    this.loadCallbacks_ = [];
  };


  /**
   * @return {string} The pathname part of this dependency's path if it is a
   *     URI.
   */
  goog.Dependency.prototype.getPathName = function() {
    var pathName = this.path;
    var protocolIndex = pathName.indexOf('://');
    if (protocolIndex >= 0) {
      pathName = pathName.substring(protocolIndex + 3);
      var slashIndex = pathName.indexOf('/');
      if (slashIndex >= 0) {
        pathName = pathName.substring(slashIndex + 1);
      }
    }
    return pathName;
  };


  /**
   * @param {function()} callback Callback to fire as soon as this has loaded.
   * @final
   */
  goog.Dependency.prototype.onLoad = function(callback) {
    if (this.loaded_) {
      callback();
    } else {
      this.loadCallbacks_.push(callback);
    }
  };


  /**
   * Marks this dependency as loaded and fires any callbacks registered with
   * onLoad.
   * @final
   */
  goog.Dependency.prototype.loaded = function() {
    this.loaded_ = true;
    var callbacks = this.loadCallbacks_;
    this.loadCallbacks_ = [];
    for (var i = 0; i < callbacks.length; i++) {
      callbacks[i]();
    }
  };


  /**
   * Whether or not document.written / appended script tags should be deferred.
   *
   * @private {boolean}
   */
  goog.Dependency.defer_ = false;


  /**
   * Map of script ready / state change callbacks. Old IE cannot handle putting
   * these properties on goog.global.
   *
   * @private @const {!Object<string, function(?):undefined>}
   */
  goog.Dependency.callbackMap_ = {};


  /**
   * @param {function(...?):?} callback
   * @return {string}
   * @private
   */
  goog.Dependency.registerCallback_ = function(callback) {
    var key = Math.random().toString(32);
    goog.Dependency.callbackMap_[key] = callback;
    return key;
  };


  /**
   * @param {string} key
   * @private
   */
  goog.Dependency.unregisterCallback_ = function(key) {
    delete goog.Dependency.callbackMap_[key];
  };


  /**
   * @param {string} key
   * @param {...?} var_args
   * @private
   * @suppress {unusedPrivateMembers}
   */
  goog.Dependency.callback_ = function(key, var_args) {
    if (key in goog.Dependency.callbackMap_) {
      var callback = goog.Dependency.callbackMap_[key];
      var args = [];
      for (var i = 1; i < arguments.length; i++) {
        args.push(arguments[i]);
      }
      callback.apply(undefined, args);
    } else {
      var errorMessage = 'Callback key ' + key +
          ' does not exist (was base.js loaded more than once?).';
      // MOE:begin_strip
      // TODO(johnplaisted): Some people internally are mistakenly loading
      // base.js twice, and this can happen while a dependency is loading,
      // wiping out state.
      goog.logToConsole_(errorMessage);
      // MOE:end_strip
      // MOE:insert throw Error(errorMessage);
    }
  };


  /**
   * Starts loading this dependency. This dependency can pause loading if it
   * needs to and resume it later via the controller interface.
   *
   * When this is loaded it should call controller.loaded(). Note that this will
   * end up calling the loaded method of this dependency; there is no need to
   * call it explicitly.
   *
   * @param {!goog.LoadController} controller
   */
  goog.Dependency.prototype.load = function(controller) {
    if (goog.global.CLOSURE_IMPORT_SCRIPT) {
      if (goog.global.CLOSURE_IMPORT_SCRIPT(this.path)) {
        controller.loaded();
      } else {
        controller.pause();
      }
      return;
    }

    if (!goog.inHtmlDocument_()) {
      goog.logToConsole_(
          'Cannot use default debug loader outside of HTML documents.');
      if (this.relativePath == 'deps.js') {
        // Some old code is relying on base.js auto loading deps.js failing with
        // no error before later setting CLOSURE_IMPORT_SCRIPT.
        // CLOSURE_IMPORT_SCRIPT should be set *before* base.js is loaded, or
        // CLOSURE_NO_DEPS set to true.
        goog.logToConsole_(
            'Consider setting CLOSURE_IMPORT_SCRIPT before loading base.js, ' +
            'or setting CLOSURE_NO_DEPS to true.');
        controller.loaded();
      } else {
        controller.pause();
      }
      return;
    }

    /** @type {!HTMLDocument} */
    var doc = goog.global.document;

    // If the user tries to require a new symbol after document load,
    // something has gone terribly wrong. Doing a document.write would
    // wipe out the page. This does not apply to the CSP-compliant method
    // of writing script tags.
    if (doc.readyState == 'complete' &&
        !goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING) {
      // Certain test frameworks load base.js multiple times, which tries
      // to write deps.js each time. If that happens, just fail silently.
      // These frameworks wipe the page between each load of base.js, so this
      // is OK.
      var isDeps = /\bdeps.js$/.test(this.path);
      if (isDeps) {
        controller.loaded();
        return;
      } else {
        throw Error('Cannot write "' + this.path + '" after document load');
      }
    }

    if (!goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING &&
        goog.isDocumentLoading_()) {
      var key = goog.Dependency.registerCallback_(function(script) {
        if (!goog.DebugLoader_.IS_OLD_IE_ || script.readyState == 'complete') {
          goog.Dependency.unregisterCallback_(key);
          controller.loaded();
        }
      });
      var nonceAttr = !goog.DebugLoader_.IS_OLD_IE_ && goog.getScriptNonce() ?
          ' nonce="' + goog.getScriptNonce() + '"' :
          '';
      var event =
          goog.DebugLoader_.IS_OLD_IE_ ? 'onreadystatechange' : 'onload';
      var defer = goog.Dependency.defer_ ? 'defer' : '';
      var script = '<script src="' + this.path + '" ' + event +
          '="goog.Dependency.callback_(\'' + key +
          '\', this)" type="text/javascript" ' + defer + nonceAttr + '><' +
          '/script>';
      doc.write(
          goog.TRUSTED_TYPES_POLICY_ ?
              goog.TRUSTED_TYPES_POLICY_.createHTML(script) :
              script);
    } else {
      var scriptEl =
          /** @type {!HTMLScriptElement} */ (doc.createElement('script'));
      scriptEl.defer = goog.Dependency.defer_;
      scriptEl.async = false;
      scriptEl.type = 'text/javascript';

      // If CSP nonces are used, propagate them to dynamically created scripts.
      // This is necessary to allow nonce-based CSPs without 'strict-dynamic'.
      var nonce = goog.getScriptNonce();
      if (nonce) {
        scriptEl.setAttribute('nonce', nonce);
      }

      if (goog.DebugLoader_.IS_OLD_IE_) {
        // Execution order is not guaranteed on old IE, halt loading and write
        // these scripts one at a time, after each loads.
        controller.pause();
        scriptEl.onreadystatechange = function() {
          if (scriptEl.readyState == 'loaded' ||
              scriptEl.readyState == 'complete') {
            controller.loaded();
            controller.resume();
          }
        };
      } else {
        scriptEl.onload = function() {
          scriptEl.onload = null;
          controller.loaded();
        };
      }

      scriptEl.src = goog.TRUSTED_TYPES_POLICY_ ?
          goog.TRUSTED_TYPES_POLICY_.createScriptURL(this.path) :
          this.path;
      doc.head.appendChild(scriptEl);
    }
  };


  /**
   * @param {string} path Absolute path of this script.
   * @param {string} relativePath Path of this script relative to goog.basePath.
   * @param {!Array<string>} provides Should be an empty array.
   *     TODO(johnplaisted) add support for adding closure namespaces to ES6
   *     modules for interop purposes.
   * @param {!Array<string>} requires goog symbols or relative paths to Closure
   *     this depends on.
   * @param {!Object<string, string>} loadFlags
   * @struct @constructor
   * @extends {goog.Dependency}
   */
  goog.Es6ModuleDependency = function(
      path, relativePath, provides, requires, loadFlags) {
    goog.Es6ModuleDependency.base(
        this, 'constructor', path, relativePath, provides, requires, loadFlags);
  };
  goog.inherits(goog.Es6ModuleDependency, goog.Dependency);


  /** @override */
  goog.Es6ModuleDependency.prototype.load = function(controller) {
    if (goog.global.CLOSURE_IMPORT_SCRIPT) {
      if (goog.global.CLOSURE_IMPORT_SCRIPT(this.path)) {
        controller.loaded();
      } else {
        controller.pause();
      }
      return;
    }

    if (!goog.inHtmlDocument_()) {
      goog.logToConsole_(
          'Cannot use default debug loader outside of HTML documents.');
      controller.pause();
      return;
    }

    /** @type {!HTMLDocument} */
    var doc = goog.global.document;

    var dep = this;

    // TODO(johnplaisted): Does document.writing really speed up anything? Any
    // difference between this and just waiting for interactive mode and then
    // appending?
    function write(src, contents) {
      if (contents) {
        var script = '<script type="module" crossorigin>' + contents + '</' +
            'script>';
        doc.write(
            goog.TRUSTED_TYPES_POLICY_ ?
                goog.TRUSTED_TYPES_POLICY_.createHTML(script) :
                script);
      } else {
        var script = '<script type="module" crossorigin src="' + src + '"></' +
            'script>';
        doc.write(
            goog.TRUSTED_TYPES_POLICY_ ?
                goog.TRUSTED_TYPES_POLICY_.createHTML(script) :
                script);
      }
    }

    function append(src, contents) {
      var scriptEl =
          /** @type {!HTMLScriptElement} */ (doc.createElement('script'));
      scriptEl.defer = true;
      scriptEl.async = false;
      scriptEl.type = 'module';
      scriptEl.setAttribute('crossorigin', true);

      // If CSP nonces are used, propagate them to dynamically created scripts.
      // This is necessary to allow nonce-based CSPs without 'strict-dynamic'.
      var nonce = goog.getScriptNonce();
      if (nonce) {
        scriptEl.setAttribute('nonce', nonce);
      }

      if (contents) {
        scriptEl.textContent = goog.TRUSTED_TYPES_POLICY_ ?
            goog.TRUSTED_TYPES_POLICY_.createScript(contents) :
            contents;
      } else {
        scriptEl.src = goog.TRUSTED_TYPES_POLICY_ ?
            goog.TRUSTED_TYPES_POLICY_.createScriptURL(src) :
            src;
      }

      doc.head.appendChild(scriptEl);
    }

    var create;

    if (goog.isDocumentLoading_()) {
      create = write;
      // We can ONLY call document.write if we are guaranteed that any
      // non-module script tags document.written after this are deferred.
      // Small optimization, in theory document.writing is faster.
      goog.Dependency.defer_ = true;
    } else {
      create = append;
    }

    // Write 4 separate tags here:
    // 1) Sets the module state at the correct time (just before execution).
    // 2) A src node for this, which just hopefully lets the browser load it a
    //    little early (no need to parse #3).
    // 3) Import the module and register it.
    // 4) Clear the module state at the correct time. Guaranteed to run even
    //    if there is an error in the module (#3 will not run if there is an
    //    error in the module).
    var beforeKey = goog.Dependency.registerCallback_(function() {
      goog.Dependency.unregisterCallback_(beforeKey);
      controller.setModuleState(goog.ModuleType.ES6);
    });
    create(undefined, 'goog.Dependency.callback_("' + beforeKey + '")');

    // TODO(johnplaisted): Does this really speed up anything?
    create(this.path, undefined);

    var registerKey = goog.Dependency.registerCallback_(function(exports) {
      goog.Dependency.unregisterCallback_(registerKey);
      controller.registerEs6ModuleExports(
          dep.path, exports, goog.moduleLoaderState_.moduleName);
    });
    create(
        undefined,
        'import * as m from "' + this.path + '"; goog.Dependency.callback_("' +
            registerKey + '", m)');

    var afterKey = goog.Dependency.registerCallback_(function() {
      goog.Dependency.unregisterCallback_(afterKey);
      controller.clearModuleState();
      controller.loaded();
    });
    create(undefined, 'goog.Dependency.callback_("' + afterKey + '")');
  };


  /**
   * Superclass of any dependency that needs to be loaded into memory,
   * transformed, and then eval'd (goog.modules and transpiled files).
   *
   * @param {string} path Absolute path of this script.
   * @param {string} relativePath Path of this script relative to goog.basePath.
   * @param {!Array<string>} provides goog.provided or goog.module symbols
   *     in this file.
   * @param {!Array<string>} requires goog symbols or relative paths to Closure
   *     this depends on.
   * @param {!Object<string, string>} loadFlags
   * @struct @constructor @abstract
   * @extends {goog.Dependency}
   */
  goog.TransformedDependency = function(
      path, relativePath, provides, requires, loadFlags) {
    goog.TransformedDependency.base(
        this, 'constructor', path, relativePath, provides, requires, loadFlags);
    /** @private {?string} */
    this.contents_ = null;

    /**
     * Whether to lazily make the synchronous XHR (when goog.require'd) or make
     * the synchronous XHR when initially loading. On FireFox 61 there is a bug
     * where an ES6 module cannot make a synchronous XHR (rather, it can, but if
     * it does then no other ES6 modules will load after).
     *
     * tl;dr we lazy load due to bugs on older browsers and eager load due to
     * bugs on newer ones.
     *
     * https://bugzilla.mozilla.org/show_bug.cgi?id=1477090
     *
     * @private @const {boolean}
     */
    this.lazyFetch_ = !goog.inHtmlDocument_() ||
        !('noModule' in goog.global.document.createElement('script'));
  };
  goog.inherits(goog.TransformedDependency, goog.Dependency);


  /** @override */
  goog.TransformedDependency.prototype.load = function(controller) {
    var dep = this;

    function fetch() {
      dep.contents_ = goog.loadFileSync_(dep.path);

      if (dep.contents_) {
        dep.contents_ = dep.transform(dep.contents_);
        if (dep.contents_) {
          dep.contents_ += '\n//# sourceURL=' + dep.path;
        }
      }
    }

    if (goog.global.CLOSURE_IMPORT_SCRIPT) {
      fetch();
      if (this.contents_ &&
          goog.global.CLOSURE_IMPORT_SCRIPT('', this.contents_)) {
        this.contents_ = null;
        controller.loaded();
      } else {
        controller.pause();
      }
      return;
    }


    var isEs6 = this.loadFlags['module'] == goog.ModuleType.ES6;

    if (!this.lazyFetch_) {
      fetch();
    }

    function load() {
      if (dep.lazyFetch_) {
        fetch();
      }

      if (!dep.contents_) {
        // loadFileSync_ or transform are responsible. Assume they logged an
        // error.
        return;
      }

      if (isEs6) {
        controller.setModuleState(goog.ModuleType.ES6);
      }

      var namespace;

      try {
        var contents = dep.contents_;
        dep.contents_ = null;
        goog.globalEval(contents);
        if (isEs6) {
          namespace = goog.moduleLoaderState_.moduleName;
        }
      } finally {
        if (isEs6) {
          controller.clearModuleState();
        }
      }

      if (isEs6) {
        // Due to circular dependencies this may not be available for require
        // right now.
        goog.global['$jscomp']['require']['ensure'](
            [dep.getPathName()], function() {
              controller.registerEs6ModuleExports(
                  dep.path,
                  goog.global['$jscomp']['require'](dep.getPathName()),
                  namespace);
            });
      }

      controller.loaded();
    }

    // Do not fetch now; in FireFox 47 the synchronous XHR doesn't block all
    // events. If we fetched now and then document.write'd the contents the
    // document.write would be an eval and would execute too soon! Instead write
    // a script tag to fetch and eval synchronously at the correct time.
    function fetchInOwnScriptThenLoad() {
      /** @type {!HTMLDocument} */
      var doc = goog.global.document;

      var key = goog.Dependency.registerCallback_(function() {
        goog.Dependency.unregisterCallback_(key);
        load();
      });

      var script = '<script type="text/javascript">' +
          goog.protectScriptTag_('goog.Dependency.callback_("' + key + '");') +
          '</' +
          'script>';
      doc.write(
          goog.TRUSTED_TYPES_POLICY_ ?
              goog.TRUSTED_TYPES_POLICY_.createHTML(script) :
              script);
    }

    // If one thing is pending it is this.
    var anythingElsePending = controller.pending().length > 1;

    // If anything else is loading we need to lazy load due to bugs in old IE.
    // Specifically script tags with src and script tags with contents could
    // execute out of order if document.write is used, so we cannot use
    // document.write. Do not pause here; it breaks old IE as well.
    var useOldIeWorkAround =
        anythingElsePending && goog.DebugLoader_.IS_OLD_IE_;

    // Additionally if we are meant to defer scripts but the page is still
    // loading (e.g. an ES6 module is loading) then also defer. Or if we are
    // meant to defer and anything else is pending then defer (those may be
    // scripts that did not need transformation and are just script tags with
    // defer set to true, and we need to evaluate after that deferred script).
    var needsAsyncLoading = goog.Dependency.defer_ &&
        (anythingElsePending || goog.isDocumentLoading_());

    if (useOldIeWorkAround || needsAsyncLoading) {
      // Note that we only defer when we have to rather than 100% of the time.
      // Always defering would work, but then in theory the order of
      // goog.require calls would then matter. We want to enforce that most of
      // the time the order of the require calls does not matter.
      controller.defer(function() {
        load();
      });
      return;
    }
    // TODO(johnplaisted): Externs are missing onreadystatechange for
    // HTMLDocument.
    /** @type {?} */
    var doc = goog.global.document;

    var isInternetExplorer =
        goog.inHtmlDocument_() && 'ActiveXObject' in goog.global;

    // Don't delay in any version of IE. There's bug around this that will
    // cause out of order script execution. This means that on older IE ES6
    // modules will load too early (while the document is still loading + the
    // dom is not available). The other option is to load too late (when the
    // document is complete and the onload even will never fire). This seems
    // to be the lesser of two evils as scripts already act like the former.
    if (isEs6 && goog.inHtmlDocument_() && goog.isDocumentLoading_() &&
        !isInternetExplorer) {
      goog.Dependency.defer_ = true;
      // Transpiled ES6 modules still need to load like regular ES6 modules,
      // aka only after the document is interactive.
      controller.pause();
      var oldCallback = doc.onreadystatechange;
      doc.onreadystatechange = function() {
        if (doc.readyState == 'interactive') {
          doc.onreadystatechange = oldCallback;
          load();
          controller.resume();
        }
        if (goog.isFunction(oldCallback)) {
          oldCallback.apply(undefined, arguments);
        }
      };
    } else {
      // Always eval on old IE.
      if (goog.DebugLoader_.IS_OLD_IE_ || !goog.inHtmlDocument_() ||
          !goog.isDocumentLoading_()) {
        load();
      } else {
        fetchInOwnScriptThenLoad();
      }
    }
  };


  /**
   * @param {string} contents
   * @return {string}
   * @abstract
   */
  goog.TransformedDependency.prototype.transform = function(contents) {};


  /**
   * Any non-goog.module dependency which needs to be transpiled before eval.
   *
   * @param {string} path Absolute path of this script.
   * @param {string} relativePath Path of this script relative to goog.basePath.
   * @param {!Array<string>} provides goog.provided or goog.module symbols
   *     in this file.
   * @param {!Array<string>} requires goog symbols or relative paths to Closure
   *     this depends on.
   * @param {!Object<string, string>} loadFlags
   * @param {!goog.Transpiler} transpiler
   * @struct @constructor
   * @extends {goog.TransformedDependency}
   */
  goog.TranspiledDependency = function(
      path, relativePath, provides, requires, loadFlags, transpiler) {
    goog.TranspiledDependency.base(
        this, 'constructor', path, relativePath, provides, requires, loadFlags);
    /** @protected @const*/
    this.transpiler = transpiler;
  };
  goog.inherits(goog.TranspiledDependency, goog.TransformedDependency);


  /** @override */
  goog.TranspiledDependency.prototype.transform = function(contents) {
    // Transpile with the pathname so that ES6 modules are domain agnostic.
    return this.transpiler.transpile(contents, this.getPathName());
  };


  /**
   * An ES6 module dependency that was transpiled to a jscomp module outside
   * of the debug loader, e.g. server side.
   *
   * @param {string} path Absolute path of this script.
   * @param {string} relativePath Path of this script relative to goog.basePath.
   * @param {!Array<string>} provides goog.provided or goog.module symbols
   *     in this file.
   * @param {!Array<string>} requires goog symbols or relative paths to Closure
   *     this depends on.
   * @param {!Object<string, string>} loadFlags
   * @struct @constructor
   * @extends {goog.TransformedDependency}
   */
  goog.PreTranspiledEs6ModuleDependency = function(
      path, relativePath, provides, requires, loadFlags) {
    goog.PreTranspiledEs6ModuleDependency.base(
        this, 'constructor', path, relativePath, provides, requires, loadFlags);
  };
  goog.inherits(
      goog.PreTranspiledEs6ModuleDependency, goog.TransformedDependency);


  /** @override */
  goog.PreTranspiledEs6ModuleDependency.prototype.transform = function(
      contents) {
    return contents;
  };


  /**
   * A goog.module, transpiled or not. Will always perform some minimal
   * transformation even when not transpiled to wrap in a goog.loadModule
   * statement.
   *
   * @param {string} path Absolute path of this script.
   * @param {string} relativePath Path of this script relative to goog.basePath.
   * @param {!Array<string>} provides goog.provided or goog.module symbols
   *     in this file.
   * @param {!Array<string>} requires goog symbols or relative paths to Closure
   *     this depends on.
   * @param {!Object<string, string>} loadFlags
   * @param {boolean} needsTranspile
   * @param {!goog.Transpiler} transpiler
   * @struct @constructor
   * @extends {goog.TransformedDependency}
   */
  goog.GoogModuleDependency = function(
      path, relativePath, provides, requires, loadFlags, needsTranspile,
      transpiler) {
    goog.GoogModuleDependency.base(
        this, 'constructor', path, relativePath, provides, requires, loadFlags);
    /** @private @const */
    this.needsTranspile_ = needsTranspile;
    /** @private @const */
    this.transpiler_ = transpiler;
  };
  goog.inherits(goog.GoogModuleDependency, goog.TransformedDependency);


  /** @override */
  goog.GoogModuleDependency.prototype.transform = function(contents) {
    if (this.needsTranspile_) {
      contents = this.transpiler_.transpile(contents, this.getPathName());
    }

    if (!goog.LOAD_MODULE_USING_EVAL || goog.global.JSON === undefined) {
      return '' +
          'goog.loadModule(function(exports) {' +
          '"use strict";' + contents +
          '\n' +  // terminate any trailing single line comment.
          ';return exports' +
          '});' +
          '\n//# sourceURL=' + this.path + '\n';
    } else {
      return '' +
          'goog.loadModule(' +
          goog.global.JSON.stringify(
              contents + '\n//# sourceURL=' + this.path + '\n') +
          ');';
    }
  };


  /**
   * Whether the browser is IE9 or earlier, which needs special handling
   * for deferred modules.
   * @const @private {boolean}
   */
  goog.DebugLoader_.IS_OLD_IE_ = !!(
      !goog.global.atob && goog.global.document && goog.global.document['all']);


  /**
   * @param {string} relPath
   * @param {!Array<string>|undefined} provides
   * @param {!Array<string>} requires
   * @param {boolean|!Object<string>=} opt_loadFlags
   * @see goog.addDependency
   */
  goog.DebugLoader_.prototype.addDependency = function(
      relPath, provides, requires, opt_loadFlags) {
    provides = provides || [];
    relPath = relPath.replace(/\\/g, '/');
    var path = goog.normalizePath_(goog.basePath + relPath);
    if (!opt_loadFlags || typeof opt_loadFlags === 'boolean') {
      opt_loadFlags = opt_loadFlags ? {'module': goog.ModuleType.GOOG} : {};
    }
    var dep = this.factory_.createDependency(
        path, relPath, provides, requires, opt_loadFlags,
        goog.transpiler_.needsTranspile(
            opt_loadFlags['lang'] || 'es3', opt_loadFlags['module']));
    this.dependencies_[path] = dep;
    for (var i = 0; i < provides.length; i++) {
      this.idToPath_[provides[i]] = path;
    }
    this.idToPath_[relPath] = path;
  };


  /**
   * Creates goog.Dependency instances for the debug loader to load.
   *
   * Should be overridden to have the debug loader use custom subclasses of
   * goog.Dependency.
   *
   * @param {!goog.Transpiler} transpiler
   * @struct @constructor
   */
  goog.DependencyFactory = function(transpiler) {
    /** @protected @const */
    this.transpiler = transpiler;
  };


  /**
   * @param {string} path Absolute path of the file.
   * @param {string} relativePath Path relative to closure’s base.js.
   * @param {!Array<string>} provides Array of provided goog.provide/module ids.
   * @param {!Array<string>} requires Array of required goog.provide/module /
   *     relative ES6 module paths.
   * @param {!Object<string, string>} loadFlags
   * @param {boolean} needsTranspile True if the file needs to be transpiled
   *     per the goog.Transpiler.
   * @return {!goog.Dependency}
   */
  goog.DependencyFactory.prototype.createDependency = function(
      path, relativePath, provides, requires, loadFlags, needsTranspile) {
    // MOE:begin_strip
    var provide, require;
    for (var i = 0; provide = provides[i]; i++) {
      goog.dependencies_.nameToPath[provide] = relativePath;
      goog.dependencies_.loadFlags[relativePath] = loadFlags;
    }
    for (var j = 0; require = requires[j]; j++) {
      if (!(relativePath in goog.dependencies_.requires)) {
        goog.dependencies_.requires[relativePath] = {};
      }
      goog.dependencies_.requires[relativePath][require] = true;
    }
    // MOE:end_strip

    if (loadFlags['module'] == goog.ModuleType.GOOG) {
      return new goog.GoogModuleDependency(
          path, relativePath, provides, requires, loadFlags, needsTranspile,
          this.transpiler);
    } else if (needsTranspile) {
      return new goog.TranspiledDependency(
          path, relativePath, provides, requires, loadFlags, this.transpiler);
    } else {
      if (loadFlags['module'] == goog.ModuleType.ES6) {
        if (goog.TRANSPILE == 'never' && goog.ASSUME_ES_MODULES_TRANSPILED) {
          return new goog.PreTranspiledEs6ModuleDependency(
              path, relativePath, provides, requires, loadFlags);
        } else {
          return new goog.Es6ModuleDependency(
              path, relativePath, provides, requires, loadFlags);
        }
      } else {
        return new goog.Dependency(
            path, relativePath, provides, requires, loadFlags);
      }
    }
  };


  /** @private @const */
  goog.debugLoader_ = new goog.DebugLoader_();


  /**
   * Loads the Closure Dependency file.
   *
   * Exposed a public function so CLOSURE_NO_DEPS can be set to false, base
   * loaded, setDependencyFactory called, and then this called. i.e. allows
   * custom loading of the deps file.
   */
  goog.loadClosureDeps = function() {
    goog.debugLoader_.loadClosureDeps();
  };


  /**
   * Sets the dependency factory, which can be used to create custom
   * goog.Dependency implementations to control how dependencies are loaded.
   *
   * Note: if you wish to call this function and provide your own implemnetation
   * it is a wise idea to set CLOSURE_NO_DEPS to true, otherwise the dependency
   * file and all of its goog.addDependency calls will use the default factory.
   * You can call goog.loadClosureDeps to load the Closure dependency file
   * later, after your factory is injected.
   *
   * @param {!goog.DependencyFactory} factory
   */
  goog.setDependencyFactory = function(factory) {
    goog.debugLoader_.setDependencyFactory(factory);
  };


  /**
   * Trusted Types policy for the debug loader.
   * @private @const {?TrustedTypePolicy}
   */
  goog.TRUSTED_TYPES_POLICY_ = goog.TRUSTED_TYPES_POLICY_NAME ?
      goog.createTrustedTypesPolicy(goog.TRUSTED_TYPES_POLICY_NAME + '#base') :
      null;

  if (!goog.global.CLOSURE_NO_DEPS) {
    goog.debugLoader_.loadClosureDeps();
  }


  /**
   * Bootstraps the given namespaces and calls the callback once they are
   * available either via goog.require. This is a replacement for using
   * `goog.require` to bootstrap Closure JavaScript. Previously a `goog.require`
   * in an HTML file would guarantee that the require'd namespace was available
   * in the next immediate script tag. With ES6 modules this no longer a
   * guarantee.
   *
   * @param {!Array<string>} namespaces
   * @param {function(): ?} callback Function to call once all the namespaces
   *     have loaded. Always called asynchronously.
   */
  goog.bootstrap = function(namespaces, callback) {
    goog.debugLoader_.bootstrap(namespaces, callback);
  };
}


/**
 * @define {string} Trusted Types policy name. If non-empty then Closure will
 * use Trusted Types.
 */
goog.TRUSTED_TYPES_POLICY_NAME =
    goog.define('goog.TRUSTED_TYPES_POLICY_NAME', '');


/**
 * Returns the parameter.
 * @param {string} s
 * @return {string}
 * @private
 */
goog.identity_ = function(s) {
  return s;
};


/**
 * Creates Trusted Types policy if Trusted Types are supported by the browser.
 * The policy just blesses any string as a Trusted Type. It is not visibility
 * restricted because anyone can also call TrustedTypes.createPolicy directly.
 * However, the allowed names should be restricted by a HTTP header and the
 * reference to the created policy should be visibility restricted.
 * @param {string} name
 * @return {?TrustedTypePolicy}
 */
goog.createTrustedTypesPolicy = function(name) {
  var policy = null;
  var policyFactory = goog.global.trustedTypes;
  if (!policyFactory || !policyFactory.createPolicy) {
    return policy;
  }
  // trustedTypes.createPolicy throws if called with a name that is already
  // registered, even in report-only mode. Until the API changes, catch the
  // error not to break the applications functionally. In such case, the code
  // will fall back to using regular Safe Types.
  // TODO(koto): Remove catching once createPolicy API stops throwing.
  try {
    policy = policyFactory.createPolicy(name, {
      createHTML: goog.identity_,
      createScript: goog.identity_,
      createScriptURL: goog.identity_
    });
  } catch (e) {
    goog.logToConsole_(e.message);
  }
  return policy;
};


//javascript/public_suffix/publicsuffixpatterns.js
goog.provide('publicsuffix.publicsuffixpatterns');

goog.scope(function() {
var publicsuffixpatterns = publicsuffix.publicsuffixpatterns;


/** @const {string} */
publicsuffixpatterns.EXACT = '&a&0&0trk9--nx?27qjf--nx?e9ebgn--nx?nbb0c7abgm--nx??1&2oa08--nx?apg6qpcbgm--nx?hbbgm--nx?rdceqa08--nx??2&8ugbgm--nx?eyh3la2ckx--nx?qbd9--nx??3&2wqq1--nx?60a0y8--nx??4x1d77xrck--nx?6&1f4a3abgm--nx?2yqyn--nx?3np8lv81qo3--nx?5b06t--nx?axq--nx?ec7q--nx?lbgw--nx??883xnn--nx?9d2c24--nx?a&a?it??b!.&gro?lim?moc?t&en?opsgolb,?ude?vog??abila?c?ihsot?m?n??c!.&b&a?m?n??c&b?g?q??ep?fn?k&s?y??ln?no?oc,pi-on,sn?t&n?opsgolb,?un?ysrab,?i&ma?r&emarp?fa??sroc??naiva?s??d&ats?n&eit?oh??om?sa?tl??eg?f&c?ob??g!emo?naripi?oy??hskihs?i&cnal?dem?hs?k!on??sa!.snduolc,??jnin?k&aso?dov?ede?usto??l!.&c,gro?m&oc?yn,?ofni?r&ep?nb,?t&en?ni??ude?vog??irgnahs?le&nisiuc?rbmuder???m!.&ca?gro?oc?sserp?ten?vog??ahokoy?e00sf7vqn--nx?m??n!.&ac?cc?eman?gro?ibom?loohcs?moc?ni?o&c?fni?rp??r&d?o??s&u?w??vt?xm??av?is?olecrab?tea??p!.&bog?ca?d&em?ls??g&ni?ro??mo&c?n??oba?ten?ude??c?g7hyabgm--nx?ra!.&461e?6pi?iru?nru?rdda-ni?siri???s??q!.&eman?gro?hcs?lim?mo&c?n,?t&en?opsgolb,?ude?vog???r&az?emac?f4a3abgm--nx?n!d5uhf8le58r4w--nx??u&kas?tan???s!.&bup?dem?gro?hcs?moc?ten?ude?vog??ac!.uban.iu,?iv??t&ad?elhta?led?oyot??u!.&a&cinniv?emirc?i&hzhziropaz?stynniv??s&edo?sedo??tlay?vatlop??bs?c&c,inimod??d&argovorik?o!roghzu??tl,?e&hzhziropaz?nvir?t??f&i?ni,?g&l?ro??hk?i&stvinrehc?ykstynlemhk??k&c?m?s&nagul?t&enod?ul??v&iknarf-onavi?orteporp&end?ind?????l&iponret?opotsa&bes?ves??p??m&k?oc?s?yrk??n&c?d?i?osrehk?v?ylov??o&c,nvor??p&d?p,z??r&c?imotihz?k?ymotyhz??sk?t&en?l?z??ude?v:c?e&alokin?ik??i&alokym?hinrehc?krahk?vl?yk??k?l?o&g!inrehc??krahk??r?,y&ikstinlemhk?mus?s&akrehc?sakrehc?tvonrehc???z&ib,u????v!aj?bb?et?iv??waniko?x&a?iacal??yogan?z&.&bew?c&a?i&n?rga???gro?l&im?oohcs??m&on?t??o&c!.topsgolb,?gn??radnorg?sin?t&en?la??ude?vog?wal??zip???b&00ave5a9iabgm--nx?1&25qhx--nx?68quv--nx?e2kc1--nx??2xtbgm--nx?3&b2kcc--nx?jca1d--nx??4&6&1rfz--nx?qif--nx??96rzc--nx??7w9u16qlj--nx?88uvor--nx?a&0dc4xbgm--nx?c?her?n?ra?t??b!.&erots?gro?moc?o&c?fni??ten?ude?v&og?t??zib??a??c&j?s??d&hesa08--nx?mi??ec?g?l!.&gro?moc?ten?ude?vog??m??s!.&gro?moc?ten?ude?vog???tc-retarebsnegmrev--nx?u&lc!.&snduolc,y&nop,srab,??smas??p!.ysrab,??wp-gnutarebsnegmrev--nx??c&1&1q54--nx?hbgw--nx??2e9c2czf--nx?4&4ub1km--nx?a1e--nx?byj9q--nx?erd5a9b1kcb--nx??779tbp--nx?8&4xx2g--nx?c9jrb2h--nx??9jr&b&2h--nx?54--nx?9s--nx??c&eg--nx?h3--nx?s2--nx???a!.&gro?lim?moc?ten?ude?vog??3a09--nx!.&ca1o--nx?gva1c--nx?h&ca1o--nx?za09--nx??ta1d--nx?ua08--nx???da??b&a?b?ci?f76a0c7ylqbgm--nx?sh??c!.&eugaelysatnaf,gnipparcs,liamwt,revres-emag,s&nduolc,otohpym,seccaptf,??0atf7b45--nx?a1l--nx??e!.&21k?bog?dem?gro?lim?m&oc?yn,?nif?o&fni?rp??ten?ude?vog??beuq?n?smoc?tnamys??fdh?i&l&buperananab?ohtac??n&agro?ilc?osanap??tic??l!.&gro?m&oc?yn,?oc?ten?ude?vog?yo,?l??m!.&mt?ossa??p1akcq--nx??n!.&mon?ossa??i?p??relcel?s!.&gro?moc?ten?ude?vog??c??t!s?w??v!.&e0,gro?lim?mo&c?n,?ten?ude?v&g:.d,,og???q??wp?yn??d&2urzc--nx?3&1wrpk--nx?c&4b11--nx?9jrcpf--nx???5xq55--nx?697uto--nx?75yrpk--nx?9ctdvkce--nx?a!.mon?d?er?olnwod??b2babgm--nx?c!.vog?g9a2g2b0ae0chclc--nx??e&m!bulc??r!k??sopxe?timil?w??fc?g!.mon,?h&d3tbgm--nx?p?t??i!.&ased?bew?ca?hcs?lim?o&c!.topsgolb,?g??ro?sepnop?ten?ym?zib??ar?b?ordna?p?rdam??l&iub?og?row??m!.topsgolb,?n&a&b?l!.citats:.&setis,ved,?,lohwen?raas???ob?uf??o&of?rp??r&a&c&tiderc?yalcrab??ugnav??ef506w4b--nx?k!.&oc,ude,?jh3a1habgm--nx??of??s!.&dem?gro?moc?ofni?ten?ude?v&og?t???m!kcrem???t!.topsgolb,excwkcc--nx?l??uolc!.&atcepsrep,drayknil,nworu,r&epolroov,opav,?xelpciffart,??za5cbgn--nx??e&1&53wlf--nx?7a1hbbgm--nx?ta3kg--nx??2a6a1b6b1i--nx?3ma0e1cvr--nx?418txh--nx?707b0e3--nx?a!.&ca?gro?hcs?lim?mon,oc?t&en?opsgolb,?vog??09--nx??b!.&ca?gnitsohbew,topsgolb,?ortal?ut!uoy???c&a&lp!.oc,?ps!.&lla4sx,rebu,slootiknil,tsafym,?artxe??sla??i!ffo??n&a&d?iler?nif?rus&e?ni!efil?srelevart????eics!.oby,??rofria??d!.&1sndnyd,42pi-nyd,7erauqs,amil4,brb-ni,decalpb,e&daregtmueart,mohsnd,nihcamyek,?hcierebsnoissuksid,keegnietsi,lsd-ni,moc,n&-i-g-o-l,aw-ym,esgnutiel,i&emtsi,lreb-n&i,yd,??oitatsksid-ygolonys,pv&-n&i,yd,?nyd,??orp-ytinummoc,p&h21,iog:ol,,?r&e&ntrapdeeps.remotsuc,su&-lautriv,lautriv,?t&adpusnd,tub-ni,uor-ym,?vres&-e&bucl,mohym,?bew-emoh:.nyd,,luhcs,??ogiv-&niem,ym,??s&d-&onys,ygolonys,?nd&-&dd,nufiat,sehcsimanyd,tenretni,yard,?isoc.nyd,ps,yard,?oper-&nvs,tig,?sndd:.&nyd,sndnyd,?,?topsgolb,vresi-&niem,tset,?xi2,y&awetag-&llawerif,ym,?srab,tic-amil,?zten&mitbel,sadtretteuf,??a&lg?rt!.oby,??i&s&doow?ruoyno??ug?wnoitan??nil?on--nx??e!.&bil?dem?eif?gro?irp?kiir?moc!.topsgolb,?pia?ude?vog??ei?ffoc?gg?r&f?ged???f&a&c?s??il!tem???g!.&gro?lim?mo&c?n,?t&en?vp??ude?vog??a&f?gtrom?p!.ycvrp,?rots?yov??elloc?na&hcxe?ro??roeg?ug??i!.&myn,topsgolb,vog??tilop?v&bba?om???j!.&gro?oc?ten???k!.&c&a?s??e&m?n??ibom?mon,o&c!.topsgolb,?fni?g??ro??i&b?l?n???l&a&dmrif?s!.rof,rof???b&a?i&b?dua???c&aro?ric??dnik?g!oog??i&bom?ms??l&asal?erauqa??ppa?uhcs?yts!efil???m!.&4&32i,pct,?66c,ailisarb,b&dnevar,g-raegelif,?ca?duolcsd,e&d-raegelif,i&-raegelif,lpad:.tsohlacol,,??g&ro?s-raegelif,?hctilg,k&catsegde,uoc,?myn,noitatsksid,o&bmoy,c!ku,?t&nigol,poh,??p&ion,j-raegelif,ohbew,?r&aegelif,ofsnd,?s&dym,ndd,ti??t&en?s&acdnuos,ohon,??u&a-raegelif,de?tcn,?v&irp?og??y&golonys,olpedew,srab,??a&g?n!.&reh.togrof,sih.togrof,???em?i&rp?twohs??o&htathgir?rhc??w??n!goloc?i&lno!.ysrab,?w??o!.&derno:.gnigats,,knilemoh,rof,?hp?latipac?ts&der?e&gdirb?rif???z!.&66duolc,amil,sh,???ruoblem??om?p!.&bog?gro?lim?m&o&c?n??yn,?t&en?opsgolb,?ude??irg?yks??r!.&mo&c?n??ossa?topsgolb,?a&c!htlaeh??pmoc?wtfos??bc?eh?if?ots?taeht?u&ces?sni?t&inruf?necca??za???s!.&a?b!ibnal?rofmok??c!a??d!b?n&arb?ubroflanummok???e?f!noc,?g!ro??h!f??i!trap??k!shf??l?m!oc,t??n!mygskurbrutan??o?p!p??r?s!serp??t!opsgolb,?u?vhf?w?x!uvmok??y?z??a&c?el?hc??i&er?urc??nesemoh?roh?uoh??t&a&d?ts&e!laer??lla???is!.&areduolc,enilnigol,n&eyb,oyc,?spvtsaf,xulel,ysrab,?bew??ov?ra?t&ioled?ol??utitsni??u&lb?qi&nilc?tuob???v!.&21e?b&ew?og??ce&r?t??erots?gro?lim?m&oc?rif??o&c?fni??stra?t&en?ni??ude?vog??as?e3gerb2h--nx?i&l?rd?ssergorp??ol??w&kct--nx?r??xul??f&0f3rkcg--nx?198xim--nx?280xim--nx?617upk--nx?7vqn--nx?a!.&gro?mo&c?n,?ten?ude?vog???b!.vog?wa9bgm--nx??c!.topsgolb,a1p--nx?ns??ea1j--nx?fo?g?iam?l&a1d--nx?og??n!.&bew?cer?erots?m&oc?rif??ofni?re&hto?p??stra?ten???orp?p!.&gro?moc?ude???rus?t!w??vd7ckaabgm--nx?w??g&2&4wq55--nx?8zrf6--nx??3&44sd3--nx?91w6j--nx!.&a5wqmg--nx?d&22svcw--nx?5xq55--nx??gla0do--nx?m1qtxm--nx?vta0cu--nx????455ses--nx?5mzt5--nx?69vqhr--nx?7&8a4d5a4prebgm--nx?rb2c--nx??a!.&gro?mo&c?n??oc?ten??vd??b!.&0?1?2?3?4?5?6?7?8?9?a?b?c?d?e?f?g?h?i?j?k?l?m?n?o?p?q?r?s?t!opsgolb,?u?v?w?x?y!srab,?z???c!b?za9a0cbgm--nx??e!.&eman?gro?ics?lim?moc!.topsgolb,?nue?ten?ude?vog??a??g!.&ayc,gro?lenap:.nomead,,oc?saak,ten???i&a?v??k!.&gro?lim?moc?ten?ude?vog???m!.&drp?gro?lim?m&o&c?n??t??oc?ude?vog??pk??n!.&dtl,eman?gro?hcs?i!bom??l&im?oc,?m&oc!.topsgolb,?rif,?neg,ogn,ten?ude?vog??aw?i!b!mulp??car?d&art?dew??h&sif?tolc??k&iv?oo&b?c???ls?n&aelc?iart??p!pohs??re&enigne?tac??t&ad?ekram?hgil?lusnoc?neg?ov?soh!.tfarcnepo,?tebdaerps??vi&g?l???o!s??u&rehcisrev?smas?tarebsnegömrev???o&d?lb?og!.duolc,??r&2n084qlj--nx?ebmoolb?o!.&77ndc.c:sr,,a&remacytirucesym,t&neimip,sivretla,?z,?d&ab-yrev-si,e&sufnocsim,vas-si,?nuof-si,oog-yrev-si,uolc&arfniarodef,mw,??e&a,cin-yrev-si,grof&loot,peh,?l&as-4-ffuts,poeparodef,?m&-morf,agevres,ohruoyslles,?n&ozdop,uma.elet,?r&ehwongniogyldlob,iwym,uces-77ndc.nigiro.lss,?t&adidnac-a-si,is&-ybboh,golb,???fehc-a-si,golbymdaer,k&eeg-a&-si,si,?h,nut,?l&i&amwt,ve-yrev-si,?lawerif&-ym,ym,?sd-ni,?m&acssecca,edom-elbac,?n&af&blm,cfu,egelloc,lfn,s&citlec-a-si,niurb-a-si,tap-a-si,?xos-a-si,?o&itatsksid,rviop,?pv-ni,?o&jodsnd,tp&az,oh,??p&i&-on,fles,?o&hbew,tksedeerf,?tf&e&moh,vres,?ym,??r&e&gatop,ppepteews,su-xunil-a-si,?gmtrec,vdmac,?s&a&ila&nyd,snd,?nymsd,?b&alfmw,bevres,?dylimaf,eirfotatophcuoc,gulku,j,koob-daer,ltbup,nd&-won,deerf,emoh,golb,kcud,mood,nyd:.&emoh,og,?,ps,rvd,tog,uolc,?s&a-skcik,ndd,?tnemhcattaomb,u,?t&ce&jorparodef.&duolc,gts.so.ppa,so.ppa,?riderbew,?e&ews-yrev-si,nretni&ehtfodne,fodne,??hgink-a-si,igude,oi-allizom,s&ixetn&od,seod,?o&h-emag,l-si,?rifyam,??ue:.&a&-q,c,?cm,dc,e&b,d,e,i,m,s,?g&b,n,?hc,i&f,s,?k&d,m,s,u,?l&a,i,n,p,?n&c,i,?o&n,r,ssa,?pj,r&f,g,h,k,t,?s&e,i:rap,,u,?t&a,en,i,l,m,ni,p,?u&a,de,h,l,r,?vl,y&c,m,?z&c,n,??,vresnyd,x&inuemoh,unilemoh,?y&limafxut,srab,???ub&mah?oj???s!.&gro?moc?rep?t&en?opsgolb,?ude?vog??gb639j43us5--nx??t?u!.&c&a?s??en?gro?mo&c?n,?o&c?g??ro?topsgolb,??v!.mon,a1c--nx??wsa08--nx??h&0ee5a3ld2ckx--nx?4wc3o--nx!.&a&2xyc3o--nx?3j0hc3m--nx?ve4b3c0oc21--nx??id1kzuc3h--nx?l8bxi8ifc21--nx?rb0ef1c21--nx???8&8yvfe--nx?a7maabgm--nx??b!.&gro?moc?ten?ude?vog??mg??c!.&7erauqs,amil4,duolc-drayknil,gniksnd,ph21,sndtog,topsgolb,xi2,ytic-amil,?aoc?et?ir!euz??r&aes!errecnac??uhc??sob?taw!s???d0sbgp--nx?f&2lpbgm--nx?k??g!.&gro?lim?moc?ude?vog???iesac?m!a1j--nx??ocir?p!.&gro?i?lim?moc?ogn?ten?ude?vog???s!.&g&nabhsah,ro??lim?moc?ten?vog?won,yolpedew,?a&c?nom??i&d?f?ri???t!.&ca?enilno,im?ni?o&c?g??pohs,ro?ten??iaf!.oby,?laeh?orxer?ra&ba?e???vo!.lopdren,?zb??i&3tupk--nx?7a0oi--nx?a!.&ffo?gro?mo&c?n,?ten?uwu,?1p--nx?bud?dnuyh?tnihc??b!.&gro?moc?oc?ro?ude??ahduba?o!m!.&duolcsd,ysrab,???s??c!.&ayb-tropora--nx?ca?d&e?m??esserp?gro?moc?nif,o&c?g?ssa??ro?t&en?ni?roporéa??ude?vuog??cug?t??d&dk?ua??e&bhf--nx?piat??f!.&aw5-nenikkh--nx,dnala?iki,nenikkäh,topsgolb,yd,?onas??g!.&d&om?tl??gro?moc?ude?vog???h&c&atih?ra??s&abodoy?ibustim???juohs?k!.&gro?moc?ofni?ten?ude?vog?zib??b4gc--nx?iw?nisleh?s?uzus??l!.&aac,m&on,yn,?topsgolb,?drahcir?iamsi??maim?n!.&b&ew?og??ca?gro?lim?mo&c?n??ni?o&c?fni??pp?t&en?ni??ude?zib??airpic?i&hgrobmal?m??re??om?rarref?s!.&mon,topsgolb,?ed??t&aresam?i&c?nifni??rahb?tagub??ut?v!.&21k?gro?moc?oc?ten???wik?xa&rp?t??yf??j&6pqgza9iabgm--nx?8da1tabbgl--nx?b!.&ossa?topsgolb,uaerrab?vuog???d?f!.&ca?eman?gro?lim?moc?o&fni?rp??ten?vog?zib???nj?s?t!.&bew?c&a?in??eman?gro?lim?mo&c?n,?o&c?g??t&en?ni?set??ude?vog?zib???yqx94qit--nx??k&8uxp3--nx?924tcf--nx?arfel?c&a&bdeef?lb??ebdnul?ilc?reme?ud??d!.&erots,ger,mrif,oc,topsgolb,zib,?t??e&es?samet??h!.&a&4ya0cu--nx?5wqmg--nx??b3qa0do--nx?cni,d&2&2svcw--nx?3rvcl--nx??5xq55--nx?tl,?g&a0nt--nx?la0do--nx?ro??i&050qmg--nx?7a0oi--nx?xa0km--nx??m&1qtxm--nx?oc?yn,?npqic--nx?t&en?opsgolb,?ude?v&di?og?ta0cu--nx??xva0fz--nx?人&个?個?箇??司公?府政?絡&網?网??織&組?组??织&組?组??络&網?网??育&敎?教???n??i&tsob?vdnas??l!.&bew?c&a?os??dtl?gro?hcs?letoh?moc?nssa?ogn?prg?t&en?ni??ude?vog??at?cd?is??m!.&eman?fni?gro?mo&c?n,?t&en?opsgolb,?ude?vog???n&ab!cfdh?etats?mmoc?t&en?fos??u??i!.gn,l!.&noyc,pepym,??p???oob?p!.&b&ew?og??gro?kog?m&af?oc??nog?ofni?pog?sog?ten?ude?vog?zib???row!.&fo,ot,?ten!.&htumiza,o&c,vra,??doof???s!.&myn,topsgolb,??t?u!.&c&a?lp??d&om?tl??e&cilop?m??gro!.&gul:g,,sgul,??nnoc,o&c!.&e&lddiwg,n&ilnoysrab,ozgniebllew,??krametyb.&hd,mv,?pi-on,topsgolb,vres-hn,ysrab,??rpoc,?shn?ten?vog!.eci&ffoemoh,vres,??ysrab,???l&04sr4w--nx?a!.&gro?lim?mo&c?n,?t&en?opsgolb,?ude?vog??bolg?c?ed?g!el??i&c&nanif!.oc,lpl??os??romem?tnedurp??n&if?oitanretni??t&i&gid!.sppaduolc:.nodnol,,?p&ac?soh???ned?ot??utum!nretsewhtron???c!.&bog?lim?mon,oc?samednerpa?topsgolb,vog???dil?e&datic?n&ahc?nahc!gnikooc?levart?rehtaew???t!ni?ria?tam??vart??f&8f&pbgo--nx?tbgm--nx??a?n??g!.&gro?mo&c?n,?oc?ten?ude?zib,??h&d?op??i!.&21k?ca?fdi?gro?inum?oc!.topsgolb,?ten?vog??a&f?m&e?g?toh???m?r?xil??l&a&b&esab?t&eksab?oof!.fo,???c?mt??e&d?hs??ihmailliw?j??m!.&esserp?gro?moc?ten?ude?v&og?uog????n!.&n&iemodleeutriv,o&med,rtsic,??oc,retsulc-gnitsoh,topsgolb,wsma,yalphk,?o??o&a?btuf?l?o&c!.ed,?hcs!.gn,??rit?u??p!.&a&cin&diws?gel??d&g,ortso?urawon??i&dem?mraw?nydg,?k&elo&guld?rtso??slopolam?tsu?ytsyrut??l&ip?o&kzs?w&-awolats?oksnok????n&img?zcel,?rog&-ai&bab?nelej??j?z??syn?tsaim?w&a&l&eib?i?o??zsraw??o&namil?tainop,??z&eiwolaib?mol???c&e&iw&alselob?o&nsos?rtso???le&im?zrogz???orw,p??d&em,ia?ragrats??e&c&i&lrog?w&ilg,o&hc&arats?orp??klop?tak????yzreibok??i&csjuoniws?ksromop?saldop??l&ahdop?opo??napokaz,tatselaer?z&romop?swozam???g&alble?ezrbo&lok?nrat??ro??hcyzrblaw?i&csomohcurein?grat?klawus??k&e&rut?walcolw??in&byr?diws,sark,?le?o&nas?tsylaib??rob&el?lam??s&als?jazel?nadg,puls?rowezrp???l&colw?e&r?vart??i&am?m???m&o&c?dar?n?tyb??s&g?iruot??t!a???n&a&gaz?nzop,?i&bul?cezczs?lbul,molow?nok?zd&eb?obeiws???uleiw?y&tzslo?z&rtek?seic????o&c,fni?k&celo?zdolk??lkan?n&leim?pek?t&uk?yzczs??z&copo?eing?rowaj???rga?tua?w&ejarg?ogarm???p&e&eb,lks??klwwortso?ohs??romophcaz?sos?t&aiwop?en?opos,ra,sezc??ude?v&irp?og!.&a&p?s!w???bni&p?w??ci?dtiw?essp?fiw?g&imu?u??hiiw?m&igu?rio?u!o???nds?o&ks?p!pu??s?wtsorats??p&a?sp!mk?pk?wk??u&m?p??wk?z??r&ksw?s??s&i?oiw?u?zu??talusnok?w&gzr?i&p?rg?w??m?opu?u!imzw???zouw????w&a&l&corw?sizdow??w??o&golg?k&ark,ul?zsurp??r&az?gew??t&rabul,sugua??z&coks?sezr????xes?y&buzsak?d&azczseib?ikseb??hcyt?n&jes?lod-zreimizak??pal?r&ogt?uzam??walup?zutrak??z&am-awar?c&aprak?iwol?zsogdyb??dalezc?ib?s&i&lak?p??uklo????l??r&as?f?s??s!.&gro?moc?ten?ude?vog???t!.vog??ubnatsi?x3b689qq6--nx?yc5rb54--nx??m&00tsb3--nx?1qtxm--nx?981rvj--nx?a!.&enummoc?gro?moc?oc?t&en?opsgolb,??c!bew??dretsma?e&rts?t!.esruocsid,??fma?rirhs?xq--nx??b!.&gro?moc?ten?ude?vog??i??c!.&moc?oc?ten?vog???d!.&gro?moc?ten?ude?vog???f!i??g!vu96d8syzf--nx??h?i!.&ca?gro?mo&c?n,?o&c!.&clp?dtl???r,?t&en?t??vt??k?rbg4--nx??k!.&drp?e&rianiretev?sserp??gro?lim?m&o&c?n??t??nicedem?ossa?pooc?s&eriaton?neicamrahp?sa??ude?v&og?uog????l&if?ohkcots??o!.&dem?gro?m&oc?uesum??o&c?rp??ten?ude?vog??b?c!.&2aq,3pmevres,a&c&-morf,ir&bafno,fa,??g&-morf,oy-sehcaet,?i-morf,m&-morf,all&-a-si,amai,??p&-morf,c-a-si,?remacytirucesym,s,tadtsudgniht,v-morf,w-morf,z,?b&dnevarym,ew&-sndnyd,ottad,?g,ildts.ipa,?c&amytirucesemoh,d-morf,esyrcs,n&-morf,vym,?p&kroweht,ytirucesemoh,?q,rievres,s-morf,?d&aerotffuts,e&calpb,ifitrec-&si,ton-si,?llortnocduolc,?i-morf,m-morf,n&-morf,abeht-htiw-si,?s-morf,uolc&-noitatsyalp,hr,meaeboda,panqym:-&ahpla,ved,?,smetsystuo,ved&j,pw,??wetomer,?e&butuoyhtiw,ciffo-sndnyd,d:-morf,o&celgoog,n&il.&recnalabedon,srebmem,?neve.&1-&su,ue,?2-&su,ue,?3-&su,ue,?4-&su,ue,????,erf&-sndnyd,sndd,?filflahevres,gnahcxeevres,i&hcet-a-si,p-sekil,?k&auqevres,irtsretnuocevres,?l&bitpa-no,googhtiw,?m&agevres,ina-otni-si,oh-&sndnyd,ta-sndnyd,??n&-morf,ilnoysrab,og-si,?r&alfduolcyrt,ihcec,uzanoppanex,?srun-a-si,t&i&nuarepo,s&-ybboh,aloy,tipohs,??omer-sndnyd,ysgolb,?v&als-elcibuc-a-si,i&lsndd,tavresnoc-a-si,??z&amkcar,eelg,iig,??fehc-a-si,g&ni&gats-swennwot,ksndd,robsikrow,?o&fgp,lb&-sndnyd,sihtsetirw,???h&n-morf,o-morf,?i&fiwehtno,h-morf,kiw-sndnyd,m-morf,r-morf,w-morf,z&ihcppa,nilppa,??jn-morf,k&a&-morf,erfocsic,?cils-si,eeg&-a&-si,si,?sndd,?h,latsnaebcitsale:.&1-&htuos-pa,lartnec-&ac,ue,?ts&ae&-&as,su,?ht&ron-pa,uos-pa,??ew-&su,ue,vog-su,???2-ts&ae&-su,ht&ron-pa,uos-pa,??ew-&su,ue,??3-ts&aehtron-pa,ew-ue,??,o-morf,r&adhtiwtliub,ow&-&sndnyd,ta-sndnyd,?ten-orehkcats,??u,?l&a&-morf,colottad,rebil-a-si,?f-morf,i&-morf,am-sndnyd,?l&ecelffaw,uf-ytnuob:.a&hpla,teb,?,?ppmswa,ru-&elpmis,taen,?ssukoreh,?m&n-morf,pml.ppa,rofererac-htlaeh,sacrasevres,uirarret-yltsaf,?n&a&cilbuper-a-si,f&-sllub-a-si,racsan-a-si,?i&cisum-a-si,ratrebil-a-si,??c,eerg-a-si,i-morf,m-morf,o&ehtnaptog,isam-al-a-tse,ollabtib,rtap-el-tse,s&iam-al-a-tse,replausunu,??pj,t-morf,?o&bordym,c,jodsnd,m-morf,n:iloxip,,ttadym,?p&2pevres,aelutym,i&-sndnyd,fles,ogol,ruoy&esol,hctid,?ymteg,?pa&-rettalp,anis:piv,,esaberif,k1,lortnocduolc,oifilauq,r&aegyks,oetem:.ue,,?tnorfegap,ukoreh,?t&fevres,thevres,??r&a:-morf,tskcor-a-si,,b,e&d&ivorpnwo,ner&.ppa,no,??e&bevres,nigne-na-si,?ggolb-a-si,h&caet-a-si,pargotohp-a-si,?krow-drah-a-si,n&gised-a-si,ia&rtlanosrep-a-si,tretne-na-si,??p&acsdnal-a-si,eekkoob-a-si,?retac-a-si,subq,tn&ecysrab,iap-a-si,uh-a-si,?vres&-s&ndnyd,pvtsaf,?inim,nmad,?y&alp-a-si,wal-a-si,?zilibomdeepsegap,?g,k,mgrp.nex,o&-morf,sivdalaicnanif-a-si,t&c&a-na-si,od-a-si,?susaym,??p-morf,u&as-o-nyd,eugolb-nom-tse,omuhevres,??s&a&ila&nyd,snd,?nymsd,?bbevres,ci&p&-sndnyd,evres,?tcatytiruces,?dylimaf,e&cived-anelab,itilitu3,lahw-eht-sevas,mag-otni-si,tyskciuq,?i&ht2tniop,paelgoog,?k&-morf,aerf-ten,colbpohsym,?m&-morf,cxolb,?n&d&-pmet,dyard,golb,mood,tog,?nyd,ootrac-otni-si,?o&-xobeerf,xobeerf,?ppatneg,r&ac-otni-si,etsohmaerd,?s&e&l-rof-slles,rtca-na-si,?ibodym,?u,wanozama.&1-&htuos-pa&-3s,.&3s,etisbew-3s,kcatslaud.3s,??la&nretxe-3s,rtnec-&ac&-3s,.&3s,etisbew-3s,kcatslaud.3s,??ue&-3s,.&3s,etisbew-3s,kcatslaud.3s,????ts&ae&-&as&-&3s,etisbew-3s,?.kcatslaud.3s,?su:-etisbew-3s,.kcatslaud.3s,,?ht&ron-pa&-&3s,etisbew-3s,?.kcatslaud.3s,?uos-pa&-&3s,etisbew-3s,?.kcatslaud.3s,???ew-&su-&3s,etisbew-3s,?ue&-&3s,etisbew-3s,?.kcatslaud.3s,?vog-su-&3s,spif-3s,????2-ts&ae&-su&-3s,.&3s,etisbew-3s,kcatslaud.3s,??ht&ron-pa&-3s,.&3s,etisbew-3s,kcatslaud.3s,??uos-pa&-&3s,etisbew-3s,?.kcatslaud.3s,???ew-&su-&3s,etisbew-3s,?ue&-3s,.&3s,etisbew-3s,kcatslaud.3s,????3&-tsew-ue&-3s,.&3s,etisbew-3s,kcatslaud.3s,??s,???t&arcomed-a-si,c-morf,eel&-si,rebu-si,?m-morf,n&atnuocca-na-si,e&duts-a-si,r-ot-ecaps,tnocresu&buhtig,elbavresbo.citats,pl,???ops&edoc,golb,ppa,?s&i&hcrana-&a-si,na-si,?laicos-a-si,pareht-a-si,tra-na-si,xetn&od,seod,??oh&piym,sfn,??u&-morf,nyekcoh-asi,?v-morf,?u&-rof-slles,4,e,h,oynahtretramssi,r:ug-a-si,,?v&n-morf,w-morf,?w&ozok,ww100,?x&bsbf.sppa,em,inuemoh,obaniateb,t-morf,unilemoh,?y&a&bnx:.&2u,lacol-2u,?,lerottad,wetag-llawerif,?dnacsekil,filten,k&-morf,niksisnd,?rotceridevitcaym,u:goo,,w-morf,x&alagkeeg,orphsilbup,???inu??m!.&dna,rof,??or?tsla??p!.nwo,?raf!.jrots,etats??s?t!.&gro?lim?mo&c?n??oc?ten?ude?vog???u&esum!.&a&92chg-seacinumocelet-e-soierroc--nx?atnav?c&i&aduj?rfatsae??rollam??d&anac?enomaledasac?irolf??e&raaihpledalihp?srednu??g&hannavas?oonattahc??hamo?i&auhsu?bmuloc!hsitirb??dem?groeg?hpledalihp?l&artsua?etalif??n&igriv?rofilac??ssur?tsonod??ksa&la?rben??l&lojal?q-snl--nx?uossim!trof???m&a&bala?nap??enic?o&m?r???n&a&cirema?idni??edasap?ilorachtuos?olecrab??r&abrabatnas?ezzivs??su?t&nalta?osennim??zalp??c&dnotgnihsaw?ebeuq?i&depolcycne?ficap?hpargonaeco?lbup?sum?t&carporihc?lec?naltadim??vu??yn??d&a&dhgab?etsmraf?m?orliar??i&rdam?ulegnedleeb??leif?n&a!l&gne?nif?ragyduj?t&ocs?rop??yram???u&brofsdgybmeh?osdnaegami???r&augria?ofxo???e&c&a&l&ap?phtrib??ps??n&a&lubma?tsiser??e&fedlatsaoc?gilletni?ics!foyrotsih????pein?rof??d&nukneklov?revasem??e&rt?tsurt??f&atnas?ildliw??g&a&lliv?tireh!lanoitan???dirbmac?rog??i&cnum?nollaw??koorbrehs?l&ab?bib?cycrotom?i&ssim?txet??oks?tsac??m&affollah?it!iram??utsoc??n&golos?ilno?recul??r&a&uqs?waled!foetats???i&hs&acnal?kroy?pmahwen??otsih??omitlab?ut&an?cetihcra?inruf?luc!irga?su???vuol??s&abatad?iacnarf?sius?uoh!lum???t&a&locohc?rak?ts!e!yrtnuoc!su?????imesoy?tevroc??u&qihpargonaeco?velleb??vit&caretni?omotua???f&iuj?ohgrub??g&n&i&dliub?ginerevmuesum?kiv?lahw?nim?peekemit?vil??ulmmastsnuk??orf?r&ebnrats?u&b&ierf?le?m&ah?uan??ram?s&mailliw!lainoloc??naitsirhc?retepts??zlas??ob&irf?mexul?????h&atu?c&raeser?sirotsih?uot??g&ea1h--nx?rubsttip??si&tirb?wej??t&laeh?ro&n?wtrof??uo&mnom?y????i&d6glbhbd9--nx?iawah?k&nisleh?s??lad!rodavlas??sissa?tannicnic??k&c&nivleeg?olc!-dna-hctaw?dnahctaw???fj?inebis?l&is?ofron??na&rfenna?t??oorbnarc?r&am&ned?reiets??oy!wen????l&a&ci&dem?golo&eahcra?meg?oz??natob?rotsih??ertnom?iromem?noita&cude?n??oc?rutluc?trop?utriv?van??e&nurb?s&ab?surb??utriv??i&artnogero?sarb??l&a&besab?hsnoegrus??e&hs?rdnevle??i&b?m!dniw????o&bup?ohcs?tsirb???m&a&dretsma?ets?h&netlehc?rud???ct?elas!urej??l&if?ohkcots?u??raf?silanruoj?u&esumyrotsihlarutan?ira&tenalp?uqa??terobra???n&a&c!irema!evitan???gihcim?i&dni?tpyge??mfoelsi?wehctaksas??e&d&alokohcs?ews?rag!cinatob?lacinatob?s&nerdlihc?u????gahnepoc?hcneum?laftsew?ppahcsnetewruutan?r&dlihc?ednaalv?hu!dnutamieh???sseig??gised!dn&atra?utsnuk???h&ab!nesie??ojts??i&lreb?tsua??l&eok?ocnil??n&ob?urbneohcs??o&dnol?gero?i&s&iv&dnadnuos?elet??nam??t&a&c&inummoc?ude!tra???dnuof?erc?i&cossa?va??kinummokelet?nissassa?r&belectsevrah?oproc?tsulli??silivic?t&nalp?s??vres&erp?noclatnemnorivne??zilivic??c&elloc?if-ecneics??ibihxe???ri?s&dnah?imaj?reffej?sral??t&erbepac?nilc?sob???r&e&b?dom?tsew?uab?zul??obredap??vahnebeok?wot??o&2a6v-seacinumoc--nx?ablib?c&edtra?ixemwen?sicnarfnas??elap?g&a&cihc?to??eidnas??i&cadnuf?diserp?ratno??llecitnom?mitiram?nirot?r&htna?ienajedoir???pohskrow?qari?r&aw!dloc?livic??dd?e&b&ma?yc??irrac?llimsiwel?naksiznarf?papswen?t&aeht?exe?nec!ecneics?larutluc?muesum?tra??s&ehc&nam?or??neum??upmoc???ia!nepo??obal?u&asonid?obal?takirak???s&a&l&g?l&ad?eh???xet??di&k?pardnarg??e&cneics!larutan??dnal?hcsi&deuj?rotsih!nizidem?rutan??selhcs??itinamuh?l&aw?egnasol?l&e&rutansecneics?xurb??iasrev???r&e&em?ugif??tsac??suohcirotsih?u&en?q&adac?itna!nacirema?su????õçacinumoc!elet-e-soierroc???gnirpsmlap?htab?i&lopanaidni?rap?uoltnias?xa??l&essurb?lod??mraeriflanoitan?n&a&blats?l??erdlihc?oi&snam?tacinummoc!elet-dna-stsop???äl??re&dnalf?lttes?mraf?nim?tnececneics??s&alg?erp??t&farc!dnastra??nalp?olip?ra!e&nif?vitaroced!su???su?xuaeb???u&b!muloc??cric???t&agilltrop?cejorp?dats?e&esum?kramnaidni??iorted?ne&m&elttes?norivne?piuqemraf??vnoc??oped?r&a!drib?enif?gttuts?hsiwej?kcor?n&acirema?ootrac??tamsa?yraropmetnoc??op&aes?snart?wen??ufknarf??s&a&cdaorb?octsae??ewhtuos?ilayol?nuk?r&ohnemled?uhlyram??urt???u&a&bgreb?etalpodaroloc??rmyc??w&ocsom?rn??x&esse?ineohp?nam?tas??y&a&bekaepasehc?w&etag?liar???camrahp?doc?e&hsub?l&ekreb?l&av!eniwydnarb??ort???n&dys?om??rrus?s&nreug?rejwen???golo&e&ahcra?g??motne?nh&cet?te??oz?po&rhtna?t??roh??hpargotohp?l&etalihp?imaf??m&edaca?onortsa??n&atob?yn??ps?r&a&ropmetnoc?tilim??e&diorbme?llag!tra??vocsid??lewej?nosameerf?otsih!dnaecneics?ecneics?gnivil!su??la&col?rutan??retupmoc?su??tsudnidnaecneics??spelipe?t&eicos!lacirotsih??i&nummoc?srevinu??nuoc???z&arg?iewhcs?nil?ojadab?urcatnas??моки?םילשורי???rof??z!.&ca?gro?hcs?lim?moc?o&c?fni??ten?ude?vog?zib????n&315rmi--nx?a&brud?cilbuper?f?grompj?hkaga?idraug?m?ol?ssin?u&hix?qna??varac?yalo??b!.&gro?moc?oc,ten?ude?vog??c??c!.&ah?bh?c&a?s??d&5xq55--nx?g?s?uolctnatsni,?eh?g&la0do--nx?ro??h&a?q?s??i&7a0oi--nx?h??j&b?f?t?x?z??kh?l&h?im?j??m&n?oc!.swanozama.&1-htron-nc.3s,be.1-&htron-nc,tsewhtron-nc,????n&h?l?s?y??om?qc?s&g?j??ten?ude?vog?wt?x&g?j?n?s??z&g?x??司公?絡網?络网??b??d&g!.ypnc,?ka??e&drag?erg?fuak?gawsklov?hctik?i&libommi?w??m!.rof,?po?r!ednaalv??sier?ves??g!.&ca?gro?moc?ten?ude?vog??is&ed!.ssb,?irev???h!.&bog?gro?lim?mo&c?n,?ten?ude???i!.&c&a?in??dni?gro?lim?mrif?neg?oc?s&er?nduolc,?t&en?opsgolb,?ude?vog?ysrab,?elknivlac?griv?ks?lreb?p!ul??v?w?x??k!.&gro?ten?ude?vog???l&eok?ocnil??m!.&cyn,gro?myn,ude?vog???o&dnol!.&fo,ni,??i&hsaf!.&fo,no,??n&o?utiderc??siv!orue??t&a&cude!.oc,?dnuof?tsyalp??c&etorp?u&a?rtsnoc?????kin?las?mrom?nac?p&q?uoc??s&iam?nhojcs?pe?scire??t&ron?sob??zama??p!.&gro?oc?ten?ude?vog??k??r&e&c?yab??op??s!.&gro?moc?osrep?t&opsgolb,ra??ude?v&inu?uog????t!.&dni?esnefed?gro?ltni?m&oc!nim??siruot??n&erut?if??o&fni?srep??sn&e?r??t&an?en!irga?ude??rnr??unr?vog??m??u&f?r!.&bdnevar,lper,sh,tnempoleved,??stad?xamay?y??v!.&ca?eman?gro?htlaeh?moc?o&fni?rp??t&en?ni?opsgolb,?ude?vog?zib???wo&rc?t!epac????o&76i4orfy--nx?a!.&bp?de?go?oc?ti?vg??boat??b!.&a&ci&sum?tilop??i&c&arcomed?neic??golo&ce?ncet??m&edaca?onoce??rt&ap?sudni??vilob??n&egidni?icidem??serpme?tsiver?vitarepooc??b&ew?og??dulas?e&rbmon?tr&a?op&ed?snart????g&olb?ro??ikiw?l&a&noi&canirulp?seforp??rutan??im??moc?o&fni?lbeup?rga?tneimivom??saiciton?t&askt?en?ni??ude?vt??h?iew?olg??c!.&bew?cer?dr&c,rac,?gro?ipym,l&im?per,?m&o&c!.topsgolb,?n??rif?udon,?ofni?s&egap&dael,l,?tra??t&4n,en?ni??ude?vog??a?e!vi??in?mara?s&edarb?ic???d!.&b&ew?og??dls?gro?lim?moc?t&en?ra??ude?vog??agoba?if?zd7acbgm--nx??e&c?d&iv?or??morafla??f!ni!.&e&g&delwonk-fo-l&errab,lerrab,?ellocevoli,?ht-skorg,rom-rof-ereh,tadpusn:d,,?llatiswonk,macrvd,ofni-v,p&i&-on,fles,?ohbew,?ruo-rof,s&iht-skorg,nd&-cimanyd,nyd,uolc,??tsrifyam,ysrab,zmurof,???g&el?ia?n!am?ib???hwsohw?i!.&35nyd,8302,a&minifed,tad-b,?b&altig,uhtig,?c&inone:.remotsuc,,zh,?d&in,u&olc&iaznab.ppa,noitacilppa,ropav,?rd,??e&civedniser,donppad.sndnyd,egipa,nilnigol,sufxob,t&isnoehtnap,newtu,??gnigatsniser.secived,k&orgn,ramytefasresworb,?m&oc?udon,?nyded,p&opilol,pa&-arusah,cs,enalpkcab,??r&evres&cisab,lautriv,?ial.sppa,?s&codehtdaer,nemeis-om,pparevelc,tacdnas,?t&enotorp,i&belet,detfihs,kecaps,?raedon.egats,sudgniht.&cersid.tsuc,dorp.tsuc,gnitset.tsuc,ved.tsuc,??vgib.0ku,xcq,y&olpedew,srab,??b?d&ar?u&a?ts???j?r?syhp??j!.&eman?gro?hcs?lim?moc?ten?ude?vog???ll&ag?o??m!.&gro?moc?ten?ude?vog??g?il?mi?orp??n!.&a&0&b-ekhgnark--nx?c-iehsrgev--nx?g-lksedlig--nx?k-negnanvk--nx??1&p-nedragy--nx?q-&asierrs--nx?grebsnt--nx?lado-rs--nx?n&egnidl--nx?orf-rs--nx??regnayh--nx?ssofenh--nx??r-datsgrt--nx?s-ladrjts--nx?v-y&senner--nx?vrejks--nx???3g-datsobegh--nx?4&5-&dnaleprj--nx?goksnerl--nx?tednalyh--nx??6-neladnjm--nx?s-&antouvachb--nx?impouvtalm--nx??y-&agrjnevvad--nx?ikhvlaraeb--nx???7k-antouvacchb--nx?8&k-rekie-erv--nx?l-ladrua-rs--nx?m-darehsdrk--nx??a!.sg??bct-eimeuvejsemn--nx?d&do?iisevvad?lov?narts?uas??f&1-&l--nx?s--nx??2-h--nx??g&10aq0-ineve--nx?av?ev?lot?r&ajn&evvad?u??ájn&evvad?u????h?iz-lf--nx?j&ddadab?sel??k&el?hoj&sarak?šárák??iiv&ag&na&el?g??ŋ&ael?ág???ran???l&f?lahrevo?o&ms?s??sennev?t-&ilm--nx?tom--nx??u&-edr--nx?s??øms??muar?n&0-tsr--nx?2-dob--nx?5-&asir--nx?tals--nx??a&r!-i-om?f?t??t??douvsatvid?kiv?m&os?øs??n&od?ød??ra?sen?t&aouvatheig?ouv&a&c&ch&ab?áb??h&ab?áb???n??i&ag?ág??sa&mo?ttvid??án???z-rey--nx?ær&f?t???o&p-&ladr--nx?sens--nx??q-nagv--nx?r-asns--nx?s-kjks--nx?v-murb--nx?w-&anr&f--nx?t--nx??ublk--nx???ppol?q&0-t&baol--nx?soum--nx?veib--nx??x-&ipphl--nx?r&embh--nx?imph--nx???y-tinks--nx??r&f-atsr--nx?g-&an&ms--nx?nd--nx??e&drf--nx?ngs--nx??murs--nx?netl--nx?olmb--nx?sorr--nx??h-&a&lms--nx?yrf--nx??emjt--nx??i&-&lboh--nx?rsir--nx?y&d&ar--nx?na--nx??ksa--nx?lem--nx?r&ul--nx?yd--nx????stu??j-&drav--nx?rolf--nx?sdav--nx??kua?l-&drojf--nx?lares--nx??m-tlohr--nx?n-esans--nx?olf?p-sdnil--nx?s-ladrl--nx?tih?v-rvsyt--nx??s&a&ns?ons??i&ar?er&dron?r&os?øs???ár??la&g?h??mor!t??sir?uf?åns??t&koulo&nka?ŋká??la?p-raddjb--nx?r-agrjnu--nx?s&aefr&ammah?ámmáh??orf?r&o?ø???u-vreiks--nx??u&h-dnusel--nx?i-&drojfk--nx?vleslm--nx??j-ekerom--nx?k-rekrem--nx?u-&dnalr--nx?goksr--nx?sensk--nx??v-nekyr--nx?w-&k&abrd--nx?ivjg--nx??oryso--nx??y-y&dnas--nx?mrak--nx?n&art--nx?nif--nx??reva--nx??z-smort--nx??v!.sg?ledatskork?reiks??wh-antouvn--nx?x&9-dlofts--nx.aoq-relv--nx?d-nmaherk--nx?f-dnalnks--nx?h-neltloh--nx?i-drgeppo--nx?j-gve&gnal--nx?lreb--nx??m-negnilr--nx?n-drojfvk--nx??y&7-ujdaehal--nx?8-antouvig--nx?b-&dlofrs--nx?goksmr--nx?kivryr--nx?retslj--nx??e-nejsom--nx?f-y&krajb--nx?re&dni--nx?tso--nx??stivk--nx??g-regark--nx?orf?ørf??z9-drojfstb--nx??b&25-akiivagael--nx?53ay7-olousech--nx?a&iy-gv--nx?le-tl&b--nx?s--nx??n0-ydr--nx??c&0-dnal-erdns--nx?z-netot-erts--nx??g&g-regnarav-rs--nx?o-nejssendnas--nx??ju-erdils-ertsy--nx?nj-dnalh-goksrua--nx?q&q-ladsmor-go-erm--nx.&ari-yreh--nx?ednas??s-neslahsladrjts--nx???ca&4s-atsaefrmmh--nx?8m-dnusynnrb--nx?il-tl--nx?le-slg--nx?n5-rdib--nx?op-drgl--nx?uw-ynnrb--nx??d&a&qx-tggrv--nx?reh!nnivk?sd&ork?ørk??uas??ts&e&bi?kkar?llyh?nnan??g&ort?ørt??k&alf?irderf??levev?mirg?obeg&ah?æh??r&ah?ejg????barm-jdddb--nx?ie!rah?s&etivk?ladman???lof&r&os?øs??ts&ev.ednas?o.relav?ø.relåv???n&a&l&-erd&n&os?øs??ron??adroh.so?dron.&a&g5-b--nx?ri-yreh--nx??ob?y&oreh?øreh??øb??e&m!lejh??pr&oj?øj??vi??gyb?n&aks?åks??o&h-goksrua?rf??r&o?ua?ø??tros?øh-goksrua??rts!e&devt?lab?mloh???s&ellil?naitsirk?rof???u&l!os??s!d&im?lejt??e&guah?l&a?å???kkoh?lavk?naitsirk?r&af?eg&e?ie???tef?y&onnorb?ønnørb?????r&a&blavs!.sg??g&eppo?la???o&j&f&a!dniv?k?vk??die?e&dnas?kkelf??llins?r&iel?ots??s&lab?t&ab?åb??yt??å!k??ævk??les??ts??åg&eppo?lå???ureksub.sen??e&ayb-yrettn--nx?d&ar?lom?r&of?øf??år??g&gyr?nats??i&meuv&ejsem&aan?åån??sekaal??rjea??j&d&ef?oks??les??k&er&aom?åom??hgna&ark?årk??iregnir?kot!s??s&ig?uaf???l&bmab?kyb?l&av?ehtats??oh??m&it?ojt?øjt??n&arg?g&os?øs??meh?reil?te?ummok?yrb??r&dils-erts&ev?y&o?ø???ua?vod??sa&ans?åns??t&robraa?spaav??urg??f&62ats-ugsrop--nx?a&10-ujvrekkhr--nx?7k-tajjrv-attm--nx??o!.sg?h??s!.sg??v!.sg???g&5aly-yr&n--nx?v--nx??a&llor?ve&gnal?lreb???n&av!snellu??org??oks&die?m&or?ør??ner&ol?øl??r&o?ø???r&eb!adnar?edyps?s&die?elf?gnok?n&ot?øt????obspras??uahatsla?åve&gnal?lreb???h&0alu-ysm--nx?7&4ay8-akiivagg--nx?5ay7-atkoulok--nx??a!.sg???i&e&hsr&agev?ågev??rf??k&h&avlaraeb?ávlaraeb??s??lm&a?å??mpouvtal&am?ám??pph&al?ál??rrounaddleid?ssaneve?ššáneve??j&0aoq-ysgv--nx?94bawh-akhojrk--nx??k&a&b&ord?ørd??jks?lleis??iv!aklejps?l&am?evs?u??mag?nel?ojg?r&a&l?n??epok?iel?y&or?ør???s&ah?kel?om??øjg??kabene?ojsarak?ram&deh.&aoq-relv--nx?rel&av?åv??so??e&let.&ag5-b--nx?ob?øb??ra???åjks??l&a!d&anrus?d&numurb?ron??e&gnard?nte?s&meh?sin??ttin??g&is?nyl??kro?l&em?l&ejfttah?of??u&ag-ertdim?s???n&am?era?gos?i&b?nroh?r??kos?nus?oj??o-&dron?r&os?øs???ppo?r&a!l?nram??e&gne?l?v??is?o&jts?ts??u&a-&dron?r&os?øs???h??å?æl?øjts??s&e&jg?nivk?ryf??kav?mor-go-er&om.&ednas?yoreh??øm.&ednas?yøreh???uag??t&las?rajh?suan??v&l&a?e-rots??u-go-eron??yt??ksedlig?res&a?å???bib&eklof?seklyf??es!dah??h!.sg??i&m?syrt??l&ejf?ov&etsua?gnit?ksa?sdie???n!.sg??o!.sg?boh?g?h??r!.sg??å!ksedlig??øboh??m&a&rah?vk??f!.sg??h!.sg??i&e&h&dnort?rtsua?ssej??rkrejb??ksa??ol?t!.sg??u&dom?esum?r&ab?drejg?evle?os?uh?æb?øs??ttals???n&a&g&av?okssman?åv??jlis?or?r&g?rev???e&d&do&sen?ton??lah?r&agy&o?ø??ojfsam???g&iets?n&a&l&as?lab??n&avk?ævk??t&arg?ddosen??v&al?essov???i&d&ol?øl??l&ar?ær???yl??reb??iks?k&srot?y&or?ør???l&a&d&gnos?n&er?ojm?øjm??om??tloh??ug?åtloh??mmard?ojs&om?sendnas??ppolg?s&lahsladr&ojts?øjts??o??t&o&l?t-erts&ev?o?ø???roh?øl??vly&kkys?nav??yam-naj!.sg??øjs&om?sendnas???g&orf?ujb??i&dnaort?vnarg??kob?ladendua?maherk&a?å??n&it?urgsrop??orf-&dron?r&os?øs???r&aieb?evats??sfev?uaks?yrts??o&6axi-ygvtsev--nx?c,d&ob?rav??ievs?kssouf?l&m&ob?øb??ous&adna?ech&ac?áč???so!.sg???msdeks?niekotuak?r&egark?olf?y&oso?øso???s&dav?mort???p&ed?p&akdron?elk???r&a&d&dj&ab?áb??iab??jtif?luag?mah?vsyt??e&gn&a&k&iel?ro??merb?n&at?mas??rav-r&os?øs??srop?talf?v&ats?el??y&oh?øh???ivsgnok??il?jkniets?k&a&nvej?rem?s&gnir?nellu???ie-er&den?v&o?ø???ram?sa?årem??la&jf?vh??m&b&ah?áh??mahellil??nnul?ts&l&oj?øj??ul??y&o?ø???imp&ah?áh??m!.sg??osir?t!.sg??ádiáb?ævsyt?øsir??s&adnil?en&dnas?e&dga?k&ri&b?k??som??ve??me&h?jg??nroh-go-ejve?s&a?ednil?k&o?ø??of?yt?å??tsev??gv?hf?igaval?o&r&or?ør??sman??so&fen&oh?øh??m?v??uh&lem?sreka.sen??å!dnil???t&a&baol?g&aov?grav??jjr&av-attam?áv-attám??l&a&b?s??ás??soum?ts?v&eib?our???e&dnaly&oh?øh??f?s&nyt?rokomsdeks?sen??vtpiks??in&aks?áks??loh&ar?år??n!.sg??o&m&a?å??psgolb,?s!.sg?efremmah?or?ør??terdi?á&baol?ggráv?lá&b?s??soum?veib???u&b!.sg?alk?e&dna?gnir?nner??les?ælk??dra&b?eb??g&nasrop?vi?ŋásrop??j&daehal&a?á??jedub?v&arekkhar?árekkhár???ksiouf?n&diaegadvoug?taed???v&irp?lesl&am?åm???y&b&essen?nart?sebel?tsev??o&d&ar?na!s??or??gavtsev?k&rajb?sa??lem?mrak?n&art?n&if?orb???r&a&mah?n?v??e&dni?t&so?ton??va??ul?yd??s&am?enner?gav?lrak?tivk??vrejks??ø&d&ar?na!s??ør??gåvtsev?k&rajb?sa??lem?mrak?n&art?n&if?ørb???r&e&dni?t&so?tøn??va??ul?yd?æ&n?v???s&enner?gåv?tivk?åm??vrejks???á&slág?tlá?vreiks??å&gåv?h?jddådåb?lf??ø&d&ob?rav??r&egark?olf??s&dav?mort????aki?i&sac?tal??u??o&b?f?g?hay?o?ttat??r!.&cer?erots?gro?m&o&c?n??rif?t?yn,?ofni?pohs,stra?t&n?opsgolb,?www??e&a!.&a&ac?cgd?idem??bulc!orea??ci&ffartria?taborea??e&cn&a&l&lievrus-ria?ubma??netniam?rusni??erefnoc??gnahcxe?mordorea?ni&gne?lria?zagam??rawtfos??gni&d&art?ilg!arap?gnah???l&dnahdnuorg?ledom??noollab?retac?sael?t&lusnoc?uhcarap??vidyks??hcraeser?l&anruoj?euf?icnuoc?ortnoc!-ciffart-ria???n&gised?oi&nu?t&a&cifitrec?ercer?gi&tsevni-tnedicca?van??i&cossa!-regnessap??valivic??redef??cudorp?neverp-tnedicca????ograc?p&ihsnoipmahc?uorg!gnikrow???r&e&dart?enigne?korb?niart?trahc??o&htua?tacude???s&citsigol?e&civres?r??krow?serp!xe??tnega??t&farcr&ia?otor??hgil&f?orcim??liubemoh?n&atlusnoc?e&duts?m&esuma?n&iatretne?revog??piuqe????olip?ropria?si&lanruoj?tneics???w&erc?ohs??y&cnegreme?dobper?tefas????rref?z??p!.&a&aa?ca?pc??dem?ecartsnd.icb,gne?r&ab?uj??snduolc,t&acova?cca?hcer??wal?ysrab,???s!.&em?gro?hcs,moc?ten?ude?vog???t!.&ayo,gro?lim?moc?sulpnpv,t&cennockciuq.tcerid,en??ude?vog??o&hp?m?v?yk??tol?ua??v&iv?lov??xas?ykot??p&a&ehc?g?m?s??cj?eej?g!.&gro?ibom?moc?ossa?ten?ude???i&r!.nalc,?v?z??j!.&a&3&5xq6f--nx?xqi0ostn--nx??5wtb6--nx?85uwuu--nx?9xtlk--nx?bihc!.&a&bihciakoy?don?ma&him?ye&ragan?tat???r&a&bom?gan?hihci??u&agedos?kas?ustak???s&os?ufomihs??t&amihcay?iran??w&a&g&im&anah?o??omak??kihci?zustum??ihsak??y&agamak?imonihci???e&akas?nagot??i&azni?esohc?h&asa?s&abanuf?ohc???ka&to?zok??musi?orihs?r&akihabihsokoy?o&dim?tak??ukujuk??usihs??nano&hc?yk??o&d&iakustoy?ustam??hsonhot?k&a&rihs?t??iba??nihsaran?sobimanim?tas&arihsimao?imot??uhc?yihcay??u&kujno?s&ayaru?t&imik?tuf???zarasik????g&as!.&a&gas?m&a&tamah?yik??ihsak??rat?t&a&gatik?hatik??ira!ihsin????e&kaira?nimimak??i&akneg?g&aruyk?o??h&c&amo?uo??siorihs??kaznak?modukuf?ra&gonihsoy?mi???nezih?u&k&at?ohuok??s&ot?tarak?????ihs!.&a&kok?m&a&hagan?yirom??ihsakat??rabiam?wagoton??e&miharot?nokih??houyr?i&azaihsin?esok?kustakat?moihsagih??na&mihcahimo?nok??o&hsia?mag?t&asoyot?ok?tir???us&ay?t&asuk?o??????k&aso!.&a&d&awihsik?eki??k&a&noyot?s&akaayahihc?oihsagih???oadat?uziak??m&ayas!akaso??odak??r&a&bustam?wihsak??ediijuf??t&akarih?i&k?us???wag&ayen?odoyihsagih???e&son?tawanojihs??honim?i&akas?h&cugirom?s&ayabadnot?i&a&kat?t??n??oyimusihsagih???k&a&rabi?sim??ustakat??muzi?r&ijat?otamuk???nan&ak?n&ah?es???o&ay?n&a&ganihcawak?simuzi?tak??eba?ikibah?oyot??t&anim?iad?omamihs??uhc??ust&oimuzi?tes????ou&kuf!.&a&d&amay?eos??g&no?ok?usak??hiku?k&awayim?uzii??ma&kan?y&asih?im???rawak?t&a&gon?ka&h?num?t???umo??wa&g&a&kan?nay?t??ias??ko!rih???y&ihsa?usak???e&m&ay?uruk??taruk?us??i&a&nohs?raihcat??goruk?h&cukuf?s&a&gih?hukuy??in???k&a&gako?muzim??iust?o?ustani??m&anim?otihsoynihs?u??r&ogo?ugasas??usu??ne&siek?zu&b?kihc???o&gukihc?h&ak?ot?ukihc??j&ono?ukihc??kayim?nihsukihc?to?uhc??u&fiazad?gnihs?stoyot????zihs!.&a&bmetog?d&amihs?eijuf?ihsoy?omihs??kouzihs?mihsim?ra&biah?honikam??tawi?wa&g&ekak?ukik??kijuf??yimonijuf??i&a&ra?sok??hcamirom?juf?kaz&eamo?ustam??ma&nnak?ta??nukonuzi?orukuf??nohenawak?o&nosus?ti??u&stamamah?z&a&mun?wak??i!ay?i&hs&agih?in??manim??mihs????????m&a&tias!.&a&d&ihsoy?ot?usah??k&a&dih?sa??o&arihs?s???m&a&tias?y&as?o&rom?tah??ustamihsagih???i&hsagurust?jawak??uri??ni?wa&g&e&ko?man??ikot?o??k&ara?i&hsoy?mak???ru?zorokot??y&a&g&amuk?ihsok?otah??kuf??imo??ziin??e&bakusak?ogawak?sogo?ttas?zokoy??i&baraw?h&cugawak?s&oyim?ubustam???iroy?k&ato?ihs?u&k?stawi???m&akoyr?i&hsoy?juf??uziimak???naznar?o&dakas?ihsay?jnoh?n&a&go?nim??imijuf?nah?oy??r&ihsayim?otagan??t&asim!ak??igus?omatik??zak??u&bihcihc!ihsagih??sonuok?ynah????y&ak&aw!.&a&d&ira?notimak??kadih?ma&h&arihs?im??y&a&kaw?tik??oduk???ru&ustakihcan?y??sauy?wa&g&a&dira?zok??orih??konik??yok?zok??e&banat?dawi??i&garustak?jiat?mani??naniak?o&bog?nimik?t&asim?omihs&ah?uk????ugnihs???o!.&a&jos?koasak?m&ay&ako?ust??ihsayah??r&abi?ukawaihsin??wi&aka?nam???e&gakay?kaw??i&gan?h&cu&kasa?otes??sahakat??k&asim?ihsaruk??miin??n&anemuk?ezib??o&hsotas?jnihs?n&amat?imagak??ohs?uhcibik?????ot!.&a&damay?got?koakat?may&etat?ot??nahoj?riat?waki&inakan?reman???eb&ayo?oruk??i&h&asa?ciimak?sahanuf??kuzanu?m&an&i?ot??ih???nezuyn?otnan?u&hcuf?stimukuf?z&imi?ou???????ihs&o&gak!.&a&m&ayuok?ihsogak??si?yonak??e&banawak?n&at&akan?imanim??uka??tomoonihsin??i&adnesamustas?k&azarukam?oih??m&ama?uzi??usuy??nesi?o&knik?os?tomustam??uzimurat???rih!.&a&ka&n?s??m&ayukuf?i&hsorihihsagih?j&ate?imakikaso????r&a&bohs?h&ekat?im???es??tiak?wiad??e&kato?ruk??i&h&ci&akustah?mono?nihs??s&inares?oyim???manimasa?uk??negokikesnij?o&gnoh?namuk??uhcuf????uk&ot!.&a&bihci?mi&hsu&kot?stamok??m??wagakan??egihsustam?i&gum?h&coganas?soyim??kijaw?m&anim?uzia??ukihsihs??nan&a?iak??o&nati?turan????uf!.&a&batuf?m&a&to?y&enak?irok???ihs&im?ukuf??os?uko??r&aboihsatik?uganat??ta&katik?mawak?rih??w&a&g&akus?emas?uy??k&a&mat?rihs?sa??ihsi??nah??ohs???e&gnabuzia?iman?ta&d?tii???i&adnab?enet?hs&agih?iimagak??k&a&wi?zimuzi??ubay??minuk?r&ook?ustamay???nihsiat?o&g&etomo?ihsin?nan?omihs??no!duruf?rih??rihsawani?ta&may?simuzia???u&rahim?stamakawuzia?zia&ihsin?nay???????nug!.&a&bawak?doyihc?k&anna?oi&hsoy?juf?mot???m&ayakat?ustagaihsagih??n&ihsatak?nak??r&ahonagan?nak?o?u&kati?mamat???t&amun?inomihs?o??w&akubihs?iem?ohs???i&hsa&beam?yabetat??kas&akat?esi??m&akanim?uzio??ogamust?rodim??o&jonakan?n&eu?oyikust??tnihs??u&komnan?stasuk?yrik?????ran!.&a&bihsak?d&akatotamay?u!o???guraki?m&ay&atik&imak?omihs??irokotamay??oki??ra&hihsak?n??wa&geson?knet???e&kayim?ozamay?sog?ustim??i&a&rukas?wak??garustak?h&ciomihs?sinawak??jo?ka&mnak?toruk??makawak?nos?r&net?otakat?ugeh???o&d&na?oyo??gnas?jnihs?nihsoy!ihsagih??tomarawat?yrok????t&ag&amay!.&a&dihsio?k&atarihs?ourust??may&a&kan?rum??enak?onimak??rukho?ta&ga&may?nuf??hakat?kas??wa&g&ekas?orumam??ki&hsin?m??z&anabo?enoy?ot???zuy??e&agas?bonamay?dii?nihsagih?o??i&a&gan?nohs??h&asa?sinawak??nugo??o&dnet?jnihs?ynan??ukohak???iin!.&a&ga?k&ium?oagan??munou!imanim??t&a&bihs?giin??ioy??w&a&gioti?kikes?zuy??irak??yijo??e&kustim?mabust??i&aniat?hcamakot?kaz&awihsak?omuzi??m&a&gat?karum??o???n&anust?esog??o&das?ihcot?jnas?k&ihay?oym??mak?naga?ries??u&ories?steoj?????i&ka!.&a&go?k&asok?oimak??t&ago!rihcah??ika!atik???w&aki?oyk???e&mojog?natim?suranihsagih?t&ado?okoy???i&hsoyirom?magatak?naokimak??nesiad?o&hakin?jnoh!iruy??nuzak?rihson?tasi&juf?m??yjnoh??u&kobmes?oppah????o!.&a&dakatognub?m&asah?ihsemih??su?t&ekat?i&h?o????e&onokok?ustimak??i&jih?k&asinuk?ias?usu??mukust??onoognub?u&fuy?juk?ppeb?suk??????wa&ga&k!.&a&mihsoan?rihotok?waga&kihsagih?ya???emaguram?i&j&nonak?ustnez??kunas?monihcu??o&hsonot?nnam?yotim??u&st&amakat?odat??zatu????nak!.&a&dustam?kus&okoy?tarih??maz?nibe?r&a&gihsaimanim?h&esi?imagas??wa&do?guy???u&im?kamak???tikamay?wa&k&ia?oyik?umas??sijuf??yimonin??e&nokah?saya??i&akan?esiak?gusta?hsuz?kasagihc?o?ukust??o&nadah?sio?tamay?????kihsi!.&a&danihcu?gak?kihs?mijaw?t&abust?ikawak??wazanak??i&gurust?hcionon?mon?ukah??nasukah?o&anan?ton!akan???u&kohak?stamok?z&imana?us?????niko!.&a&han?m&arat?ijemuk?uru??n&e&dak?zi??no??ra&hihsin?rih??wa&kihsi?niko??yehi?zonig??e&osaru?seay??i&hsagih?jomihs?k&a&gihsi?not??ihsakot??m&a&ginuk?kihsug?maz??igo?otekat??nuga!noy???n&a&moti?timoy?wonig??i&jikan?k???o&gan?jnan?tiad&atik?imanim???u&botom?kusug&akan!atik??imot??rab&anoy?eah???????c&204ugv--nx?462a0t7--nx?678z7vq5d--nx?94ptr5--nx?a??d&17sql1--nx?3thr--nx?5&20xbz--nx?40sj5--nx??7&87tlk--nx?ptlk--nx??861ti4--nx?a?e??e&16thr--nx?5&1a4m2--nx?9ny7k--nx??im!.&a&bot?k&asustam?uzus??m&a&him?y&emak?im???ihs??nawuk?wi&em?k???e&bani?ogawak?si!imanim???i&arataw?gusim?h&asa?ciakkoy??k&a&mat?sosik?t??iat??raban??o&dat?hik?n&amuk?ihseru?o&du?mok????ust???mihe!.&a&m&a&h&ataway?iin??yustam??ij&awu?imak???taki!man???ebot?i&anoh?kasam?rabami??n&ania?egokamuk?oot??o&jias?kihcu?nustam?uhcukokihs?yi!es???u&kohik?zo????n!.&nriheg,teniesa.resu,?amihs!.&a&d&amah?ho?usam??kustay?m&a?ihsoni&hsin?ko???wakih??e&namihs?ustam??i&g&aka?usay??konikak?mikih??nannu?o&mu&kay?zi!ihsagih?uko???nawust?tasim??u&stog?yamat?????tawi!.&a&bahay?d&amay?on??koirom?t&a&honat?katnezukir??imus??w&as&ijuf?uzim??ihs???e&hon&i&hci?n??uk??tawi??i&a&duf?murak?wak??h&custo?si&amak?ukuzihs???j&oboj?uk??k&a&m&anah?uzuk??sagenak??esonihci??m&akatik?uzia&rih?wi????o&kayim?no&rih?t??tanufo??uhso????g&3zsiu--nx?71qstn--nx?l??h&03pv23--nx?13ynr--nx?22tsiu--nx?61qqle--nx??i&54urkm--nx?g&ayim!.&a&dukak?m&a&goihs?kihs??ihsustam!ihsagih??unawi??r&awago?iho??ta&bihs?rum??w&a&gano?kuruf??iat??y&imot?ukaw???e&mot?nimes??i&hsiorihs?ka&monihsi?s&awak?o???mak?r&ataw?o&muram?tan????o&az?jagat?t&asim?omamay???u&fir?k&irnasimanim?uhsakihcihs?????ihcot!.&a&g&a&h?kihsa??ust??kom?m&ay&o?usarak??unak??r&a&boihsusan?watho??iho?ukas??t&akihsin?iay??wa&konimak?zenakat??y&imonustu?oihs???e&iiju?kustomihs?nufawi??i&akihci?g&etom?ihcot?on???o&k&ihsam?kin??nas?sioruk?tab??u&bim?san?????h&c&ia!.&a&dnah?m&a!h&akat?im??yuni??ihs&ibot?ust???r&a&hat?tihs??ik?u&ihsagih?kawi???t&ihc?o&k?yot???wa&koyot?zani??yi&monihci?rak???e&inak?k&aoyot?usa??manokot?noyot??i&a&gusak?kot?sia??eot?h&asairawo?cugo?s&ahoyot?oyim???k&a&mok?zako??ihssi??motay?rogamag??n&an&ikeh?ok??ihssin??o&got?ihsin?jna?rihsnihs?suf?tes??u&bo?raho?s&oyik?takihs??yrihc?zah????ok!.&a&dusay?kadih?mayotom?r&ah&im?usuy??umakan??sot!ihsin??wa&g&atik?odoyin??k&as?o????i&esieg?hco!k??jamu?k&a!sus??usto??ma&gak?k??rahan??o&mukus?n&i?ust!ihsagih???torum?yot!o???u&koknan?zimihsasot????ugamay!.&a&m&ayukot?ihso??toyot??e&bu?subat??i&gah?kesonomihs?nukawi?rakih??nanuhs?otagan?u&ba?foh?otim?stamaduk?uy?????sanamay!.&a&dihsoyijuf?mayabat?r&ahoneu?ustakihsin??w&a&k&ayah?ijuf??suran??ohs???egusok?i&ak?h&cimakan?s&anamay?od???k&asarin?u&feuf?sto????o&k&akanamay?ihcugawakijuf??nihso?t&asimawakihci?ukoh??uhc??spla-imanim?u&b&nan?onim??fok?hsok?rust?????ka&rabi!.&a&bukust?gok?kan!ihcatih??m&a&sak?timo?wi??ihsak?ustomihs??ni?r&a&hihcu?way??u&agimusak?ihcust???t&ag&amay?eman??oihcatih??w&ag&arukas?o??os??yi&moihcatih?rom???e&bomot?dirot?not?tadomihs??i&a&k&as?ot??rao??esukihc?gahakat?h&asa?catih??k&a&rabi?saguyr??ihsani?uy??ma?rukustamat??o&dnab?giad?him?kati?rihsijuf?soj?t&asorihs?im??yihcay??u&fius?kihsu?simak????sagan!.&a&m&abo?ihsust??natawak?r&abamihs?u&mo?ustam???wijihc?yahasi??i&akias?hies?k&asagan?i??masah??neznu?o&besas?darih?t&eso?og!imaknihs????ust&igot?onihcuk?uf????zayim!.&a&biihs?guyh?k&oebon?ustorom??mihsuk?r&emihsin?uatik??ta&katik?mim??wag&atik?odak??ya??e&banakat?sakog??i&hsayabok?kaza&kat?yim??m&animawak?ot&inuk?nihs????nanihcin?o&j&ik?onokayim??n&ibe?ust??tias??urahakat????ro&moa!.&a&dawot?turust?wasim??e&hon&ihc&ah?ihs??nas?og?ukor??sario??i&anarih?ganayati?hsioruk?jehon?kasorih?makihsah?nawo?r&amodakan?omoa???o&gnihs?kkat??u&ragust?stum????ttot!.&a&r&ahawak?uotok??sa&kaw?sim???egok?irottot?nanihcin?o&ganoy?nih?tanimiakas??u&bnan?z&ay?ihc??????ukuf!.&a&deki?gurust?ma&bo?h&akat?im??yustak??sakaw??eabas?i&akas?ho?jiehie?ukuf??nezihce!imanim??ono????k&26rtl8--nx?4&3qtr5--nx?ytjd--nx??522tin--nx?797ti4--nx??l33ussp--nx?m&11tqqq--nx?41s3c--nx??n&30sql1--nx?65zqhe--nx?n7p7qrt0--nx??o&131rot--nx?7qrbk--nx?c?diakkoh!.&a&deki?gakihset?hcebihs?k&adih?u&fib?narihs???m&ayiruk?hot?ihs&orihatik?ukuf??oras?usta??r&ib&a!ka??o?uruf??ozo?u&gakihsagih?oyot???sakim?ta&gikust?mun??w&a&ga&k&an?uf??nus!imak???k&aru?i&h&asa?sagih??kat?mak??omihs?um??zimawi??ine?oyk??yot??e&a&mustam?nan??b&a&kihs?yak??o&noroh?to???ian?k&ihsam?ufoto??nakami?ppoko!ihsin??sotihc?tad!okah??uonikat??i&a&bib?mokamot?n&a&k&kaw?oroh??wi??eomak?ihsatu?okik?usta&moruk?sakan????eib?h&c&ioy?u&bmek?irihs???s&ase?ekka?oknar?uesom???jufirihsir?k&amamihs?i&at?n???m&atik?otoyot??oa&kihs?rihs??r&a&hs?kihsi?mot??ihs&aba?ir??otarib???n&a&hctuk?rorum?se?tokahs??uber??o&kayot?m&ire?ukay??naruf!ima&k?nim???orih?r&ih&ibo?suk??o&bah?h&i&b?hsimak??sa??pnan?yan??umen??t&asoyik?eko?ukoh???u&bassa?kotnihs?m&assaw?uo??pp&akiin?en&ioto?nuk??ip??rato?s&akat?t&eb&e?i&a?hs!a??robon??m&e?o&m?takan???no&h?tamah??o&mik?s?t??u&kir?ppihc?st???onihsnihs?ufuras??uaru??yru!koh??zimihs!ok?????g!oyh!.&a&bmat?dnas?gusak?k&at?o&oyot?y??uzarakat??m&ayasas?irah??wa&g&ani?okak??k&i&hci?mak??oy???yi&hsa?monihsin???i&asak?hs&aka?i&at?nawak???j&awa!imanim??emih??k&a&goa?s&agama?ukuf??wihsin??i&hsog?m???mati?oia?rogimak??n&annas?esnonihs??o&gasa!kat??ka?n&ikat?o?ustat??rihsay?sihs?tomus?yas??u&bay?gnihs?????nagan!.&a&bukah?d&a&w?yim??e&ki?u??ii??k&a&s&ay?uki??zus??ihsoo?ousay??m&ay&akat?ii??i&hsukufosik?jii??ukihc??n&i!hsetat??uzii??r&ah?ugot??saim?t&agamay?oyim??w&a&g&a&kan?n??o??kustam?ziurak??onim!imanim??u&koo?s!omihs????ya&ko?rih???e&akas?nagamok?subo??i&gakat?h&asa?c&a!mo!nanihs???uonamay??sukagot??k&a&kas?mimanim?to??ia&atik?imanim??oa?uzihcom??m&akawak?ijuf?o!t???r&ato?ijoihs?omakat???n&ana?esnoawazon??o&hukas?n&a&gan?kan??i&hc?muza??ustat??romok?si&gan?k??tomustam??u&k&as?ohukihc??stamega????to&mamuk!.&a&gamay?rahihsin?sukama!imak??tamanim??enufim?i&hcukik?k&ihsam?u??nugo!imanim??romakat??o&ara?rihsustay?sa?t&amay?om&amuk?us??u!koyg???yohc??u&sagan?zo????yk!.&a&bmatoyk?k&ies?oemak?uzaw??mayi&h&cukuf?sagih??muk??nihsamay?rawatiju?t&away?ik???e&ba&nat!oyk??ya??di?ni??i&ju?kazamayo?manim??natnan?o&gnatoyk?kum?mak?rihsamayimanim?y&gakan?ka&koagan?s??oj???u&ruziam?z&ayim?ik??????wtc1--nx?ykot!.&a&d&i&hcam?mus??oyihc??k&atim?ihsustak??m&a&t!uko??yarumihsa&gih?sum???i&hs&agoa?ika?o!t??uzuok??ren???r&a&honih?wasago??iadok?umah??ssuf?t&ik?o??wa&g&anihs?ode??k&ara?ihcat???y&agates?ubihs???e&amok?donih?m&o?urukihsagih??soyik??i&enagok?gani?h&ca&da?tinuk??sabati??j&nubukok?oihcah??manigus??o&huzim?jihcah?n&akan?ih!sasum??urika??rugem?t&a&mayihsagih?nim??iat?ok??uhc?yknub??u&fohc?hcuf?kujnihs?????r&2xro6--nx?g?o??s&9nvfe--nx?xvp4--nx??t&netnocresu,opsgolb,?u&4rvp8--nx?fig!.&a&d&eki?ih??kimot?m&ayakat?ihsah??ne?raha&gi&kes?makak??sak??taga&may?tik??wa&g&ibi?ustakan??karihs!ihsagih????e&katim?uawak??i&gohakas?hc&apna?uonaw??k&ago?es?ot??m&anuzim?ijat??nak?urat??nanig?o&dog?jug?makonim?nim?roy?sihcih??u&fig?s&otom?t&amasak?oay???????x5ytlk--nx?yu6d27srjd--nx?z72thr--nx?井福?京東?分大?取鳥?口山?城&宮?茨??媛愛?山&富?岡?歌和??岡&福?静??島&児鹿?広?徳?福??崎&宮?長??川&奈神?石?香??庫兵?形山?手岩?木栃?本熊?根島?梨山?森青?潟新?玉埼?田秋?知&愛?高??縄沖?良奈?葉千?賀&佐?滋??道海北?都京?重三?野長?阜岐?阪大?馬群???k!.&art?gro?moc?per?ude?vog???l&eh?l??m!ac?j??nd?o&g?h&pih?s!.ysrab,??lnud?oc?t!.&lldtn,snd-won,???pa!.&arusah,enilnigol,nur:.a,,t&ibelet,xenw,?yfilten,??ra&a?hs??u&ekam?llag?org!.esruocsid,cts?kouk?nayalo???vsr?xece4ibgm--nx??q&a!3a9y--nx??g?i!.&gro?lim?moc?ten?ude?vog???m?se??r&a!.&acisum?bog?gro?lim?moc!.topsgolb,?rut?t&en?ni??ude?vog??4d5a4prebgm--nx?b?c?eydoog?los?t&at?s!uen???ugaj??b!.&21g?a&b&a&coros?iuc??itiruc??cnogoas?dicerapa?gniram?i&naiog?ramatnas??n&erom?irdnol??op?p&acam?irolf?ma&j?s???rief?tsivaob??b!aj?mi?sb??c&ba?er?js?sp?t!e???d&em?mb?n&f?i??rt??e&dnarganipmac?ficer?ht?llivnioj?rdnaotnas??f&dj?ed?gg?ni??g&el!.&a&b,m,p,?bp,c&a,s,?e&c,p,s,?fd,gm,ip,jr,la,ma,nr,o&g,r,t,?p&a,s,?r&p,r,?s&e,m,r,?tm,??l&s?z??n&c?e?o??ol&b?f?v??pp?ro??hvp?i&du?kiw?nana?oretin?r&c?eurab??sp?te?xat??l&at&an?rof??el?im?sq??m&a?da?e&gatnoc?leb??f?ic?oc!.topsgolb,??nce?o&ariebir?c&e?narboir?saso??d&o?ranreboas??et?i&b?dar?ecam?r??rp?t&a?erpoir???p&m!e?t??ooc?se??qra?r&af?ga?o&davlas?j??tn?ut??s&a&ixac?mlap?nipmac??u&anam?j?m???t&am?e&n?v??nc?o&f?n??ra?sf??u&caug9?de?ja?rg??v&da?og!.&a&b?m?p??bp?c&a?s??e&c?p?s??fd?gm?ip?jr?la?ma?nr?o&g?r?t??p&a?s??r&p?r??s&e?m?r??tm???rs?t??xiv?z&hb?ls?of????c!.&as?ca?de?if?o&c?g??ro???e&bew?ccos?dnik?e&b?n&igne?oip??rac??gni&arg?rheob??h&cor?sok?t&aew?orb???itnorf?k&col?o&p?rb???l&aed?ffeahcs??mal?nes?pinuj?t&a&eht?rebsnegömrev??law?nec?s&acnal?nom?ubkcolb??upmoc??v&o&c&sid?tfiws??rdnal??resbo??wulksretlow?ywal?zifp??f!.&aterg?bew-no,drp?e&c&itsuj-reissiuh?narf-ne-setsitned-sneigrurihc,?rianiretev??i&cc?rgabmahc??m&o&c?n??t??n&eicamrahp?icedem??ossa?s&e&lbatpmoc-strepxe?riaton?tsitned-sneigrurihc?uova??o&-x&bf,obeerf,?x&bf,obeerf,???t&acova?o&or-ne,psgolb,?r&epxe-ertemoeg?op!orea????vuog??avc7ylqbgm--nx?s??g!.&gro?m&oc?yn,?t&en?opsgolb,?ude?vog???h!.&e&erf,man??mo&c?rf??topsgolb,zi??ur??i!.&a&61f4a3abgm--nx?rf4a3abgm--nx??ca?di?gro?hcs?oc?ten?vog?نار&يا?یا???a&h?per??ew?lf??k!.&c&a?s??e&n?p?r??gk?iggnoeyg?kub&gn&oeyg?uhc??noej??l&im?uoes??man&gn&oeyg?uhc??noej??n&as&lu?ub??o&e&hcni?jead??wgnag???o&c?g??ro?s&e?h?m??topsgolb,u&gead?j&ej?gnawg????cilf??l!.&gro?moc?ten?ude?vog???m!.&topsgolb,vog???n!.&gro?moc?ofni?ten?ude?vog?zib???o&cs?htua?odtnorf?t&c&a?od??laer???p!.&alsi?ca?eman?forp?gro?moc?o&fni?rp??t&en?se??ude?vog?zib???s?t!.&21k?bew?cn!.vog??eman?gro?kst?l&e&b?t??im?op??moc!.topsgolb,?neg?ofni?pek?rd?sbb?ten?ude?v&a?og?t??zib??f?m??ubad?vd??s&8sqif--nx?9zqif--nx?a!.vog?birappnb?gev?lliv?mtsirhc?s??b!.&ew,gro?moc?ten?ude?vog??c?oj?s?u??c&i&hparg?p?t&sigolyrrek?ylana???od??d&a?d?l?n&iwriaf?omaid??oogemoh?rac??e!.&bog?gro?mo&c!.topsgolb,?n??ude??civres!.enilnigol,?d&d2bgm--nx?oc??h&ctaw?guh??i&lppus?rtsudni?treporp!yrrek???jaiv?l&aw?cycrotom?etoh?gnis?pats??m&ag?oh?reh??nut?ohs?picer?r&it?ut&cip!.7331,?nev???s!i&rpretne?urc??ruoc??taicossa?vig??g!nidloh??h5c822qif--nx?i!.&ekacpuc,gro?moc?t&en?ni?opsgolb,?ude?vog??a09--nx?nnet?rap?targ??k&c&or!.&ecapsbew,snddym,ytic-amil,??us??hxda08--nx?row??l!.&c&a?s??gro?o&c?fni??ten?ude?vog?zib??a&ed?tner??e&ssurb?toh!yrrek???lahsram?m?oot??m!.&bal,gro?moc?ten?ude?vog??b?etsys!.tniopthgink,?ialc??n&a&f?gorf?ol??egassap?i&a&grab?mod??giro??o&it&acav?cudorp?ulos??puoc???o&dnoc?geuj?leuv?ppaz?t&ohp?ua???p!.&ces?gro?moc?olp?ten?ude?vog??i&hsralohcs?lihp?t??u??r!.&au,ca?gro?mon,ni?oc?topsgolb,ude?vog?xo,?a&c?p?tiug??c?e&dliub?erac?gor?levart?mraf?n&niw?trap??wolf??ot&cartnoc?omatat??pj?uot??s!.&gro?moc?ten?ude?vog?zib??alg?e&n&isub!.oc,?tif??rp!xe!nacirema???xnal??iws??t&a&e&b?ytic??ob??ek&cit?ram??fig?h&cay?gilf??n&atnuocca?e&mt&rapa?sevni??ve!.oc,???rap??u!.&a&c!.&21k?bil?cc???g!.&21k?bil?cc???i!.&21k?bil?cc???l!.&21k?bil?cc???m!.&21k!.&hcorap?rthc?tvp???bil?cc???p!.&21k?bil?cc???si?v!.&21k?bil?cc???w!.&21k?bil?cc????c&d!.&21k?bil?cc???n!.&21k?bil?cc???s!.&21k?bil?cc????d&ef?i!.&21k?bil?cc???m!.&21k?bil?cc???n!.&bil?cc???s!.&bil?cc???urd,?e&d!.&21k?bil,cc???las-4-&dnal,ffuts,?m!.&21k?bil?cc???n!.&21k?bil?cc????h&n!.&21k?bil?cc???o!.&21k?bil?cc????i&h!.&bil?cc???m!.&21k?bil?c&c?et??goc?n&eg?otae??robra-nna?sum?tsd?wanethsaw???nd?r!.&bil?cc???v!.&21k?bil?cc???w!.&21k?bil?cc????jn!.&21k?bil?cc???k&a!.&21k?bil?cc???o!.&21k?bil?cc????l&a!.&21k?bil?cc???f!.&21k?bil?cc???i!.&21k?bil?cc????mn!.&21k?bil?cc???n&afflog,i!.&21k?bil?cc???m!.&21k?bil?cc???sn?t!.&21k?bil?cc????o&c!.&21k?bil?cc???m!.&21k?bil?cc???ttniop,?p&ion,rettalp,?r&a!.&21k?bil?cc???o!.&21k?bil?cc???p!.&21k?bil?cc????s&a!.&21k?bil?cc???dik?k!.&21k?bil?cc???m!.&21k?bil?cc???nd&deerf,uolc,??t&c!.&21k?bil?cc???m!.&21k?bil?cc???u!.&21k?bil?cc???v!.&21k?bil?cc????ug!.&21k?bil?cc???v&n!.&21k?bil?cc???w!.cc???x&ohparg,t!.&21k?bil?cc????y&b-si,k!.&21k?bil?cc???n!.&21k?bil?cc???w!.&21k?bil?cc????za!.&21k?bil?cc????ah!uab??bria?col?e!.ytrap.resu,?ineserf?lp?xe&l?n???vt?w!.&66duolc,gro?moc?s&ndnyd,tepym,?ten?ude?vog??a?e&iver?n??odniw??y&alcrab?cam?ot???t&0srzc--nx?a!.&amil4,ca!.hts??gni&liamerutuf,tsoherutuf,?o&c!.topsgolb,?fni,?ph21,ro?v&g?irp,?xi2,ytic-amil,zib,?c?e!s??hc?if?l!asite??mami?rcomed??b!.&gro?moc?ten?ude?vog??b?gl??c&atnoc?e&les?rid!.lenaptsaf,txen????dimhcs?e!.&eman?gro?moc?ofni?ten?ude?vog?zib??b?em?grat?id?k&circ?ram??n!.&5inu,6vnyd,7&7ndc.r,erauqs,?a&l&-morf,moob,?minifed,remacytirucesym,tadsyawla,z,?b&boi,g,lyltsaf:.pam,,?c&inagro-gnitae,paidemym,?d&ecalpb,nab-eht-ni,uolc&meaeboda,xednay:.e&garots,tisbew,?,??e&cnarusnihtlaehezitavirp,ht-no-eciffo,l&acsnoom,ibom-eruza,?m&ecnuob,ohtanyd,tcerider,?nozdop,rehurht,s,tis-repparcs,zamkcar,?f&aeletis,crs.&cos,resu,?ehc-a-si,?g&ni&reesnes,tsohnnylf,?olbevres,?k&eeg-a&-si,si,?u,?l&acolottad,iamwt,s&d-ni,s-77ndc,??m&ac&asac,ih,?urofniem,?n&a&f&agp,lhn,?ibed,?dcduabkcalb,i,pv-ni,?o&c-morf,jodsnd,rp-ytinummoc,ttadym,?p&i&-&etsef,on,?emoh,fles,nwo,?j,mac-dnab-ta,o&-oidar-mah,hbew,?paduolc,tfe&moh,vres,?usnd,?r&e&tsulcyduolc,vres-xnk,?vdslennahc:.u,,?s&a&ila&nyd,snd,?nymsd,?bbevres,dylimaf,e&suohsyub,t&isbeweruza,ys,??kekokohcs,n&d&-won,d,golb,npv,?oitcnufduolc,?s&a-skcik,ecca&-citats,duolc,???t&ceffeym,e&nretnifodne,smem,?farcenimevres,i-&ekorb,s&eod,lles,teg,??n&essidym,orfduolc,?r0p3l3t,s&ixetnod,ohgnik,??u&h,nyd,r,?x&inuemoh,spym,unilemoh,?y&awetag-llawerif,ltsaf.&dorp.&a,labolg,?lss.&a,b,labolg,?pam,slteerf,?n&-morf,ofipi,?srab,tieduolc,?z&a-morf,tirfym,???p?tcip?v??f&ig?o&l?sorcim???g!.&bog?dni?gro?lim?mo&c?n,?ten?ude???h!.&dem?gro?l&er?op??m&oc?rif??o&fni?rp?s&rep?sa???po&hs?oc??t&en?luda?ra??ude?vuog???i!.&a&2n-loritds--nx?7e-etsoaellav--nx?8&c-aneseclrof--nx?i-lrofanesec--nx??at?b?c!cul??dv?i&blo&-oipmet?oipmet??cserb?drabmol?g&gof?urep??l&gup?i&cis?me&-oigger?oigger???uig&-&aizenev&-iluirf?iluirf??ev&-iluirf?iluirf??v&-iluirf?iluirf???aizenev&-iluirf?iluirf??ev&-iluirf?iluirf??v&-iluirf?iluirf????n&a&brev?cul?pmac?tac??idras?obrac&-saiselgi?saiselgi??resi??otsip?r&b&alac!-oigger?oigger??mu??dna&-&attelrab-inart?inart-attelrab??attelrabinart?inartattelrab?ssela??epmi?ugil??tnelav&-obiv?obiv??vap?z&e&nev?ps&-al?al???irog???l&iuqa!l??leib??m&or?rap??n!acsot?e&dom?is?sec&-&ilrof?ìlrof??ilrof?ìlrof???g&amor&-ailime?ailime??edras?olob??i&ssem?tal??ne!var??o&cna?merc?rev?vas???oneg?p?r!a&csep?rr&ac&-assam?assam??ef??von??etam?tsailgo!-lled?lled???s!ip?sam&-ararrac?ararrac??u&caris?gar???t!a&cilisab?recam??resac?soa!-&d&-&ellav?lav??ellav?lav??ellav??d&-&ellav?lav??ellav?lav??ellav??te&lrab&-&airdna-inart?inart-airdna??airdnainart?inartairdna??ssinatlac???udap?v!o&dap?neg?tnam???zn&airb&-a&lled-e-aznom?znom??a&lledeaznom?znom??eaznom??e&c&aip?iv??soc?top??om???b&-&23,46,61,?3c-lorit-ds-onitnert--nx?be-etsoa&-ellav--nx?dellav--nx??c!f-anesec-lrof--nx?m-lrof-anesec--nx??he-etsoa-d-ellav--nx?m!u??o2-loritds-nezob--nx?sn-loritds&-nasl&ab--nx?ub--nx??nitnert--nx??v!6-lorit-dsnitnert--nx?7-loritds&-nitnert--nx?onitnert--nx???z&r-lorit-ds&-nitnert--nx?onitnert--nx??s-loritds-onitnert--nx???c&f?is?l?m?p?r?v??d&p?u!olcnys,??e&c!cel?inev?nerolf??f?g!ida&-&a&-onitnert?onitnert??otla!-onitnert?onitnert???a&-onitnert?onitnert??otla!-on&azlob?itnert??onitnert????hcram?l?m!or??n&idu?o&n&edrop?isorf??torc???p?r?s&erav?ilom??t!nomeip?s&eirt?oa!-&d-e&ellav?éllav??e&ellav?éllav???de&ellav?éllav??e&ellav?éllav?????v?znerif??g&a?b?f?il?o?p?r?up?vf??hc?i&b?c?dol?f?l!lecrev?opan?rof&-anesec?anesec???m?n&a&part?rt&-attelrab-airdna?attelrabairdna???imir?ret??p?r!a&b?ilgac?ssas???s!idnirb??t&ei&hc?r??sa??v??l&a!c??b?c?o&m?rit&-&d&eus&-&nitnert?onitnert??nitnert?onitnert??us&-&nitnert?onitnert??nitnert?onitnert??üs&-&nitnert?onitnert??nitnert?onitnert???s&-onitnert?onitnert???d&eus!-&n&asl&ab?ub??ezob?itnert??onitnert??nitnert?onitnert??us&-&n&asl&ab?ub??ezob?itnert??onitnert??nitnert?onitnert??üs!-&n&asl&ab?ub??ezob?itnert??onitnert??nitnert?onitnert???s&-onitnert?onitnert?????m&ac?f?i?ol?r??n&a!lim?sl&ab?ub???b?c?e!v?zob??irut?m!p??p?r?t??o&a!v??b!retiv??c!cel??enuc?g!ivor??i&dem&-onadipmac?onadipmac??pmet&-aiblo?aiblo??rdnos?zal??l?m!a&greb?ret??oc?re&f?lap???n!a&dipmac&-oidem?oidem??lim?tsiro?zlob??ecip&-ilocsa?ilocsa??i&bru&-orasep?orasep??lleva?rot?tnert??r&elas?ovil??ulleb??p?r!a&sep&-onibru?onibru??znatac??oun??s!ivert?sabopmac??t!arp?e&nev?ssorg??n&arat?e&girga?rt?veneb????zz&era?urba???p&a?s?t??qa?r&a!m?s??b!a??c?f?g?k?me?o?p?s?t?v??s&a&b?iselgi&-ainobrac?ainobrac???b?c?elpan?i?m?ot?s?t?v??t&a?b?c?l?m?nomdeip?o!psgolb,?p?v??u&de?l?n?p??v&a?og?p?s?t?v??y&drabmol?ellav&-atsoa?atsoa??licis?nacsut??z&al?b?c?p??ìlrof&-anesec?anesec???derc?er?f!.sulptp,?m!r??utni??je3a3abgm--nx?kh?l!.&myn,topsgolb,vog??uda??m!.&gro?moc!.topsgolb,?ten?ude???n&a&morockivdnas?ruatser?tnuocca??e&g?m&eganam!.retuor,?piuqe??r??i!.ue?m?opdlog??opud?uocsid??o&b?cs!.vog,?d?g?h?j?oferab?p&edemoh?s???p!.&emon?gro?lbup?m&oc?yn,?t&en?ni?opsgolb,?ude?vog???r&a!m&law?s???epxe?op&er?pus!.ysrab,?s???s!.&adaxiabme?e&motoas?picnirp?rots??gro?lim?mo&c?n,?o&c?dalusnoc?hon,?ten?ude?vog??a&cmoc?f??e&b?padub?r?uq??i!rolf?tned??o&h!.&duolcp,etiseerf,flah,s&pvtsaf,seccaduolc,?tsafym,??p!sua???urt??t!.&eman?gro?ibom?levart?m&oc?uesum??o&c?fni?r&ea?p???pooc?sboj?t&en?ni??ude?vog?zib??ayh?n?o!bba?irram???uognah?xen?y?ztej??u&2&5te9--nx?yssp--nx??a!.&a&s?w??civ?d&i?lq??fnoc?gro?moc!.topsgolb,?nsa?ofni?sat?t&ca?en?n??ude!.&a&s?w??ci&lohtac?v??dlq?sat!.noitacude??t&ca?n??wsn!.sloohcs????vog!.&a&s?w??civ?dlq?sat???wsn?zo??ti??c!.&fni?gro?moc?ten?ude?vog??i??d&e!.tir.segap-tig,?iab??e!.&dcym,enozgniebllew,noitatsksid,snd&ps,uolc,?ysrab,??g!.&bew?gro?m&aug?oc??ofni?ten?ude?vog???h!.&0002?a&citore?idem?kitore??edszot?gro?ilus?letoh?m&alker?lif?t?urof??naltagni?o&c?ediv?fni?levynok?nisac??pohs?rarga?s&a&kal?zatu??emag?wen??t&lob?opsgolb,rops??virp?xe&s?zs??ytic?zsagoj??os?sut??l!.&myn,topsgolb,??m!.&ca?gro?moc?oc?ro?ten?vog???n!.&duolcesirpretne,eni&esrem,m,?mon,tenkcahs,?em!.ysrab,??o&ggnaw?y!c???r!.&a&i&kymlak,rikhsab,vodrom,?yegyda,?bps,ca,eniram,g&bc,ro,?ianatsuk,k&ihclan,s&m,rogitayp,??li&amdlc.bh,m,?moc,natsegad,onijym,pp,ri&b,midalv,?s&ar,itym,?t&en,ni,opsgolb,set,?ude,vo&g,n,?ynzorg,zakvakidalv,?myc?p?ug??s!.&a&d&golov,nagarak,?gulak,i&groeg,kymlak,lerak,nemra,rikhsab,ssakahk,vodrom,zahkba,?lut,rahkub,vut,yegyda,znep,?bps,da&baghsa,rgonilest,?gunel,i&anatsuk,hcos,ovan,ttailgot,?k&alhsygnam,ihclan,s&legnahkra,m,n&a&mrum,yrb,?i&buytka,nbo,??tiort,vorkop,??l&ocarak,ybmaj,?myn,na&gruk,jiabreza,ts&egad,hkazak-&htron,tsae,???ovonavi,r&adonsark,imidalv,?t&enxe,nek&hsat,mihc,??vo&hsalab,n,?ynzorg,z&akvakidalv,emret,??t&amok?i&juf?masih????v!.&gro?moc?ten?ude???ykuyr??v&b?c!.topsgolb,?ed!.&enilnigol,ppa-rettalp,srekrow,vr&esi,uc,???ih?l!.&di?fnoc?gro?lim?mo&c?n,?nsa?ten?ude?vog???m!.&eman?gro?lim?m&oc?uesum??o&fni?r&ea?p???pooc?t&en?ni??ude?vog?zib???o&g?m??rt?s!.&bog?der?gro?moc?ude???t!.&bew-eht-no,naht-&esrow,retteb,?sndnyd,?d?gh?i?won??uqhv--nx??w&a!.moc?hs?l??b!.&gro?oc???c!.&gro?moc?ten?ude??cp??e&iver!.oby,?n?s??g?k!.&bme?dni?gro?moc?ten?ude?vog???m!.&ca?gro?m&oc?uesum??oc?pooc?t&en?ni??ude?vog?zib??b??o&csom?h!s??n?w??p!.&344x,de?en?mon,o&c?g??ro?snduolc,ualeb???r!.&ca?gro?lim?oc?pooc?ten?vog??n??t!.&a46oa0fz--nx?b&82wrzc--nx?ulc??emag?gro?l&im?ru,?m&oc!.reliamym,?yn,?t&en?opsgolb,?ude?v&di?og?ta0cu--nx??zibe?業商?織組?路網???z!.&ca?gro?lim?oc?vog????x&a!t??c!.&hta,ofni,vog???e&d&ef?nay??ma!nab??rof?s??ilften?jt?m!.&bog?gro?m&oc?yn,?t&en?opsgolb,?ude??g?ma2ibgy--nx??o&b!x??f?rex!ijuf???rbgn--nx?s!.&myn,vog???x&am&jt?kt??x???y&4punu--nx?7rr03--nx?a&d!i&loh?rfkcalb??ot??g?lp?p!ila??rot?ssin?wdaorb??b!.&fo?lim?m&oc!.topsgolb,?yn,?vog??ab?gur??c!.&ca?dtl?eman?gro?m&oc!.topsgolb,?t??orp?s&egolke?serp??t&en?nemailrap??vog?zib??amrahp?nega??d&dadog?uts??e&kcoh?ltneb?n&dys?om?rotta??snikcm??g!.&gro?m&oc?yn,?oc?ten?ude?vog??olonhcet!.oc,?rene??hpargotohp?id?k!.&gro?moc?ten?ude?vog??s??l!.&clp?d&em?i??gro?hcs?moc?ten?ude?vog??f?imaf!nacirema??l&a?il??ppus??m!.&eman?gro?lim?moc?t&en?opsgolb,?ude?vog??edaca!.laiciffo,?ra??n&a&ffit?pmoc!ylimafa???os??o&j?s??p!.&gro?lim?moc?pooc?ten?ude?vog???r&e&corg?grus?llag?viled??lewej?otcerid?tnuoc?uxul??s!.&gro?lim?moc?ten?ude?vog??pil??t&efas?i&c!.gn,?ledif?n&ifx?ummoc!.&bdnevar,murofym,???r&ahc?uces??srevinu??laer?r&ap!.oby,?eporp??uaeb??u!.&bug?gro?lim?mo&c!.topsgolb,?n,?ten?ude??b!tseb???van!dlo??xes??z&a!.&eman?gro?lim?moc?o&fni?rp??pp?t&en?ni??ude?vog?zib???b!.&az,gro?m&o&c?n,?yn,?ten?ude?vog???c!.&4e,inum.duolc.&rsu,tlf,?m&laer,urtnecatem.&duolc,motsuc,??oc,topsgolb,??d!.&gro?lop?moc?ossa?t&en?ra??ude?vog???ib!.&duolcsd,e&ht-rof,mos-rof,rom-rof,?lpb,nafamm,p&i&-on,fles,?ohbew,tfym,?retteb-rof,snd&nyd,uolc,?xro,?g??k!.&gro?lim?m&oc?yn,?ten?ude?vog???m!.&ca?gro?lim?oc?ten?ude?v&da?og????n!.&asq-irom--nx?ca?gro?htlaeh?i&r&c?o&am?ām???wi!k???keeg?l&im?oohcs??myn,neg?oc!.topsgolb,?t&en?nemailrap?vog???a!niflla???rawhcs?s!.&ca?gro?oc???t!.&c&a?s??e&m?n??ibom?l&etoh?im??o&c?fni?g??ro?vt???u!.&gro?moc?oc?ten??rwon??yx!.&etisgolb,gnitfarc,otpaz,ppahf,??zub??λε?υε?авксом?брс!.&гро?до?ка?р&бо?п!у?????г&б?ро??дкм?зақ?итед?килотак?леб?мок?н&йално?ом??рку?сур?тйас?фр?юе?յահ?םוק?اي&روس?سيلم?ناتيروم??بر&ع?غملا??ة&كبش?ي&دوعسلا?روس??یدوعسلا??ت&ا&راما?لاصتا??را&ب?ڀ?ھب???ر&ئازجلا?ازاب?صم?طق??سنوت?عقوم?قارع?ك&تيب?يلوثاك??موك?ن&ا&تس&كاپ?کاپ??دوس?ر&يا?یا??مع?يلعلا??درالا?ميلا?ي&رحبلا?طسلف???ه&ارمه?يدوعسلا??وكمارا?يبظوبا?ۃیدوعسلا?टेन?त&राभ?ोराभ??नठगंस?मॉक?्मतराभ?ত&রাভ?ৰাভ??ালংাব?ਤਰਾਭ?તરાભ?ତରାଭ?ாயித்நஇ?ைக்ஙலஇ?்ரூப்பக்ஙிச?్తరాభ?ತರಾಭ?ംതരാഭ?ාකංල?มอค?ยทไ!.&จิกรุธ?ต็นเ?ร&ก์คงอ?าหท??ลาบฐัร?าษกึศ???ວາລ?ეგ?なんみ?アトス?トンイポ?ドウラク?ムコ?ル&グーグ?ーセ??ン&ゾマア?ョシッァフ??业企?东广?乐娱?亚基诺?你爱我?信中?务政?动移?博微?卦八?厅餐?司公?品食?善慈?团集?国中?國中?址网?坡加新?城商?宝珠?尚时?山佛?店&商?网?酒大里嘉??府政?康健?息信?戏游?拉里格香?拿大?教主天?机手?构机!织组??标商?歌谷?浦利飞?港香!.&人個?司公?府政?絡網?織組?育教???湾台?灣&台?臺??物购?界世?益公?看点?科盈訊電?站网?籍書?线在?络网?网文中?聘招?表手?販通?车汽众大?逊马亚?通联?里嘉?锡马淡?門澳?门澳?闻新?電家?국한?넷닷?성삼?컴닷??';


/** @const {string} */
publicsuffixpatterns.UNDER = '&ac.vedwa,d&b?uolc.&etiso&isnes,tnegam,?rehcnar-no,scitats,??e&b.lrusnart,d.ecapsrebu,noz.notirt,t&atse.etupmoc,is.hsmroftalp,?y??gp?h&k?s.mroftalp,trae.sppad:.zzb,,?k&c?f?nil.bewd,rowten.secla,u.hcs??ln.lrusnart,m&j?m?oc.&duolcmeaeboda.ved,ico-remotsuc:.&ico,pco,sco,?,mme0,s&tnemelepiuq,wanozama.&1-etupmoc,ble,etupmoc,??t&neyoj.snc,opsppa.r,???nc.moc.swanozama.&ble,etupmoc,?o&c.pato,i.&elacsnoom,oir-no,reniatnoceruza,s&3k-no,olots,?xcq.sys,y5s,??p&j.&a&mahokoy?yogan??ebok?i&adnes?kasawak??oroppas?uhsuykatik??n??r&b.mon?e??sw.rosivda,t&a.&ofnistro.&nednuk,xe,?smcerutuf:.&ni,xe,?,?en.cimonotpyrc,?u&e.lrusnart,r.onijym.&gni&dnal,tsoh,?murtceps,spv,??ved.&erahbew,gts,lcl,?zyx.tibelet,?';


/** @const {string} */
publicsuffixpatterns.EXCLUDED = '&kc.www?pj.&a&mahokoy.ytic?yogan.ytic??ebok.ytic?i&adnes.ytic?kasawak.ytic??oroppas.ytic?uhsuykatik.ytic???';

});  // goog.scope

//third_party/javascript/closure/debug/error.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Provides a base class for custom Error objects such that the
 * stack is correctly maintained.
 *
 * You should never need to throw goog.debug.Error(msg) directly, Error(msg) is
 * sufficient.
 */

goog.provide('goog.debug.Error');



/**
 * Base class for custom error objects.
 * @param {*=} opt_msg The message associated with the error.
 * @constructor
 * @extends {Error}
 */
goog.debug.Error = function(opt_msg) {

  // Attempt to ensure there is a stack trace.
  if (Error.captureStackTrace) {
    Error.captureStackTrace(this, goog.debug.Error);
  } else {
    const stack = new Error().stack;
    if (stack) {
      /** @override */
      this.stack = stack;
    }
  }

  if (opt_msg) {
    /** @override */
    this.message = String(opt_msg);
  }

  /**
   * Whether to report this error to the server. Setting this to false will
   * cause the error reporter to not report the error back to the server,
   * which can be useful if the client knows that the error has already been
   * logged on the server.
   * @type {boolean}
   */
  this.reportErrorToServer = true;
};
goog.inherits(goog.debug.Error, Error);


/** @override */
goog.debug.Error.prototype.name = 'CustomError';

//third_party/javascript/closure/dom/nodetype.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Definition of goog.dom.NodeType.
 */

goog.provide('goog.dom.NodeType');


/**
 * Constants for the nodeType attribute in the Node interface.
 *
 * These constants match those specified in the Node interface. These are
 * usually present on the Node object in recent browsers, but not in older
 * browsers (specifically, early IEs) and thus are given here.
 *
 * In some browsers (early IEs), these are not defined on the Node object,
 * so they are provided here.
 *
 * See http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-1950641247
 * @enum {number}
 */
goog.dom.NodeType = {
  ELEMENT: 1,
  ATTRIBUTE: 2,
  TEXT: 3,
  CDATA_SECTION: 4,
  ENTITY_REFERENCE: 5,
  ENTITY: 6,
  PROCESSING_INSTRUCTION: 7,
  COMMENT: 8,
  DOCUMENT: 9,
  DOCUMENT_TYPE: 10,
  DOCUMENT_FRAGMENT: 11,
  NOTATION: 12
};

//third_party/javascript/closure/asserts/asserts.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Utilities to check the preconditions, postconditions and
 * invariants runtime.
 *
 * Methods in this package are given special treatment by the compiler
 * for type-inference. For example, <code>goog.asserts.assert(foo)</code>
 * will make the compiler treat <code>foo</code> as non-nullable. Similarly,
 * <code>goog.asserts.assertNumber(foo)</code> informs the compiler about the
 * type of <code>foo</code>. Where applicable, such assertions are preferable to
 * casts by jsdoc with <code>@type</code>.
 *
 * The compiler has an option to disable asserts. So code like:
 * <code>
 * var x = goog.asserts.assert(foo());
 * goog.asserts.assert(bar());
 * </code>
 * will be transformed into:
 * <code>
 * var x = foo();
 * </code>
 * The compiler will leave in foo() (because its return value is used),
 * but it will remove bar() because it assumes it does not have side-effects.
 *
 * Additionally, note the compiler will consider the type to be "tightened" for
 * all statements <em>after</em> the assertion. For example:
 * <code>
 * const /** ?Object &#ast;/ value = foo();
 * goog.asserts.assert(value);
 * // "value" is of type {!Object} at this point.
 * </code>
 */

goog.provide('goog.asserts');
goog.provide('goog.asserts.AssertionError');

goog.require('goog.debug.Error');
goog.require('goog.dom.NodeType');


/**
 * @define {boolean} Whether to strip out asserts or to leave them in.
 */
goog.asserts.ENABLE_ASSERTS =
    goog.define('goog.asserts.ENABLE_ASSERTS', goog.DEBUG);



/**
 * Error object for failed assertions.
 * @param {string} messagePattern The pattern that was used to form message.
 * @param {!Array<*>} messageArgs The items to substitute into the pattern.
 * @constructor
 * @extends {goog.debug.Error}
 * @final
 */
goog.asserts.AssertionError = function(messagePattern, messageArgs) {
  goog.debug.Error.call(this, goog.asserts.subs_(messagePattern, messageArgs));

  /**
   * The message pattern used to format the error message. Error handlers can
   * use this to uniquely identify the assertion.
   * @type {string}
   */
  this.messagePattern = messagePattern;
};
goog.inherits(goog.asserts.AssertionError, goog.debug.Error);


/** @override */
goog.asserts.AssertionError.prototype.name = 'AssertionError';


/**
 * The default error handler.
 * @param {!goog.asserts.AssertionError} e The exception to be handled.
 */
goog.asserts.DEFAULT_ERROR_HANDLER = function(e) {
  throw e;
};


/**
 * The handler responsible for throwing or logging assertion errors.
 * @private {function(!goog.asserts.AssertionError)}
 */
goog.asserts.errorHandler_ = goog.asserts.DEFAULT_ERROR_HANDLER;


/**
 * Does simple python-style string substitution.
 * subs("foo%s hot%s", "bar", "dog") becomes "foobar hotdog".
 * @param {string} pattern The string containing the pattern.
 * @param {!Array<*>} subs The items to substitute into the pattern.
 * @return {string} A copy of `str` in which each occurrence of
 *     {@code %s} has been replaced an argument from `var_args`.
 * @private
 */
goog.asserts.subs_ = function(pattern, subs) {
  var splitParts = pattern.split('%s');
  var returnString = '';

  // Replace up to the last split part. We are inserting in the
  // positions between split parts.
  var subLast = splitParts.length - 1;
  for (var i = 0; i < subLast; i++) {
    // keep unsupplied as '%s'
    var sub = (i < subs.length) ? subs[i] : '%s';
    returnString += splitParts[i] + sub;
  }
  return returnString + splitParts[subLast];
};


/**
 * Throws an exception with the given message and "Assertion failed" prefixed
 * onto it.
 * @param {string} defaultMessage The message to use if givenMessage is empty.
 * @param {Array<*>} defaultArgs The substitution arguments for defaultMessage.
 * @param {string|undefined} givenMessage Message supplied by the caller.
 * @param {Array<*>} givenArgs The substitution arguments for givenMessage.
 * @throws {goog.asserts.AssertionError} When the value is not a number.
 * @private
 */
goog.asserts.doAssertFailure_ = function(
    defaultMessage, defaultArgs, givenMessage, givenArgs) {
  var message = 'Assertion failed';
  if (givenMessage) {
    message += ': ' + givenMessage;
    var args = givenArgs;
  } else if (defaultMessage) {
    message += ': ' + defaultMessage;
    args = defaultArgs;
  }
  // The '' + works around an Opera 10 bug in the unit tests. Without it,
  // a stack trace is added to var message above. With this, a stack trace is
  // not added until this line (it causes the extra garbage to be added after
  // the assertion message instead of in the middle of it).
  var e = new goog.asserts.AssertionError('' + message, args || []);
  goog.asserts.errorHandler_(e);
};


/**
 * Sets a custom error handler that can be used to customize the behavior of
 * assertion failures, for example by turning all assertion failures into log
 * messages.
 * @param {function(!goog.asserts.AssertionError)} errorHandler
 */
goog.asserts.setErrorHandler = function(errorHandler) {
  if (goog.asserts.ENABLE_ASSERTS) {
    goog.asserts.errorHandler_ = errorHandler;
  }
};


/**
 * Checks if the condition evaluates to true if goog.asserts.ENABLE_ASSERTS is
 * true.
 * @template T
 * @param {T} condition The condition to check.
 * @param {string=} opt_message Error message in case of failure.
 * @param {...*} var_args The items to substitute into the failure message.
 * @return {T} The value of the condition.
 * @throws {goog.asserts.AssertionError} When the condition evaluates to false.
 * @closurePrimitive {asserts.truthy}
 */
goog.asserts.assert = function(condition, opt_message, var_args) {
  if (goog.asserts.ENABLE_ASSERTS && !condition) {
    goog.asserts.doAssertFailure_(
        '', null, opt_message, Array.prototype.slice.call(arguments, 2));
  }
  return condition;
};


/**
 * Checks if `value` is `null` or `undefined` if goog.asserts.ENABLE_ASSERTS is
 * true.
 *
 * @param {T} value The value to check.
 * @param {string=} opt_message Error message in case of failure.
 * @param {...*} var_args The items to substitute into the failure message.
 * @return {R} `value` with its type narrowed to exclude `null` and `undefined`.
 *
 * @template T
 * @template R :=
 *     mapunion(T, (V) =>
 *         cond(eq(V, 'null'),
 *             none(),
 *             cond(eq(V, 'undefined'),
 *                 none(),
 *                 V)))
 *  =:
 *
 * @throws {!goog.asserts.AssertionError} When `value` is `null` or `undefined`.
 * @closurePrimitive {asserts.matchesReturn}
 */
goog.asserts.assertExists = function(value, opt_message, var_args) {
  if (goog.asserts.ENABLE_ASSERTS && value == null) {
    goog.asserts.doAssertFailure_(
        'Expected to exist: %s.', [value], opt_message,
        Array.prototype.slice.call(arguments, 2));
  }
  return value;
};


/**
 * Fails if goog.asserts.ENABLE_ASSERTS is true. This function is useful in case
 * when we want to add a check in the unreachable area like switch-case
 * statement:
 *
 * <pre>
 *  switch(type) {
 *    case FOO: doSomething(); break;
 *    case BAR: doSomethingElse(); break;
 *    default: goog.asserts.fail('Unrecognized type: ' + type);
 *      // We have only 2 types - "default:" section is unreachable code.
 *  }
 * </pre>
 *
 * @param {string=} opt_message Error message in case of failure.
 * @param {...*} var_args The items to substitute into the failure message.
 * @throws {goog.asserts.AssertionError} Failure.
 * @closurePrimitive {asserts.fail}
 */
goog.asserts.fail = function(opt_message, var_args) {
  if (goog.asserts.ENABLE_ASSERTS) {
    goog.asserts.errorHandler_(new goog.asserts.AssertionError(
        'Failure' + (opt_message ? ': ' + opt_message : ''),
        Array.prototype.slice.call(arguments, 1)));
  }
};


/**
 * Checks if the value is a number if goog.asserts.ENABLE_ASSERTS is true.
 * @param {*} value The value to check.
 * @param {string=} opt_message Error message in case of failure.
 * @param {...*} var_args The items to substitute into the failure message.
 * @return {number} The value, guaranteed to be a number when asserts enabled.
 * @throws {goog.asserts.AssertionError} When the value is not a number.
 * @closurePrimitive {asserts.matchesReturn}
 */
goog.asserts.assertNumber = function(value, opt_message, var_args) {
  if (goog.asserts.ENABLE_ASSERTS && typeof value !== 'number') {
    goog.asserts.doAssertFailure_(
        'Expected number but got %s: %s.', [goog.typeOf(value), value],
        opt_message, Array.prototype.slice.call(arguments, 2));
  }
  return /** @type {number} */ (value);
};


/**
 * Checks if the value is a string if goog.asserts.ENABLE_ASSERTS is true.
 * @param {*} value The value to check.
 * @param {string=} opt_message Error message in case of failure.
 * @param {...*} var_args The items to substitute into the failure message.
 * @return {string} The value, guaranteed to be a string when asserts enabled.
 * @throws {goog.asserts.AssertionError} When the value is not a string.
 * @closurePrimitive {asserts.matchesReturn}
 */
goog.asserts.assertString = function(value, opt_message, var_args) {
  if (goog.asserts.ENABLE_ASSERTS && typeof value !== 'string') {
    goog.asserts.doAssertFailure_(
        'Expected string but got %s: %s.', [goog.typeOf(value), value],
        opt_message, Array.prototype.slice.call(arguments, 2));
  }
  return /** @type {string} */ (value);
};


/**
 * Checks if the value is a function if goog.asserts.ENABLE_ASSERTS is true.
 * @param {*} value The value to check.
 * @param {string=} opt_message Error message in case of failure.
 * @param {...*} var_args The items to substitute into the failure message.
 * @return {!Function} The value, guaranteed to be a function when asserts
 *     enabled.
 * @throws {goog.asserts.AssertionError} When the value is not a function.
 * @closurePrimitive {asserts.matchesReturn}
 */
goog.asserts.assertFunction = function(value, opt_message, var_args) {
  if (goog.asserts.ENABLE_ASSERTS && !goog.isFunction(value)) {
    goog.asserts.doAssertFailure_(
        'Expected function but got %s: %s.', [goog.typeOf(value), value],
        opt_message, Array.prototype.slice.call(arguments, 2));
  }
  return /** @type {!Function} */ (value);
};


/**
 * Checks if the value is an Object if goog.asserts.ENABLE_ASSERTS is true.
 * @param {*} value The value to check.
 * @param {string=} opt_message Error message in case of failure.
 * @param {...*} var_args The items to substitute into the failure message.
 * @return {!Object} The value, guaranteed to be a non-null object.
 * @throws {goog.asserts.AssertionError} When the value is not an object.
 * @closurePrimitive {asserts.matchesReturn}
 */
goog.asserts.assertObject = function(value, opt_message, var_args) {
  if (goog.asserts.ENABLE_ASSERTS && !goog.isObject(value)) {
    goog.asserts.doAssertFailure_(
        'Expected object but got %s: %s.', [goog.typeOf(value), value],
        opt_message, Array.prototype.slice.call(arguments, 2));
  }
  return /** @type {!Object} */ (value);
};


/**
 * Checks if the value is an Array if goog.asserts.ENABLE_ASSERTS is true.
 * @param {*} value The value to check.
 * @param {string=} opt_message Error message in case of failure.
 * @param {...*} var_args The items to substitute into the failure message.
 * @return {!Array<?>} The value, guaranteed to be a non-null array.
 * @throws {goog.asserts.AssertionError} When the value is not an array.
 * @closurePrimitive {asserts.matchesReturn}
 */
goog.asserts.assertArray = function(value, opt_message, var_args) {
  if (goog.asserts.ENABLE_ASSERTS && !Array.isArray(value)) {
    goog.asserts.doAssertFailure_(
        'Expected array but got %s: %s.', [goog.typeOf(value), value],
        opt_message, Array.prototype.slice.call(arguments, 2));
  }
  return /** @type {!Array<?>} */ (value);
};


/**
 * Checks if the value is a boolean if goog.asserts.ENABLE_ASSERTS is true.
 * @param {*} value The value to check.
 * @param {string=} opt_message Error message in case of failure.
 * @param {...*} var_args The items to substitute into the failure message.
 * @return {boolean} The value, guaranteed to be a boolean when asserts are
 *     enabled.
 * @throws {goog.asserts.AssertionError} When the value is not a boolean.
 * @closurePrimitive {asserts.matchesReturn}
 */
goog.asserts.assertBoolean = function(value, opt_message, var_args) {
  if (goog.asserts.ENABLE_ASSERTS && typeof value !== 'boolean') {
    goog.asserts.doAssertFailure_(
        'Expected boolean but got %s: %s.', [goog.typeOf(value), value],
        opt_message, Array.prototype.slice.call(arguments, 2));
  }
  return /** @type {boolean} */ (value);
};


/**
 * Checks if the value is a DOM Element if goog.asserts.ENABLE_ASSERTS is true.
 * @param {*} value The value to check.
 * @param {string=} opt_message Error message in case of failure.
 * @param {...*} var_args The items to substitute into the failure message.
 * @return {!Element} The value, likely to be a DOM Element when asserts are
 *     enabled.
 * @throws {goog.asserts.AssertionError} When the value is not an Element.
 * @closurePrimitive {asserts.matchesReturn}
 * @deprecated Use goog.asserts.dom.assertIsElement instead.
 */
goog.asserts.assertElement = function(value, opt_message, var_args) {
  if (goog.asserts.ENABLE_ASSERTS &&
      (!goog.isObject(value) ||
       /** @type {!Node} */ (value).nodeType != goog.dom.NodeType.ELEMENT)) {
    goog.asserts.doAssertFailure_(
        'Expected Element but got %s: %s.', [goog.typeOf(value), value],
        opt_message, Array.prototype.slice.call(arguments, 2));
  }
  return /** @type {!Element} */ (value);
};


/**
 * Checks if the value is an instance of the user-defined type if
 * goog.asserts.ENABLE_ASSERTS is true.
 *
 * The compiler may tighten the type returned by this function.
 *
 * Do not use this to ensure a value is an HTMLElement or a subclass! Cross-
 * document DOM inherits from separate - though identical - browser classes, and
 * such a check will unexpectedly fail. Please use the methods in
 * goog.asserts.dom for these purposes.
 *
 * @param {?} value The value to check.
 * @param {function(new: T, ...)} type A user-defined constructor.
 * @param {string=} opt_message Error message in case of failure.
 * @param {...*} var_args The items to substitute into the failure message.
 * @throws {goog.asserts.AssertionError} When the value is not an instance of
 *     type.
 * @return {T}
 * @template T
 * @closurePrimitive {asserts.matchesReturn}
 */
goog.asserts.assertInstanceof = function(value, type, opt_message, var_args) {
  if (goog.asserts.ENABLE_ASSERTS && !(value instanceof type)) {
    goog.asserts.doAssertFailure_(
        'Expected instanceof %s but got %s.',
        [goog.asserts.getType_(type), goog.asserts.getType_(value)],
        opt_message, Array.prototype.slice.call(arguments, 3));
  }
  return value;
};


/**
 * Checks whether the value is a finite number, if goog.asserts.ENABLE_ASSERTS
 * is true.
 *
 * @param {*} value The value to check.
 * @param {string=} opt_message Error message in case of failure.
 * @param {...*} var_args The items to substitute into the failure message.
 * @throws {goog.asserts.AssertionError} When the value is not a number, or is
 *     a non-finite number such as NaN, Infinity or -Infinity.
 * @return {number} The value initially passed in.
 */
goog.asserts.assertFinite = function(value, opt_message, var_args) {
  if (goog.asserts.ENABLE_ASSERTS &&
      (typeof value != 'number' || !isFinite(value))) {
    goog.asserts.doAssertFailure_(
        'Expected %s to be a finite number but it is not.', [value],
        opt_message, Array.prototype.slice.call(arguments, 2));
  }
  return /** @type {number} */ (value);
};

/**
 * Checks that no enumerable keys are present in Object.prototype. Such keys
 * would break most code that use {@code for (var ... in ...)} loops.
 */
goog.asserts.assertObjectPrototypeIsIntact = function() {
  for (var key in Object.prototype) {
    goog.asserts.fail(key + ' should not be enumerable in Object.prototype.');
  }
};


/**
 * Returns the type of a value. If a constructor is passed, and a suitable
 * string cannot be found, 'unknown type name' will be returned.
 * @param {*} value A constructor, object, or primitive.
 * @return {string} The best display name for the value, or 'unknown type name'.
 * @private
 */
goog.asserts.getType_ = function(value) {
  if (value instanceof Function) {
    return value.displayName || value.name || 'unknown type name';
  } else if (value instanceof Object) {
    return /** @type {string} */ (value.constructor.displayName) ||
        value.constructor.name || Object.prototype.toString.call(value);
  } else {
    return value === null ? 'null' : typeof value;
  }
};

//third_party/javascript/closure/dom/asserts.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

goog.provide('goog.dom.asserts');

goog.require('goog.asserts');

/**
 * @fileoverview Custom assertions to ensure that an element has the appropriate
 * type.
 *
 * Using a goog.dom.safe wrapper on an object on the incorrect type (via an
 * incorrect static type cast) can result in security bugs: For instance,
 * g.d.s.setAnchorHref ensures that the URL assigned to the .href attribute
 * satisfies the SafeUrl contract, i.e., is safe to dereference as a hyperlink.
 * However, the value assigned to a HTMLLinkElement's .href property requires
 * the stronger TrustedResourceUrl contract, since it can refer to a stylesheet.
 * Thus, using g.d.s.setAnchorHref on an (incorrectly statically typed) object
 * of type HTMLLinkElement can result in a security vulnerability.
 * Assertions of the correct run-time type help prevent such incorrect use.
 *
 * In some cases, code using the DOM API is tested using mock objects (e.g., a
 * plain object such as {'href': url} instead of an actual Location object).
 * To allow such mocking, the assertions permit objects of types that are not
 * relevant DOM API objects at all (for instance, not Element or Location).
 *
 * Note that instanceof checks don't work straightforwardly in older versions of
 * IE, or across frames (see,
 * http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object,
 * http://stackoverflow.com/questions/26248599/instanceof-htmlelement-in-iframe-is-not-element-or-object).
 *
 * Hence, these assertions may pass vacuously in such scenarios. The resulting
 * risk of security bugs is limited by the following factors:
 *  - A bug can only arise in scenarios involving incorrect static typing (the
 *    wrapper methods are statically typed to demand objects of the appropriate,
 *    precise type).
 *  - Typically, code is tested and exercised in multiple browsers.
 */

/**
 * Asserts that a given object is a Location.
 *
 * To permit this assertion to pass in the context of tests where DOM APIs might
 * be mocked, also accepts any other type except for subtypes of {!Element}.
 * This is to ensure that, for instance, HTMLLinkElement is not being used in
 * place of a Location, since this could result in security bugs due to stronger
 * contracts required for assignments to the href property of the latter.
 *
 * @param {?Object} o The object whose type to assert.
 * @return {!Location}
 */
goog.dom.asserts.assertIsLocation = function(o) {
  if (goog.asserts.ENABLE_ASSERTS) {
    var win = goog.dom.asserts.getWindow_(o);
    if (win) {
      if (!o || (!(o instanceof win.Location) && o instanceof win.Element)) {
        goog.asserts.fail(
            'Argument is not a Location (or a non-Element mock); got: %s',
            goog.dom.asserts.debugStringForType_(o));
      }
    }
  }
  return /** @type {!Location} */ (o);
};


/**
 * Asserts that a given object is either the given subtype of Element
 * or a non-Element, non-Location Mock.
 *
 * To permit this assertion to pass in the context of tests where DOM
 * APIs might be mocked, also accepts any other type except for
 * subtypes of {!Element}.  This is to ensure that, for instance,
 * HTMLScriptElement is not being used in place of a HTMLImageElement,
 * since this could result in security bugs due to stronger contracts
 * required for assignments to the src property of the latter.
 *
 * The DOM type is looked up in the window the object belongs to.  In
 * some contexts, this might not be possible (e.g. when running tests
 * outside a browser, cross-domain lookup). In this case, the
 * assertions are skipped.
 *
 * @param {?Object} o The object whose type to assert.
 * @param {string} typename The name of the DOM type.
 * @return {!Element} The object.
 * @private
 */
// TODO(bangert): Make an analog of goog.dom.TagName to correctly handle casts?
goog.dom.asserts.assertIsElementType_ = function(o, typename) {
  if (goog.asserts.ENABLE_ASSERTS) {
    var win = goog.dom.asserts.getWindow_(o);
    if (win && typeof win[typename] != 'undefined') {
      if (!o ||
          (!(o instanceof win[typename]) &&
           (o instanceof win.Location || o instanceof win.Element))) {
        goog.asserts.fail(
            'Argument is not a %s (or a non-Element, non-Location mock); ' +
                'got: %s',
            typename, goog.dom.asserts.debugStringForType_(o));
      }
    }
  }
  return /** @type {!Element} */ (o);
};

/**
 * Asserts that a given object is a HTMLAnchorElement.
 *
 * To permit this assertion to pass in the context of tests where elements might
 * be mocked, also accepts objects that are not of type Location nor a subtype
 * of Element.
 *
 * @param {?Object} o The object whose type to assert.
 * @return {!HTMLAnchorElement}
 * @deprecated Use goog.asserts.dom.assertIsHtmlAnchorElement instead.
 */
goog.dom.asserts.assertIsHTMLAnchorElement = function(o) {
  return /** @type {!HTMLAnchorElement} */ (
      goog.dom.asserts.assertIsElementType_(o, 'HTMLAnchorElement'));
};

/**
 * Asserts that a given object is a HTMLButtonElement.
 *
 * To permit this assertion to pass in the context of tests where elements might
 * be mocked, also accepts objects that are not a subtype of Element.
 *
 * @param {?Object} o The object whose type to assert.
 * @return {!HTMLButtonElement}
 * @deprecated Use goog.asserts.dom.assertIsHtmlButtonElement instead.
 */
goog.dom.asserts.assertIsHTMLButtonElement = function(o) {
  return /** @type {!HTMLButtonElement} */ (
      goog.dom.asserts.assertIsElementType_(o, 'HTMLButtonElement'));
};

/**
 * Asserts that a given object is a HTMLLinkElement.
 *
 * To permit this assertion to pass in the context of tests where elements might
 * be mocked, also accepts objects that are not a subtype of Element.
 *
 * @param {?Object} o The object whose type to assert.
 * @return {!HTMLLinkElement}
 * @deprecated Use goog.asserts.dom.assertIsHtmlLinkElement instead.
 */
goog.dom.asserts.assertIsHTMLLinkElement = function(o) {
  return /** @type {!HTMLLinkElement} */ (
      goog.dom.asserts.assertIsElementType_(o, 'HTMLLinkElement'));
};

/**
 * Asserts that a given object is a HTMLImageElement.
 *
 * To permit this assertion to pass in the context of tests where elements might
 * be mocked, also accepts objects that are not a subtype of Element.
 *
 * @param {?Object} o The object whose type to assert.
 * @return {!HTMLImageElement}
 * @deprecated Use goog.asserts.dom.assertIsHtmlImageElement instead.
 */
goog.dom.asserts.assertIsHTMLImageElement = function(o) {
  return /** @type {!HTMLImageElement} */ (
      goog.dom.asserts.assertIsElementType_(o, 'HTMLImageElement'));
};

/**
 * Asserts that a given object is a HTMLAudioElement.
 *
 * To permit this assertion to pass in the context of tests where elements might
 * be mocked, also accepts objects that are not a subtype of Element.
 *
 * @param {?Object} o The object whose type to assert.
 * @return {!HTMLAudioElement}
 * @deprecated Use goog.asserts.dom.assertIsHtmlAudioElement instead.
 */
goog.dom.asserts.assertIsHTMLAudioElement = function(o) {
  return /** @type {!HTMLAudioElement} */ (
      goog.dom.asserts.assertIsElementType_(o, 'HTMLAudioElement'));
};

/**
 * Asserts that a given object is a HTMLVideoElement.
 *
 * To permit this assertion to pass in the context of tests where elements might
 * be mocked, also accepts objects that are not a subtype of Element.
 *
 * @param {?Object} o The object whose type to assert.
 * @return {!HTMLVideoElement}
 * @deprecated Use goog.asserts.dom.assertIsHtmlVideoElement instead.
 */
goog.dom.asserts.assertIsHTMLVideoElement = function(o) {
  return /** @type {!HTMLVideoElement} */ (
      goog.dom.asserts.assertIsElementType_(o, 'HTMLVideoElement'));
};

/**
 * Asserts that a given object is a HTMLInputElement.
 *
 * To permit this assertion to pass in the context of tests where elements might
 * be mocked, also accepts objects that are not a subtype of Element.
 *
 * @param {?Object} o The object whose type to assert.
 * @return {!HTMLInputElement}
 * @deprecated Use goog.asserts.dom.assertIsHtmlInputElement instead.
 */
goog.dom.asserts.assertIsHTMLInputElement = function(o) {
  return /** @type {!HTMLInputElement} */ (
      goog.dom.asserts.assertIsElementType_(o, 'HTMLInputElement'));
};

/**
 * Asserts that a given object is a HTMLTextAreaElement.
 *
 * To permit this assertion to pass in the context of tests where elements might
 * be mocked, also accepts objects that are not a subtype of Element.
 *
 * @param {?Object} o The object whose type to assert.
 * @return {!HTMLTextAreaElement}
 * @deprecated Use goog.asserts.dom.assertIsHtmlTextAreaElement instead.
 */
goog.dom.asserts.assertIsHTMLTextAreaElement = function(o) {
  return /** @type {!HTMLTextAreaElement} */ (
      goog.dom.asserts.assertIsElementType_(o, 'HTMLTextAreaElement'));
};

/**
 * Asserts that a given object is a HTMLCanvasElement.
 *
 * To permit this assertion to pass in the context of tests where elements might
 * be mocked, also accepts objects that are not a subtype of Element.
 *
 * @param {?Object} o The object whose type to assert.
 * @return {!HTMLCanvasElement}
 * @deprecated Use goog.asserts.dom.assertIsHtmlCanvasElement instead.
 */
goog.dom.asserts.assertIsHTMLCanvasElement = function(o) {
  return /** @type {!HTMLCanvasElement} */ (
      goog.dom.asserts.assertIsElementType_(o, 'HTMLCanvasElement'));
};

/**
 * Asserts that a given object is a HTMLEmbedElement.
 *
 * To permit this assertion to pass in the context of tests where elements might
 * be mocked, also accepts objects that are not a subtype of Element.
 *
 * @param {?Object} o The object whose type to assert.
 * @return {!HTMLEmbedElement}
 * @deprecated Use goog.asserts.dom.assertIsHtmlEmbedElement instead.
 */
goog.dom.asserts.assertIsHTMLEmbedElement = function(o) {
  return /** @type {!HTMLEmbedElement} */ (
      goog.dom.asserts.assertIsElementType_(o, 'HTMLEmbedElement'));
};

/**
 * Asserts that a given object is a HTMLFormElement.
 *
 * To permit this assertion to pass in the context of tests where elements might
 * be mocked, also accepts objects that are not a subtype of Element.
 *
 * @param {?Object} o The object whose type to assert.
 * @return {!HTMLFormElement}
 * @deprecated Use goog.asserts.dom.assertIsHtmlFormElement instead.
 */
goog.dom.asserts.assertIsHTMLFormElement = function(o) {
  return /** @type {!HTMLFormElement} */ (
      goog.dom.asserts.assertIsElementType_(o, 'HTMLFormElement'));
};

/**
 * Asserts that a given object is a HTMLFrameElement.
 *
 * To permit this assertion to pass in the context of tests where elements might
 * be mocked, also accepts objects that are not a subtype of Element.
 *
 * @param {?Object} o The object whose type to assert.
 * @return {!HTMLFrameElement}
 * @deprecated Use goog.asserts.dom.assertIsHtmlFrameElement instead.
 */
goog.dom.asserts.assertIsHTMLFrameElement = function(o) {
  return /** @type {!HTMLFrameElement} */ (
      goog.dom.asserts.assertIsElementType_(o, 'HTMLFrameElement'));
};

/**
 * Asserts that a given object is a HTMLIFrameElement.
 *
 * To permit this assertion to pass in the context of tests where elements might
 * be mocked, also accepts objects that are not a subtype of Element.
 *
 * @param {?Object} o The object whose type to assert.
 * @return {!HTMLIFrameElement}
 * @deprecated Use goog.asserts.dom.assertIsHtmlIFrameElement instead.
 */
goog.dom.asserts.assertIsHTMLIFrameElement = function(o) {
  return /** @type {!HTMLIFrameElement} */ (
      goog.dom.asserts.assertIsElementType_(o, 'HTMLIFrameElement'));
};

/**
 * Asserts that a given object is a HTMLObjectElement.
 *
 * To permit this assertion to pass in the context of tests where elements might
 * be mocked, also accepts objects that are not a subtype of Element.
 *
 * @param {?Object} o The object whose type to assert.
 * @return {!HTMLObjectElement}
 * @deprecated Use goog.asserts.dom.assertIsHtmlObjectElement instead.
 */
goog.dom.asserts.assertIsHTMLObjectElement = function(o) {
  return /** @type {!HTMLObjectElement} */ (
      goog.dom.asserts.assertIsElementType_(o, 'HTMLObjectElement'));
};

/**
 * Asserts that a given object is a HTMLScriptElement.
 *
 * To permit this assertion to pass in the context of tests where elements might
 * be mocked, also accepts objects that are not a subtype of Element.
 *
 * @param {?Object} o The object whose type to assert.
 * @return {!HTMLScriptElement}
 * @deprecated Use goog.asserts.dom.assertIsHtmlScriptElement instead.
 */
goog.dom.asserts.assertIsHTMLScriptElement = function(o) {
  return /** @type {!HTMLScriptElement} */ (
      goog.dom.asserts.assertIsElementType_(o, 'HTMLScriptElement'));
};

/**
 * Returns a string representation of a value's type.
 *
 * @param {*} value An object, or primitive.
 * @return {string} The best display name for the value.
 * @private
 */
goog.dom.asserts.debugStringForType_ = function(value) {
  if (goog.isObject(value)) {
    try {
      return /** @type {string|undefined} */ (value.constructor.displayName) ||
          value.constructor.name || Object.prototype.toString.call(value);
    } catch (e) {
      return '<object could not be stringified>';
    }
  } else {
    return value === undefined ? 'undefined' :
                                 value === null ? 'null' : typeof value;
  }
};

/**
 * Gets window of element.
 * @param {?Object} o
 * @return {?Window}
 * @private
 * @suppress {strictMissingProperties} ownerDocument not defined on Object
 */
goog.dom.asserts.getWindow_ = function(o) {
  try {
    var doc = o && o.ownerDocument;
    // This can throw “Blocked a frame with origin "chrome-extension://..." from
    // accessing a cross-origin frame” in Chrome extension.
    // MOE:begin_strip See
    // http://yaqs/6574360854528000#a6424563401097216.
    // MOE:end_strip
    var win =
        doc && /** @type {?Window} */ (doc.defaultView || doc.parentWindow);
    win = win || /** @type {!Window} */ (goog.global);
    // This can throw “Permission denied to access property "Element" on
    // cross-origin object”.
    if (win.Element && win.Location) {
      return win;
    }
  } catch (ex) {
  }
  return null;
};

//third_party/javascript/closure/functions/functions.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Utilities for creating functions. Loosely inspired by these
 * java classes from the Guava library:
 * com.google.common.base.Functions
 * https://google.github.io/guava/releases/snapshot-jre/api/docs/index.html?com/google/common/base/Functions.html
 *
 * com.google.common.base.Predicates
 * https://google.github.io/guava/releases/snapshot-jre/api/docs/index.html?com/google/common/base/Predicates.html
 *
 * More about these can be found at
 * https://github.com/google/guava/wiki/FunctionalExplained
 */


goog.provide('goog.functions');


/**
 * Creates a function that always returns the same value.
 * @param {T} retValue The value to return.
 * @return {function():T} The new function.
 * @template T
 */
goog.functions.constant = function(retValue) {
  return function() { return retValue; };
};


/**
 * Always returns false.
 * @type {function(...): boolean}
 */
goog.functions.FALSE = function() {
  return false;
};


/**
 * Always returns true.
 * @type {function(...): boolean}
 */
goog.functions.TRUE = function() {
  return true;
};


/**
 * Always returns NULL.
 * @type {function(...): null}
 */
goog.functions.NULL = function() {
  return null;
};


/**
 * A simple function that returns the first argument of whatever is passed
 * into it.
 * @param {T=} opt_returnValue The single value that will be returned.
 * @param {...*} var_args Optional trailing arguments. These are ignored.
 * @return {T} The first argument passed in, or undefined if nothing was passed.
 * @template T
 */
goog.functions.identity = function(opt_returnValue, var_args) {
  return opt_returnValue;
};


/**
 * Creates a function that always throws an error with the given message.
 * @param {string} message The error message.
 * @return {!Function} The error-throwing function.
 */
goog.functions.error = function(message) {
  return function() {
    throw new Error(message);
  };
};


/**
 * Creates a function that throws the given object.
 * @param {*} err An object to be thrown.
 * @return {!Function} The error-throwing function.
 */
goog.functions.fail = function(err) {
  return function() { throw err; };
};


/**
 * Given a function, create a function that keeps opt_numArgs arguments and
 * silently discards all additional arguments.
 * @param {Function} f The original function.
 * @param {number=} opt_numArgs The number of arguments to keep. Defaults to 0.
 * @return {!Function} A version of f that only keeps the first opt_numArgs
 *     arguments.
 */
goog.functions.lock = function(f, opt_numArgs) {
  opt_numArgs = opt_numArgs || 0;
  return function() {
    const self = /** @type {*} */ (this);
    return f.apply(self, Array.prototype.slice.call(arguments, 0, opt_numArgs));
  };
};


/**
 * Creates a function that returns its nth argument.
 * @param {number} n The position of the return argument.
 * @return {!Function} A new function.
 */
goog.functions.nth = function(n) {
  return function() { return arguments[n]; };
};


/**
 * Like goog.partial(), except that arguments are added after arguments to the
 * returned function.
 *
 * Usage:
 * function f(arg1, arg2, arg3, arg4) { ... }
 * var g = goog.functions.partialRight(f, arg3, arg4);
 * g(arg1, arg2);
 *
 * @param {!Function} fn A function to partially apply.
 * @param {...*} var_args Additional arguments that are partially applied to fn
 *     at the end.
 * @return {!Function} A partially-applied form of the function goog.partial()
 *     was invoked as a method of.
 */
goog.functions.partialRight = function(fn, var_args) {
  const rightArgs = Array.prototype.slice.call(arguments, 1);
  return function() {
    const self = /** @type {*} */ (this);
    const newArgs = Array.prototype.slice.call(arguments);
    newArgs.push.apply(newArgs, rightArgs);
    return fn.apply(self, newArgs);
  };
};


/**
 * Given a function, create a new function that swallows its return value
 * and replaces it with a new one.
 * @param {Function} f A function.
 * @param {T} retValue A new return value.
 * @return {function(...?):T} A new function.
 * @template T
 */
goog.functions.withReturnValue = function(f, retValue) {
  return goog.functions.sequence(f, goog.functions.constant(retValue));
};


/**
 * Creates a function that returns whether its argument equals the given value.
 *
 * Example:
 * var key = goog.object.findKey(obj, goog.functions.equalTo('needle'));
 *
 * @param {*} value The value to compare to.
 * @param {boolean=} opt_useLooseComparison Whether to use a loose (==)
 *     comparison rather than a strict (===) one. Defaults to false.
 * @return {function(*):boolean} The new function.
 */
goog.functions.equalTo = function(value, opt_useLooseComparison) {
  return function(other) {
    return opt_useLooseComparison ? (value == other) : (value === other);
  };
};


/**
 * Creates the composition of the functions passed in.
 * For example, (goog.functions.compose(f, g))(a) is equivalent to f(g(a)).
 * @param {function(...?):T} fn The final function.
 * @param {...Function} var_args A list of functions.
 * @return {function(...?):T} The composition of all inputs.
 * @template T
 */
goog.functions.compose = function(fn, var_args) {
  const functions = arguments;
  const length = functions.length;
  return function() {
    const self = /** @type {*} */ (this);
    let result;
    if (length) {
      result = functions[length - 1].apply(self, arguments);
    }

    for (let i = length - 2; i >= 0; i--) {
      result = functions[i].call(self, result);
    }
    return result;
  };
};


/**
 * Creates a function that calls the functions passed in in sequence, and
 * returns the value of the last function. For example,
 * (goog.functions.sequence(f, g))(x) is equivalent to f(x),g(x).
 * @param {...Function} var_args A list of functions.
 * @return {!Function} A function that calls all inputs in sequence.
 */
goog.functions.sequence = function(var_args) {
  const functions = arguments;
  const length = functions.length;
  return function() {
    const self = /** @type {*} */ (this);
    let result;
    for (let i = 0; i < length; i++) {
      result = functions[i].apply(self, arguments);
    }
    return result;
  };
};


/**
 * Creates a function that returns true if each of its components evaluates
 * to true. The components are evaluated in order, and the evaluation will be
 * short-circuited as soon as a function returns false.
 * For example, (goog.functions.and(f, g))(x) is equivalent to f(x) && g(x).
 * @param {...Function} var_args A list of functions.
 * @return {function(...?):boolean} A function that ANDs its component
 *      functions.
 */
goog.functions.and = function(var_args) {
  const functions = arguments;
  const length = functions.length;
  return function() {
    const self = /** @type {*} */ (this);
    for (let i = 0; i < length; i++) {
      if (!functions[i].apply(self, arguments)) {
        return false;
      }
    }
    return true;
  };
};


/**
 * Creates a function that returns true if any of its components evaluates
 * to true. The components are evaluated in order, and the evaluation will be
 * short-circuited as soon as a function returns true.
 * For example, (goog.functions.or(f, g))(x) is equivalent to f(x) || g(x).
 * @param {...Function} var_args A list of functions.
 * @return {function(...?):boolean} A function that ORs its component
 *    functions.
 */
goog.functions.or = function(var_args) {
  const functions = arguments;
  const length = functions.length;
  return function() {
    const self = /** @type {*} */ (this);
    for (let i = 0; i < length; i++) {
      if (functions[i].apply(self, arguments)) {
        return true;
      }
    }
    return false;
  };
};


/**
 * Creates a function that returns the Boolean opposite of a provided function.
 * For example, (goog.functions.not(f))(x) is equivalent to !f(x).
 * @param {!Function} f The original function.
 * @return {function(...?):boolean} A function that delegates to f and returns
 * opposite.
 */
goog.functions.not = function(f) {
  return function() {
    const self = /** @type {*} */ (this);
    return !f.apply(self, arguments);
  };
};


/**
 * Generic factory function to construct an object given the constructor
 * and the arguments. Intended to be bound to create object factories.
 *
 * Example:
 *
 * var factory = goog.partial(goog.functions.create, Class);
 *
 * @param {function(new:T, ...)} constructor The constructor for the Object.
 * @param {...*} var_args The arguments to be passed to the constructor.
 * @return {T} A new instance of the class given in `constructor`.
 * @template T
 * @deprecated This function does not work with ES6 class constructors. Use
 *     arrow functions + spread args instead.
 */
goog.functions.create = function(constructor, var_args) {
  /**
   * @constructor
   * @final
   */
  const temp = function() {};
  temp.prototype = constructor.prototype;

  // obj will have constructor's prototype in its chain and
  // 'obj instanceof constructor' will be true.
  const obj = new temp();

  // obj is initialized by constructor.
  // arguments is only array-like so lacks shift(), but can be used with
  // the Array prototype function.
  constructor.apply(obj, Array.prototype.slice.call(arguments, 1));
  return obj;
};


/**
 * @define {boolean} Whether the return value cache should be used.
 *    This should only be used to disable caches when testing.
 */
goog.functions.CACHE_RETURN_VALUE =
    goog.define('goog.functions.CACHE_RETURN_VALUE', true);


/**
 * Gives a wrapper function that caches the return value of a parameterless
 * function when first called.
 *
 * When called for the first time, the given function is called and its
 * return value is cached (thus this is only appropriate for idempotent
 * functions).  Subsequent calls will return the cached return value. This
 * allows the evaluation of expensive functions to be delayed until first used.
 *
 * To cache the return values of functions with parameters, see goog.memoize.
 *
 * @param {function():T} fn A function to lazily evaluate.
 * @return {function():T} A wrapped version the function.
 * @template T
 */
goog.functions.cacheReturnValue = function(fn) {
  let called = false;
  let value;

  return function() {
    if (!goog.functions.CACHE_RETURN_VALUE) {
      return fn();
    }

    if (!called) {
      value = fn();
      called = true;
    }

    return value;
  };
};


/**
 * Wraps a function to allow it to be called, at most, once. All
 * additional calls are no-ops.
 *
 * This is particularly useful for initialization functions
 * that should be called, at most, once.
 *
 * @param {function():*} f Function to call.
 * @return {function():undefined} Wrapped function.
 */
goog.functions.once = function(f) {
  // Keep a reference to the function that we null out when we're done with
  // it -- that way, the function can be GC'd when we're done with it.
  let inner = f;
  return function() {
    if (inner) {
      const tmp = inner;
      inner = null;
      tmp();
    }
  };
};


/**
 * Wraps a function to allow it to be called, at most, once per interval
 * (specified in milliseconds). If the wrapper function is called N times within
 * that interval, only the Nth call will go through.
 *
 * This is particularly useful for batching up repeated actions where the
 * last action should win. This can be used, for example, for refreshing an
 * autocomplete pop-up every so often rather than updating with every keystroke,
 * since the final text typed by the user is the one that should produce the
 * final autocomplete results. For more stateful debouncing with support for
 * pausing, resuming, and canceling debounced actions, use
 * `goog.async.Debouncer`.
 *
 * @param {function(this:SCOPE, ...?)} f Function to call.
 * @param {number} interval Interval over which to debounce. The function will
 *     only be called after the full interval has elapsed since the last call.
 * @param {SCOPE=} opt_scope Object in whose scope to call the function.
 * @return {function(...?): undefined} Wrapped function.
 * @template SCOPE
 */
goog.functions.debounce = function(f, interval, opt_scope) {
  let timeout = 0;
  return /** @type {function(...?)} */ (function(var_args) {
    goog.global.clearTimeout(timeout);
    const args = arguments;
    timeout = goog.global.setTimeout(function() {
      f.apply(opt_scope, args);
    }, interval);
  });
};


/**
 * Wraps a function to allow it to be called, at most, once per interval
 * (specified in milliseconds). If the wrapper function is called N times in
 * that interval, both the 1st and the Nth calls will go through.
 *
 * This is particularly useful for limiting repeated user requests where the
 * the last action should win, but you also don't want to wait until the end of
 * the interval before sending a request out, as it leads to a perception of
 * slowness for the user.
 *
 * @param {function(this:SCOPE, ...?)} f Function to call.
 * @param {number} interval Interval over which to throttle. The function can
 *     only be called once per interval.
 * @param {SCOPE=} opt_scope Object in whose scope to call the function.
 * @return {function(...?): undefined} Wrapped function.
 * @template SCOPE
 */
goog.functions.throttle = function(f, interval, opt_scope) {
  let timeout = 0;
  let shouldFire = false;
  let args = [];

  const handleTimeout = function() {
    timeout = 0;
    if (shouldFire) {
      shouldFire = false;
      fire();
    }
  };

  const fire = function() {
    timeout = goog.global.setTimeout(handleTimeout, interval);
    f.apply(opt_scope, args);
  };

  return /** @type {function(...?)} */ (function(var_args) {
    args = arguments;
    if (!timeout) {
      fire();
    } else {
      shouldFire = true;
    }
  });
};


/**
 * Wraps a function to allow it to be called, at most, once per interval
 * (specified in milliseconds). If the wrapper function is called N times within
 * that interval, only the 1st call will go through.
 *
 * This is particularly useful for limiting repeated user requests where the
 * first request is guaranteed to have all the data required to perform the
 * final action, so there's no need to wait until the end of the interval before
 * sending the request out.
 *
 * @param {function(this:SCOPE, ...?)} f Function to call.
 * @param {number} interval Interval over which to rate-limit. The function will
 *     only be called once per interval, and ignored for the remainer of the
 *     interval.
 * @param {SCOPE=} opt_scope Object in whose scope to call the function.
 * @return {function(...?): undefined} Wrapped function.
 * @template SCOPE
 */
goog.functions.rateLimit = function(f, interval, opt_scope) {
  let timeout = 0;

  const handleTimeout = function() {
    timeout = 0;
  };

  return /** @type {function(...?)} */ (function(var_args) {
    if (!timeout) {
      timeout = goog.global.setTimeout(handleTimeout, interval);
      f.apply(opt_scope, arguments);
    }
  });
};

//third_party/javascript/closure/array/array.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Utilities for manipulating arrays.
 */


goog.provide('goog.array');

goog.require('goog.asserts');


/**
 * @define {boolean} NATIVE_ARRAY_PROTOTYPES indicates whether the code should
 * rely on Array.prototype functions, if available.
 *
 * The Array.prototype functions can be defined by external libraries like
 * Prototype and setting this flag to false forces closure to use its own
 * goog.array implementation.
 *
 * If your javascript can be loaded by a third party site and you are wary about
 * relying on the prototype functions, specify
 * "--define goog.NATIVE_ARRAY_PROTOTYPES=false" to the JSCompiler.
 *
 * Setting goog.TRUSTED_SITE to false will automatically set
 * NATIVE_ARRAY_PROTOTYPES to false.
 */
goog.NATIVE_ARRAY_PROTOTYPES =
    goog.define('goog.NATIVE_ARRAY_PROTOTYPES', goog.TRUSTED_SITE);


/**
 * @define {boolean} If true, JSCompiler will use the native implementation of
 * array functions where appropriate (e.g., `Array#filter`) and remove the
 * unused pure JS implementation.
 */
goog.array.ASSUME_NATIVE_FUNCTIONS = goog.define(
    'goog.array.ASSUME_NATIVE_FUNCTIONS', goog.FEATURESET_YEAR > 2012);


/**
 * Returns the last element in an array without removing it.
 * Same as goog.array.last.
 * @param {IArrayLike<T>|string} array The array.
 * @return {T} Last item in array.
 * @template T
 */
goog.array.peek = function(array) {
  return array[array.length - 1];
};


/**
 * Returns the last element in an array without removing it.
 * Same as goog.array.peek.
 * @param {IArrayLike<T>|string} array The array.
 * @return {T} Last item in array.
 * @template T
 */
goog.array.last = goog.array.peek;

// NOTE(arv): Since most of the array functions are generic it allows you to
// pass an array-like object. Strings have a length and are considered array-
// like. However, the 'in' operator does not work on strings so we cannot just
// use the array path even if the browser supports indexing into strings. We
// therefore end up splitting the string.


/**
 * Returns the index of the first element of an array with a specified value, or
 * -1 if the element is not present in the array.
 *
 * See {@link http://tinyurl.com/developer-mozilla-org-array-indexof}
 *
 * @param {IArrayLike<T>|string} arr The array to be searched.
 * @param {T} obj The object for which we are searching.
 * @param {number=} opt_fromIndex The index at which to start the search. If
 *     omitted the search starts at index 0.
 * @return {number} The index of the first matching array element.
 * @template T
 */
goog.array.indexOf = goog.NATIVE_ARRAY_PROTOTYPES &&
        (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.indexOf) ?
    function(arr, obj, opt_fromIndex) {
      goog.asserts.assert(arr.length != null);

      return Array.prototype.indexOf.call(arr, obj, opt_fromIndex);
    } :
    function(arr, obj, opt_fromIndex) {
      var fromIndex = opt_fromIndex == null ?
          0 :
          (opt_fromIndex < 0 ? Math.max(0, arr.length + opt_fromIndex) :
                               opt_fromIndex);

      if (typeof arr === 'string') {
        // Array.prototype.indexOf uses === so only strings should be found.
        if (typeof obj !== 'string' || obj.length != 1) {
          return -1;
        }
        return arr.indexOf(obj, fromIndex);
      }

      for (var i = fromIndex; i < arr.length; i++) {
        if (i in arr && arr[i] === obj) return i;
      }
      return -1;
    };


/**
 * Returns the index of the last element of an array with a specified value, or
 * -1 if the element is not present in the array.
 *
 * See {@link http://tinyurl.com/developer-mozilla-org-array-lastindexof}
 *
 * @param {!IArrayLike<T>|string} arr The array to be searched.
 * @param {T} obj The object for which we are searching.
 * @param {?number=} opt_fromIndex The index at which to start the search. If
 *     omitted the search starts at the end of the array.
 * @return {number} The index of the last matching array element.
 * @template T
 */
goog.array.lastIndexOf = goog.NATIVE_ARRAY_PROTOTYPES &&
        (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.lastIndexOf) ?
    function(arr, obj, opt_fromIndex) {
      goog.asserts.assert(arr.length != null);

      // Firefox treats undefined and null as 0 in the fromIndex argument which
      // leads it to always return -1
      var fromIndex = opt_fromIndex == null ? arr.length - 1 : opt_fromIndex;
      return Array.prototype.lastIndexOf.call(arr, obj, fromIndex);
    } :
    function(arr, obj, opt_fromIndex) {
      var fromIndex = opt_fromIndex == null ? arr.length - 1 : opt_fromIndex;

      if (fromIndex < 0) {
        fromIndex = Math.max(0, arr.length + fromIndex);
      }

      if (typeof arr === 'string') {
        // Array.prototype.lastIndexOf uses === so only strings should be found.
        if (typeof obj !== 'string' || obj.length != 1) {
          return -1;
        }
        return arr.lastIndexOf(obj, fromIndex);
      }

      for (var i = fromIndex; i >= 0; i--) {
        if (i in arr && arr[i] === obj) return i;
      }
      return -1;
    };


/**
 * Calls a function for each element in an array. Skips holes in the array.
 * See {@link http://tinyurl.com/developer-mozilla-org-array-foreach}
 *
 * @param {IArrayLike<T>|string} arr Array or array like object over
 *     which to iterate.
 * @param {?function(this: S, T, number, ?): ?} f The function to call for every
 *     element. This function takes 3 arguments (the element, the index and the
 *     array). The return value is ignored.
 * @param {S=} opt_obj The object to be used as the value of 'this' within f.
 * @template T,S
 */
goog.array.forEach = goog.NATIVE_ARRAY_PROTOTYPES &&
        (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.forEach) ?
    function(arr, f, opt_obj) {
      goog.asserts.assert(arr.length != null);

      Array.prototype.forEach.call(arr, f, opt_obj);
    } :
    function(arr, f, opt_obj) {
      var l = arr.length;  // must be fixed during loop... see docs
      var arr2 = (typeof arr === 'string') ? arr.split('') : arr;
      for (var i = 0; i < l; i++) {
        if (i in arr2) {
          f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr);
        }
      }
    };


/**
 * Calls a function for each element in an array, starting from the last
 * element rather than the first.
 *
 * @param {IArrayLike<T>|string} arr Array or array
 *     like object over which to iterate.
 * @param {?function(this: S, T, number, ?): ?} f The function to call for every
 *     element. This function
 *     takes 3 arguments (the element, the index and the array). The return
 *     value is ignored.
 * @param {S=} opt_obj The object to be used as the value of 'this'
 *     within f.
 * @template T,S
 */
goog.array.forEachRight = function(arr, f, opt_obj) {
  var l = arr.length;  // must be fixed during loop... see docs
  var arr2 = (typeof arr === 'string') ? arr.split('') : arr;
  for (var i = l - 1; i >= 0; --i) {
    if (i in arr2) {
      f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr);
    }
  }
};


/**
 * Calls a function for each element in an array, and if the function returns
 * true adds the element to a new array.
 *
 * See {@link http://tinyurl.com/developer-mozilla-org-array-filter}
 *
 * @param {IArrayLike<T>|string} arr Array or array
 *     like object over which to iterate.
 * @param {?function(this:S, T, number, ?):boolean} f The function to call for
 *     every element. This function
 *     takes 3 arguments (the element, the index and the array) and must
 *     return a Boolean. If the return value is true the element is added to the
 *     result array. If it is false the element is not included.
 * @param {S=} opt_obj The object to be used as the value of 'this'
 *     within f.
 * @return {!Array<T>} a new array in which only elements that passed the test
 *     are present.
 * @template T,S
 */
goog.array.filter = goog.NATIVE_ARRAY_PROTOTYPES &&
        (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.filter) ?
    function(arr, f, opt_obj) {
      goog.asserts.assert(arr.length != null);

      return Array.prototype.filter.call(arr, f, opt_obj);
    } :
    function(arr, f, opt_obj) {
      var l = arr.length;  // must be fixed during loop... see docs
      var res = [];
      var resLength = 0;
      var arr2 = (typeof arr === 'string') ? arr.split('') : arr;
      for (var i = 0; i < l; i++) {
        if (i in arr2) {
          var val = arr2[i];  // in case f mutates arr2
          if (f.call(/** @type {?} */ (opt_obj), val, i, arr)) {
            res[resLength++] = val;
          }
        }
      }
      return res;
    };


/**
 * Calls a function for each element in an array and inserts the result into a
 * new array.
 *
 * See {@link http://tinyurl.com/developer-mozilla-org-array-map}
 *
 * @param {IArrayLike<VALUE>|string} arr Array or array like object
 *     over which to iterate.
 * @param {function(this:THIS, VALUE, number, ?): RESULT} f The function to call
 *     for every element. This function takes 3 arguments (the element,
 *     the index and the array) and should return something. The result will be
 *     inserted into a new array.
 * @param {THIS=} opt_obj The object to be used as the value of 'this' within f.
 * @return {!Array<RESULT>} a new array with the results from f.
 * @template THIS, VALUE, RESULT
 */
goog.array.map = goog.NATIVE_ARRAY_PROTOTYPES &&
        (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.map) ?
    function(arr, f, opt_obj) {
      goog.asserts.assert(arr.length != null);

      return Array.prototype.map.call(arr, f, opt_obj);
    } :
    function(arr, f, opt_obj) {
      var l = arr.length;  // must be fixed during loop... see docs
      var res = new Array(l);
      var arr2 = (typeof arr === 'string') ? arr.split('') : arr;
      for (var i = 0; i < l; i++) {
        if (i in arr2) {
          res[i] = f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr);
        }
      }
      return res;
    };


/**
 * Passes every element of an array into a function and accumulates the result.
 *
 * See {@link http://tinyurl.com/developer-mozilla-org-array-reduce}
 *
 * For example:
 * var a = [1, 2, 3, 4];
 * goog.array.reduce(a, function(r, v, i, arr) {return r + v;}, 0);
 * returns 10
 *
 * @param {IArrayLike<T>|string} arr Array or array
 *     like object over which to iterate.
 * @param {function(this:S, R, T, number, ?) : R} f The function to call for
 *     every element. This function
 *     takes 4 arguments (the function's previous result or the initial value,
 *     the value of the current array element, the current array index, and the
 *     array itself)
 *     function(previousValue, currentValue, index, array).
 * @param {?} val The initial value to pass into the function on the first call.
 * @param {S=} opt_obj  The object to be used as the value of 'this'
 *     within f.
 * @return {R} Result of evaluating f repeatedly across the values of the array.
 * @template T,S,R
 */
goog.array.reduce = goog.NATIVE_ARRAY_PROTOTYPES &&
        (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.reduce) ?
    function(arr, f, val, opt_obj) {
      goog.asserts.assert(arr.length != null);
      if (opt_obj) {
        f = goog.bind(f, opt_obj);
      }
      return Array.prototype.reduce.call(arr, f, val);
    } :
    function(arr, f, val, opt_obj) {
      var rval = val;
      goog.array.forEach(arr, function(val, index) {
        rval = f.call(/** @type {?} */ (opt_obj), rval, val, index, arr);
      });
      return rval;
    };


/**
 * Passes every element of an array into a function and accumulates the result,
 * starting from the last element and working towards the first.
 *
 * See {@link http://tinyurl.com/developer-mozilla-org-array-reduceright}
 *
 * For example:
 * var a = ['a', 'b', 'c'];
 * goog.array.reduceRight(a, function(r, v, i, arr) {return r + v;}, '');
 * returns 'cba'
 *
 * @param {IArrayLike<T>|string} arr Array or array
 *     like object over which to iterate.
 * @param {?function(this:S, R, T, number, ?) : R} f The function to call for
 *     every element. This function
 *     takes 4 arguments (the function's previous result or the initial value,
 *     the value of the current array element, the current array index, and the
 *     array itself)
 *     function(previousValue, currentValue, index, array).
 * @param {?} val The initial value to pass into the function on the first call.
 * @param {S=} opt_obj The object to be used as the value of 'this'
 *     within f.
 * @return {R} Object returned as a result of evaluating f repeatedly across the
 *     values of the array.
 * @template T,S,R
 */
goog.array.reduceRight = goog.NATIVE_ARRAY_PROTOTYPES &&
        (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.reduceRight) ?
    function(arr, f, val, opt_obj) {
      goog.asserts.assert(arr.length != null);
      goog.asserts.assert(f != null);
      if (opt_obj) {
        f = goog.bind(f, opt_obj);
      }
      return Array.prototype.reduceRight.call(arr, f, val);
    } :
    function(arr, f, val, opt_obj) {
      var rval = val;
      goog.array.forEachRight(arr, function(val, index) {
        rval = f.call(/** @type {?} */ (opt_obj), rval, val, index, arr);
      });
      return rval;
    };


/**
 * Calls f for each element of an array. If any call returns true, some()
 * returns true (without checking the remaining elements). If all calls
 * return false, some() returns false.
 *
 * See {@link http://tinyurl.com/developer-mozilla-org-array-some}
 *
 * @param {IArrayLike<T>|string} arr Array or array
 *     like object over which to iterate.
 * @param {?function(this:S, T, number, ?) : boolean} f The function to call for
 *     for every element. This function takes 3 arguments (the element, the
 *     index and the array) and should return a boolean.
 * @param {S=} opt_obj  The object to be used as the value of 'this'
 *     within f.
 * @return {boolean} true if any element passes the test.
 * @template T,S
 */
goog.array.some = goog.NATIVE_ARRAY_PROTOTYPES &&
        (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.some) ?
    function(arr, f, opt_obj) {
      goog.asserts.assert(arr.length != null);

      return Array.prototype.some.call(arr, f, opt_obj);
    } :
    function(arr, f, opt_obj) {
      var l = arr.length;  // must be fixed during loop... see docs
      var arr2 = (typeof arr === 'string') ? arr.split('') : arr;
      for (var i = 0; i < l; i++) {
        if (i in arr2 && f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) {
          return true;
        }
      }
      return false;
    };


/**
 * Call f for each element of an array. If all calls return true, every()
 * returns true. If any call returns false, every() returns false and
 * does not continue to check the remaining elements.
 *
 * See {@link http://tinyurl.com/developer-mozilla-org-array-every}
 *
 * @param {IArrayLike<T>|string} arr Array or array
 *     like object over which to iterate.
 * @param {?function(this:S, T, number, ?) : boolean} f The function to call for
 *     for every element. This function takes 3 arguments (the element, the
 *     index and the array) and should return a boolean.
 * @param {S=} opt_obj The object to be used as the value of 'this'
 *     within f.
 * @return {boolean} false if any element fails the test.
 * @template T,S
 */
goog.array.every = goog.NATIVE_ARRAY_PROTOTYPES &&
        (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.every) ?
    function(arr, f, opt_obj) {
      goog.asserts.assert(arr.length != null);

      return Array.prototype.every.call(arr, f, opt_obj);
    } :
    function(arr, f, opt_obj) {
      var l = arr.length;  // must be fixed during loop... see docs
      var arr2 = (typeof arr === 'string') ? arr.split('') : arr;
      for (var i = 0; i < l; i++) {
        if (i in arr2 && !f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) {
          return false;
        }
      }
      return true;
    };


/**
 * Counts the array elements that fulfill the predicate, i.e. for which the
 * callback function returns true. Skips holes in the array.
 *
 * @param {!IArrayLike<T>|string} arr Array or array like object
 *     over which to iterate.
 * @param {function(this: S, T, number, ?): boolean} f The function to call for
 *     every element. Takes 3 arguments (the element, the index and the array).
 * @param {S=} opt_obj The object to be used as the value of 'this' within f.
 * @return {number} The number of the matching elements.
 * @template T,S
 */
goog.array.count = function(arr, f, opt_obj) {
  var count = 0;
  goog.array.forEach(arr, function(element, index, arr) {
    if (f.call(/** @type {?} */ (opt_obj), element, index, arr)) {
      ++count;
    }
  }, opt_obj);
  return count;
};


/**
 * Search an array for the first element that satisfies a given condition and
 * return that element.
 * @param {IArrayLike<T>|string} arr Array or array
 *     like object over which to iterate.
 * @param {?function(this:S, T, number, ?) : boolean} f The function to call
 *     for every element. This function takes 3 arguments (the element, the
 *     index and the array) and should return a boolean.
 * @param {S=} opt_obj An optional "this" context for the function.
 * @return {T|null} The first array element that passes the test, or null if no
 *     element is found.
 * @template T,S
 */
goog.array.find = function(arr, f, opt_obj) {
  var i = goog.array.findIndex(arr, f, opt_obj);
  return i < 0 ? null : typeof arr === 'string' ? arr.charAt(i) : arr[i];
};


/**
 * Search an array for the first element that satisfies a given condition and
 * return its index.
 * @param {IArrayLike<T>|string} arr Array or array
 *     like object over which to iterate.
 * @param {?function(this:S, T, number, ?) : boolean} f The function to call for
 *     every element. This function
 *     takes 3 arguments (the element, the index and the array) and should
 *     return a boolean.
 * @param {S=} opt_obj An optional "this" context for the function.
 * @return {number} The index of the first array element that passes the test,
 *     or -1 if no element is found.
 * @template T,S
 */
goog.array.findIndex = function(arr, f, opt_obj) {
  var l = arr.length;  // must be fixed during loop... see docs
  var arr2 = (typeof arr === 'string') ? arr.split('') : arr;
  for (var i = 0; i < l; i++) {
    if (i in arr2 && f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) {
      return i;
    }
  }
  return -1;
};


/**
 * Search an array (in reverse order) for the last element that satisfies a
 * given condition and return that element.
 * @param {IArrayLike<T>|string} arr Array or array
 *     like object over which to iterate.
 * @param {?function(this:S, T, number, ?) : boolean} f The function to call
 *     for every element. This function
 *     takes 3 arguments (the element, the index and the array) and should
 *     return a boolean.
 * @param {S=} opt_obj An optional "this" context for the function.
 * @return {T|null} The last array element that passes the test, or null if no
 *     element is found.
 * @template T,S
 */
goog.array.findRight = function(arr, f, opt_obj) {
  var i = goog.array.findIndexRight(arr, f, opt_obj);
  return i < 0 ? null : typeof arr === 'string' ? arr.charAt(i) : arr[i];
};


/**
 * Search an array (in reverse order) for the last element that satisfies a
 * given condition and return its index.
 * @param {IArrayLike<T>|string} arr Array or array
 *     like object over which to iterate.
 * @param {?function(this:S, T, number, ?) : boolean} f The function to call
 *     for every element. This function
 *     takes 3 arguments (the element, the index and the array) and should
 *     return a boolean.
 * @param {S=} opt_obj An optional "this" context for the function.
 * @return {number} The index of the last array element that passes the test,
 *     or -1 if no element is found.
 * @template T,S
 */
goog.array.findIndexRight = function(arr, f, opt_obj) {
  var l = arr.length;  // must be fixed during loop... see docs
  var arr2 = (typeof arr === 'string') ? arr.split('') : arr;
  for (var i = l - 1; i >= 0; i--) {
    if (i in arr2 && f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) {
      return i;
    }
  }
  return -1;
};


/**
 * Whether the array contains the given object.
 * @param {IArrayLike<?>|string} arr The array to test for the presence of the
 *     element.
 * @param {*} obj The object for which to test.
 * @return {boolean} true if obj is present.
 */
goog.array.contains = function(arr, obj) {
  return goog.array.indexOf(arr, obj) >= 0;
};


/**
 * Whether the array is empty.
 * @param {IArrayLike<?>|string} arr The array to test.
 * @return {boolean} true if empty.
 */
goog.array.isEmpty = function(arr) {
  return arr.length == 0;
};


/**
 * Clears the array.
 * @param {IArrayLike<?>} arr Array or array like object to clear.
 */
goog.array.clear = function(arr) {
  // For non real arrays we don't have the magic length so we delete the
  // indices.
  if (!Array.isArray(arr)) {
    for (var i = arr.length - 1; i >= 0; i--) {
      delete arr[i];
    }
  }
  arr.length = 0;
};


/**
 * Pushes an item into an array, if it's not already in the array.
 * @param {Array<T>} arr Array into which to insert the item.
 * @param {T} obj Value to add.
 * @template T
 */
goog.array.insert = function(arr, obj) {
  if (!goog.array.contains(arr, obj)) {
    arr.push(obj);
  }
};


/**
 * Inserts an object at the given index of the array.
 * @param {IArrayLike<?>} arr The array to modify.
 * @param {*} obj The object to insert.
 * @param {number=} opt_i The index at which to insert the object. If omitted,
 *      treated as 0. A negative index is counted from the end of the array.
 */
goog.array.insertAt = function(arr, obj, opt_i) {
  goog.array.splice(arr, opt_i, 0, obj);
};


/**
 * Inserts at the given index of the array, all elements of another array.
 * @param {IArrayLike<?>} arr The array to modify.
 * @param {IArrayLike<?>} elementsToAdd The array of elements to add.
 * @param {number=} opt_i The index at which to insert the object. If omitted,
 *      treated as 0. A negative index is counted from the end of the array.
 */
goog.array.insertArrayAt = function(arr, elementsToAdd, opt_i) {
  goog.partial(goog.array.splice, arr, opt_i, 0).apply(null, elementsToAdd);
};


/**
 * Inserts an object into an array before a specified object.
 * @param {Array<T>} arr The array to modify.
 * @param {T} obj The object to insert.
 * @param {T=} opt_obj2 The object before which obj should be inserted. If obj2
 *     is omitted or not found, obj is inserted at the end of the array.
 * @template T
 */
goog.array.insertBefore = function(arr, obj, opt_obj2) {
  var i;
  if (arguments.length == 2 || (i = goog.array.indexOf(arr, opt_obj2)) < 0) {
    arr.push(obj);
  } else {
    goog.array.insertAt(arr, obj, i);
  }
};


/**
 * Removes the first occurrence of a particular value from an array.
 * @param {IArrayLike<T>} arr Array from which to remove
 *     value.
 * @param {T} obj Object to remove.
 * @return {boolean} True if an element was removed.
 * @template T
 */
goog.array.remove = function(arr, obj) {
  var i = goog.array.indexOf(arr, obj);
  var rv;
  if ((rv = i >= 0)) {
    goog.array.removeAt(arr, i);
  }
  return rv;
};


/**
 * Removes the last occurrence of a particular value from an array.
 * @param {!IArrayLike<T>} arr Array from which to remove value.
 * @param {T} obj Object to remove.
 * @return {boolean} True if an element was removed.
 * @template T
 */
goog.array.removeLast = function(arr, obj) {
  var i = goog.array.lastIndexOf(arr, obj);
  if (i >= 0) {
    goog.array.removeAt(arr, i);
    return true;
  }
  return false;
};


/**
 * Removes from an array the element at index i
 * @param {IArrayLike<?>} arr Array or array like object from which to
 *     remove value.
 * @param {number} i The index to remove.
 * @return {boolean} True if an element was removed.
 */
goog.array.removeAt = function(arr, i) {
  goog.asserts.assert(arr.length != null);

  // use generic form of splice
  // splice returns the removed items and if successful the length of that
  // will be 1
  return Array.prototype.splice.call(arr, i, 1).length == 1;
};


/**
 * Removes the first value that satisfies the given condition.
 * @param {IArrayLike<T>} arr Array or array
 *     like object over which to iterate.
 * @param {?function(this:S, T, number, ?) : boolean} f The function to call
 *     for every element. This function
 *     takes 3 arguments (the element, the index and the array) and should
 *     return a boolean.
 * @param {S=} opt_obj An optional "this" context for the function.
 * @return {boolean} True if an element was removed.
 * @template T,S
 */
goog.array.removeIf = function(arr, f, opt_obj) {
  var i = goog.array.findIndex(arr, f, opt_obj);
  if (i >= 0) {
    goog.array.removeAt(arr, i);
    return true;
  }
  return false;
};


/**
 * Removes all values that satisfy the given condition.
 * @param {IArrayLike<T>} arr Array or array
 *     like object over which to iterate.
 * @param {?function(this:S, T, number, ?) : boolean} f The function to call
 *     for every element. This function
 *     takes 3 arguments (the element, the index and the array) and should
 *     return a boolean.
 * @param {S=} opt_obj An optional "this" context for the function.
 * @return {number} The number of items removed
 * @template T,S
 */
goog.array.removeAllIf = function(arr, f, opt_obj) {
  var removedCount = 0;
  goog.array.forEachRight(arr, function(val, index) {
    if (f.call(/** @type {?} */ (opt_obj), val, index, arr)) {
      if (goog.array.removeAt(arr, index)) {
        removedCount++;
      }
    }
  });
  return removedCount;
};


/**
 * Returns a new array that is the result of joining the arguments.  If arrays
 * are passed then their items are added, however, if non-arrays are passed they
 * will be added to the return array as is.
 *
 * Note that ArrayLike objects will be added as is, rather than having their
 * items added.
 *
 * goog.array.concat([1, 2], [3, 4]) -> [1, 2, 3, 4]
 * goog.array.concat(0, [1, 2]) -> [0, 1, 2]
 * goog.array.concat([1, 2], null) -> [1, 2, null]
 *
 * There is bug in all current versions of IE (6, 7 and 8) where arrays created
 * in an iframe become corrupted soon (not immediately) after the iframe is
 * destroyed. This is common if loading data via goog.net.IframeIo, for example.
 * This corruption only affects the concat method which will start throwing
 * Catastrophic Errors (#-2147418113).
 *
 * See http://endoflow.com/scratch/corrupted-arrays.html for a test case.
 *
 * Internally goog.array should use this, so that all methods will continue to
 * work on these broken array objects.
 *
 * @param {...*} var_args Items to concatenate.  Arrays will have each item
 *     added, while primitives and objects will be added as is.
 * @return {!Array<?>} The new resultant array.
 */
goog.array.concat = function(var_args) {
  return Array.prototype.concat.apply([], arguments);
};


/**
 * Returns a new array that contains the contents of all the arrays passed.
 * @param {...!Array<T>} var_args
 * @return {!Array<T>}
 * @template T
 */
goog.array.join = function(var_args) {
  return Array.prototype.concat.apply([], arguments);
};


/**
 * Converts an object to an array.
 * @param {IArrayLike<T>|string} object  The object to convert to an
 *     array.
 * @return {!Array<T>} The object converted into an array. If object has a
 *     length property, every property indexed with a non-negative number
 *     less than length will be included in the result. If object does not
 *     have a length property, an empty array will be returned.
 * @template T
 */
goog.array.toArray = function(object) {
  var length = object.length;

  // If length is not a number the following is false. This case is kept for
  // backwards compatibility since there are callers that pass objects that are
  // not array like.
  if (length > 0) {
    var rv = new Array(length);
    for (var i = 0; i < length; i++) {
      rv[i] = object[i];
    }
    return rv;
  }
  return [];
};


/**
 * Does a shallow copy of an array.
 * @param {IArrayLike<T>|string} arr  Array or array-like object to
 *     clone.
 * @return {!Array<T>} Clone of the input array.
 * @template T
 */
goog.array.clone = goog.array.toArray;


/**
 * Extends an array with another array, element, or "array like" object.
 * This function operates 'in-place', it does not create a new Array.
 *
 * Example:
 * var a = [];
 * goog.array.extend(a, [0, 1]);
 * a; // [0, 1]
 * goog.array.extend(a, 2);
 * a; // [0, 1, 2]
 *
 * @param {Array<VALUE>} arr1  The array to modify.
 * @param {...(IArrayLike<VALUE>|VALUE)} var_args The elements or arrays of
 *     elements to add to arr1.
 * @template VALUE
 */
goog.array.extend = function(arr1, var_args) {
  for (var i = 1; i < arguments.length; i++) {
    var arr2 = arguments[i];
    if (goog.isArrayLike(arr2)) {
      var len1 = arr1.length || 0;
      var len2 = arr2.length || 0;
      arr1.length = len1 + len2;
      for (var j = 0; j < len2; j++) {
        arr1[len1 + j] = arr2[j];
      }
    } else {
      arr1.push(arr2);
    }
  }
};


/**
 * Adds or removes elements from an array. This is a generic version of Array
 * splice. This means that it might work on other objects similar to arrays,
 * such as the arguments object.
 *
 * @param {IArrayLike<T>} arr The array to modify.
 * @param {number|undefined} index The index at which to start changing the
 *     array. If not defined, treated as 0.
 * @param {number} howMany How many elements to remove (0 means no removal. A
 *     value below 0 is treated as zero and so is any other non number. Numbers
 *     are floored).
 * @param {...T} var_args Optional, additional elements to insert into the
 *     array.
 * @return {!Array<T>} the removed elements.
 * @template T
 */
goog.array.splice = function(arr, index, howMany, var_args) {
  goog.asserts.assert(arr.length != null);

  return Array.prototype.splice.apply(arr, goog.array.slice(arguments, 1));
};


/**
 * Returns a new array from a segment of an array. This is a generic version of
 * Array slice. This means that it might work on other objects similar to
 * arrays, such as the arguments object.
 *
 * @param {IArrayLike<T>|string} arr The array from
 * which to copy a segment.
 * @param {number} start The index of the first element to copy.
 * @param {number=} opt_end The index after the last element to copy.
 * @return {!Array<T>} A new array containing the specified segment of the
 *     original array.
 * @template T
 */
goog.array.slice = function(arr, start, opt_end) {
  goog.asserts.assert(arr.length != null);

  // passing 1 arg to slice is not the same as passing 2 where the second is
  // null or undefined (in that case the second argument is treated as 0).
  // we could use slice on the arguments object and then use apply instead of
  // testing the length
  if (arguments.length <= 2) {
    return Array.prototype.slice.call(arr, start);
  } else {
    return Array.prototype.slice.call(arr, start, opt_end);
  }
};


/**
 * Removes all duplicates from an array (retaining only the first
 * occurrence of each array element).  This function modifies the
 * array in place and doesn't change the order of the non-duplicate items.
 *
 * For objects, duplicates are identified as having the same unique ID as
 * defined by {@link goog.getUid}.
 *
 * Alternatively you can specify a custom hash function that returns a unique
 * value for each item in the array it should consider unique.
 *
 * Runtime: N,
 * Worstcase space: 2N (no dupes)
 *
 * @param {IArrayLike<T>} arr The array from which to remove
 *     duplicates.
 * @param {Array=} opt_rv An optional array in which to return the results,
 *     instead of performing the removal inplace.  If specified, the original
 *     array will remain unchanged.
 * @param {function(T):string=} opt_hashFn An optional function to use to
 *     apply to every item in the array. This function should return a unique
 *     value for each item in the array it should consider unique.
 * @template T
 */
goog.array.removeDuplicates = function(arr, opt_rv, opt_hashFn) {
  var returnArray = opt_rv || arr;
  var defaultHashFn = function(item) {
    // Prefix each type with a single character representing the type to
    // prevent conflicting keys (e.g. true and 'true').
    return goog.isObject(item) ? 'o' + goog.getUid(item) :
                                 (typeof item).charAt(0) + item;
  };
  var hashFn = opt_hashFn || defaultHashFn;

  var seen = {}, cursorInsert = 0, cursorRead = 0;
  while (cursorRead < arr.length) {
    var current = arr[cursorRead++];
    var key = hashFn(current);
    if (!Object.prototype.hasOwnProperty.call(seen, key)) {
      seen[key] = true;
      returnArray[cursorInsert++] = current;
    }
  }
  returnArray.length = cursorInsert;
};


/**
 * Searches the specified array for the specified target using the binary
 * search algorithm.  If no opt_compareFn is specified, elements are compared
 * using <code>goog.array.defaultCompare</code>, which compares the elements
 * using the built in < and > operators.  This will produce the expected
 * behavior for homogeneous arrays of String(s) and Number(s). The array
 * specified <b>must</b> be sorted in ascending order (as defined by the
 * comparison function).  If the array is not sorted, results are undefined.
 * If the array contains multiple instances of the specified target value, the
 * left-most instance will be found.
 *
 * Runtime: O(log n)
 *
 * @param {IArrayLike<VALUE>} arr The array to be searched.
 * @param {TARGET} target The sought value.
 * @param {function(TARGET, VALUE): number=} opt_compareFn Optional comparison
 *     function by which the array is ordered. Should take 2 arguments to
 *     compare, the target value and an element from your array, and return a
 *     negative number, zero, or a positive number depending on whether the
 *     first argument is less than, equal to, or greater than the second.
 * @return {number} Lowest index of the target value if found, otherwise
 *     (-(insertion point) - 1). The insertion point is where the value should
 *     be inserted into arr to preserve the sorted property.  Return value >= 0
 *     iff target is found.
 * @template TARGET, VALUE
 */
goog.array.binarySearch = function(arr, target, opt_compareFn) {
  return goog.array.binarySearch_(
      arr, opt_compareFn || goog.array.defaultCompare, false /* isEvaluator */,
      target);
};


/**
 * Selects an index in the specified array using the binary search algorithm.
 * The evaluator receives an element and determines whether the desired index
 * is before, at, or after it.  The evaluator must be consistent (formally,
 * goog.array.map(goog.array.map(arr, evaluator, opt_obj), goog.math.sign)
 * must be monotonically non-increasing).
 *
 * Runtime: O(log n)
 *
 * @param {IArrayLike<VALUE>} arr The array to be searched.
 * @param {function(this:THIS, VALUE, number, ?): number} evaluator
 *     Evaluator function that receives 3 arguments (the element, the index and
 *     the array). Should return a negative number, zero, or a positive number
 *     depending on whether the desired index is before, at, or after the
 *     element passed to it.
 * @param {THIS=} opt_obj The object to be used as the value of 'this'
 *     within evaluator.
 * @return {number} Index of the leftmost element matched by the evaluator, if
 *     such exists; otherwise (-(insertion point) - 1). The insertion point is
 *     the index of the first element for which the evaluator returns negative,
 *     or arr.length if no such element exists. The return value is non-negative
 *     iff a match is found.
 * @template THIS, VALUE
 */
goog.array.binarySelect = function(arr, evaluator, opt_obj) {
  return goog.array.binarySearch_(
      arr, evaluator, true /* isEvaluator */, undefined /* opt_target */,
      opt_obj);
};


/**
 * Implementation of a binary search algorithm which knows how to use both
 * comparison functions and evaluators. If an evaluator is provided, will call
 * the evaluator with the given optional data object, conforming to the
 * interface defined in binarySelect. Otherwise, if a comparison function is
 * provided, will call the comparison function against the given data object.
 *
 * This implementation purposefully does not use goog.bind or goog.partial for
 * performance reasons.
 *
 * Runtime: O(log n)
 *
 * @param {IArrayLike<?>} arr The array to be searched.
 * @param {function(?, ?, ?): number | function(?, ?): number} compareFn
 *     Either an evaluator or a comparison function, as defined by binarySearch
 *     and binarySelect above.
 * @param {boolean} isEvaluator Whether the function is an evaluator or a
 *     comparison function.
 * @param {?=} opt_target If the function is a comparison function, then
 *     this is the target to binary search for.
 * @param {Object=} opt_selfObj If the function is an evaluator, this is an
 *     optional this object for the evaluator.
 * @return {number} Lowest index of the target value if found, otherwise
 *     (-(insertion point) - 1). The insertion point is where the value should
 *     be inserted into arr to preserve the sorted property.  Return value >= 0
 *     iff target is found.
 * @private
 */
goog.array.binarySearch_ = function(
    arr, compareFn, isEvaluator, opt_target, opt_selfObj) {
  var left = 0;            // inclusive
  var right = arr.length;  // exclusive
  var found;
  while (left < right) {
    var middle = left + ((right - left) >>> 1);
    var compareResult;
    if (isEvaluator) {
      compareResult = compareFn.call(opt_selfObj, arr[middle], middle, arr);
    } else {
      // NOTE(dimvar): To avoid this cast, we'd have to use function overloading
      // for the type of binarySearch_, which the type system can't express yet.
      compareResult = /** @type {function(?, ?): number} */ (compareFn)(
          opt_target, arr[middle]);
    }
    if (compareResult > 0) {
      left = middle + 1;
    } else {
      right = middle;
      // We are looking for the lowest index so we can't return immediately.
      found = !compareResult;
    }
  }
  // left is the index if found, or the insertion point otherwise.
  // Avoiding bitwise not operator, as that causes a loss in precision for array
  // indexes outside the bounds of a 32-bit signed integer.  Array indexes have
  // a maximum value of 2^32-2 https://tc39.es/ecma262/#array-index
  return found ? left : -left - 1;
};


/**
 * Sorts the specified array into ascending order.  If no opt_compareFn is
 * specified, elements are compared using
 * <code>goog.array.defaultCompare</code>, which compares the elements using
 * the built in < and > operators.  This will produce the expected behavior
 * for homogeneous arrays of String(s) and Number(s), unlike the native sort,
 * but will give unpredictable results for heterogeneous lists of strings and
 * numbers with different numbers of digits.
 *
 * This sort is not guaranteed to be stable.
 *
 * Runtime: Same as <code>Array.prototype.sort</code>
 *
 * @param {Array<T>} arr The array to be sorted.
 * @param {?function(T,T):number=} opt_compareFn Optional comparison
 *     function by which the
 *     array is to be ordered. Should take 2 arguments to compare, and return a
 *     negative number, zero, or a positive number depending on whether the
 *     first argument is less than, equal to, or greater than the second.
 * @template T
 */
goog.array.sort = function(arr, opt_compareFn) {
  // TODO(arv): Update type annotation since null is not accepted.
  arr.sort(opt_compareFn || goog.array.defaultCompare);
};


/**
 * Sorts the specified array into ascending order in a stable way.  If no
 * opt_compareFn is specified, elements are compared using
 * <code>goog.array.defaultCompare</code>, which compares the elements using
 * the built in < and > operators.  This will produce the expected behavior
 * for homogeneous arrays of String(s) and Number(s).
 *
 * Runtime: Same as <code>Array.prototype.sort</code>, plus an additional
 * O(n) overhead of copying the array twice.
 *
 * @param {Array<T>} arr The array to be sorted.
 * @param {?function(T, T): number=} opt_compareFn Optional comparison function
 *     by which the array is to be ordered. Should take 2 arguments to compare,
 *     and return a negative number, zero, or a positive number depending on
 *     whether the first argument is less than, equal to, or greater than the
 *     second.
 * @template T
 */
goog.array.stableSort = function(arr, opt_compareFn) {
  var compArr = new Array(arr.length);
  for (var i = 0; i < arr.length; i++) {
    compArr[i] = {index: i, value: arr[i]};
  }
  var valueCompareFn = opt_compareFn || goog.array.defaultCompare;
  function stableCompareFn(obj1, obj2) {
    return valueCompareFn(obj1.value, obj2.value) || obj1.index - obj2.index;
  }
  goog.array.sort(compArr, stableCompareFn);
  for (var i = 0; i < arr.length; i++) {
    arr[i] = compArr[i].value;
  }
};


/**
 * Sort the specified array into ascending order based on item keys
 * returned by the specified key function.
 * If no opt_compareFn is specified, the keys are compared in ascending order
 * using <code>goog.array.defaultCompare</code>.
 *
 * Runtime: O(S(f(n)), where S is runtime of <code>goog.array.sort</code>
 * and f(n) is runtime of the key function.
 *
 * @param {Array<T>} arr The array to be sorted.
 * @param {function(T): K} keyFn Function taking array element and returning
 *     a key used for sorting this element.
 * @param {?function(K, K): number=} opt_compareFn Optional comparison function
 *     by which the keys are to be ordered. Should take 2 arguments to compare,
 *     and return a negative number, zero, or a positive number depending on
 *     whether the first argument is less than, equal to, or greater than the
 *     second.
 * @template T,K
 */
goog.array.sortByKey = function(arr, keyFn, opt_compareFn) {
  var keyCompareFn = opt_compareFn || goog.array.defaultCompare;
  goog.array.sort(
      arr, function(a, b) { return keyCompareFn(keyFn(a), keyFn(b)); });
};


/**
 * Sorts an array of objects by the specified object key and compare
 * function. If no compare function is provided, the key values are
 * compared in ascending order using <code>goog.array.defaultCompare</code>.
 * This won't work for keys that get renamed by the compiler. So use
 * {'foo': 1, 'bar': 2} rather than {foo: 1, bar: 2}.
 * @param {Array<Object>} arr An array of objects to sort.
 * @param {string} key The object key to sort by.
 * @param {Function=} opt_compareFn The function to use to compare key
 *     values.
 */
goog.array.sortObjectsByKey = function(arr, key, opt_compareFn) {
  goog.array.sortByKey(arr, function(obj) { return obj[key]; }, opt_compareFn);
};


/**
 * Tells if the array is sorted.
 * @param {!IArrayLike<T>} arr The array.
 * @param {?function(T,T):number=} opt_compareFn Function to compare the
 *     array elements.
 *     Should take 2 arguments to compare, and return a negative number, zero,
 *     or a positive number depending on whether the first argument is less
 *     than, equal to, or greater than the second.
 * @param {boolean=} opt_strict If true no equal elements are allowed.
 * @return {boolean} Whether the array is sorted.
 * @template T
 */
goog.array.isSorted = function(arr, opt_compareFn, opt_strict) {
  var compare = opt_compareFn || goog.array.defaultCompare;
  for (var i = 1; i < arr.length; i++) {
    var compareResult = compare(arr[i - 1], arr[i]);
    if (compareResult > 0 || compareResult == 0 && opt_strict) {
      return false;
    }
  }
  return true;
};


/**
 * Compares two arrays for equality. Two arrays are considered equal if they
 * have the same length and their corresponding elements are equal according to
 * the comparison function.
 *
 * @param {IArrayLike<?>} arr1 The first array to compare.
 * @param {IArrayLike<?>} arr2 The second array to compare.
 * @param {Function=} opt_equalsFn Optional comparison function.
 *     Should take 2 arguments to compare, and return true if the arguments
 *     are equal. Defaults to {@link goog.array.defaultCompareEquality} which
 *     compares the elements using the built-in '===' operator.
 * @return {boolean} Whether the two arrays are equal.
 */
goog.array.equals = function(arr1, arr2, opt_equalsFn) {
  if (!goog.isArrayLike(arr1) || !goog.isArrayLike(arr2) ||
      arr1.length != arr2.length) {
    return false;
  }
  var l = arr1.length;
  var equalsFn = opt_equalsFn || goog.array.defaultCompareEquality;
  for (var i = 0; i < l; i++) {
    if (!equalsFn(arr1[i], arr2[i])) {
      return false;
    }
  }
  return true;
};


/**
 * 3-way array compare function.
 * @param {!IArrayLike<VALUE>} arr1 The first array to
 *     compare.
 * @param {!IArrayLike<VALUE>} arr2 The second array to
 *     compare.
 * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison
 *     function by which the array is to be ordered. Should take 2 arguments to
 *     compare, and return a negative number, zero, or a positive number
 *     depending on whether the first argument is less than, equal to, or
 *     greater than the second.
 * @return {number} Negative number, zero, or a positive number depending on
 *     whether the first argument is less than, equal to, or greater than the
 *     second.
 * @template VALUE
 */
goog.array.compare3 = function(arr1, arr2, opt_compareFn) {
  var compare = opt_compareFn || goog.array.defaultCompare;
  var l = Math.min(arr1.length, arr2.length);
  for (var i = 0; i < l; i++) {
    var result = compare(arr1[i], arr2[i]);
    if (result != 0) {
      return result;
    }
  }
  return goog.array.defaultCompare(arr1.length, arr2.length);
};


/**
 * Compares its two arguments for order, using the built in < and >
 * operators.
 * @param {VALUE} a The first object to be compared.
 * @param {VALUE} b The second object to be compared.
 * @return {number} A negative number, zero, or a positive number as the first
 *     argument is less than, equal to, or greater than the second,
 *     respectively.
 * @template VALUE
 */
goog.array.defaultCompare = function(a, b) {
  return a > b ? 1 : a < b ? -1 : 0;
};


/**
 * Compares its two arguments for inverse order, using the built in < and >
 * operators.
 * @param {VALUE} a The first object to be compared.
 * @param {VALUE} b The second object to be compared.
 * @return {number} A negative number, zero, or a positive number as the first
 *     argument is greater than, equal to, or less than the second,
 *     respectively.
 * @template VALUE
 */
goog.array.inverseDefaultCompare = function(a, b) {
  return -goog.array.defaultCompare(a, b);
};


/**
 * Compares its two arguments for equality, using the built in === operator.
 * @param {*} a The first object to compare.
 * @param {*} b The second object to compare.
 * @return {boolean} True if the two arguments are equal, false otherwise.
 */
goog.array.defaultCompareEquality = function(a, b) {
  return a === b;
};


/**
 * Inserts a value into a sorted array. The array is not modified if the
 * value is already present.
 * @param {IArrayLike<VALUE>} array The array to modify.
 * @param {VALUE} value The object to insert.
 * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison
 *     function by which the array is ordered. Should take 2 arguments to
 *     compare, and return a negative number, zero, or a positive number
 *     depending on whether the first argument is less than, equal to, or
 *     greater than the second.
 * @return {boolean} True if an element was inserted.
 * @template VALUE
 */
goog.array.binaryInsert = function(array, value, opt_compareFn) {
  var index = goog.array.binarySearch(array, value, opt_compareFn);
  if (index < 0) {
    goog.array.insertAt(array, value, -(index + 1));
    return true;
  }
  return false;
};


/**
 * Removes a value from a sorted array.
 * @param {!IArrayLike<VALUE>} array The array to modify.
 * @param {VALUE} value The object to remove.
 * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison
 *     function by which the array is ordered. Should take 2 arguments to
 *     compare, and return a negative number, zero, or a positive number
 *     depending on whether the first argument is less than, equal to, or
 *     greater than the second.
 * @return {boolean} True if an element was removed.
 * @template VALUE
 */
goog.array.binaryRemove = function(array, value, opt_compareFn) {
  var index = goog.array.binarySearch(array, value, opt_compareFn);
  return (index >= 0) ? goog.array.removeAt(array, index) : false;
};


/**
 * Splits an array into disjoint buckets according to a splitting function.
 * @param {IArrayLike<T>} array The array.
 * @param {function(this:S, T, number, !IArrayLike<T>):?} sorter Function to
 *     call for every element.  This takes 3 arguments (the element, the index
 *     and the array) and must return a valid object key (a string, number,
 *     etc), or undefined, if that object should not be placed in a bucket.
 * @param {S=} opt_obj The object to be used as the value of 'this' within
 *     sorter.
 * @return {!Object<!Array<T>>} An object, with keys being all of the unique
 *     return values of sorter, and values being arrays containing the items for
 *     which the splitter returned that key.
 * @template T,S
 */
goog.array.bucket = function(array, sorter, opt_obj) {
  var buckets = {};

  for (var i = 0; i < array.length; i++) {
    var value = array[i];
    var key = sorter.call(/** @type {?} */ (opt_obj), value, i, array);
    if (key !== undefined) {
      // Push the value to the right bucket, creating it if necessary.
      var bucket = buckets[key] || (buckets[key] = []);
      bucket.push(value);
    }
  }

  return buckets;
};


/**
 * Creates a new object built from the provided array and the key-generation
 * function.
 * @param {IArrayLike<T>} arr Array or array like object over
 *     which to iterate whose elements will be the values in the new object.
 * @param {?function(this:S, T, number, ?) : string} keyFunc The function to
 *     call for every element. This function takes 3 arguments (the element, the
 *     index and the array) and should return a string that will be used as the
 *     key for the element in the new object. If the function returns the same
 *     key for more than one element, the value for that key is
 *     implementation-defined.
 * @param {S=} opt_obj The object to be used as the value of 'this'
 *     within keyFunc.
 * @return {!Object<T>} The new object.
 * @template T,S
 */
goog.array.toObject = function(arr, keyFunc, opt_obj) {
  var ret = {};
  goog.array.forEach(arr, function(element, index) {
    ret[keyFunc.call(/** @type {?} */ (opt_obj), element, index, arr)] =
        element;
  });
  return ret;
};


/**
 * Creates a range of numbers in an arithmetic progression.
 *
 * Range takes 1, 2, or 3 arguments:
 * <pre>
 * range(5) is the same as range(0, 5, 1) and produces [0, 1, 2, 3, 4]
 * range(2, 5) is the same as range(2, 5, 1) and produces [2, 3, 4]
 * range(-2, -5, -1) produces [-2, -3, -4]
 * range(-2, -5, 1) produces [], since stepping by 1 wouldn't ever reach -5.
 * </pre>
 *
 * @param {number} startOrEnd The starting value of the range if an end argument
 *     is provided. Otherwise, the start value is 0, and this is the end value.
 * @param {number=} opt_end The optional end value of the range.
 * @param {number=} opt_step The step size between range values. Defaults to 1
 *     if opt_step is undefined or 0.
 * @return {!Array<number>} An array of numbers for the requested range. May be
 *     an empty array if adding the step would not converge toward the end
 *     value.
 */
goog.array.range = function(startOrEnd, opt_end, opt_step) {
  var array = [];
  var start = 0;
  var end = startOrEnd;
  var step = opt_step || 1;
  if (opt_end !== undefined) {
    start = startOrEnd;
    end = opt_end;
  }

  if (step * (end - start) < 0) {
    // Sign mismatch: start + step will never reach the end value.
    return [];
  }

  if (step > 0) {
    for (var i = start; i < end; i += step) {
      array.push(i);
    }
  } else {
    for (var i = start; i > end; i += step) {
      array.push(i);
    }
  }
  return array;
};


/**
 * Returns an array consisting of the given value repeated N times.
 *
 * @param {VALUE} value The value to repeat.
 * @param {number} n The repeat count.
 * @return {!Array<VALUE>} An array with the repeated value.
 * @template VALUE
 */
goog.array.repeat = function(value, n) {
  var array = [];
  for (var i = 0; i < n; i++) {
    array[i] = value;
  }
  return array;
};


/**
 * Returns an array consisting of every argument with all arrays
 * expanded in-place recursively.
 *
 * @param {...*} var_args The values to flatten.
 * @return {!Array<?>} An array containing the flattened values.
 */
goog.array.flatten = function(var_args) {
  var CHUNK_SIZE = 8192;

  var result = [];
  for (var i = 0; i < arguments.length; i++) {
    var element = arguments[i];
    if (Array.isArray(element)) {
      for (var c = 0; c < element.length; c += CHUNK_SIZE) {
        var chunk = goog.array.slice(element, c, c + CHUNK_SIZE);
        var recurseResult = goog.array.flatten.apply(null, chunk);
        for (var r = 0; r < recurseResult.length; r++) {
          result.push(recurseResult[r]);
        }
      }
    } else {
      result.push(element);
    }
  }
  return result;
};


/**
 * Rotates an array in-place. After calling this method, the element at
 * index i will be the element previously at index (i - n) %
 * array.length, for all values of i between 0 and array.length - 1,
 * inclusive.
 *
 * For example, suppose list comprises [t, a, n, k, s]. After invoking
 * rotate(array, 1) (or rotate(array, -4)), array will comprise [s, t, a, n, k].
 *
 * @param {!Array<T>} array The array to rotate.
 * @param {number} n The amount to rotate.
 * @return {!Array<T>} The array.
 * @template T
 */
goog.array.rotate = function(array, n) {
  goog.asserts.assert(array.length != null);

  if (array.length) {
    n %= array.length;
    if (n > 0) {
      Array.prototype.unshift.apply(array, array.splice(-n, n));
    } else if (n < 0) {
      Array.prototype.push.apply(array, array.splice(0, -n));
    }
  }
  return array;
};


/**
 * Moves one item of an array to a new position keeping the order of the rest
 * of the items. Example use case: keeping a list of JavaScript objects
 * synchronized with the corresponding list of DOM elements after one of the
 * elements has been dragged to a new position.
 * @param {!IArrayLike<?>} arr The array to modify.
 * @param {number} fromIndex Index of the item to move between 0 and
 *     {@code arr.length - 1}.
 * @param {number} toIndex Target index between 0 and {@code arr.length - 1}.
 */
goog.array.moveItem = function(arr, fromIndex, toIndex) {
  goog.asserts.assert(fromIndex >= 0 && fromIndex < arr.length);
  goog.asserts.assert(toIndex >= 0 && toIndex < arr.length);
  // Remove 1 item at fromIndex.
  var removedItems = Array.prototype.splice.call(arr, fromIndex, 1);
  // Insert the removed item at toIndex.
  Array.prototype.splice.call(arr, toIndex, 0, removedItems[0]);
  // We don't use goog.array.insertAt and goog.array.removeAt, because they're
  // significantly slower than splice.
};


/**
 * Creates a new array for which the element at position i is an array of the
 * ith element of the provided arrays.  The returned array will only be as long
 * as the shortest array provided; additional values are ignored.  For example,
 * the result of zipping [1, 2] and [3, 4, 5] is [[1,3], [2, 4]].
 *
 * This is similar to the zip() function in Python.  See {@link
 * http://docs.python.org/library/functions.html#zip}
 *
 * @param {...!IArrayLike<?>} var_args Arrays to be combined.
 * @return {!Array<!Array<?>>} A new array of arrays created from
 *     provided arrays.
 */
goog.array.zip = function(var_args) {
  if (!arguments.length) {
    return [];
  }
  var result = [];
  var minLen = arguments[0].length;
  for (var i = 1; i < arguments.length; i++) {
    if (arguments[i].length < minLen) {
      minLen = arguments[i].length;
    }
  }
  for (var i = 0; i < minLen; i++) {
    var value = [];
    for (var j = 0; j < arguments.length; j++) {
      value.push(arguments[j][i]);
    }
    result.push(value);
  }
  return result;
};


/**
 * Shuffles the values in the specified array using the Fisher-Yates in-place
 * shuffle (also known as the Knuth Shuffle). By default, calls Math.random()
 * and so resets the state of that random number generator. Similarly, may reset
 * the state of any other specified random number generator.
 *
 * Runtime: O(n)
 *
 * @param {!Array<?>} arr The array to be shuffled.
 * @param {function():number=} opt_randFn Optional random function to use for
 *     shuffling.
 *     Takes no arguments, and returns a random number on the interval [0, 1).
 *     Defaults to Math.random() using JavaScript's built-in Math library.
 */
goog.array.shuffle = function(arr, opt_randFn) {
  var randFn = opt_randFn || Math.random;

  for (var i = arr.length - 1; i > 0; i--) {
    // Choose a random array index in [0, i] (inclusive with i).
    var j = Math.floor(randFn() * (i + 1));

    var tmp = arr[i];
    arr[i] = arr[j];
    arr[j] = tmp;
  }
};


/**
 * Returns a new array of elements from arr, based on the indexes of elements
 * provided by index_arr. For example, the result of index copying
 * ['a', 'b', 'c'] with index_arr [1,0,0,2] is ['b', 'a', 'a', 'c'].
 *
 * @param {!IArrayLike<T>} arr The array to get a indexed copy from.
 * @param {!IArrayLike<number>} index_arr An array of indexes to get from arr.
 * @return {!Array<T>} A new array of elements from arr in index_arr order.
 * @template T
 */
goog.array.copyByIndex = function(arr, index_arr) {
  var result = [];
  goog.array.forEach(index_arr, function(index) { result.push(arr[index]); });
  return result;
};


/**
 * Maps each element of the input array into zero or more elements of the output
 * array.
 *
 * @param {!IArrayLike<VALUE>|string} arr Array or array like object
 *     over which to iterate.
 * @param {function(this:THIS, VALUE, number, ?): !Array<RESULT>} f The function
 *     to call for every element. This function takes 3 arguments (the element,
 *     the index and the array) and should return an array. The result will be
 *     used to extend a new array.
 * @param {THIS=} opt_obj The object to be used as the value of 'this' within f.
 * @return {!Array<RESULT>} a new array with the concatenation of all arrays
 *     returned from f.
 * @template THIS, VALUE, RESULT
 */
goog.array.concatMap = function(arr, f, opt_obj) {
  return goog.array.concat.apply([], goog.array.map(arr, f, opt_obj));
};

//third_party/javascript/closure/dom/htmlelement.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

goog.provide('goog.dom.HtmlElement');



/**
 * This subclass of HTMLElement is used when only a HTMLElement is possible and
 * not any of its subclasses. Normally, a type can refer to an instance of
 * itself or an instance of any subtype. More concretely, if HTMLElement is used
 * then the compiler must assume that it might still be e.g. HTMLScriptElement.
 * With this, the type check knows that it couldn't be any special element.
 *
 * @constructor
 * @extends {HTMLElement}
 */
goog.dom.HtmlElement = function() {};

//third_party/javascript/closure/dom/tagname.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Defines the goog.dom.TagName class. Its constants enumerate
 * all HTML tag names specified in either the W3C HTML 4.01 index of elements
 * or the HTML5.1 specification.
 *
 * References:
 * https://www.w3.org/TR/html401/index/elements.html
 * https://www.w3.org/TR/html51/dom.html#elements
 */
goog.provide('goog.dom.TagName');

goog.require('goog.dom.HtmlElement');


/**
 * A tag name with the type of the element stored in the generic.
 * @param {string} tagName
 * @constructor
 * @template T
 */
goog.dom.TagName = function(tagName) {
  /** @private {string} */
  this.tagName_ = tagName;
};


/**
 * Returns the tag name.
 * @return {string}
 * @override
 */
goog.dom.TagName.prototype.toString = function() {
  return this.tagName_;
};


// Closure Compiler unconditionally converts the following constants to their
// string value (goog.dom.TagName.A -> 'A'). These are the consequences:
// 1. Don't add any members or static members to goog.dom.TagName as they
//    couldn't be accessed after this optimization.
// 2. Keep the constant name and its string value the same:
//    goog.dom.TagName.X = new goog.dom.TagName('Y');
//    is converted to 'X', not 'Y'.


/** @type {!goog.dom.TagName<!HTMLAnchorElement>} */
goog.dom.TagName.A = new goog.dom.TagName('A');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.ABBR = new goog.dom.TagName('ABBR');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.ACRONYM = new goog.dom.TagName('ACRONYM');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.ADDRESS = new goog.dom.TagName('ADDRESS');


/** @type {!goog.dom.TagName<!HTMLAppletElement>} */
goog.dom.TagName.APPLET = new goog.dom.TagName('APPLET');


/** @type {!goog.dom.TagName<!HTMLAreaElement>} */
goog.dom.TagName.AREA = new goog.dom.TagName('AREA');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.ARTICLE = new goog.dom.TagName('ARTICLE');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.ASIDE = new goog.dom.TagName('ASIDE');


/** @type {!goog.dom.TagName<!HTMLAudioElement>} */
goog.dom.TagName.AUDIO = new goog.dom.TagName('AUDIO');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.B = new goog.dom.TagName('B');


/** @type {!goog.dom.TagName<!HTMLBaseElement>} */
goog.dom.TagName.BASE = new goog.dom.TagName('BASE');


/** @type {!goog.dom.TagName<!HTMLBaseFontElement>} */
goog.dom.TagName.BASEFONT = new goog.dom.TagName('BASEFONT');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.BDI = new goog.dom.TagName('BDI');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.BDO = new goog.dom.TagName('BDO');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.BIG = new goog.dom.TagName('BIG');


/** @type {!goog.dom.TagName<!HTMLQuoteElement>} */
goog.dom.TagName.BLOCKQUOTE = new goog.dom.TagName('BLOCKQUOTE');


/** @type {!goog.dom.TagName<!HTMLBodyElement>} */
goog.dom.TagName.BODY = new goog.dom.TagName('BODY');


/** @type {!goog.dom.TagName<!HTMLBRElement>} */
goog.dom.TagName.BR = new goog.dom.TagName('BR');


/** @type {!goog.dom.TagName<!HTMLButtonElement>} */
goog.dom.TagName.BUTTON = new goog.dom.TagName('BUTTON');


/** @type {!goog.dom.TagName<!HTMLCanvasElement>} */
goog.dom.TagName.CANVAS = new goog.dom.TagName('CANVAS');


/** @type {!goog.dom.TagName<!HTMLTableCaptionElement>} */
goog.dom.TagName.CAPTION = new goog.dom.TagName('CAPTION');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.CENTER = new goog.dom.TagName('CENTER');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.CITE = new goog.dom.TagName('CITE');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.CODE = new goog.dom.TagName('CODE');


/** @type {!goog.dom.TagName<!HTMLTableColElement>} */
goog.dom.TagName.COL = new goog.dom.TagName('COL');


/** @type {!goog.dom.TagName<!HTMLTableColElement>} */
goog.dom.TagName.COLGROUP = new goog.dom.TagName('COLGROUP');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.COMMAND = new goog.dom.TagName('COMMAND');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.DATA = new goog.dom.TagName('DATA');


/** @type {!goog.dom.TagName<!HTMLDataListElement>} */
goog.dom.TagName.DATALIST = new goog.dom.TagName('DATALIST');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.DD = new goog.dom.TagName('DD');


/** @type {!goog.dom.TagName<!HTMLModElement>} */
goog.dom.TagName.DEL = new goog.dom.TagName('DEL');


/** @type {!goog.dom.TagName<!HTMLDetailsElement>} */
goog.dom.TagName.DETAILS = new goog.dom.TagName('DETAILS');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.DFN = new goog.dom.TagName('DFN');


/** @type {!goog.dom.TagName<!HTMLDialogElement>} */
goog.dom.TagName.DIALOG = new goog.dom.TagName('DIALOG');


/** @type {!goog.dom.TagName<!HTMLDirectoryElement>} */
goog.dom.TagName.DIR = new goog.dom.TagName('DIR');


/** @type {!goog.dom.TagName<!HTMLDivElement>} */
goog.dom.TagName.DIV = new goog.dom.TagName('DIV');


/** @type {!goog.dom.TagName<!HTMLDListElement>} */
goog.dom.TagName.DL = new goog.dom.TagName('DL');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.DT = new goog.dom.TagName('DT');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.EM = new goog.dom.TagName('EM');


/** @type {!goog.dom.TagName<!HTMLEmbedElement>} */
goog.dom.TagName.EMBED = new goog.dom.TagName('EMBED');


/** @type {!goog.dom.TagName<!HTMLFieldSetElement>} */
goog.dom.TagName.FIELDSET = new goog.dom.TagName('FIELDSET');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.FIGCAPTION = new goog.dom.TagName('FIGCAPTION');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.FIGURE = new goog.dom.TagName('FIGURE');


/** @type {!goog.dom.TagName<!HTMLFontElement>} */
goog.dom.TagName.FONT = new goog.dom.TagName('FONT');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.FOOTER = new goog.dom.TagName('FOOTER');


/** @type {!goog.dom.TagName<!HTMLFormElement>} */
goog.dom.TagName.FORM = new goog.dom.TagName('FORM');


/** @type {!goog.dom.TagName<!HTMLFrameElement>} */
goog.dom.TagName.FRAME = new goog.dom.TagName('FRAME');


/** @type {!goog.dom.TagName<!HTMLFrameSetElement>} */
goog.dom.TagName.FRAMESET = new goog.dom.TagName('FRAMESET');


/** @type {!goog.dom.TagName<!HTMLHeadingElement>} */
goog.dom.TagName.H1 = new goog.dom.TagName('H1');


/** @type {!goog.dom.TagName<!HTMLHeadingElement>} */
goog.dom.TagName.H2 = new goog.dom.TagName('H2');


/** @type {!goog.dom.TagName<!HTMLHeadingElement>} */
goog.dom.TagName.H3 = new goog.dom.TagName('H3');


/** @type {!goog.dom.TagName<!HTMLHeadingElement>} */
goog.dom.TagName.H4 = new goog.dom.TagName('H4');


/** @type {!goog.dom.TagName<!HTMLHeadingElement>} */
goog.dom.TagName.H5 = new goog.dom.TagName('H5');


/** @type {!goog.dom.TagName<!HTMLHeadingElement>} */
goog.dom.TagName.H6 = new goog.dom.TagName('H6');


/** @type {!goog.dom.TagName<!HTMLHeadElement>} */
goog.dom.TagName.HEAD = new goog.dom.TagName('HEAD');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.HEADER = new goog.dom.TagName('HEADER');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.HGROUP = new goog.dom.TagName('HGROUP');


/** @type {!goog.dom.TagName<!HTMLHRElement>} */
goog.dom.TagName.HR = new goog.dom.TagName('HR');


/** @type {!goog.dom.TagName<!HTMLHtmlElement>} */
goog.dom.TagName.HTML = new goog.dom.TagName('HTML');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.I = new goog.dom.TagName('I');


/** @type {!goog.dom.TagName<!HTMLIFrameElement>} */
goog.dom.TagName.IFRAME = new goog.dom.TagName('IFRAME');


/** @type {!goog.dom.TagName<!HTMLImageElement>} */
goog.dom.TagName.IMG = new goog.dom.TagName('IMG');


/** @type {!goog.dom.TagName<!HTMLInputElement>} */
goog.dom.TagName.INPUT = new goog.dom.TagName('INPUT');


/** @type {!goog.dom.TagName<!HTMLModElement>} */
goog.dom.TagName.INS = new goog.dom.TagName('INS');


/** @type {!goog.dom.TagName<!HTMLIsIndexElement>} */
goog.dom.TagName.ISINDEX = new goog.dom.TagName('ISINDEX');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.KBD = new goog.dom.TagName('KBD');


// HTMLKeygenElement is deprecated.
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.KEYGEN = new goog.dom.TagName('KEYGEN');


/** @type {!goog.dom.TagName<!HTMLLabelElement>} */
goog.dom.TagName.LABEL = new goog.dom.TagName('LABEL');


/** @type {!goog.dom.TagName<!HTMLLegendElement>} */
goog.dom.TagName.LEGEND = new goog.dom.TagName('LEGEND');


/** @type {!goog.dom.TagName<!HTMLLIElement>} */
goog.dom.TagName.LI = new goog.dom.TagName('LI');


/** @type {!goog.dom.TagName<!HTMLLinkElement>} */
goog.dom.TagName.LINK = new goog.dom.TagName('LINK');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.MAIN = new goog.dom.TagName('MAIN');


/** @type {!goog.dom.TagName<!HTMLMapElement>} */
goog.dom.TagName.MAP = new goog.dom.TagName('MAP');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.MARK = new goog.dom.TagName('MARK');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.MATH = new goog.dom.TagName('MATH');


/** @type {!goog.dom.TagName<!HTMLMenuElement>} */
goog.dom.TagName.MENU = new goog.dom.TagName('MENU');


/** @type {!goog.dom.TagName<!HTMLMenuItemElement>} */
goog.dom.TagName.MENUITEM = new goog.dom.TagName('MENUITEM');


/** @type {!goog.dom.TagName<!HTMLMetaElement>} */
goog.dom.TagName.META = new goog.dom.TagName('META');


/** @type {!goog.dom.TagName<!HTMLMeterElement>} */
goog.dom.TagName.METER = new goog.dom.TagName('METER');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.NAV = new goog.dom.TagName('NAV');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.NOFRAMES = new goog.dom.TagName('NOFRAMES');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.NOSCRIPT = new goog.dom.TagName('NOSCRIPT');


/** @type {!goog.dom.TagName<!HTMLObjectElement>} */
goog.dom.TagName.OBJECT = new goog.dom.TagName('OBJECT');


/** @type {!goog.dom.TagName<!HTMLOListElement>} */
goog.dom.TagName.OL = new goog.dom.TagName('OL');


/** @type {!goog.dom.TagName<!HTMLOptGroupElement>} */
goog.dom.TagName.OPTGROUP = new goog.dom.TagName('OPTGROUP');


/** @type {!goog.dom.TagName<!HTMLOptionElement>} */
goog.dom.TagName.OPTION = new goog.dom.TagName('OPTION');


/** @type {!goog.dom.TagName<!HTMLOutputElement>} */
goog.dom.TagName.OUTPUT = new goog.dom.TagName('OUTPUT');


/** @type {!goog.dom.TagName<!HTMLParagraphElement>} */
goog.dom.TagName.P = new goog.dom.TagName('P');


/** @type {!goog.dom.TagName<!HTMLParamElement>} */
goog.dom.TagName.PARAM = new goog.dom.TagName('PARAM');


/** @type {!goog.dom.TagName<!HTMLPictureElement>} */
goog.dom.TagName.PICTURE = new goog.dom.TagName('PICTURE');


/** @type {!goog.dom.TagName<!HTMLPreElement>} */
goog.dom.TagName.PRE = new goog.dom.TagName('PRE');


/** @type {!goog.dom.TagName<!HTMLProgressElement>} */
goog.dom.TagName.PROGRESS = new goog.dom.TagName('PROGRESS');


/** @type {!goog.dom.TagName<!HTMLQuoteElement>} */
goog.dom.TagName.Q = new goog.dom.TagName('Q');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.RP = new goog.dom.TagName('RP');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.RT = new goog.dom.TagName('RT');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.RTC = new goog.dom.TagName('RTC');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.RUBY = new goog.dom.TagName('RUBY');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.S = new goog.dom.TagName('S');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.SAMP = new goog.dom.TagName('SAMP');


/** @type {!goog.dom.TagName<!HTMLScriptElement>} */
goog.dom.TagName.SCRIPT = new goog.dom.TagName('SCRIPT');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.SECTION = new goog.dom.TagName('SECTION');


/** @type {!goog.dom.TagName<!HTMLSelectElement>} */
goog.dom.TagName.SELECT = new goog.dom.TagName('SELECT');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.SMALL = new goog.dom.TagName('SMALL');


/** @type {!goog.dom.TagName<!HTMLSourceElement>} */
goog.dom.TagName.SOURCE = new goog.dom.TagName('SOURCE');


/** @type {!goog.dom.TagName<!HTMLSpanElement>} */
goog.dom.TagName.SPAN = new goog.dom.TagName('SPAN');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.STRIKE = new goog.dom.TagName('STRIKE');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.STRONG = new goog.dom.TagName('STRONG');


/** @type {!goog.dom.TagName<!HTMLStyleElement>} */
goog.dom.TagName.STYLE = new goog.dom.TagName('STYLE');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.SUB = new goog.dom.TagName('SUB');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.SUMMARY = new goog.dom.TagName('SUMMARY');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.SUP = new goog.dom.TagName('SUP');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.SVG = new goog.dom.TagName('SVG');


/** @type {!goog.dom.TagName<!HTMLTableElement>} */
goog.dom.TagName.TABLE = new goog.dom.TagName('TABLE');


/** @type {!goog.dom.TagName<!HTMLTableSectionElement>} */
goog.dom.TagName.TBODY = new goog.dom.TagName('TBODY');


/** @type {!goog.dom.TagName<!HTMLTableCellElement>} */
goog.dom.TagName.TD = new goog.dom.TagName('TD');


/** @type {!goog.dom.TagName<!HTMLTemplateElement>} */
goog.dom.TagName.TEMPLATE = new goog.dom.TagName('TEMPLATE');


/** @type {!goog.dom.TagName<!HTMLTextAreaElement>} */
goog.dom.TagName.TEXTAREA = new goog.dom.TagName('TEXTAREA');


/** @type {!goog.dom.TagName<!HTMLTableSectionElement>} */
goog.dom.TagName.TFOOT = new goog.dom.TagName('TFOOT');


/** @type {!goog.dom.TagName<!HTMLTableCellElement>} */
goog.dom.TagName.TH = new goog.dom.TagName('TH');


/** @type {!goog.dom.TagName<!HTMLTableSectionElement>} */
goog.dom.TagName.THEAD = new goog.dom.TagName('THEAD');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.TIME = new goog.dom.TagName('TIME');


/** @type {!goog.dom.TagName<!HTMLTitleElement>} */
goog.dom.TagName.TITLE = new goog.dom.TagName('TITLE');


/** @type {!goog.dom.TagName<!HTMLTableRowElement>} */
goog.dom.TagName.TR = new goog.dom.TagName('TR');


/** @type {!goog.dom.TagName<!HTMLTrackElement>} */
goog.dom.TagName.TRACK = new goog.dom.TagName('TRACK');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.TT = new goog.dom.TagName('TT');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.U = new goog.dom.TagName('U');


/** @type {!goog.dom.TagName<!HTMLUListElement>} */
goog.dom.TagName.UL = new goog.dom.TagName('UL');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.VAR = new goog.dom.TagName('VAR');


/** @type {!goog.dom.TagName<!HTMLVideoElement>} */
goog.dom.TagName.VIDEO = new goog.dom.TagName('VIDEO');


/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.WBR = new goog.dom.TagName('WBR');

//third_party/javascript/closure/object/object.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Utilities for manipulating objects/maps/hashes.
 */

goog.provide('goog.object');


/**
 * Whether two values are not observably distinguishable. This
 * correctly detects that 0 is not the same as -0 and two NaNs are
 * practically equivalent.
 *
 * The implementation is as suggested by harmony:egal proposal.
 *
 * @param {*} v The first value to compare.
 * @param {*} v2 The second value to compare.
 * @return {boolean} Whether two values are not observably distinguishable.
 * @see http://wiki.ecmascript.org/doku.php?id=harmony:egal
 */
goog.object.is = function(v, v2) {
  if (v === v2) {
    // 0 === -0, but they are not identical.
    // We need the cast because the compiler requires that v2 is a
    // number (although 1/v2 works with non-number). We cast to ? to
    // stop the compiler from type-checking this statement.
    return v !== 0 || 1 / v === 1 / /** @type {?} */ (v2);
  }

  // NaN is non-reflexive: NaN !== NaN, although they are identical.
  return v !== v && v2 !== v2;
};


/**
 * Calls a function for each element in an object/map/hash.
 *
 * @param {Object<K,V>} obj The object over which to iterate.
 * @param {function(this:T,V,?,Object<K,V>):?} f The function to call
 *     for every element. This function takes 3 arguments (the value, the
 *     key and the object) and the return value is ignored.
 * @param {T=} opt_obj This is used as the 'this' object within f.
 * @template T,K,V
 */
goog.object.forEach = function(obj, f, opt_obj) {
  for (const key in obj) {
    f.call(/** @type {?} */ (opt_obj), obj[key], key, obj);
  }
};


/**
 * Calls a function for each element in an object/map/hash. If that call returns
 * true, adds the element to a new object.
 *
 * @param {Object<K,V>} obj The object over which to iterate.
 * @param {function(this:T,V,?,Object<K,V>):boolean} f The function to call
 *     for every element. This
 *     function takes 3 arguments (the value, the key and the object)
 *     and should return a boolean. If the return value is true the
 *     element is added to the result object. If it is false the
 *     element is not included.
 * @param {T=} opt_obj This is used as the 'this' object within f.
 * @return {!Object<K,V>} a new object in which only elements that passed the
 *     test are present.
 * @template T,K,V
 */
goog.object.filter = function(obj, f, opt_obj) {
  const res = {};
  for (const key in obj) {
    if (f.call(/** @type {?} */ (opt_obj), obj[key], key, obj)) {
      res[key] = obj[key];
    }
  }
  return res;
};


/**
 * For every element in an object/map/hash calls a function and inserts the
 * result into a new object.
 *
 * @param {Object<K,V>} obj The object over which to iterate.
 * @param {function(this:T,V,?,Object<K,V>):R} f The function to call
 *     for every element. This function
 *     takes 3 arguments (the value, the key and the object)
 *     and should return something. The result will be inserted
 *     into a new object.
 * @param {T=} opt_obj This is used as the 'this' object within f.
 * @return {!Object<K,R>} a new object with the results from f.
 * @template T,K,V,R
 */
goog.object.map = function(obj, f, opt_obj) {
  const res = {};
  for (const key in obj) {
    res[key] = f.call(/** @type {?} */ (opt_obj), obj[key], key, obj);
  }
  return res;
};


/**
 * Calls a function for each element in an object/map/hash. If any
 * call returns true, returns true (without checking the rest). If
 * all calls return false, returns false.
 *
 * @param {Object<K,V>} obj The object to check.
 * @param {function(this:T,V,?,Object<K,V>):boolean} f The function to
 *     call for every element. This function
 *     takes 3 arguments (the value, the key and the object) and should
 *     return a boolean.
 * @param {T=} opt_obj This is used as the 'this' object within f.
 * @return {boolean} true if any element passes the test.
 * @template T,K,V
 */
goog.object.some = function(obj, f, opt_obj) {
  for (const key in obj) {
    if (f.call(/** @type {?} */ (opt_obj), obj[key], key, obj)) {
      return true;
    }
  }
  return false;
};


/**
 * Calls a function for each element in an object/map/hash. If
 * all calls return true, returns true. If any call returns false, returns
 * false at this point and does not continue to check the remaining elements.
 *
 * @param {Object<K,V>} obj The object to check.
 * @param {?function(this:T,V,?,Object<K,V>):boolean} f The function to
 *     call for every element. This function
 *     takes 3 arguments (the value, the key and the object) and should
 *     return a boolean.
 * @param {T=} opt_obj This is used as the 'this' object within f.
 * @return {boolean} false if any element fails the test.
 * @template T,K,V
 */
goog.object.every = function(obj, f, opt_obj) {
  for (const key in obj) {
    if (!f.call(/** @type {?} */ (opt_obj), obj[key], key, obj)) {
      return false;
    }
  }
  return true;
};


/**
 * Returns the number of key-value pairs in the object map.
 *
 * @param {Object} obj The object for which to get the number of key-value
 *     pairs.
 * @return {number} The number of key-value pairs in the object map.
 */
goog.object.getCount = function(obj) {
  let rv = 0;
  for (const key in obj) {
    rv++;
  }
  return rv;
};


/**
 * Returns one key from the object map, if any exists.
 * For map literals the returned key will be the first one in most of the
 * browsers (a know exception is Konqueror).
 *
 * @param {Object} obj The object to pick a key from.
 * @return {string|undefined} The key or undefined if the object is empty.
 */
goog.object.getAnyKey = function(obj) {
  for (const key in obj) {
    return key;
  }
};


/**
 * Returns one value from the object map, if any exists.
 * For map literals the returned value will be the first one in most of the
 * browsers (a know exception is Konqueror).
 *
 * @param {Object<K,V>} obj The object to pick a value from.
 * @return {V|undefined} The value or undefined if the object is empty.
 * @template K,V
 */
goog.object.getAnyValue = function(obj) {
  for (const key in obj) {
    return obj[key];
  }
};


/**
 * Whether the object/hash/map contains the given object as a value.
 * An alias for goog.object.containsValue(obj, val).
 *
 * @param {Object<K,V>} obj The object in which to look for val.
 * @param {V} val The object for which to check.
 * @return {boolean} true if val is present.
 * @template K,V
 */
goog.object.contains = function(obj, val) {
  return goog.object.containsValue(obj, val);
};


/**
 * Returns the values of the object/map/hash.
 *
 * @param {Object<K,V>} obj The object from which to get the values.
 * @return {!Array<V>} The values in the object/map/hash.
 * @template K,V
 */
goog.object.getValues = function(obj) {
  const res = [];
  let i = 0;
  for (const key in obj) {
    res[i++] = obj[key];
  }
  return res;
};


/**
 * Returns the keys of the object/map/hash.
 *
 * @param {Object} obj The object from which to get the keys.
 * @return {!Array<string>} Array of property keys.
 */
goog.object.getKeys = function(obj) {
  const res = [];
  let i = 0;
  for (const key in obj) {
    res[i++] = key;
  }
  return res;
};


/**
 * Get a value from an object multiple levels deep.  This is useful for
 * pulling values from deeply nested objects, such as JSON responses.
 * Example usage: getValueByKeys(jsonObj, 'foo', 'entries', 3)
 *
 * @param {!Object} obj An object to get the value from.  Can be array-like.
 * @param {...(string|number|!IArrayLike<number|string>)}
 *     var_args A number of keys
 *     (as strings, or numbers, for array-like objects).  Can also be
 *     specified as a single array of keys.
 * @return {*} The resulting value.  If, at any point, the value for a key
 *     in the current object is null or undefined, returns undefined.
 */
goog.object.getValueByKeys = function(obj, var_args) {
  const isArrayLike = goog.isArrayLike(var_args);
  const keys = isArrayLike ?
      /** @type {!IArrayLike<number|string>} */ (var_args) :
      arguments;

  // Start with the 2nd parameter for the variable parameters syntax.
  for (let i = isArrayLike ? 0 : 1; i < keys.length; i++) {
    if (obj == null) return undefined;
    obj = obj[keys[i]];
  }

  return obj;
};


/**
 * Whether the object/map/hash contains the given key.
 *
 * @param {Object} obj The object in which to look for key.
 * @param {?} key The key for which to check.
 * @return {boolean} true If the map contains the key.
 */
goog.object.containsKey = function(obj, key) {
  return obj !== null && key in obj;
};


/**
 * Whether the object/map/hash contains the given value. This is O(n).
 *
 * @param {Object<K,V>} obj The object in which to look for val.
 * @param {V} val The value for which to check.
 * @return {boolean} true If the map contains the value.
 * @template K,V
 */
goog.object.containsValue = function(obj, val) {
  for (const key in obj) {
    if (obj[key] == val) {
      return true;
    }
  }
  return false;
};


/**
 * Searches an object for an element that satisfies the given condition and
 * returns its key.
 * @param {Object<K,V>} obj The object to search in.
 * @param {function(this:T,V,string,Object<K,V>):boolean} f The
 *      function to call for every element. Takes 3 arguments (the value,
 *     the key and the object) and should return a boolean.
 * @param {T=} opt_this An optional "this" context for the function.
 * @return {string|undefined} The key of an element for which the function
 *     returns true or undefined if no such element is found.
 * @template T,K,V
 */
goog.object.findKey = function(obj, f, opt_this) {
  for (const key in obj) {
    if (f.call(/** @type {?} */ (opt_this), obj[key], key, obj)) {
      return key;
    }
  }
  return undefined;
};


/**
 * Searches an object for an element that satisfies the given condition and
 * returns its value.
 * @param {Object<K,V>} obj The object to search in.
 * @param {function(this:T,V,string,Object<K,V>):boolean} f The function
 *     to call for every element. Takes 3 arguments (the value, the key
 *     and the object) and should return a boolean.
 * @param {T=} opt_this An optional "this" context for the function.
 * @return {V} The value of an element for which the function returns true or
 *     undefined if no such element is found.
 * @template T,K,V
 */
goog.object.findValue = function(obj, f, opt_this) {
  const key = goog.object.findKey(obj, f, opt_this);
  return key && obj[key];
};


/**
 * Whether the object/map/hash is empty.
 *
 * @param {Object} obj The object to test.
 * @return {boolean} true if obj is empty.
 */
goog.object.isEmpty = function(obj) {
  for (const key in obj) {
    return false;
  }
  return true;
};


/**
 * Removes all key value pairs from the object/map/hash.
 *
 * @param {Object} obj The object to clear.
 */
goog.object.clear = function(obj) {
  for (const i in obj) {
    delete obj[i];
  }
};


/**
 * Removes a key-value pair based on the key.
 *
 * @param {Object} obj The object from which to remove the key.
 * @param {?} key The key to remove.
 * @return {boolean} Whether an element was removed.
 */
goog.object.remove = function(obj, key) {
  let rv;
  if (rv = key in /** @type {!Object} */ (obj)) {
    delete obj[key];
  }
  return rv;
};


/**
 * Adds a key-value pair to the object. Throws an exception if the key is
 * already in use. Use set if you want to change an existing pair.
 *
 * @param {Object<K,V>} obj The object to which to add the key-value pair.
 * @param {string} key The key to add.
 * @param {V} val The value to add.
 * @template K,V
 */
goog.object.add = function(obj, key, val) {
  if (obj !== null && key in obj) {
    throw new Error('The object already contains the key "' + key + '"');
  }
  goog.object.set(obj, key, val);
};


/**
 * Returns the value for the given key.
 *
 * @param {Object<K,V>} obj The object from which to get the value.
 * @param {string} key The key for which to get the value.
 * @param {R=} opt_val The value to return if no item is found for the given
 *     key (default is undefined).
 * @return {V|R|undefined} The value for the given key.
 * @template K,V,R
 */
goog.object.get = function(obj, key, opt_val) {
  if (obj !== null && key in obj) {
    return obj[key];
  }
  return opt_val;
};


/**
 * Adds a key-value pair to the object/map/hash.
 *
 * @param {Object<K,V>} obj The object to which to add the key-value pair.
 * @param {string} key The key to add.
 * @param {V} value The value to add.
 * @template K,V
 */
goog.object.set = function(obj, key, value) {
  obj[key] = value;
};


/**
 * Adds a key-value pair to the object/map/hash if it doesn't exist yet.
 *
 * @param {Object<K,V>} obj The object to which to add the key-value pair.
 * @param {string} key The key to add.
 * @param {V} value The value to add if the key wasn't present.
 * @return {V} The value of the entry at the end of the function.
 * @template K,V
 */
goog.object.setIfUndefined = function(obj, key, value) {
  return key in /** @type {!Object} */ (obj) ? obj[key] : (obj[key] = value);
};


/**
 * Sets a key and value to an object if the key is not set. The value will be
 * the return value of the given function. If the key already exists, the
 * object will not be changed and the function will not be called (the function
 * will be lazily evaluated -- only called if necessary).
 *
 * This function is particularly useful when used with an `Object` which is
 * acting as a cache.
 *
 * @param {!Object<K,V>} obj The object to which to add the key-value pair.
 * @param {string} key The key to add.
 * @param {function():V} f The value to add if the key wasn't present.
 * @return {V} The value of the entry at the end of the function.
 * @template K,V
 */
goog.object.setWithReturnValueIfNotSet = function(obj, key, f) {
  if (key in obj) {
    return obj[key];
  }

  const val = f();
  obj[key] = val;
  return val;
};


/**
 * Compares two objects for equality using === on the values.
 *
 * @param {!Object<K,V>} a
 * @param {!Object<K,V>} b
 * @return {boolean}
 * @template K,V
 */
goog.object.equals = function(a, b) {
  for (const k in a) {
    if (!(k in b) || a[k] !== b[k]) {
      return false;
    }
  }
  for (const k in b) {
    if (!(k in a)) {
      return false;
    }
  }
  return true;
};


/**
 * Returns a shallow clone of the object.
 *
 * @param {Object<K,V>} obj Object to clone.
 * @return {!Object<K,V>} Clone of the input object.
 * @template K,V
 */
goog.object.clone = function(obj) {
  // We cannot use the prototype trick because a lot of methods depend on where
  // the actual key is set.

  const res = {};
  for (const key in obj) {
    res[key] = obj[key];
  }
  return res;
  // We could also use goog.mixin but I wanted this to be independent from that.
};


/**
 * Clones a value. The input may be an Object, Array, or basic type. Objects and
 * arrays will be cloned recursively.
 *
 * WARNINGS:
 * <code>goog.object.unsafeClone</code> does not detect reference loops. Objects
 * that refer to themselves will cause infinite recursion.
 *
 * <code>goog.object.unsafeClone</code> is unaware of unique identifiers, and
 * copies UIDs created by <code>getUid</code> into cloned results.
 *
 * @param {T} obj The value to clone.
 * @return {T} A clone of the input value.
 * @template T
 */
goog.object.unsafeClone = function(obj) {
  const type = goog.typeOf(obj);
  if (type == 'object' || type == 'array') {
    if (goog.isFunction(obj.clone)) {
      return obj.clone();
    }
    const clone = type == 'array' ? [] : {};
    for (const key in obj) {
      clone[key] = goog.object.unsafeClone(obj[key]);
    }
    return clone;
  }

  return obj;
};


/**
 * Returns a new object in which all the keys and values are interchanged
 * (keys become values and values become keys). If multiple keys map to the
 * same value, the chosen transposed value is implementation-dependent.
 *
 * @param {Object} obj The object to transpose.
 * @return {!Object} The transposed object.
 */
goog.object.transpose = function(obj) {
  const transposed = {};
  for (const key in obj) {
    transposed[obj[key]] = key;
  }
  return transposed;
};


/**
 * The names of the fields that are defined on Object.prototype.
 * @type {Array<string>}
 * @private
 */
goog.object.PROTOTYPE_FIELDS_ = [
  'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
  'toLocaleString', 'toString', 'valueOf'
];


/**
 * Extends an object with another object.
 * This operates 'in-place'; it does not create a new Object.
 *
 * Example:
 * var o = {};
 * goog.object.extend(o, {a: 0, b: 1});
 * o; // {a: 0, b: 1}
 * goog.object.extend(o, {b: 2, c: 3});
 * o; // {a: 0, b: 2, c: 3}
 *
 * @param {Object} target The object to modify. Existing properties will be
 *     overwritten if they are also present in one of the objects in
 *     `var_args`.
 * @param {...(Object|null|undefined)} var_args The objects from which values
 *     will be copied.
 * @deprecated Prefer Object.assign
 */
goog.object.extend = function(target, var_args) {
  let key;
  let source;
  for (let i = 1; i < arguments.length; i++) {
    source = arguments[i];
    for (key in source) {
      target[key] = source[key];
    }

    // For IE the for-in-loop does not contain any properties that are not
    // enumerable on the prototype object (for example isPrototypeOf from
    // Object.prototype) and it will also not include 'replace' on objects that
    // extend String and change 'replace' (not that it is common for anyone to
    // extend anything except Object).

    for (let j = 0; j < goog.object.PROTOTYPE_FIELDS_.length; j++) {
      key = goog.object.PROTOTYPE_FIELDS_[j];
      if (Object.prototype.hasOwnProperty.call(source, key)) {
        target[key] = source[key];
      }
    }
  }
};


/**
 * Creates a new object built from the key-value pairs provided as arguments.
 * @param {...*} var_args If only one argument is provided and it is an array
 *     then this is used as the arguments, otherwise even arguments are used as
 *     the property names and odd arguments are used as the property values.
 * @return {!Object} The new object.
 * @throws {Error} If there are uneven number of arguments or there is only one
 *     non array argument.
 */
goog.object.create = function(var_args) {
  const argLength = arguments.length;
  if (argLength == 1 && Array.isArray(arguments[0])) {
    return goog.object.create.apply(null, arguments[0]);
  }

  if (argLength % 2) {
    throw new Error('Uneven number of arguments');
  }

  const rv = {};
  for (let i = 0; i < argLength; i += 2) {
    rv[arguments[i]] = arguments[i + 1];
  }
  return rv;
};


/**
 * Creates a new object where the property names come from the arguments but
 * the value is always set to true
 * @param {...*} var_args If only one argument is provided and it is an array
 *     then this is used as the arguments, otherwise the arguments are used
 *     as the property names.
 * @return {!Object} The new object.
 */
goog.object.createSet = function(var_args) {
  const argLength = arguments.length;
  if (argLength == 1 && Array.isArray(arguments[0])) {
    return goog.object.createSet.apply(null, arguments[0]);
  }

  const rv = {};
  for (let i = 0; i < argLength; i++) {
    rv[arguments[i]] = true;
  }
  return rv;
};


/**
 * Creates an immutable view of the underlying object, if the browser
 * supports immutable objects.
 *
 * In default mode, writes to this view will fail silently. In strict mode,
 * they will throw an error.
 *
 * @param {!Object<K,V>} obj An object.
 * @return {!Object<K,V>} An immutable view of that object, or the
 *     original object if this browser does not support immutables.
 * @template K,V
 */
goog.object.createImmutableView = function(obj) {
  let result = obj;
  if (Object.isFrozen && !Object.isFrozen(obj)) {
    result = Object.create(obj);
    Object.freeze(result);
  }
  return result;
};


/**
 * @param {!Object} obj An object.
 * @return {boolean} Whether this is an immutable view of the object.
 */
goog.object.isImmutableView = function(obj) {
  return !!Object.isFrozen && Object.isFrozen(obj);
};


/**
 * Get all properties names on a given Object regardless of enumerability.
 *
 * <p> If the browser does not support `Object.getOwnPropertyNames` nor
 * `Object.getPrototypeOf` then this is equivalent to using
 * `goog.object.getKeys`
 *
 * @param {?Object} obj The object to get the properties of.
 * @param {boolean=} opt_includeObjectPrototype Whether properties defined on
 *     `Object.prototype` should be included in the result.
 * @param {boolean=} opt_includeFunctionPrototype Whether properties defined on
 *     `Function.prototype` should be included in the result.
 * @return {!Array<string>}
 * @public
 */
goog.object.getAllPropertyNames = function(
    obj, opt_includeObjectPrototype, opt_includeFunctionPrototype) {
  if (!obj) {
    return [];
  }

  // Naively use a for..in loop to get the property names if the browser doesn't
  // support any other APIs for getting it.
  if (!Object.getOwnPropertyNames || !Object.getPrototypeOf) {
    return goog.object.getKeys(obj);
  }

  const visitedSet = {};

  // Traverse the prototype chain and add all properties to the visited set.
  let proto = obj;
  while (proto &&
         (proto !== Object.prototype || !!opt_includeObjectPrototype) &&
         (proto !== Function.prototype || !!opt_includeFunctionPrototype)) {
    const names = Object.getOwnPropertyNames(proto);
    for (let i = 0; i < names.length; i++) {
      visitedSet[names[i]] = true;
    }
    proto = Object.getPrototypeOf(proto);
  }

  return goog.object.getKeys(visitedSet);
};


/**
 * Given a ES5 or ES6 class reference, return its super class / super
 * constructor.
 *
 * This should be used in rare cases where you need to walk up the inheritance
 * tree (this is generally a bad idea). But this work with ES5 and ES6 classes,
 * unlike relying on the superClass_ property.
 *
 * Note: To start walking up the hierarchy from an instance call this with its
 * `constructor` property; e.g. `getSuperClass(instance.constructor)`.
 *
 * @param {function(new: ?)} constructor
 * @return {?Object}
 */
goog.object.getSuperClass = function(constructor) {
  var proto = Object.getPrototypeOf(constructor.prototype);
  return proto && proto.constructor;
};

//third_party/javascript/closure/dom/tags.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Utilities for HTML element tag names.
 */
goog.provide('goog.dom.tags');

goog.require('goog.object');


/**
 * The void elements specified by
 * http://www.w3.org/TR/html-markup/syntax.html#void-elements.
 * @const @private {!Object<string, boolean>}
 */
goog.dom.tags.VOID_TAGS_ = goog.object.createSet(
    'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input',
    'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr');


/**
 * Checks whether the tag is void (with no contents allowed and no legal end
 * tag), for example 'br'.
 * @param {string} tagName The tag name in lower case.
 * @return {boolean}
 */
goog.dom.tags.isVoidTag = function(tagName) {
  return goog.dom.tags.VOID_TAGS_[tagName] === true;
};

//third_party/javascript/closure/html/trustedtypes.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Policy to convert strings to Trusted Types. See
 * https://github.com/WICG/trusted-types for details.
 *
 * @visibility {//javascript/closure:__pkg__}
 * @visibility {//javascript/closure/bin/sizetests:__pkg__}
 * @visibility {//javascript/closure/dom:__pkg__}
 */

goog.provide('goog.html.trustedtypes');

/** @package @const {?TrustedTypePolicy} */
goog.html.trustedtypes.PRIVATE_DO_NOT_ACCESS_OR_ELSE_POLICY =
    goog.TRUSTED_TYPES_POLICY_NAME ?
    goog.createTrustedTypesPolicy(goog.TRUSTED_TYPES_POLICY_NAME + '#html') :
    null;

//third_party/javascript/closure/string/typedstring.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

goog.provide('goog.string.TypedString');



/**
 * Wrapper for strings that conform to a data type or language.
 *
 * Implementations of this interface are wrappers for strings, and typically
 * associate a type contract with the wrapped string.  Concrete implementations
 * of this interface may choose to implement additional run-time type checking,
 * see for example `goog.html.SafeHtml`. If available, client code that
 * needs to ensure type membership of an object should use the type's function
 * to assert type membership, such as `goog.html.SafeHtml.unwrap`.
 * @interface
 */
goog.string.TypedString = function() {};


/**
 * Interface marker of the TypedString interface.
 *
 * This property can be used to determine at runtime whether or not an object
 * implements this interface.  All implementations of this interface set this
 * property to `true`.
 * @type {boolean}
 */
goog.string.TypedString.prototype.implementsGoogStringTypedString;


/**
 * Retrieves this wrapped string's value.
 * @return {string} The wrapped string's value.
 */
goog.string.TypedString.prototype.getTypedStringValue;

//third_party/javascript/closure/string/const.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

goog.provide('goog.string.Const');

goog.require('goog.asserts');
goog.require('goog.string.TypedString');



/**
 * Wrapper for compile-time-constant strings.
 *
 * Const is a wrapper for strings that can only be created from program
 * constants (i.e., string literals).  This property relies on a custom Closure
 * compiler check that `goog.string.Const.from` is only invoked on
 * compile-time-constant expressions.
 *
 * Const is useful in APIs whose correct and secure use requires that certain
 * arguments are not attacker controlled: Compile-time constants are inherently
 * under the control of the application and not under control of external
 * attackers, and hence are safe to use in such contexts.
 *
 * Instances of this type must be created via its factory method
 * `goog.string.Const.from` and not by invoking its constructor.  The
 * constructor intentionally takes no parameters and the type is immutable;
 * hence only a default instance corresponding to the empty string can be
 * obtained via constructor invocation.  Use goog.string.Const.EMPTY
 * instead of using this constructor to get an empty Const string.
 *
 * @see goog.string.Const#from
 * @constructor
 * @final
 * @struct
 * @implements {goog.string.TypedString}
 * @param {Object=} opt_token package-internal implementation detail.
 * @param {string=} opt_content package-internal implementation detail.
 */
goog.string.Const = function(opt_token, opt_content) {
  /**
   * The wrapped value of this Const object.  The field has a purposely ugly
   * name to make (non-compiled) code that attempts to directly access this
   * field stand out.
   * @private {string}
   */
  this.stringConstValueWithSecurityContract__googStringSecurityPrivate_ =
      ((opt_token ===
        goog.string.Const.GOOG_STRING_CONSTRUCTOR_TOKEN_PRIVATE_) &&
       opt_content) ||
      '';

  /**
   * A type marker used to implement additional run-time type checking.
   * @see goog.string.Const#unwrap
   * @const {!Object}
   * @private
   */
  this.STRING_CONST_TYPE_MARKER__GOOG_STRING_SECURITY_PRIVATE_ =
      goog.string.Const.TYPE_MARKER_;
};


/**
 * @override
 * @const
 */
goog.string.Const.prototype.implementsGoogStringTypedString = true;


/**
 * Returns this Const's value as a string.
 *
 * IMPORTANT: In code where it is security-relevant that an object's type is
 * indeed `goog.string.Const`, use `goog.string.Const.unwrap`
 * instead of this method.
 *
 * @see goog.string.Const#unwrap
 * @override
 */
goog.string.Const.prototype.getTypedStringValue = function() {
  return this.stringConstValueWithSecurityContract__googStringSecurityPrivate_;
};


if (goog.DEBUG) {
  /**
   * Returns a debug-string representation of this value.
   *
   * To obtain the actual string value wrapped inside an object of this type,
   * use `goog.string.Const.unwrap`.
   *
   * @see goog.string.Const#unwrap
   * @override
   */
  goog.string.Const.prototype.toString = function() {
    return 'Const{' +
        this.stringConstValueWithSecurityContract__googStringSecurityPrivate_ +
        '}';
  };
}


/**
 * Performs a runtime check that the provided object is indeed an instance
 * of `goog.string.Const`, and returns its value.
 * @param {!goog.string.Const} stringConst The object to extract from.
 * @return {string} The Const object's contained string, unless the run-time
 *     type check fails. In that case, `unwrap` returns an innocuous
 *     string, or, if assertions are enabled, throws
 *     `goog.asserts.AssertionError`.
 */
goog.string.Const.unwrap = function(stringConst) {
  // Perform additional run-time type-checking to ensure that stringConst is
  // indeed an instance of the expected type.  This provides some additional
  // protection against security bugs due to application code that disables type
  // checks.
  if (stringConst instanceof goog.string.Const &&
      stringConst.constructor === goog.string.Const &&
      stringConst.STRING_CONST_TYPE_MARKER__GOOG_STRING_SECURITY_PRIVATE_ ===
          goog.string.Const.TYPE_MARKER_) {
    return stringConst
        .stringConstValueWithSecurityContract__googStringSecurityPrivate_;
  } else {
    goog.asserts.fail(
        'expected object of type Const, got \'' + stringConst + '\'');
    return 'type_error:Const';
  }
};


/**
 * Creates a Const object from a compile-time constant string.
 *
 * It is illegal to invoke this function on an expression whose
 * compile-time-constant value cannot be determined by the Closure compiler.
 *
 * Correct invocations include,
 * <pre>
 *   var s = goog.string.Const.from('hello');
 *   var t = goog.string.Const.from('hello' + 'world');
 * </pre>
 *
 * In contrast, the following are illegal:
 * <pre>
 *   var s = goog.string.Const.from(getHello());
 *   var t = goog.string.Const.from('hello' + world);
 * </pre>
 *
 * @param {string} s A constant string from which to create a Const.
 * @return {!goog.string.Const} A Const object initialized to stringConst.
 */
goog.string.Const.from = function(s) {
  return new goog.string.Const(
      goog.string.Const.GOOG_STRING_CONSTRUCTOR_TOKEN_PRIVATE_, s);
};

/**
 * Type marker for the Const type, used to implement additional run-time
 * type checking.
 * @const {!Object}
 * @private
 */
goog.string.Const.TYPE_MARKER_ = {};

/**
 * @type {!Object}
 * @private
 * @const
 */
goog.string.Const.GOOG_STRING_CONSTRUCTOR_TOKEN_PRIVATE_ = {};

/**
 * A Const instance wrapping the empty string.
 * @const {!goog.string.Const}
 */
goog.string.Const.EMPTY = goog.string.Const.from('');

//third_party/javascript/closure/html/safescript.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview The SafeScript type and its builders.
 *
 * TODO(xtof): Link to document stating type contract.
 */

goog.provide('goog.html.SafeScript');

goog.require('goog.asserts');
goog.require('goog.html.trustedtypes');
goog.require('goog.string.Const');
goog.require('goog.string.TypedString');



/**
 * A string-like object which represents JavaScript code and that carries the
 * security type contract that its value, as a string, will not cause execution
 * of unconstrained attacker controlled code (XSS) when evaluated as JavaScript
 * in a browser.
 *
 * Instances of this type must be created via the factory method
 * `goog.html.SafeScript.fromConstant` and not by invoking its
 * constructor. The constructor intentionally takes no parameters and the type
 * is immutable; hence only a default instance corresponding to the empty string
 * can be obtained via constructor invocation.
 *
 * A SafeScript's string representation can safely be interpolated as the
 * content of a script element within HTML. The SafeScript string should not be
 * escaped before interpolation.
 *
 * Note that the SafeScript might contain text that is attacker-controlled but
 * that text should have been interpolated with appropriate escaping,
 * sanitization and/or validation into the right location in the script, such
 * that it is highly constrained in its effect (for example, it had to match a
 * set of whitelisted words).
 *
 * A SafeScript can be constructed via security-reviewed unchecked
 * conversions. In this case producers of SafeScript must ensure themselves that
 * the SafeScript does not contain unsafe script. Note in particular that
 * {@code &lt;} is dangerous, even when inside JavaScript strings, and so should
 * always be forbidden or JavaScript escaped in user controlled input. For
 * example, if {@code &lt;/script&gt;&lt;script&gt;evil&lt;/script&gt;"} were
 * interpolated inside a JavaScript string, it would break out of the context
 * of the original script element and `evil` would execute. Also note
 * that within an HTML script (raw text) element, HTML character references,
 * such as "&lt;" are not allowed. See
 * http://www.w3.org/TR/html5/scripting-1.html#restrictions-for-contents-of-script-elements.
 *
 * @see goog.html.SafeScript#fromConstant
 * @constructor
 * @final
 * @struct
 * @implements {goog.string.TypedString}
 */
goog.html.SafeScript = function() {
  /**
   * The contained value of this SafeScript.  The field has a purposely
   * ugly name to make (non-compiled) code that attempts to directly access this
   * field stand out.
   * @private {!TrustedScript|string}
   */
  this.privateDoNotAccessOrElseSafeScriptWrappedValue_ = '';

  /**
   * A type marker used to implement additional run-time type checking.
   * @see goog.html.SafeScript#unwrap
   * @const {!Object}
   * @private
   */
  this.SAFE_SCRIPT_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
      goog.html.SafeScript.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
};


/**
 * @override
 * @const
 */
goog.html.SafeScript.prototype.implementsGoogStringTypedString = true;


/**
 * Type marker for the SafeScript type, used to implement additional
 * run-time type checking.
 * @const {!Object}
 * @private
 */
goog.html.SafeScript.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};


/**
 * Creates a SafeScript object from a compile-time constant string.
 *
 * @param {!goog.string.Const} script A compile-time-constant string from which
 *     to create a SafeScript.
 * @return {!goog.html.SafeScript} A SafeScript object initialized to
 *     `script`.
 */
goog.html.SafeScript.fromConstant = function(script) {
  var scriptString = goog.string.Const.unwrap(script);
  if (scriptString.length === 0) {
    return goog.html.SafeScript.EMPTY;
  }
  return goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse(
      scriptString);
};


/**
 * Creates a SafeScript from a compile-time constant string but with arguments
 * that can vary at run-time. The code argument should be formatted as an
 * inline function (see example below). The arguments will be JSON-encoded and
 * provided as input to the function specified in code.
 *
 * Example Usage:
 *
 *     let safeScript = SafeScript.fromConstantAndArgs(
 *         Const.from('function(arg1, arg2) { doSomething(arg1, arg2); }'),
 *         arg1,
 *         arg2);
 *
 * This produces a SafeScript equivalent to the following:
 *
 *     (function(arg1, arg2) { doSomething(arg1, arg2); })("value1", "value2");
 *
 * @param {!goog.string.Const} code
 * @param {...*} var_args
 * @return {!goog.html.SafeScript}
 */
goog.html.SafeScript.fromConstantAndArgs = function(code, var_args) {
  var args = [];
  for (var i = 1; i < arguments.length; i++) {
    args.push(goog.html.SafeScript.stringify_(arguments[i]));
  }
  return goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse(
      '(' + goog.string.Const.unwrap(code) + ')(' + args.join(', ') + ');');
};


/**
 * Creates a SafeScript JSON representation from anything that could be passed
 * to JSON.stringify.
 * @param {*} val
 * @return {!goog.html.SafeScript}
 */
goog.html.SafeScript.fromJson = function(val) {
  return goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse(
      goog.html.SafeScript.stringify_(val));
};


/**
 * Returns this SafeScript's value as a string.
 *
 * IMPORTANT: In code where it is security relevant that an object's type is
 * indeed `SafeScript`, use `goog.html.SafeScript.unwrap` instead of
 * this method. If in doubt, assume that it's security relevant. In particular,
 * note that goog.html functions which return a goog.html type do not guarantee
 * the returned instance is of the right type. For example:
 *
 * <pre>
 * var fakeSafeHtml = new String('fake');
 * fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
 * var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
 * // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
 * // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml
 * // instanceof goog.html.SafeHtml.
 * </pre>
 *
 * @see goog.html.SafeScript#unwrap
 * @override
 */
goog.html.SafeScript.prototype.getTypedStringValue = function() {
  return this.privateDoNotAccessOrElseSafeScriptWrappedValue_.toString();
};


if (goog.DEBUG) {
  /**
   * Returns a debug string-representation of this value.
   *
   * To obtain the actual string value wrapped in a SafeScript, use
   * `goog.html.SafeScript.unwrap`.
   *
   * @see goog.html.SafeScript#unwrap
   * @override
   */
  goog.html.SafeScript.prototype.toString = function() {
    return 'SafeScript{' +
        this.privateDoNotAccessOrElseSafeScriptWrappedValue_ + '}';
  };
}


/**
 * Performs a runtime check that the provided object is indeed a
 * SafeScript object, and returns its value.
 *
 * @param {!goog.html.SafeScript} safeScript The object to extract from.
 * @return {string} The safeScript object's contained string, unless
 *     the run-time type check fails. In that case, `unwrap` returns an
 *     innocuous string, or, if assertions are enabled, throws
 *     `goog.asserts.AssertionError`.
 */
goog.html.SafeScript.unwrap = function(safeScript) {
  return goog.html.SafeScript.unwrapTrustedScript(safeScript).toString();
};


/**
 * Unwraps value as TrustedScript if supported or as a string if not.
 * @param {!goog.html.SafeScript} safeScript
 * @return {!TrustedScript|string}
 * @see goog.html.SafeScript.unwrap
 */
goog.html.SafeScript.unwrapTrustedScript = function(safeScript) {
  // Perform additional Run-time type-checking to ensure that
  // safeScript is indeed an instance of the expected type.  This
  // provides some additional protection against security bugs due to
  // application code that disables type checks.
  // Specifically, the following checks are performed:
  // 1. The object is an instance of the expected type.
  // 2. The object is not an instance of a subclass.
  // 3. The object carries a type marker for the expected type. "Faking" an
  // object requires a reference to the type marker, which has names intended
  // to stand out in code reviews.
  if (safeScript instanceof goog.html.SafeScript &&
      safeScript.constructor === goog.html.SafeScript &&
      safeScript.SAFE_SCRIPT_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
          goog.html.SafeScript.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
    return safeScript.privateDoNotAccessOrElseSafeScriptWrappedValue_;
  } else {
    goog.asserts.fail('expected object of type SafeScript, got \'' +
        safeScript + '\' of type ' + goog.typeOf(safeScript));
    return 'type_error:SafeScript';
  }
};


/**
 * Converts the given value to a embeddabel JSON string and returns it. The
 * resulting string can be embedded in HTML because the '<' character is
 * encoded.
 *
 * @param {*} val
 * @return {string}
 * @private
 */
goog.html.SafeScript.stringify_ = function(val) {
  var json = JSON.stringify(val);
  return json.replace(/</g, '\\x3c');
};

/**
 * Package-internal utility method to create SafeScript instances.
 *
 * @param {string} script The string to initialize the SafeScript object with.
 * @return {!goog.html.SafeScript} The initialized SafeScript object.
 * @package
 */
goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse =
    function(script) {
  return new goog.html.SafeScript().initSecurityPrivateDoNotAccessOrElse_(
      script);
};


/**
 * Called from createSafeScriptSecurityPrivateDoNotAccessOrElse(). This
 * method exists only so that the compiler can dead code eliminate static
 * fields (like EMPTY) when they're not accessed.
 * @param {string} script
 * @return {!goog.html.SafeScript}
 * @private
 */
goog.html.SafeScript.prototype.initSecurityPrivateDoNotAccessOrElse_ = function(
    script) {
  this.privateDoNotAccessOrElseSafeScriptWrappedValue_ =
      goog.html.trustedtypes.PRIVATE_DO_NOT_ACCESS_OR_ELSE_POLICY ?
      goog.html.trustedtypes.PRIVATE_DO_NOT_ACCESS_OR_ELSE_POLICY.createScript(
          script) :
      script;
  return this;
};


/**
 * A SafeScript instance corresponding to the empty string.
 * @const {!goog.html.SafeScript}
 */
goog.html.SafeScript.EMPTY =
    goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse('');

//third_party/javascript/closure/fs/url.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Wrapper for URL and its createObjectUrl and revokeObjectUrl
 * methods that are part of the HTML5 File API.
 */

goog.provide('goog.fs.url');


/**
 * Creates a blob URL for a blob object.
 * Throws an error if the browser does not support Object Urls.
 *
 * @param {!File|!Blob|!MediaSource|!MediaStream} obj The object for which
 *   to create the URL.
 * @return {string} The URL for the object.
 */
goog.fs.url.createObjectUrl = function(obj) {
  return goog.fs.url.getUrlObject_().createObjectURL(obj);
};


/**
 * Revokes a URL created by {@link goog.fs.url.createObjectUrl}.
 * Throws an error if the browser does not support Object Urls.
 *
 * @param {string} url The URL to revoke.
 */
goog.fs.url.revokeObjectUrl = function(url) {
  goog.fs.url.getUrlObject_().revokeObjectURL(url);
};


/**
 * @record
 * @private
 */
goog.fs.url.UrlObject_ = function() {};

/**
 * @param {!File|!Blob|!MediaSource|!MediaStream} arg
 * @return {string}
 */
goog.fs.url.UrlObject_.prototype.createObjectURL = function(arg) {};

/**
 * @param {string} s
 */
goog.fs.url.UrlObject_.prototype.revokeObjectURL = function(s) {};


/**
 * Get the object that has the createObjectURL and revokeObjectURL functions for
 * this browser.
 *
 * @return {!goog.fs.url.UrlObject_} The object for this browser.
 * @private
 */
goog.fs.url.getUrlObject_ = function() {
  const urlObject = goog.fs.url.findUrlObject_();
  if (urlObject != null) {
    return urlObject;
  } else {
    throw new Error('This browser doesn\'t seem to support blob URLs');
  }
};


/**
 * Finds the object that has the createObjectURL and revokeObjectURL functions
 * for this browser.
 *
 * @return {?goog.fs.url.UrlObject_} The object for this browser or null if the
 *     browser does not support Object Urls.
 * @private
 */
goog.fs.url.findUrlObject_ = function() {
  // This is what the spec says to do
  // http://dev.w3.org/2006/webapi/FileAPI/#dfn-createObjectURL
  if (goog.global.URL !== undefined &&
      goog.global.URL.createObjectURL !== undefined) {
    return /** @type {!goog.fs.url.UrlObject_} */ (goog.global.URL);
    // This is what Chrome does (as of 10.0.648.6 dev)
  } else if (
      goog.global.webkitURL !== undefined &&
      goog.global.webkitURL.createObjectURL !== undefined) {
    return /** @type {!goog.fs.url.UrlObject_} */ (goog.global.webkitURL);
    // This is what the spec used to say to do
  } else if (goog.global.createObjectURL !== undefined) {
    return /** @type {!goog.fs.url.UrlObject_} */ (goog.global);
  } else {
    return null;
  }
};


/**
 * Checks whether this browser supports Object Urls. If not, calls to
 * createObjectUrl and revokeObjectUrl will result in an error.
 *
 * @return {boolean} True if this browser supports Object Urls.
 */
goog.fs.url.browserSupportsObjectUrls = function() {
  return goog.fs.url.findUrlObject_() != null;
};

//third_party/javascript/closure/fs/blob.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Wrappers for the HTML5 File API. These wrappers closely mirror
 * the underlying APIs, but use Closure-style events and Deferred return values.
 * Their existence also makes it possible to mock the FileSystem API for testing
 * in browsers that don't support it natively.
 *
 * When adding public functions to anything under this namespace, be sure to add
 * its mock counterpart to goog.testing.fs.
 */

goog.provide('goog.fs.blob');

goog.require('goog.array');

/**
 * Concatenates one or more values together and converts them to a Blob.
 *
 * @param {...(string|!Blob|!ArrayBuffer)} var_args The values that will make up
 *     the resulting blob.
 * @return {!Blob} The blob.
 */
goog.fs.blob.getBlob = function(var_args) {
  var BlobBuilder = goog.global.BlobBuilder || goog.global.WebKitBlobBuilder;

  if (BlobBuilder !== undefined) {
    var bb = new BlobBuilder();
    for (var i = 0; i < arguments.length; i++) {
      bb.append(arguments[i]);
    }
    return bb.getBlob();
  } else {
    return goog.fs.blob.getBlobWithProperties(goog.array.toArray(arguments));
  }
};


/**
 * Creates a blob with the given properties.
 * See https://developer.mozilla.org/en-US/docs/Web/API/Blob for more details.
 *
 * @param {!Array<string|!Blob|!ArrayBuffer>} parts The values that will make up
 *     the resulting blob (subset supported by both BlobBuilder.append() and
 *     Blob constructor).
 * @param {string=} opt_type The MIME type of the Blob.
 * @param {string=} opt_endings Specifies how strings containing newlines are to
 *     be written out.
 * @return {!Blob} The blob.
 */
goog.fs.blob.getBlobWithProperties = function(parts, opt_type, opt_endings) {
  var BlobBuilder = goog.global.BlobBuilder || goog.global.WebKitBlobBuilder;

  if (BlobBuilder !== undefined) {
    var bb = new BlobBuilder();
    for (var i = 0; i < parts.length; i++) {
      bb.append(parts[i], opt_endings);
    }
    return bb.getBlob(opt_type);
  } else if (goog.global.Blob !== undefined) {
    var properties = {};
    if (opt_type) {
      properties['type'] = opt_type;
    }
    if (opt_endings) {
      properties['endings'] = opt_endings;
    }
    return new Blob(parts, properties);
  } else {
    throw new Error('This browser doesn\'t seem to support creating Blobs');
  }
};

//third_party/javascript/closure/i18n/bidi.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Utility functions for supporting Bidi issues.
 */


/**
 * Namespace for bidi supporting functions.
 */
goog.provide('goog.i18n.bidi');
goog.provide('goog.i18n.bidi.Dir');
goog.provide('goog.i18n.bidi.DirectionalString');
goog.provide('goog.i18n.bidi.Format');


/**
 * @define {boolean} FORCE_RTL forces the {@link goog.i18n.bidi.IS_RTL} constant
 * to say that the current locale is a RTL locale.  This should only be used
 * if you want to override the default behavior for deciding whether the
 * current locale is RTL or not.
 *
 * {@see goog.i18n.bidi.IS_RTL}
 */
goog.i18n.bidi.FORCE_RTL = goog.define('goog.i18n.bidi.FORCE_RTL', false);


/**
 * Constant that defines whether or not the current locale is a RTL locale.
 * If {@link goog.i18n.bidi.FORCE_RTL} is not true, this constant will default
 * to check that {@link goog.LOCALE} is one of a few major RTL locales.
 *
 * <p>This is designed to be a maximally efficient compile-time constant. For
 * example, for the default goog.LOCALE, compiling
 * "if (goog.i18n.bidi.IS_RTL) alert('rtl') else {}" should produce no code. It
 * is this design consideration that limits the implementation to only
 * supporting a few major RTL locales, as opposed to the broader repertoire of
 * something like goog.i18n.bidi.isRtlLanguage.
 *
 * <p>Since this constant refers to the directionality of the locale, it is up
 * to the caller to determine if this constant should also be used for the
 * direction of the UI.
 *
 * {@see goog.LOCALE}
 *
 * @type {boolean}
 *
 * TODO(aharon): write a test that checks that this is a compile-time constant.
 */
// LINT.IfChange
goog.i18n.bidi.IS_RTL =
    goog.i18n.bidi.FORCE_RTL ||
    ((goog.LOCALE.substring(0, 2).toLowerCase() == 'ar' ||
      goog.LOCALE.substring(0, 2).toLowerCase() == 'fa' ||
      goog.LOCALE.substring(0, 2).toLowerCase() == 'he' ||
      goog.LOCALE.substring(0, 2).toLowerCase() == 'iw' ||
      goog.LOCALE.substring(0, 2).toLowerCase() == 'ps' ||
      goog.LOCALE.substring(0, 2).toLowerCase() == 'sd' ||
      goog.LOCALE.substring(0, 2).toLowerCase() == 'ug' ||
      goog.LOCALE.substring(0, 2).toLowerCase() == 'ur' ||
      goog.LOCALE.substring(0, 2).toLowerCase() == 'yi') &&
     (goog.LOCALE.length == 2 || goog.LOCALE.substring(2, 3) == '-' ||
      goog.LOCALE.substring(2, 3) == '_')) ||
    (  // Specific to CKB (Central Kurdish)
        goog.LOCALE.length >= 3 &&
        goog.LOCALE.substring(0, 3).toLowerCase() == 'ckb' &&
        (goog.LOCALE.length == 3 || goog.LOCALE.substring(3, 4) == '-' ||
         goog.LOCALE.substring(3, 4) == '_')) ||
    (  // 2 letter language codes with RTL scripts
        goog.LOCALE.length >= 7 &&
        ((goog.LOCALE.substring(2, 3) == '-' ||
          goog.LOCALE.substring(2, 3) == '_') &&
         (goog.LOCALE.substring(3, 7).toLowerCase() == 'adlm' ||
          goog.LOCALE.substring(3, 7).toLowerCase() == 'arab' ||
          goog.LOCALE.substring(3, 7).toLowerCase() == 'hebr' ||
          goog.LOCALE.substring(3, 7).toLowerCase() == 'nkoo' ||
          goog.LOCALE.substring(3, 7).toLowerCase() == 'rohg' ||
          goog.LOCALE.substring(3, 7).toLowerCase() == 'thaa'))) ||
    (  // 3 letter languages codes with RTL scripts
        goog.LOCALE.length >= 8 &&
        ((goog.LOCALE.substring(3, 4) == '-' ||
          goog.LOCALE.substring(3, 4) == '_') &&
         (goog.LOCALE.substring(4, 8).toLowerCase() == 'adlm' ||
          goog.LOCALE.substring(4, 8).toLowerCase() == 'arab' ||
          goog.LOCALE.substring(4, 8).toLowerCase() == 'hebr' ||
          goog.LOCALE.substring(4, 8).toLowerCase() == 'nkoo' ||
          goog.LOCALE.substring(4, 8).toLowerCase() == 'rohg' ||
          goog.LOCALE.substring(4, 8).toLowerCase() == 'thaa')));
//    closure/RtlLocalesTest.java)

// TODO(b/77919903): Add additional scripts and languages that are RTL,
// e.g., mende, samaritan, etc.


/**
 * Unicode formatting characters and directionality string constants.
 * @enum {string}
 */
goog.i18n.bidi.Format = {
  /** Unicode "Left-To-Right Embedding" (LRE) character. */
  LRE: '\u202A',
  /** Unicode "Right-To-Left Embedding" (RLE) character. */
  RLE: '\u202B',
  /** Unicode "Pop Directional Formatting" (PDF) character. */
  PDF: '\u202C',
  /** Unicode "Left-To-Right Mark" (LRM) character. */
  LRM: '\u200E',
  /** Unicode "Right-To-Left Mark" (RLM) character. */
  RLM: '\u200F'
};


/**
 * Directionality enum.
 * @enum {number}
 */
goog.i18n.bidi.Dir = {
  /**
   * Left-to-right.
   */
  LTR: 1,

  /**
   * Right-to-left.
   */
  RTL: -1,

  /**
   * Neither left-to-right nor right-to-left.
   */
  NEUTRAL: 0
};


/**
 * 'right' string constant.
 * @type {string}
 */
goog.i18n.bidi.RIGHT = 'right';


/**
 * 'left' string constant.
 * @type {string}
 */
goog.i18n.bidi.LEFT = 'left';


/**
 * 'left' if locale is RTL, 'right' if not.
 * @type {string}
 */
goog.i18n.bidi.I18N_RIGHT =
    goog.i18n.bidi.IS_RTL ? goog.i18n.bidi.LEFT : goog.i18n.bidi.RIGHT;


/**
 * 'right' if locale is RTL, 'left' if not.
 * @type {string}
 */
goog.i18n.bidi.I18N_LEFT =
    goog.i18n.bidi.IS_RTL ? goog.i18n.bidi.RIGHT : goog.i18n.bidi.LEFT;


/**
 * Convert a directionality given in various formats to a goog.i18n.bidi.Dir
 * constant. Useful for interaction with different standards of directionality
 * representation.
 *
 * @param {goog.i18n.bidi.Dir|number|boolean|null} givenDir Directionality given
 *     in one of the following formats:
 *     1. A goog.i18n.bidi.Dir constant.
 *     2. A number (positive = LTR, negative = RTL, 0 = neutral).
 *     3. A boolean (true = RTL, false = LTR).
 *     4. A null for unknown directionality.
 * @param {boolean=} opt_noNeutral Whether a givenDir of zero or
 *     goog.i18n.bidi.Dir.NEUTRAL should be treated as null, i.e. unknown, in
 *     order to preserve legacy behavior.
 * @return {?goog.i18n.bidi.Dir} A goog.i18n.bidi.Dir constant matching the
 *     given directionality. If given null, returns null (i.e. unknown).
 */
goog.i18n.bidi.toDir = function(givenDir, opt_noNeutral) {
  if (typeof givenDir == 'number') {
    // This includes the non-null goog.i18n.bidi.Dir case.
    return givenDir > 0 ?
        goog.i18n.bidi.Dir.LTR :
        givenDir < 0 ? goog.i18n.bidi.Dir.RTL :
                       opt_noNeutral ? null : goog.i18n.bidi.Dir.NEUTRAL;
  } else if (givenDir == null) {
    return null;
  } else {
    // Must be typeof givenDir == 'boolean'.
    return givenDir ? goog.i18n.bidi.Dir.RTL : goog.i18n.bidi.Dir.LTR;
  }
};


/**
 * A practical pattern to identify strong LTR character in the BMP.
 * This pattern is not theoretically correct according to the Unicode
 * standard. It is simplified for performance and small code size.
 * It also partially supports LTR scripts beyond U+FFFF by including
 * UTF-16 high surrogate values corresponding to mostly L-class code
 * point ranges.
 * However, low surrogate values and private-use regions are not included
 * in this RegEx.
 * @type {string}
 * @private
 */
goog.i18n.bidi.ltrChars_ =
    'A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590\u0900-\u1FFF' +
    '\u200E\u2C00-\uD801\uD804-\uD839\uD83C-\uDBFF' +
    '\uF900-\uFB1C\uFE00-\uFE6F\uFEFD-\uFFFF';

/**
 * A practical pattern to identify strong RTL character. This pattern is not
 * theoretically correct according to the Unicode standard. It is simplified
 * for performance and small code size.
 * It also partially supports RTL scripts beyond U+FFFF by including
 * UTF-16 high surrogate values corresponding to mostly R- or AL-class
 * code point ranges.
 * However, low surrogate values and private-use regions are not included
 * in this RegEx.
 * @type {string}
 * @private
 */
goog.i18n.bidi.rtlChars_ =
    '\u0591-\u06EF\u06FA-\u08FF\u200F\uD802-\uD803\uD83A-\uD83B' +
    '\uFB1D-\uFDFF\uFE70-\uFEFC';

/**
 * Simplified regular expression for an HTML tag (opening or closing) or an HTML
 * escape. We might want to skip over such expressions when estimating the text
 * directionality.
 * @type {RegExp}
 * @private
 */
goog.i18n.bidi.htmlSkipReg_ = /<[^>]*>|&[^;]+;/g;


/**
 * Returns the input text with spaces instead of HTML tags or HTML escapes, if
 * opt_isStripNeeded is true. Else returns the input as is.
 * Useful for text directionality estimation.
 * Note: the function should not be used in other contexts; it is not 100%
 * correct, but rather a good-enough implementation for directionality
 * estimation purposes.
 * @param {string} str The given string.
 * @param {boolean=} opt_isStripNeeded Whether to perform the stripping.
 *     Default: false (to retain consistency with calling functions).
 * @return {string} The given string cleaned of HTML tags / escapes.
 * @private
 */
goog.i18n.bidi.stripHtmlIfNeeded_ = function(str, opt_isStripNeeded) {
  return opt_isStripNeeded ? str.replace(goog.i18n.bidi.htmlSkipReg_, '') : str;
};


/**
 * Regular expression to check for RTL characters, BMP and high surrogate.
 * @type {RegExp}
 * @private
 */
goog.i18n.bidi.rtlCharReg_ = new RegExp('[' + goog.i18n.bidi.rtlChars_ + ']');


/**
 * Regular expression to check for LTR characters.
 * @type {RegExp}
 * @private
 */
goog.i18n.bidi.ltrCharReg_ = new RegExp('[' + goog.i18n.bidi.ltrChars_ + ']');


/**
 * Test whether the given string has any RTL characters in it.
 * @param {string} str The given string that need to be tested.
 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
 *     Default: false.
 * @return {boolean} Whether the string contains RTL characters.
 */
goog.i18n.bidi.hasAnyRtl = function(str, opt_isHtml) {
  return goog.i18n.bidi.rtlCharReg_.test(
      goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml));
};


/**
 * Test whether the given string has any RTL characters in it.
 * @param {string} str The given string that need to be tested.
 * @return {boolean} Whether the string contains RTL characters.
 * @deprecated Use hasAnyRtl.
 */
goog.i18n.bidi.hasRtlChar = goog.i18n.bidi.hasAnyRtl;


/**
 * Test whether the given string has any LTR characters in it.
 * @param {string} str The given string that need to be tested.
 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
 *     Default: false.
 * @return {boolean} Whether the string contains LTR characters.
 */
goog.i18n.bidi.hasAnyLtr = function(str, opt_isHtml) {
  return goog.i18n.bidi.ltrCharReg_.test(
      goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml));
};


/**
 * Regular expression pattern to check if the first character in the string
 * is LTR.
 * @type {RegExp}
 * @private
 */
goog.i18n.bidi.ltrRe_ = new RegExp('^[' + goog.i18n.bidi.ltrChars_ + ']');


/**
 * Regular expression pattern to check if the first character in the string
 * is RTL.
 * @type {RegExp}
 * @private
 */
goog.i18n.bidi.rtlRe_ = new RegExp('^[' + goog.i18n.bidi.rtlChars_ + ']');


/**
 * Check if the first character in the string is RTL or not.
 * @param {string} str The given string that need to be tested.
 * @return {boolean} Whether the first character in str is an RTL char.
 */
goog.i18n.bidi.isRtlChar = function(str) {
  return goog.i18n.bidi.rtlRe_.test(str);
};


/**
 * Check if the first character in the string is LTR or not.
 * @param {string} str The given string that need to be tested.
 * @return {boolean} Whether the first character in str is an LTR char.
 */
goog.i18n.bidi.isLtrChar = function(str) {
  return goog.i18n.bidi.ltrRe_.test(str);
};


/**
 * Check if the first character in the string is neutral or not.
 * @param {string} str The given string that need to be tested.
 * @return {boolean} Whether the first character in str is a neutral char.
 */
goog.i18n.bidi.isNeutralChar = function(str) {
  return !goog.i18n.bidi.isLtrChar(str) && !goog.i18n.bidi.isRtlChar(str);
};


/**
 * Regular expressions to check if a piece of text is of LTR directionality
 * on first character with strong directionality.
 * @type {RegExp}
 * @private
 */
goog.i18n.bidi.ltrDirCheckRe_ = new RegExp(
    '^[^' + goog.i18n.bidi.rtlChars_ + ']*[' + goog.i18n.bidi.ltrChars_ + ']');


/**
 * Regular expressions to check if a piece of text is of RTL directionality
 * on first character with strong directionality.
 * @type {RegExp}
 * @private
 */
goog.i18n.bidi.rtlDirCheckRe_ = new RegExp(
    '^[^' + goog.i18n.bidi.ltrChars_ + ']*[' + goog.i18n.bidi.rtlChars_ + ']');


/**
 * Check whether the first strongly directional character (if any) is RTL.
 * @param {string} str String being checked.
 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
 *     Default: false.
 * @return {boolean} Whether RTL directionality is detected using the first
 *     strongly-directional character method.
 */
goog.i18n.bidi.startsWithRtl = function(str, opt_isHtml) {
  return goog.i18n.bidi.rtlDirCheckRe_.test(
      goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml));
};


/**
 * Check whether the first strongly directional character (if any) is RTL.
 * @param {string} str String being checked.
 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
 *     Default: false.
 * @return {boolean} Whether RTL directionality is detected using the first
 *     strongly-directional character method.
 * @deprecated Use startsWithRtl.
 */
goog.i18n.bidi.isRtlText = goog.i18n.bidi.startsWithRtl;


/**
 * Check whether the first strongly directional character (if any) is LTR.
 * @param {string} str String being checked.
 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
 *     Default: false.
 * @return {boolean} Whether LTR directionality is detected using the first
 *     strongly-directional character method.
 */
goog.i18n.bidi.startsWithLtr = function(str, opt_isHtml) {
  return goog.i18n.bidi.ltrDirCheckRe_.test(
      goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml));
};


/**
 * Check whether the first strongly directional character (if any) is LTR.
 * @param {string} str String being checked.
 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
 *     Default: false.
 * @return {boolean} Whether LTR directionality is detected using the first
 *     strongly-directional character method.
 * @deprecated Use startsWithLtr.
 */
goog.i18n.bidi.isLtrText = goog.i18n.bidi.startsWithLtr;


/**
 * Regular expression to check if a string looks like something that must
 * always be LTR even in RTL text, e.g. a URL. When estimating the
 * directionality of text containing these, we treat these as weakly LTR,
 * like numbers.
 * @type {RegExp}
 * @private
 */
goog.i18n.bidi.isRequiredLtrRe_ = /^http:\/\/.*/;


/**
 * Check whether the input string either contains no strongly directional
 * characters or looks like a url.
 * @param {string} str String being checked.
 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
 *     Default: false.
 * @return {boolean} Whether neutral directionality is detected.
 */
goog.i18n.bidi.isNeutralText = function(str, opt_isHtml) {
  str = goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml);
  return goog.i18n.bidi.isRequiredLtrRe_.test(str) ||
      !goog.i18n.bidi.hasAnyLtr(str) && !goog.i18n.bidi.hasAnyRtl(str);
};


/**
 * Regular expressions to check if the last strongly-directional character in a
 * piece of text is LTR.
 * @type {RegExp}
 * @private
 */
goog.i18n.bidi.ltrExitDirCheckRe_ = new RegExp(
    '[' + goog.i18n.bidi.ltrChars_ + ']' +
    '[^' + goog.i18n.bidi.rtlChars_ + ']*$');


/**
 * Regular expressions to check if the last strongly-directional character in a
 * piece of text is RTL.
 * @type {RegExp}
 * @private
 */
goog.i18n.bidi.rtlExitDirCheckRe_ = new RegExp(
    '[' + goog.i18n.bidi.rtlChars_ + ']' +
    '[^' + goog.i18n.bidi.ltrChars_ + ']*$');


/**
 * Check if the exit directionality a piece of text is LTR, i.e. if the last
 * strongly-directional character in the string is LTR.
 * @param {string} str String being checked.
 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
 *     Default: false.
 * @return {boolean} Whether LTR exit directionality was detected.
 */
goog.i18n.bidi.endsWithLtr = function(str, opt_isHtml) {
  return goog.i18n.bidi.ltrExitDirCheckRe_.test(
      goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml));
};


/**
 * Check if the exit directionality a piece of text is LTR, i.e. if the last
 * strongly-directional character in the string is LTR.
 * @param {string} str String being checked.
 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
 *     Default: false.
 * @return {boolean} Whether LTR exit directionality was detected.
 * @deprecated Use endsWithLtr.
 */
goog.i18n.bidi.isLtrExitText = goog.i18n.bidi.endsWithLtr;


/**
 * Check if the exit directionality a piece of text is RTL, i.e. if the last
 * strongly-directional character in the string is RTL.
 * @param {string} str String being checked.
 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
 *     Default: false.
 * @return {boolean} Whether RTL exit directionality was detected.
 */
goog.i18n.bidi.endsWithRtl = function(str, opt_isHtml) {
  return goog.i18n.bidi.rtlExitDirCheckRe_.test(
      goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml));
};


/**
 * Check if the exit directionality a piece of text is RTL, i.e. if the last
 * strongly-directional character in the string is RTL.
 * @param {string} str String being checked.
 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
 *     Default: false.
 * @return {boolean} Whether RTL exit directionality was detected.
 * @deprecated Use endsWithRtl.
 */
goog.i18n.bidi.isRtlExitText = goog.i18n.bidi.endsWithRtl;


/**
 * A regular expression for matching right-to-left language codes.
 * See {@link #isRtlLanguage} for the design.
 * Note that not all RTL scripts are included.
 * @type {!RegExp}
 * @private
 */
goog.i18n.bidi.rtlLocalesRe_ = new RegExp(
    '^(ar|ckb|dv|he|iw|fa|nqo|ps|sd|ug|ur|yi|' +
        '.*[-_](Adlm|Arab|Hebr|Nkoo|Rohg|Thaa))' +
        '(?!.*[-_](Latn|Cyrl)($|-|_))($|-|_)',
    'i');


/**
 * Check if a BCP 47 / III language code indicates an RTL language, i.e. either:
 * - a language code explicitly specifying one of the right-to-left scripts,
 *   e.g. "az-Arab", or<p>
 * - a language code specifying one of the languages normally written in a
 *   right-to-left script, e.g. "fa" (Farsi), except ones explicitly specifying
 *   Latin or Cyrillic script (which are the usual LTR alternatives).<p>
 * The list of right-to-left scripts appears in the 100-199 range in
 * http://www.unicode.org/iso15924/iso15924-num.html, of which Arabic and
 * Hebrew are by far the most widely used. We also recognize Thaana, and N'Ko,
 * which also have significant modern usage. Adlam and Rohingya
 * scripts are now included since they can be expected to be used in the
 * future. The rest (Syriac, Samaritan, Mandaic, etc.) seem to have extremely
 * limited or no modern usage and are not recognized to save on code size. The
 * languages usually written in a right-to-left script are taken as those with
 * Suppress-Script: Hebr|Arab|Thaa|Nkoo|Adlm|Rohg in
 * http://www.iana.org/assignments/language-subtag-registry,
 * as well as Central (or Sorani) Kurdish (ckb), Sindhi (sd) and Uyghur (ug).
 * Other subtags of the language code, e.g. regions like EG (Egypt), are
 * ignored.
 * @param {string} lang BCP 47 (a.k.a III) language code.
 * @return {boolean} Whether the language code is an RTL language.
 */
goog.i18n.bidi.isRtlLanguage = function(lang) {
  return goog.i18n.bidi.rtlLocalesRe_.test(lang);
};


/**
 * Regular expression for bracket guard replacement in text.
 * @type {RegExp}
 * @private
 */
goog.i18n.bidi.bracketGuardTextRe_ =
    /(\(.*?\)+)|(\[.*?\]+)|(\{.*?\}+)|(<.*?>+)/g;


/**
 * Apply bracket guard using LRM and RLM. This is to address the problem of
 * messy bracket display frequently happens in RTL layout.
 * This function works for plain text, not for HTML. In HTML, the opening
 * bracket might be in a different context than the closing bracket (such as
 * an attribute value).
 * @param {string} s The string that need to be processed.
 * @param {boolean=} opt_isRtlContext specifies default direction (usually
 *     direction of the UI).
 * @return {string} The processed string, with all bracket guarded.
 */
goog.i18n.bidi.guardBracketInText = function(s, opt_isRtlContext) {
  const useRtl = opt_isRtlContext === undefined ? goog.i18n.bidi.hasAnyRtl(s) :
                                                  opt_isRtlContext;
  const mark = useRtl ? goog.i18n.bidi.Format.RLM : goog.i18n.bidi.Format.LRM;
  return s.replace(goog.i18n.bidi.bracketGuardTextRe_, mark + '$&' + mark);
};


/**
 * Enforce the html snippet in RTL directionality regardless of overall context.
 * If the html piece was enclosed by tag, dir will be applied to existing
 * tag, otherwise a span tag will be added as wrapper. For this reason, if
 * html snippet starts with a tag, this tag must enclose the whole piece. If
 * the tag already has a dir specified, this new one will override existing
 * one in behavior (tested on FF and IE).
 * @param {string} html The string that need to be processed.
 * @return {string} The processed string, with directionality enforced to RTL.
 */
goog.i18n.bidi.enforceRtlInHtml = function(html) {
  if (html.charAt(0) == '<') {
    return html.replace(/<\w+/, '$& dir=rtl');
  }
  // '\n' is important for FF so that it won't incorrectly merge span groups
  return '\n<span dir=rtl>' + html + '</span>';
};


/**
 * Enforce RTL on both end of the given text piece using unicode BiDi formatting
 * characters RLE and PDF.
 * @param {string} text The piece of text that need to be wrapped.
 * @return {string} The wrapped string after process.
 */
goog.i18n.bidi.enforceRtlInText = function(text) {
  return goog.i18n.bidi.Format.RLE + text + goog.i18n.bidi.Format.PDF;
};


/**
 * Enforce the html snippet in RTL directionality regardless or overall context.
 * If the html piece was enclosed by tag, dir will be applied to existing
 * tag, otherwise a span tag will be added as wrapper. For this reason, if
 * html snippet starts with a tag, this tag must enclose the whole piece. If
 * the tag already has a dir specified, this new one will override existing
 * one in behavior (tested on FF and IE).
 * @param {string} html The string that need to be processed.
 * @return {string} The processed string, with directionality enforced to RTL.
 */
goog.i18n.bidi.enforceLtrInHtml = function(html) {
  if (html.charAt(0) == '<') {
    return html.replace(/<\w+/, '$& dir=ltr');
  }
  // '\n' is important for FF so that it won't incorrectly merge span groups
  return '\n<span dir=ltr>' + html + '</span>';
};


/**
 * Enforce LTR on both end of the given text piece using unicode BiDi formatting
 * characters LRE and PDF.
 * @param {string} text The piece of text that need to be wrapped.
 * @return {string} The wrapped string after process.
 */
goog.i18n.bidi.enforceLtrInText = function(text) {
  return goog.i18n.bidi.Format.LRE + text + goog.i18n.bidi.Format.PDF;
};


/**
 * Regular expression to find dimensions such as "padding: .3 0.4ex 5px 6;"
 * @type {RegExp}
 * @private
 */
goog.i18n.bidi.dimensionsRe_ =
    /:\s*([.\d][.\w]*)\s+([.\d][.\w]*)\s+([.\d][.\w]*)\s+([.\d][.\w]*)/g;


/**
 * Regular expression for left.
 * @type {RegExp}
 * @private
 */
goog.i18n.bidi.leftRe_ = /left/gi;


/**
 * Regular expression for right.
 * @type {RegExp}
 * @private
 */
goog.i18n.bidi.rightRe_ = /right/gi;


/**
 * Placeholder regular expression for swapping.
 * @type {RegExp}
 * @private
 */
goog.i18n.bidi.tempRe_ = /%%%%/g;


/**
 * Swap location parameters and 'left'/'right' in CSS specification. The
 * processed string will be suited for RTL layout. Though this function can
 * cover most cases, there are always exceptions. It is suggested to put
 * those exceptions in separate group of CSS string.
 * @param {string} cssStr CSS spefication string.
 * @return {string} Processed CSS specification string.
 */
goog.i18n.bidi.mirrorCSS = function(cssStr) {
  return cssStr
      .
      // reverse dimensions
      replace(goog.i18n.bidi.dimensionsRe_, ':$1 $4 $3 $2')
      .replace(goog.i18n.bidi.leftRe_, '%%%%')
      .  // swap left and right
      replace(goog.i18n.bidi.rightRe_, goog.i18n.bidi.LEFT)
      .replace(goog.i18n.bidi.tempRe_, goog.i18n.bidi.RIGHT);
};


/**
 * Regular expression for hebrew double quote substitution, finding quote
 * directly after hebrew characters.
 * @type {RegExp}
 * @private
 */
goog.i18n.bidi.doubleQuoteSubstituteRe_ = /([\u0591-\u05f2])"/g;


/**
 * Regular expression for hebrew single quote substitution, finding quote
 * directly after hebrew characters.
 * @type {RegExp}
 * @private
 */
goog.i18n.bidi.singleQuoteSubstituteRe_ = /([\u0591-\u05f2])'/g;


/**
 * Replace the double and single quote directly after a Hebrew character with
 * GERESH and GERSHAYIM. In such case, most likely that's user intention.
 * @param {string} str String that need to be processed.
 * @return {string} Processed string with double/single quote replaced.
 */
goog.i18n.bidi.normalizeHebrewQuote = function(str) {
  return str.replace(goog.i18n.bidi.doubleQuoteSubstituteRe_, '$1\u05f4')
      .replace(goog.i18n.bidi.singleQuoteSubstituteRe_, '$1\u05f3');
};


/**
 * Regular expression to split a string into "words" for directionality
 * estimation based on relative word counts.
 * @type {RegExp}
 * @private
 */
goog.i18n.bidi.wordSeparatorRe_ = /\s+/;


/**
 * Regular expression to check if a string contains any numerals. Used to
 * differentiate between completely neutral strings and those containing
 * numbers, which are weakly LTR.
 *
 * Native Arabic digits (\u0660 - \u0669) are not included because although they
 * do flow left-to-right inside a number, this is the case even if the  overall
 * directionality is RTL, and a mathematical expression using these digits is
 * supposed to flow right-to-left overall, including unary plus and minus
 * appearing to the right of a number, and this does depend on the overall
 * directionality being RTL. The digits used in Farsi (\u06F0 - \u06F9), on the
 * other hand, are included, since Farsi math (including unary plus and minus)
 * does flow left-to-right.
 * TODO: Consider other systems of digits, e.g., Adlam.
 *
 * @type {RegExp}
 * @private
 */
goog.i18n.bidi.hasNumeralsRe_ = /[\d\u06f0-\u06f9]/;


/**
 * This constant controls threshold of RTL directionality.
 * @type {number}
 * @private
 */
goog.i18n.bidi.rtlDetectionThreshold_ = 0.40;


/**
 * Estimates the directionality of a string based on relative word counts.
 * If the number of RTL words is above a certain percentage of the total number
 * of strongly directional words, returns RTL.
 * Otherwise, if any words are strongly or weakly LTR, returns LTR.
 * Otherwise, returns UNKNOWN, which is used to mean "neutral".
 * Numbers are counted as weakly LTR.
 * @param {string} str The string to be checked.
 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
 *     Default: false.
 * @return {goog.i18n.bidi.Dir} Estimated overall directionality of `str`.
 */
goog.i18n.bidi.estimateDirection = function(str, opt_isHtml) {
  let rtlCount = 0;
  let totalCount = 0;
  let hasWeaklyLtr = false;
  const tokens = goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml)
                     .split(goog.i18n.bidi.wordSeparatorRe_);
  for (let i = 0; i < tokens.length; i++) {
    const token = tokens[i];
    if (goog.i18n.bidi.startsWithRtl(token)) {
      rtlCount++;
      totalCount++;
    } else if (goog.i18n.bidi.isRequiredLtrRe_.test(token)) {
      hasWeaklyLtr = true;
    } else if (goog.i18n.bidi.hasAnyLtr(token)) {
      totalCount++;
    } else if (goog.i18n.bidi.hasNumeralsRe_.test(token)) {
      hasWeaklyLtr = true;
    }
  }

  return totalCount == 0 ?
      (hasWeaklyLtr ? goog.i18n.bidi.Dir.LTR : goog.i18n.bidi.Dir.NEUTRAL) :
      (rtlCount / totalCount > goog.i18n.bidi.rtlDetectionThreshold_ ?
           goog.i18n.bidi.Dir.RTL :
           goog.i18n.bidi.Dir.LTR);
};


/**
 * Check the directionality of a piece of text, return true if the piece of
 * text should be laid out in RTL direction.
 * @param {string} str The piece of text that need to be detected.
 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
 *     Default: false.
 * @return {boolean} Whether this piece of text should be laid out in RTL.
 */
goog.i18n.bidi.detectRtlDirectionality = function(str, opt_isHtml) {
  return goog.i18n.bidi.estimateDirection(str, opt_isHtml) ==
      goog.i18n.bidi.Dir.RTL;
};


/**
 * Sets text input element's directionality and text alignment based on a
 * given directionality. Does nothing if the given directionality is unknown or
 * neutral.
 * @param {Element} element Input field element to set directionality to.
 * @param {goog.i18n.bidi.Dir|number|boolean|null} dir Desired directionality,
 *     given in one of the following formats:
 *     1. A goog.i18n.bidi.Dir constant.
 *     2. A number (positive = LRT, negative = RTL, 0 = neutral).
 *     3. A boolean (true = RTL, false = LTR).
 *     4. A null for unknown directionality.
 */
goog.i18n.bidi.setElementDirAndAlign = function(element, dir) {
  if (element) {
    const htmlElement = /** @type {!HTMLElement} */ (element);
    dir = goog.i18n.bidi.toDir(dir);
    if (dir) {
      htmlElement.style.textAlign = dir == goog.i18n.bidi.Dir.RTL ?
          goog.i18n.bidi.RIGHT :
          goog.i18n.bidi.LEFT;
      htmlElement.dir = dir == goog.i18n.bidi.Dir.RTL ? 'rtl' : 'ltr';
    }
  }
};


/**
 * Sets element dir based on estimated directionality of the given text.
 * @param {!Element} element
 * @param {string} text
 */
goog.i18n.bidi.setElementDirByTextDirectionality = function(element, text) {
  const htmlElement = /** @type {!HTMLElement} */ (element);
  switch (goog.i18n.bidi.estimateDirection(text)) {
    case (goog.i18n.bidi.Dir.LTR):
      htmlElement.dir = 'ltr';
      break;
    case (goog.i18n.bidi.Dir.RTL):
      htmlElement.dir = 'rtl';
      break;
    default:
      // Default for no direction, inherit from document.
      htmlElement.removeAttribute('dir');
  }
};



/**
 * Strings that have an (optional) known direction.
 *
 * Implementations of this interface are string-like objects that carry an
 * attached direction, if known.
 * @interface
 */
goog.i18n.bidi.DirectionalString = function() {};


/**
 * Interface marker of the DirectionalString interface.
 *
 * This property can be used to determine at runtime whether or not an object
 * implements this interface.  All implementations of this interface set this
 * property to `true`.
 * @type {boolean}
 */
goog.i18n.bidi.DirectionalString.prototype
    .implementsGoogI18nBidiDirectionalString;


/**
 * Retrieves this object's known direction (if any).
 * @return {?goog.i18n.bidi.Dir} The known direction. Null if unknown.
 */
goog.i18n.bidi.DirectionalString.prototype.getDirection;

//third_party/javascript/closure/html/trustedresourceurl.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview The TrustedResourceUrl type and its builders.
 *
 * TODO(xtof): Link to document stating type contract.
 */

goog.provide('goog.html.TrustedResourceUrl');

goog.require('goog.asserts');
goog.require('goog.fs.blob');
goog.require('goog.fs.url');
goog.require('goog.html.SafeScript');
goog.require('goog.html.trustedtypes');
goog.require('goog.i18n.bidi.Dir');
goog.require('goog.i18n.bidi.DirectionalString');
goog.require('goog.string.Const');
goog.require('goog.string.TypedString');



/**
 * A URL which is under application control and from which script, CSS, and
 * other resources that represent executable code, can be fetched.
 *
 * Given that the URL can only be constructed from strings under application
 * control and is used to load resources, bugs resulting in a malformed URL
 * should not have a security impact and are likely to be easily detectable
 * during testing. Given the wide number of non-RFC compliant URLs in use,
 * stricter validation could prevent some applications from being able to use
 * this type.
 *
 * Instances of this type must be created via the factory method,
 * (`fromConstant`, `fromConstants`, `format` or
 * `formatWithParams`), and not by invoking its constructor. The constructor
 * is organized in a way that only methods from that file can call it and
 * initialize with non-empty values. Anyone else calling constructor will
 * get default instance with empty value.
 *
 * @see goog.html.TrustedResourceUrl#fromConstant
 * @constructor
 * @final
 * @struct
 * @implements {goog.i18n.bidi.DirectionalString}
 * @implements {goog.string.TypedString}
 * @param {!Object=} opt_token package-internal implementation detail.
 * @param {!TrustedScriptURL|string=} opt_content package-internal
 *     implementation detail.
 */
goog.html.TrustedResourceUrl = function(opt_token, opt_content) {
  /**
   * The contained value of this TrustedResourceUrl.  The field has a purposely
   * ugly name to make (non-compiled) code that attempts to directly access this
   * field stand out.
   * @const
   * @private {!TrustedScriptURL|string}
   */
  this.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_ =
      ((opt_token ===
        goog.html.TrustedResourceUrl.CONSTRUCTOR_TOKEN_PRIVATE_) &&
       opt_content) ||
      '';

  /**
   * A type marker used to implement additional run-time type checking.
   * @see goog.html.TrustedResourceUrl#unwrap
   * @const {!Object}
   * @private
   */
  this.TRUSTED_RESOURCE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
      goog.html.TrustedResourceUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
};


/**
 * @override
 * @const
 */
goog.html.TrustedResourceUrl.prototype.implementsGoogStringTypedString = true;


/**
 * Returns this TrustedResourceUrl's value as a string.
 *
 * IMPORTANT: In code where it is security relevant that an object's type is
 * indeed `TrustedResourceUrl`, use
 * `goog.html.TrustedResourceUrl.unwrap` instead of this method. If in
 * doubt, assume that it's security relevant. In particular, note that
 * goog.html functions which return a goog.html type do not guarantee that
 * the returned instance is of the right type. For example:
 *
 * <pre>
 * var fakeSafeHtml = new String('fake');
 * fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
 * var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
 * // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
 * // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml instanceof
 * // goog.html.SafeHtml.
 * </pre>
 *
 * @see goog.html.TrustedResourceUrl#unwrap
 * @override
 */
goog.html.TrustedResourceUrl.prototype.getTypedStringValue = function() {
  return this.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_
      .toString();
};


/**
 * @override
 * @const
 */
goog.html.TrustedResourceUrl.prototype.implementsGoogI18nBidiDirectionalString =
    true;


/**
 * Returns this URLs directionality, which is always `LTR`.
 * @override
 */
goog.html.TrustedResourceUrl.prototype.getDirection = function() {
  return goog.i18n.bidi.Dir.LTR;
};


/**
 * Creates a new TrustedResourceUrl with params added to URL. Both search and
 * hash params can be specified.
 *
 * @param {string|?Object<string, *>|undefined} searchParams Search parameters
 *     to add to URL. See goog.html.TrustedResourceUrl.stringifyParams_ for
 *     exact format definition.
 * @param {(string|?Object<string, *>)=} opt_hashParams Hash parameters to add
 *     to URL. See goog.html.TrustedResourceUrl.stringifyParams_ for exact
 *     format definition.
 * @return {!goog.html.TrustedResourceUrl} New TrustedResourceUrl with params.
 */
goog.html.TrustedResourceUrl.prototype.cloneWithParams = function(
    searchParams, opt_hashParams) {
  var url = goog.html.TrustedResourceUrl.unwrap(this);
  var parts = goog.html.TrustedResourceUrl.URL_PARAM_PARSER_.exec(url);
  var urlBase = parts[1];
  var urlSearch = parts[2] || '';
  var urlHash = parts[3] || '';

  return goog.html.TrustedResourceUrl
      .createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(
          urlBase +
          goog.html.TrustedResourceUrl.stringifyParams_(
              '?', urlSearch, searchParams) +
          goog.html.TrustedResourceUrl.stringifyParams_(
              '#', urlHash, opt_hashParams));
};


if (goog.DEBUG) {
  /**
   * Returns a debug string-representation of this value.
   *
   * To obtain the actual string value wrapped in a TrustedResourceUrl, use
   * `goog.html.TrustedResourceUrl.unwrap`.
   *
   * @see goog.html.TrustedResourceUrl#unwrap
   * @override
   */
  goog.html.TrustedResourceUrl.prototype.toString = function() {
    return 'TrustedResourceUrl{' +
        this.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_ + '}';
  };
}


/**
 * Performs a runtime check that the provided object is indeed a
 * TrustedResourceUrl object, and returns its value.
 *
 * @param {!goog.html.TrustedResourceUrl} trustedResourceUrl The object to
 *     extract from.
 * @return {string} The trustedResourceUrl object's contained string, unless
 *     the run-time type check fails. In that case, `unwrap` returns an
 *     innocuous string, or, if assertions are enabled, throws
 *     `goog.asserts.AssertionError`.
 */
goog.html.TrustedResourceUrl.unwrap = function(trustedResourceUrl) {
  return goog.html.TrustedResourceUrl.unwrapTrustedScriptURL(trustedResourceUrl)
      .toString();
};


/**
 * Unwraps value as TrustedScriptURL if supported or as a string if not.
 * @param {!goog.html.TrustedResourceUrl} trustedResourceUrl
 * @return {!TrustedScriptURL|string}
 * @see goog.html.TrustedResourceUrl.unwrap
 */
goog.html.TrustedResourceUrl.unwrapTrustedScriptURL = function(
    trustedResourceUrl) {
  // Perform additional Run-time type-checking to ensure that
  // trustedResourceUrl is indeed an instance of the expected type.  This
  // provides some additional protection against security bugs due to
  // application code that disables type checks.
  // Specifically, the following checks are performed:
  // 1. The object is an instance of the expected type.
  // 2. The object is not an instance of a subclass.
  // 3. The object carries a type marker for the expected type. "Faking" an
  // object requires a reference to the type marker, which has names intended
  // to stand out in code reviews.
  if (trustedResourceUrl instanceof goog.html.TrustedResourceUrl &&
      trustedResourceUrl.constructor === goog.html.TrustedResourceUrl &&
      trustedResourceUrl
              .TRUSTED_RESOURCE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
          goog.html.TrustedResourceUrl
              .TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
    return trustedResourceUrl
        .privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_;
  } else {
    goog.asserts.fail('expected object of type TrustedResourceUrl, got \'' +
        trustedResourceUrl + '\' of type ' + goog.typeOf(trustedResourceUrl));
    return 'type_error:TrustedResourceUrl';
  }
};


/**
 * Creates a TrustedResourceUrl from a format string and arguments.
 *
 * The arguments for interpolation into the format string map labels to values.
 * Values of type `goog.string.Const` are interpolated without modifcation.
 * Values of other types are cast to string and encoded with
 * encodeURIComponent.
 *
 * `%{<label>}` markers are used in the format string to indicate locations
 * to be interpolated with the valued mapped to the given label. `<label>`
 * must contain only alphanumeric and `_` characters.
 *
 * The format string must match goog.html.TrustedResourceUrl.BASE_URL_.
 *
 * Example usage:
 *
 *    var url = goog.html.TrustedResourceUrl.format(goog.string.Const.from(
 *        'https://www.google.com/search?q=%{query}'), {'query': searchTerm});
 *
 *    var url = goog.html.TrustedResourceUrl.format(goog.string.Const.from(
 *        '//www.youtube.com/v/%{videoId}?hl=en&fs=1%{autoplay}'), {
 *        'videoId': videoId,
 *        'autoplay': opt_autoplay ?
 *            goog.string.Const.from('&autoplay=1') : goog.string.Const.EMPTY
 *    });
 *
 * While this function can be used to create a TrustedResourceUrl from only
 * constants, fromConstant() and fromConstants() are generally preferable for
 * that purpose.
 *
 * @param {!goog.string.Const} format The format string.
 * @param {!Object<string, (string|number|!goog.string.Const)>} args Mapping
 *     of labels to values to be interpolated into the format string.
 *     goog.string.Const values are interpolated without encoding.
 * @return {!goog.html.TrustedResourceUrl}
 * @throws {!Error} On an invalid format string or if a label used in the
 *     the format string is not present in args.
 */
goog.html.TrustedResourceUrl.format = function(format, args) {
  var formatStr = goog.string.Const.unwrap(format);
  if (!goog.html.TrustedResourceUrl.BASE_URL_.test(formatStr)) {
    throw new Error('Invalid TrustedResourceUrl format: ' + formatStr);
  }
  var result = formatStr.replace(
      goog.html.TrustedResourceUrl.FORMAT_MARKER_, function(match, id) {
        if (!Object.prototype.hasOwnProperty.call(args, id)) {
          throw new Error(
              'Found marker, "' + id + '", in format string, "' + formatStr +
              '", but no valid label mapping found ' +
              'in args: ' + JSON.stringify(args));
        }
        var arg = args[id];
        if (arg instanceof goog.string.Const) {
          return goog.string.Const.unwrap(arg);
        } else {
          return encodeURIComponent(String(arg));
        }
      });
  return goog.html.TrustedResourceUrl
      .createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(result);
};


/**
 * @private @const {!RegExp}
 */
goog.html.TrustedResourceUrl.FORMAT_MARKER_ = /%{(\w+)}/g;


/**
 * The URL must be absolute, scheme-relative or path-absolute. So it must
 * start with:
 * - https:// followed by allowed origin characters.
 * - // followed by allowed origin characters.
 * - Any absolute or relative path.
 *
 * Based on
 * https://url.spec.whatwg.org/commit-snapshots/56b74ce7cca8883eab62e9a12666e2fac665d03d/#url-parsing
 * an initial / which is not followed by another / or \ will end up in the "path
 * state" and from there it can only go to "fragment state" and "query state".
 *
 * We don't enforce a well-formed domain name. So '.' or '1.2' are valid.
 * That's ok because the origin comes from a compile-time constant.
 *
 * A regular expression is used instead of goog.uri for several reasons:
 * - Strictness. E.g. we don't want any userinfo component and we don't
 *   want '/./, nor \' in the first path component.
 * - Small trusted base. goog.uri is generic and might need to change,
 *   reasoning about all the ways it can parse a URL now and in the future
 *   is error-prone.
 * - Code size. We expect many calls to .format(), many of which might
 *   not be using goog.uri.
 * - Simplicity. Using goog.uri would likely not result in simpler nor shorter
 *   code.
 * @private @const {!RegExp}
 */
goog.html.TrustedResourceUrl.BASE_URL_ = new RegExp(
    '^((https:)?//[0-9a-z.:[\\]-]+/'  // Origin.
        + '|/[^/\\\\]'                // Absolute path.
        + '|[^:/\\\\%]+/'             // Relative path.
        + '|[^:/\\\\%]*[?#]'          // Query string or fragment.
        + '|about:blank#'             // about:blank with fragment.
        + ')',
    'i');

/**
 * RegExp for splitting a URL into the base, search field, and hash field.
 *
 * @private @const {!RegExp}
 */
goog.html.TrustedResourceUrl.URL_PARAM_PARSER_ =
    /^([^?#]*)(\?[^#]*)?(#[\s\S]*)?/;


/**
 * Formats the URL same as TrustedResourceUrl.format and then adds extra URL
 * parameters.
 *
 * Example usage:
 *
 *     // Creates '//www.youtube.com/v/abc?autoplay=1' for videoId='abc' and
 *     // opt_autoplay=1. Creates '//www.youtube.com/v/abc' for videoId='abc'
 *     // and opt_autoplay=undefined.
 *     var url = goog.html.TrustedResourceUrl.formatWithParams(
 *         goog.string.Const.from('//www.youtube.com/v/%{videoId}'),
 *         {'videoId': videoId},
 *         {'autoplay': opt_autoplay});
 *
 * @param {!goog.string.Const} format The format string.
 * @param {!Object<string, (string|number|!goog.string.Const)>} args Mapping
 *     of labels to values to be interpolated into the format string.
 *     goog.string.Const values are interpolated without encoding.
 * @param {string|?Object<string, *>|undefined} searchParams Parameters to add
 *     to URL. See goog.html.TrustedResourceUrl.stringifyParams_ for exact
 *     format definition.
 * @param {(string|?Object<string, *>)=} opt_hashParams Hash parameters to add
 *     to URL. See goog.html.TrustedResourceUrl.stringifyParams_ for exact
 *     format definition.
 * @return {!goog.html.TrustedResourceUrl}
 * @throws {!Error} On an invalid format string or if a label used in the
 *     the format string is not present in args.
 */
goog.html.TrustedResourceUrl.formatWithParams = function(
    format, args, searchParams, opt_hashParams) {
  var url = goog.html.TrustedResourceUrl.format(format, args);
  return url.cloneWithParams(searchParams, opt_hashParams);
};


/**
 * Creates a TrustedResourceUrl object from a compile-time constant string.
 *
 * Compile-time constant strings are inherently program-controlled and hence
 * trusted.
 *
 * @param {!goog.string.Const} url A compile-time-constant string from which to
 *     create a TrustedResourceUrl.
 * @return {!goog.html.TrustedResourceUrl} A TrustedResourceUrl object
 *     initialized to `url`.
 */
goog.html.TrustedResourceUrl.fromConstant = function(url) {
  return goog.html.TrustedResourceUrl
      .createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(
          goog.string.Const.unwrap(url));
};


/**
 * Creates a TrustedResourceUrl object from a compile-time constant strings.
 *
 * Compile-time constant strings are inherently program-controlled and hence
 * trusted.
 *
 * @param {!Array<!goog.string.Const>} parts Compile-time-constant strings from
 *     which to create a TrustedResourceUrl.
 * @return {!goog.html.TrustedResourceUrl} A TrustedResourceUrl object
 *     initialized to concatenation of `parts`.
 */
goog.html.TrustedResourceUrl.fromConstants = function(parts) {
  var unwrapped = '';
  for (var i = 0; i < parts.length; i++) {
    unwrapped += goog.string.Const.unwrap(parts[i]);
  }
  return goog.html.TrustedResourceUrl
      .createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(unwrapped);
};

/**
 * Creates a TrustedResourceUrl object by generating a Blob from a SafeScript
 * object and then calling createObjectURL with that blob.
 *
 * SafeScript objects are trusted to contain executable JavaScript code.
 *
 * Caller must call goog.fs.url.revokeObjectUrl() on the unwrapped url to
 * release the underlying blob.
 *
 * Throws if browser doesn't support blob construction.
 *
 * @param {!goog.html.SafeScript} safeScript A script from which to create a
 *     TrustedResourceUrl.
 * @return {!goog.html.TrustedResourceUrl} A TrustedResourceUrl object
 *     initialized to a new blob URL.
 */
goog.html.TrustedResourceUrl.fromSafeScript = function(safeScript) {
  var blob = goog.fs.blob.getBlobWithProperties(
      [goog.html.SafeScript.unwrap(safeScript)], 'text/javascript');
  var url = goog.fs.url.createObjectUrl(blob);
  return goog.html.TrustedResourceUrl
      .createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(url);
};


/**
 * Type marker for the TrustedResourceUrl type, used to implement additional
 * run-time type checking.
 * @const {!Object}
 * @private
 */
goog.html.TrustedResourceUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};


/**
 * Package-internal utility method to create TrustedResourceUrl instances.
 *
 * @param {string} url The string to initialize the TrustedResourceUrl object
 *     with.
 * @return {!goog.html.TrustedResourceUrl} The initialized TrustedResourceUrl
 *     object.
 * @package
 */
goog.html.TrustedResourceUrl
    .createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse = function(url) {
  var value = goog.html.trustedtypes.PRIVATE_DO_NOT_ACCESS_OR_ELSE_POLICY ?
      goog.html.trustedtypes.PRIVATE_DO_NOT_ACCESS_OR_ELSE_POLICY
          .createScriptURL(url) :
      url;
  return new goog.html.TrustedResourceUrl(
      goog.html.TrustedResourceUrl.CONSTRUCTOR_TOKEN_PRIVATE_, value);
};


/**
 * Stringifies the passed params to be used as either a search or hash field of
 * a URL.
 *
 * @param {string} prefix The prefix character for the given field ('?' or '#').
 * @param {string} currentString The existing field value (including the prefix
 *     character, if the field is present).
 * @param {string|?Object<string, *>|undefined} params The params to set or
 *     append to the field.
 * - If `undefined` or `null`, the field remains unchanged.
 * - If a string, then the string will be escaped and the field will be
 *   overwritten with that value.
 * - If an Object, that object is treated as a set of key-value pairs to be
 *   appended to the current field. Note that JavaScript doesn't guarantee the
 *   order of values in an object which might result in non-deterministic order
 *   of the parameters. However, browsers currently preserve the order. The
 *   rules for each entry:
 *   - If an array, it will be processed as if each entry were an additional
 *     parameter with exactly the same key, following the same logic below.
 *   - If `undefined` or `null`, it will be skipped.
 *   - Otherwise, it will be turned into a string, escaped, and appended.
 * @return {string}
 * @private
 */
goog.html.TrustedResourceUrl.stringifyParams_ = function(
    prefix, currentString, params) {
  if (params == null) {
    // Do not modify the field.
    return currentString;
  }
  if (typeof params === 'string') {
    // Set field to the passed string.
    return params ? prefix + encodeURIComponent(params) : '';
  }
  // Add on parameters to field from key-value object.
  for (var key in params) {
    var value = params[key];
    var outputValues = Array.isArray(value) ? value : [value];
    for (var i = 0; i < outputValues.length; i++) {
      var outputValue = outputValues[i];
      if (outputValue != null) {
        if (!currentString) {
          currentString = prefix;
        }
        currentString += (currentString.length > prefix.length ? '&' : '') +
            encodeURIComponent(key) + '=' +
            encodeURIComponent(String(outputValue));
      }
    }
  }
  return currentString;
};

/**
 * Token used to ensure that object is created only from this file. No code
 * outside of this file can access this token.
 * @private {!Object}
 * @const
 */
goog.html.TrustedResourceUrl.CONSTRUCTOR_TOKEN_PRIVATE_ = {};

//third_party/javascript/closure/string/internal.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview String functions called from Closure packages that couldn't
 * depend on each other. Outside Closure, use goog.string function which
 * delegate to these.
 * @visibility {//javascript/closure:__pkg__}
 * @visibility {//javascript/closure/bin/sizetests:__pkg__}
 * @visibility {//javascript/closure/dom:__pkg__}
 * @visibility {//javascript/closure/html:__pkg__}
 * @visibility {//javascript/closure/labs/useragent:__pkg__}
 */


goog.provide('goog.string.internal');


/**
 * Fast prefix-checker.
 * @param {string} str The string to check.
 * @param {string} prefix A string to look for at the start of `str`.
 * @return {boolean} True if `str` begins with `prefix`.
 * @see goog.string.startsWith
 */
goog.string.internal.startsWith = function(str, prefix) {
  return str.lastIndexOf(prefix, 0) == 0;
};


/**
 * Fast suffix-checker.
 * @param {string} str The string to check.
 * @param {string} suffix A string to look for at the end of `str`.
 * @return {boolean} True if `str` ends with `suffix`.
 * @see goog.string.endsWith
 */
goog.string.internal.endsWith = function(str, suffix) {
  const l = str.length - suffix.length;
  return l >= 0 && str.indexOf(suffix, l) == l;
};


/**
 * Case-insensitive prefix-checker.
 * @param {string} str The string to check.
 * @param {string} prefix  A string to look for at the end of `str`.
 * @return {boolean} True if `str` begins with `prefix` (ignoring
 *     case).
 * @see goog.string.caseInsensitiveStartsWith
 */
goog.string.internal.caseInsensitiveStartsWith = function(str, prefix) {
  return goog.string.internal.caseInsensitiveCompare(
             prefix, str.substr(0, prefix.length)) == 0;
};


/**
 * Case-insensitive suffix-checker.
 * @param {string} str The string to check.
 * @param {string} suffix A string to look for at the end of `str`.
 * @return {boolean} True if `str` ends with `suffix` (ignoring
 *     case).
 * @see goog.string.caseInsensitiveEndsWith
 */
goog.string.internal.caseInsensitiveEndsWith = function(str, suffix) {
  return (
      goog.string.internal.caseInsensitiveCompare(
          suffix, str.substr(str.length - suffix.length, suffix.length)) == 0);
};


/**
 * Case-insensitive equality checker.
 * @param {string} str1 First string to check.
 * @param {string} str2 Second string to check.
 * @return {boolean} True if `str1` and `str2` are the same string,
 *     ignoring case.
 * @see goog.string.caseInsensitiveEquals
 */
goog.string.internal.caseInsensitiveEquals = function(str1, str2) {
  return str1.toLowerCase() == str2.toLowerCase();
};


/**
 * Checks if a string is empty or contains only whitespaces.
 * @param {string} str The string to check.
 * @return {boolean} Whether `str` is empty or whitespace only.
 * @see goog.string.isEmptyOrWhitespace
 */
goog.string.internal.isEmptyOrWhitespace = function(str) {
  // testing length == 0 first is actually slower in all browsers (about the
  // same in Opera).
  // Since IE doesn't include non-breaking-space (0xa0) in their \s character
  // class (as required by section 7.2 of the ECMAScript spec), we explicitly
  // include it in the regexp to enforce consistent cross-browser behavior.
  return /^[\s\xa0]*$/.test(str);
};


/**
 * Trims white spaces to the left and right of a string.
 * @param {string} str The string to trim.
 * @return {string} A trimmed copy of `str`.
 */
goog.string.internal.trim =
    (goog.TRUSTED_SITE && String.prototype.trim) ? function(str) {
      return str.trim();
    } : function(str) {
      // Since IE doesn't include non-breaking-space (0xa0) in their \s
      // character class (as required by section 7.2 of the ECMAScript spec),
      // we explicitly include it in the regexp to enforce consistent
      // cross-browser behavior.
      // NOTE: We don't use String#replace because it might have side effects
      // causing this function to not compile to 0 bytes.
      return /^[\s\xa0]*([\s\S]*?)[\s\xa0]*$/.exec(str)[1];
    };


/**
 * A string comparator that ignores case.
 * -1 = str1 less than str2
 *  0 = str1 equals str2
 *  1 = str1 greater than str2
 *
 * @param {string} str1 The string to compare.
 * @param {string} str2 The string to compare `str1` to.
 * @return {number} The comparator result, as described above.
 * @see goog.string.caseInsensitiveCompare
 */
goog.string.internal.caseInsensitiveCompare = function(str1, str2) {
  const test1 = String(str1).toLowerCase();
  const test2 = String(str2).toLowerCase();

  if (test1 < test2) {
    return -1;
  } else if (test1 == test2) {
    return 0;
  } else {
    return 1;
  }
};


/**
 * Converts \n to <br>s or <br />s.
 * @param {string} str The string in which to convert newlines.
 * @param {boolean=} opt_xml Whether to use XML compatible tags.
 * @return {string} A copy of `str` with converted newlines.
 * @see goog.string.newLineToBr
 */
goog.string.internal.newLineToBr = function(str, opt_xml) {
  return str.replace(/(\r\n|\r|\n)/g, opt_xml ? '<br />' : '<br>');
};


/**
 * Escapes double quote '"' and single quote '\'' characters in addition to
 * '&', '<', and '>' so that a string can be included in an HTML tag attribute
 * value within double or single quotes.
 * @param {string} str string to be escaped.
 * @param {boolean=} opt_isLikelyToContainHtmlChars
 * @return {string} An escaped copy of `str`.
 * @see goog.string.htmlEscape
 */
goog.string.internal.htmlEscape = function(
    str, opt_isLikelyToContainHtmlChars) {
  if (opt_isLikelyToContainHtmlChars) {
    str = str.replace(goog.string.internal.AMP_RE_, '&amp;')
              .replace(goog.string.internal.LT_RE_, '&lt;')
              .replace(goog.string.internal.GT_RE_, '&gt;')
              .replace(goog.string.internal.QUOT_RE_, '&quot;')
              .replace(goog.string.internal.SINGLE_QUOTE_RE_, '&#39;')
              .replace(goog.string.internal.NULL_RE_, '&#0;');
    return str;

  } else {
    // quick test helps in the case when there are no chars to replace, in
    // worst case this makes barely a difference to the time taken
    if (!goog.string.internal.ALL_RE_.test(str)) return str;

    // str.indexOf is faster than regex.test in this case
    if (str.indexOf('&') != -1) {
      str = str.replace(goog.string.internal.AMP_RE_, '&amp;');
    }
    if (str.indexOf('<') != -1) {
      str = str.replace(goog.string.internal.LT_RE_, '&lt;');
    }
    if (str.indexOf('>') != -1) {
      str = str.replace(goog.string.internal.GT_RE_, '&gt;');
    }
    if (str.indexOf('"') != -1) {
      str = str.replace(goog.string.internal.QUOT_RE_, '&quot;');
    }
    if (str.indexOf('\'') != -1) {
      str = str.replace(goog.string.internal.SINGLE_QUOTE_RE_, '&#39;');
    }
    if (str.indexOf('\x00') != -1) {
      str = str.replace(goog.string.internal.NULL_RE_, '&#0;');
    }
    return str;
  }
};


/**
 * Regular expression that matches an ampersand, for use in escaping.
 * @const {!RegExp}
 * @private
 */
goog.string.internal.AMP_RE_ = /&/g;


/**
 * Regular expression that matches a less than sign, for use in escaping.
 * @const {!RegExp}
 * @private
 */
goog.string.internal.LT_RE_ = /</g;


/**
 * Regular expression that matches a greater than sign, for use in escaping.
 * @const {!RegExp}
 * @private
 */
goog.string.internal.GT_RE_ = />/g;


/**
 * Regular expression that matches a double quote, for use in escaping.
 * @const {!RegExp}
 * @private
 */
goog.string.internal.QUOT_RE_ = /"/g;


/**
 * Regular expression that matches a single quote, for use in escaping.
 * @const {!RegExp}
 * @private
 */
goog.string.internal.SINGLE_QUOTE_RE_ = /'/g;


/**
 * Regular expression that matches null character, for use in escaping.
 * @const {!RegExp}
 * @private
 */
goog.string.internal.NULL_RE_ = /\x00/g;


/**
 * Regular expression that matches any character that needs to be escaped.
 * @const {!RegExp}
 * @private
 */
goog.string.internal.ALL_RE_ = /[\x00&<>"']/;


/**
 * Do escaping of whitespace to preserve spatial formatting. We use character
 * entity #160 to make it safer for xml.
 * @param {string} str The string in which to escape whitespace.
 * @param {boolean=} opt_xml Whether to use XML compatible tags.
 * @return {string} An escaped copy of `str`.
 * @see goog.string.whitespaceEscape
 */
goog.string.internal.whitespaceEscape = function(str, opt_xml) {
  // This doesn't use goog.string.preserveSpaces for backwards compatibility.
  return goog.string.internal.newLineToBr(
      str.replace(/  /g, ' &#160;'), opt_xml);
};


/**
 * Determines whether a string contains a substring.
 * @param {string} str The string to search.
 * @param {string} subString The substring to search for.
 * @return {boolean} Whether `str` contains `subString`.
 * @see goog.string.contains
 */
goog.string.internal.contains = function(str, subString) {
  return str.indexOf(subString) != -1;
};


/**
 * Determines whether a string contains a substring, ignoring case.
 * @param {string} str The string to search.
 * @param {string} subString The substring to search for.
 * @return {boolean} Whether `str` contains `subString`.
 * @see goog.string.caseInsensitiveContains
 */
goog.string.internal.caseInsensitiveContains = function(str, subString) {
  return goog.string.internal.contains(
      str.toLowerCase(), subString.toLowerCase());
};


/**
 * Compares two version numbers.
 *
 * @param {string|number} version1 Version of first item.
 * @param {string|number} version2 Version of second item.
 *
 * @return {number}  1 if `version1` is higher.
 *                   0 if arguments are equal.
 *                  -1 if `version2` is higher.
 * @see goog.string.compareVersions
 */
goog.string.internal.compareVersions = function(version1, version2) {
  let order = 0;
  // Trim leading and trailing whitespace and split the versions into
  // subversions.
  const v1Subs = goog.string.internal.trim(String(version1)).split('.');
  const v2Subs = goog.string.internal.trim(String(version2)).split('.');
  const subCount = Math.max(v1Subs.length, v2Subs.length);

  // Iterate over the subversions, as long as they appear to be equivalent.
  for (let subIdx = 0; order == 0 && subIdx < subCount; subIdx++) {
    let v1Sub = v1Subs[subIdx] || '';
    let v2Sub = v2Subs[subIdx] || '';

    do {
      // Split the subversions into pairs of numbers and qualifiers (like 'b').
      // Two different RegExp objects are use to make it clear the code
      // is side-effect free
      const v1Comp = /(\d*)(\D*)(.*)/.exec(v1Sub) || ['', '', '', ''];
      const v2Comp = /(\d*)(\D*)(.*)/.exec(v2Sub) || ['', '', '', ''];
      // Break if there are no more matches.
      if (v1Comp[0].length == 0 && v2Comp[0].length == 0) {
        break;
      }

      // Parse the numeric part of the subversion. A missing number is
      // equivalent to 0.
      const v1CompNum = v1Comp[1].length == 0 ? 0 : parseInt(v1Comp[1], 10);
      const v2CompNum = v2Comp[1].length == 0 ? 0 : parseInt(v2Comp[1], 10);

      // Compare the subversion components. The number has the highest
      // precedence. Next, if the numbers are equal, a subversion without any
      // qualifier is always higher than a subversion with any qualifier. Next,
      // the qualifiers are compared as strings.
      order = goog.string.internal.compareElements_(v1CompNum, v2CompNum) ||
          goog.string.internal.compareElements_(
              v1Comp[2].length == 0, v2Comp[2].length == 0) ||
          goog.string.internal.compareElements_(v1Comp[2], v2Comp[2]);
      // Stop as soon as an inequality is discovered.

      v1Sub = v1Comp[3];
      v2Sub = v2Comp[3];
    } while (order == 0);
  }

  return order;
};


/**
 * Compares elements of a version number.
 *
 * @param {string|number|boolean} left An element from a version number.
 * @param {string|number|boolean} right An element from a version number.
 *
 * @return {number}  1 if `left` is higher.
 *                   0 if arguments are equal.
 *                  -1 if `right` is higher.
 * @private
 */
goog.string.internal.compareElements_ = function(left, right) {
  if (left < right) {
    return -1;
  } else if (left > right) {
    return 1;
  }
  return 0;
};

//third_party/javascript/closure/html/safeurl.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview The SafeUrl type and its builders.
 *
 * TODO(xtof): Link to document stating type contract.
 */

goog.provide('goog.html.SafeUrl');

goog.require('goog.asserts');
goog.require('goog.fs.url');
goog.require('goog.html.TrustedResourceUrl');
goog.require('goog.i18n.bidi.Dir');
goog.require('goog.i18n.bidi.DirectionalString');
goog.require('goog.string.Const');
goog.require('goog.string.TypedString');
goog.require('goog.string.internal');



/**
 * A string that is safe to use in URL context in DOM APIs and HTML documents.
 *
 * A SafeUrl is a string-like object that carries the security type contract
 * that its value as a string will not cause untrusted script execution
 * when evaluated as a hyperlink URL in a browser.
 *
 * Values of this type are guaranteed to be safe to use in URL/hyperlink
 * contexts, such as assignment to URL-valued DOM properties, in the sense that
 * the use will not result in a Cross-Site-Scripting vulnerability. Similarly,
 * SafeUrls can be interpolated into the URL context of an HTML template (e.g.,
 * inside a href attribute). However, appropriate HTML-escaping must still be
 * applied.
 *
 * Note that, as documented in `goog.html.SafeUrl.unwrap`, this type's
 * contract does not guarantee that instances are safe to interpolate into HTML
 * without appropriate escaping.
 *
 * Note also that this type's contract does not imply any guarantees regarding
 * the resource the URL refers to.  In particular, SafeUrls are <b>not</b>
 * safe to use in a context where the referred-to resource is interpreted as
 * trusted code, e.g., as the src of a script tag.
 *
 * Instances of this type must be created via the factory methods
 * (`goog.html.SafeUrl.fromConstant`, `goog.html.SafeUrl.sanitize`),
 * etc and not by invoking its constructor. The constructor is organized in a
 * way that only methods from that file can call it and initialize with
 * non-empty values. Anyone else calling constructor will get default instance
 * with empty value.
 *
 * @see goog.html.SafeUrl#fromConstant
 * @see goog.html.SafeUrl#from
 * @see goog.html.SafeUrl#sanitize
 * @constructor
 * @final
 * @struct
 * @implements {goog.i18n.bidi.DirectionalString}
 * @implements {goog.string.TypedString}
 * @param {!Object=} opt_token package-internal implementation detail.
 * @param {string=} opt_content package-internal implementation detail.
 */
goog.html.SafeUrl = function(opt_token, opt_content) {
  /**
   * The contained value of this SafeUrl.  The field has a purposely ugly
   * name to make (non-compiled) code that attempts to directly access this
   * field stand out.
   * @private {string}
   */
  this.privateDoNotAccessOrElseSafeUrlWrappedValue_ =
      ((opt_token === goog.html.SafeUrl.CONSTRUCTOR_TOKEN_PRIVATE_) &&
       opt_content) ||
      '';

  /**
   * A type marker used to implement additional run-time type checking.
   * @see goog.html.SafeUrl#unwrap
   * @const {!Object}
   * @private
   */
  this.SAFE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
      goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
};


/**
 * The innocuous string generated by goog.html.SafeUrl.sanitize when passed
 * an unsafe URL.
 *
 * about:invalid is registered in
 * http://www.w3.org/TR/css3-values/#about-invalid.
 * http://tools.ietf.org/html/rfc6694#section-2.2.1 permits about URLs to
 * contain a fragment, which is not to be considered when determining if an
 * about URL is well-known.
 *
 * Using about:invalid seems preferable to using a fixed data URL, since
 * browsers might choose to not report CSP violations on it, as legitimate
 * CSS function calls to attr() can result in this URL being produced. It is
 * also a standard URL which matches exactly the semantics we need:
 * "The about:invalid URI references a non-existent document with a generic
 * error condition. It can be used when a URI is necessary, but the default
 * value shouldn't be resolveable as any type of document".
 *
 * @const {string}
 */
goog.html.SafeUrl.INNOCUOUS_STRING = 'about:invalid#zClosurez';


/**
 * @override
 * @const
 */
goog.html.SafeUrl.prototype.implementsGoogStringTypedString = true;


/**
 * Returns this SafeUrl's value as a string.
 *
 * IMPORTANT: In code where it is security relevant that an object's type is
 * indeed `SafeUrl`, use `goog.html.SafeUrl.unwrap` instead of this
 * method. If in doubt, assume that it's security relevant. In particular, note
 * that goog.html functions which return a goog.html type do not guarantee that
 * the returned instance is of the right type.
 *
 * IMPORTANT: The guarantees of the SafeUrl type contract only extend to the
 * behavior of browsers when interpreting URLs. Values of SafeUrl objects MUST
 * be appropriately escaped before embedding in a HTML document. Note that the
 * required escaping is context-sensitive (e.g. a different escaping is
 * required for embedding a URL in a style property within a style
 * attribute, as opposed to embedding in a href attribute).
 *
 * @see goog.html.SafeUrl#unwrap
 * @override
 */
goog.html.SafeUrl.prototype.getTypedStringValue = function() {
  return this.privateDoNotAccessOrElseSafeUrlWrappedValue_.toString();
};


/**
 * @override
 * @const
 */
goog.html.SafeUrl.prototype.implementsGoogI18nBidiDirectionalString = true;


/**
 * Returns this URLs directionality, which is always `LTR`.
 * @override
 */
goog.html.SafeUrl.prototype.getDirection = function() {
  return goog.i18n.bidi.Dir.LTR;
};


if (goog.DEBUG) {
  /**
   * Returns a debug string-representation of this value.
   *
   * To obtain the actual string value wrapped in a SafeUrl, use
   * `goog.html.SafeUrl.unwrap`.
   *
   * @see goog.html.SafeUrl#unwrap
   * @override
   */
  goog.html.SafeUrl.prototype.toString = function() {
    return 'SafeUrl{' + this.privateDoNotAccessOrElseSafeUrlWrappedValue_ + '}';
  };
}


/**
 * Performs a runtime check that the provided object is indeed a SafeUrl
 * object, and returns its value.
 *
 * IMPORTANT: The guarantees of the SafeUrl type contract only extend to the
 * behavior of  browsers when interpreting URLs. Values of SafeUrl objects MUST
 * be appropriately escaped before embedding in a HTML document. Note that the
 * required escaping is context-sensitive (e.g. a different escaping is
 * required for embedding a URL in a style property within a style
 * attribute, as opposed to embedding in a href attribute).
 *
 * @param {!goog.html.SafeUrl} safeUrl The object to extract from.
 * @return {string} The SafeUrl object's contained string, unless the run-time
 *     type check fails. In that case, `unwrap` returns an innocuous
 *     string, or, if assertions are enabled, throws
 *     `goog.asserts.AssertionError`.
 */
goog.html.SafeUrl.unwrap = function(safeUrl) {
  // Perform additional Run-time type-checking to ensure that safeUrl is indeed
  // an instance of the expected type.  This provides some additional protection
  // against security bugs due to application code that disables type checks.
  // Specifically, the following checks are performed:
  // 1. The object is an instance of the expected type.
  // 2. The object is not an instance of a subclass.
  // 3. The object carries a type marker for the expected type. "Faking" an
  // object requires a reference to the type marker, which has names intended
  // to stand out in code reviews.
  if (safeUrl instanceof goog.html.SafeUrl &&
      safeUrl.constructor === goog.html.SafeUrl &&
      safeUrl.SAFE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
          goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
    return safeUrl.privateDoNotAccessOrElseSafeUrlWrappedValue_;
  } else {
    goog.asserts.fail('expected object of type SafeUrl, got \'' +
        safeUrl + '\' of type ' + goog.typeOf(safeUrl));
    return 'type_error:SafeUrl';
  }
};


/**
 * Creates a SafeUrl object from a compile-time constant string.
 *
 * Compile-time constant strings are inherently program-controlled and hence
 * trusted.
 *
 * @param {!goog.string.Const} url A compile-time-constant string from which to
 *         create a SafeUrl.
 * @return {!goog.html.SafeUrl} A SafeUrl object initialized to `url`.
 */
goog.html.SafeUrl.fromConstant = function(url) {
  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
      goog.string.Const.unwrap(url));
};


/**
 * A pattern that matches Blob or data types that can have SafeUrls created
 * from URL.createObjectURL(blob) or via a data: URI.
 *
 * This has some parameter support (most notably, we haven't implemented the
 * more complex parts like %-encoded characters or non-alphanumerical ones for
 * simplicity's sake). The specs are fairly complex, and they don't
 * always match Chrome's behavior: we settled on a subset where we're confident
 * all parties involved agree.
 *
 * The spec is available at https://mimesniff.spec.whatwg.org/ (and see
 * https://tools.ietf.org/html/rfc2397 for data: urls, which override some of
 * it).
 * @const
 * @private
 */
goog.html.SAFE_MIME_TYPE_PATTERN_ = new RegExp(
    // Note: Due to content-sniffing concerns, only add MIME types for
    // media formats.
    '^(?:audio/(?:3gpp2|3gpp|aac|L16|midi|mp3|mp4|mpeg|oga|ogg|opus|x-m4a|x-matroska|x-wav|wav|webm)|' +
        'font/\\w+|' +
        'image/(?:bmp|gif|jpeg|jpg|png|tiff|webp|x-icon)|' +
        // TODO(b/68188949): Due to content-sniffing concerns, text/csv should
        // be removed from the whitelist.
        'text/csv|' +
        'video/(?:mpeg|mp4|ogg|webm|quicktime|x-matroska))' +
        '(?:;\\w+=(?:\\w+|"[\\w;,= ]+"))*$',  // MIME type parameters
    'i');


/**
 * @param {string} mimeType The MIME type to check if safe.
 * @return {boolean} True if the MIME type is safe and creating a Blob via
 *   `SafeUrl.fromBlob()` with that type will not fail due to the type. False
 *   otherwise.
 */
goog.html.SafeUrl.isSafeMimeType = function(mimeType) {
  return goog.html.SAFE_MIME_TYPE_PATTERN_.test(mimeType);
};


/**
 * Creates a SafeUrl wrapping a blob URL for the given `blob`.
 *
 * The blob URL is created with `URL.createObjectURL`. If the MIME type
 * for `blob` is not of a known safe audio, image or video MIME type,
 * then the SafeUrl will wrap {@link #INNOCUOUS_STRING}.
 *
 * @see http://www.w3.org/TR/FileAPI/#url
 * @param {!Blob} blob
 * @return {!goog.html.SafeUrl} The blob URL, or an innocuous string wrapped
 *   as a SafeUrl.
 */
goog.html.SafeUrl.fromBlob = function(blob) {
  var url = goog.html.SafeUrl.isSafeMimeType(blob.type) ?
      goog.fs.url.createObjectUrl(blob) :
      goog.html.SafeUrl.INNOCUOUS_STRING;
  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
};


/**
 * Creates a SafeUrl wrapping a blob URL created for a MediaSource.
 * @param {!MediaSource} mediaSource
 * @return {!goog.html.SafeUrl} The blob URL.
 */
goog.html.SafeUrl.fromMediaSource = function(mediaSource) {
  goog.asserts.assert(
      'MediaSource' in goog.global, 'No support for MediaSource');
  const url = mediaSource instanceof MediaSource ?
      goog.fs.url.createObjectUrl(mediaSource) :
      goog.html.SafeUrl.INNOCUOUS_STRING;
  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
};


/**
 * Matches a base-64 data URL, with the first match group being the MIME type.
 * @const
 * @private
 */
goog.html.DATA_URL_PATTERN_ = /^data:(.*);base64,[a-z0-9+\/]+=*$/i;


/**
 * Creates a SafeUrl wrapping a data: URL, after validating it matches a
 * known-safe media MIME type.
 *
 * @param {string} dataUrl A valid base64 data URL with one of the whitelisted
 *     media MIME types.
 * @return {!goog.html.SafeUrl} A matching safe URL, or {@link INNOCUOUS_STRING}
 *     wrapped as a SafeUrl if it does not pass.
 */
goog.html.SafeUrl.fromDataUrl = function(dataUrl) {
  // RFC4648 suggest to ignore CRLF in base64 encoding.
  // See https://tools.ietf.org/html/rfc4648.
  // Remove the CR (%0D) and LF (%0A) from the dataUrl.
  var filteredDataUrl = dataUrl.replace(/(%0A|%0D)/g, '');
  var match = filteredDataUrl.match(goog.html.DATA_URL_PATTERN_);
  // Note: The only risk of XSS here is if the `data:` URL results in a
  // same-origin document. In which case content-sniffing might cause the
  // browser to interpret the contents as html.
  // All modern browsers consider `data:` URL documents to have unique empty
  // origins. Only Firefox for versions prior to v57 behaves differently:
  // https://blog.mozilla.org/security/2017/10/04/treating-data-urls-unique-origins-firefox-57/
  // Older versions of IE don't understand `data:` urls, so it is not an issue.
  var valid = match && goog.html.SafeUrl.isSafeMimeType(match[1]);
  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
      valid ? filteredDataUrl : goog.html.SafeUrl.INNOCUOUS_STRING);
};


/**
 * Creates a SafeUrl wrapping a tel: URL.
 *
 * @param {string} telUrl A tel URL.
 * @return {!goog.html.SafeUrl} A matching safe URL, or {@link INNOCUOUS_STRING}
 *     wrapped as a SafeUrl if it does not pass.
 */
goog.html.SafeUrl.fromTelUrl = function(telUrl) {
  // There's a risk that a tel: URL could immediately place a call once
  // clicked, without requiring user confirmation. For that reason it is
  // handled in this separate function.
  if (!goog.string.internal.caseInsensitiveStartsWith(telUrl, 'tel:')) {
    telUrl = goog.html.SafeUrl.INNOCUOUS_STRING;
  }
  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
      telUrl);
};


/**
 * Matches a sip/sips URL. We only allow urls that consist of an email address.
 * The characters '?' and '#' are not allowed in the local part of the email
 * address.
 * @const
 * @private
 */
goog.html.SIP_URL_PATTERN_ = new RegExp(
    '^sip[s]?:[+a-z0-9_.!$%&\'*\\/=^`{|}~-]+@([a-z0-9-]+\\.)+[a-z0-9]{2,63}$',
    'i');


/**
 * Creates a SafeUrl wrapping a sip: URL. We only allow urls that consist of an
 * email address. The characters '?' and '#' are not allowed in the local part
 * of the email address.
 *
 * @param {string} sipUrl A sip URL.
 * @return {!goog.html.SafeUrl} A matching safe URL, or {@link INNOCUOUS_STRING}
 *     wrapped as a SafeUrl if it does not pass.
 */
goog.html.SafeUrl.fromSipUrl = function(sipUrl) {
  if (!goog.html.SIP_URL_PATTERN_.test(decodeURIComponent(sipUrl))) {
    sipUrl = goog.html.SafeUrl.INNOCUOUS_STRING;
  }
  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
      sipUrl);
};


/**
 * Creates a SafeUrl wrapping a fb-messenger://share URL.
 *
 * @param {string} facebookMessengerUrl A facebook messenger URL.
 * @return {!goog.html.SafeUrl} A matching safe URL, or {@link INNOCUOUS_STRING}
 *     wrapped as a SafeUrl if it does not pass.
 */
goog.html.SafeUrl.fromFacebookMessengerUrl = function(facebookMessengerUrl) {
  if (!goog.string.internal.caseInsensitiveStartsWith(
          facebookMessengerUrl, 'fb-messenger://share')) {
    facebookMessengerUrl = goog.html.SafeUrl.INNOCUOUS_STRING;
  }
  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
      facebookMessengerUrl);
};

/**
 * Creates a SafeUrl wrapping a whatsapp://send URL.
 *
 * @param {string} whatsAppUrl A WhatsApp URL.
 * @return {!goog.html.SafeUrl} A matching safe URL, or {@link INNOCUOUS_STRING}
 *     wrapped as a SafeUrl if it does not pass.
 */
goog.html.SafeUrl.fromWhatsAppUrl = function(whatsAppUrl) {
  if (!goog.string.internal.caseInsensitiveStartsWith(
          whatsAppUrl, 'whatsapp://send')) {
    whatsAppUrl = goog.html.SafeUrl.INNOCUOUS_STRING;
  }
  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
      whatsAppUrl);
};

/**
 * Creates a SafeUrl wrapping a sms: URL.
 *
 * @param {string} smsUrl A sms URL.
 * @return {!goog.html.SafeUrl} A matching safe URL, or {@link INNOCUOUS_STRING}
 *     wrapped as a SafeUrl if it does not pass.
 */
goog.html.SafeUrl.fromSmsUrl = function(smsUrl) {
  if (!goog.string.internal.caseInsensitiveStartsWith(smsUrl, 'sms:') ||
      !goog.html.SafeUrl.isSmsUrlBodyValid_(smsUrl)) {
    smsUrl = goog.html.SafeUrl.INNOCUOUS_STRING;
  }
  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
      smsUrl);
};


/**
 * Validates SMS URL `body` parameter, which is optional and should appear at
 * most once and should be percent-encoded if present. Rejects many malformed
 * bodies, but may spuriously reject some URLs and does not reject all malformed
 * sms: URLs.
 *
 * @param {string} smsUrl A sms URL.
 * @return {boolean} Whether SMS URL has a valid `body` parameter if it exists.
 * @private
 */
goog.html.SafeUrl.isSmsUrlBodyValid_ = function(smsUrl) {
  var hash = smsUrl.indexOf('#');
  if (hash > 0) {
    smsUrl = smsUrl.substring(0, hash);
  }
  var bodyParams = smsUrl.match(/[?&]body=/gi);
  // "body" param is optional
  if (!bodyParams) {
    return true;
  }
  // "body" MUST only appear once
  if (bodyParams.length > 1) {
    return false;
  }
  // Get the encoded `body` parameter value.
  var bodyValue = smsUrl.match(/[?&]body=([^&]*)/)[1];
  if (!bodyValue) {
    return true;
  }
  try {
    decodeURIComponent(bodyValue);
  } catch (error) {
    return false;
  }
  return /^(?:[a-z0-9\-_.~]|%[0-9a-f]{2})+$/i.test(bodyValue);
};


/**
 * Creates a SafeUrl wrapping a ssh: URL.
 *
 * @param {string} sshUrl A ssh URL.
 * @return {!goog.html.SafeUrl} A matching safe URL, or {@link INNOCUOUS_STRING}
 *     wrapped as a SafeUrl if it does not pass.
 */
goog.html.SafeUrl.fromSshUrl = function(sshUrl) {
  if (!goog.string.internal.caseInsensitiveStartsWith(sshUrl, 'ssh://')) {
    sshUrl = goog.html.SafeUrl.INNOCUOUS_STRING;
  }
  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
      sshUrl);
};

/**
 * Sanitizes a Chrome extension URL to SafeUrl, given a compile-time-constant
 * extension identifier. Can also be restricted to chrome extensions.
 *
 * @param {string} url The url to sanitize. Should start with the extension
 *     scheme and the extension identifier.
 * @param {!goog.string.Const|!Array<!goog.string.Const>} extensionId The
 *     extension id to accept, as a compile-time constant or an array of those.
 *
 * @return {!goog.html.SafeUrl} Either `url` if it's deemed safe, or
 *     `INNOCUOUS_STRING` if it's not.
 */
goog.html.SafeUrl.sanitizeChromeExtensionUrl = function(url, extensionId) {
  return goog.html.SafeUrl.sanitizeExtensionUrl_(
      /^chrome-extension:\/\/([^\/]+)\//, url, extensionId);
};

/**
 * Sanitizes a Firefox extension URL to SafeUrl, given a compile-time-constant
 * extension identifier. Can also be restricted to chrome extensions.
 *
 * @param {string} url The url to sanitize. Should start with the extension
 *     scheme and the extension identifier.
 * @param {!goog.string.Const|!Array<!goog.string.Const>} extensionId The
 *     extension id to accept, as a compile-time constant or an array of those.
 *
 * @return {!goog.html.SafeUrl} Either `url` if it's deemed safe, or
 *     `INNOCUOUS_STRING` if it's not.
 */
goog.html.SafeUrl.sanitizeFirefoxExtensionUrl = function(url, extensionId) {
  return goog.html.SafeUrl.sanitizeExtensionUrl_(
      /^moz-extension:\/\/([^\/]+)\//, url, extensionId);
};

/**
 * Sanitizes a Edge extension URL to SafeUrl, given a compile-time-constant
 * extension identifier. Can also be restricted to chrome extensions.
 *
 * @param {string} url The url to sanitize. Should start with the extension
 *     scheme and the extension identifier.
 * @param {!goog.string.Const|!Array<!goog.string.Const>} extensionId The
 *     extension id to accept, as a compile-time constant or an array of those.
 *
 * @return {!goog.html.SafeUrl} Either `url` if it's deemed safe, or
 *     `INNOCUOUS_STRING` if it's not.
 */
goog.html.SafeUrl.sanitizeEdgeExtensionUrl = function(url, extensionId) {
  return goog.html.SafeUrl.sanitizeExtensionUrl_(
      /^ms-browser-extension:\/\/([^\/]+)\//, url, extensionId);
};

/**
 * Private helper for converting extension URLs to SafeUrl, given the scheme for
 * that particular extension type. Use the sanitizeFirefoxExtensionUrl,
 * sanitizeChromeExtensionUrl or sanitizeEdgeExtensionUrl unless you're building
 * new helpers.
 *
 * @private
 * @param {!RegExp} scheme The scheme to accept as a RegExp extracting the
 *     extension identifier.
 * @param {string} url The url to sanitize. Should start with the extension
 *     scheme and the extension identifier.
 * @param {!goog.string.Const|!Array<!goog.string.Const>} extensionId The
 *     extension id to accept, as a compile-time constant or an array of those.
 *
 * @return {!goog.html.SafeUrl} Either `url` if it's deemed safe, or
 *     `INNOCUOUS_STRING` if it's not.
 */
goog.html.SafeUrl.sanitizeExtensionUrl_ = function(scheme, url, extensionId) {
  var matches = scheme.exec(url);
  if (!matches) {
    url = goog.html.SafeUrl.INNOCUOUS_STRING;
  } else {
    var extractedExtensionId = matches[1];
    var acceptedExtensionIds;
    if (extensionId instanceof goog.string.Const) {
      acceptedExtensionIds = [goog.string.Const.unwrap(extensionId)];
    } else {
      acceptedExtensionIds = extensionId.map(function unwrap(x) {
        return goog.string.Const.unwrap(x);
      });
    }
    if (acceptedExtensionIds.indexOf(extractedExtensionId) == -1) {
      url = goog.html.SafeUrl.INNOCUOUS_STRING;
    }
  }
  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
};


/**
 * Creates a SafeUrl from TrustedResourceUrl. This is safe because
 * TrustedResourceUrl is more tightly restricted than SafeUrl.
 *
 * @param {!goog.html.TrustedResourceUrl} trustedResourceUrl
 * @return {!goog.html.SafeUrl}
 */
goog.html.SafeUrl.fromTrustedResourceUrl = function(trustedResourceUrl) {
  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
      goog.html.TrustedResourceUrl.unwrap(trustedResourceUrl));
};


/**
 * A pattern that recognizes a commonly useful subset of URLs that satisfy
 * the SafeUrl contract.
 *
 * This regular expression matches a subset of URLs that will not cause script
 * execution if used in URL context within a HTML document. Specifically, this
 * regular expression matches if (comment from here on and regex copied from
 * Soy's EscapingConventions):
 * (1) Either a protocol in a whitelist (http, https, mailto or ftp).
 * (2) or no protocol.  A protocol must be followed by a colon. The below
 *     allows that by allowing colons only after one of the characters [/?#].
 *     A colon after a hash (#) must be in the fragment.
 *     Otherwise, a colon after a (?) must be in a query.
 *     Otherwise, a colon after a single solidus (/) must be in a path.
 *     Otherwise, a colon after a double solidus (//) must be in the authority
 *     (before port).
 *
 * @private
 * @const {!RegExp}
 */
goog.html.SAFE_URL_PATTERN_ =
    /^(?:(?:https?|mailto|ftp):|[^:/?#]*(?:[/?#]|$))/i;

/**
 * Public version of goog.html.SAFE_URL_PATTERN_. Updating
 * goog.html.SAFE_URL_PATTERN_ doesn't seem to be backward compatible.
 * Namespace is also changed to goog.html.SafeUrl so it can be imported using
 * goog.require('goog.dom.SafeUrl').
 *
 * TODO(bangert): Remove SAFE_URL_PATTERN_
 * @const {!RegExp}
 */
goog.html.SafeUrl.SAFE_URL_PATTERN = goog.html.SAFE_URL_PATTERN_;


/**
 * Creates a SafeUrl object from `url`. If `url` is a
 * goog.html.SafeUrl then it is simply returned. Otherwise the input string is
 * validated to match a pattern of commonly used safe URLs.
 *
 * `url` may be a URL with the http, https, mailto or ftp scheme,
 * or a relative URL (i.e., a URL without a scheme; specifically, a
 * scheme-relative, absolute-path-relative, or path-relative URL).
 *
 * @see http://url.spec.whatwg.org/#concept-relative-url
 * @param {string|!goog.string.TypedString} url The URL to validate.
 * @return {!goog.html.SafeUrl} The validated URL, wrapped as a SafeUrl.
 */
goog.html.SafeUrl.sanitize = function(url) {
  if (url instanceof goog.html.SafeUrl) {
    return url;
  } else if (typeof url == 'object' && url.implementsGoogStringTypedString) {
    url = /** @type {!goog.string.TypedString} */ (url).getTypedStringValue();
  } else {
    url = String(url);
  }
  if (!goog.html.SAFE_URL_PATTERN_.test(url)) {
    url = goog.html.SafeUrl.INNOCUOUS_STRING;
  }
  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
};

/**
 * Creates a SafeUrl object from `url`. If `url` is a
 * goog.html.SafeUrl then it is simply returned. Otherwise the input string is
 * validated to match a pattern of commonly used safe URLs.
 *
 * `url` may be a URL with the http, https, mailto or ftp scheme,
 * or a relative URL (i.e., a URL without a scheme; specifically, a
 * scheme-relative, absolute-path-relative, or path-relative URL).
 *
 * This function asserts (using goog.asserts) that the URL matches this pattern.
 * If it does not, in addition to failing the assert, an innocous URL will be
 * returned.
 *
 * @see http://url.spec.whatwg.org/#concept-relative-url
 * @param {string|!goog.string.TypedString} url The URL to validate.
 * @param {boolean=} opt_allowDataUrl Whether to allow valid data: URLs.
 * @return {!goog.html.SafeUrl} The validated URL, wrapped as a SafeUrl.
 */
goog.html.SafeUrl.sanitizeAssertUnchanged = function(url, opt_allowDataUrl) {
  if (url instanceof goog.html.SafeUrl) {
    return url;
  } else if (typeof url == 'object' && url.implementsGoogStringTypedString) {
    url = /** @type {!goog.string.TypedString} */ (url).getTypedStringValue();
  } else {
    url = String(url);
  }
  if (opt_allowDataUrl && /^data:/i.test(url)) {
    var safeUrl = goog.html.SafeUrl.fromDataUrl(url);
    if (safeUrl.getTypedStringValue() == url) {
      return safeUrl;
    }
  }
  if (!goog.asserts.assert(
          goog.html.SAFE_URL_PATTERN_.test(url),
          '%s does not match the safe URL pattern', url)) {
    url = goog.html.SafeUrl.INNOCUOUS_STRING;
  }
  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
};



/**
 * Type marker for the SafeUrl type, used to implement additional run-time
 * type checking.
 * @const {!Object}
 * @private
 */
goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};


/**
 * Package-internal utility method to create SafeUrl instances.
 *
 * @param {string} url The string to initialize the SafeUrl object with.
 * @return {!goog.html.SafeUrl} The initialized SafeUrl object.
 * @package
 */
goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse = function(
    url) {
  return new goog.html.SafeUrl(
      goog.html.SafeUrl.CONSTRUCTOR_TOKEN_PRIVATE_, url);
};


/**
 * A SafeUrl corresponding to the special about:blank url.
 * @const {!goog.html.SafeUrl}
 */
goog.html.SafeUrl.ABOUT_BLANK =
    goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
        'about:blank');

/**
 * Token used to ensure that object is created only from this file. No code
 * outside of this file can access this token.
 * @private {!Object}
 * @const
 */
goog.html.SafeUrl.CONSTRUCTOR_TOKEN_PRIVATE_ = {};

//third_party/javascript/closure/html/safestyle.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview The SafeStyle type and its builders.
 *
 * TODO(xtof): Link to document stating type contract.
 */

goog.provide('goog.html.SafeStyle');

goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.html.SafeUrl');
goog.require('goog.string.Const');
goog.require('goog.string.TypedString');
goog.require('goog.string.internal');



/**
 * A string-like object which represents a sequence of CSS declarations
 * ({@code propertyName1: propertyvalue1; propertyName2: propertyValue2; ...})
 * and that carries the security type contract that its value, as a string,
 * will not cause untrusted script execution (XSS) when evaluated as CSS in a
 * browser.
 *
 * Instances of this type must be created via the factory methods
 * (`goog.html.SafeStyle.create` or
 * `goog.html.SafeStyle.fromConstant`) and not by invoking its
 * constructor. The constructor intentionally takes no parameters and the type
 * is immutable; hence only a default instance corresponding to the empty string
 * can be obtained via constructor invocation.
 *
 * SafeStyle's string representation can safely be:
 * <ul>
 *   <li>Interpolated as the content of a *quoted* HTML style attribute.
 *       However, the SafeStyle string *must be HTML-attribute-escaped* before
 *       interpolation.
 *   <li>Interpolated as the content of a {}-wrapped block within a stylesheet.
 *       '<' characters in the SafeStyle string *must be CSS-escaped* before
 *       interpolation. The SafeStyle string is also guaranteed not to be able
 *       to introduce new properties or elide existing ones.
 *   <li>Interpolated as the content of a {}-wrapped block within an HTML
 *       &lt;style&gt; element. '<' characters in the SafeStyle string
 *       *must be CSS-escaped* before interpolation.
 *   <li>Assigned to the style property of a DOM node. The SafeStyle string
 *       should not be escaped before being assigned to the property.
 * </ul>
 *
 * A SafeStyle may never contain literal angle brackets. Otherwise, it could
 * be unsafe to place a SafeStyle into a &lt;style&gt; tag (where it can't
 * be HTML escaped). For example, if the SafeStyle containing
 * "{@code font: 'foo &lt;style/&gt;&lt;script&gt;evil&lt;/script&gt;'}" were
 * interpolated within a &lt;style&gt; tag, this would then break out of the
 * style context into HTML.
 *
 * A SafeStyle may contain literal single or double quotes, and as such the
 * entire style string must be escaped when used in a style attribute (if
 * this were not the case, the string could contain a matching quote that
 * would escape from the style attribute).
 *
 * Values of this type must be composable, i.e. for any two values
 * `style1` and `style2` of this type,
 * {@code goog.html.SafeStyle.unwrap(style1) +
 * goog.html.SafeStyle.unwrap(style2)} must itself be a value that satisfies
 * the SafeStyle type constraint. This requirement implies that for any value
 * `style` of this type, `goog.html.SafeStyle.unwrap(style)` must
 * not end in a "property value" or "property name" context. For example,
 * a value of {@code background:url("} or {@code font-} would not satisfy the
 * SafeStyle contract. This is because concatenating such strings with a
 * second value that itself does not contain unsafe CSS can result in an
 * overall string that does. For example, if {@code javascript:evil())"} is
 * appended to {@code background:url("}, the resulting string may result in
 * the execution of a malicious script.
 *
 * TODO(mlourenco): Consider whether we should implement UTF-8 interchange
 * validity checks and blacklisting of newlines (including Unicode ones) and
 * other whitespace characters (\t, \f). Document here if so and also update
 * SafeStyle.fromConstant().
 *
 * The following example values comply with this type's contract:
 * <ul>
 *   <li><pre>width: 1em;</pre>
 *   <li><pre>height:1em;</pre>
 *   <li><pre>width: 1em;height: 1em;</pre>
 *   <li><pre>background:url('http://url');</pre>
 * </ul>
 * In addition, the empty string is safe for use in a CSS attribute.
 *
 * The following example values do NOT comply with this type's contract:
 * <ul>
 *   <li><pre>background: red</pre> (missing a trailing semi-colon)
 *   <li><pre>background:</pre> (missing a value and a trailing semi-colon)
 *   <li><pre>1em</pre> (missing an attribute name, which provides context for
 *       the value)
 * </ul>
 *
 * @see goog.html.SafeStyle#create
 * @see goog.html.SafeStyle#fromConstant
 * @see http://www.w3.org/TR/css3-syntax/
 * @constructor
 * @final
 * @struct
 * @implements {goog.string.TypedString}
 */
goog.html.SafeStyle = function() {
  /**
   * The contained value of this SafeStyle.  The field has a purposely
   * ugly name to make (non-compiled) code that attempts to directly access this
   * field stand out.
   * @private {string}
   */
  this.privateDoNotAccessOrElseSafeStyleWrappedValue_ = '';

  /**
   * A type marker used to implement additional run-time type checking.
   * @see goog.html.SafeStyle#unwrap
   * @const {!Object}
   * @private
   */
  this.SAFE_STYLE_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
      goog.html.SafeStyle.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
};


/**
 * @override
 * @const
 */
goog.html.SafeStyle.prototype.implementsGoogStringTypedString = true;


/**
 * Type marker for the SafeStyle type, used to implement additional
 * run-time type checking.
 * @const {!Object}
 * @private
 */
goog.html.SafeStyle.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};


/**
 * Creates a SafeStyle object from a compile-time constant string.
 *
 * `style` should be in the format
 * {@code name: value; [name: value; ...]} and must not have any < or >
 * characters in it. This is so that SafeStyle's contract is preserved,
 * allowing the SafeStyle to correctly be interpreted as a sequence of CSS
 * declarations and without affecting the syntactic structure of any
 * surrounding CSS and HTML.
 *
 * This method performs basic sanity checks on the format of `style`
 * but does not constrain the format of `name` and `value`, except
 * for disallowing tag characters.
 *
 * @param {!goog.string.Const} style A compile-time-constant string from which
 *     to create a SafeStyle.
 * @return {!goog.html.SafeStyle} A SafeStyle object initialized to
 *     `style`.
 */
goog.html.SafeStyle.fromConstant = function(style) {
  var styleString = goog.string.Const.unwrap(style);
  if (styleString.length === 0) {
    return goog.html.SafeStyle.EMPTY;
  }
  goog.asserts.assert(
      goog.string.internal.endsWith(styleString, ';'),
      'Last character of style string is not \';\': ' + styleString);
  goog.asserts.assert(
      goog.string.internal.contains(styleString, ':'),
      'Style string must contain at least one \':\', to ' +
          'specify a "name: value" pair: ' + styleString);
  return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(
      styleString);
};


/**
 * Returns this SafeStyle's value as a string.
 *
 * IMPORTANT: In code where it is security relevant that an object's type is
 * indeed `SafeStyle`, use `goog.html.SafeStyle.unwrap` instead of
 * this method. If in doubt, assume that it's security relevant. In particular,
 * note that goog.html functions which return a goog.html type do not guarantee
 * the returned instance is of the right type. For example:
 *
 * <pre>
 * var fakeSafeHtml = new String('fake');
 * fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
 * var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
 * // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
 * // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml
 * // instanceof goog.html.SafeHtml.
 * </pre>
 *
 * @see goog.html.SafeStyle#unwrap
 * @override
 */
goog.html.SafeStyle.prototype.getTypedStringValue = function() {
  return this.privateDoNotAccessOrElseSafeStyleWrappedValue_;
};


if (goog.DEBUG) {
  /**
   * Returns a debug string-representation of this value.
   *
   * To obtain the actual string value wrapped in a SafeStyle, use
   * `goog.html.SafeStyle.unwrap`.
   *
   * @see goog.html.SafeStyle#unwrap
   * @override
   */
  goog.html.SafeStyle.prototype.toString = function() {
    return 'SafeStyle{' + this.privateDoNotAccessOrElseSafeStyleWrappedValue_ +
        '}';
  };
}


/**
 * Performs a runtime check that the provided object is indeed a
 * SafeStyle object, and returns its value.
 *
 * @param {!goog.html.SafeStyle} safeStyle The object to extract from.
 * @return {string} The safeStyle object's contained string, unless
 *     the run-time type check fails. In that case, `unwrap` returns an
 *     innocuous string, or, if assertions are enabled, throws
 *     `goog.asserts.AssertionError`.
 */
goog.html.SafeStyle.unwrap = function(safeStyle) {
  // Perform additional Run-time type-checking to ensure that
  // safeStyle is indeed an instance of the expected type.  This
  // provides some additional protection against security bugs due to
  // application code that disables type checks.
  // Specifically, the following checks are performed:
  // 1. The object is an instance of the expected type.
  // 2. The object is not an instance of a subclass.
  // 3. The object carries a type marker for the expected type. "Faking" an
  // object requires a reference to the type marker, which has names intended
  // to stand out in code reviews.
  if (safeStyle instanceof goog.html.SafeStyle &&
      safeStyle.constructor === goog.html.SafeStyle &&
      safeStyle.SAFE_STYLE_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
          goog.html.SafeStyle.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
    return safeStyle.privateDoNotAccessOrElseSafeStyleWrappedValue_;
  } else {
    goog.asserts.fail('expected object of type SafeStyle, got \'' +
        safeStyle + '\' of type ' + goog.typeOf(safeStyle));
    return 'type_error:SafeStyle';
  }
};


/**
 * Package-internal utility method to create SafeStyle instances.
 *
 * @param {string} style The string to initialize the SafeStyle object with.
 * @return {!goog.html.SafeStyle} The initialized SafeStyle object.
 * @package
 */
goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse = function(
    style) {
  return new goog.html.SafeStyle().initSecurityPrivateDoNotAccessOrElse_(style);
};


/**
 * Called from createSafeStyleSecurityPrivateDoNotAccessOrElse(). This
 * method exists only so that the compiler can dead code eliminate static
 * fields (like EMPTY) when they're not accessed.
 * @param {string} style
 * @return {!goog.html.SafeStyle}
 * @private
 */
goog.html.SafeStyle.prototype.initSecurityPrivateDoNotAccessOrElse_ = function(
    style) {
  this.privateDoNotAccessOrElseSafeStyleWrappedValue_ = style;
  return this;
};


/**
 * A SafeStyle instance corresponding to the empty string.
 * @const {!goog.html.SafeStyle}
 */
goog.html.SafeStyle.EMPTY =
    goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse('');


/**
 * The innocuous string generated by goog.html.SafeStyle.create when passed
 * an unsafe value.
 * @const {string}
 */
goog.html.SafeStyle.INNOCUOUS_STRING = 'zClosurez';


/**
 * A single property value.
 * @typedef {string|!goog.string.Const|!goog.html.SafeUrl}
 */
goog.html.SafeStyle.PropertyValue;


/**
 * Mapping of property names to their values.
 * We don't support numbers even though some values might be numbers (e.g.
 * line-height or 0 for any length). The reason is that most numeric values need
 * units (e.g. '1px') and allowing numbers could cause users forgetting about
 * them.
 * @typedef {!Object<string, ?goog.html.SafeStyle.PropertyValue|
 *     ?Array<!goog.html.SafeStyle.PropertyValue>>}
 */
goog.html.SafeStyle.PropertyMap;


/**
 * Creates a new SafeStyle object from the properties specified in the map.
 * @param {goog.html.SafeStyle.PropertyMap} map Mapping of property names to
 *     their values, for example {'margin': '1px'}. Names must consist of
 *     [-_a-zA-Z0-9]. Values might be strings consisting of
 *     [-,.'"%_!# a-zA-Z0-9[\]], where ", ', and [] must be properly balanced.
 *     We also allow simple functions like rgb() and url() which sanitizes its
 *     contents. Other values must be wrapped in goog.string.Const. URLs might
 *     be passed as goog.html.SafeUrl which will be wrapped into url(""). We
 *     also support array whose elements are joined with ' '. Null value causes
 *     skipping the property.
 * @return {!goog.html.SafeStyle}
 * @throws {Error} If invalid name is provided.
 * @throws {goog.asserts.AssertionError} If invalid value is provided. With
 *     disabled assertions, invalid value is replaced by
 *     goog.html.SafeStyle.INNOCUOUS_STRING.
 */
goog.html.SafeStyle.create = function(map) {
  var style = '';
  for (var name in map) {
    if (!/^[-_a-zA-Z0-9]+$/.test(name)) {
      throw new Error('Name allows only [-_a-zA-Z0-9], got: ' + name);
    }
    var value = map[name];
    if (value == null) {
      continue;
    }
    if (Array.isArray(value)) {
      value = goog.array.map(value, goog.html.SafeStyle.sanitizePropertyValue_)
                  .join(' ');
    } else {
      value = goog.html.SafeStyle.sanitizePropertyValue_(value);
    }
    style += name + ':' + value + ';';
  }
  if (!style) {
    return goog.html.SafeStyle.EMPTY;
  }
  return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(
      style);
};


/**
 * Checks and converts value to string.
 * @param {!goog.html.SafeStyle.PropertyValue} value
 * @return {string}
 * @private
 */
goog.html.SafeStyle.sanitizePropertyValue_ = function(value) {
  if (value instanceof goog.html.SafeUrl) {
    var url = goog.html.SafeUrl.unwrap(value);
    return 'url("' + url.replace(/</g, '%3c').replace(/[\\"]/g, '\\$&') + '")';
  }
  var result = value instanceof goog.string.Const ?
      goog.string.Const.unwrap(value) :
      goog.html.SafeStyle.sanitizePropertyValueString_(String(value));
  // These characters can be used to change context and we don't want that even
  // with const values.
  if (/[{;}]/.test(result)) {
    throw new goog.asserts.AssertionError(
        'Value does not allow [{;}], got: %s.', [result]);
  }
  return result;
};


/**
 * Checks string value.
 * @param {string} value
 * @return {string}
 * @private
 */
goog.html.SafeStyle.sanitizePropertyValueString_ = function(value) {
  // Some CSS property values permit nested functions. We allow one level of
  // nesting, and all nested functions must also be in the FUNCTIONS_RE_ list.
  var valueWithoutFunctions =
      value.replace(goog.html.SafeStyle.FUNCTIONS_RE_, '$1')
          .replace(goog.html.SafeStyle.FUNCTIONS_RE_, '$1')
          .replace(goog.html.SafeStyle.URL_RE_, 'url');
  if (!goog.html.SafeStyle.VALUE_RE_.test(valueWithoutFunctions)) {
    goog.asserts.fail(
        'String value allows only ' + goog.html.SafeStyle.VALUE_ALLOWED_CHARS_ +
        ' and simple functions, got: ' + value);
    return goog.html.SafeStyle.INNOCUOUS_STRING;
  } else if (goog.html.SafeStyle.COMMENT_RE_.test(value)) {
    goog.asserts.fail('String value disallows comments, got: ' + value);
    return goog.html.SafeStyle.INNOCUOUS_STRING;
  } else if (!goog.html.SafeStyle.hasBalancedQuotes_(value)) {
    goog.asserts.fail('String value requires balanced quotes, got: ' + value);
    return goog.html.SafeStyle.INNOCUOUS_STRING;
  } else if (!goog.html.SafeStyle.hasBalancedSquareBrackets_(value)) {
    goog.asserts.fail(
        'String value requires balanced square brackets and one' +
        ' identifier per pair of brackets, got: ' + value);
    return goog.html.SafeStyle.INNOCUOUS_STRING;
  }
  return goog.html.SafeStyle.sanitizeUrl_(value);
};


/**
 * Checks that quotes (" and ') are properly balanced inside a string. Assumes
 * that neither escape (\) nor any other character that could result in
 * breaking out of a string parsing context are allowed;
 * see http://www.w3.org/TR/css3-syntax/#string-token-diagram.
 * @param {string} value Untrusted CSS property value.
 * @return {boolean} True if property value is safe with respect to quote
 *     balancedness.
 * @private
 */
goog.html.SafeStyle.hasBalancedQuotes_ = function(value) {
  var outsideSingle = true;
  var outsideDouble = true;
  for (var i = 0; i < value.length; i++) {
    var c = value.charAt(i);
    if (c == "'" && outsideDouble) {
      outsideSingle = !outsideSingle;
    } else if (c == '"' && outsideSingle) {
      outsideDouble = !outsideDouble;
    }
  }
  return outsideSingle && outsideDouble;
};


/**
 * Checks that square brackets ([ and ]) are properly balanced inside a string,
 * and that the content in the square brackets is one ident-token;
 * see https://www.w3.org/TR/css-syntax-3/#ident-token-diagram.
 * For practicality, and in line with other restrictions posed on SafeStyle
 * strings, we restrict the character set allowable in the ident-token to
 * [-_a-zA-Z0-9].
 * @param {string} value Untrusted CSS property value.
 * @return {boolean} True if property value is safe with respect to square
 *     bracket balancedness.
 * @private
 */
goog.html.SafeStyle.hasBalancedSquareBrackets_ = function(value) {
  var outside = true;
  var tokenRe = /^[-_a-zA-Z0-9]$/;
  for (var i = 0; i < value.length; i++) {
    var c = value.charAt(i);
    if (c == ']') {
      if (outside) return false;  // Unbalanced ].
      outside = true;
    } else if (c == '[') {
      if (!outside) return false;  // No nesting.
      outside = false;
    } else if (!outside && !tokenRe.test(c)) {
      return false;
    }
  }
  return outside;
};


/**
 * Characters allowed in goog.html.SafeStyle.VALUE_RE_.
 * @private {string}
 */
goog.html.SafeStyle.VALUE_ALLOWED_CHARS_ = '[-,."\'%_!# a-zA-Z0-9\\[\\]]';


/**
 * Regular expression for safe values.
 *
 * Quotes (" and ') are allowed, but a check must be done elsewhere to ensure
 * they're balanced.
 *
 * Square brackets ([ and ]) are allowed, but a check must be done elsewhere
 * to ensure they're balanced. The content inside a pair of square brackets must
 * be one alphanumeric identifier.
 *
 * ',' allows multiple values to be assigned to the same property
 * (e.g. background-attachment or font-family) and hence could allow
 * multiple values to get injected, but that should pose no risk of XSS.
 *
 * The expression checks only for XSS safety, not for CSS validity.
 * @const {!RegExp}
 * @private
 */
goog.html.SafeStyle.VALUE_RE_ =
    new RegExp('^' + goog.html.SafeStyle.VALUE_ALLOWED_CHARS_ + '+$');


/**
 * Regular expression for url(). We support URLs allowed by
 * https://www.w3.org/TR/css-syntax-3/#url-token-diagram without using escape
 * sequences. Use percent-encoding if you need to use special characters like
 * backslash.
 * @private @const {!RegExp}
 */
goog.html.SafeStyle.URL_RE_ = new RegExp(
    '\\b(url\\([ \t\n]*)(' +
        '\'[ -&(-\\[\\]-~]*\'' +  // Printable characters except ' and \.
        '|"[ !#-\\[\\]-~]*"' +    // Printable characters except " and \.
        '|[!#-&*-\\[\\]-~]*' +    // Printable characters except [ "'()\\].
        ')([ \t\n]*\\))',
    'g');

/**
 * Names of functions allowed in FUNCTIONS_RE_.
 * @private @const {!Array<string>}
 */
goog.html.SafeStyle.ALLOWED_FUNCTIONS_ = [
  'calc',
  'cubic-bezier',
  'fit-content',
  'hsl',
  'hsla',
  'linear-gradient',
  'matrix',
  'minmax',
  'repeat',
  'rgb',
  'rgba',
  '(rotate|scale|translate)(X|Y|Z|3d)?',
];


/**
 * Regular expression for simple functions.
 * @private @const {!RegExp}
 */
goog.html.SafeStyle.FUNCTIONS_RE_ = new RegExp(
    '\\b(' + goog.html.SafeStyle.ALLOWED_FUNCTIONS_.join('|') + ')' +
        '\\([-+*/0-9a-z.%\\[\\], ]+\\)',
    'g');


/**
 * Regular expression for comments. These are disallowed in CSS property values.
 * @private @const {!RegExp}
 */
goog.html.SafeStyle.COMMENT_RE_ = /\/\*/;


/**
 * Sanitize URLs inside url().
 *
 * NOTE: We could also consider using CSS.escape once that's available in the
 * browsers. However, loosely matching URL e.g. with url\(.*\) and then escaping
 * the contents would result in a slightly different language than CSS leading
 * to confusion of users. E.g. url(")") is valid in CSS but it would be invalid
 * as seen by our parser. On the other hand, url(\) is invalid in CSS but our
 * parser would be fine with it.
 *
 * @param {string} value Untrusted CSS property value.
 * @return {string}
 * @private
 */
goog.html.SafeStyle.sanitizeUrl_ = function(value) {
  return value.replace(
      goog.html.SafeStyle.URL_RE_, function(match, before, url, after) {
        var quote = '';
        url = url.replace(/^(['"])(.*)\1$/, function(match, start, inside) {
          quote = start;
          return inside;
        });
        var sanitized = goog.html.SafeUrl.sanitize(url).getTypedStringValue();
        return before + quote + sanitized + quote + after;
      });
};


/**
 * Creates a new SafeStyle object by concatenating the values.
 * @param {...(!goog.html.SafeStyle|!Array<!goog.html.SafeStyle>)} var_args
 *     SafeStyles to concatenate.
 * @return {!goog.html.SafeStyle}
 */
goog.html.SafeStyle.concat = function(var_args) {
  var style = '';

  /**
   * @param {!goog.html.SafeStyle|!Array<!goog.html.SafeStyle>} argument
   */
  var addArgument = function(argument) {
    if (Array.isArray(argument)) {
      goog.array.forEach(argument, addArgument);
    } else {
      style += goog.html.SafeStyle.unwrap(argument);
    }
  };

  goog.array.forEach(arguments, addArgument);
  if (!style) {
    return goog.html.SafeStyle.EMPTY;
  }
  return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(
      style);
};

//third_party/javascript/closure/html/safestylesheet.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview The SafeStyleSheet type and its builders.
 *
 * TODO(xtof): Link to document stating type contract.
 */

goog.provide('goog.html.SafeStyleSheet');

goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.html.SafeStyle');
goog.require('goog.object');
goog.require('goog.string.Const');
goog.require('goog.string.TypedString');
goog.require('goog.string.internal');



/**
 * A string-like object which represents a CSS style sheet and that carries the
 * security type contract that its value, as a string, will not cause untrusted
 * script execution (XSS) when evaluated as CSS in a browser.
 *
 * Instances of this type must be created via the factory method
 * `goog.html.SafeStyleSheet.fromConstant` and not by invoking its
 * constructor. The constructor intentionally takes no parameters and the type
 * is immutable; hence only a default instance corresponding to the empty string
 * can be obtained via constructor invocation.
 *
 * A SafeStyleSheet's string representation can safely be interpolated as the
 * content of a style element within HTML. The SafeStyleSheet string should
 * not be escaped before interpolation.
 *
 * Values of this type must be composable, i.e. for any two values
 * `styleSheet1` and `styleSheet2` of this type,
 * {@code goog.html.SafeStyleSheet.unwrap(styleSheet1) +
 * goog.html.SafeStyleSheet.unwrap(styleSheet2)} must itself be a value that
 * satisfies the SafeStyleSheet type constraint. This requirement implies that
 * for any value `styleSheet` of this type,
 * `goog.html.SafeStyleSheet.unwrap(styleSheet1)` must end in
 * "beginning of rule" context.

 * A SafeStyleSheet can be constructed via security-reviewed unchecked
 * conversions. In this case producers of SafeStyleSheet must ensure themselves
 * that the SafeStyleSheet does not contain unsafe script. Note in particular
 * that {@code &lt;} is dangerous, even when inside CSS strings, and so should
 * always be forbidden or CSS-escaped in user controlled input. For example, if
 * {@code &lt;/style&gt;&lt;script&gt;evil&lt;/script&gt;"} were interpolated
 * inside a CSS string, it would break out of the context of the original
 * style element and `evil` would execute. Also note that within an HTML
 * style (raw text) element, HTML character references, such as
 * {@code &amp;lt;}, are not allowed. See
 *
 http://www.w3.org/TR/html5/scripting-1.html#restrictions-for-contents-of-script-elements
 * (similar considerations apply to the style element).
 *
 * @see goog.html.SafeStyleSheet#fromConstant
 * @constructor
 * @final
 * @struct
 * @implements {goog.string.TypedString}
 */
goog.html.SafeStyleSheet = function() {
  /**
   * The contained value of this SafeStyleSheet.  The field has a purposely
   * ugly name to make (non-compiled) code that attempts to directly access this
   * field stand out.
   * @private {string}
   */
  this.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_ = '';

  /**
   * A type marker used to implement additional run-time type checking.
   * @see goog.html.SafeStyleSheet#unwrap
   * @const {!Object}
   * @private
   */
  this.SAFE_STYLE_SHEET_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
      goog.html.SafeStyleSheet.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
};


/**
 * @override
 * @const
 */
goog.html.SafeStyleSheet.prototype.implementsGoogStringTypedString = true;


/**
 * Type marker for the SafeStyleSheet type, used to implement additional
 * run-time type checking.
 * @const {!Object}
 * @private
 */
goog.html.SafeStyleSheet.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};


/**
 * Creates a style sheet consisting of one selector and one style definition.
 * Use {@link goog.html.SafeStyleSheet.concat} to create longer style sheets.
 * This function doesn't support @import, @media and similar constructs.
 * @param {string} selector CSS selector, e.g. '#id' or 'tag .class, #id'. We
 *     support CSS3 selectors: https://w3.org/TR/css3-selectors/#selectors.
 * @param {!goog.html.SafeStyle.PropertyMap|!goog.html.SafeStyle} style Style
 *     definition associated with the selector.
 * @return {!goog.html.SafeStyleSheet}
 * @throws {Error} If invalid selector is provided.
 */
goog.html.SafeStyleSheet.createRule = function(selector, style) {
  if (goog.string.internal.contains(selector, '<')) {
    throw new Error('Selector does not allow \'<\', got: ' + selector);
  }

  // Remove strings.
  var selectorToCheck =
      selector.replace(/('|")((?!\1)[^\r\n\f\\]|\\[\s\S])*\1/g, '');

  // Check characters allowed in CSS3 selectors.
  if (!/^[-_a-zA-Z0-9#.:* ,>+~[\]()=^$|]+$/.test(selectorToCheck)) {
    throw new Error(
        'Selector allows only [-_a-zA-Z0-9#.:* ,>+~[\\]()=^$|] and ' +
        'strings, got: ' + selector);
  }

  // Check balanced () and [].
  if (!goog.html.SafeStyleSheet.hasBalancedBrackets_(selectorToCheck)) {
    throw new Error('() and [] in selector must be balanced, got: ' + selector);
  }

  if (!(style instanceof goog.html.SafeStyle)) {
    style = goog.html.SafeStyle.create(style);
  }
  var styleSheet = selector + '{' +
      goog.html.SafeStyle.unwrap(style).replace(/</g, '\\3C ') + '}';
  return goog.html.SafeStyleSheet
      .createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(styleSheet);
};


/**
 * Checks if a string has balanced () and [] brackets.
 * @param {string} s String to check.
 * @return {boolean}
 * @private
 */
goog.html.SafeStyleSheet.hasBalancedBrackets_ = function(s) {
  var brackets = {'(': ')', '[': ']'};
  var expectedBrackets = [];
  for (var i = 0; i < s.length; i++) {
    var ch = s[i];
    if (brackets[ch]) {
      expectedBrackets.push(brackets[ch]);
    } else if (goog.object.contains(brackets, ch)) {
      if (expectedBrackets.pop() != ch) {
        return false;
      }
    }
  }
  return expectedBrackets.length == 0;
};


/**
 * Creates a new SafeStyleSheet object by concatenating values.
 * @param {...(!goog.html.SafeStyleSheet|!Array<!goog.html.SafeStyleSheet>)}
 *     var_args Values to concatenate.
 * @return {!goog.html.SafeStyleSheet}
 */
goog.html.SafeStyleSheet.concat = function(var_args) {
  var result = '';

  /**
   * @param {!goog.html.SafeStyleSheet|!Array<!goog.html.SafeStyleSheet>}
   *     argument
   */
  var addArgument = function(argument) {
    if (Array.isArray(argument)) {
      goog.array.forEach(argument, addArgument);
    } else {
      result += goog.html.SafeStyleSheet.unwrap(argument);
    }
  };

  goog.array.forEach(arguments, addArgument);
  return goog.html.SafeStyleSheet
      .createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(result);
};


/**
 * Creates a SafeStyleSheet object from a compile-time constant string.
 *
 * `styleSheet` must not have any &lt; characters in it, so that
 * the syntactic structure of the surrounding HTML is not affected.
 *
 * @param {!goog.string.Const} styleSheet A compile-time-constant string from
 *     which to create a SafeStyleSheet.
 * @return {!goog.html.SafeStyleSheet} A SafeStyleSheet object initialized to
 *     `styleSheet`.
 */
goog.html.SafeStyleSheet.fromConstant = function(styleSheet) {
  var styleSheetString = goog.string.Const.unwrap(styleSheet);
  if (styleSheetString.length === 0) {
    return goog.html.SafeStyleSheet.EMPTY;
  }
  // > is a valid character in CSS selectors and there's no strict need to
  // block it if we already block <.
  goog.asserts.assert(
      !goog.string.internal.contains(styleSheetString, '<'),
      'Forbidden \'<\' character in style sheet string: ' + styleSheetString);
  return goog.html.SafeStyleSheet
      .createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(styleSheetString);
};


/**
 * Returns this SafeStyleSheet's value as a string.
 *
 * IMPORTANT: In code where it is security relevant that an object's type is
 * indeed `SafeStyleSheet`, use `goog.html.SafeStyleSheet.unwrap`
 * instead of this method. If in doubt, assume that it's security relevant. In
 * particular, note that goog.html functions which return a goog.html type do
 * not guarantee the returned instance is of the right type. For example:
 *
 * <pre>
 * var fakeSafeHtml = new String('fake');
 * fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
 * var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
 * // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
 * // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml
 * // instanceof goog.html.SafeHtml.
 * </pre>
 *
 * @see goog.html.SafeStyleSheet#unwrap
 * @override
 */
goog.html.SafeStyleSheet.prototype.getTypedStringValue = function() {
  return this.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_;
};


if (goog.DEBUG) {
  /**
   * Returns a debug string-representation of this value.
   *
   * To obtain the actual string value wrapped in a SafeStyleSheet, use
   * `goog.html.SafeStyleSheet.unwrap`.
   *
   * @see goog.html.SafeStyleSheet#unwrap
   * @override
   */
  goog.html.SafeStyleSheet.prototype.toString = function() {
    return 'SafeStyleSheet{' +
        this.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_ + '}';
  };
}


/**
 * Performs a runtime check that the provided object is indeed a
 * SafeStyleSheet object, and returns its value.
 *
 * @param {!goog.html.SafeStyleSheet} safeStyleSheet The object to extract from.
 * @return {string} The safeStyleSheet object's contained string, unless
 *     the run-time type check fails. In that case, `unwrap` returns an
 *     innocuous string, or, if assertions are enabled, throws
 *     `goog.asserts.AssertionError`.
 */
goog.html.SafeStyleSheet.unwrap = function(safeStyleSheet) {
  // Perform additional Run-time type-checking to ensure that
  // safeStyleSheet is indeed an instance of the expected type.  This
  // provides some additional protection against security bugs due to
  // application code that disables type checks.
  // Specifically, the following checks are performed:
  // 1. The object is an instance of the expected type.
  // 2. The object is not an instance of a subclass.
  // 3. The object carries a type marker for the expected type. "Faking" an
  // object requires a reference to the type marker, which has names intended
  // to stand out in code reviews.
  if (safeStyleSheet instanceof goog.html.SafeStyleSheet &&
      safeStyleSheet.constructor === goog.html.SafeStyleSheet &&
      safeStyleSheet
              .SAFE_STYLE_SHEET_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
          goog.html.SafeStyleSheet.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
    return safeStyleSheet.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_;
  } else {
    goog.asserts.fail('expected object of type SafeStyleSheet, got \'' +
        safeStyleSheet + '\' of type ' + goog.typeOf(safeStyleSheet));
    return 'type_error:SafeStyleSheet';
  }
};


/**
 * Package-internal utility method to create SafeStyleSheet instances.
 *
 * @param {string} styleSheet The string to initialize the SafeStyleSheet
 *     object with.
 * @return {!goog.html.SafeStyleSheet} The initialized SafeStyleSheet object.
 * @package
 */
goog.html.SafeStyleSheet.createSafeStyleSheetSecurityPrivateDoNotAccessOrElse =
    function(styleSheet) {
  return new goog.html.SafeStyleSheet().initSecurityPrivateDoNotAccessOrElse_(
      styleSheet);
};


/**
 * Called from createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(). This
 * method exists only so that the compiler can dead code eliminate static
 * fields (like EMPTY) when they're not accessed.
 * @param {string} styleSheet
 * @return {!goog.html.SafeStyleSheet}
 * @private
 */
goog.html.SafeStyleSheet.prototype.initSecurityPrivateDoNotAccessOrElse_ =
    function(styleSheet) {
  this.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_ = styleSheet;
  return this;
};


/**
 * A SafeStyleSheet instance corresponding to the empty string.
 * @const {!goog.html.SafeStyleSheet}
 */
goog.html.SafeStyleSheet.EMPTY =
    goog.html.SafeStyleSheet
        .createSafeStyleSheetSecurityPrivateDoNotAccessOrElse('');

//third_party/javascript/closure/labs/useragent/util.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Utilities used by goog.labs.userAgent tools. These functions
 * should not be used outside of goog.labs.userAgent.*.
 *
 * @visibility {//javascript/abc/libs/objects3d:__subpackages__}
 * @visibility {//javascript/closure/dom:__subpackages__}
 * @visibility {//javascript/closure/style:__pkg__}
 * @visibility {//javascript/closure/testing:__pkg__}
 * @visibility {//javascript/closure/useragent:__subpackages__}
 * @visibility {//testing/puppet/modules:__pkg__}
 * @visibility {:util_legacy_users}
 */

goog.provide('goog.labs.userAgent.util');

goog.require('goog.string.internal');


/**
 * Gets the native userAgent string from navigator if it exists.
 * If navigator or navigator.userAgent string is missing, returns an empty
 * string.
 * @return {string}
 * @private
 */
goog.labs.userAgent.util.getNativeUserAgentString_ = function() {
  var navigator = goog.labs.userAgent.util.getNavigator_();
  if (navigator) {
    var userAgent = navigator.userAgent;
    if (userAgent) {
      return userAgent;
    }
  }
  return '';
};


/**
 * Getter for the native navigator.
 * This is a separate function so it can be stubbed out in testing.
 * @return {!Navigator}
 * @private
 */
goog.labs.userAgent.util.getNavigator_ = function() {
  return goog.global.navigator;
};


/**
 * A possible override for applications which wish to not check
 * navigator.userAgent but use a specified value for detection instead.
 * @private {string}
 */
goog.labs.userAgent.util.userAgent_ =
    goog.labs.userAgent.util.getNativeUserAgentString_();


/**
 * Applications may override browser detection on the built in
 * navigator.userAgent object by setting this string. Set to null to use the
 * browser object instead.
 * @param {?string=} opt_userAgent The User-Agent override.
 */
goog.labs.userAgent.util.setUserAgent = function(opt_userAgent) {
  goog.labs.userAgent.util.userAgent_ =
      opt_userAgent || goog.labs.userAgent.util.getNativeUserAgentString_();
};


/**
 * @return {string} The user agent string.
 */
goog.labs.userAgent.util.getUserAgent = function() {
  return goog.labs.userAgent.util.userAgent_;
};


/**
 * @param {string} str
 * @return {boolean} Whether the user agent contains the given string.
 */
goog.labs.userAgent.util.matchUserAgent = function(str) {
  var userAgent = goog.labs.userAgent.util.getUserAgent();
  return goog.string.internal.contains(userAgent, str);
};


/**
 * @param {string} str
 * @return {boolean} Whether the user agent contains the given string, ignoring
 *     case.
 */
goog.labs.userAgent.util.matchUserAgentIgnoreCase = function(str) {
  var userAgent = goog.labs.userAgent.util.getUserAgent();
  return goog.string.internal.caseInsensitiveContains(userAgent, str);
};


/**
 * Parses the user agent into tuples for each section.
 * @param {string} userAgent
 * @return {!Array<!Array<string>>} Tuples of key, version, and the contents
 *     of the parenthetical.
 */
goog.labs.userAgent.util.extractVersionTuples = function(userAgent) {
  // Matches each section of a user agent string.
  // Example UA:
  // Mozilla/5.0 (iPad; U; CPU OS 3_2_1 like Mac OS X; en-us)
  // AppleWebKit/531.21.10 (KHTML, like Gecko) Mobile/7B405
  // This has three version tuples: Mozilla, AppleWebKit, and Mobile.

  var versionRegExp = new RegExp(
      // Key. Note that a key may have a space.
      // (i.e. 'Mobile Safari' in 'Mobile Safari/5.0')
      '(\\w[\\w ]+)' +

          '/' +                // slash
          '([^\\s]+)' +        // version (i.e. '5.0b')
          '\\s*' +             // whitespace
          '(?:\\((.*?)\\))?',  // parenthetical info. parentheses not matched.
      'g');

  var data = [];
  var match;

  // Iterate and collect the version tuples.  Each iteration will be the
  // next regex match.
  while (match = versionRegExp.exec(userAgent)) {
    data.push([
      match[1],  // key
      match[2],  // value
      // || undefined as this is not undefined in IE7 and IE8
      match[3] || undefined  // info
    ]);
  }

  return data;
};

//third_party/javascript/closure/labs/useragent/browser.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Closure user agent detection (Browser).
 * @see <a href="http://www.useragentstring.com/">User agent strings</a>
 * For more information on rendering engine, platform, or device see the other
 * sub-namespaces in goog.labs.userAgent, goog.labs.userAgent.platform,
 * goog.labs.userAgent.device respectively.)
 */

goog.provide('goog.labs.userAgent.browser');

goog.require('goog.array');
goog.require('goog.labs.userAgent.util');
goog.require('goog.object');
goog.require('goog.string.internal');


// TODO(nnaze): Refactor to remove excessive exclusion logic in matching
// functions.


/**
 * @return {boolean} Whether the user's browser is Opera.  Note: Chromium
 *     based Opera (Opera 15+) is detected as Chrome to avoid unnecessary
 *     special casing.
 * @private
 */
goog.labs.userAgent.browser.matchOpera_ = function() {
  return goog.labs.userAgent.util.matchUserAgent('Opera');
};


/**
 * @return {boolean} Whether the user's browser is IE.
 * @private
 */
goog.labs.userAgent.browser.matchIE_ = function() {
  return goog.labs.userAgent.util.matchUserAgent('Trident') ||
      goog.labs.userAgent.util.matchUserAgent('MSIE');
};


/**
 * @return {boolean} Whether the user's browser is Edge. This refers to EdgeHTML
 * based Edge.
 * @private
 */
goog.labs.userAgent.browser.matchEdgeHtml_ = function() {
  return goog.labs.userAgent.util.matchUserAgent('Edge');
};


/**
 * @return {boolean} Whether the user's browser is Chromium based Edge.
 * @private
 */
goog.labs.userAgent.browser.matchEdgeChromium_ = function() {
  return goog.labs.userAgent.util.matchUserAgent('Edg/');
};


/**
 * @return {boolean} Whether the user's browser is Chromium based Opera.
 * @private
 */
goog.labs.userAgent.browser.matchOperaChromium_ = function() {
  return goog.labs.userAgent.util.matchUserAgent('OPR');
};


/**
 * @return {boolean} Whether the user's browser is Firefox.
 * @private
 */
goog.labs.userAgent.browser.matchFirefox_ = function() {
  return goog.labs.userAgent.util.matchUserAgent('Firefox') ||
      goog.labs.userAgent.util.matchUserAgent('FxiOS');
};


/**
 * @return {boolean} Whether the user's browser is Safari.
 * @private
 */
goog.labs.userAgent.browser.matchSafari_ = function() {
  return goog.labs.userAgent.util.matchUserAgent('Safari') &&
      !(goog.labs.userAgent.browser.matchChrome_() ||
        goog.labs.userAgent.browser.matchCoast_() ||
        goog.labs.userAgent.browser.matchOpera_() ||
        goog.labs.userAgent.browser.matchEdgeHtml_() ||
        goog.labs.userAgent.browser.matchEdgeChromium_() ||
        goog.labs.userAgent.browser.matchOperaChromium_() ||
        goog.labs.userAgent.browser.matchFirefox_() ||
        goog.labs.userAgent.browser.isSilk() ||
        goog.labs.userAgent.util.matchUserAgent('Android'));
};


/**
 * @return {boolean} Whether the user's browser is Coast (Opera's Webkit-based
 *     iOS browser).
 * @private
 */
goog.labs.userAgent.browser.matchCoast_ = function() {
  return goog.labs.userAgent.util.matchUserAgent('Coast');
};


/**
 * @return {boolean} Whether the user's browser is iOS Webview.
 * @private
 */
goog.labs.userAgent.browser.matchIosWebview_ = function() {
  // iOS Webview does not show up as Chrome or Safari. Also check for Opera's
  // WebKit-based iOS browser, Coast.
  return (goog.labs.userAgent.util.matchUserAgent('iPad') ||
          goog.labs.userAgent.util.matchUserAgent('iPhone')) &&
      !goog.labs.userAgent.browser.matchSafari_() &&
      !goog.labs.userAgent.browser.matchChrome_() &&
      !goog.labs.userAgent.browser.matchCoast_() &&
      !goog.labs.userAgent.browser.matchFirefox_() &&
      goog.labs.userAgent.util.matchUserAgent('AppleWebKit');
};


/**
 * @return {boolean} Whether the user's browser is any Chromium browser. This
 * returns true for Chrome, Opera 15+, and Edge Chromium.
 * @private
 */
goog.labs.userAgent.browser.matchChrome_ = function() {
  return (goog.labs.userAgent.util.matchUserAgent('Chrome') ||
          goog.labs.userAgent.util.matchUserAgent('CriOS')) &&
      !goog.labs.userAgent.browser.matchEdgeHtml_();
};


/**
 * @return {boolean} Whether the user's browser is the Android browser.
 * @private
 */
goog.labs.userAgent.browser.matchAndroidBrowser_ = function() {
  // Android can appear in the user agent string for Chrome on Android.
  // This is not the Android standalone browser if it does.
  return goog.labs.userAgent.util.matchUserAgent('Android') &&
      !(goog.labs.userAgent.browser.isChrome() ||
        goog.labs.userAgent.browser.isFirefox() ||
        goog.labs.userAgent.browser.isOpera() ||
        goog.labs.userAgent.browser.isSilk());
};


/**
 * @return {boolean} Whether the user's browser is Opera.
 */
goog.labs.userAgent.browser.isOpera = goog.labs.userAgent.browser.matchOpera_;


/**
 * @return {boolean} Whether the user's browser is IE.
 */
goog.labs.userAgent.browser.isIE = goog.labs.userAgent.browser.matchIE_;


/**
 * @return {boolean} Whether the user's browser is EdgeHTML based Edge.
 */
goog.labs.userAgent.browser.isEdge = goog.labs.userAgent.browser.matchEdgeHtml_;


/**
 * @return {boolean} Whether the user's browser is Chromium based Edge.
 */
goog.labs.userAgent.browser.isEdgeChromium =
    goog.labs.userAgent.browser.matchEdgeChromium_;

/**
 * @return {boolean} Whether the user's browser is Chromium based Opera.
 */
goog.labs.userAgent.browser.isOperaChromium =
    goog.labs.userAgent.browser.matchOperaChromium_;

/**
 * @return {boolean} Whether the user's browser is Firefox.
 */
goog.labs.userAgent.browser.isFirefox =
    goog.labs.userAgent.browser.matchFirefox_;


/**
 * @return {boolean} Whether the user's browser is Safari.
 */
goog.labs.userAgent.browser.isSafari = goog.labs.userAgent.browser.matchSafari_;


/**
 * @return {boolean} Whether the user's browser is Coast (Opera's Webkit-based
 *     iOS browser).
 */
goog.labs.userAgent.browser.isCoast = goog.labs.userAgent.browser.matchCoast_;


/**
 * @return {boolean} Whether the user's browser is iOS Webview.
 */
goog.labs.userAgent.browser.isIosWebview =
    goog.labs.userAgent.browser.matchIosWebview_;


/**
 * @return {boolean} Whether the user's browser is any Chromium based browser (
 * Chrome, Blink-based Opera (15+) and Edge Chromium).
 */
goog.labs.userAgent.browser.isChrome = goog.labs.userAgent.browser.matchChrome_;


/**
 * @return {boolean} Whether the user's browser is the Android browser.
 */
goog.labs.userAgent.browser.isAndroidBrowser =
    goog.labs.userAgent.browser.matchAndroidBrowser_;


/**
 * For more information, see:
 * http://docs.aws.amazon.com/silk/latest/developerguide/user-agent.html
 * @return {boolean} Whether the user's browser is Silk.
 */
goog.labs.userAgent.browser.isSilk = function() {
  return goog.labs.userAgent.util.matchUserAgent('Silk');
};


/**
 * @return {string} The browser version or empty string if version cannot be
 *     determined. Note that for Internet Explorer, this returns the version of
 *     the browser, not the version of the rendering engine. (IE 8 in
 *     compatibility mode will return 8.0 rather than 7.0. To determine the
 *     rendering engine version, look at document.documentMode instead. See
 *     http://msdn.microsoft.com/en-us/library/cc196988(v=vs.85).aspx for more
 *     details.)
 */
goog.labs.userAgent.browser.getVersion = function() {
  var userAgentString = goog.labs.userAgent.util.getUserAgent();
  // Special case IE since IE's version is inside the parenthesis and
  // without the '/'.
  if (goog.labs.userAgent.browser.isIE()) {
    return goog.labs.userAgent.browser.getIEVersion_(userAgentString);
  }

  var versionTuples =
      goog.labs.userAgent.util.extractVersionTuples(userAgentString);

  // Construct a map for easy lookup.
  var versionMap = {};
  goog.array.forEach(versionTuples, function(tuple) {
    // Note that the tuple is of length three, but we only care about the
    // first two.
    var key = tuple[0];
    var value = tuple[1];
    versionMap[key] = value;
  });

  var versionMapHasKey = goog.partial(goog.object.containsKey, versionMap);

  // Gives the value with the first key it finds, otherwise empty string.
  function lookUpValueWithKeys(keys) {
    var key = goog.array.find(keys, versionMapHasKey);
    return versionMap[key] || '';
  }

  // Check Opera before Chrome since Opera 15+ has "Chrome" in the string.
  // See
  // http://my.opera.com/ODIN/blog/2013/07/15/opera-user-agent-strings-opera-15-and-beyond
  if (goog.labs.userAgent.browser.isOpera()) {
    // Opera 10 has Version/10.0 but Opera/9.8, so look for "Version" first.
    // Opera uses 'OPR' for more recent UAs.
    return lookUpValueWithKeys(['Version', 'Opera']);
  }

  // Check Edge before Chrome since it has Chrome in the string.
  if (goog.labs.userAgent.browser.isEdge()) {
    return lookUpValueWithKeys(['Edge']);
  }

  // Check Chromium Edge before Chrome since it has Chrome in the string.
  if (goog.labs.userAgent.browser.isEdgeChromium()) {
    return lookUpValueWithKeys(['Edg']);
  }

  if (goog.labs.userAgent.browser.isChrome()) {
    return lookUpValueWithKeys(['Chrome', 'CriOS', 'HeadlessChrome']);
  }

  // Usually products browser versions are in the third tuple after "Mozilla"
  // and the engine.
  var tuple = versionTuples[2];
  return tuple && tuple[1] || '';
};


/**
 * @param {string|number} version The version to check.
 * @return {boolean} Whether the browser version is higher or the same as the
 *     given version.
 */
goog.labs.userAgent.browser.isVersionOrHigher = function(version) {
  return goog.string.internal.compareVersions(
             goog.labs.userAgent.browser.getVersion(), version) >= 0;
};


/**
 * Determines IE version. More information:
 * http://msdn.microsoft.com/en-us/library/ie/bg182625(v=vs.85).aspx#uaString
 * http://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx
 * http://blogs.msdn.com/b/ie/archive/2010/03/23/introducing-ie9-s-user-agent-string.aspx
 * http://blogs.msdn.com/b/ie/archive/2009/01/09/the-internet-explorer-8-user-agent-string-updated-edition.aspx
 *
 * @param {string} userAgent the User-Agent.
 * @return {string}
 * @private
 */
goog.labs.userAgent.browser.getIEVersion_ = function(userAgent) {
  // IE11 may identify itself as MSIE 9.0 or MSIE 10.0 due to an IE 11 upgrade
  // bug. Example UA:
  // Mozilla/5.0 (MSIE 9.0; Windows NT 6.1; WOW64; Trident/7.0; rv:11.0)
  // like Gecko.
  // See http://www.whatismybrowser.com/developers/unknown-user-agent-fragments.
  var rv = /rv: *([\d\.]*)/.exec(userAgent);
  if (rv && rv[1]) {
    return rv[1];
  }

  var version = '';
  var msie = /MSIE +([\d\.]+)/.exec(userAgent);
  if (msie && msie[1]) {
    // IE in compatibility mode usually identifies itself as MSIE 7.0; in this
    // case, use the Trident version to determine the version of IE. For more
    // details, see the links above.
    var tridentVersion = /Trident\/(\d.\d)/.exec(userAgent);
    if (msie[1] == '7.0') {
      if (tridentVersion && tridentVersion[1]) {
        switch (tridentVersion[1]) {
          case '4.0':
            version = '8.0';
            break;
          case '5.0':
            version = '9.0';
            break;
          case '6.0':
            version = '10.0';
            break;
          case '7.0':
            version = '11.0';
            break;
        }
      } else {
        version = '7.0';
      }
    } else {
      version = msie[1];
    }
  }
  return version;
};

//third_party/javascript/closure/html/safehtml.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */


/**
 * @fileoverview The SafeHtml type and its builders.
 *
 * TODO(xtof): Link to document stating type contract.
 */

goog.provide('goog.html.SafeHtml');

goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.dom.TagName');
goog.require('goog.dom.tags');
goog.require('goog.html.SafeScript');
goog.require('goog.html.SafeStyle');
goog.require('goog.html.SafeStyleSheet');
goog.require('goog.html.SafeUrl');
goog.require('goog.html.TrustedResourceUrl');
goog.require('goog.html.trustedtypes');
goog.require('goog.i18n.bidi.Dir');
goog.require('goog.i18n.bidi.DirectionalString');
goog.require('goog.labs.userAgent.browser');
goog.require('goog.object');
goog.require('goog.string.Const');
goog.require('goog.string.TypedString');
goog.require('goog.string.internal');



/**
 * A string that is safe to use in HTML context in DOM APIs and HTML documents.
 *
 * A SafeHtml is a string-like object that carries the security type contract
 * that its value as a string will not cause untrusted script execution when
 * evaluated as HTML in a browser.
 *
 * Values of this type are guaranteed to be safe to use in HTML contexts,
 * such as, assignment to the innerHTML DOM property, or interpolation into
 * a HTML template in HTML PC_DATA context, in the sense that the use will not
 * result in a Cross-Site-Scripting vulnerability.
 *
 * Instances of this type must be created via the factory methods
 * (`goog.html.SafeHtml.create`, `goog.html.SafeHtml.htmlEscape`),
 * etc and not by invoking its constructor.  The constructor intentionally
 * takes no parameters and the type is immutable; hence only a default instance
 * corresponding to the empty string can be obtained via constructor invocation.
 *
 * Note that there is no `goog.html.SafeHtml.fromConstant`. The reason is that
 * the following code would create an unsafe HTML:
 *
 * ```
 * goog.html.SafeHtml.concat(
 *     goog.html.SafeHtml.fromConstant(goog.string.Const.from('<script>')),
 *     goog.html.SafeHtml.htmlEscape(userInput),
 *     goog.html.SafeHtml.fromConstant(goog.string.Const.from('<\/script>')));
 * ```
 *
 * There's `goog.dom.constHtmlToNode` to create a node from constant strings
 * only.
 *
 * @see goog.html.SafeHtml.create
 * @see goog.html.SafeHtml.htmlEscape
 * @constructor
 * @final
 * @struct
 * @implements {goog.i18n.bidi.DirectionalString}
 * @implements {goog.string.TypedString}
 */
goog.html.SafeHtml = function() {
  /**
   * The contained value of this SafeHtml.  The field has a purposely ugly
   * name to make (non-compiled) code that attempts to directly access this
   * field stand out.
   * @private {!TrustedHTML|string}
   */
  this.privateDoNotAccessOrElseSafeHtmlWrappedValue_ = '';

  /**
   * A type marker used to implement additional run-time type checking.
   * @see goog.html.SafeHtml.unwrap
   * @const {!Object}
   * @private
   */
  this.SAFE_HTML_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
      goog.html.SafeHtml.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;

  /**
   * This SafeHtml's directionality, or null if unknown.
   * @private {?goog.i18n.bidi.Dir}
   */
  this.dir_ = null;
};


/**
 * @define {boolean} Whether to strip out error messages or to leave them in.
 */
goog.html.SafeHtml.ENABLE_ERROR_MESSAGES =
    goog.define('goog.html.SafeHtml.ENABLE_ERROR_MESSAGES', goog.DEBUG);


/**
 * Whether the `style` attribute is supported. Set to false to avoid the byte
 * weight of `goog.html.SafeStyle` where unneeded. An error will be thrown if
 * the `style` attribute is used.
 * @define {boolean}
 */
goog.html.SafeHtml.SUPPORT_STYLE_ATTRIBUTE =
    goog.define('goog.html.SafeHtml.SUPPORT_STYLE_ATTRIBUTE', true);


/**
 * @override
 * @const
 */
goog.html.SafeHtml.prototype.implementsGoogI18nBidiDirectionalString = true;


/** @override */
goog.html.SafeHtml.prototype.getDirection = function() {
  return this.dir_;
};


/**
 * @override
 * @const
 */
goog.html.SafeHtml.prototype.implementsGoogStringTypedString = true;


/**
 * Returns this SafeHtml's value as string.
 *
 * IMPORTANT: In code where it is security relevant that an object's type is
 * indeed `SafeHtml`, use `goog.html.SafeHtml.unwrap` instead of
 * this method. If in doubt, assume that it's security relevant. In particular,
 * note that goog.html functions which return a goog.html type do not guarantee
 * that the returned instance is of the right type. For example:
 *
 * <pre>
 * var fakeSafeHtml = new String('fake');
 * fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
 * var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
 * // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
 * // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml
 * // instanceof goog.html.SafeHtml.
 * </pre>
 *
 * @see goog.html.SafeHtml.unwrap
 * @override
 */
goog.html.SafeHtml.prototype.getTypedStringValue = function() {
  return this.privateDoNotAccessOrElseSafeHtmlWrappedValue_.toString();
};


if (goog.DEBUG) {
  /**
   * Returns a debug string-representation of this value.
   *
   * To obtain the actual string value wrapped in a SafeHtml, use
   * `goog.html.SafeHtml.unwrap`.
   *
   * @see goog.html.SafeHtml.unwrap
   * @override
   */
  goog.html.SafeHtml.prototype.toString = function() {
    return 'SafeHtml{' + this.privateDoNotAccessOrElseSafeHtmlWrappedValue_ +
        '}';
  };
}


/**
 * Performs a runtime check that the provided object is indeed a SafeHtml
 * object, and returns its value.
 * @param {!goog.html.SafeHtml} safeHtml The object to extract from.
 * @return {string} The SafeHtml object's contained string, unless the run-time
 *     type check fails. In that case, `unwrap` returns an innocuous
 *     string, or, if assertions are enabled, throws
 *     `goog.asserts.AssertionError`.
 */
goog.html.SafeHtml.unwrap = function(safeHtml) {
  return goog.html.SafeHtml.unwrapTrustedHTML(safeHtml).toString();
};


/**
 * Unwraps value as TrustedHTML if supported or as a string if not.
 * @param {!goog.html.SafeHtml} safeHtml
 * @return {!TrustedHTML|string}
 * @see goog.html.SafeHtml.unwrap
 */
goog.html.SafeHtml.unwrapTrustedHTML = function(safeHtml) {
  // Perform additional run-time type-checking to ensure that safeHtml is indeed
  // an instance of the expected type.  This provides some additional protection
  // against security bugs due to application code that disables type checks.
  // Specifically, the following checks are performed:
  // 1. The object is an instance of the expected type.
  // 2. The object is not an instance of a subclass.
  // 3. The object carries a type marker for the expected type. "Faking" an
  // object requires a reference to the type marker, which has names intended
  // to stand out in code reviews.
  if (safeHtml instanceof goog.html.SafeHtml &&
      safeHtml.constructor === goog.html.SafeHtml &&
      safeHtml.SAFE_HTML_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
          goog.html.SafeHtml.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
    return safeHtml.privateDoNotAccessOrElseSafeHtmlWrappedValue_;
  } else {
    goog.asserts.fail('expected object of type SafeHtml, got \'' +
        safeHtml + '\' of type ' + goog.typeOf(safeHtml));
    return 'type_error:SafeHtml';
  }
};


/**
 * Shorthand for union of types that can sensibly be converted to strings
 * or might already be SafeHtml (as SafeHtml is a goog.string.TypedString).
 * @private
 * @typedef {string|number|boolean|!goog.string.TypedString|
 *           !goog.i18n.bidi.DirectionalString}
 */
goog.html.SafeHtml.TextOrHtml_;


/**
 * Returns HTML-escaped text as a SafeHtml object.
 *
 * If text is of a type that implements
 * `goog.i18n.bidi.DirectionalString`, the directionality of the new
 * `SafeHtml` object is set to `text`'s directionality, if known.
 * Otherwise, the directionality of the resulting SafeHtml is unknown (i.e.,
 * `null`).
 *
 * @param {!goog.html.SafeHtml.TextOrHtml_} textOrHtml The text to escape. If
 *     the parameter is of type SafeHtml it is returned directly (no escaping
 *     is done).
 * @return {!goog.html.SafeHtml} The escaped text, wrapped as a SafeHtml.
 */
goog.html.SafeHtml.htmlEscape = function(textOrHtml) {
  if (textOrHtml instanceof goog.html.SafeHtml) {
    return textOrHtml;
  }
  var textIsObject = typeof textOrHtml == 'object';
  var dir = null;
  if (textIsObject && textOrHtml.implementsGoogI18nBidiDirectionalString) {
    dir = /** @type {!goog.i18n.bidi.DirectionalString} */ (textOrHtml)
              .getDirection();
  }
  var textAsString;
  if (textIsObject && textOrHtml.implementsGoogStringTypedString) {
    textAsString = /** @type {!goog.string.TypedString} */ (textOrHtml)
                       .getTypedStringValue();
  } else {
    textAsString = String(textOrHtml);
  }
  return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
      goog.string.internal.htmlEscape(textAsString), dir);
};


/**
 * Returns HTML-escaped text as a SafeHtml object, with newlines changed to
 * &lt;br&gt;.
 * @param {!goog.html.SafeHtml.TextOrHtml_} textOrHtml The text to escape. If
 *     the parameter is of type SafeHtml it is returned directly (no escaping
 *     is done).
 * @return {!goog.html.SafeHtml} The escaped text, wrapped as a SafeHtml.
 */
goog.html.SafeHtml.htmlEscapePreservingNewlines = function(textOrHtml) {
  if (textOrHtml instanceof goog.html.SafeHtml) {
    return textOrHtml;
  }
  var html = goog.html.SafeHtml.htmlEscape(textOrHtml);
  return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
      goog.string.internal.newLineToBr(goog.html.SafeHtml.unwrap(html)),
      html.getDirection());
};


/**
 * Returns HTML-escaped text as a SafeHtml object, with newlines changed to
 * &lt;br&gt; and escaping whitespace to preserve spatial formatting. Character
 * entity #160 is used to make it safer for XML.
 * @param {!goog.html.SafeHtml.TextOrHtml_} textOrHtml The text to escape. If
 *     the parameter is of type SafeHtml it is returned directly (no escaping
 *     is done).
 * @return {!goog.html.SafeHtml} The escaped text, wrapped as a SafeHtml.
 */
goog.html.SafeHtml.htmlEscapePreservingNewlinesAndSpaces = function(
    textOrHtml) {
  if (textOrHtml instanceof goog.html.SafeHtml) {
    return textOrHtml;
  }
  var html = goog.html.SafeHtml.htmlEscape(textOrHtml);
  return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
      goog.string.internal.whitespaceEscape(goog.html.SafeHtml.unwrap(html)),
      html.getDirection());
};


/**
 * Coerces an arbitrary object into a SafeHtml object.
 *
 * If `textOrHtml` is already of type `goog.html.SafeHtml`, the same
 * object is returned. Otherwise, `textOrHtml` is coerced to string, and
 * HTML-escaped. If `textOrHtml` is of a type that implements
 * `goog.i18n.bidi.DirectionalString`, its directionality, if known, is
 * preserved.
 *
 * @param {!goog.html.SafeHtml.TextOrHtml_} textOrHtml The text or SafeHtml to
 *     coerce.
 * @return {!goog.html.SafeHtml} The resulting SafeHtml object.
 * @deprecated Use goog.html.SafeHtml.htmlEscape.
 */
goog.html.SafeHtml.from = goog.html.SafeHtml.htmlEscape;


/**
 * Converts an arbitrary string into an HTML comment by HTML-escaping the
 * contents and embedding the result between HTML comment markers.
 *
 * Escaping is needed because Internet Explorer supports conditional comments
 * and so may render HTML markup within comments.
 *
 * @param {string} text
 * @return {!goog.html.SafeHtml}
 */
goog.html.SafeHtml.comment = function(text) {
  return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
      '<!--' + goog.string.internal.htmlEscape(text) + '-->', null);
};


/**
 * @const
 * @private
 */
goog.html.SafeHtml.VALID_NAMES_IN_TAG_ = /^[a-zA-Z0-9-]+$/;


/**
 * Set of attributes containing URL as defined at
 * http://www.w3.org/TR/html5/index.html#attributes-1.
 * @private @const {!Object<string,boolean>}
 */
goog.html.SafeHtml.URL_ATTRIBUTES_ = goog.object.createSet(
    'action', 'cite', 'data', 'formaction', 'href', 'manifest', 'poster',
    'src');


/**
 * Tags which are unsupported via create(). They might be supported via a
 * tag-specific create method. These are tags which might require a
 * TrustedResourceUrl in one of their attributes or a restricted type for
 * their content.
 * @private @const {!Object<string,boolean>}
 */
goog.html.SafeHtml.NOT_ALLOWED_TAG_NAMES_ = goog.object.createSet(
    goog.dom.TagName.APPLET, goog.dom.TagName.BASE, goog.dom.TagName.EMBED,
    goog.dom.TagName.IFRAME, goog.dom.TagName.LINK, goog.dom.TagName.MATH,
    goog.dom.TagName.META, goog.dom.TagName.OBJECT, goog.dom.TagName.SCRIPT,
    goog.dom.TagName.STYLE, goog.dom.TagName.SVG, goog.dom.TagName.TEMPLATE);


/**
 * @typedef {string|number|goog.string.TypedString|
 *     goog.html.SafeStyle.PropertyMap|undefined}
 */
goog.html.SafeHtml.AttributeValue;


/**
 * Creates a SafeHtml content consisting of a tag with optional attributes and
 * optional content.
 *
 * For convenience tag names and attribute names are accepted as regular
 * strings, instead of goog.string.Const. Nevertheless, you should not pass
 * user-controlled values to these parameters. Note that these parameters are
 * syntactically validated at runtime, and invalid values will result in
 * an exception.
 *
 * Example usage:
 *
 * goog.html.SafeHtml.create('br');
 * goog.html.SafeHtml.create('div', {'class': 'a'});
 * goog.html.SafeHtml.create('p', {}, 'a');
 * goog.html.SafeHtml.create('p', {}, goog.html.SafeHtml.create('br'));
 *
 * goog.html.SafeHtml.create('span', {
 *   'style': {'margin': '0'}
 * });
 *
 * To guarantee SafeHtml's type contract is upheld there are restrictions on
 * attribute values and tag names.
 *
 * - For attributes which contain script code (on*), a goog.string.Const is
 *   required.
 * - For attributes which contain style (style), a goog.html.SafeStyle or a
 *   goog.html.SafeStyle.PropertyMap is required.
 * - For attributes which are interpreted as URLs (e.g. src, href) a
 *   goog.html.SafeUrl, goog.string.Const or string is required. If a string
 *   is passed, it will be sanitized with SafeUrl.sanitize().
 * - For tags which can load code or set security relevant page metadata,
 *   more specific goog.html.SafeHtml.create*() functions must be used. Tags
 *   which are not supported by this function are applet, base, embed, iframe,
 *   link, math, object, script, style, svg, and template.
 *
 * @param {!goog.dom.TagName|string} tagName The name of the tag. Only tag names
 *     consisting of [a-zA-Z0-9-] are allowed. Tag names documented above are
 *     disallowed.
 * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
 *     Mapping from attribute names to their values. Only attribute names
 *     consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined causes
 *     the attribute to be omitted.
 * @param {!goog.html.SafeHtml.TextOrHtml_|
 *     !Array<!goog.html.SafeHtml.TextOrHtml_>=} opt_content Content to
 *     HTML-escape and put inside the tag. This must be empty for void tags
 *     like <br>. Array elements are concatenated.
 * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
 * @throws {Error} If invalid tag name, attribute name, or attribute value is
 *     provided.
 * @throws {goog.asserts.AssertionError} If content for void tag is provided.
 */
goog.html.SafeHtml.create = function(tagName, opt_attributes, opt_content) {
  goog.html.SafeHtml.verifyTagName(String(tagName));
  return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
      String(tagName), opt_attributes, opt_content);
};


/**
 * Verifies if the tag name is valid and if it doesn't change the context.
 * E.g. STRONG is fine but SCRIPT throws because it changes context. See
 * goog.html.SafeHtml.create for an explanation of allowed tags.
 * @param {string} tagName
 * @throws {Error} If invalid tag name is provided.
 * @package
 */
goog.html.SafeHtml.verifyTagName = function(tagName) {
  if (!goog.html.SafeHtml.VALID_NAMES_IN_TAG_.test(tagName)) {
    throw new Error(
        goog.html.SafeHtml.ENABLE_ERROR_MESSAGES ?
            'Invalid tag name <' + tagName + '>.' :
            '');
  }
  if (tagName.toUpperCase() in goog.html.SafeHtml.NOT_ALLOWED_TAG_NAMES_) {
    throw new Error(
        goog.html.SafeHtml.ENABLE_ERROR_MESSAGES ?

            'Tag name <' + tagName + '> is not allowed for SafeHtml.' :
            '');
  }
};


/**
 * Creates a SafeHtml representing an iframe tag.
 *
 * This by default restricts the iframe as much as possible by setting the
 * sandbox attribute to the empty string. If the iframe requires less
 * restrictions, set the sandbox attribute as tight as possible, but do not rely
 * on the sandbox as a security feature because it is not supported by older
 * browsers. If a sandbox is essential to security (e.g. for third-party
 * frames), use createSandboxIframe which checks for browser support.
 *
 * @see https://developer.mozilla.org/en/docs/Web/HTML/Element/iframe#attr-sandbox
 *
 * @param {?goog.html.TrustedResourceUrl=} opt_src The value of the src
 *     attribute. If null or undefined src will not be set.
 * @param {?goog.html.SafeHtml=} opt_srcdoc The value of the srcdoc attribute.
 *     If null or undefined srcdoc will not be set.
 * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
 *     Mapping from attribute names to their values. Only attribute names
 *     consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined causes
 *     the attribute to be omitted.
 * @param {!goog.html.SafeHtml.TextOrHtml_|
 *     !Array<!goog.html.SafeHtml.TextOrHtml_>=} opt_content Content to
 *     HTML-escape and put inside the tag. Array elements are concatenated.
 * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
 * @throws {Error} If invalid tag name, attribute name, or attribute value is
 *     provided. If opt_attributes contains the src or srcdoc attributes.
 */
goog.html.SafeHtml.createIframe = function(
    opt_src, opt_srcdoc, opt_attributes, opt_content) {
  if (opt_src) {
    // Check whether this is really TrustedResourceUrl.
    goog.html.TrustedResourceUrl.unwrap(opt_src);
  }

  var fixedAttributes = {};
  fixedAttributes['src'] = opt_src || null;
  fixedAttributes['srcdoc'] =
      opt_srcdoc && goog.html.SafeHtml.unwrap(opt_srcdoc);
  var defaultAttributes = {'sandbox': ''};
  var attributes = goog.html.SafeHtml.combineAttributes(
      fixedAttributes, defaultAttributes, opt_attributes);
  return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
      'iframe', attributes, opt_content);
};


/**
 * Creates a SafeHtml representing a sandboxed iframe tag.
 *
 * The sandbox attribute is enforced in its most restrictive mode, an empty
 * string. Consequently, the security requirements for the src and srcdoc
 * attributes are relaxed compared to SafeHtml.createIframe. This function
 * will throw on browsers that do not support the sandbox attribute, as
 * determined by SafeHtml.canUseSandboxIframe.
 *
 * The SafeHtml returned by this function can trigger downloads with no
 * user interaction on Chrome (though only a few, further attempts are blocked).
 * Firefox and IE will block all downloads from the sandbox.
 *
 * @see https://developer.mozilla.org/en/docs/Web/HTML/Element/iframe#attr-sandbox
 * @see https://lists.w3.org/Archives/Public/public-whatwg-archive/2013Feb/0112.html
 *
 * @param {string|!goog.html.SafeUrl=} opt_src The value of the src
 *     attribute. If null or undefined src will not be set.
 * @param {string=} opt_srcdoc The value of the srcdoc attribute.
 *     If null or undefined srcdoc will not be set. Will not be sanitized.
 * @param {!Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
 *     Mapping from attribute names to their values. Only attribute names
 *     consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined causes
 *     the attribute to be omitted.
 * @param {!goog.html.SafeHtml.TextOrHtml_|
 *     !Array<!goog.html.SafeHtml.TextOrHtml_>=} opt_content Content to
 *     HTML-escape and put inside the tag. Array elements are concatenated.
 * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
 * @throws {Error} If invalid tag name, attribute name, or attribute value is
 *     provided. If opt_attributes contains the src, srcdoc or sandbox
 *     attributes. If browser does not support the sandbox attribute on iframe.
 */
goog.html.SafeHtml.createSandboxIframe = function(
    opt_src, opt_srcdoc, opt_attributes, opt_content) {
  if (!goog.html.SafeHtml.canUseSandboxIframe()) {
    throw new Error(
        goog.html.SafeHtml.ENABLE_ERROR_MESSAGES ?
            'The browser does not support sandboxed iframes.' :
            '');
  }

  var fixedAttributes = {};
  if (opt_src) {
    // Note that sanitize is a no-op on SafeUrl.
    fixedAttributes['src'] =
        goog.html.SafeUrl.unwrap(goog.html.SafeUrl.sanitize(opt_src));
  } else {
    fixedAttributes['src'] = null;
  }
  fixedAttributes['srcdoc'] = opt_srcdoc || null;
  fixedAttributes['sandbox'] = '';
  var attributes =
      goog.html.SafeHtml.combineAttributes(fixedAttributes, {}, opt_attributes);
  return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
      'iframe', attributes, opt_content);
};


/**
 * Checks if the user agent supports sandboxed iframes.
 * @return {boolean}
 */
goog.html.SafeHtml.canUseSandboxIframe = function() {
  return goog.global['HTMLIFrameElement'] &&
      ('sandbox' in goog.global['HTMLIFrameElement'].prototype);
};


/**
 * Creates a SafeHtml representing a script tag with the src attribute.
 * @param {!goog.html.TrustedResourceUrl} src The value of the src
 * attribute.
 * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=}
 * opt_attributes
 *     Mapping from attribute names to their values. Only attribute names
 *     consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined
 *     causes the attribute to be omitted.
 * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
 * @throws {Error} If invalid attribute name or value is provided. If
 *     opt_attributes contains the src attribute.
 */
goog.html.SafeHtml.createScriptSrc = function(src, opt_attributes) {
  // TODO(mlourenco): The charset attribute should probably be blocked. If
  // its value is attacker controlled, the script contains attacker controlled
  // sub-strings (even if properly escaped) and the server does not set charset
  // then XSS is likely possible.
  // https://html.spec.whatwg.org/multipage/scripting.html#dom-script-charset

  // Check whether this is really TrustedResourceUrl.
  goog.html.TrustedResourceUrl.unwrap(src);

  var fixedAttributes = {'src': src};
  var defaultAttributes = {};
  var attributes = goog.html.SafeHtml.combineAttributes(
      fixedAttributes, defaultAttributes, opt_attributes);
  return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
      'script', attributes);
};


/**
 * Creates a SafeHtml representing a script tag. Does not allow the language,
 * src, text or type attributes to be set.
 * @param {!goog.html.SafeScript|!Array<!goog.html.SafeScript>}
 *     script Content to put inside the tag. Array elements are
 *     concatenated.
 * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
 *     Mapping from attribute names to their values. Only attribute names
 *     consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined causes
 *     the attribute to be omitted.
 * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
 * @throws {Error} If invalid attribute name or attribute value is provided. If
 *     opt_attributes contains the language, src, text or type attribute.
 */
goog.html.SafeHtml.createScript = function(script, opt_attributes) {
  for (var attr in opt_attributes) {
    var attrLower = attr.toLowerCase();
    if (attrLower == 'language' || attrLower == 'src' || attrLower == 'text' ||
        attrLower == 'type') {
      throw new Error(
          goog.html.SafeHtml.ENABLE_ERROR_MESSAGES ?
              'Cannot set "' + attrLower + '" attribute' :
              '');
    }
  }

  var content = '';
  script = goog.array.concat(script);
  for (var i = 0; i < script.length; i++) {
    content += goog.html.SafeScript.unwrap(script[i]);
  }
  // Convert to SafeHtml so that it's not HTML-escaped. This is safe because
  // as part of its contract, SafeScript should have no dangerous '<'.
  var htmlContent =
      goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
          content, goog.i18n.bidi.Dir.NEUTRAL);
  return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
      'script', opt_attributes, htmlContent);
};


/**
 * Creates a SafeHtml representing a style tag. The type attribute is set
 * to "text/css".
 * @param {!goog.html.SafeStyleSheet|!Array<!goog.html.SafeStyleSheet>}
 *     styleSheet Content to put inside the tag. Array elements are
 *     concatenated.
 * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
 *     Mapping from attribute names to their values. Only attribute names
 *     consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined causes
 *     the attribute to be omitted.
 * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
 * @throws {Error} If invalid attribute name or attribute value is provided. If
 *     opt_attributes contains the type attribute.
 */
goog.html.SafeHtml.createStyle = function(styleSheet, opt_attributes) {
  var fixedAttributes = {'type': 'text/css'};
  var defaultAttributes = {};
  var attributes = goog.html.SafeHtml.combineAttributes(
      fixedAttributes, defaultAttributes, opt_attributes);

  var content = '';
  styleSheet = goog.array.concat(styleSheet);
  for (var i = 0; i < styleSheet.length; i++) {
    content += goog.html.SafeStyleSheet.unwrap(styleSheet[i]);
  }
  // Convert to SafeHtml so that it's not HTML-escaped. This is safe because
  // as part of its contract, SafeStyleSheet should have no dangerous '<'.
  var htmlContent =
      goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
          content, goog.i18n.bidi.Dir.NEUTRAL);
  return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
      'style', attributes, htmlContent);
};


/**
 * Creates a SafeHtml representing a meta refresh tag.
 * @param {!goog.html.SafeUrl|string} url Where to redirect. If a string is
 *     passed, it will be sanitized with SafeUrl.sanitize().
 * @param {number=} opt_secs Number of seconds until the page should be
 *     reloaded. Will be set to 0 if unspecified.
 * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
 */
goog.html.SafeHtml.createMetaRefresh = function(url, opt_secs) {

  // Note that sanitize is a no-op on SafeUrl.
  var unwrappedUrl = goog.html.SafeUrl.unwrap(goog.html.SafeUrl.sanitize(url));

  if (goog.labs.userAgent.browser.isIE() ||
      goog.labs.userAgent.browser.isEdge()) {
    // IE/EDGE can't parse the content attribute if the url contains a
    // semicolon. We can fix this by adding quotes around the url, but then we
    // can't parse quotes in the URL correctly. Also, it seems that IE/EDGE
    // did not unescape semicolons in these URLs at some point in the past. We
    // take a best-effort approach.
    //
    // If the URL has semicolons (which may happen in some cases, see
    // http://www.w3.org/TR/1999/REC-html401-19991224/appendix/notes.html#h-B.2
    // for instance), wrap it in single quotes to protect the semicolons.
    // If the URL has semicolons and single quotes, url-encode the single quotes
    // as well.
    //
    // This is imperfect. Notice that both ' and ; are reserved characters in
    // URIs, so this could do the wrong thing, but at least it will do the wrong
    // thing in only rare cases.
    if (goog.string.internal.contains(unwrappedUrl, ';')) {
      unwrappedUrl = "'" + unwrappedUrl.replace(/'/g, '%27') + "'";
    }
  }
  var attributes = {
    'http-equiv': 'refresh',
    'content': (opt_secs || 0) + '; url=' + unwrappedUrl
  };

  // This function will handle the HTML escaping for attributes.
  return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
      'meta', attributes);
};


/**
 * @param {string} tagName The tag name.
 * @param {string} name The attribute name.
 * @param {!goog.html.SafeHtml.AttributeValue} value The attribute value.
 * @return {string} A "name=value" string.
 * @throws {Error} If attribute value is unsafe for the given tag and attribute.
 * @private
 */
goog.html.SafeHtml.getAttrNameAndValue_ = function(tagName, name, value) {
  // If it's goog.string.Const, allow any valid attribute name.
  if (value instanceof goog.string.Const) {
    value = goog.string.Const.unwrap(value);
  } else if (name.toLowerCase() == 'style') {
    if (goog.html.SafeHtml.SUPPORT_STYLE_ATTRIBUTE) {
      value = goog.html.SafeHtml.getStyleValue_(value);
    } else {
      throw new Error(
          goog.html.SafeHtml.ENABLE_ERROR_MESSAGES ?
              'Attribute "style" not supported.' :
              '');
    }
  } else if (/^on/i.test(name)) {
    // TODO(jakubvrana): Disallow more attributes with a special meaning.
    throw new Error(
        goog.html.SafeHtml.ENABLE_ERROR_MESSAGES ? 'Attribute "' + name +
                '" requires goog.string.Const value, "' + value + '" given.' :
                                                   '');
    // URL attributes handled differently according to tag.
  } else if (name.toLowerCase() in goog.html.SafeHtml.URL_ATTRIBUTES_) {
    if (value instanceof goog.html.TrustedResourceUrl) {
      value = goog.html.TrustedResourceUrl.unwrap(value);
    } else if (value instanceof goog.html.SafeUrl) {
      value = goog.html.SafeUrl.unwrap(value);
    } else if (typeof value === 'string') {
      value = goog.html.SafeUrl.sanitize(value).getTypedStringValue();
    } else {
      throw new Error(
          goog.html.SafeHtml.ENABLE_ERROR_MESSAGES ?
              'Attribute "' + name + '" on tag "' + tagName +
                  '" requires goog.html.SafeUrl, goog.string.Const, or' +
                  ' string, value "' + value + '" given.' :
              '');
    }
  }

  // Accept SafeUrl, TrustedResourceUrl, etc. for attributes which only require
  // HTML-escaping.
  if (value.implementsGoogStringTypedString) {
    // Ok to call getTypedStringValue() since there's no reliance on the type
    // contract for security here.
    value =
        /** @type {!goog.string.TypedString} */ (value).getTypedStringValue();
  }

  goog.asserts.assert(
      typeof value === 'string' || typeof value === 'number',
      'String or number value expected, got ' + (typeof value) +
          ' with value: ' + value);
  return name + '="' + goog.string.internal.htmlEscape(String(value)) + '"';
};


/**
 * Gets value allowed in "style" attribute.
 * @param {!goog.html.SafeHtml.AttributeValue} value It could be SafeStyle or a
 *     map which will be passed to goog.html.SafeStyle.create.
 * @return {string} Unwrapped value.
 * @throws {Error} If string value is given.
 * @private
 */
goog.html.SafeHtml.getStyleValue_ = function(value) {
  if (!goog.isObject(value)) {
    throw new Error(
        goog.html.SafeHtml.ENABLE_ERROR_MESSAGES ?
            'The "style" attribute requires goog.html.SafeStyle or map ' +
                'of style properties, ' + (typeof value) + ' given: ' + value :
            '');
  }
  if (!(value instanceof goog.html.SafeStyle)) {
    // Process the property bag into a style object.
    value = goog.html.SafeStyle.create(value);
  }
  return goog.html.SafeStyle.unwrap(value);
};


/**
 * Creates a SafeHtml content with known directionality consisting of a tag with
 * optional attributes and optional content.
 * @param {!goog.i18n.bidi.Dir} dir Directionality.
 * @param {string} tagName
 * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
 * @param {!goog.html.SafeHtml.TextOrHtml_|
 *     !Array<!goog.html.SafeHtml.TextOrHtml_>=} opt_content
 * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
 */
goog.html.SafeHtml.createWithDir = function(
    dir, tagName, opt_attributes, opt_content) {
  var html = goog.html.SafeHtml.create(tagName, opt_attributes, opt_content);
  html.dir_ = dir;
  return html;
};


/**
 * Creates a new SafeHtml object by joining the parts with separator.
 * @param {!goog.html.SafeHtml.TextOrHtml_} separator
 * @param {!Array<!goog.html.SafeHtml.TextOrHtml_|
 *     !Array<!goog.html.SafeHtml.TextOrHtml_>>} parts Parts to join. If a part
 *     contains an array then each member of this array is also joined with the
 *     separator.
 * @return {!goog.html.SafeHtml}
 */
goog.html.SafeHtml.join = function(separator, parts) {
  var separatorHtml = goog.html.SafeHtml.htmlEscape(separator);
  var dir = separatorHtml.getDirection();
  var content = [];

  /**
   * @param {!goog.html.SafeHtml.TextOrHtml_|
   *     !Array<!goog.html.SafeHtml.TextOrHtml_>} argument
   */
  var addArgument = function(argument) {
    if (Array.isArray(argument)) {
      goog.array.forEach(argument, addArgument);
    } else {
      var html = goog.html.SafeHtml.htmlEscape(argument);
      content.push(goog.html.SafeHtml.unwrap(html));
      var htmlDir = html.getDirection();
      if (dir == goog.i18n.bidi.Dir.NEUTRAL) {
        dir = htmlDir;
      } else if (htmlDir != goog.i18n.bidi.Dir.NEUTRAL && dir != htmlDir) {
        dir = null;
      }
    }
  };

  goog.array.forEach(parts, addArgument);
  return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
      content.join(goog.html.SafeHtml.unwrap(separatorHtml)), dir);
};


/**
 * Creates a new SafeHtml object by concatenating values.
 * @param {...(!goog.html.SafeHtml.TextOrHtml_|
 *     !Array<!goog.html.SafeHtml.TextOrHtml_>)} var_args Values to concatenate.
 * @return {!goog.html.SafeHtml}
 */
goog.html.SafeHtml.concat = function(var_args) {
  return goog.html.SafeHtml.join(
      goog.html.SafeHtml.EMPTY, Array.prototype.slice.call(arguments));
};


/**
 * Creates a new SafeHtml object with known directionality by concatenating the
 * values.
 * @param {!goog.i18n.bidi.Dir} dir Directionality.
 * @param {...(!goog.html.SafeHtml.TextOrHtml_|
 *     !Array<!goog.html.SafeHtml.TextOrHtml_>)} var_args Elements of array
 *     arguments would be processed recursively.
 * @return {!goog.html.SafeHtml}
 */
goog.html.SafeHtml.concatWithDir = function(dir, var_args) {
  var html = goog.html.SafeHtml.concat(goog.array.slice(arguments, 1));
  html.dir_ = dir;
  return html;
};


/**
 * Type marker for the SafeHtml type, used to implement additional run-time
 * type checking.
 * @const {!Object}
 * @private
 */
goog.html.SafeHtml.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};


/**
 * Package-internal utility method to create SafeHtml instances.
 *
 * @param {string} html The string to initialize the SafeHtml object with.
 * @param {?goog.i18n.bidi.Dir} dir The directionality of the SafeHtml to be
 *     constructed, or null if unknown.
 * @return {!goog.html.SafeHtml} The initialized SafeHtml object.
 * @package
 */
goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse = function(
    html, dir) {
  return new goog.html.SafeHtml().initSecurityPrivateDoNotAccessOrElse_(
      html, dir);
};


/**
 * Called from createSafeHtmlSecurityPrivateDoNotAccessOrElse(). This
 * method exists only so that the compiler can dead code eliminate static
 * fields (like EMPTY) when they're not accessed.
 * @param {string} html
 * @param {?goog.i18n.bidi.Dir} dir
 * @return {!goog.html.SafeHtml}
 * @private
 */
goog.html.SafeHtml.prototype.initSecurityPrivateDoNotAccessOrElse_ = function(
    html, dir) {
  this.privateDoNotAccessOrElseSafeHtmlWrappedValue_ =
      goog.html.trustedtypes.PRIVATE_DO_NOT_ACCESS_OR_ELSE_POLICY ?
      goog.html.trustedtypes.PRIVATE_DO_NOT_ACCESS_OR_ELSE_POLICY.createHTML(
          html) :
      html;
  this.dir_ = dir;
  return this;
};


/**
 * Like create() but does not restrict which tags can be constructed.
 *
 * @param {string} tagName Tag name. Set or validated by caller.
 * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
 * @param {(!goog.html.SafeHtml.TextOrHtml_|
 *     !Array<!goog.html.SafeHtml.TextOrHtml_>)=} opt_content
 * @return {!goog.html.SafeHtml}
 * @throws {Error} If invalid or unsafe attribute name or value is provided.
 * @throws {goog.asserts.AssertionError} If content for void tag is provided.
 * @package
 */
goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse = function(
    tagName, opt_attributes, opt_content) {
  var dir = null;
  var result = '<' + tagName;
  result += goog.html.SafeHtml.stringifyAttributes(tagName, opt_attributes);

  var content = opt_content;
  if (content == null) {
    content = [];
  } else if (!Array.isArray(content)) {
    content = [content];
  }

  if (goog.dom.tags.isVoidTag(tagName.toLowerCase())) {
    goog.asserts.assert(
        !content.length, 'Void tag <' + tagName + '> does not allow content.');
    result += '>';
  } else {
    var html = goog.html.SafeHtml.concat(content);
    result += '>' + goog.html.SafeHtml.unwrap(html) + '</' + tagName + '>';
    dir = html.getDirection();
  }

  var dirAttribute = opt_attributes && opt_attributes['dir'];
  if (dirAttribute) {
    if (/^(ltr|rtl|auto)$/i.test(dirAttribute)) {
      // If the tag has the "dir" attribute specified then its direction is
      // neutral because it can be safely used in any context.
      dir = goog.i18n.bidi.Dir.NEUTRAL;
    } else {
      dir = null;
    }
  }

  return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
      result, dir);
};


/**
 * Creates a string with attributes to insert after tagName.
 * @param {string} tagName
 * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
 * @return {string} Returns an empty string if there are no attributes, returns
 *     a string starting with a space otherwise.
 * @throws {Error} If attribute value is unsafe for the given tag and attribute.
 * @package
 */
goog.html.SafeHtml.stringifyAttributes = function(tagName, opt_attributes) {
  var result = '';
  if (opt_attributes) {
    for (var name in opt_attributes) {
      if (!goog.html.SafeHtml.VALID_NAMES_IN_TAG_.test(name)) {
        throw new Error(
            goog.html.SafeHtml.ENABLE_ERROR_MESSAGES ?
                'Invalid attribute name "' + name + '".' :
                '');
      }
      var value = opt_attributes[name];
      if (value == null) {
        continue;
      }
      result +=
          ' ' + goog.html.SafeHtml.getAttrNameAndValue_(tagName, name, value);
    }
  }
  return result;
};


/**
 * @param {!Object<string, ?goog.html.SafeHtml.AttributeValue>} fixedAttributes
 * @param {!Object<string, string>} defaultAttributes
 * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
 *     Optional attributes passed to create*().
 * @return {!Object<string, ?goog.html.SafeHtml.AttributeValue>}
 * @throws {Error} If opt_attributes contains an attribute with the same name
 *     as an attribute in fixedAttributes.
 * @package
 */
goog.html.SafeHtml.combineAttributes = function(
    fixedAttributes, defaultAttributes, opt_attributes) {
  var combinedAttributes = {};
  var name;

  for (name in fixedAttributes) {
    goog.asserts.assert(name.toLowerCase() == name, 'Must be lower case');
    combinedAttributes[name] = fixedAttributes[name];
  }
  for (name in defaultAttributes) {
    goog.asserts.assert(name.toLowerCase() == name, 'Must be lower case');
    combinedAttributes[name] = defaultAttributes[name];
  }

  if (opt_attributes) {
    for (name in opt_attributes) {
      var nameLower = name.toLowerCase();
      if (nameLower in fixedAttributes) {
        throw new Error(
            goog.html.SafeHtml.ENABLE_ERROR_MESSAGES ?
                'Cannot override "' + nameLower + '" attribute, got "' + name +
                    '" with value "' + opt_attributes[name] + '"' :
                '');
      }
      if (nameLower in defaultAttributes) {
        delete combinedAttributes[nameLower];
      }
      combinedAttributes[name] = opt_attributes[name];
    }
  }

  return combinedAttributes;
};


/**
 * A SafeHtml instance corresponding to the HTML doctype: "<!DOCTYPE html>".
 * @const {!goog.html.SafeHtml}
 */
goog.html.SafeHtml.DOCTYPE_HTML =
    goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
        '<!DOCTYPE html>', goog.i18n.bidi.Dir.NEUTRAL);


/**
 * A SafeHtml instance corresponding to the empty string.
 * @const {!goog.html.SafeHtml}
 */
goog.html.SafeHtml.EMPTY =
    goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
        '', goog.i18n.bidi.Dir.NEUTRAL);


/**
 * A SafeHtml instance corresponding to the <br> tag.
 * @const {!goog.html.SafeHtml}
 */
goog.html.SafeHtml.BR =
    goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
        '<br>', goog.i18n.bidi.Dir.NEUTRAL);

//third_party/javascript/closure/html/uncheckedconversions.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Unchecked conversions to create values of goog.html types from
 * plain strings.  Use of these functions could potentially result in instances
 * of goog.html types that violate their type contracts, and hence result in
 * security vulnerabilties.
 *
 * Therefore, all uses of the methods herein must be carefully security
 * reviewed.  Avoid use of the methods in this file whenever possible; instead
 * prefer to create instances of goog.html types using inherently safe builders
 * or template systems.
 *
 * MOE:begin_intracomment_strip
 * See http://go/safehtml-unchecked for guidelines on using these functions.
 * MOE:end_intracomment_strip
 *
 * MOE:begin_intracomment_strip
 * MAINTAINERS: Use of these functions is detected with a Tricorder analyzer.
 * If adding functions here also add them to analyzer's list at
 * j/c/g/devtools/staticanalysis/pipeline/analyzers/shared/SafeHtmlAnalyzers.java.
 * MOE:end_intracomment_strip
 */


goog.provide('goog.html.uncheckedconversions');

goog.require('goog.asserts');
goog.require('goog.html.SafeHtml');
goog.require('goog.html.SafeScript');
goog.require('goog.html.SafeStyle');
goog.require('goog.html.SafeStyleSheet');
goog.require('goog.html.SafeUrl');
goog.require('goog.html.TrustedResourceUrl');
goog.require('goog.string.Const');
goog.require('goog.string.internal');


/**
 * Performs an "unchecked conversion" to SafeHtml from a plain string that is
 * known to satisfy the SafeHtml type contract.
 *
 * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
 * that the value of `html` satisfies the SafeHtml type contract in all
 * possible program states.
 *
 * MOE:begin_intracomment_strip
 * See http://go/safehtml-unchecked for guidelines on using these functions.
 * MOE:end_intracomment_strip
 *
 * @param {!goog.string.Const} justification A constant string explaining why
 *     this use of this method is safe. May include a security review ticket
 *     number.
 * @param {string} html A string that is claimed to adhere to the SafeHtml
 *     contract.
 * @param {?goog.i18n.bidi.Dir=} opt_dir The optional directionality of the
 *     SafeHtml to be constructed. A null or undefined value signifies an
 *     unknown directionality.
 * @return {!goog.html.SafeHtml} The value of html, wrapped in a SafeHtml
 *     object.
 */
goog.html.uncheckedconversions.safeHtmlFromStringKnownToSatisfyTypeContract =
    function(justification, html, opt_dir) {
  // unwrap() called inside an assert so that justification can be optimized
  // away in production code.
  goog.asserts.assertString(
      goog.string.Const.unwrap(justification), 'must provide justification');
  goog.asserts.assert(
      !goog.string.internal.isEmptyOrWhitespace(
          goog.string.Const.unwrap(justification)),
      'must provide non-empty justification');
  return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
      html, opt_dir || null);
};


/**
 * Performs an "unchecked conversion" to SafeScript from a plain string that is
 * known to satisfy the SafeScript type contract.
 *
 * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
 * that the value of `script` satisfies the SafeScript type contract in
 * all possible program states.
 *
 * MOE:begin_intracomment_strip
 * See http://go/safehtml-unchecked for guidelines on using these functions.
 * MOE:end_intracomment_strip
 *
 * @param {!goog.string.Const} justification A constant string explaining why
 *     this use of this method is safe. May include a security review ticket
 *     number.
 * @param {string} script The string to wrap as a SafeScript.
 * @return {!goog.html.SafeScript} The value of `script`, wrapped in a
 *     SafeScript object.
 */
goog.html.uncheckedconversions.safeScriptFromStringKnownToSatisfyTypeContract =
    function(justification, script) {
  // unwrap() called inside an assert so that justification can be optimized
  // away in production code.
  goog.asserts.assertString(
      goog.string.Const.unwrap(justification), 'must provide justification');
  goog.asserts.assert(
      !goog.string.internal.isEmptyOrWhitespace(
          goog.string.Const.unwrap(justification)),
      'must provide non-empty justification');
  return goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse(
      script);
};


/**
 * Performs an "unchecked conversion" to SafeStyle from a plain string that is
 * known to satisfy the SafeStyle type contract.
 *
 * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
 * that the value of `style` satisfies the SafeStyle type contract in all
 * possible program states.
 *
 * MOE:begin_intracomment_strip
 * See http://go/safehtml-unchecked for guidelines on using these functions.
 * MOE:end_intracomment_strip
 *
 * @param {!goog.string.Const} justification A constant string explaining why
 *     this use of this method is safe. May include a security review ticket
 *     number.
 * @param {string} style The string to wrap as a SafeStyle.
 * @return {!goog.html.SafeStyle} The value of `style`, wrapped in a
 *     SafeStyle object.
 */
goog.html.uncheckedconversions.safeStyleFromStringKnownToSatisfyTypeContract =
    function(justification, style) {
  // unwrap() called inside an assert so that justification can be optimized
  // away in production code.
  goog.asserts.assertString(
      goog.string.Const.unwrap(justification), 'must provide justification');
  goog.asserts.assert(
      !goog.string.internal.isEmptyOrWhitespace(
          goog.string.Const.unwrap(justification)),
      'must provide non-empty justification');
  return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(
      style);
};


/**
 * Performs an "unchecked conversion" to SafeStyleSheet from a plain string
 * that is known to satisfy the SafeStyleSheet type contract.
 *
 * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
 * that the value of `styleSheet` satisfies the SafeStyleSheet type
 * contract in all possible program states.
 *
 * MOE:begin_intracomment_strip
 * See http://go/safehtml-unchecked for guidelines on using these functions.
 * MOE:end_intracomment_strip
 *
 * @param {!goog.string.Const} justification A constant string explaining why
 *     this use of this method is safe. May include a security review ticket
 *     number.
 * @param {string} styleSheet The string to wrap as a SafeStyleSheet.
 * @return {!goog.html.SafeStyleSheet} The value of `styleSheet`, wrapped
 *     in a SafeStyleSheet object.
 */
goog.html.uncheckedconversions
    .safeStyleSheetFromStringKnownToSatisfyTypeContract = function(
    justification, styleSheet) {
  // unwrap() called inside an assert so that justification can be optimized
  // away in production code.
  goog.asserts.assertString(
      goog.string.Const.unwrap(justification), 'must provide justification');
  goog.asserts.assert(
      !goog.string.internal.isEmptyOrWhitespace(
          goog.string.Const.unwrap(justification)),
      'must provide non-empty justification');
  return goog.html.SafeStyleSheet
      .createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(styleSheet);
};


/**
 * Performs an "unchecked conversion" to SafeUrl from a plain string that is
 * known to satisfy the SafeUrl type contract.
 *
 * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
 * that the value of `url` satisfies the SafeUrl type contract in all
 * possible program states.
 *
 * MOE:begin_intracomment_strip
 * See http://go/safehtml-unchecked for guidelines on using these functions.
 * MOE:end_intracomment_strip
 *
 * @param {!goog.string.Const} justification A constant string explaining why
 *     this use of this method is safe. May include a security review ticket
 *     number.
 * @param {string} url The string to wrap as a SafeUrl.
 * @return {!goog.html.SafeUrl} The value of `url`, wrapped in a SafeUrl
 *     object.
 */
goog.html.uncheckedconversions.safeUrlFromStringKnownToSatisfyTypeContract =
    function(justification, url) {
  // unwrap() called inside an assert so that justification can be optimized
  // away in production code.
  goog.asserts.assertString(
      goog.string.Const.unwrap(justification), 'must provide justification');
  goog.asserts.assert(
      !goog.string.internal.isEmptyOrWhitespace(
          goog.string.Const.unwrap(justification)),
      'must provide non-empty justification');
  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
};


/**
 * Performs an "unchecked conversion" to TrustedResourceUrl from a plain string
 * that is known to satisfy the TrustedResourceUrl type contract.
 *
 * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
 * that the value of `url` satisfies the TrustedResourceUrl type contract
 * in all possible program states.
 *
 * MOE:begin_intracomment_strip
 * See http://go/safehtml-unchecked for guidelines on using these functions.
 * MOE:end_intracomment_strip
 *
 * @param {!goog.string.Const} justification A constant string explaining why
 *     this use of this method is safe. May include a security review ticket
 *     number.
 * @param {string} url The string to wrap as a TrustedResourceUrl.
 * @return {!goog.html.TrustedResourceUrl} The value of `url`, wrapped in
 *     a TrustedResourceUrl object.
 */
goog.html.uncheckedconversions
    .trustedResourceUrlFromStringKnownToSatisfyTypeContract = function(
    justification, url) {
  // unwrap() called inside an assert so that justification can be optimized
  // away in production code.
  goog.asserts.assertString(
      goog.string.Const.unwrap(justification), 'must provide justification');
  goog.asserts.assert(
      !goog.string.internal.isEmptyOrWhitespace(
          goog.string.Const.unwrap(justification)),
      'must provide non-empty justification');
  return goog.html.TrustedResourceUrl
      .createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(url);
};

//third_party/javascript/closure/dom/safe.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Type-safe wrappers for unsafe DOM APIs.
 *
 * This file provides type-safe wrappers for DOM APIs that can result in
 * cross-site scripting (XSS) vulnerabilities, if the API is supplied with
 * untrusted (attacker-controlled) input.  Instead of plain strings, the type
 * safe wrappers consume values of types from the goog.html package whose
 * contract promises that values are safe to use in the corresponding context.
 *
 * Hence, a program that exclusively uses the wrappers in this file (i.e., whose
 * only reference to security-sensitive raw DOM APIs are in this file) is
 * guaranteed to be free of XSS due to incorrect use of such DOM APIs (modulo
 * correctness of code that produces values of the respective goog.html types,
 * and absent code that violates type safety).
 *
 * For example, assigning to an element's .innerHTML property a string that is
 * derived (even partially) from untrusted input typically results in an XSS
 * vulnerability. The type-safe wrapper goog.dom.safe.setInnerHtml consumes a
 * value of type goog.html.SafeHtml, whose contract states that using its values
 * in a HTML context will not result in XSS. Hence a program that is free of
 * direct assignments to any element's innerHTML property (with the exception of
 * the assignment to .innerHTML in this file) is guaranteed to be free of XSS
 * due to assignment of untrusted strings to the innerHTML property.
 */

goog.provide('goog.dom.safe');
goog.provide('goog.dom.safe.InsertAdjacentHtmlPosition');

goog.require('goog.asserts');
goog.require('goog.dom.asserts');
goog.require('goog.functions');
goog.require('goog.html.SafeHtml');
goog.require('goog.html.SafeScript');
goog.require('goog.html.SafeStyle');
goog.require('goog.html.SafeUrl');
goog.require('goog.html.TrustedResourceUrl');
goog.require('goog.html.uncheckedconversions');
goog.require('goog.string.Const');
goog.require('goog.string.internal');


/** @enum {string} */
goog.dom.safe.InsertAdjacentHtmlPosition = {
  AFTERBEGIN: 'afterbegin',
  AFTEREND: 'afterend',
  BEFOREBEGIN: 'beforebegin',
  BEFOREEND: 'beforeend'
};


/**
 * Inserts known-safe HTML into a Node, at the specified position.
 * @param {!Node} node The node on which to call insertAdjacentHTML.
 * @param {!goog.dom.safe.InsertAdjacentHtmlPosition} position Position where
 *     to insert the HTML.
 * @param {!goog.html.SafeHtml} html The known-safe HTML to insert.
 */
goog.dom.safe.insertAdjacentHtml = function(node, position, html) {
  node.insertAdjacentHTML(position, goog.html.SafeHtml.unwrapTrustedHTML(html));
};


/**
 * Tags not allowed in goog.dom.safe.setInnerHtml.
 * @private @const {!Object<string, boolean>}
 */
goog.dom.safe.SET_INNER_HTML_DISALLOWED_TAGS_ = {
  'MATH': true,
  'SCRIPT': true,
  'STYLE': true,
  'SVG': true,
  'TEMPLATE': true
};


/**
 * Whether assigning to innerHTML results in a non-spec-compliant clean-up. Used
 * to define goog.dom.safe.unsafeSetInnerHtmlDoNotUseOrElse.
 *
 * <p>As mentioned in https://stackoverflow.com/questions/28741528, re-rendering
 * an element in IE by setting innerHTML causes IE to recursively disconnect all
 * parent/children connections that were in the previous contents of the
 * element. Unfortunately, this can unexpectedly result in confusing cases where
 * a function is run (typically asynchronously) on element that has since
 * disconnected from the DOM but assumes the presence of its children. A simple
 * workaround is to remove all children first. Testing on IE11 via
 * https://jsperf.com/innerhtml-vs-removechild/239, removeChild seems to be
 * ~10x faster than innerHTML='' for a large number of children (perhaps due
 * to the latter's recursive behavior), implying that this workaround would
 * not hurt performance and might actually improve it.
 * @return {boolean}
 * @private
 */
goog.dom.safe.isInnerHtmlCleanupRecursive_ =
    goog.functions.cacheReturnValue(function() {
      // `document` missing in some test frameworks.
      if (goog.DEBUG && typeof document === 'undefined') {
        return false;
      }
      // Create 3 nested <div>s without using innerHTML.
      // We're not chaining the appendChilds in one call,  as this breaks
      // in a DocumentFragment.
      var div = document.createElement('div');
      var childDiv = document.createElement('div');
      childDiv.appendChild(document.createElement('div'));
      div.appendChild(childDiv);
      // `firstChild` is null in Google Js Test.
      if (goog.DEBUG && !div.firstChild) {
        return false;
      }
      var innerChild = div.firstChild.firstChild;
      div.innerHTML =
          goog.html.SafeHtml.unwrapTrustedHTML(goog.html.SafeHtml.EMPTY);
      return !innerChild.parentElement;
    });


/**
 * Assigns HTML to an element's innerHTML property. Helper to use only here and
 * in soy.js.
 * @param {?Element} elem The element whose innerHTML is to be assigned to.
 * @param {!goog.html.SafeHtml} html
 */
goog.dom.safe.unsafeSetInnerHtmlDoNotUseOrElse = function(elem, html) {
  // See comment above goog.dom.safe.isInnerHtmlCleanupRecursive_.
  if (goog.dom.safe.isInnerHtmlCleanupRecursive_()) {
    while (elem.lastChild) {
      elem.removeChild(elem.lastChild);
    }
  }
  elem.innerHTML = goog.html.SafeHtml.unwrapTrustedHTML(html);
};


/**
 * Assigns known-safe HTML to an element's innerHTML property.
 * @param {!Element} elem The element whose innerHTML is to be assigned to.
 * @param {!goog.html.SafeHtml} html The known-safe HTML to assign.
 * @throws {Error} If called with one of these tags: math, script, style, svg,
 *     template.
 */
goog.dom.safe.setInnerHtml = function(elem, html) {
  if (goog.asserts.ENABLE_ASSERTS) {
    var tagName = elem.tagName.toUpperCase();
    if (goog.dom.safe.SET_INNER_HTML_DISALLOWED_TAGS_[tagName]) {
      throw new Error(
          'goog.dom.safe.setInnerHtml cannot be used to set content of ' +
          elem.tagName + '.');
    }
  }

  goog.dom.safe.unsafeSetInnerHtmlDoNotUseOrElse(elem, html);
};


/**
 * Assigns known-safe HTML to an element's outerHTML property.
 * @param {!Element} elem The element whose outerHTML is to be assigned to.
 * @param {!goog.html.SafeHtml} html The known-safe HTML to assign.
 */
goog.dom.safe.setOuterHtml = function(elem, html) {
  elem.outerHTML = goog.html.SafeHtml.unwrapTrustedHTML(html);
};


/**
 * Safely assigns a URL a form element's action property.
 *
 * If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to
 * form's action property.  If url is of type string however, it is first
 * sanitized using goog.html.SafeUrl.sanitize.
 *
 * Example usage:
 *   goog.dom.safe.setFormElementAction(formEl, url);
 * which is a safe alternative to
 *   formEl.action = url;
 * The latter can result in XSS vulnerabilities if url is a
 * user-/attacker-controlled value.
 *
 * @param {!Element} form The form element whose action property
 *     is to be assigned to.
 * @param {string|!goog.html.SafeUrl} url The URL to assign.
 * @see goog.html.SafeUrl#sanitize
 */
goog.dom.safe.setFormElementAction = function(form, url) {
  /** @type {!goog.html.SafeUrl} */
  var safeUrl;
  if (url instanceof goog.html.SafeUrl) {
    safeUrl = url;
  } else {
    safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url);
  }
  goog.dom.asserts.assertIsHTMLFormElement(form).action =
      goog.html.SafeUrl.unwrap(safeUrl);
};

/**
 * Safely assigns a URL to a button element's formaction property.
 *
 * If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to
 * button's formaction property.  If url is of type string however, it is first
 * sanitized using goog.html.SafeUrl.sanitize.
 *
 * Example usage:
 *   goog.dom.safe.setButtonFormAction(buttonEl, url);
 * which is a safe alternative to
 *   buttonEl.action = url;
 * The latter can result in XSS vulnerabilities if url is a
 * user-/attacker-controlled value.
 *
 * @param {!Element} button The button element whose action property
 *     is to be assigned to.
 * @param {string|!goog.html.SafeUrl} url The URL to assign.
 * @see goog.html.SafeUrl#sanitize
 */
goog.dom.safe.setButtonFormAction = function(button, url) {
  /** @type {!goog.html.SafeUrl} */
  var safeUrl;
  if (url instanceof goog.html.SafeUrl) {
    safeUrl = url;
  } else {
    safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url);
  }
  goog.dom.asserts.assertIsHTMLButtonElement(button).formAction =
      goog.html.SafeUrl.unwrap(safeUrl);
};
/**
 * Safely assigns a URL to an input element's formaction property.
 *
 * If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to
 * input's formaction property.  If url is of type string however, it is first
 * sanitized using goog.html.SafeUrl.sanitize.
 *
 * Example usage:
 *   goog.dom.safe.setInputFormAction(inputEl, url);
 * which is a safe alternative to
 *   inputEl.action = url;
 * The latter can result in XSS vulnerabilities if url is a
 * user-/attacker-controlled value.
 *
 * @param {!Element} input The input element whose action property
 *     is to be assigned to.
 * @param {string|!goog.html.SafeUrl} url The URL to assign.
 * @see goog.html.SafeUrl#sanitize
 */
goog.dom.safe.setInputFormAction = function(input, url) {
  /** @type {!goog.html.SafeUrl} */
  var safeUrl;
  if (url instanceof goog.html.SafeUrl) {
    safeUrl = url;
  } else {
    safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url);
  }
  goog.dom.asserts.assertIsHTMLInputElement(input).formAction =
      goog.html.SafeUrl.unwrap(safeUrl);
};

/**
 * Sets the given element's style property to the contents of the provided
 * SafeStyle object.
 * @param {!Element} elem
 * @param {!goog.html.SafeStyle} style
 */
goog.dom.safe.setStyle = function(elem, style) {
  elem.style.cssText = goog.html.SafeStyle.unwrap(style);
};


/**
 * Writes known-safe HTML to a document.
 * @param {!Document} doc The document to be written to.
 * @param {!goog.html.SafeHtml} html The known-safe HTML to assign.
 */
goog.dom.safe.documentWrite = function(doc, html) {
  doc.write(goog.html.SafeHtml.unwrapTrustedHTML(html));
};


/**
 * Safely assigns a URL to an anchor element's href property.
 *
 * If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to
 * anchor's href property.  If url is of type string however, it is first
 * sanitized using goog.html.SafeUrl.sanitize.
 *
 * Example usage:
 *   goog.dom.safe.setAnchorHref(anchorEl, url);
 * which is a safe alternative to
 *   anchorEl.href = url;
 * The latter can result in XSS vulnerabilities if url is a
 * user-/attacker-controlled value.
 *
 * @param {!HTMLAnchorElement} anchor The anchor element whose href property
 *     is to be assigned to.
 * @param {string|!goog.html.SafeUrl} url The URL to assign.
 * @see goog.html.SafeUrl#sanitize
 */
goog.dom.safe.setAnchorHref = function(anchor, url) {
  goog.dom.asserts.assertIsHTMLAnchorElement(anchor);
  /** @type {!goog.html.SafeUrl} */
  var safeUrl;
  if (url instanceof goog.html.SafeUrl) {
    safeUrl = url;
  } else {
    safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url);
  }
  anchor.href = goog.html.SafeUrl.unwrap(safeUrl);
};


/**
 * Safely assigns a URL to an image element's src property.
 *
 * If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to
 * image's src property.  If url is of type string however, it is first
 * sanitized using goog.html.SafeUrl.sanitize.
 *
 * @param {!HTMLImageElement} imageElement The image element whose src property
 *     is to be assigned to.
 * @param {string|!goog.html.SafeUrl} url The URL to assign.
 * @see goog.html.SafeUrl#sanitize
 */
goog.dom.safe.setImageSrc = function(imageElement, url) {
  goog.dom.asserts.assertIsHTMLImageElement(imageElement);
  /** @type {!goog.html.SafeUrl} */
  var safeUrl;
  if (url instanceof goog.html.SafeUrl) {
    safeUrl = url;
  } else {
    var allowDataUrl = /^data:image\//i.test(url);
    safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url, allowDataUrl);
  }
  imageElement.src = goog.html.SafeUrl.unwrap(safeUrl);
};

/**
 * Safely assigns a URL to a audio element's src property.
 *
 * If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to
 * audio's src property.  If url is of type string however, it is first
 * sanitized using goog.html.SafeUrl.sanitize.
 *
 * @param {!HTMLAudioElement} audioElement The audio element whose src property
 *     is to be assigned to.
 * @param {string|!goog.html.SafeUrl} url The URL to assign.
 * @see goog.html.SafeUrl#sanitize
 */
goog.dom.safe.setAudioSrc = function(audioElement, url) {
  goog.dom.asserts.assertIsHTMLAudioElement(audioElement);
  /** @type {!goog.html.SafeUrl} */
  var safeUrl;
  if (url instanceof goog.html.SafeUrl) {
    safeUrl = url;
  } else {
    var allowDataUrl = /^data:audio\//i.test(url);
    safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url, allowDataUrl);
  }
  audioElement.src = goog.html.SafeUrl.unwrap(safeUrl);
};

/**
 * Safely assigns a URL to a video element's src property.
 *
 * If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to
 * video's src property.  If url is of type string however, it is first
 * sanitized using goog.html.SafeUrl.sanitize.
 *
 * @param {!HTMLVideoElement} videoElement The video element whose src property
 *     is to be assigned to.
 * @param {string|!goog.html.SafeUrl} url The URL to assign.
 * @see goog.html.SafeUrl#sanitize
 */
goog.dom.safe.setVideoSrc = function(videoElement, url) {
  goog.dom.asserts.assertIsHTMLVideoElement(videoElement);
  /** @type {!goog.html.SafeUrl} */
  var safeUrl;
  if (url instanceof goog.html.SafeUrl) {
    safeUrl = url;
  } else {
    var allowDataUrl = /^data:video\//i.test(url);
    safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url, allowDataUrl);
  }
  videoElement.src = goog.html.SafeUrl.unwrap(safeUrl);
};

/**
 * Safely assigns a URL to an embed element's src property.
 *
 * Example usage:
 *   goog.dom.safe.setEmbedSrc(embedEl, url);
 * which is a safe alternative to
 *   embedEl.src = url;
 * The latter can result in loading untrusted code unless it is ensured that
 * the URL refers to a trustworthy resource.
 *
 * @param {!HTMLEmbedElement} embed The embed element whose src property
 *     is to be assigned to.
 * @param {!goog.html.TrustedResourceUrl} url The URL to assign.
 */
goog.dom.safe.setEmbedSrc = function(embed, url) {
  goog.dom.asserts.assertIsHTMLEmbedElement(embed);
  embed.src = goog.html.TrustedResourceUrl.unwrapTrustedScriptURL(url);
};


/**
 * Safely assigns a URL to a frame element's src property.
 *
 * Example usage:
 *   goog.dom.safe.setFrameSrc(frameEl, url);
 * which is a safe alternative to
 *   frameEl.src = url;
 * The latter can result in loading untrusted code unless it is ensured that
 * the URL refers to a trustworthy resource.
 *
 * @param {!HTMLFrameElement} frame The frame element whose src property
 *     is to be assigned to.
 * @param {!goog.html.TrustedResourceUrl} url The URL to assign.
 */
goog.dom.safe.setFrameSrc = function(frame, url) {
  goog.dom.asserts.assertIsHTMLFrameElement(frame);
  frame.src = goog.html.TrustedResourceUrl.unwrap(url);
};


/**
 * Safely assigns a URL to an iframe element's src property.
 *
 * Example usage:
 *   goog.dom.safe.setIframeSrc(iframeEl, url);
 * which is a safe alternative to
 *   iframeEl.src = url;
 * The latter can result in loading untrusted code unless it is ensured that
 * the URL refers to a trustworthy resource.
 *
 * @param {!HTMLIFrameElement} iframe The iframe element whose src property
 *     is to be assigned to.
 * @param {!goog.html.TrustedResourceUrl} url The URL to assign.
 */
goog.dom.safe.setIframeSrc = function(iframe, url) {
  goog.dom.asserts.assertIsHTMLIFrameElement(iframe);
  iframe.src = goog.html.TrustedResourceUrl.unwrap(url);
};


/**
 * Safely assigns HTML to an iframe element's srcdoc property.
 *
 * Example usage:
 *   goog.dom.safe.setIframeSrcdoc(iframeEl, safeHtml);
 * which is a safe alternative to
 *   iframeEl.srcdoc = html;
 * The latter can result in loading untrusted code.
 *
 * @param {!HTMLIFrameElement} iframe The iframe element whose srcdoc property
 *     is to be assigned to.
 * @param {!goog.html.SafeHtml} html The HTML to assign.
 */
goog.dom.safe.setIframeSrcdoc = function(iframe, html) {
  goog.dom.asserts.assertIsHTMLIFrameElement(iframe);
  iframe.srcdoc = goog.html.SafeHtml.unwrapTrustedHTML(html);
};


/**
 * Safely sets a link element's href and rel properties. Whether or not
 * the URL assigned to href has to be a goog.html.TrustedResourceUrl
 * depends on the value of the rel property. If rel contains "stylesheet"
 * then a TrustedResourceUrl is required.
 *
 * Example usage:
 *   goog.dom.safe.setLinkHrefAndRel(linkEl, url, 'stylesheet');
 * which is a safe alternative to
 *   linkEl.rel = 'stylesheet';
 *   linkEl.href = url;
 * The latter can result in loading untrusted code unless it is ensured that
 * the URL refers to a trustworthy resource.
 *
 * @param {!HTMLLinkElement} link The link element whose href property
 *     is to be assigned to.
 * @param {string|!goog.html.SafeUrl|!goog.html.TrustedResourceUrl} url The URL
 *     to assign to the href property. Must be a TrustedResourceUrl if the
 *     value assigned to rel contains "stylesheet". A string value is
 *     sanitized with goog.html.SafeUrl.sanitize.
 * @param {string} rel The value to assign to the rel property.
 * @throws {Error} if rel contains "stylesheet" and url is not a
 *     TrustedResourceUrl
 * @see goog.html.SafeUrl#sanitize
 */
goog.dom.safe.setLinkHrefAndRel = function(link, url, rel) {
  goog.dom.asserts.assertIsHTMLLinkElement(link);
  link.rel = rel;
  if (goog.string.internal.caseInsensitiveContains(rel, 'stylesheet')) {
    goog.asserts.assert(
        url instanceof goog.html.TrustedResourceUrl,
        'URL must be TrustedResourceUrl because "rel" contains "stylesheet"');
    link.href = goog.html.TrustedResourceUrl.unwrap(url);
  } else if (url instanceof goog.html.TrustedResourceUrl) {
    link.href = goog.html.TrustedResourceUrl.unwrap(url);
  } else if (url instanceof goog.html.SafeUrl) {
    link.href = goog.html.SafeUrl.unwrap(url);
  } else {  // string
    // SafeUrl.sanitize must return legitimate SafeUrl when passed a string.
    link.href = goog.html.SafeUrl.unwrap(
        goog.html.SafeUrl.sanitizeAssertUnchanged(url));
  }
};


/**
 * Safely assigns a URL to an object element's data property.
 *
 * Example usage:
 *   goog.dom.safe.setObjectData(objectEl, url);
 * which is a safe alternative to
 *   objectEl.data = url;
 * The latter can result in loading untrusted code unless setit is ensured that
 * the URL refers to a trustworthy resource.
 *
 * @param {!HTMLObjectElement} object The object element whose data property
 *     is to be assigned to.
 * @param {!goog.html.TrustedResourceUrl} url The URL to assign.
 */
goog.dom.safe.setObjectData = function(object, url) {
  goog.dom.asserts.assertIsHTMLObjectElement(object);
  object.data = goog.html.TrustedResourceUrl.unwrapTrustedScriptURL(url);
};


/**
 * Safely assigns a URL to a script element's src property.
 *
 * Example usage:
 *   goog.dom.safe.setScriptSrc(scriptEl, url);
 * which is a safe alternative to
 *   scriptEl.src = url;
 * The latter can result in loading untrusted code unless it is ensured that
 * the URL refers to a trustworthy resource.
 *
 * @param {!HTMLScriptElement} script The script element whose src property
 *     is to be assigned to.
 * @param {!goog.html.TrustedResourceUrl} url The URL to assign.
 */
goog.dom.safe.setScriptSrc = function(script, url) {
  goog.dom.asserts.assertIsHTMLScriptElement(script);
  script.src = goog.html.TrustedResourceUrl.unwrapTrustedScriptURL(url);
  goog.dom.safe.setNonceForScriptElement_(script);
};


/**
 * Safely assigns a value to a script element's content.
 *
 * Example usage:
 *   goog.dom.safe.setScriptContent(scriptEl, content);
 * which is a safe alternative to
 *   scriptEl.text = content;
 * The latter can result in executing untrusted code unless it is ensured that
 * the code is loaded from a trustworthy resource.
 *
 * @param {!HTMLScriptElement} script The script element whose content is being
 *     set.
 * @param {!goog.html.SafeScript} content The content to assign.
 */
goog.dom.safe.setScriptContent = function(script, content) {
  goog.dom.asserts.assertIsHTMLScriptElement(script);
  script.text = goog.html.SafeScript.unwrapTrustedScript(content);
  goog.dom.safe.setNonceForScriptElement_(script);
};


/**
 * Set nonce-based CSPs to dynamically created scripts.
 * @param {!HTMLScriptElement} script The script element whose nonce value
 *     is to be calculated
 * @private
 */
goog.dom.safe.setNonceForScriptElement_ = function(script) {
  var win = script.ownerDocument && script.ownerDocument.defaultView;
  var nonce = goog.getScriptNonce(win);
  if (nonce) {
    script.setAttribute('nonce', nonce);
  }
};


/**
 * Safely assigns a URL to a Location object's href property.
 *
 * If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to
 * loc's href property.  If url is of type string however, it is first sanitized
 * using goog.html.SafeUrl.sanitize.
 *
 * Example usage:
 *   goog.dom.safe.setLocationHref(document.location, redirectUrl);
 * which is a safe alternative to
 *   document.location.href = redirectUrl;
 * The latter can result in XSS vulnerabilities if redirectUrl is a
 * user-/attacker-controlled value.
 *
 * @param {!Location} loc The Location object whose href property is to be
 *     assigned to.
 * @param {string|!goog.html.SafeUrl} url The URL to assign.
 * @see goog.html.SafeUrl#sanitize
 */
goog.dom.safe.setLocationHref = function(loc, url) {
  goog.dom.asserts.assertIsLocation(loc);
  /** @type {!goog.html.SafeUrl} */
  var safeUrl;
  if (url instanceof goog.html.SafeUrl) {
    safeUrl = url;
  } else {
    safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url);
  }
  loc.href = goog.html.SafeUrl.unwrap(safeUrl);
};

/**
 * Safely assigns the URL of a Location object.
 *
 * If url is of type goog.html.SafeUrl, its value is unwrapped and
 * passed to Location#assign. If url is of type string however, it is
 * first sanitized using goog.html.SafeUrl.sanitize.
 *
 * Example usage:
 *   goog.dom.safe.assignLocation(document.location, newUrl);
 * which is a safe alternative to
 *   document.location.assign(newUrl);
 * The latter can result in XSS vulnerabilities if newUrl is a
 * user-/attacker-controlled value.
 *
 * This has the same behaviour as setLocationHref, however some test
 * mock Location.assign instead of a property assignment.
 *
 * @param {!Location} loc The Location object which is to be assigned.
 * @param {string|!goog.html.SafeUrl} url The URL to assign.
 * @see goog.html.SafeUrl#sanitize
 */
goog.dom.safe.assignLocation = function(loc, url) {
  goog.dom.asserts.assertIsLocation(loc);
  /** @type {!goog.html.SafeUrl} */
  var safeUrl;
  if (url instanceof goog.html.SafeUrl) {
    safeUrl = url;
  } else {
    safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url);
  }
  loc.assign(goog.html.SafeUrl.unwrap(safeUrl));
};


/**
 * Safely replaces the URL of a Location object.
 *
 * If url is of type goog.html.SafeUrl, its value is unwrapped and
 * passed to Location#replace. If url is of type string however, it is
 * first sanitized using goog.html.SafeUrl.sanitize.
 *
 * Example usage:
 *   goog.dom.safe.replaceLocation(document.location, newUrl);
 * which is a safe alternative to
 *   document.location.replace(newUrl);
 * The latter can result in XSS vulnerabilities if newUrl is a
 * user-/attacker-controlled value.
 *
 * @param {!Location} loc The Location object which is to be replaced.
 * @param {string|!goog.html.SafeUrl} url The URL to assign.
 * @see goog.html.SafeUrl#sanitize
 */
goog.dom.safe.replaceLocation = function(loc, url) {
  /** @type {!goog.html.SafeUrl} */
  var safeUrl;
  if (url instanceof goog.html.SafeUrl) {
    safeUrl = url;
  } else {
    safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url);
  }
  loc.replace(goog.html.SafeUrl.unwrap(safeUrl));
};


/**
 * Safely opens a URL in a new window (via window.open).
 *
 * If url is of type goog.html.SafeUrl, its value is unwrapped and passed in to
 * window.open.  If url is of type string however, it is first sanitized
 * using goog.html.SafeUrl.sanitize.
 *
 * Note that this function does not prevent leakages via the referer that is
 * sent by window.open. It is advised to only use this to open 1st party URLs.
 *
 * Example usage:
 *   goog.dom.safe.openInWindow(url);
 * which is a safe alternative to
 *   window.open(url);
 * The latter can result in XSS vulnerabilities if url is a
 * user-/attacker-controlled value.
 *
 * @param {string|!goog.html.SafeUrl} url The URL to open.
 * @param {Window=} opt_openerWin Window of which to call the .open() method.
 *     Defaults to the global window.
 * @param {!goog.string.Const|string=} opt_name Name of the window to open in.
 *     Can be _top, etc as allowed by window.open(). This accepts string for
 *     legacy reasons. Pass goog.string.Const if possible.
 * @param {string=} opt_specs Comma-separated list of specifications, same as
 *     in window.open().
 * @param {boolean=} opt_replace Whether to replace the current entry in browser
 *     history, same as in window.open().
 * @return {Window} Window the url was opened in.
 */
goog.dom.safe.openInWindow = function(
    url, opt_openerWin, opt_name, opt_specs, opt_replace) {
  /** @type {!goog.html.SafeUrl} */
  var safeUrl;
  if (url instanceof goog.html.SafeUrl) {
    safeUrl = url;
  } else {
    safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url);
  }
  var win = opt_openerWin || goog.global;
  // If opt_name is undefined, simply passing that in to open() causes IE to
  // reuse the current window instead of opening a new one. Thus we pass '' in
  // instead, which according to spec opens a new window. See
  // https://html.spec.whatwg.org/multipage/browsers.html#dom-open .
  var name = opt_name instanceof goog.string.Const ?
      goog.string.Const.unwrap(opt_name) :
      opt_name || '';
  return win.open(
      goog.html.SafeUrl.unwrap(safeUrl), name, opt_specs, opt_replace);
};


/**
 * Parses the HTML as 'text/html'.
 * @param {!DOMParser} parser
 * @param {!goog.html.SafeHtml} html The HTML to be parsed.
 * @return {?Document}
 */
goog.dom.safe.parseFromStringHtml = function(parser, html) {
  return goog.dom.safe.parseFromString(parser, html, 'text/html');
};


/**
 * Parses the string.
 * @param {!DOMParser} parser
 * @param {!goog.html.SafeHtml} content Note: We don't have a special type for
 *     XML od SVG supported by this function so we use SafeHtml.
 * @param {string} type
 * @return {?Document}
 */
goog.dom.safe.parseFromString = function(parser, content, type) {
  return parser.parseFromString(
      goog.html.SafeHtml.unwrapTrustedHTML(content), type);
};


/**
 * Safely creates an HTMLImageElement from a Blob.
 *
 * Example usage:
 *     goog.dom.safe.createImageFromBlob(blob);
 * which is a safe alternative to
 *     image.src = createObjectUrl(blob)
 * The latter can result in executing malicious same-origin scripts from a bad
 * Blob.
 * @param {!Blob} blob The blob to create the image from.
 * @return {!HTMLImageElement} The image element created from the blob.
 * @throws {!Error} If called with a Blob with a MIME type other than image/.*.
 */
goog.dom.safe.createImageFromBlob = function(blob) {
  // Any image/* MIME type is accepted as safe.
  if (!/^image\/.*/g.test(blob.type)) {
    throw new Error(
        'goog.dom.safe.createImageFromBlob only accepts MIME type image/.*.');
  }
  var objectUrl = goog.global.URL.createObjectURL(blob);
  var image = new goog.global.Image();
  image.onload = function() {
    goog.global.URL.revokeObjectURL(objectUrl);
  };
  goog.dom.safe.setImageSrc(
      image,
      goog.html.uncheckedconversions
          .safeUrlFromStringKnownToSatisfyTypeContract(
              goog.string.Const.from('Image blob URL.'), objectUrl));
  return image;
};

//third_party/javascript/closure/string/string.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Utilities for string manipulation.
 */


/**
 * Namespace for string utilities
 */
goog.provide('goog.string');
goog.provide('goog.string.Unicode');

goog.require('goog.dom.safe');
goog.require('goog.html.uncheckedconversions');
goog.require('goog.string.Const');
goog.require('goog.string.internal');


/**
 * @define {boolean} Enables HTML escaping of lowercase letter "e" which helps
 * with detection of double-escaping as this letter is frequently used.
 */
goog.string.DETECT_DOUBLE_ESCAPING =
    goog.define('goog.string.DETECT_DOUBLE_ESCAPING', false);


/**
 * @define {boolean} Whether to force non-dom html unescaping.
 */
goog.string.FORCE_NON_DOM_HTML_UNESCAPING =
    goog.define('goog.string.FORCE_NON_DOM_HTML_UNESCAPING', false);


/**
 * Common Unicode string characters.
 * @enum {string}
 */
goog.string.Unicode = {
  NBSP: '\xa0'
};


/**
 * Fast prefix-checker.
 * @param {string} str The string to check.
 * @param {string} prefix A string to look for at the start of `str`.
 * @return {boolean} True if `str` begins with `prefix`.
 */
goog.string.startsWith = goog.string.internal.startsWith;


/**
 * Fast suffix-checker.
 * @param {string} str The string to check.
 * @param {string} suffix A string to look for at the end of `str`.
 * @return {boolean} True if `str` ends with `suffix`.
 */
goog.string.endsWith = goog.string.internal.endsWith;


/**
 * Case-insensitive prefix-checker.
 * @param {string} str The string to check.
 * @param {string} prefix  A string to look for at the end of `str`.
 * @return {boolean} True if `str` begins with `prefix` (ignoring
 *     case).
 */
goog.string.caseInsensitiveStartsWith =
    goog.string.internal.caseInsensitiveStartsWith;


/**
 * Case-insensitive suffix-checker.
 * @param {string} str The string to check.
 * @param {string} suffix A string to look for at the end of `str`.
 * @return {boolean} True if `str` ends with `suffix` (ignoring
 *     case).
 */
goog.string.caseInsensitiveEndsWith =
    goog.string.internal.caseInsensitiveEndsWith;


/**
 * Case-insensitive equality checker.
 * @param {string} str1 First string to check.
 * @param {string} str2 Second string to check.
 * @return {boolean} True if `str1` and `str2` are the same string,
 *     ignoring case.
 */
goog.string.caseInsensitiveEquals = goog.string.internal.caseInsensitiveEquals;


/**
 * Does simple python-style string substitution.
 * subs("foo%s hot%s", "bar", "dog") becomes "foobar hotdog".
 * @param {string} str The string containing the pattern.
 * @param {...*} var_args The items to substitute into the pattern.
 * @return {string} A copy of `str` in which each occurrence of
 *     {@code %s} has been replaced an argument from `var_args`.
 */
goog.string.subs = function(str, var_args) {
  var splitParts = str.split('%s');
  var returnString = '';

  var subsArguments = Array.prototype.slice.call(arguments, 1);
  while (subsArguments.length &&
         // Replace up to the last split part. We are inserting in the
         // positions between split parts.
         splitParts.length > 1) {
    returnString += splitParts.shift() + subsArguments.shift();
  }

  return returnString + splitParts.join('%s');  // Join unused '%s'
};


/**
 * Converts multiple whitespace chars (spaces, non-breaking-spaces, new lines
 * and tabs) to a single space, and strips leading and trailing whitespace.
 * @param {string} str Input string.
 * @return {string} A copy of `str` with collapsed whitespace.
 */
goog.string.collapseWhitespace = function(str) {
  // Since IE doesn't include non-breaking-space (0xa0) in their \s character
  // class (as required by section 7.2 of the ECMAScript spec), we explicitly
  // include it in the regexp to enforce consistent cross-browser behavior.
  return str.replace(/[\s\xa0]+/g, ' ').replace(/^\s+|\s+$/g, '');
};


/**
 * Checks if a string is empty or contains only whitespaces.
 * @param {string} str The string to check.
 * @return {boolean} Whether `str` is empty or whitespace only.
 */
goog.string.isEmptyOrWhitespace = goog.string.internal.isEmptyOrWhitespace;


/**
 * Checks if a string is empty.
 * @param {string} str The string to check.
 * @return {boolean} Whether `str` is empty.
 */
goog.string.isEmptyString = function(str) {
  return str.length == 0;
};


/**
 * Checks if a string is empty or contains only whitespaces.
 *
 * @param {string} str The string to check.
 * @return {boolean} Whether `str` is empty or whitespace only.
 * @deprecated Use goog.string.isEmptyOrWhitespace instead.
 */
goog.string.isEmpty = goog.string.isEmptyOrWhitespace;


/**
 * Checks if a string is null, undefined, empty or contains only whitespaces.
 * @param {*} str The string to check.
 * @return {boolean} Whether `str` is null, undefined, empty, or
 *     whitespace only.
 * @deprecated Use goog.string.isEmptyOrWhitespace(goog.string.makeSafe(str))
 *     instead.
 */
goog.string.isEmptyOrWhitespaceSafe = function(str) {
  return goog.string.isEmptyOrWhitespace(goog.string.makeSafe(str));
};


/**
 * Checks if a string is null, undefined, empty or contains only whitespaces.
 *
 * @param {*} str The string to check.
 * @return {boolean} Whether `str` is null, undefined, empty, or
 *     whitespace only.
 * @deprecated Use goog.string.isEmptyOrWhitespace instead.
 */
goog.string.isEmptySafe = goog.string.isEmptyOrWhitespaceSafe;


/**
 * Checks if a string is all breaking whitespace.
 * @param {string} str The string to check.
 * @return {boolean} Whether the string is all breaking whitespace.
 */
goog.string.isBreakingWhitespace = function(str) {
  return !/[^\t\n\r ]/.test(str);
};


/**
 * Checks if a string contains all letters.
 * @param {string} str string to check.
 * @return {boolean} True if `str` consists entirely of letters.
 */
goog.string.isAlpha = function(str) {
  return !/[^a-zA-Z]/.test(str);
};


/**
 * Checks if a string contains only numbers.
 * @param {*} str string to check. If not a string, it will be
 *     casted to one.
 * @return {boolean} True if `str` is numeric.
 */
goog.string.isNumeric = function(str) {
  return !/[^0-9]/.test(str);
};


/**
 * Checks if a string contains only numbers or letters.
 * @param {string} str string to check.
 * @return {boolean} True if `str` is alphanumeric.
 */
goog.string.isAlphaNumeric = function(str) {
  return !/[^a-zA-Z0-9]/.test(str);
};


/**
 * Checks if a character is a space character.
 * @param {string} ch Character to check.
 * @return {boolean} True if `ch` is a space.
 */
goog.string.isSpace = function(ch) {
  return ch == ' ';
};


/**
 * Checks if a character is a valid unicode character.
 * @param {string} ch Character to check.
 * @return {boolean} True if `ch` is a valid unicode character.
 */
goog.string.isUnicodeChar = function(ch) {
  return ch.length == 1 && ch >= ' ' && ch <= '~' ||
      ch >= '\u0080' && ch <= '\uFFFD';
};


/**
 * Takes a string and replaces newlines with a space. Multiple lines are
 * replaced with a single space.
 * @param {string} str The string from which to strip newlines.
 * @return {string} A copy of `str` stripped of newlines.
 */
goog.string.stripNewlines = function(str) {
  return str.replace(/(\r\n|\r|\n)+/g, ' ');
};


/**
 * Replaces Windows and Mac new lines with unix style: \r or \r\n with \n.
 * @param {string} str The string to in which to canonicalize newlines.
 * @return {string} `str` A copy of {@code} with canonicalized newlines.
 */
goog.string.canonicalizeNewlines = function(str) {
  return str.replace(/(\r\n|\r|\n)/g, '\n');
};


/**
 * Normalizes whitespace in a string, replacing all whitespace chars with
 * a space.
 * @param {string} str The string in which to normalize whitespace.
 * @return {string} A copy of `str` with all whitespace normalized.
 */
goog.string.normalizeWhitespace = function(str) {
  return str.replace(/\xa0|\s/g, ' ');
};


/**
 * Normalizes spaces in a string, replacing all consecutive spaces and tabs
 * with a single space. Replaces non-breaking space with a space.
 * @param {string} str The string in which to normalize spaces.
 * @return {string} A copy of `str` with all consecutive spaces and tabs
 *    replaced with a single space.
 */
goog.string.normalizeSpaces = function(str) {
  return str.replace(/\xa0|[ \t]+/g, ' ');
};


/**
 * Removes the breaking spaces from the left and right of the string and
 * collapses the sequences of breaking spaces in the middle into single spaces.
 * The original and the result strings render the same way in HTML.
 * @param {string} str A string in which to collapse spaces.
 * @return {string} Copy of the string with normalized breaking spaces.
 */
goog.string.collapseBreakingSpaces = function(str) {
  return str.replace(/[\t\r\n ]+/g, ' ')
      .replace(/^[\t\r\n ]+|[\t\r\n ]+$/g, '');
};


/**
 * Trims white spaces to the left and right of a string.
 * @param {string} str The string to trim.
 * @return {string} A trimmed copy of `str`.
 */
goog.string.trim = goog.string.internal.trim;


/**
 * Trims whitespaces at the left end of a string.
 * @param {string} str The string to left trim.
 * @return {string} A trimmed copy of `str`.
 */
goog.string.trimLeft = function(str) {
  // Since IE doesn't include non-breaking-space (0xa0) in their \s character
  // class (as required by section 7.2 of the ECMAScript spec), we explicitly
  // include it in the regexp to enforce consistent cross-browser behavior.
  return str.replace(/^[\s\xa0]+/, '');
};


/**
 * Trims whitespaces at the right end of a string.
 * @param {string} str The string to right trim.
 * @return {string} A trimmed copy of `str`.
 */
goog.string.trimRight = function(str) {
  // Since IE doesn't include non-breaking-space (0xa0) in their \s character
  // class (as required by section 7.2 of the ECMAScript spec), we explicitly
  // include it in the regexp to enforce consistent cross-browser behavior.
  return str.replace(/[\s\xa0]+$/, '');
};


/**
 * A string comparator that ignores case.
 * -1 = str1 less than str2
 *  0 = str1 equals str2
 *  1 = str1 greater than str2
 *
 * @param {string} str1 The string to compare.
 * @param {string} str2 The string to compare `str1` to.
 * @return {number} The comparator result, as described above.
 */
goog.string.caseInsensitiveCompare =
    goog.string.internal.caseInsensitiveCompare;


/**
 * Compares two strings interpreting their numeric substrings as numbers.
 *
 * @param {string} str1 First string.
 * @param {string} str2 Second string.
 * @param {!RegExp} tokenizerRegExp Splits a string into substrings of
 *     non-negative integers, non-numeric characters and optionally fractional
 *     numbers starting with a decimal point.
 * @return {number} Negative if str1 < str2, 0 is str1 == str2, positive if
 *     str1 > str2.
 * @private
 */
goog.string.numberAwareCompare_ = function(str1, str2, tokenizerRegExp) {
  if (str1 == str2) {
    return 0;
  }
  if (!str1) {
    return -1;
  }
  if (!str2) {
    return 1;
  }

  // Using match to split the entire string ahead of time turns out to be faster
  // for most inputs than using RegExp.exec or iterating over each character.
  var tokens1 = str1.toLowerCase().match(tokenizerRegExp);
  var tokens2 = str2.toLowerCase().match(tokenizerRegExp);

  var count = Math.min(tokens1.length, tokens2.length);

  for (var i = 0; i < count; i++) {
    var a = tokens1[i];
    var b = tokens2[i];

    // Compare pairs of tokens, returning if one token sorts before the other.
    if (a != b) {
      // Only if both tokens are integers is a special comparison required.
      // Decimal numbers are sorted as strings (e.g., '.09' < '.1').
      var num1 = parseInt(a, 10);
      if (!isNaN(num1)) {
        var num2 = parseInt(b, 10);
        if (!isNaN(num2) && num1 - num2) {
          return num1 - num2;
        }
      }
      return a < b ? -1 : 1;
    }
  }

  // If one string is a substring of the other, the shorter string sorts first.
  if (tokens1.length != tokens2.length) {
    return tokens1.length - tokens2.length;
  }

  // The two strings must be equivalent except for case (perfect equality is
  // tested at the head of the function.) Revert to default ASCII string
  // comparison to stabilize the sort.
  return str1 < str2 ? -1 : 1;
};


/**
 * String comparison function that handles non-negative integer numbers in a
 * way humans might expect. Using this function, the string 'File 2.jpg' sorts
 * before 'File 10.jpg', and 'Version 1.9' before 'Version 1.10'. The comparison
 * is mostly case-insensitive, though strings that are identical except for case
 * are sorted with the upper-case strings before lower-case.
 *
 * This comparison function is up to 50x slower than either the default or the
 * case-insensitive compare. It should not be used in time-critical code, but
 * should be fast enough to sort several hundred short strings (like filenames)
 * with a reasonable delay.
 *
 * @param {string} str1 The string to compare in a numerically sensitive way.
 * @param {string} str2 The string to compare `str1` to.
 * @return {number} less than 0 if str1 < str2, 0 if str1 == str2, greater than
 *     0 if str1 > str2.
 */
goog.string.intAwareCompare = function(str1, str2) {
  return goog.string.numberAwareCompare_(str1, str2, /\d+|\D+/g);
};


/**
 * String comparison function that handles non-negative integer and fractional
 * numbers in a way humans might expect. Using this function, the string
 * 'File 2.jpg' sorts before 'File 10.jpg', and '3.14' before '3.2'. Equivalent
 * to {@link goog.string.intAwareCompare} apart from the way how it interprets
 * dots.
 *
 * @param {string} str1 The string to compare in a numerically sensitive way.
 * @param {string} str2 The string to compare `str1` to.
 * @return {number} less than 0 if str1 < str2, 0 if str1 == str2, greater than
 *     0 if str1 > str2.
 */
goog.string.floatAwareCompare = function(str1, str2) {
  return goog.string.numberAwareCompare_(str1, str2, /\d+|\.\d+|\D+/g);
};


/**
 * Alias for {@link goog.string.floatAwareCompare}.
 *
 * @param {string} str1
 * @param {string} str2
 * @return {number}
 */
goog.string.numerateCompare = goog.string.floatAwareCompare;


/**
 * URL-encodes a string
 * @param {*} str The string to url-encode.
 * @return {string} An encoded copy of `str` that is safe for urls.
 *     Note that '#', ':', and other characters used to delimit portions
 *     of URLs *will* be encoded.
 */
goog.string.urlEncode = function(str) {
  return encodeURIComponent(String(str));
};


/**
 * URL-decodes the string. We need to specially handle '+'s because
 * the javascript library doesn't convert them to spaces.
 * @param {string} str The string to url decode.
 * @return {string} The decoded `str`.
 */
goog.string.urlDecode = function(str) {
  return decodeURIComponent(str.replace(/\+/g, ' '));
};


/**
 * Converts \n to <br>s or <br />s.
 * @param {string} str The string in which to convert newlines.
 * @param {boolean=} opt_xml Whether to use XML compatible tags.
 * @return {string} A copy of `str` with converted newlines.
 */
goog.string.newLineToBr = goog.string.internal.newLineToBr;


/**
 * Escapes double quote '"' and single quote '\'' characters in addition to
 * '&', '<', and '>' so that a string can be included in an HTML tag attribute
 * value within double or single quotes.
 *
 * It should be noted that > doesn't need to be escaped for the HTML or XML to
 * be valid, but it has been decided to escape it for consistency with other
 * implementations.
 *
 * With goog.string.DETECT_DOUBLE_ESCAPING, this function escapes also the
 * lowercase letter "e".
 *
 * NOTE(pupius):
 * HtmlEscape is often called during the generation of large blocks of HTML.
 * Using statics for the regular expressions and strings is an optimization
 * that can more than half the amount of time IE spends in this function for
 * large apps, since strings and regexes both contribute to GC allocations.
 *
 * Testing for the presence of a character before escaping increases the number
 * of function calls, but actually provides a speed increase for the average
 * case -- since the average case often doesn't require the escaping of all 4
 * characters and indexOf() is much cheaper than replace().
 * The worst case does suffer slightly from the additional calls, therefore the
 * opt_isLikelyToContainHtmlChars option has been included for situations
 * where all 4 HTML entities are very likely to be present and need escaping.
 *
 * Some benchmarks (times tended to fluctuate +-0.05ms):
 *                                     FireFox                     IE6
 * (no chars / average (mix of cases) / all 4 chars)
 * no checks                     0.13 / 0.22 / 0.22         0.23 / 0.53 / 0.80
 * indexOf                       0.08 / 0.17 / 0.26         0.22 / 0.54 / 0.84
 * indexOf + re test             0.07 / 0.17 / 0.28         0.19 / 0.50 / 0.85
 *
 * An additional advantage of checking if replace actually needs to be called
 * is a reduction in the number of object allocations, so as the size of the
 * application grows the difference between the various methods would increase.
 *
 * @param {string} str string to be escaped.
 * @param {boolean=} opt_isLikelyToContainHtmlChars Don't perform a check to see
 *     if the character needs replacing - use this option if you expect each of
 *     the characters to appear often. Leave false if you expect few html
 *     characters to occur in your strings, such as if you are escaping HTML.
 * @return {string} An escaped copy of `str`.
 */
goog.string.htmlEscape = function(str, opt_isLikelyToContainHtmlChars) {
  str = goog.string.internal.htmlEscape(str, opt_isLikelyToContainHtmlChars);
  if (goog.string.DETECT_DOUBLE_ESCAPING) {
    str = str.replace(goog.string.E_RE_, '&#101;');
  }
  return str;
};


/**
 * Regular expression that matches a lowercase letter "e", for use in escaping.
 * @const {!RegExp}
 * @private
 */
goog.string.E_RE_ = /e/g;


/**
 * Unescapes an HTML string.
 *
 * @param {string} str The string to unescape.
 * @return {string} An unescaped copy of `str`.
 */
goog.string.unescapeEntities = function(str) {
  if (goog.string.contains(str, '&')) {
    // We are careful not to use a DOM if we do not have one or we explicitly
    // requested non-DOM html unescaping.
    if (!goog.string.FORCE_NON_DOM_HTML_UNESCAPING &&
        'document' in goog.global) {
      return goog.string.unescapeEntitiesUsingDom_(str);
    } else {
      // Fall back on pure XML entities
      return goog.string.unescapePureXmlEntities_(str);
    }
  }
  return str;
};


/**
 * Unescapes a HTML string using the provided document.
 *
 * @param {string} str The string to unescape.
 * @param {!Document} document A document to use in escaping the string.
 * @return {string} An unescaped copy of `str`.
 */
goog.string.unescapeEntitiesWithDocument = function(str, document) {
  if (goog.string.contains(str, '&')) {
    return goog.string.unescapeEntitiesUsingDom_(str, document);
  }
  return str;
};


/**
 * Unescapes an HTML string using a DOM to resolve non-XML, non-numeric
 * entities. This function is XSS-safe and whitespace-preserving.
 * @private
 * @param {string} str The string to unescape.
 * @param {Document=} opt_document An optional document to use for creating
 *     elements. If this is not specified then the default window.document
 *     will be used.
 * @return {string} The unescaped `str` string.
 */
goog.string.unescapeEntitiesUsingDom_ = function(str, opt_document) {
  /** @type {!Object<string, string>} */
  var seen = {'&amp;': '&', '&lt;': '<', '&gt;': '>', '&quot;': '"'};
  /** @type {!Element} */
  var div;
  if (opt_document) {
    div = opt_document.createElement('div');
  } else {
    div = goog.global.document.createElement('div');
  }
  // Match as many valid entity characters as possible. If the actual entity
  // happens to be shorter, it will still work as innerHTML will return the
  // trailing characters unchanged. Since the entity characters do not include
  // open angle bracket, there is no chance of XSS from the innerHTML use.
  // Since no whitespace is passed to innerHTML, whitespace is preserved.
  return str.replace(goog.string.HTML_ENTITY_PATTERN_, function(s, entity) {
    // Check for cached entity.
    var value = seen[s];
    if (value) {
      return value;
    }
    // Check for numeric entity.
    if (entity.charAt(0) == '#') {
      // Prefix with 0 so that hex entities (e.g. &#x10) parse as hex numbers.
      var n = Number('0' + entity.substr(1));
      if (!isNaN(n)) {
        value = String.fromCharCode(n);
      }
    }
    // Fall back to innerHTML otherwise.
    if (!value) {
      // Append a non-entity character to avoid a bug in Webkit that parses
      // an invalid entity at the end of innerHTML text as the empty string.
      goog.dom.safe.setInnerHtml(
          div,
          goog.html.uncheckedconversions
              .safeHtmlFromStringKnownToSatisfyTypeContract(
                  goog.string.Const.from('Single HTML entity.'), s + ' '));
      // Then remove the trailing character from the result.
      value = div.firstChild.nodeValue.slice(0, -1);
    }
    // Cache and return.
    return seen[s] = value;
  });
};


/**
 * Unescapes XML entities.
 * @private
 * @param {string} str The string to unescape.
 * @return {string} An unescaped copy of `str`.
 */
goog.string.unescapePureXmlEntities_ = function(str) {
  return str.replace(/&([^;]+);/g, function(s, entity) {
    switch (entity) {
      case 'amp':
        return '&';
      case 'lt':
        return '<';
      case 'gt':
        return '>';
      case 'quot':
        return '"';
      default:
        if (entity.charAt(0) == '#') {
          // Prefix with 0 so that hex entities (e.g. &#x10) parse as hex.
          var n = Number('0' + entity.substr(1));
          if (!isNaN(n)) {
            return String.fromCharCode(n);
          }
        }
        // For invalid entities we just return the entity
        return s;
    }
  });
};


/**
 * Regular expression that matches an HTML entity.
 * See also HTML5: Tokenization / Tokenizing character references.
 * @private
 * @type {!RegExp}
 */
goog.string.HTML_ENTITY_PATTERN_ = /&([^;\s<&]+);?/g;


/**
 * Do escaping of whitespace to preserve spatial formatting. We use character
 * entity #160 to make it safer for xml.
 * @param {string} str The string in which to escape whitespace.
 * @param {boolean=} opt_xml Whether to use XML compatible tags.
 * @return {string} An escaped copy of `str`.
 */
goog.string.whitespaceEscape = function(str, opt_xml) {
  // This doesn't use goog.string.preserveSpaces for backwards compatibility.
  return goog.string.newLineToBr(str.replace(/  /g, ' &#160;'), opt_xml);
};


/**
 * Preserve spaces that would be otherwise collapsed in HTML by replacing them
 * with non-breaking space Unicode characters.
 * @param {string} str The string in which to preserve whitespace.
 * @return {string} A copy of `str` with preserved whitespace.
 */
goog.string.preserveSpaces = function(str) {
  return str.replace(/(^|[\n ]) /g, '$1' + goog.string.Unicode.NBSP);
};


/**
 * Strip quote characters around a string.  The second argument is a string of
 * characters to treat as quotes.  This can be a single character or a string of
 * multiple character and in that case each of those are treated as possible
 * quote characters. For example:
 *
 * <pre>
 * goog.string.stripQuotes('"abc"', '"`') --> 'abc'
 * goog.string.stripQuotes('`abc`', '"`') --> 'abc'
 * </pre>
 *
 * @param {string} str The string to strip.
 * @param {string} quoteChars The quote characters to strip.
 * @return {string} A copy of `str` without the quotes.
 */
goog.string.stripQuotes = function(str, quoteChars) {
  var length = quoteChars.length;
  for (var i = 0; i < length; i++) {
    var quoteChar = length == 1 ? quoteChars : quoteChars.charAt(i);
    if (str.charAt(0) == quoteChar && str.charAt(str.length - 1) == quoteChar) {
      return str.substring(1, str.length - 1);
    }
  }
  return str;
};


/**
 * Truncates a string to a certain length and adds '...' if necessary.  The
 * length also accounts for the ellipsis, so a maximum length of 10 and a string
 * 'Hello World!' produces 'Hello W...'.
 * @param {string} str The string to truncate.
 * @param {number} chars Max number of characters.
 * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped
 *     characters from being cut off in the middle.
 * @return {string} The truncated `str` string.
 */
goog.string.truncate = function(str, chars, opt_protectEscapedCharacters) {
  if (opt_protectEscapedCharacters) {
    str = goog.string.unescapeEntities(str);
  }

  if (str.length > chars) {
    str = str.substring(0, chars - 3) + '...';
  }

  if (opt_protectEscapedCharacters) {
    str = goog.string.htmlEscape(str);
  }

  return str;
};


/**
 * Truncate a string in the middle, adding "..." if necessary,
 * and favoring the beginning of the string.
 * @param {string} str The string to truncate the middle of.
 * @param {number} chars Max number of characters.
 * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped
 *     characters from being cutoff in the middle.
 * @param {number=} opt_trailingChars Optional number of trailing characters to
 *     leave at the end of the string, instead of truncating as close to the
 *     middle as possible.
 * @return {string} A truncated copy of `str`.
 */
goog.string.truncateMiddle = function(
    str, chars, opt_protectEscapedCharacters, opt_trailingChars) {
  if (opt_protectEscapedCharacters) {
    str = goog.string.unescapeEntities(str);
  }

  if (opt_trailingChars && str.length > chars) {
    if (opt_trailingChars > chars) {
      opt_trailingChars = chars;
    }
    var endPoint = str.length - opt_trailingChars;
    var startPoint = chars - opt_trailingChars;
    str = str.substring(0, startPoint) + '...' + str.substring(endPoint);
  } else if (str.length > chars) {
    // Favor the beginning of the string:
    var half = Math.floor(chars / 2);
    var endPos = str.length - half;
    half += chars % 2;
    str = str.substring(0, half) + '...' + str.substring(endPos);
  }

  if (opt_protectEscapedCharacters) {
    str = goog.string.htmlEscape(str);
  }

  return str;
};


/**
 * Special chars that need to be escaped for goog.string.quote.
 * @private {!Object<string, string>}
 */
goog.string.specialEscapeChars_ = {
  '\0': '\\0',
  '\b': '\\b',
  '\f': '\\f',
  '\n': '\\n',
  '\r': '\\r',
  '\t': '\\t',
  '\x0B': '\\x0B',  // '\v' is not supported in JScript
  '"': '\\"',
  '\\': '\\\\',
  // To support the use case of embedding quoted strings inside of script
  // tags, we have to make sure HTML comments and opening/closing script tags do
  // not appear in the resulting string. The specific strings that must be
  // escaped are documented at:
  // https://html.spec.whatwg.org/multipage/scripting.html#restrictions-for-contents-of-script-elements
  '<': '\\u003C'  // NOTE: JSON.parse crashes on '\\x3c'.
};


/**
 * Character mappings used internally for goog.string.escapeChar.
 * @private {!Object<string, string>}
 */
goog.string.jsEscapeCache_ = {
  '\'': '\\\''
};


/**
 * Encloses a string in double quotes and escapes characters so that the
 * string is a valid JS string. The resulting string is safe to embed in
 * `<script>` tags as "<" is escaped.
 * @param {string} s The string to quote.
 * @return {string} A copy of `s` surrounded by double quotes.
 */
goog.string.quote = function(s) {
  s = String(s);
  var sb = ['"'];
  for (var i = 0; i < s.length; i++) {
    var ch = s.charAt(i);
    var cc = ch.charCodeAt(0);
    sb[i + 1] = goog.string.specialEscapeChars_[ch] ||
        ((cc > 31 && cc < 127) ? ch : goog.string.escapeChar(ch));
  }
  sb.push('"');
  return sb.join('');
};


/**
 * Takes a string and returns the escaped string for that input string.
 * @param {string} str The string to escape.
 * @return {string} An escaped string representing `str`.
 */
goog.string.escapeString = function(str) {
  var sb = [];
  for (var i = 0; i < str.length; i++) {
    sb[i] = goog.string.escapeChar(str.charAt(i));
  }
  return sb.join('');
};


/**
 * Takes a character and returns the escaped string for that character. For
 * example escapeChar(String.fromCharCode(15)) -> "\\x0E".
 * @param {string} c The character to escape.
 * @return {string} An escaped string representing `c`.
 */
goog.string.escapeChar = function(c) {
  if (c in goog.string.jsEscapeCache_) {
    return goog.string.jsEscapeCache_[c];
  }

  if (c in goog.string.specialEscapeChars_) {
    return goog.string.jsEscapeCache_[c] = goog.string.specialEscapeChars_[c];
  }

  var rv = c;
  var cc = c.charCodeAt(0);
  if (cc > 31 && cc < 127) {
    rv = c;
  } else {
    // tab is 9 but handled above
    if (cc < 256) {
      rv = '\\x';
      if (cc < 16 || cc > 256) {
        rv += '0';
      }
    } else {
      rv = '\\u';
      if (cc < 4096) {  // \u1000
        rv += '0';
      }
    }
    rv += cc.toString(16).toUpperCase();
  }

  return goog.string.jsEscapeCache_[c] = rv;
};


/**
 * Determines whether a string contains a substring.
 * @param {string} str The string to search.
 * @param {string} subString The substring to search for.
 * @return {boolean} Whether `str` contains `subString`.
 */
goog.string.contains = goog.string.internal.contains;


/**
 * Determines whether a string contains a substring, ignoring case.
 * @param {string} str The string to search.
 * @param {string} subString The substring to search for.
 * @return {boolean} Whether `str` contains `subString`.
 */
goog.string.caseInsensitiveContains =
    goog.string.internal.caseInsensitiveContains;


/**
 * Returns the non-overlapping occurrences of ss in s.
 * If either s or ss evalutes to false, then returns zero.
 * @param {string} s The string to look in.
 * @param {string} ss The string to look for.
 * @return {number} Number of occurrences of ss in s.
 */
goog.string.countOf = function(s, ss) {
  return s && ss ? s.split(ss).length - 1 : 0;
};


/**
 * Removes a substring of a specified length at a specific
 * index in a string.
 * @param {string} s The base string from which to remove.
 * @param {number} index The index at which to remove the substring.
 * @param {number} stringLength The length of the substring to remove.
 * @return {string} A copy of `s` with the substring removed or the full
 *     string if nothing is removed or the input is invalid.
 */
goog.string.removeAt = function(s, index, stringLength) {
  var resultStr = s;
  // If the index is greater or equal to 0 then remove substring
  if (index >= 0 && index < s.length && stringLength > 0) {
    resultStr = s.substr(0, index) +
        s.substr(index + stringLength, s.length - index - stringLength);
  }
  return resultStr;
};


/**
 * Removes the first occurrence of a substring from a string.
 * @param {string} str The base string from which to remove.
 * @param {string} substr The string to remove.
 * @return {string} A copy of `str` with `substr` removed or the
 *     full string if nothing is removed.
 */
goog.string.remove = function(str, substr) {
  return str.replace(substr, '');
};


/**
 *  Removes all occurrences of a substring from a string.
 *  @param {string} s The base string from which to remove.
 *  @param {string} ss The string to remove.
 *  @return {string} A copy of `s` with `ss` removed or the full
 *      string if nothing is removed.
 */
goog.string.removeAll = function(s, ss) {
  var re = new RegExp(goog.string.regExpEscape(ss), 'g');
  return s.replace(re, '');
};


/**
 *  Replaces all occurrences of a substring of a string with a new substring.
 *  @param {string} s The base string from which to remove.
 *  @param {string} ss The string to replace.
 *  @param {string} replacement The replacement string.
 *  @return {string} A copy of `s` with `ss` replaced by
 *      `replacement` or the original string if nothing is replaced.
 */
goog.string.replaceAll = function(s, ss, replacement) {
  var re = new RegExp(goog.string.regExpEscape(ss), 'g');
  return s.replace(re, replacement.replace(/\$/g, '$$$$'));
};


/**
 * Escapes characters in the string that are not safe to use in a RegExp.
 * @param {*} s The string to escape. If not a string, it will be casted
 *     to one.
 * @return {string} A RegExp safe, escaped copy of `s`.
 */
goog.string.regExpEscape = function(s) {
  return String(s)
      .replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1')
      .replace(/\x08/g, '\\x08');
};


/**
 * Repeats a string n times.
 * @param {string} string The string to repeat.
 * @param {number} length The number of times to repeat.
 * @return {string} A string containing `length` repetitions of
 *     `string`.
 */
goog.string.repeat = (String.prototype.repeat) ? function(string, length) {
  // The native method is over 100 times faster than the alternative.
  return string.repeat(length);
} : function(string, length) {
  return new Array(length + 1).join(string);
};


/**
 * Pads number to given length and optionally rounds it to a given precision.
 * For example:
 * <pre>padNumber(1.25, 2, 3) -> '01.250'
 * padNumber(1.25, 2) -> '01.25'
 * padNumber(1.25, 2, 1) -> '01.3'
 * padNumber(1.25, 0) -> '1.25'</pre>
 *
 * @param {number} num The number to pad.
 * @param {number} length The desired length.
 * @param {number=} opt_precision The desired precision.
 * @return {string} `num` as a string with the given options.
 */
goog.string.padNumber = function(num, length, opt_precision) {
  var s =
      (opt_precision !== undefined) ? num.toFixed(opt_precision) : String(num);
  var index = s.indexOf('.');
  if (index == -1) {
    index = s.length;
  }
  return goog.string.repeat('0', Math.max(0, length - index)) + s;
};


/**
 * Returns a string representation of the given object, with
 * null and undefined being returned as the empty string.
 *
 * @param {*} obj The object to convert.
 * @return {string} A string representation of the `obj`.
 */
goog.string.makeSafe = function(obj) {
  return obj == null ? '' : String(obj);
};


/**
 * Concatenates string expressions. This is useful
 * since some browsers are very inefficient when it comes to using plus to
 * concat strings. Be careful when using null and undefined here since
 * these will not be included in the result. If you need to represent these
 * be sure to cast the argument to a String first.
 * For example:
 * <pre>buildString('a', 'b', 'c', 'd') -> 'abcd'
 * buildString(null, undefined) -> ''
 * </pre>
 * @param {...*} var_args A list of strings to concatenate. If not a string,
 *     it will be casted to one.
 * @return {string} The concatenation of `var_args`.
 */
goog.string.buildString = function(var_args) {
  return Array.prototype.join.call(arguments, '');
};


/**
 * Returns a string with at least 64-bits of randomness.
 *
 * Doesn't trust JavaScript's random function entirely. Uses a combination of
 * random and current timestamp, and then encodes the string in base-36 to
 * make it shorter.
 *
 * @return {string} A random string, e.g. sn1s7vb4gcic.
 */
goog.string.getRandomString = function() {
  var x = 2147483648;
  return Math.floor(Math.random() * x).toString(36) +
      Math.abs(Math.floor(Math.random() * x) ^ goog.now()).toString(36);
};


/**
 * Compares two version numbers.
 *
 * @param {string|number} version1 Version of first item.
 * @param {string|number} version2 Version of second item.
 *
 * @return {number}  1 if `version1` is higher.
 *                   0 if arguments are equal.
 *                  -1 if `version2` is higher.
 */
goog.string.compareVersions = goog.string.internal.compareVersions;


/**
 * String hash function similar to java.lang.String.hashCode().
 * The hash code for a string is computed as
 * s[0] * 31 ^ (n - 1) + s[1] * 31 ^ (n - 2) + ... + s[n - 1],
 * where s[i] is the ith character of the string and n is the length of
 * the string. We mod the result to make it between 0 (inclusive) and 2^32
 * (exclusive).
 * @param {string} str A string.
 * @return {number} Hash value for `str`, between 0 (inclusive) and 2^32
 *  (exclusive). The empty string returns 0.
 */
goog.string.hashCode = function(str) {
  var result = 0;
  for (var i = 0; i < str.length; ++i) {
    // Normalize to 4 byte range, 0 ... 2^32.
    result = (31 * result + str.charCodeAt(i)) >>> 0;
  }
  return result;
};


/**
 * The most recent unique ID. |0 is equivalent to Math.floor in this case.
 * @type {number}
 * @private
 */
goog.string.uniqueStringCounter_ = Math.random() * 0x80000000 | 0;


/**
 * Generates and returns a string which is unique in the current document.
 * This is useful, for example, to create unique IDs for DOM elements.
 * @return {string} A unique id.
 */
goog.string.createUniqueString = function() {
  return 'goog_' + goog.string.uniqueStringCounter_++;
};


/**
 * Converts the supplied string to a number, which may be Infinity or NaN.
 * This function strips whitespace: (toNumber(' 123') === 123)
 * This function accepts scientific notation: (toNumber('1e1') === 10)
 *
 * This is better than JavaScript's built-in conversions because, sadly:
 *     (Number(' ') === 0) and (parseFloat('123a') === 123)
 *
 * @param {string} str The string to convert.
 * @return {number} The number the supplied string represents, or NaN.
 */
goog.string.toNumber = function(str) {
  var num = Number(str);
  if (num == 0 && goog.string.isEmptyOrWhitespace(str)) {
    return NaN;
  }
  return num;
};


/**
 * Returns whether the given string is lower camel case (e.g. "isFooBar").
 *
 * Note that this assumes the string is entirely letters.
 * @see http://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms
 *
 * @param {string} str String to test.
 * @return {boolean} Whether the string is lower camel case.
 */
goog.string.isLowerCamelCase = function(str) {
  return /^[a-z]+([A-Z][a-z]*)*$/.test(str);
};


/**
 * Returns whether the given string is upper camel case (e.g. "FooBarBaz").
 *
 * Note that this assumes the string is entirely letters.
 * @see http://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms
 *
 * @param {string} str String to test.
 * @return {boolean} Whether the string is upper camel case.
 */
goog.string.isUpperCamelCase = function(str) {
  return /^([A-Z][a-z]*)+$/.test(str);
};


/**
 * Converts a string from selector-case to camelCase (e.g. from
 * "multi-part-string" to "multiPartString"), useful for converting
 * CSS selectors and HTML dataset keys to their equivalent JS properties.
 * @param {string} str The string in selector-case form.
 * @return {string} The string in camelCase form.
 */
goog.string.toCamelCase = function(str) {
  return String(str).replace(/\-([a-z])/g, function(all, match) {
    return match.toUpperCase();
  });
};


/**
 * Converts a string from camelCase to selector-case (e.g. from
 * "multiPartString" to "multi-part-string"), useful for converting JS
 * style and dataset properties to equivalent CSS selectors and HTML keys.
 * @param {string} str The string in camelCase form.
 * @return {string} The string in selector-case form.
 */
goog.string.toSelectorCase = function(str) {
  return String(str).replace(/([A-Z])/g, '-$1').toLowerCase();
};


/**
 * Converts a string into TitleCase. First character of the string is always
 * capitalized in addition to the first letter of every subsequent word.
 * Words are delimited by one or more whitespaces by default. Custom delimiters
 * can optionally be specified to replace the default, which doesn't preserve
 * whitespace delimiters and instead must be explicitly included if needed.
 *
 * Default delimiter => " ":
 *    goog.string.toTitleCase('oneTwoThree')    => 'OneTwoThree'
 *    goog.string.toTitleCase('one two three')  => 'One Two Three'
 *    goog.string.toTitleCase('  one   two   ') => '  One   Two   '
 *    goog.string.toTitleCase('one_two_three')  => 'One_two_three'
 *    goog.string.toTitleCase('one-two-three')  => 'One-two-three'
 *
 * Custom delimiter => "_-.":
 *    goog.string.toTitleCase('oneTwoThree', '_-.')       => 'OneTwoThree'
 *    goog.string.toTitleCase('one two three', '_-.')     => 'One two three'
 *    goog.string.toTitleCase('  one   two   ', '_-.')    => '  one   two   '
 *    goog.string.toTitleCase('one_two_three', '_-.')     => 'One_Two_Three'
 *    goog.string.toTitleCase('one-two-three', '_-.')     => 'One-Two-Three'
 *    goog.string.toTitleCase('one...two...three', '_-.') => 'One...Two...Three'
 *    goog.string.toTitleCase('one. two. three', '_-.')   => 'One. two. three'
 *    goog.string.toTitleCase('one-two.three', '_-.')     => 'One-Two.Three'
 *
 * @param {string} str String value in camelCase form.
 * @param {string=} opt_delimiters Custom delimiter character set used to
 *      distinguish words in the string value. Each character represents a
 *      single delimiter. When provided, default whitespace delimiter is
 *      overridden and must be explicitly included if needed.
 * @return {string} String value in TitleCase form.
 */
goog.string.toTitleCase = function(str, opt_delimiters) {
  var delimiters = (typeof opt_delimiters === 'string') ?
      goog.string.regExpEscape(opt_delimiters) :
      '\\s';

  // For IE8, we need to prevent using an empty character set. Otherwise,
  // incorrect matching will occur.
  delimiters = delimiters ? '|[' + delimiters + ']+' : '';

  var regexp = new RegExp('(^' + delimiters + ')([a-z])', 'g');
  return str.replace(regexp, function(all, p1, p2) {
    return p1 + p2.toUpperCase();
  });
};


/**
 * Capitalizes a string, i.e. converts the first letter to uppercase
 * and all other letters to lowercase, e.g.:
 *
 * goog.string.capitalize('one')     => 'One'
 * goog.string.capitalize('ONE')     => 'One'
 * goog.string.capitalize('one two') => 'One two'
 *
 * Note that this function does not trim initial whitespace.
 *
 * @param {string} str String value to capitalize.
 * @return {string} String value with first letter in uppercase.
 */
goog.string.capitalize = function(str) {
  return String(str.charAt(0)).toUpperCase() +
      String(str.substr(1)).toLowerCase();
};


/**
 * Parse a string in decimal or hexidecimal ('0xFFFF') form.
 *
 * To parse a particular radix, please use parseInt(string, radix) directly. See
 * https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/parseInt
 *
 * This is a wrapper for the built-in parseInt function that will only parse
 * numbers as base 10 or base 16.  Some JS implementations assume strings
 * starting with "0" are intended to be octal. ES3 allowed but discouraged
 * this behavior. ES5 forbids it.  This function emulates the ES5 behavior.
 *
 * For more information, see Mozilla JS Reference: http://goo.gl/8RiFj
 *
 * @param {string|number|null|undefined} value The value to be parsed.
 * @return {number} The number, parsed. If the string failed to parse, this
 *     will be NaN.
 */
goog.string.parseInt = function(value) {
  // Force finite numbers to strings.
  if (isFinite(value)) {
    value = String(value);
  }

  if (typeof value === 'string') {
    // If the string starts with '0x' or '-0x', parse as hex.
    return /^\s*-?0x/i.test(value) ? parseInt(value, 16) : parseInt(value, 10);
  }

  return NaN;
};


/**
 * Splits a string on a separator a limited number of times.
 *
 * This implementation is more similar to Python or Java, where the limit
 * parameter specifies the maximum number of splits rather than truncating
 * the number of results.
 *
 * See http://docs.python.org/2/library/stdtypes.html#str.split
 * See JavaDoc: http://goo.gl/F2AsY
 * See Mozilla reference: http://goo.gl/dZdZs
 *
 * @param {string} str String to split.
 * @param {string} separator The separator.
 * @param {number} limit The limit to the number of splits. The resulting array
 *     will have a maximum length of limit+1.  Negative numbers are the same
 *     as zero.
 * @return {!Array<string>} The string, split.
 */
goog.string.splitLimit = function(str, separator, limit) {
  var parts = str.split(separator);
  var returnVal = [];

  // Only continue doing this while we haven't hit the limit and we have
  // parts left.
  while (limit > 0 && parts.length) {
    returnVal.push(parts.shift());
    limit--;
  }

  // If there are remaining parts, append them to the end.
  if (parts.length) {
    returnVal.push(parts.join(separator));
  }

  return returnVal;
};


/**
 * Finds the characters to the right of the last instance of any separator
 *
 * This function is similar to goog.string.path.baseName, except it can take a
 * list of characters to split the string on. It will return the rightmost
 * grouping of characters to the right of any separator as a left-to-right
 * oriented string.
 *
 * @see goog.string.path.baseName
 * @param {string} str The string
 * @param {string|!Array<string>} separators A list of separator characters
 * @return {string} The last part of the string with respect to the separators
 */
goog.string.lastComponent = function(str, separators) {
  if (!separators) {
    return str;
  } else if (typeof separators == 'string') {
    separators = [separators];
  }

  var lastSeparatorIndex = -1;
  for (var i = 0; i < separators.length; i++) {
    if (separators[i] == '') {
      continue;
    }
    var currentSeparatorIndex = str.lastIndexOf(separators[i]);
    if (currentSeparatorIndex > lastSeparatorIndex) {
      lastSeparatorIndex = currentSeparatorIndex;
    }
  }
  if (lastSeparatorIndex == -1) {
    return str;
  }
  return str.slice(lastSeparatorIndex + 1);
};


/**
 * Computes the Levenshtein edit distance between two strings.
 * @param {string} a
 * @param {string} b
 * @return {number} The edit distance between the two strings.
 */
goog.string.editDistance = function(a, b) {
  var v0 = [];
  var v1 = [];

  if (a == b) {
    return 0;
  }

  if (!a.length || !b.length) {
    return Math.max(a.length, b.length);
  }

  for (var i = 0; i < b.length + 1; i++) {
    v0[i] = i;
  }

  for (var i = 0; i < a.length; i++) {
    v1[0] = i + 1;

    for (var j = 0; j < b.length; j++) {
      var cost = Number(a[i] != b[j]);
      // Cost for the substring is the minimum of adding one character, removing
      // one character, or a swap.
      v1[j + 1] = Math.min(v1[j] + 1, v0[j + 1] + 1, v0[j] + cost);
    }

    for (var j = 0; j < v0.length; j++) {
      v0[j] = v1[j];
    }
  }

  return v1[b.length];
};

//third_party/javascript/closure/structs/structs.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Generics method for collection-like classes and objects.
 *
 *
 * This file contains functions to work with collections. It supports using
 * Map, Set, Array and Object and other classes that implement collection-like
 * methods.
 * @suppress {strictMissingProperties}
 */


goog.provide('goog.structs');

goog.require('goog.array');
goog.require('goog.object');


// We treat an object as a dictionary if it has getKeys or it is an object that
// isn't arrayLike.


/**
 * Returns the number of values in the collection-like object.
 * @param {Object} col The collection-like object.
 * @return {number} The number of values in the collection-like object.
 */
goog.structs.getCount = function(col) {
  if (col.getCount && typeof col.getCount == 'function') {
    return col.getCount();
  }
  if (goog.isArrayLike(col) || typeof col === 'string') {
    return col.length;
  }
  return goog.object.getCount(col);
};


/**
 * Returns the values of the collection-like object.
 * @param {Object} col The collection-like object.
 * @return {!Array<?>} The values in the collection-like object.
 */
goog.structs.getValues = function(col) {
  if (col.getValues && typeof col.getValues == 'function') {
    return col.getValues();
  }
  if (typeof col === 'string') {
    return col.split('');
  }
  if (goog.isArrayLike(col)) {
    var rv = [];
    var l = col.length;
    for (var i = 0; i < l; i++) {
      rv.push(col[i]);
    }
    return rv;
  }
  return goog.object.getValues(col);
};


/**
 * Returns the keys of the collection. Some collections have no notion of
 * keys/indexes and this function will return undefined in those cases.
 * @param {Object} col The collection-like object.
 * @return {!Array|undefined} The keys in the collection.
 */
goog.structs.getKeys = function(col) {
  if (col.getKeys && typeof col.getKeys == 'function') {
    return col.getKeys();
  }
  // if we have getValues but no getKeys we know this is a key-less collection
  if (col.getValues && typeof col.getValues == 'function') {
    return undefined;
  }
  if (goog.isArrayLike(col) || typeof col === 'string') {
    var rv = [];
    var l = col.length;
    for (var i = 0; i < l; i++) {
      rv.push(i);
    }
    return rv;
  }

  return goog.object.getKeys(col);
};


/**
 * Whether the collection contains the given value. This is O(n) and uses
 * equals (==) to test the existence.
 * @param {Object} col The collection-like object.
 * @param {*} val The value to check for.
 * @return {boolean} True if the map contains the value.
 */
goog.structs.contains = function(col, val) {
  if (col.contains && typeof col.contains == 'function') {
    return col.contains(val);
  }
  if (col.containsValue && typeof col.containsValue == 'function') {
    return col.containsValue(val);
  }
  if (goog.isArrayLike(col) || typeof col === 'string') {
    return goog.array.contains(/** @type {!Array<?>} */ (col), val);
  }
  return goog.object.containsValue(col, val);
};


/**
 * Whether the collection is empty.
 * @param {Object} col The collection-like object.
 * @return {boolean} True if empty.
 */
goog.structs.isEmpty = function(col) {
  if (col.isEmpty && typeof col.isEmpty == 'function') {
    return col.isEmpty();
  }

  // We do not use goog.string.isEmptyOrWhitespace because here we treat the
  // string as
  // collection and as such even whitespace matters

  if (goog.isArrayLike(col) || typeof col === 'string') {
    return goog.array.isEmpty(/** @type {!Array<?>} */ (col));
  }
  return goog.object.isEmpty(col);
};


/**
 * Removes all the elements from the collection.
 * @param {Object} col The collection-like object.
 */
goog.structs.clear = function(col) {
  // NOTE(arv): This should not contain strings because strings are immutable
  if (col.clear && typeof col.clear == 'function') {
    col.clear();
  } else if (goog.isArrayLike(col)) {
    goog.array.clear(/** @type {IArrayLike<?>} */ (col));
  } else {
    goog.object.clear(col);
  }
};


/**
 * Calls a function for each value in a collection. The function takes
 * three arguments; the value, the key and the collection.
 *
 * @param {S} col The collection-like object.
 * @param {function(this:T,?,?,S):?} f The function to call for every value.
 *     This function takes
 *     3 arguments (the value, the key or undefined if the collection has no
 *     notion of keys, and the collection) and the return value is irrelevant.
 * @param {T=} opt_obj The object to be used as the value of 'this'
 *     within `f`.
 * @template T,S
 * @deprecated Use a more specific method, e.g. goog.array.forEach,
 *     goog.object.forEach, or for-of.
 */
goog.structs.forEach = function(col, f, opt_obj) {
  if (col.forEach && typeof col.forEach == 'function') {
    col.forEach(f, opt_obj);
  } else if (goog.isArrayLike(col) || typeof col === 'string') {
    goog.array.forEach(/** @type {!Array<?>} */ (col), f, opt_obj);
  } else {
    var keys = goog.structs.getKeys(col);
    var values = goog.structs.getValues(col);
    var l = values.length;
    for (var i = 0; i < l; i++) {
      f.call(/** @type {?} */ (opt_obj), values[i], keys && keys[i], col);
    }
  }
};


/**
 * Calls a function for every value in the collection. When a call returns true,
 * adds the value to a new collection (Array is returned by default).
 *
 * @param {S} col The collection-like object.
 * @param {function(this:T,?,?,S):boolean} f The function to call for every
 *     value. This function takes
 *     3 arguments (the value, the key or undefined if the collection has no
 *     notion of keys, and the collection) and should return a Boolean. If the
 *     return value is true the value is added to the result collection. If it
 *     is false the value is not included.
 * @param {T=} opt_obj The object to be used as the value of 'this'
 *     within `f`.
 * @return {!Object|!Array<?>} A new collection where the passed values are
 *     present. If col is a key-less collection an array is returned.  If col
 *     has keys and values a plain old JS object is returned.
 * @template T,S
 */
goog.structs.filter = function(col, f, opt_obj) {
  if (typeof col.filter == 'function') {
    return col.filter(f, opt_obj);
  }
  if (goog.isArrayLike(col) || typeof col === 'string') {
    return goog.array.filter(/** @type {!Array<?>} */ (col), f, opt_obj);
  }

  var rv;
  var keys = goog.structs.getKeys(col);
  var values = goog.structs.getValues(col);
  var l = values.length;
  if (keys) {
    rv = {};
    for (var i = 0; i < l; i++) {
      if (f.call(/** @type {?} */ (opt_obj), values[i], keys[i], col)) {
        rv[keys[i]] = values[i];
      }
    }
  } else {
    // We should not use goog.array.filter here since we want to make sure that
    // the index is undefined as well as make sure that col is passed to the
    // function.
    rv = [];
    for (var i = 0; i < l; i++) {
      if (f.call(opt_obj, values[i], undefined, col)) {
        rv.push(values[i]);
      }
    }
  }
  return rv;
};


/**
 * Calls a function for every value in the collection and adds the result into a
 * new collection (defaults to creating a new Array).
 *
 * @param {S} col The collection-like object.
 * @param {function(this:T,?,?,S):V} f The function to call for every value.
 *     This function takes 3 arguments (the value, the key or undefined if the
 *     collection has no notion of keys, and the collection) and should return
 *     something. The result will be used as the value in the new collection.
 * @param {T=} opt_obj  The object to be used as the value of 'this'
 *     within `f`.
 * @return {!Object<V>|!Array<V>} A new collection with the new values.  If
 *     col is a key-less collection an array is returned.  If col has keys and
 *     values a plain old JS object is returned.
 * @template T,S,V
 */
goog.structs.map = function(col, f, opt_obj) {
  if (typeof col.map == 'function') {
    return col.map(f, opt_obj);
  }
  if (goog.isArrayLike(col) || typeof col === 'string') {
    return goog.array.map(/** @type {!Array<?>} */ (col), f, opt_obj);
  }

  var rv;
  var keys = goog.structs.getKeys(col);
  var values = goog.structs.getValues(col);
  var l = values.length;
  if (keys) {
    rv = {};
    for (var i = 0; i < l; i++) {
      rv[keys[i]] = f.call(/** @type {?} */ (opt_obj), values[i], keys[i], col);
    }
  } else {
    // We should not use goog.array.map here since we want to make sure that
    // the index is undefined as well as make sure that col is passed to the
    // function.
    rv = [];
    for (var i = 0; i < l; i++) {
      rv[i] = f.call(/** @type {?} */ (opt_obj), values[i], undefined, col);
    }
  }
  return rv;
};


/**
 * Calls f for each value in a collection. If any call returns true this returns
 * true (without checking the rest). If all returns false this returns false.
 *
 * @param {S} col The collection-like object.
 * @param {function(this:T,?,?,S):boolean} f The function to call for every
 *     value. This function takes 3 arguments (the value, the key or undefined
 *     if the collection has no notion of keys, and the collection) and should
 *     return a boolean.
 * @param {T=} opt_obj  The object to be used as the value of 'this'
 *     within `f`.
 * @return {boolean} True if any value passes the test.
 * @template T,S
 */
goog.structs.some = function(col, f, opt_obj) {
  if (typeof col.some == 'function') {
    return col.some(f, opt_obj);
  }
  if (goog.isArrayLike(col) || typeof col === 'string') {
    return goog.array.some(/** @type {!Array<?>} */ (col), f, opt_obj);
  }
  var keys = goog.structs.getKeys(col);
  var values = goog.structs.getValues(col);
  var l = values.length;
  for (var i = 0; i < l; i++) {
    if (f.call(/** @type {?} */ (opt_obj), values[i], keys && keys[i], col)) {
      return true;
    }
  }
  return false;
};


/**
 * Calls f for each value in a collection. If all calls return true this return
 * true this returns true. If any returns false this returns false at this point
 *  and does not continue to check the remaining values.
 *
 * @param {S} col The collection-like object.
 * @param {function(this:T,?,?,S):boolean} f The function to call for every
 *     value. This function takes 3 arguments (the value, the key or
 *     undefined if the collection has no notion of keys, and the collection)
 *     and should return a boolean.
 * @param {T=} opt_obj  The object to be used as the value of 'this'
 *     within `f`.
 * @return {boolean} True if all key-value pairs pass the test.
 * @template T,S
 */
goog.structs.every = function(col, f, opt_obj) {
  if (typeof col.every == 'function') {
    return col.every(f, opt_obj);
  }
  if (goog.isArrayLike(col) || typeof col === 'string') {
    return goog.array.every(/** @type {!Array<?>} */ (col), f, opt_obj);
  }
  var keys = goog.structs.getKeys(col);
  var values = goog.structs.getValues(col);
  var l = values.length;
  for (var i = 0; i < l; i++) {
    if (!f.call(/** @type {?} */ (opt_obj), values[i], keys && keys[i], col)) {
      return false;
    }
  }
  return true;
};

//third_party/javascript/closure/structs/trie.js
/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Datastructure: Trie.
 *
 *
 * This file provides the implementation of a trie data structure.  A trie is a
 * data structure that stores key/value pairs in a prefix tree.  See:
 *     http://en.wikipedia.org/wiki/Trie
 */


goog.provide('goog.structs.Trie');

goog.require('goog.object');
goog.require('goog.structs');



/**
 * Class for a Trie datastructure.  Trie data structures are made out of trees
 * of Trie classes.
 *
 * @param {goog.structs.Trie<VALUE>|Object<string, VALUE>=} opt_trie Optional
 *     goog.structs.Trie or Object to initialize trie with.
 * @constructor
 * @template VALUE
 */
goog.structs.Trie = function(opt_trie) {
  /**
   * This trie's value.  For the base trie, this will be the value of the
   * empty key, if defined.
   * @private {VALUE}
   */
  this.value_ = undefined;

  /**
   * This trie's child nodes.
   * @private {!Object<!goog.structs.Trie<VALUE>>}
   */
  this.childNodes_ = {};

  if (opt_trie) {
    this.setAll(opt_trie);
  }
};


/**
 * Sets the given key/value pair in the trie.  O(L), where L is the length
 * of the key.
 * @param {string} key The key.
 * @param {VALUE} value The value.
 */
goog.structs.Trie.prototype.set = function(key, value) {
  this.setOrAdd_(key, value, false);
};


/**
 * Adds the given key/value pair in the trie.  Throw an exception if the key
 * already exists in the trie.  O(L), where L is the length of the key.
 * @param {string} key The key.
 * @param {VALUE} value The value.
 */
goog.structs.Trie.prototype.add = function(key, value) {
  this.setOrAdd_(key, value, true);
};


/**
 * Helper function for set and add.  Adds the given key/value pair to
 * the trie, or, if the key already exists, sets the value of the key. If
 * opt_add is true, then throws an exception if the key already has a value in
 * the trie.  O(L), where L is the length of the key.
 * @param {string} key The key.
 * @param {VALUE} value The value.
 * @param {boolean=} opt_add Throw exception if key is already in the trie.
 * @private
 */
goog.structs.Trie.prototype.setOrAdd_ = function(key, value, opt_add) {
  var node = this;
  for (var characterPosition = 0; characterPosition < key.length;
       characterPosition++) {
    var currentCharacter = key.charAt(characterPosition);
    if (!node.childNodes_[currentCharacter]) {
      node.childNodes_[currentCharacter] = new goog.structs.Trie();
    }
    node = node.childNodes_[currentCharacter];
  }
  if (opt_add && node.value_ !== undefined) {
    throw new Error('The collection already contains the key "' + key + '"');
  } else {
    node.value_ = value;
  }
};


/**
 * Adds multiple key/value pairs from another goog.structs.Trie or Object.
 * O(N) where N is the number of nodes in the trie.
 * @param {!Object<string, VALUE>|!goog.structs.Trie<VALUE>} trie Object
 *     containing the data to add.
 */
goog.structs.Trie.prototype.setAll = function(trie) {
  var keys = goog.structs.getKeys(trie);
  var values = goog.structs.getValues(trie);

  for (var i = 0; i < keys.length; i++) {
    this.set(keys[i], values[i]);
  }
};


/**
 * Traverse along the given path, returns the child node at ending.
 * Returns undefined if node for the path doesn't exist.
 * @param {string} path The path to traverse.
 * @return {!goog.structs.Trie<VALUE>|undefined}
 * @private
 */
goog.structs.Trie.prototype.getChildNode_ = function(path) {
  var node = this;
  for (var characterPosition = 0; characterPosition < path.length;
       characterPosition++) {
    var currentCharacter = path.charAt(characterPosition);
    node = node.childNodes_[currentCharacter];
    if (!node) {
      return undefined;
    }
  }
  return node;
};


/**
 * Retrieves a value from the trie given a key.  O(L), where L is the length of
 * the key.
 * @param {string} key The key to retrieve from the trie.
 * @return {VALUE|undefined} The value of the key in the trie, or undefined if
 *     the trie does not contain this key.
 */
goog.structs.Trie.prototype.get = function(key) {
  var node = this.getChildNode_(key);
  return node ? node.value_ : undefined;
};


/**
 * Retrieves all values from the trie that correspond to prefixes of the given
 * input key. O(L), where L is the length of the key.
 *
 * @param {string} key The key to use for lookup. The given key as well as all
 *     prefixes of the key are retrieved.
 * @param {?number=} opt_keyStartIndex Optional position in key to start lookup
 *     from. Defaults to 0 if not specified.
 * @return {!Object<VALUE>} Map of end index of matching prefixes and
 *     corresponding values. Empty if no match found.  Note that the key is the
 *     index of the final character of the entry, rather than the length, so
 *     entries with a single character will clobber an empty-string entry with
 *     key '0'.
 */
goog.structs.Trie.prototype.getKeyAndPrefixes = function(
    key, opt_keyStartIndex) {
  /** @type {!goog.structs.Trie<VALUE>} */
  var node = this;
  var matches = {};
  var characterPosition = opt_keyStartIndex || 0;

  if (node.value_ !== undefined) {
    matches[characterPosition] = node.value_;
  }

  for (; characterPosition < key.length; characterPosition++) {
    var currentCharacter = key.charAt(characterPosition);
    if (!(currentCharacter in node.childNodes_)) {
      break;
    }
    node = node.childNodes_[currentCharacter];
    if (/** @type {VALUE} */ (node.value_) !== undefined) {
      matches[characterPosition] = node.value_;
    }
  }

  return matches;
};


/**
 * Gets the values of the trie.  Not returned in any reliable order.  O(N) where
 * N is the number of nodes in the trie.  Calls getValuesInternal_.
 * @return {!Array<VALUE>} The values in the trie.
 */
goog.structs.Trie.prototype.getValues = function() {
  var allValues = [];
  this.getValuesInternal_(allValues);
  return allValues;
};


/**
 * Gets the values of the trie.  Not returned in any reliable order.  O(N) where
 * N is the number of nodes in the trie.  Builds the values as it goes.
 * @param {!Array<VALUE>} allValues Array to place values into.
 * @private
 */
goog.structs.Trie.prototype.getValuesInternal_ = function(allValues) {
  if (this.value_ !== undefined) {
    allValues.push(this.value_);
  }
  for (var childNode in this.childNodes_) {
    this.childNodes_[childNode].getValuesInternal_(allValues);
  }
};


/**
 * Gets the keys of the trie.  Not returned in any reliable order.  O(N) where
 * N is the number of nodes in the trie (or prefix subtree).
 * @param {string=} opt_prefix Find only keys with this optional prefix.
 * @return {!Array<string>} The keys in the trie.
 */
goog.structs.Trie.prototype.getKeys = function(opt_prefix) {
  var allKeys = [];
  if (opt_prefix) {
    // Traverse to the given prefix, then call getKeysInternal_ to dump the
    // keys below that point.
    var node = this;
    for (var characterPosition = 0; characterPosition < opt_prefix.length;
         characterPosition++) {
      var currentCharacter = opt_prefix.charAt(characterPosition);
      if (!node.childNodes_[currentCharacter]) {
        return [];
      }
      node = node.childNodes_[currentCharacter];
    }
    node.getKeysInternal_(opt_prefix, allKeys);
  } else {
    this.getKeysInternal_('', allKeys);
  }
  return allKeys;
};


/**
 * Private method to get keys from the trie.  Builds the keys as it goes.
 * @param {string} keySoFar The partial key (prefix) traversed so far.
 * @param {!Array<string>} allKeys The partially built array of keys seen so
 *     far.
 * @private
 */
goog.structs.Trie.prototype.getKeysInternal_ = function(keySoFar, allKeys) {
  if (this.value_ !== undefined) {
    allKeys.push(keySoFar);
  }
  for (var childNode in this.childNodes_) {
    this.childNodes_[childNode].getKeysInternal_(keySoFar + childNode, allKeys);
  }
};


/**
 * Checks to see if a certain key is in the trie.  O(L), where L is the length
 * of the key.
 * @param {string} key A key that may be in the trie.
 * @return {boolean} Whether the trie contains key.
 */
goog.structs.Trie.prototype.containsKey = function(key) {
  return this.get(key) !== undefined;
};


/**
 * Checks to see if a certain prefix is in the trie. O(L), where L is the length
 * of the prefix.
 * @param {string} prefix A prefix that may be in the trie.
 * @return {boolean} Whether any key of the trie has the prefix.
 */
goog.structs.Trie.prototype.containsPrefix = function(prefix) {
  // Empty string is any key's prefix.
  if (prefix.length == 0) {
    return !this.isEmpty();
  }
  return !!this.getChildNode_(prefix);
};


/**
 * Checks to see if a certain value is in the trie.  Worst case is O(N) where
 * N is the number of nodes in the trie.
 * @param {VALUE} value A value that may be in the trie.
 * @return {boolean} Whether the trie contains the value.
 */
goog.structs.Trie.prototype.containsValue = function(value) {
  if (this.value_ === value) {
    return true;
  }
  for (var childNode in this.childNodes_) {
    if (this.childNodes_[childNode].containsValue(value)) {
      return true;
    }
  }
  return false;
};


/**
 * Completely empties a trie of all keys and values.  ~O(1)
 */
goog.structs.Trie.prototype.clear = function() {
  this.childNodes_ = {};
  this.value_ = undefined;
};


/**
 * Removes a key from the trie or throws an exception if the key is not in the
 * trie.  O(L), where L is the length of the key.
 * @param {string} key A key that should be removed from the trie.
 * @return {VALUE} The value whose key was removed.
 */
goog.structs.Trie.prototype.remove = function(key) {
  var node = this;
  var parents = [];
  for (var characterPosition = 0; characterPosition < key.length;
       characterPosition++) {
    var currentCharacter = key.charAt(characterPosition);
    if (!node.childNodes_[currentCharacter]) {
      throw new Error('The collection does not have the key "' + key + '"');
    }

    // Archive the current parent and child name (key in childNodes_) so that
    // we may remove the following node and its parents if they are empty.
    parents.push([node, currentCharacter]);

    node = node.childNodes_[currentCharacter];
  }
  var oldValue = node.value_;
  delete node.value_;

  while (parents.length > 0) {
    var currentParentAndCharacter = parents.pop();
    var currentParent = currentParentAndCharacter[0];
    var currentCharacter = currentParentAndCharacter[1];
    if (currentParent.childNodes_[currentCharacter].isEmpty()) {
      // If the child is empty, then remove it.
      delete currentParent.childNodes_[currentCharacter];
    } else {
      // No point of traversing back any further, since we can't remove this
      // path.
      break;
    }
  }
  return oldValue;
};


/**
 * Clones a trie and returns a new trie.  O(N), where N is the number of nodes
 * in the trie.
 * @return {!goog.structs.Trie<VALUE>} A new goog.structs.Trie with the same
 *     key value pairs.
 */
goog.structs.Trie.prototype.clone = function() {
  return new goog.structs.Trie(this);
};


/**
 * Returns the number of key value pairs in the trie.  O(N), where N is the
 * number of nodes in the trie.
 * TODO: This could be optimized by storing a weight (count below) in every
 * node.
 * @return {number} The number of pairs.
 */
goog.structs.Trie.prototype.getCount = function() {
  return goog.structs.getCount(this.getValues());
};


/**
 * Returns true if this trie contains no elements.  ~O(1).
 * @return {boolean} True iff this trie contains no elements.
 */
goog.structs.Trie.prototype.isEmpty = function() {
  return this.value_ === undefined && goog.object.isEmpty(this.childNodes_);
};

//javascript/public_suffix/tld.js
goog.provide('publicsuffix.Tld');

goog.require('goog.asserts');
goog.require('goog.string');
goog.require('goog.structs.Trie');
goog.require('publicsuffix.publicsuffixpatterns');

goog.scope(function() {
var publicsuffixpatterns = publicsuffix.publicsuffixpatterns;



/**
 * The class for determining the top-level domain of a given host.
 * @constructor
 * @struct
 */
publicsuffix.Tld = function() {
  /** @private @const {!goog.structs.Trie<boolean>} */
  this.exactPatterns_ = Tld.createTrie_(publicsuffixpatterns.EXACT);

  /** @private @const {!goog.structs.Trie<boolean>} */
  this.excludePatterns_ = Tld.createTrie_(publicsuffixpatterns.EXCLUDED);

  /** @private @const {!goog.structs.Trie<boolean>} */
  this.wildcardPatterns_ = Tld.createTrie_(publicsuffixpatterns.UNDER);
};
var Tld = publicsuffix.Tld;
goog.addSingletonGetter(Tld);


/**
 * Gets the top-level domain (also known as public suffix) of the given host.
 * The top-level domain is the suffix of the domain name under which other
 * domain names can be registered. For example the top-level domain of
 * 'www.google.com' is 'com', the top-level domain of 'www.google.co.uk' is
 * 'co.uk'. All top-level domain definitions in the public suffix list of
 * Mozilla are considered, but it can also be restricted to official top-level
 * domain definitions by ICANN.
 * @param {string} host The host name.
 * @param {boolean} icannOnly True if only official ICANN public suffixes are
 *     allowed, otherwise all TLDs from the public suffix list will be
 *     recognized.
 * @return {string} The top-level domain.
 */
Tld.prototype.getTld = function(host, icannOnly) {
  // Reverse the host name.
  host = host.split('').reverse().join('');

  // Check for the earliest match with a valid tld.
  // It can either be an exact match.
  var tldCandidate =
      Tld.getLongestMatch_(this.exactPatterns_, host, icannOnly);
  // Or be the suffix of an exact match with an exclude tld.
  var excludeCandidate =
      Tld.getLongestMatch_(this.excludePatterns_, host, icannOnly);
  if (excludeCandidate.length > 0) {
    goog.asserts.assert(excludeCandidate.indexOf('.') >= 0);
    excludeCandidate =
        excludeCandidate.substr(0, excludeCandidate.lastIndexOf('.'));
    if (excludeCandidate.length > tldCandidate.length) {
      tldCandidate = excludeCandidate;
    }
  }
  // Or be a tld without dots which allows any preceding string.
  var wildcardCandidate =
      Tld.getLongestMatch_(this.wildcardPatterns_, host, icannOnly);
  if (wildcardCandidate.length > 0 &&
      host.length > wildcardCandidate.length &&
      wildcardCandidate.length != excludeCandidate.length) {
    host = host.substr(wildcardCandidate.length + 1);
    wildcardCandidate += '.' + host.split('.')[0];
    if (wildcardCandidate.length > tldCandidate.length) {
      tldCandidate = wildcardCandidate;
    }
  }
  // Reverse the match to get the original string orientation.
  return tldCandidate.split('').reverse().join('');
};


/**
 * Determines the longest top-level domain in the trie that matches the given
 * host. Only accept matches that fulfill the |icannOnly| requirement.
 * @param {!goog.structs.Trie.<boolean>} trie The trie containing the top-level
 *     domains.
 * @param {string} host The reversed host name.
 * @param {boolean} icannOnly True iff only official ICANN public suffixes are
 *     allowed.
 * @return {string} The longest matching top-level domain if there is one.
 * @private
 */
Tld.getLongestMatch_ = function(trie, host, icannOnly) {
  // Check for the earliest exact match matching |host| completely, or a match
  // where the next character in |host| is a dot. If |icannOnly| is true, ignore
  // matches which correspond to private public suffix definitions.
  var maxLength = -1;
  var matches = trie.getKeyAndPrefixes(host);
  for (var positionKey in matches) {
    var position = parseInt(positionKey, 10);
    if ((position + 1 == host.length || host.charAt(position + 1) == '.') &&
        (!icannOnly || matches[positionKey] == true) && position > maxLength) {
      maxLength = position;
    }
  }
  return host.substr(0, maxLength + 1);
};


/**
 * Create a trie from the given encoded trie.
 * @param {string} pattern The encoding of the trie as a pre-order traversal.
 * @return {!goog.structs.Trie.<boolean>} The decoded trie.
 * @private
 */
Tld.createTrie_ = function(pattern) {
  var trie = new goog.structs.Trie();
  var idx = Tld.doParseTrie_(0, '', pattern, trie);
  // Assert that we indeed parsed the whole representation of the trie.
  goog.asserts.assert(idx == pattern.length);
  return trie;
};


/**
 * Parse the encoded trie recursively.
 * @param {number} idx The start index for parsing the pattern.
 * @param {string} prefix The characters on the path to the current node.
 * @param {string} pattern The encoded trie.
 * @param {!goog.structs.Trie<boolean>} trie The trie to be constructed.
 * @return {number} The index of the next character to be parsed.
 * @private
 */
Tld.doParseTrie_ = function(idx, prefix, pattern, trie) {
  // '!' represents an interior node that is an ICANN public suffix definition.
  // ':' represents an interior node that is a private public suffix definition.
  // '?' represents a leaf node that is an ICANN public suffix definition.
  // ',' represents a leaf node that is a private public suffix definition.
  // '&' represents an interior node that does not belong to the set of public
  //     suffix definitions.
  // First process all nodes until we hit either a leave or an interior
  // splitting node.
  var c = '\0';
  for (; idx < pattern.length; idx++) {
    c = pattern.charAt(idx);
    // Check if c is one of the special characters.
    if (goog.string.contains('!:?,&', c)) {
      // If the current prefix belongs to the set of tlds, store it in the trie.
      if (c != '&') {
        trie.set(prefix, c == '!' || c == '?');
      }
      break;
    }
    prefix += c;
  }
  goog.asserts.assert(idx < pattern.length);

  // Skip the special character.
  idx++;

  // If the current node is a leave node, we can stop here.
  if (c == '?' || c == ',') {
    return idx;
  }

  // We keep processing the subtrees until we hit a '?' character indicating we
  // are done with the current node.
  do {
    idx = Tld.doParseTrie_(idx, prefix, pattern, trie);
    c = pattern.charAt(idx);
  } while (c != '?' && c != ',');

  // Return idx + 1 because we also need to consume the last '?' character.
  return idx + 1;
};
});  // goog.scope

//third_party/javascript/suspicious_site_reporter/extension/alerts.js
goog.loadModule(function(exports) {'use strict';/**
 * @license
 * Copyright 2019 Google LLC
 *
 * 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 Retrieve relevant alerts for a site.
 */

goog.module('suspiciousSiteReporter.alerts');

const Tld = goog.require('publicsuffix.Tld');

/** @const {!Object<string, string>} Map of signals to messages for the UI. */
const ALERT_MESSAGES = {
  'isIDN': 'Domain uses uncommon characters',
  'longSubdomains': 'Unusually long subdomains',
  'notVisitedBefore': 'Haven\'t visited site in the last 3 months',
  'manySubdomains': 'Unusually many subdomains',
  'redirectsThroughSuspiciousTld':
      'Site redirected through a TLD potentially associated with abuse',
  'redirectsFromOutsideProgramOrWebmail':
      'Visit maybe initiated from outside program or webmail',
  'urlShortenerRedirects': 'Has multiple redirects through URL shorteners',
};

/** @const {number} If a domain has this many subdomains or more, it is flagged. */
const NUM_SUSPICIOUS_SUBDOMAINS = 4;

/**
 * @const {number} If a domain has a subdomain with this many characters or
 * more, it is flagged.
 */
const SUSPICIOUS_SUBDOMAIN_LENGTH = 22;

/** {!Object<string, boolean>} Dictionary with top site domains as keys. */
let topSitesList = {};

/**
 * Returns the domain from a URL.
 * @param {string} url The URL of a page.
 * @return {string} The domain of the page.
 */
const getDomain = (url) => {
  return new URL(url).hostname;
};

/**
 * Returns the domain split into parts and excluding the tld.
 * @param {string} domain The domain of a page.
 * @return {!Array<string>} The domain of the page.
 */
const getDomainPartsWithoutTld = (domain) => {
  const suffix = '.' + Tld.getInstance().getTld(domain, /* icannOnly= */ false);
  return domain.slice(0, domain.lastIndexOf(suffix)).split('.');
};

/**
 * Determines whether the site uses an IDN.
 * @param {string} domain The domain of the page. Regardless of how the
 *     omnibox displays the URL, this should be encoded here.
 * @return {boolean} Whether domain has label starting with 'xn--' (is IDN).
 */
const isIDN = (domain) => {
  // Check that xn-- appears after '.' or at the start of the domain name.
  // Regex used instead of splitting by '.' because splitting causes
  // the international character encoding to get stripped out.
  const regexPunycode = /\.(xn--)/;
  return domain.startsWith('xn--') || regexPunycode.test(domain);
};

/**
 * Checks whether a site is in the top site list.
 * @param {string} domain The domain of the page.
 * @return {boolean} Whether the site is in the top 5k.
 */
const isTopSite = (domain) => {
  const suffix = '.' + Tld.getInstance().getTld(domain, /* icannOnly= */ false);
  const domainPartsWithoutTld = getDomainPartsWithoutTld(domain);
  const etldPlusOne =
      domainPartsWithoutTld[domainPartsWithoutTld.length - 1] + suffix;
  // The below assumes that the top sites list uses lower case only.
  return topSitesList[etldPlusOne.toLowerCase()];
};

/**
 * Fetches the top sites list from JSON.
 * @param {function(?)} callback Callback function.
 */
const fetchTopSites = (callback) => {
  const topSitesList = chrome.runtime.getURL('topsites.json');
  const xhr = new XMLHttpRequest();
  xhr.open('GET', topSitesList);
  xhr.onreadystatechange = () => {
    if (xhr.readyState === 4 && xhr.status === 200) {
      callback(JSON.parse(xhr.responseText));
    }
  };
  xhr.send();
};

/**
 * Sets the value of top sites list variable after fetching from JSON.
 */
const setTopSitesList = () => {
  fetchTopSites((topSites) => {
    topSitesList = topSites;
  });
};

/**
 * Determines whether user has visited specified domain within last 3 months.
 * We use 3 months because recently visited sites are more relevant and because
 * Chrome only stores 3 months of history. We also ignore sites visited today
 * for the first time to reduce false negatives.
 * @param {string} domain The domain of the page.
 * @return {!Promise<boolean>} Whether site was visited recently, before today.
 */
const visitedBeforeToday = (domain) => {
  // Visit time in Chrome history is in milliseconds since epoch, so convert
  // to this unit.
  const currentTime = new Date().getTime();
  const msInDay = 24 * 60 * 60 * 1000;
  const timeYesterday = currentTime - msInDay;
  const timeThreeMonthsAgo = currentTime - (msInDay * 90);
  return new Promise((resolve, reject) => {
    chrome.history.search(
        {
          text: '',  // empty string returns everything
          startTime: timeThreeMonthsAgo,
          endTime: timeYesterday,
          maxResults: 0  // unlimited
        },
        function(pages) {
          // If there is no browsing history returned, assume that the user has
          // browsing history turned off, meaning this signal is noisy. Resolve
          // true to effectively turn off this alert.
          if (pages.length === 0) resolve(true);
          resolve(pages.some((page) => getDomain(page.url) === domain));
        });
  });
};

/**
 * Determines whether the site has unusually many subdomains.
 * @param {string} domain The domain of the page.
 * @return {boolean} True if the site has many subdomains.
*/
const hasManySubdomains = (domain) => {
  const domainPartsWithoutTld = getDomainPartsWithoutTld(domain);
  return domainPartsWithoutTld.length >= NUM_SUSPICIOUS_SUBDOMAINS;
};

/**
 * Determines whether the site has unusually long subdomains.
 * @param {string} domain The domain of the page.
 * @return {boolean} True if the site has long subdomains.
 */
const hasLongSubdomains = (domain) => {
  const domainPartsWithoutTld = getDomainPartsWithoutTld(domain);
  return domainPartsWithoutTld.some(
      (subdomain) => subdomain.length >= SUSPICIOUS_SUBDOMAIN_LENGTH);
};

/**
 * Determines whether the site has multiple redirects through URL shorteners.
 * @param {!Set<string>} redirectUrls A list of unique URLs redirected through
 *     to land on the current site.
 * @return {boolean} Whether the redirect chain has multiple redirects
 *     through URL shorteners.
 */
const hasMultipleUrlShortenerRedirects = (redirectUrls) => {
  // List of some popular URL shorteners obtained from combining some public
  // lists. Limitation is that it is unable to cover custom branded links.
  const urlShorteners = new Set([
    'bc.vc',
    'bit.do',
    'bit.ly',
    'goo.gl',
    'is.gd',
    'ity.im',
    'lc.chat',
    'ow.ly',
    's2r.co',
    'soo.gd',
    'tinyurl.com',
    'tiny.cc',
  ]);
  let urlShortenerRedirects = 0;
  redirectUrls.forEach((url) => {
    if (urlShorteners.has(getDomain(url))) {
      // Only increase the redirect count if it was not an HTTPS upgrade.
      if (!(url.startsWith('https://') &&
            redirectUrls.has(url.replace('https', 'http')))) {
        urlShortenerRedirects += 1;
      }
    }
  });
  return urlShortenerRedirects > 1;
};

/**
 * Determines whether the site redirects through a suspicious TLD.
 * @param {!Set<string>} redirectUrls A list of unique URLs redirected through
 *     to land on the current site.
 * @return {boolean} Whether the redirect chain includes URLs with a suspicious
 *     TLD.
 */
const redirectsThroughSuspiciousTld = (redirectUrls) => {
  // List of TLDs that have a high percentage of spammy or malicious domain
  // registrations.
  const suspiciousTlds = new Set([
    '.accountant', '.bid',    '.click',  '.cricket', '.date',  '.download',
    '.faith',      '.gdn',    '.kim',    '.loan',    '.men',   '.party',
    '.pro',        '.racing', '.review', '.science', '.space', '.stream',
    '.top',        '.trade',  '.win',    '.work',    '.xyz',
  ]);
  for (const url of redirectUrls) {
    const tld =
        '.' + Tld.getInstance().getTld(getDomain(url), /* icannOnly= */ false);
    if (suspiciousTlds.has(tld)) return true;
  }
  return false;
};

/**
 * Determines if redirect chain initiated from outside program or webmail.
 * @param {!Array<!chrome.safeBrowsingPrivate.ReferrerChainEntry>} redirectChain
 *     The redirect chain starting from the latest user interaction.
 * @return {boolean} Whether the redirect chain was initiated from an outside
 *     program or webmail.
 */
const redirectsFromOutsideProgramOrWebmail = (redirectChain) => {
  if (redirectChain.length === 0) return false;
  // Initial domain list--should be updated on a rolling basis.
  const webmailDomains = [
    'connect.xfinity.com',
    'en.mail.qq.com',
    'e.mail.ru',
    'mail.aol.com',
    'mail.google.com',
    'mail.yahoo.com',
    'outlook.office365.com',
    'outlook.live.com',
    'service.mail.com',
  ];
  // The last entry in the redirect chain should be the original referrer.
  const initiatingEntry = redirectChain[redirectChain.length - 1];
  if (initiatingEntry && initiatingEntry.referrerUrl) {
    for (const webmailDomain of webmailDomains) {
      // Don't just do an exact match because some webmail domains might
      // include additional subdomains, e.g. mg.mail.yahoo.com.
      if (getDomain(initiatingEntry.referrerUrl).endsWith(webmailDomain)) {
        return true;
      }
    }
  }
  // When maybeLaunchedByExternalApp is true, this means that there's a
  // possibility the entry was launched by an external application, but it might
  // be a false positive. If false, the entry definitely was not launched
  // by an external application.
  if (initiatingEntry.maybeLaunchedByExternalApp) return true;
  return false;
};

/**
 * Returns the redirect URLs from a referrer chain.
 * @param {!Array<!chrome.safeBrowsingPrivate.ReferrerChainEntry>} redirectChain
 *     The redirect chain starting from the latest user interaction.
 * @return {!Set<string>} A list of URLs redirected through to land
 *     on the current site, including the final page URL.
 */
const getRedirectUrls = (redirectChain) => {
  const redirectUrls = new Set();
  for (const referrerEntry of redirectChain) {
    if (referrerEntry.referrerUrl) {
      redirectUrls.add(referrerEntry.referrerUrl);
    }
    if (referrerEntry.serverRedirectChain) {
      referrerEntry.serverRedirectChain.forEach((serverRedirect) => {
        redirectUrls.add(serverRedirect.url);
      });
    }
  }
  return redirectUrls;
};


/**
 * Filters referrer entries to those resulting from the latest user interaction.
 * @param {string} url The URL of the current tab.
 * @param {number} tabId The ID of the tab for which to fetch the referrer.
 * @return {!Promise<!Array<!chrome.safeBrowsingPrivate.ReferrerChainEntry>>}
 *     The redirect chain, which is a list of referrer entries starting from
 *     immediately after the latest user action.
 */
const fetchRedirectChain = (url, tabId) => {
  const referrerEntries = [];
  if (chrome.safeBrowsingPrivate &&
      chrome.safeBrowsingPrivate.getReferrerChain) {
    return new Promise((resolve, reject) => {
      chrome.safeBrowsingPrivate.getReferrerChain(tabId, (referrer) => {
        // Microsoft Edge returns null for the referrer chain.
        if (referrer) {
          for (const referrerEntry of referrer) {
            // The referrer chain is returned in order of recency, so after
            // seeing the first referrer chain entry that no longer contains a
            // client redirect, break out of the loop since subsequent entries
            // likely came from a user interaction, e.g. typing URL into the
            // URL bar or clicking a link, and were not part of the relevant
            // stream of redirects.
            if (referrerEntry.urlType !== 'CLIENT_REDIRECT') break;
            referrerEntries.push(referrerEntry);
          }
        }
        resolve(referrerEntries);
      });
    });
  }
  return Promise.resolve(referrerEntries);
};

/**
 * Compute alerts and populate alerts array.
 * @param {string} url The URL of the page.
 * @param {number} tabId The ID of the current tab.
 * @return {!Promise<!Array<string>>} List of alerts for page.
 */
const computeAlerts = async (url, tabId) => {
  const newAlerts = [];
  const domain = getDomain(url).toLowerCase();
  const visited = await visitedBeforeToday(domain);
  const redirectChain = await fetchRedirectChain(domain, tabId);
  const redirectUrls = getRedirectUrls(redirectChain);
  // Only warn about IDNs, redirect chain initiated from outside program, and
  // unvisited sites when the final URL is not on a top site.
  if (!isTopSite(domain)) {
    if (isIDN(domain)) newAlerts.push(ALERT_MESSAGES['isIDN']);
    if (redirectChain && redirectsFromOutsideProgramOrWebmail(redirectChain))
      newAlerts.push(ALERT_MESSAGES['redirectsFromOutsideProgramOrWebmail']);
    if (!visited) newAlerts.push(ALERT_MESSAGES['notVisitedBefore']);
  }
  if (hasManySubdomains(domain))
    newAlerts.push(ALERT_MESSAGES['manySubdomains']);
  if (hasLongSubdomains(domain))
    newAlerts.push(ALERT_MESSAGES['longSubdomains']);
  if (hasMultipleUrlShortenerRedirects(redirectUrls))
    newAlerts.push(ALERT_MESSAGES['urlShortenerRedirects']);
  if (redirectsThroughSuspiciousTld(redirectUrls))
    newAlerts.push(ALERT_MESSAGES['redirectsThroughSuspiciousTld']);
  return Promise.resolve(newAlerts);
};

exports = {
  ALERT_MESSAGES,
  computeAlerts,
  fetchRedirectChain,
  getRedirectUrls,
  hasManySubdomains,
  hasMultipleUrlShortenerRedirects,
  hasLongSubdomains,
  isIDN,
  redirectsFromOutsideProgramOrWebmail,
  redirectsThroughSuspiciousTld,
  setTopSitesList,
  visitedBeforeToday,
};

;return exports;});

