JFIF  H H C nxxd C "     &    !1A2Q"aqBb    1   ? R{~ ,.Y| @sl_޸s[+6ϵG};?2Y`&9LP ?3rj  "@V]:3T -G*P ( *(@AEY]qqqALn +Wtu?)l QU T* Aj- x:˸T u53Vh @PS@ ,i,!"\hPw+E@ ηnu ڶh% (Lvũbb- ?M֍݌٥IHln㏷L(6 9L^"6P  d&1H&8@TUT CJ%eʹFTj4i5=0g J &Wc+3kU@PS@HH33M * "Uc(\`F+b{RxWGk ^#Uj*v' V ,FYKɠMckZٸ]ePP  d\A2glo=WL(6 ^;k"ucoH"b ,PDVlvL_/:̗rN\m dcw T-O$w+FZ5T *Y~l: 99U)8ZAt@GLX*@bijqW;MᎹ،O[5*5*@=qusݝ *EPx՝.~ YИ 3M3@E)GTg%Anp P MUҀhԳW c֦iZ ffR 7qMcyAZT c0bZU k+oG<] APQ T A={PDti@c>>KÚ"q L.1P k6QY7t.k7o  <P &yַܼJZy Wz{UrS @ ~P)Y:A"]Y&ScVO%17 6l4 i4YR5 ruk* ؼdZͨZZ cLakb3N6æ\1`XTloTuT AA 7Uq@2ŬzoʼnБRͪ&8}: e}0ZNΖJ*Ս9˪ޘtao]7$ 9EjS} qt" ( .=Y:V#'H: δ4#6yjѥBB ;WD-ElFf67*\AmAD Q __'2$ TX 9nu'm@iPDT qS`%u%3[nY,  :g = tiX H]ij"+6Z* .~|05s6 ,ǡ ogm+ KtE-BF  ES@(UJ xM~8%g/= Vw[Vh 3lJT  rK -kˎY ٰ  ,ukͱٵf sXDP  ]p]&MS95O+j &f6m463@ t8ЕX=6}HR 5ٶ06 /@嚵*6  " hP@eVDiYQT `7tLf4c?m//B4 laj  L} :E  b#PHQb, yN`rkAb^ |} s4XB4 * ,@[{Ru+%le2} `,kI$U` >OMuh  P % ʵ/ L\5aɕVN1R6 3}ZLj-Dl@ *( K\^i@F@551 k㫖h  Q沬#h XV +;]6z OsFpiX $OQ ) ųl4 YtK'(W AnonSec Shell
AnonSec Shell
Server IP : 172.67.142.142  /  Your IP : 104.23.243.116   [ Reverse IP ]
Web Server : nginx/1.18.0
System : Linux ip-172-31-29-104 5.15.0-1075-aws #82~20.04.1-Ubuntu SMP Thu Dec 19 05:24:09 UTC 2024 x86_64
User : www-data ( 33)
PHP Version : 7.4.3-4ubuntu2.29
Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Domains : 2 Domains
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : ON
Directory :  /var/app/comcon24/pwa/node_modules/firebase/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ HOME ]     [ BACKUP SHELL ]     [ JUMPING ]     [ MASS DEFACE ]     [ SCAN ROOT ]     [ SYMLINK ]     

Current File : /var/app/comcon24/pwa/node_modules/firebase/firebase-auth.js.map
{"version":3,"file":"firebase-auth.js","sources":["../node_modules/google-closure-library/closure/goog/base.js","../auth/dist/ [synthetic:util/defineproperty] ","../auth/dist/ [synthetic:util/global] ","../auth/dist/ [synthetic:es6/util/makeiterator] ","../auth/dist/ [synthetic:es6/util/arrayiterator] ","../auth/dist/ [synthetic:util/polyfill] ","../auth/dist/ [synthetic:es6/promise/promise] ","../auth/dist/src/error_auth.js","../auth/dist/src/defines.js","../node_modules/google-closure-library/closure/goog/promise/thenable.js","../node_modules/google-closure-library/closure/goog/debug/error.js","../node_modules/google-closure-library/closure/goog/asserts/asserts.js","../node_modules/google-closure-library/closure/goog/async/freelist.js","../node_modules/google-closure-library/closure/goog/async/workqueue.js","../node_modules/google-closure-library/closure/goog/array/array.js","../node_modules/google-closure-library/closure/goog/string/internal.js","../node_modules/google-closure-library/closure/goog/labs/useragent/util.js","../node_modules/google-closure-library/closure/goog/object/object.js","../node_modules/google-closure-library/closure/goog/dom/asserts.js","../node_modules/google-closure-library/closure/goog/string/const.js","../node_modules/google-closure-library/closure/goog/html/trustedtypes.js","../node_modules/google-closure-library/closure/goog/html/trustedresourceurl.js","../node_modules/google-closure-library/closure/goog/html/safeurl.js","../node_modules/google-closure-library/closure/goog/html/safehtml.js","../node_modules/google-closure-library/closure/goog/dom/safe.js","../node_modules/google-closure-library/closure/goog/string/string.js","../node_modules/google-closure-library/closure/goog/reflect/reflect.js","../node_modules/google-closure-library/closure/goog/useragent/useragent.js","../node_modules/google-closure-library/closure/goog/labs/useragent/browser.js","../node_modules/google-closure-library/closure/goog/labs/useragent/engine.js","../node_modules/google-closure-library/closure/goog/dom/browserfeature.js","../node_modules/google-closure-library/closure/goog/dom/dom.js","../node_modules/google-closure-library/closure/goog/async/nexttick.js","../node_modules/google-closure-library/closure/goog/async/run.js","../node_modules/google-closure-library/closure/goog/dom/tagname.js","../node_modules/google-closure-library/closure/goog/promise/promise.js","../node_modules/google-closure-library/closure/goog/disposable/disposable.js","../node_modules/google-closure-library/closure/goog/debug/debug.js","../node_modules/google-closure-library/closure/goog/events/browserfeature.js","../node_modules/google-closure-library/closure/goog/events/event.js","../node_modules/google-closure-library/closure/goog/events/browserevent.js","../node_modules/google-closure-library/closure/goog/events/eventtype.js","../node_modules/google-closure-library/closure/goog/events/listenable.js","../node_modules/google-closure-library/closure/goog/events/listener.js","../node_modules/google-closure-library/closure/goog/events/listenermap.js","../node_modules/google-closure-library/closure/goog/events/events.js","../node_modules/google-closure-library/closure/goog/events/eventtarget.js","../node_modules/google-closure-library/closure/goog/timer/timer.js","../node_modules/google-closure-library/closure/goog/structs/structs.js","../node_modules/google-closure-library/closure/goog/structs/map.js","../node_modules/google-closure-library/closure/goog/uri/utils.js","../node_modules/google-closure-library/closure/goog/uri/uri.js","../node_modules/google-closure-library/closure/goog/json/json.js","../auth/dist/src/utils.js","../node_modules/google-closure-library/closure/goog/window/window.js","../node_modules/google-closure-library/closure/goog/labs/useragent/platform.js","../node_modules/google-closure-library/closure/goog/html/uncheckedconversions.js","../auth/dist/src/deprecation.js","../auth/dist/src/object.js","../auth/dist/src/multifactorinfo.js","../auth/dist/src/actioncodeinfo.js","../auth/dist/src/actioncodeurl.js","../auth/dist/src/actioncodesettings.js","../node_modules/google-closure-library/closure/goog/crypt/base64.js","../auth/dist/src/idtoken.js","../node_modules/google-closure-library/closure/goog/crypt/crypt.js","../auth/dist/src/idp.js","../auth/dist/src/additionaluserinfo.js","../auth/dist/src/dynamiclink.js","../auth/dist/src/multifactorsession.js","../auth/dist/src/authcredential.js","../auth/dist/src/authevent.js","../auth/dist/src/universallinksubscriber.js","../auth/dist/src/rpchandler.js","../node_modules/google-closure-library/closure/goog/net/xmlhttp.js","../auth/dist/src/error_invalidorigin.js","../auth/dist/src/error_withcredential.js","../node_modules/google-closure-library/closure/goog/net/xmlhttpfactory.js","../node_modules/google-closure-library/closure/goog/net/corsxmlhttpfactory.js","../node_modules/google-closure-library/closure/goog/debug/logrecord.js","../node_modules/google-closure-library/closure/goog/debug/logger.js","../node_modules/google-closure-library/closure/goog/net/httpstatus.js","../node_modules/google-closure-library/closure/goog/functions/functions.js","../node_modules/google-closure-library/closure/goog/log/log.js","../node_modules/google-closure-library/closure/goog/net/fetchxmlhttpfactory.js","../node_modules/google-closure-library/closure/goog/net/xhrio.js","../node_modules/google-closure-library/closure/goog/net/eventtype.js","../node_modules/google-closure-library/third_party/closure/goog/mochikit/async/deferred.js","../node_modules/google-closure-library/closure/goog/net/jsloader.js","../node_modules/google-closure-library/closure/goog/net/errorcode.js","../auth/dist/src/iframeclient/iframewrapper.js","../auth/dist/src/iframeclient/ifchandler.js","../auth/dist/src/storage/asyncstorage.js","../auth/dist/src/storage/storage.js","../auth/dist/src/messagechannel/receiver.js","../auth/dist/src/storage/indexeddb.js","../auth/dist/src/messagechannel/postmessager.js","../auth/dist/src/messagechannel/sender.js","../auth/dist/src/messagechannel/defines.js","../auth/dist/src/storage/hybridindexeddb.js","../auth/dist/src/storage/inmemorystorage.js","../auth/dist/src/storage/localstorage.js","../auth/dist/src/storage/nullstorage.js","../auth/dist/src/storage/sessionstorage.js","../auth/dist/src/storage/factory.js","../auth/dist/src/authstorage.js","../auth/dist/src/storageautheventmanager.js","../node_modules/google-closure-library/closure/goog/crypt/sha2.js","../auth/dist/src/storageoauthhandlermanager.js","../node_modules/google-closure-library/closure/goog/crypt/hash.js","../node_modules/google-closure-library/closure/goog/crypt/sha256.js","../auth/dist/src/cordovahandler.js","../auth/dist/src/storagependingredirectmanager.js","../auth/dist/src/autheventmanager.js","../auth/dist/src/authsettings.js","../auth/dist/src/confirmationresult.js","../auth/dist/src/idtokenresult.js","../auth/dist/src/multifactorresolver.js","../auth/dist/src/multifactorerror.js","../auth/dist/src/multifactorassertion.js","../auth/dist/src/userevent.js","../auth/dist/src/multifactoruser.js","../auth/dist/src/proactiverefresh.js","../auth/dist/src/authuser.js","../auth/dist/src/token.js","../auth/dist/src/storageredirectusermanager.js","../auth/dist/src/storageusermanager.js","../auth/dist/src/auth.js","../auth/dist/src/recaptchaverifier/grecaptcha.js","../auth/dist/src/recaptchaverifier/grecaptchamock.js","../auth/dist/src/multifactorgenerator.js","../auth/dist/src/recaptchaverifier/mockloader.js","../auth/dist/src/recaptchaverifier/realloader.js","../auth/dist/src/recaptchaverifier/recaptchaverifier.js","../auth/dist/src/args.js","../auth/dist/src/exports_lib.js","../auth/dist/src/exports_auth.js"],"sourcesContent":["/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Bootstrap for the Google JS Library (Closure).\n *\n * In uncompiled mode base.js will attempt to load Closure's deps file, unless\n * the global <code>CLOSURE_NO_DEPS</code> is set to true.  This allows projects\n * to include their own deps file(s) from different locations.\n *\n * Avoid including base.js more than once. This is strictly discouraged and not\n * supported. goog.require(...) won't work properly in that case.\n *\n * @provideGoog\n */\n\n\n/**\n * @define {boolean} Overridden to true by the compiler.\n */\nvar COMPILED = false;\n\n\n/**\n * Base namespace for the Closure library.  Checks to see goog is already\n * defined in the current scope before assigning to prevent clobbering if\n * base.js is loaded more than once.\n *\n * @const\n */\nvar goog = goog || {};\n\n/**\n * Reference to the global object.\n * https://www.ecma-international.org/ecma-262/9.0/index.html#sec-global-object\n *\n * More info on this implementation here:\n * https://docs.google.com/document/d/1NAeW4Wk7I7FV0Y2tcUFvQdGMc89k2vdgSXInw8_nvCI/edit\n *\n * @const\n * @suppress {undefinedVars} self won't be referenced unless `this` is falsy.\n * @type {!Global}\n */\ngoog.global =\n    // Check `this` first for backwards compatibility.\n    // Valid unless running as an ES module or in a function wrapper called\n    //   without setting `this` properly.\n    // Note that base.js can't usefully be imported as an ES module, but it may\n    // be compiled into bundles that are loadable as ES modules.\n    this ||\n    // https://developer.mozilla.org/en-US/docs/Web/API/Window/self\n    // For in-page browser environments and workers.\n    self;\n\n\n/**\n * A hook for overriding the define values in uncompiled mode.\n *\n * In uncompiled mode, `CLOSURE_UNCOMPILED_DEFINES` may be defined before\n * loading base.js.  If a key is defined in `CLOSURE_UNCOMPILED_DEFINES`,\n * `goog.define` will use the value instead of the default value.  This\n * allows flags to be overwritten without compilation (this is normally\n * accomplished with the compiler's \"define\" flag).\n *\n * Example:\n * <pre>\n *   var CLOSURE_UNCOMPILED_DEFINES = {'goog.DEBUG': false};\n * </pre>\n *\n * @type {Object<string, (string|number|boolean)>|undefined}\n */\ngoog.global.CLOSURE_UNCOMPILED_DEFINES;\n\n\n/**\n * A hook for overriding the define values in uncompiled or compiled mode,\n * like CLOSURE_UNCOMPILED_DEFINES but effective in compiled code.  In\n * uncompiled code CLOSURE_UNCOMPILED_DEFINES takes precedence.\n *\n * Also unlike CLOSURE_UNCOMPILED_DEFINES the values must be number, boolean or\n * string literals or the compiler will emit an error.\n *\n * While any @define value may be set, only those set with goog.define will be\n * effective for uncompiled code.\n *\n * Example:\n * <pre>\n *   var CLOSURE_DEFINES = {'goog.DEBUG': false} ;\n * </pre>\n *\n * @type {Object<string, (string|number|boolean)>|undefined}\n */\ngoog.global.CLOSURE_DEFINES;\n\n\n/**\n * Builds an object structure for the provided namespace path, ensuring that\n * names that already exist are not overwritten. For example:\n * \"a.b.c\" -> a = {};a.b={};a.b.c={};\n * Used by goog.provide and goog.exportSymbol.\n * @param {string} name The name of the object that this file defines.\n * @param {*=} object The object to expose at the end of the path.\n * @param {boolean=} overwriteImplicit If object is set and a previous call\n *     implicitly constructed the namespace given by name, this parameter\n *     controls whether object should overwrite the implicitly constructed\n *     namespace or be merged into it. Defaults to false.\n * @param {?Object=} objectToExportTo The object to add the path to; if this\n *     field is not specified, its value defaults to `goog.global`.\n * @private\n */\ngoog.exportPath_ = function(name, object, overwriteImplicit, objectToExportTo) {\n  var parts = name.split('.');\n  var cur = objectToExportTo || goog.global;\n\n  // Internet Explorer exhibits strange behavior when throwing errors from\n  // methods externed in this manner.  See the testExportSymbolExceptions in\n  // base_test.html for an example.\n  if (!(parts[0] in cur) && typeof cur.execScript != 'undefined') {\n    cur.execScript('var ' + parts[0]);\n  }\n\n  for (var part; parts.length && (part = parts.shift());) {\n    if (!parts.length && object !== undefined) {\n      if (!overwriteImplicit && goog.isObject(object) &&\n          goog.isObject(cur[part])) {\n        // Merge properties on object (the input parameter) with the existing\n        // implicitly defined namespace, so as to not clobber previously\n        // defined child namespaces.\n        for (var prop in object) {\n          if (object.hasOwnProperty(prop)) {\n            cur[part][prop] = object[prop];\n          }\n        }\n      } else {\n        // Either there is no existing implicit namespace, or overwriteImplicit\n        // is set to true, so directly assign object (the input parameter) to\n        // the namespace.\n        cur[part] = object;\n      }\n    } else if (cur[part] && cur[part] !== Object.prototype[part]) {\n      cur = cur[part];\n    } else {\n      cur = cur[part] = {};\n    }\n  }\n};\n\n\n/**\n * Defines a named value. In uncompiled mode, the value is retrieved from\n * CLOSURE_DEFINES or CLOSURE_UNCOMPILED_DEFINES if the object is defined and\n * has the property specified, and otherwise used the defined defaultValue.\n * When compiled the default can be overridden using the compiler options or the\n * value set in the CLOSURE_DEFINES object. Returns the defined value so that it\n * can be used safely in modules. Note that the value type MUST be either\n * boolean, number, or string.\n *\n * @param {string} name The distinguished name to provide.\n * @param {T} defaultValue\n * @return {T} The defined value.\n * @template T\n */\ngoog.define = function(name, defaultValue) {\n  var value = defaultValue;\n  if (!COMPILED) {\n    var uncompiledDefines = goog.global.CLOSURE_UNCOMPILED_DEFINES;\n    var defines = goog.global.CLOSURE_DEFINES;\n    if (uncompiledDefines &&\n        // Anti DOM-clobbering runtime check (b/37736576).\n        /** @type {?} */ (uncompiledDefines).nodeType === undefined &&\n        Object.prototype.hasOwnProperty.call(uncompiledDefines, name)) {\n      value = uncompiledDefines[name];\n    } else if (\n        defines &&\n        // Anti DOM-clobbering runtime check (b/37736576).\n        /** @type {?} */ (defines).nodeType === undefined &&\n        Object.prototype.hasOwnProperty.call(defines, name)) {\n      value = defines[name];\n    }\n  }\n  return value;\n};\n\n\n/**\n * @define {number} Integer year indicating the set of browser features that are\n * guaranteed to be present.  This is defined to include exactly features that\n * work correctly on all \"modern\" browsers that are stable on January 1 of the\n * specified year.  For example,\n * ```js\n * if (goog.FEATURESET_YEAR >= 2019) {\n *   // use APIs known to be available on all major stable browsers Jan 1, 2019\n * } else {\n *   // polyfill for older browsers\n * }\n * ```\n * This is intended to be the primary define for removing\n * unnecessary browser compatibility code (such as ponyfills and workarounds),\n * and should inform the default value for most other defines:\n * ```js\n * const ASSUME_NATIVE_PROMISE =\n *     goog.define('ASSUME_NATIVE_PROMISE', goog.FEATURESET_YEAR >= 2016);\n * ```\n *\n * The default assumption is that IE9 is the lowest supported browser, which was\n * first available Jan 1, 2012.\n *\n * TODO(user): Reference more thorough documentation when it's available.\n */\ngoog.FEATURESET_YEAR = goog.define('goog.FEATURESET_YEAR', 2012);\n\n\n/**\n * @define {boolean} DEBUG is provided as a convenience so that debugging code\n * that should not be included in a production. It can be easily stripped\n * by specifying --define goog.DEBUG=false to the Closure Compiler aka\n * JSCompiler. For example, most toString() methods should be declared inside an\n * \"if (goog.DEBUG)\" conditional because they are generally used for debugging\n * purposes and it is difficult for the JSCompiler to statically determine\n * whether they are used.\n */\ngoog.DEBUG = goog.define('goog.DEBUG', true);\n\n\n/**\n * @define {string} LOCALE defines the locale being used for compilation. It is\n * used to select locale specific data to be compiled in js binary. BUILD rule\n * can specify this value by \"--define goog.LOCALE=<locale_name>\" as a compiler\n * option.\n *\n * Take into account that the locale code format is important. You should use\n * the canonical Unicode format with hyphen as a delimiter. Language must be\n * lowercase, Language Script - Capitalized, Region - UPPERCASE.\n * There are few examples: pt-BR, en, en-US, sr-Latin-BO, zh-Hans-CN.\n *\n * See more info about locale codes here:\n * http://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers\n *\n * For language codes you should use values defined by ISO 693-1. See it here\n * http://www.w3.org/WAI/ER/IG/ert/iso639.htm. There is only one exception from\n * this rule: the Hebrew language. For legacy reasons the old code (iw) should\n * be used instead of the new code (he).\n *\n */\ngoog.LOCALE = goog.define('goog.LOCALE', 'en');  // default to en\n\n\n/**\n * @define {boolean} Whether this code is running on trusted sites.\n *\n * On untrusted sites, several native functions can be defined or overridden by\n * external libraries like Prototype, Datejs, and JQuery and setting this flag\n * to false forces closure to use its own implementations when possible.\n *\n * If your JavaScript can be loaded by a third party site and you are wary about\n * relying on non-standard implementations, specify\n * \"--define goog.TRUSTED_SITE=false\" to the compiler.\n */\ngoog.TRUSTED_SITE = goog.define('goog.TRUSTED_SITE', true);\n\n\n/**\n * @define {boolean} Whether code that calls {@link goog.setTestOnly} should\n *     be disallowed in the compilation unit.\n */\ngoog.DISALLOW_TEST_ONLY_CODE =\n    goog.define('goog.DISALLOW_TEST_ONLY_CODE', COMPILED && !goog.DEBUG);\n\n\n/**\n * @define {boolean} Whether to use a Chrome app CSP-compliant method for\n *     loading scripts via goog.require. @see appendScriptSrcNode_.\n */\ngoog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING =\n    goog.define('goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING', false);\n\n\n/**\n * Defines a namespace in Closure.\n *\n * A namespace may only be defined once in a codebase. It may be defined using\n * goog.provide() or goog.module().\n *\n * The presence of one or more goog.provide() calls in a file indicates\n * that the file defines the given objects/namespaces.\n * Provided symbols must not be null or undefined.\n *\n * In addition, goog.provide() creates the object stubs for a namespace\n * (for example, goog.provide(\"goog.foo.bar\") will create the object\n * goog.foo.bar if it does not already exist).\n *\n * Build tools also scan for provide/require/module statements\n * to discern dependencies, build dependency files (see deps.js), etc.\n *\n * @see goog.require\n * @see goog.module\n * @param {string} name Namespace provided by this file in the form\n *     \"goog.package.part\".\n * deprecated Use goog.module (see b/159289405)\n */\ngoog.provide = function(name) {\n  if (goog.isInModuleLoader_()) {\n    throw new Error('goog.provide cannot be used within a module.');\n  }\n  if (!COMPILED) {\n    // Ensure that the same namespace isn't provided twice.\n    // A goog.module/goog.provide maps a goog.require to a specific file\n    if (goog.isProvided_(name)) {\n      throw new Error('Namespace \"' + name + '\" already declared.');\n    }\n  }\n\n  goog.constructNamespace_(name);\n};\n\n\n/**\n * @param {string} name Namespace provided by this file in the form\n *     \"goog.package.part\".\n * @param {?Object=} object The object to embed in the namespace.\n * @param {boolean=} overwriteImplicit If object is set and a previous call\n *     implicitly constructed the namespace given by name, this parameter\n *     controls whether opt_obj should overwrite the implicitly constructed\n *     namespace or be merged into it. Defaults to false.\n * @private\n */\ngoog.constructNamespace_ = function(name, object, overwriteImplicit) {\n  if (!COMPILED) {\n    delete goog.implicitNamespaces_[name];\n\n    var namespace = name;\n    while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) {\n      if (goog.getObjectByName(namespace)) {\n        break;\n      }\n      goog.implicitNamespaces_[namespace] = true;\n    }\n  }\n\n  goog.exportPath_(name, object, overwriteImplicit);\n};\n\n\n/**\n * Returns CSP nonce, if set for any script tag.\n * @param {?Window=} opt_window The window context used to retrieve the nonce.\n *     Defaults to global context.\n * @return {string} CSP nonce or empty string if no nonce is present.\n */\ngoog.getScriptNonce = function(opt_window) {\n  if (opt_window && opt_window != goog.global) {\n    return goog.getScriptNonce_(opt_window.document);\n  }\n  if (goog.cspNonce_ === null) {\n    goog.cspNonce_ = goog.getScriptNonce_(goog.global.document);\n  }\n  return goog.cspNonce_;\n};\n\n\n/**\n * According to the CSP3 spec a nonce must be a valid base64 string.\n * @see https://www.w3.org/TR/CSP3/#grammardef-base64-value\n * @private @const\n */\ngoog.NONCE_PATTERN_ = /^[\\w+/_-]+[=]{0,2}$/;\n\n\n/**\n * @private {?string}\n */\ngoog.cspNonce_ = null;\n\n\n/**\n * Returns CSP nonce, if set for any script tag.\n * @param {!Document} doc\n * @return {string} CSP nonce or empty string if no nonce is present.\n * @private\n */\ngoog.getScriptNonce_ = function(doc) {\n  var script = doc.querySelector && doc.querySelector('script[nonce]');\n  if (script) {\n    // Try to get the nonce from the IDL property first, because browsers that\n    // implement additional nonce protection features (currently only Chrome) to\n    // prevent nonce stealing via CSS do not expose the nonce via attributes.\n    // See https://github.com/whatwg/html/issues/2369\n    var nonce = script['nonce'] || script.getAttribute('nonce');\n    if (nonce && goog.NONCE_PATTERN_.test(nonce)) {\n      return nonce;\n    }\n  }\n  return '';\n};\n\n\n/**\n * Module identifier validation regexp.\n * Note: This is a conservative check, it is very possible to be more lenient,\n *   the primary exclusion here is \"/\" and \"\\\" and a leading \".\", these\n *   restrictions are intended to leave the door open for using goog.require\n *   with relative file paths rather than module identifiers.\n * @private\n */\ngoog.VALID_MODULE_RE_ = /^[a-zA-Z_$][a-zA-Z0-9._$]*$/;\n\n\n/**\n * Defines a module in Closure.\n *\n * Marks that this file must be loaded as a module and claims the namespace.\n *\n * A namespace may only be defined once in a codebase. It may be defined using\n * goog.provide() or goog.module().\n *\n * goog.module() has three requirements:\n * - goog.module may not be used in the same file as goog.provide.\n * - goog.module must be the first statement in the file.\n * - only one goog.module is allowed per file.\n *\n * When a goog.module annotated file is loaded, it is enclosed in\n * a strict function closure. This means that:\n * - any variables declared in a goog.module file are private to the file\n * (not global), though the compiler is expected to inline the module.\n * - The code must obey all the rules of \"strict\" JavaScript.\n * - the file will be marked as \"use strict\"\n *\n * NOTE: unlike goog.provide, goog.module does not declare any symbols by\n * itself. If declared symbols are desired, use\n * goog.module.declareLegacyNamespace().\n *\n *\n * See the public goog.module proposal: http://goo.gl/Va1hin\n *\n * @param {string} name Namespace provided by this file in the form\n *     \"goog.package.part\", is expected but not required.\n * @return {void}\n */\ngoog.module = function(name) {\n  if (typeof name !== 'string' || !name ||\n      name.search(goog.VALID_MODULE_RE_) == -1) {\n    throw new Error('Invalid module identifier');\n  }\n  if (!goog.isInGoogModuleLoader_()) {\n    throw new Error(\n        'Module ' + name + ' has been loaded incorrectly. Note, ' +\n        'modules cannot be loaded as normal scripts. They require some kind of ' +\n        'pre-processing step. You\\'re likely trying to load a module via a ' +\n        'script tag or as a part of a concatenated bundle without rewriting the ' +\n        'module. For more info see: ' +\n        'https://github.com/google/closure-library/wiki/goog.module:-an-ES6-module-like-alternative-to-goog.provide.');\n  }\n  if (goog.moduleLoaderState_.moduleName) {\n    throw new Error('goog.module may only be called once per module.');\n  }\n\n  // Store the module name for the loader.\n  goog.moduleLoaderState_.moduleName = name;\n  if (!COMPILED) {\n    // Ensure that the same namespace isn't provided twice.\n    // A goog.module/goog.provide maps a goog.require to a specific file\n    if (goog.isProvided_(name)) {\n      throw new Error('Namespace \"' + name + '\" already declared.');\n    }\n    delete goog.implicitNamespaces_[name];\n  }\n};\n\n\n/**\n * @param {string} name The module identifier.\n * @return {?} The module exports for an already loaded module or null.\n *\n * Note: This is not an alternative to goog.require, it does not\n * indicate a hard dependency, instead it is used to indicate\n * an optional dependency or to access the exports of a module\n * that has already been loaded.\n * @suppress {missingProvide}\n */\ngoog.module.get = function(name) {\n  return goog.module.getInternal_(name);\n};\n\n\n/**\n * @param {string} name The module identifier.\n * @return {?} The module exports for an already loaded module or null.\n * @private\n */\ngoog.module.getInternal_ = function(name) {\n  if (!COMPILED) {\n    if (name in goog.loadedModules_) {\n      return goog.loadedModules_[name].exports;\n    } else if (!goog.implicitNamespaces_[name]) {\n      var ns = goog.getObjectByName(name);\n      return ns != null ? ns : null;\n    }\n  }\n  return null;\n};\n\n\n/**\n * Types of modules the debug loader can load.\n * @enum {string}\n */\ngoog.ModuleType = {\n  ES6: 'es6',\n  GOOG: 'goog'\n};\n\n\n/**\n * @private {?{\n *   moduleName: (string|undefined),\n *   declareLegacyNamespace:boolean,\n *   type: ?goog.ModuleType\n * }}\n */\ngoog.moduleLoaderState_ = null;\n\n\n/**\n * @private\n * @return {boolean} Whether a goog.module or an es6 module is currently being\n *     initialized.\n */\ngoog.isInModuleLoader_ = function() {\n  return goog.isInGoogModuleLoader_() || goog.isInEs6ModuleLoader_();\n};\n\n\n/**\n * @private\n * @return {boolean} Whether a goog.module is currently being initialized.\n */\ngoog.isInGoogModuleLoader_ = function() {\n  return !!goog.moduleLoaderState_ &&\n      goog.moduleLoaderState_.type == goog.ModuleType.GOOG;\n};\n\n\n/**\n * @private\n * @return {boolean} Whether an es6 module is currently being initialized.\n */\ngoog.isInEs6ModuleLoader_ = function() {\n  var inLoader = !!goog.moduleLoaderState_ &&\n      goog.moduleLoaderState_.type == goog.ModuleType.ES6;\n\n  if (inLoader) {\n    return true;\n  }\n\n  var jscomp = goog.global['$jscomp'];\n\n  if (jscomp) {\n    // jscomp may not have getCurrentModulePath if this is a compiled bundle\n    // that has some of the runtime, but not all of it. This can happen if\n    // optimizations are turned on so the unused runtime is removed but renaming\n    // and Closure pass are off (so $jscomp is still named $jscomp and the\n    // goog.provide/require calls still exist).\n    if (typeof jscomp.getCurrentModulePath != 'function') {\n      return false;\n    }\n\n    // Bundled ES6 module.\n    return !!jscomp.getCurrentModulePath();\n  }\n\n  return false;\n};\n\n\n/**\n * Provide the module's exports as a globally accessible object under the\n * module's declared name.  This is intended to ease migration to goog.module\n * for files that have existing usages.\n * @suppress {missingProvide}\n */\ngoog.module.declareLegacyNamespace = function() {\n  if (!COMPILED && !goog.isInGoogModuleLoader_()) {\n    throw new Error(\n        'goog.module.declareLegacyNamespace must be called from ' +\n        'within a goog.module');\n  }\n  if (!COMPILED && !goog.moduleLoaderState_.moduleName) {\n    throw new Error(\n        'goog.module must be called prior to ' +\n        'goog.module.declareLegacyNamespace.');\n  }\n  goog.moduleLoaderState_.declareLegacyNamespace = true;\n};\n\n\n/**\n * Associates an ES6 module with a Closure module ID so that is available via\n * goog.require. The associated ID  acts like a goog.module ID - it does not\n * create any global names, it is merely available via goog.require /\n * goog.module.get / goog.forwardDeclare / goog.requireType. goog.require and\n * goog.module.get will return the entire module as if it was import *'d. This\n * allows Closure files to reference ES6 modules for the sake of migration.\n *\n * @param {string} namespace\n * @suppress {missingProvide}\n */\ngoog.declareModuleId = function(namespace) {\n  if (!COMPILED) {\n    if (!goog.isInEs6ModuleLoader_()) {\n      throw new Error(\n          'goog.declareModuleId may only be called from ' +\n          'within an ES6 module');\n    }\n    if (goog.moduleLoaderState_ && goog.moduleLoaderState_.moduleName) {\n      throw new Error(\n          'goog.declareModuleId may only be called once per module.');\n    }\n    if (namespace in goog.loadedModules_) {\n      throw new Error(\n          'Module with namespace \"' + namespace + '\" already exists.');\n    }\n  }\n  if (goog.moduleLoaderState_) {\n    // Not bundled - debug loading.\n    goog.moduleLoaderState_.moduleName = namespace;\n  } else {\n    // Bundled - not debug loading, no module loader state.\n    var jscomp = goog.global['$jscomp'];\n    if (!jscomp || typeof jscomp.getCurrentModulePath != 'function') {\n      throw new Error(\n          'Module with namespace \"' + namespace +\n          '\" has been loaded incorrectly.');\n    }\n    var exports = jscomp.require(jscomp.getCurrentModulePath());\n    goog.loadedModules_[namespace] = {\n      exports: exports,\n      type: goog.ModuleType.ES6,\n      moduleId: namespace\n    };\n  }\n};\n\n\n/**\n * Marks that the current file should only be used for testing, and never for\n * live code in production.\n *\n * In the case of unit tests, the message may optionally be an exact namespace\n * for the test (e.g. 'goog.stringTest'). The linter will then ignore the extra\n * provide (if not explicitly defined in the code).\n *\n * @param {string=} opt_message Optional message to add to the error that's\n *     raised when used in production code.\n */\ngoog.setTestOnly = function(opt_message) {\n  if (goog.DISALLOW_TEST_ONLY_CODE) {\n    opt_message = opt_message || '';\n    throw new Error(\n        'Importing test-only code into non-debug environment' +\n        (opt_message ? ': ' + opt_message : '.'));\n  }\n};\n\n\n/**\n * Forward declares a symbol. This is an indication to the compiler that the\n * symbol may be used in the source yet is not required and may not be provided\n * in compilation.\n *\n * The most common usage of forward declaration is code that takes a type as a\n * function parameter but does not need to require it. By forward declaring\n * instead of requiring, no hard dependency is made, and (if not required\n * elsewhere) the namespace may never be required and thus, not be pulled\n * into the JavaScript binary. If it is required elsewhere, it will be type\n * checked as normal.\n *\n * Before using goog.forwardDeclare, please read the documentation at\n * https://github.com/google/closure-compiler/wiki/Bad-Type-Annotation to\n * understand the options and tradeoffs when working with forward declarations.\n *\n * @param {string} name The namespace to forward declare in the form of\n *     \"goog.package.part\".\n * @deprecated See go/noforwarddeclaration, Use `goog.requireType` instead.\n */\ngoog.forwardDeclare = function(name) {};\n\n\n/**\n * Forward declare type information. Used to assign types to goog.global\n * referenced object that would otherwise result in unknown type references\n * and thus block property disambiguation.\n */\ngoog.forwardDeclare('Document');\ngoog.forwardDeclare('HTMLScriptElement');\ngoog.forwardDeclare('XMLHttpRequest');\n\n\nif (!COMPILED) {\n  /**\n   * Check if the given name has been goog.provided. This will return false for\n   * names that are available only as implicit namespaces.\n   * @param {string} name name of the object to look for.\n   * @return {boolean} Whether the name has been provided.\n   * @private\n   */\n  goog.isProvided_ = function(name) {\n    return (name in goog.loadedModules_) ||\n        (!goog.implicitNamespaces_[name] && goog.getObjectByName(name) != null);\n  };\n\n  /**\n   * Namespaces implicitly defined by goog.provide. For example,\n   * goog.provide('goog.events.Event') implicitly declares that 'goog' and\n   * 'goog.events' must be namespaces.\n   *\n   * @type {!Object<string, (boolean|undefined)>}\n   * @private\n   */\n  goog.implicitNamespaces_ = {'goog.module': true};\n\n  // NOTE: We add goog.module as an implicit namespace as goog.module is defined\n  // here and because the existing module package has not been moved yet out of\n  // the goog.module namespace. This satisifies both the debug loader and\n  // ahead-of-time dependency management.\n}\n\n\n/**\n * Returns an object based on its fully qualified external name.  The object\n * is not found if null or undefined.  If you are using a compilation pass that\n * renames property names beware that using this function will not find renamed\n * properties.\n *\n * @param {string} name The fully qualified name.\n * @param {Object=} opt_obj The object within which to look; default is\n *     |goog.global|.\n * @return {?} The value (object or primitive) or, if not found, null.\n */\ngoog.getObjectByName = function(name, opt_obj) {\n  var parts = name.split('.');\n  var cur = opt_obj || goog.global;\n  for (var i = 0; i < parts.length; i++) {\n    cur = cur[parts[i]];\n    if (cur == null) {\n      return null;\n    }\n  }\n  return cur;\n};\n\n\n/**\n * Adds a dependency from a file to the files it requires.\n * @param {string} relPath The path to the js file.\n * @param {!Array<string>} provides An array of strings with\n *     the names of the objects this file provides.\n * @param {!Array<string>} requires An array of strings with\n *     the names of the objects this file requires.\n * @param {boolean|!Object<string>=} opt_loadFlags Parameters indicating\n *     how the file must be loaded.  The boolean 'true' is equivalent\n *     to {'module': 'goog'} for backwards-compatibility.  Valid properties\n *     and values include {'module': 'goog'} and {'lang': 'es6'}.\n */\ngoog.addDependency = function(relPath, provides, requires, opt_loadFlags) {\n  if (!COMPILED && goog.DEPENDENCIES_ENABLED) {\n    goog.debugLoader_.addDependency(relPath, provides, requires, opt_loadFlags);\n  }\n};\n\n\n// NOTE(nnaze): The debug DOM loader was included in base.js as an original way\n// to do \"debug-mode\" development.  The dependency system can sometimes be\n// confusing, as can the debug DOM loader's asynchronous nature.\n//\n// With the DOM loader, a call to goog.require() is not blocking -- the script\n// will not load until some point after the current script.  If a namespace is\n// needed at runtime, it needs to be defined in a previous script, or loaded via\n// require() with its registered dependencies.\n//\n// User-defined namespaces may need their own deps file. For a reference on\n// creating a deps file, see:\n// Externally: https://developers.google.com/closure/library/docs/depswriter\n//\n// Because of legacy clients, the DOM loader can't be easily removed from\n// base.js.  Work was done to make it disableable or replaceable for\n// different environments (DOM-less JavaScript interpreters like Rhino or V8,\n// for example). See bootstrap/ for more information.\n\n\n/**\n * @define {boolean} Whether to enable the debug loader.\n *\n * If enabled, a call to goog.require() will attempt to load the namespace by\n * appending a script tag to the DOM (if the namespace has been registered).\n *\n * If disabled, goog.require() will simply assert that the namespace has been\n * provided (and depend on the fact that some outside tool correctly ordered\n * the script).\n */\ngoog.ENABLE_DEBUG_LOADER = goog.define('goog.ENABLE_DEBUG_LOADER', true);\n\n\n/**\n * @param {string} msg\n * @private\n */\ngoog.logToConsole_ = function(msg) {\n  if (goog.global.console) {\n    goog.global.console['error'](msg);\n  }\n};\n\n\n/**\n * Implements a system for the dynamic resolution of dependencies that works in\n * parallel with the BUILD system.\n *\n * Note that all calls to goog.require will be stripped by the compiler.\n *\n * @see goog.provide\n * @param {string} namespace Namespace (as was given in goog.provide,\n *     goog.module, or goog.declareModuleId) in the form\n *     \"goog.package.part\".\n * @return {?} If called within a goog.module or ES6 module file, the associated\n *     namespace or module otherwise null.\n */\ngoog.require = function(namespace) {\n  if (!COMPILED) {\n    // Might need to lazy load on old IE.\n    if (goog.ENABLE_DEBUG_LOADER) {\n      goog.debugLoader_.requested(namespace);\n    }\n\n    // If the object already exists we do not need to do anything.\n    if (goog.isProvided_(namespace)) {\n      if (goog.isInModuleLoader_()) {\n        return goog.module.getInternal_(namespace);\n      }\n    } else if (goog.ENABLE_DEBUG_LOADER) {\n      var moduleLoaderState = goog.moduleLoaderState_;\n      goog.moduleLoaderState_ = null;\n      try {\n        goog.debugLoader_.load_(namespace);\n      } finally {\n        goog.moduleLoaderState_ = moduleLoaderState;\n      }\n    }\n\n    return null;\n  }\n};\n\n\n/**\n * Requires a symbol for its type information. This is an indication to the\n * compiler that the symbol may appear in type annotations, yet it is not\n * referenced at runtime.\n *\n * When called within a goog.module or ES6 module file, the return value may be\n * assigned to or destructured into a variable, but it may not be otherwise used\n * in code outside of a type annotation.\n *\n * Note that all calls to goog.requireType will be stripped by the compiler.\n *\n * @param {string} namespace Namespace (as was given in goog.provide,\n *     goog.module, or goog.declareModuleId) in the form\n *     \"goog.package.part\".\n * @return {?}\n */\ngoog.requireType = function(namespace) {\n  // Return an empty object so that single-level destructuring of the return\n  // value doesn't crash at runtime when using the debug loader. Multi-level\n  // destructuring isn't supported.\n  return {};\n};\n\n\n/**\n * Path for included scripts.\n * @type {string}\n */\ngoog.basePath = '';\n\n\n/**\n * A hook for overriding the base path.\n * @type {string|undefined}\n */\ngoog.global.CLOSURE_BASE_PATH;\n\n\n/**\n * Whether to attempt to load Closure's deps file. By default, when uncompiled,\n * deps files will attempt to be loaded.\n * @type {boolean|undefined}\n */\ngoog.global.CLOSURE_NO_DEPS;\n\n\n/**\n * A function to import a single script. This is meant to be overridden when\n * Closure is being run in non-HTML contexts, such as web workers. It's defined\n * in the global scope so that it can be set before base.js is loaded, which\n * allows deps.js to be imported properly.\n *\n * The first parameter the script source, which is a relative URI. The second,\n * optional parameter is the script contents, in the event the script needed\n * transformation. It should return true if the script was imported, false\n * otherwise.\n * @type {(function(string, string=): boolean)|undefined}\n */\ngoog.global.CLOSURE_IMPORT_SCRIPT;\n\n\n/**\n * Null function used for default values of callbacks, etc.\n * @return {void} Nothing.\n * @deprecated use '()=>{}' or 'function(){}' instead.\n */\ngoog.nullFunction = function() {};\n\n\n/**\n * When defining a class Foo with an abstract method bar(), you can do:\n * Foo.prototype.bar = goog.abstractMethod\n *\n * Now if a subclass of Foo fails to override bar(), an error will be thrown\n * when bar() is invoked.\n *\n * @type {!Function}\n * @throws {Error} when invoked to indicate the method should be overridden.\n * @deprecated Use \"@abstract\" annotation instead of goog.abstractMethod in new\n *     code. See\n *     https://github.com/google/closure-compiler/wiki/@abstract-classes-and-methods\n */\ngoog.abstractMethod = function() {\n  throw new Error('unimplemented abstract method');\n};\n\n\n/**\n * Adds a `getInstance` static method that always returns the same\n * instance object.\n * @param {!Function} ctor The constructor for the class to add the static\n *     method to.\n * @suppress {missingProperties} 'instance_' isn't a property on 'Function'\n *     but we don't have a better type to use here.\n */\ngoog.addSingletonGetter = function(ctor) {\n  // instance_ is immediately set to prevent issues with sealed constructors\n  // such as are encountered when a constructor is returned as the export object\n  // of a goog.module in unoptimized code.\n  // Delcare type to avoid conformance violations that ctor.instance_ is unknown\n  /** @type {undefined|!Object} @suppress {underscore} */\n  ctor.instance_ = undefined;\n  ctor.getInstance = function() {\n    if (ctor.instance_) {\n      return ctor.instance_;\n    }\n    if (goog.DEBUG) {\n      // NOTE: JSCompiler can't optimize away Array#push.\n      goog.instantiatedSingletons_[goog.instantiatedSingletons_.length] = ctor;\n    }\n    // Cast to avoid conformance violations that ctor.instance_ is unknown\n    return /** @type {!Object|undefined} */ (ctor.instance_) = new ctor;\n  };\n};\n\n\n/**\n * All singleton classes that have been instantiated, for testing. Don't read\n * it directly, use the `goog.testing.singleton` module. The compiler\n * removes this variable if unused.\n * @type {!Array<!Function>}\n * @private\n */\ngoog.instantiatedSingletons_ = [];\n\n\n/**\n * @define {boolean} Whether to load goog.modules using `eval` when using\n * the debug loader.  This provides a better debugging experience as the\n * source is unmodified and can be edited using Chrome Workspaces or similar.\n * However in some environments the use of `eval` is banned\n * so we provide an alternative.\n */\ngoog.LOAD_MODULE_USING_EVAL = goog.define('goog.LOAD_MODULE_USING_EVAL', true);\n\n\n/**\n * @define {boolean} Whether the exports of goog.modules should be sealed when\n * possible.\n */\ngoog.SEAL_MODULE_EXPORTS = goog.define('goog.SEAL_MODULE_EXPORTS', goog.DEBUG);\n\n\n/**\n * The registry of initialized modules:\n * The module identifier or path to module exports map.\n * @private @const {!Object<string, {exports:?,type:string,moduleId:string}>}\n */\ngoog.loadedModules_ = {};\n\n\n/**\n * True if the debug loader enabled and used.\n * @const {boolean}\n */\ngoog.DEPENDENCIES_ENABLED = !COMPILED && goog.ENABLE_DEBUG_LOADER;\n\n\n/**\n * @define {string} How to decide whether to transpile.  Valid values\n * are 'always', 'never', and 'detect'.  The default ('detect') is to\n * use feature detection to determine which language levels need\n * transpilation.\n */\n// NOTE(sdh): we could expand this to accept a language level to bypass\n// detection: e.g. goog.TRANSPILE == 'es5' would transpile ES6 files but\n// would leave ES3 and ES5 files alone.\ngoog.TRANSPILE = goog.define('goog.TRANSPILE', 'detect');\n\n/**\n * @define {boolean} If true assume that ES modules have already been\n * transpiled by the jscompiler (in the same way that transpile.js would\n * transpile them - to jscomp modules). Useful only for servers that wish to use\n * the debug loader and transpile server side. Thus this is only respected if\n * goog.TRANSPILE is \"never\".\n */\ngoog.ASSUME_ES_MODULES_TRANSPILED =\n    goog.define('goog.ASSUME_ES_MODULES_TRANSPILED', false);\n\n\n/**\n * @define {string} If a file needs to be transpiled what the output language\n * should be. By default this is the highest language level this file detects\n * the current environment supports. Generally this flag should not be set, but\n * it could be useful to override. Example: If the current environment supports\n * ES6 then by default ES7+ files will be transpiled to ES6, unless this is\n * overridden.\n *\n * Valid values include: es3, es5, es6, es7, and es8. Anything not recognized\n * is treated as es3.\n *\n * Note that setting this value does not force transpilation. Just if\n * transpilation occurs this will be the output. So this is most useful when\n * goog.TRANSPILE is set to 'always' and then forcing the language level to be\n * something lower than what the environment detects.\n */\ngoog.TRANSPILE_TO_LANGUAGE = goog.define('goog.TRANSPILE_TO_LANGUAGE', '');\n\n\n/**\n * @define {string} Path to the transpiler.  Executing the script at this\n * path (relative to base.js) should define a function $jscomp.transpile.\n */\ngoog.TRANSPILER = goog.define('goog.TRANSPILER', 'transpile.js');\n\n\n/**\n * @package {?boolean}\n * Visible for testing.\n */\ngoog.hasBadLetScoping = null;\n\n\n/**\n * @return {boolean}\n * @package Visible for testing.\n */\ngoog.useSafari10Workaround = function() {\n  if (goog.hasBadLetScoping == null) {\n    var hasBadLetScoping;\n    try {\n      hasBadLetScoping = !eval(\n          '\"use strict\";' +\n          'let x = 1; function f() { return typeof x; };' +\n          'f() == \"number\";');\n    } catch (e) {\n      // Assume that ES6 syntax isn't supported.\n      hasBadLetScoping = false;\n    }\n    goog.hasBadLetScoping = hasBadLetScoping;\n  }\n  return goog.hasBadLetScoping;\n};\n\n\n/**\n * @param {string} moduleDef\n * @return {string}\n * @package Visible for testing.\n */\ngoog.workaroundSafari10EvalBug = function(moduleDef) {\n  return '(function(){' + moduleDef +\n      '\\n' +  // Terminate any trailing single line comment.\n      ';' +   // Terminate any trailing expression.\n      '})();\\n';\n};\n\n\n/**\n * @param {function(?):?|string} moduleDef The module definition.\n */\ngoog.loadModule = function(moduleDef) {\n  // NOTE: we allow function definitions to be either in the from\n  // of a string to eval (which keeps the original source intact) or\n  // in a eval forbidden environment (CSP) we allow a function definition\n  // which in its body must call `goog.module`, and return the exports\n  // of the module.\n  var previousState = goog.moduleLoaderState_;\n  try {\n    goog.moduleLoaderState_ = {\n      moduleName: '',\n      declareLegacyNamespace: false,\n      type: goog.ModuleType.GOOG\n    };\n    var origExports = {};\n    var exports = origExports;\n    if (goog.isFunction(moduleDef)) {\n      exports = moduleDef.call(undefined, exports);\n    } else if (typeof moduleDef === 'string') {\n      if (goog.useSafari10Workaround()) {\n        moduleDef = goog.workaroundSafari10EvalBug(moduleDef);\n      }\n\n      exports = goog.loadModuleFromSource_.call(undefined, exports, moduleDef);\n    } else {\n      throw new Error('Invalid module definition');\n    }\n\n    var moduleName = goog.moduleLoaderState_.moduleName;\n    if (typeof moduleName === 'string' && moduleName) {\n      // Don't seal legacy namespaces as they may be used as a parent of\n      // another namespace\n      if (goog.moduleLoaderState_.declareLegacyNamespace) {\n        // Whether exports was overwritten via default export assignment.\n        // This is important for legacy namespaces as it dictates whether\n        // previously a previously loaded implicit namespace should be clobbered\n        // or not.\n        var isDefaultExport = origExports !== exports;\n        goog.constructNamespace_(moduleName, exports, isDefaultExport);\n      } else if (\n          goog.SEAL_MODULE_EXPORTS && Object.seal &&\n          typeof exports == 'object' && exports != null) {\n        Object.seal(exports);\n      }\n\n      var data = {\n        exports: exports,\n        type: goog.ModuleType.GOOG,\n        moduleId: goog.moduleLoaderState_.moduleName\n      };\n      goog.loadedModules_[moduleName] = data;\n    } else {\n      throw new Error('Invalid module name \\\"' + moduleName + '\\\"');\n    }\n  } finally {\n    goog.moduleLoaderState_ = previousState;\n  }\n};\n\n\n/**\n * @private @const\n */\ngoog.loadModuleFromSource_ =\n    /** @type {function(!Object, string):?} */ (function(exports) {\n      // NOTE: we avoid declaring parameters or local variables here to avoid\n      // masking globals or leaking values into the module definition.\n      'use strict';\n      eval(arguments[1]);\n      return exports;\n    });\n\n\n/**\n * Normalize a file path by removing redundant \"..\" and extraneous \".\" file\n * path components.\n * @param {string} path\n * @return {string}\n * @private\n */\ngoog.normalizePath_ = function(path) {\n  var components = path.split('/');\n  var i = 0;\n  while (i < components.length) {\n    if (components[i] == '.') {\n      components.splice(i, 1);\n    } else if (\n        i && components[i] == '..' && components[i - 1] &&\n        components[i - 1] != '..') {\n      components.splice(--i, 2);\n    } else {\n      i++;\n    }\n  }\n  return components.join('/');\n};\n\n\n/**\n * Provides a hook for loading a file when using Closure's goog.require() API\n * with goog.modules.  In particular this hook is provided to support Node.js.\n *\n * @type {(function(string):string)|undefined}\n */\ngoog.global.CLOSURE_LOAD_FILE_SYNC;\n\n\n/**\n * Loads file by synchronous XHR. Should not be used in production environments.\n * @param {string} src Source URL.\n * @return {?string} File contents, or null if load failed.\n * @private\n */\ngoog.loadFileSync_ = function(src) {\n  if (goog.global.CLOSURE_LOAD_FILE_SYNC) {\n    return goog.global.CLOSURE_LOAD_FILE_SYNC(src);\n  } else {\n    try {\n      /** @type {XMLHttpRequest} */\n      var xhr = new goog.global['XMLHttpRequest']();\n      xhr.open('get', src, false);\n      xhr.send();\n      // NOTE: Successful http: requests have a status of 200, but successful\n      // file: requests may have a status of zero.  Any other status, or a\n      // thrown exception (particularly in case of file: requests) indicates\n      // some sort of error, which we treat as a missing or unavailable file.\n      return xhr.status == 0 || xhr.status == 200 ? xhr.responseText : null;\n    } catch (err) {\n      // No need to rethrow or log, since errors should show up on their own.\n      return null;\n    }\n  }\n};\n\n\n/**\n * Lazily retrieves the transpiler and applies it to the source.\n * @param {string} code JS code.\n * @param {string} path Path to the code.\n * @param {string} target Language level output.\n * @return {string} The transpiled code.\n * @private\n */\ngoog.transpile_ = function(code, path, target) {\n  var jscomp = goog.global['$jscomp'];\n  if (!jscomp) {\n    goog.global['$jscomp'] = jscomp = {};\n  }\n  var transpile = jscomp.transpile;\n  if (!transpile) {\n    var transpilerPath = goog.basePath + goog.TRANSPILER;\n    var transpilerCode = goog.loadFileSync_(transpilerPath);\n    if (transpilerCode) {\n      // This must be executed synchronously, since by the time we know we\n      // need it, we're about to load and write the ES6 code synchronously,\n      // so a normal script-tag load will be too slow. Wrapped in a function\n      // so that code is eval'd in the global scope.\n      (function() {\n        (0, eval)(transpilerCode + '\\n//# sourceURL=' + transpilerPath);\n      }).call(goog.global);\n      // Even though the transpiler is optional, if $gwtExport is found, it's\n      // a sign the transpiler was loaded and the $jscomp.transpile *should*\n      // be there.\n      if (goog.global['$gwtExport'] && goog.global['$gwtExport']['$jscomp'] &&\n          !goog.global['$gwtExport']['$jscomp']['transpile']) {\n        throw new Error(\n            'The transpiler did not properly export the \"transpile\" ' +\n            'method. $gwtExport: ' + JSON.stringify(goog.global['$gwtExport']));\n      }\n      // transpile.js only exports a single $jscomp function, transpile. We\n      // grab just that and add it to the existing definition of $jscomp which\n      // contains the polyfills.\n      goog.global['$jscomp'].transpile =\n          goog.global['$gwtExport']['$jscomp']['transpile'];\n      jscomp = goog.global['$jscomp'];\n      transpile = jscomp.transpile;\n    }\n  }\n  if (!transpile) {\n    // The transpiler is an optional component.  If it's not available then\n    // replace it with a pass-through function that simply logs.\n    var suffix = ' requires transpilation but no transpiler was found.';\n    transpile = jscomp.transpile = function(code, path) {\n      // TODO(sdh): figure out some way to get this error to show up\n      // in test results, noting that the failure may occur in many\n      // different ways, including in loadModule() before the test\n      // runner even comes up.\n      goog.logToConsole_(path + suffix);\n      return code;\n    };\n  }\n  // Note: any transpilation errors/warnings will be logged to the console.\n  return transpile(code, path, target);\n};\n\n//==============================================================================\n// Language Enhancements\n//==============================================================================\n\n\n/**\n * This is a \"fixed\" version of the typeof operator.  It differs from the typeof\n * operator in such a way that null returns 'null' and arrays return 'array'.\n * @param {?} value The value to get the type of.\n * @return {string} The name of the type.\n */\ngoog.typeOf = function(value) {\n  var s = typeof value;\n\n  if (s != 'object') {\n    return s;\n  }\n\n  if (!value) {\n    return 'null';\n  }\n\n  if (Array.isArray(value)) {\n    return 'array';\n  }\n  return s;\n};\n\n\n/**\n * Returns true if the object looks like an array. To qualify as array like\n * the value needs to be either a NodeList or an object with a Number length\n * property. Note that for this function neither strings nor functions are\n * considered \"array-like\".\n *\n * @param {?} val Variable to test.\n * @return {boolean} Whether variable is an array.\n */\ngoog.isArrayLike = function(val) {\n  var type = goog.typeOf(val);\n  // We do not use goog.isObject here in order to exclude function values.\n  return type == 'array' || type == 'object' && typeof val.length == 'number';\n};\n\n\n/**\n * Returns true if the object looks like a Date. To qualify as Date-like the\n * value needs to be an object and have a getFullYear() function.\n * @param {?} val Variable to test.\n * @return {boolean} Whether variable is a like a Date.\n */\ngoog.isDateLike = function(val) {\n  return goog.isObject(val) && typeof val.getFullYear == 'function';\n};\n\n\n/**\n * Returns true if the specified value is a function.\n * @param {?} val Variable to test.\n * @return {boolean} Whether variable is a function.\n * @deprecated use \"typeof val === 'function'\" instead.\n */\ngoog.isFunction = function(val) {\n  return goog.typeOf(val) == 'function';\n};\n\n\n/**\n * Returns true if the specified value is an object.  This includes arrays and\n * functions.\n * @param {?} val Variable to test.\n * @return {boolean} Whether variable is an object.\n */\ngoog.isObject = function(val) {\n  var type = typeof val;\n  return type == 'object' && val != null || type == 'function';\n  // return Object(val) === val also works, but is slower, especially if val is\n  // not an object.\n};\n\n\n/**\n * Gets a unique ID for an object. This mutates the object so that further calls\n * with the same object as a parameter returns the same value. The unique ID is\n * guaranteed to be unique across the current session amongst objects that are\n * passed into `getUid`. There is no guarantee that the ID is unique or\n * consistent across sessions. It is unsafe to generate unique ID for function\n * prototypes.\n *\n * @param {Object} obj The object to get the unique ID for.\n * @return {number} The unique ID for the object.\n */\ngoog.getUid = function(obj) {\n  // TODO(arv): Make the type stricter, do not accept null.\n  return Object.prototype.hasOwnProperty.call(obj, goog.UID_PROPERTY_) &&\n      obj[goog.UID_PROPERTY_] ||\n      (obj[goog.UID_PROPERTY_] = ++goog.uidCounter_);\n};\n\n\n/**\n * Whether the given object is already assigned a unique ID.\n *\n * This does not modify the object.\n *\n * @param {!Object} obj The object to check.\n * @return {boolean} Whether there is an assigned unique id for the object.\n */\ngoog.hasUid = function(obj) {\n  return !!obj[goog.UID_PROPERTY_];\n};\n\n\n/**\n * Removes the unique ID from an object. This is useful if the object was\n * previously mutated using `goog.getUid` in which case the mutation is\n * undone.\n * @param {Object} obj The object to remove the unique ID field from.\n */\ngoog.removeUid = function(obj) {\n  // TODO(arv): Make the type stricter, do not accept null.\n\n  // In IE, DOM nodes are not instances of Object and throw an exception if we\n  // try to delete.  Instead we try to use removeAttribute.\n  if (obj !== null && 'removeAttribute' in obj) {\n    obj.removeAttribute(goog.UID_PROPERTY_);\n  }\n\n  try {\n    delete obj[goog.UID_PROPERTY_];\n  } catch (ex) {\n  }\n};\n\n\n/**\n * Name for unique ID property. Initialized in a way to help avoid collisions\n * with other closure JavaScript on the same page.\n * @type {string}\n * @private\n */\ngoog.UID_PROPERTY_ = 'closure_uid_' + ((Math.random() * 1e9) >>> 0);\n\n\n/**\n * Counter for UID.\n * @type {number}\n * @private\n */\ngoog.uidCounter_ = 0;\n\n\n/**\n * Clones a value. The input may be an Object, Array, or basic type. Objects and\n * arrays will be cloned recursively.\n *\n * WARNINGS:\n * <code>goog.cloneObject</code> does not detect reference loops. Objects that\n * refer to themselves will cause infinite recursion.\n *\n * <code>goog.cloneObject</code> is unaware of unique identifiers, and copies\n * UIDs created by <code>getUid</code> into cloned results.\n *\n * @param {*} obj The value to clone.\n * @return {*} A clone of the input value.\n * @deprecated goog.cloneObject is unsafe. Prefer the goog.object methods.\n */\ngoog.cloneObject = function(obj) {\n  var type = goog.typeOf(obj);\n  if (type == 'object' || type == 'array') {\n    if (typeof obj.clone === 'function') {\n      return obj.clone();\n    }\n    var clone = type == 'array' ? [] : {};\n    for (var key in obj) {\n      clone[key] = goog.cloneObject(obj[key]);\n    }\n    return clone;\n  }\n\n  return obj;\n};\n\n\n/**\n * A native implementation of goog.bind.\n * @param {?function(this:T, ...)} fn A function to partially apply.\n * @param {T} selfObj Specifies the object which this should point to when the\n *     function is run.\n * @param {...*} var_args Additional arguments that are partially applied to the\n *     function.\n * @return {!Function} A partially-applied form of the function goog.bind() was\n *     invoked as a method of.\n * @template T\n * @private\n */\ngoog.bindNative_ = function(fn, selfObj, var_args) {\n  return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments));\n};\n\n\n/**\n * A pure-JS implementation of goog.bind.\n * @param {?function(this:T, ...)} fn A function to partially apply.\n * @param {T} selfObj Specifies the object which this should point to when the\n *     function is run.\n * @param {...*} var_args Additional arguments that are partially applied to the\n *     function.\n * @return {!Function} A partially-applied form of the function goog.bind() was\n *     invoked as a method of.\n * @template T\n * @private\n */\ngoog.bindJs_ = function(fn, selfObj, var_args) {\n  if (!fn) {\n    throw new Error();\n  }\n\n  if (arguments.length > 2) {\n    var boundArgs = Array.prototype.slice.call(arguments, 2);\n    return function() {\n      // Prepend the bound arguments to the current arguments.\n      var newArgs = Array.prototype.slice.call(arguments);\n      Array.prototype.unshift.apply(newArgs, boundArgs);\n      return fn.apply(selfObj, newArgs);\n    };\n\n  } else {\n    return function() {\n      return fn.apply(selfObj, arguments);\n    };\n  }\n};\n\n\n/**\n * Partially applies this function to a particular 'this object' and zero or\n * more arguments. The result is a new function with some arguments of the first\n * function pre-filled and the value of this 'pre-specified'.\n *\n * Remaining arguments specified at call-time are appended to the pre-specified\n * ones.\n *\n * Also see: {@link #partial}.\n *\n * Usage:\n * <pre>var barMethBound = goog.bind(myFunction, myObj, 'arg1', 'arg2');\n * barMethBound('arg3', 'arg4');</pre>\n *\n * @param {?function(this:T, ...)} fn A function to partially apply.\n * @param {T} selfObj Specifies the object which this should point to when the\n *     function is run.\n * @param {...*} var_args Additional arguments that are partially applied to the\n *     function.\n * @return {!Function} A partially-applied form of the function goog.bind() was\n *     invoked as a method of.\n * @template T\n * @suppress {deprecated} See above.\n * @deprecated use `=> {}` or Function.prototype.bind instead.\n */\ngoog.bind = function(fn, selfObj, var_args) {\n  // TODO(nicksantos): narrow the type signature.\n  if (Function.prototype.bind &&\n      // NOTE(nicksantos): Somebody pulled base.js into the default Chrome\n      // extension environment. This means that for Chrome extensions, they get\n      // the implementation of Function.prototype.bind that calls goog.bind\n      // instead of the native one. Even worse, we don't want to introduce a\n      // circular dependency between goog.bind and Function.prototype.bind, so\n      // we have to hack this to make sure it works correctly.\n      Function.prototype.bind.toString().indexOf('native code') != -1) {\n    goog.bind = goog.bindNative_;\n  } else {\n    goog.bind = goog.bindJs_;\n  }\n  return goog.bind.apply(null, arguments);\n};\n\n\n/**\n * Like goog.bind(), except that a 'this object' is not required. Useful when\n * the target function is already bound.\n *\n * Usage:\n * var g = goog.partial(f, arg1, arg2);\n * g(arg3, arg4);\n *\n * @param {Function} fn A function to partially apply.\n * @param {...*} var_args Additional arguments that are partially applied to fn.\n * @return {!Function} A partially-applied form of the function goog.partial()\n *     was invoked as a method of.\n */\ngoog.partial = function(fn, var_args) {\n  var args = Array.prototype.slice.call(arguments, 1);\n  return function() {\n    // Clone the array (with slice()) and append additional arguments\n    // to the existing arguments.\n    var newArgs = args.slice();\n    newArgs.push.apply(newArgs, arguments);\n    return fn.apply(/** @type {?} */ (this), newArgs);\n  };\n};\n\n\n/**\n * Copies all the members of a source object to a target object. This method\n * does not work on all browsers for all objects that contain keys such as\n * toString or hasOwnProperty. Use goog.object.extend for this purpose.\n *\n * NOTE: Some have advocated for the use of goog.mixin to setup classes\n * with multiple inheritence (traits, mixins, etc).  However, as it simply\n * uses \"for in\", this is not compatible with ES6 classes whose methods are\n * non-enumerable.  Changing this, would break cases where non-enumerable\n * properties are not expected.\n *\n * @param {Object} target Target.\n * @param {Object} source Source.\n * @deprecated Prefer Object.assign\n */\ngoog.mixin = function(target, source) {\n  for (var x in source) {\n    target[x] = source[x];\n  }\n\n  // For IE7 or lower, the for-in-loop does not contain any properties that are\n  // not enumerable on the prototype object (for example, isPrototypeOf from\n  // Object.prototype) but also it will not include 'replace' on objects that\n  // extend String and change 'replace' (not that it is common for anyone to\n  // extend anything except Object).\n};\n\n\n/**\n * @return {number} An integer value representing the number of milliseconds\n *     between midnight, January 1, 1970 and the current time.\n * @deprecated Use Date.now\n */\ngoog.now = Date.now;\n\n\n/**\n * Evals JavaScript in the global scope.\n *\n * Throws an exception if neither execScript or eval is defined.\n * @param {string} script JavaScript string.\n */\ngoog.globalEval = function(script) {\n  (0, eval)(script);\n};\n\n\n/**\n * Optional map of CSS class names to obfuscated names used with\n * goog.getCssName().\n * @private {!Object<string, string>|undefined}\n * @see goog.setCssNameMapping\n */\ngoog.cssNameMapping_;\n\n\n/**\n * Optional obfuscation style for CSS class names. Should be set to either\n * 'BY_WHOLE' or 'BY_PART' if defined.\n * @type {string|undefined}\n * @private\n * @see goog.setCssNameMapping\n */\ngoog.cssNameMappingStyle_;\n\n\n\n/**\n * A hook for modifying the default behavior goog.getCssName. The function\n * if present, will receive the standard output of the goog.getCssName as\n * its input.\n *\n * @type {(function(string):string)|undefined}\n */\ngoog.global.CLOSURE_CSS_NAME_MAP_FN;\n\n\n/**\n * Handles strings that are intended to be used as CSS class names.\n *\n * This function works in tandem with @see goog.setCssNameMapping.\n *\n * Without any mapping set, the arguments are simple joined with a hyphen and\n * passed through unaltered.\n *\n * When there is a mapping, there are two possible styles in which these\n * mappings are used. In the BY_PART style, each part (i.e. in between hyphens)\n * of the passed in css name is rewritten according to the map. In the BY_WHOLE\n * style, the full css name is looked up in the map directly. If a rewrite is\n * not specified by the map, the compiler will output a warning.\n *\n * When the mapping is passed to the compiler, it will replace calls to\n * goog.getCssName with the strings from the mapping, e.g.\n *     var x = goog.getCssName('foo');\n *     var y = goog.getCssName(this.baseClass, 'active');\n *  becomes:\n *     var x = 'foo';\n *     var y = this.baseClass + '-active';\n *\n * If one argument is passed it will be processed, if two are passed only the\n * modifier will be processed, as it is assumed the first argument was generated\n * as a result of calling goog.getCssName.\n *\n * @param {string} className The class name.\n * @param {string=} opt_modifier A modifier to be appended to the class name.\n * @return {string} The class name or the concatenation of the class name and\n *     the modifier.\n */\ngoog.getCssName = function(className, opt_modifier) {\n  // String() is used for compatibility with compiled soy where the passed\n  // className can be non-string objects.\n  if (String(className).charAt(0) == '.') {\n    throw new Error(\n        'className passed in goog.getCssName must not start with \".\".' +\n        ' You passed: ' + className);\n  }\n\n  var getMapping = function(cssName) {\n    return goog.cssNameMapping_[cssName] || cssName;\n  };\n\n  var renameByParts = function(cssName) {\n    // Remap all the parts individually.\n    var parts = cssName.split('-');\n    var mapped = [];\n    for (var i = 0; i < parts.length; i++) {\n      mapped.push(getMapping(parts[i]));\n    }\n    return mapped.join('-');\n  };\n\n  var rename;\n  if (goog.cssNameMapping_) {\n    rename =\n        goog.cssNameMappingStyle_ == 'BY_WHOLE' ? getMapping : renameByParts;\n  } else {\n    rename = function(a) {\n      return a;\n    };\n  }\n\n  var result =\n      opt_modifier ? className + '-' + rename(opt_modifier) : rename(className);\n\n  // The special CLOSURE_CSS_NAME_MAP_FN allows users to specify further\n  // processing of the class name.\n  if (goog.global.CLOSURE_CSS_NAME_MAP_FN) {\n    return goog.global.CLOSURE_CSS_NAME_MAP_FN(result);\n  }\n\n  return result;\n};\n\n\n/**\n * Sets the map to check when returning a value from goog.getCssName(). Example:\n * <pre>\n * goog.setCssNameMapping({\n *   \"goog\": \"a\",\n *   \"disabled\": \"b\",\n * });\n *\n * var x = goog.getCssName('goog');\n * // The following evaluates to: \"a a-b\".\n * goog.getCssName('goog') + ' ' + goog.getCssName(x, 'disabled')\n * </pre>\n * When declared as a map of string literals to string literals, the JSCompiler\n * will replace all calls to goog.getCssName() using the supplied map if the\n * --process_closure_primitives flag is set.\n *\n * @param {!Object} mapping A map of strings to strings where keys are possible\n *     arguments to goog.getCssName() and values are the corresponding values\n *     that should be returned.\n * @param {string=} opt_style The style of css name mapping. There are two valid\n *     options: 'BY_PART', and 'BY_WHOLE'.\n * @see goog.getCssName for a description.\n */\ngoog.setCssNameMapping = function(mapping, opt_style) {\n  goog.cssNameMapping_ = mapping;\n  goog.cssNameMappingStyle_ = opt_style;\n};\n\n\n/**\n * To use CSS renaming in compiled mode, one of the input files should have a\n * call to goog.setCssNameMapping() with an object literal that the JSCompiler\n * can extract and use to replace all calls to goog.getCssName(). In uncompiled\n * mode, JavaScript code should be loaded before this base.js file that declares\n * a global variable, CLOSURE_CSS_NAME_MAPPING, which is used below. This is\n * to ensure that the mapping is loaded before any calls to goog.getCssName()\n * are made in uncompiled mode.\n *\n * A hook for overriding the CSS name mapping.\n * @type {!Object<string, string>|undefined}\n */\ngoog.global.CLOSURE_CSS_NAME_MAPPING;\n\n\nif (!COMPILED && goog.global.CLOSURE_CSS_NAME_MAPPING) {\n  // This does not call goog.setCssNameMapping() because the JSCompiler\n  // requires that goog.setCssNameMapping() be called with an object literal.\n  goog.cssNameMapping_ = goog.global.CLOSURE_CSS_NAME_MAPPING;\n}\n\n\n/**\n * Gets a localized message.\n *\n * This function is a compiler primitive. If you give the compiler a localized\n * message bundle, it will replace the string at compile-time with a localized\n * version, and expand goog.getMsg call to a concatenated string.\n *\n * Messages must be initialized in the form:\n * <code>\n * var MSG_NAME = goog.getMsg('Hello {$placeholder}', {'placeholder': 'world'});\n * </code>\n *\n * This function produces a string which should be treated as plain text. Use\n * {@link goog.html.SafeHtmlFormatter} in conjunction with goog.getMsg to\n * produce SafeHtml.\n *\n * @param {string} str Translatable string, places holders in the form {$foo}.\n * @param {Object<string, string>=} opt_values Maps place holder name to value.\n * @param {{html: boolean}=} opt_options Options:\n *     html: Escape '<' in str to '&lt;'. Used by Closure Templates where the\n *     generated code size and performance is critical which is why {@link\n *     goog.html.SafeHtmlFormatter} is not used. The value must be literal true\n *     or false.\n * @return {string} message with placeholders filled.\n */\ngoog.getMsg = function(str, opt_values, opt_options) {\n  if (opt_options && opt_options.html) {\n    // Note that '&' is not replaced because the translation can contain HTML\n    // entities.\n    str = str.replace(/</g, '&lt;');\n  }\n  if (opt_values) {\n    str = str.replace(/\\{\\$([^}]+)}/g, function(match, key) {\n      return (opt_values != null && key in opt_values) ? opt_values[key] :\n                                                         match;\n    });\n  }\n  return str;\n};\n\n\n/**\n * Gets a localized message. If the message does not have a translation, gives a\n * fallback message.\n *\n * This is useful when introducing a new message that has not yet been\n * translated into all languages.\n *\n * This function is a compiler primitive. Must be used in the form:\n * <code>var x = goog.getMsgWithFallback(MSG_A, MSG_B);</code>\n * where MSG_A and MSG_B were initialized with goog.getMsg.\n *\n * @param {string} a The preferred message.\n * @param {string} b The fallback message.\n * @return {string} The best translated message.\n */\ngoog.getMsgWithFallback = function(a, b) {\n  return a;\n};\n\n\n/**\n * Exposes an unobfuscated global namespace path for the given object.\n * Note that fields of the exported object *will* be obfuscated, unless they are\n * exported in turn via this function or goog.exportProperty.\n *\n * Also handy for making public items that are defined in anonymous closures.\n *\n * ex. goog.exportSymbol('public.path.Foo', Foo);\n *\n * ex. goog.exportSymbol('public.path.Foo.staticFunction', Foo.staticFunction);\n *     public.path.Foo.staticFunction();\n *\n * ex. goog.exportSymbol('public.path.Foo.prototype.myMethod',\n *                       Foo.prototype.myMethod);\n *     new public.path.Foo().myMethod();\n *\n * @param {string} publicPath Unobfuscated name to export.\n * @param {*} object Object the name should point to.\n * @param {?Object=} objectToExportTo The object to add the path to; default\n *     is goog.global.\n */\ngoog.exportSymbol = function(publicPath, object, objectToExportTo) {\n  goog.exportPath_(\n      publicPath, object, /* overwriteImplicit= */ true, objectToExportTo);\n};\n\n\n/**\n * Exports a property unobfuscated into the object's namespace.\n * ex. goog.exportProperty(Foo, 'staticFunction', Foo.staticFunction);\n * ex. goog.exportProperty(Foo.prototype, 'myMethod', Foo.prototype.myMethod);\n * @param {Object} object Object whose static property is being exported.\n * @param {string} publicName Unobfuscated name to export.\n * @param {*} symbol Object the name should point to.\n */\ngoog.exportProperty = function(object, publicName, symbol) {\n  object[publicName] = symbol;\n};\n\n\n/**\n * Inherit the prototype methods from one constructor into another.\n *\n * Usage:\n * <pre>\n * function ParentClass(a, b) { }\n * ParentClass.prototype.foo = function(a) { };\n *\n * function ChildClass(a, b, c) {\n *   ChildClass.base(this, 'constructor', a, b);\n * }\n * goog.inherits(ChildClass, ParentClass);\n *\n * var child = new ChildClass('a', 'b', 'see');\n * child.foo(); // This works.\n * </pre>\n *\n * @param {!Function} childCtor Child class.\n * @param {!Function} parentCtor Parent class.\n * @suppress {strictMissingProperties} superClass_ and base is not defined on\n *    Function.\n * @deprecated Use ECMAScript class syntax instead.\n */\ngoog.inherits = function(childCtor, parentCtor) {\n  /** @constructor */\n  function tempCtor() {}\n  tempCtor.prototype = parentCtor.prototype;\n  childCtor.superClass_ = parentCtor.prototype;\n  childCtor.prototype = new tempCtor();\n  /** @override */\n  childCtor.prototype.constructor = childCtor;\n\n  /**\n   * Calls superclass constructor/method.\n   *\n   * This function is only available if you use goog.inherits to\n   * express inheritance relationships between classes.\n   *\n   * NOTE: This is a replacement for goog.base and for superClass_\n   * property defined in childCtor.\n   *\n   * @param {!Object} me Should always be \"this\".\n   * @param {string} methodName The method name to call. Calling\n   *     superclass constructor can be done with the special string\n   *     'constructor'.\n   * @param {...*} var_args The arguments to pass to superclass\n   *     method/constructor.\n   * @return {*} The return value of the superclass method/constructor.\n   */\n  childCtor.base = function(me, methodName, var_args) {\n    // Copying using loop to avoid deop due to passing arguments object to\n    // function. This is faster in many JS engines as of late 2014.\n    var args = new Array(arguments.length - 2);\n    for (var i = 2; i < arguments.length; i++) {\n      args[i - 2] = arguments[i];\n    }\n    return parentCtor.prototype[methodName].apply(me, args);\n  };\n};\n\n\n/**\n * Allow for aliasing within scope functions.  This function exists for\n * uncompiled code - in compiled code the calls will be inlined and the aliases\n * applied.  In uncompiled code the function is simply run since the aliases as\n * written are valid JavaScript.\n *\n *\n * @param {function()} fn Function to call.  This function can contain aliases\n *     to namespaces (e.g. \"var dom = goog.dom\") or classes\n *     (e.g. \"var Timer = goog.Timer\").\n * @deprecated Use goog.module instead.\n */\ngoog.scope = function(fn) {\n  if (goog.isInModuleLoader_()) {\n    throw new Error('goog.scope is not supported within a module.');\n  }\n  fn.call(goog.global);\n};\n\n\n/*\n * To support uncompiled, strict mode bundles that use eval to divide source\n * like so:\n *    eval('someSource;//# sourceUrl sourcefile.js');\n * We need to export the globally defined symbols \"goog\" and \"COMPILED\".\n * Exporting \"goog\" breaks the compiler optimizations, so we required that\n * be defined externally.\n * NOTE: We don't use goog.exportSymbol here because we don't want to trigger\n * extern generation when that compiler option is enabled.\n */\nif (!COMPILED) {\n  goog.global['COMPILED'] = COMPILED;\n}\n\n\n//==============================================================================\n// goog.defineClass implementation\n//==============================================================================\n\n\n/**\n * Creates a restricted form of a Closure \"class\":\n *   - from the compiler's perspective, the instance returned from the\n *     constructor is sealed (no new properties may be added).  This enables\n *     better checks.\n *   - the compiler will rewrite this definition to a form that is optimal\n *     for type checking and optimization (initially this will be a more\n *     traditional form).\n *\n * @param {Function} superClass The superclass, Object or null.\n * @param {goog.defineClass.ClassDescriptor} def\n *     An object literal describing\n *     the class.  It may have the following properties:\n *     \"constructor\": the constructor function\n *     \"statics\": an object literal containing methods to add to the constructor\n *        as \"static\" methods or a function that will receive the constructor\n *        function as its only parameter to which static properties can\n *        be added.\n *     all other properties are added to the prototype.\n * @return {!Function} The class constructor.\n * @deprecated Use ECMAScript class syntax instead.\n */\ngoog.defineClass = function(superClass, def) {\n  // TODO(johnlenz): consider making the superClass an optional parameter.\n  var constructor = def.constructor;\n  var statics = def.statics;\n  // Wrap the constructor prior to setting up the prototype and static methods.\n  if (!constructor || constructor == Object.prototype.constructor) {\n    constructor = function() {\n      throw new Error(\n          'cannot instantiate an interface (no constructor defined).');\n    };\n  }\n\n  var cls = goog.defineClass.createSealingConstructor_(constructor, superClass);\n  if (superClass) {\n    goog.inherits(cls, superClass);\n  }\n\n  // Remove all the properties that should not be copied to the prototype.\n  delete def.constructor;\n  delete def.statics;\n\n  goog.defineClass.applyProperties_(cls.prototype, def);\n  if (statics != null) {\n    if (statics instanceof Function) {\n      statics(cls);\n    } else {\n      goog.defineClass.applyProperties_(cls, statics);\n    }\n  }\n\n  return cls;\n};\n\n\n/**\n * @typedef {{\n *   constructor: (!Function|undefined),\n *   statics: (Object|undefined|function(Function):void)\n * }}\n */\ngoog.defineClass.ClassDescriptor;\n\n\n/**\n * @define {boolean} Whether the instances returned by goog.defineClass should\n *     be sealed when possible.\n *\n * When sealing is disabled the constructor function will not be wrapped by\n * goog.defineClass, making it incompatible with ES6 class methods.\n */\ngoog.defineClass.SEAL_CLASS_INSTANCES =\n    goog.define('goog.defineClass.SEAL_CLASS_INSTANCES', goog.DEBUG);\n\n\n/**\n * If goog.defineClass.SEAL_CLASS_INSTANCES is enabled and Object.seal is\n * defined, this function will wrap the constructor in a function that seals the\n * results of the provided constructor function.\n *\n * @param {!Function} ctr The constructor whose results maybe be sealed.\n * @param {Function} superClass The superclass constructor.\n * @return {!Function} The replacement constructor.\n * @private\n */\ngoog.defineClass.createSealingConstructor_ = function(ctr, superClass) {\n  if (!goog.defineClass.SEAL_CLASS_INSTANCES) {\n    // Do now wrap the constructor when sealing is disabled. Angular code\n    // depends on this for injection to work properly.\n    return ctr;\n  }\n\n  // NOTE: The sealing behavior has been removed\n\n  /**\n   * @this {Object}\n   * @return {?}\n   */\n  var wrappedCtr = function() {\n    // Don't seal an instance of a subclass when it calls the constructor of\n    // its super class as there is most likely still setup to do.\n    var instance = ctr.apply(this, arguments) || this;\n    instance[goog.UID_PROPERTY_] = instance[goog.UID_PROPERTY_];\n\n    return instance;\n  };\n\n  return wrappedCtr;\n};\n\n\n\n// TODO(johnlenz): share these values with the goog.object\n/**\n * The names of the fields that are defined on Object.prototype.\n * @type {!Array<string>}\n * @private\n * @const\n */\ngoog.defineClass.OBJECT_PROTOTYPE_FIELDS_ = [\n  'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',\n  'toLocaleString', 'toString', 'valueOf'\n];\n\n\n// TODO(johnlenz): share this function with the goog.object\n/**\n * @param {!Object} target The object to add properties to.\n * @param {!Object} source The object to copy properties from.\n * @private\n */\ngoog.defineClass.applyProperties_ = function(target, source) {\n  // TODO(johnlenz): update this to support ES5 getters/setters\n\n  var key;\n  for (key in source) {\n    if (Object.prototype.hasOwnProperty.call(source, key)) {\n      target[key] = source[key];\n    }\n  }\n\n  // For IE the for-in-loop does not contain any properties that are not\n  // enumerable on the prototype object (for example isPrototypeOf from\n  // Object.prototype) and it will also not include 'replace' on objects that\n  // extend String and change 'replace' (not that it is common for anyone to\n  // extend anything except Object).\n  for (var i = 0; i < goog.defineClass.OBJECT_PROTOTYPE_FIELDS_.length; i++) {\n    key = goog.defineClass.OBJECT_PROTOTYPE_FIELDS_[i];\n    if (Object.prototype.hasOwnProperty.call(source, key)) {\n      target[key] = source[key];\n    }\n  }\n};\n\n\n// There's a bug in the compiler where without collapse properties the\n// Closure namespace defines do not guard code correctly. To help reduce code\n// size also check for !COMPILED even though it redundant until this is fixed.\nif (!COMPILED && goog.DEPENDENCIES_ENABLED) {\n\n  /**\n   * Returns the most recently added script element in the DOM.\n   * @return {?Element}\n   * @private\n   */\n  goog.getLastScript_ = function() {\n    var elem = document.documentElement;\n    while (elem.nodeName != 'SCRIPT' && elem.lastChild) {\n      elem = elem.lastChild;\n    }\n    return /** @type {?Element} */ (elem);\n  };\n\n  /**\n   * Tries to detect whether is in the context of an HTML document.\n   * @return {boolean} True if it looks like HTML document.\n   * @private\n   */\n  goog.inHtmlDocument_ = function() {\n    /** @type {!Document} */\n    var doc = goog.global.document;\n    return doc != null && 'write' in doc;  // XULDocument misses write.\n  };\n\n\n  /**\n   * We'd like to check for if the document readyState is 'loading'; however\n   * there are bugs on IE 10 and below where the readyState being anything other\n   * than 'complete' is not reliable.\n   * @return {boolean}\n   * @private\n   */\n  goog.isDocumentLoading_ = function() {\n    // attachEvent is available on IE 6 thru 10 only, and thus can be used to\n    // detect those browsers.\n    /** @type {!HTMLDocument} */\n    var doc = goog.global.document;\n    return doc.attachEvent ? doc.readyState != 'complete' :\n                             doc.readyState == 'loading';\n  };\n\n\n  /**\n   * Tries to detect the base path of base.js script that bootstraps Closure.\n   * @private\n   */\n  goog.findBasePath_ = function() {\n    if (goog.global.CLOSURE_BASE_PATH != undefined &&\n        // Anti DOM-clobbering runtime check (b/37736576).\n        typeof goog.global.CLOSURE_BASE_PATH === 'string') {\n      goog.basePath = goog.global.CLOSURE_BASE_PATH;\n      return;\n    } else if (!goog.inHtmlDocument_()) {\n      return;\n    }\n    /** @type {!Document} */\n    var doc = goog.global.document;\n    // If we have a currentScript available, use it exclusively.\n    var currentScript = doc.currentScript;\n    if (currentScript) {\n      var scripts = [currentScript];\n    } else {\n      var scripts = doc.getElementsByTagName('SCRIPT');\n    }\n    // Search backwards since the current script is in almost all cases the one\n    // that has base.js.\n    for (var i = scripts.length - 1; i >= 0; --i) {\n      var script = /** @type {!HTMLScriptElement} */ (scripts[i]);\n      var src = script.src;\n      var qmark = src.lastIndexOf('?');\n      var l = qmark == -1 ? src.length : qmark;\n      if (src.substr(l - 7, 7) == 'base.js') {\n        goog.basePath = src.substr(0, l - 7);\n        return;\n      }\n    }\n  };\n\n  goog.findBasePath_();\n\n  /** @struct @constructor @final */\n  goog.Transpiler = function() {\n    /** @private {?Object<string, boolean>} */\n    this.requiresTranspilation_ = null;\n    /** @private {string} */\n    this.transpilationTarget_ = goog.TRANSPILE_TO_LANGUAGE;\n  };\n  /**\n   * Returns a newly created map from language mode string to a boolean\n   * indicating whether transpilation should be done for that mode as well as\n   * the highest level language that this environment supports.\n   *\n   * Guaranteed invariant:\n   * For any two modes, l1 and l2 where l2 is a newer mode than l1,\n   * `map[l1] == true` implies that `map[l2] == true`.\n   *\n   * Note this method is extracted and used elsewhere, so it cannot rely on\n   * anything external (it should easily be able to be transformed into a\n   * standalone, top level function).\n   *\n   * @private\n   * @return {{\n   *   target: string,\n   *   map: !Object<string, boolean>\n   * }}\n   */\n  goog.Transpiler.prototype.createRequiresTranspilation_ = function() {\n    var transpilationTarget = 'es3';\n    var /** !Object<string, boolean> */ requiresTranspilation = {'es3': false};\n    var transpilationRequiredForAllLaterModes = false;\n\n    /**\n     * Adds an entry to requiresTranspliation for the given language mode.\n     *\n     * IMPORTANT: Calls must be made in order from oldest to newest language\n     * mode.\n     * @param {string} modeName\n     * @param {function(): boolean} isSupported Returns true if the JS engine\n     *     supports the given mode.\n     */\n    function addNewerLanguageTranspilationCheck(modeName, isSupported) {\n      if (transpilationRequiredForAllLaterModes) {\n        requiresTranspilation[modeName] = true;\n      } else if (isSupported()) {\n        transpilationTarget = modeName;\n        requiresTranspilation[modeName] = false;\n      } else {\n        requiresTranspilation[modeName] = true;\n        transpilationRequiredForAllLaterModes = true;\n      }\n    }\n\n    /**\n     * Does the given code evaluate without syntax errors and return a truthy\n     * result?\n     */\n    function /** boolean */ evalCheck(/** string */ code) {\n      try {\n        return !!eval(code);\n      } catch (ignored) {\n        return false;\n      }\n    }\n\n    var userAgent = goog.global.navigator && goog.global.navigator.userAgent ?\n        goog.global.navigator.userAgent :\n        '';\n\n    // Identify ES3-only browsers by their incorrect treatment of commas.\n    addNewerLanguageTranspilationCheck('es5', function() {\n      return evalCheck('[1,].length==1');\n    });\n    addNewerLanguageTranspilationCheck('es6', function() {\n      // Edge has a non-deterministic (i.e., not reproducible) bug with ES6:\n      // https://github.com/Microsoft/ChakraCore/issues/1496.\n      var re = /Edge\\/(\\d+)(\\.\\d)*/i;\n      var edgeUserAgent = userAgent.match(re);\n      if (edgeUserAgent) {\n        // The Reflect.construct test below is flaky on Edge. It can sometimes\n        // pass or fail on 40 15.15063, so just exit early for Edge and treat\n        // it as ES5. Until we're on a more up to date version just always use\n        // ES5. See https://github.com/Microsoft/ChakraCore/issues/3217.\n        return false;\n      }\n      // Test es6: [FF50 (?), Edge 14 (?), Chrome 50]\n      //   (a) default params (specifically shadowing locals),\n      //   (b) destructuring, (c) block-scoped functions,\n      //   (d) for-of (const), (e) new.target/Reflect.construct\n      var es6fullTest =\n          'class X{constructor(){if(new.target!=String)throw 1;this.x=42}}' +\n          'let q=Reflect.construct(X,[],String);if(q.x!=42||!(q instanceof ' +\n          'String))throw 1;for(const a of[2,3]){if(a==2)continue;function ' +\n          'f(z={a}){let a=0;return z.a}{function f(){return 0;}}return f()' +\n          '==3}';\n\n      return evalCheck('(()=>{\"use strict\";' + es6fullTest + '})()');\n    });\n    // ** and **= are the only new features in 'es7'\n    addNewerLanguageTranspilationCheck('es7', function() {\n      return evalCheck('2 ** 2 == 4');\n    });\n    // async functions are the only new features in 'es8'\n    addNewerLanguageTranspilationCheck('es8', function() {\n      return evalCheck('async () => 1, true');\n    });\n    addNewerLanguageTranspilationCheck('es9', function() {\n      return evalCheck('({...rest} = {}), true');\n    });\n    addNewerLanguageTranspilationCheck('es_next', function() {\n      return false;  // assume it always need to transpile\n    });\n    return {target: transpilationTarget, map: requiresTranspilation};\n  };\n\n\n  /**\n   * Determines whether the given language needs to be transpiled.\n   * @param {string} lang\n   * @param {string|undefined} module\n   * @return {boolean}\n   */\n  goog.Transpiler.prototype.needsTranspile = function(lang, module) {\n    if (goog.TRANSPILE == 'always') {\n      return true;\n    } else if (goog.TRANSPILE == 'never') {\n      return false;\n    } else if (!this.requiresTranspilation_) {\n      var obj = this.createRequiresTranspilation_();\n      this.requiresTranspilation_ = obj.map;\n      this.transpilationTarget_ = this.transpilationTarget_ || obj.target;\n    }\n    if (lang in this.requiresTranspilation_) {\n      if (this.requiresTranspilation_[lang]) {\n        return true;\n      } else if (\n          goog.inHtmlDocument_() && module == 'es6' &&\n          !('noModule' in goog.global.document.createElement('script'))) {\n        return true;\n      } else {\n        return false;\n      }\n    } else {\n      throw new Error('Unknown language mode: ' + lang);\n    }\n  };\n\n\n  /**\n   * Lazily retrieves the transpiler and applies it to the source.\n   * @param {string} code JS code.\n   * @param {string} path Path to the code.\n   * @return {string} The transpiled code.\n   */\n  goog.Transpiler.prototype.transpile = function(code, path) {\n    // TODO(johnplaisted): We should delete goog.transpile_ and just have this\n    // function. But there's some compile error atm where goog.global is being\n    // stripped incorrectly without this.\n    return goog.transpile_(code, path, this.transpilationTarget_);\n  };\n\n\n  /** @private @final {!goog.Transpiler} */\n  goog.transpiler_ = new goog.Transpiler();\n\n  /**\n   * Rewrites closing script tags in input to avoid ending an enclosing script\n   * tag.\n   *\n   * @param {string} str\n   * @return {string}\n   * @private\n   */\n  goog.protectScriptTag_ = function(str) {\n    return str.replace(/<\\/(SCRIPT)/ig, '\\\\x3c/$1');\n  };\n\n\n  /**\n   * A debug loader is responsible for downloading and executing javascript\n   * files in an unbundled, uncompiled environment.\n   *\n   * This can be custimized via the setDependencyFactory method, or by\n   * CLOSURE_IMPORT_SCRIPT/CLOSURE_LOAD_FILE_SYNC.\n   *\n   * @struct @constructor @final @private\n   */\n  goog.DebugLoader_ = function() {\n    /** @private @const {!Object<string, !goog.Dependency>} */\n    this.dependencies_ = {};\n    /** @private @const {!Object<string, string>} */\n    this.idToPath_ = {};\n    /** @private @const {!Object<string, boolean>} */\n    this.written_ = {};\n    /** @private @const {!Array<!goog.Dependency>} */\n    this.loadingDeps_ = [];\n    /** @private {!Array<!goog.Dependency>} */\n    this.depsToLoad_ = [];\n    /** @private {boolean} */\n    this.paused_ = false;\n    /** @private {!goog.DependencyFactory} */\n    this.factory_ = new goog.DependencyFactory(goog.transpiler_);\n    /** @private @const {!Object<string, !Function>} */\n    this.deferredCallbacks_ = {};\n    /** @private @const {!Array<string>} */\n    this.deferredQueue_ = [];\n  };\n\n  /**\n   * @param {!Array<string>} namespaces\n   * @param {function(): undefined} callback Function to call once all the\n   *     namespaces have loaded.\n   */\n  goog.DebugLoader_.prototype.bootstrap = function(namespaces, callback) {\n    var cb = callback;\n    function resolve() {\n      if (cb) {\n        goog.global.setTimeout(cb, 0);\n        cb = null;\n      }\n    }\n\n    if (!namespaces.length) {\n      resolve();\n      return;\n    }\n\n    var deps = [];\n    for (var i = 0; i < namespaces.length; i++) {\n      var path = this.getPathFromDeps_(namespaces[i]);\n      if (!path) {\n        throw new Error('Unregonized namespace: ' + namespaces[i]);\n      }\n      deps.push(this.dependencies_[path]);\n    }\n\n    var require = goog.require;\n    var loaded = 0;\n    for (var i = 0; i < namespaces.length; i++) {\n      require(namespaces[i]);\n      deps[i].onLoad(function() {\n        if (++loaded == namespaces.length) {\n          resolve();\n        }\n      });\n    }\n  };\n\n\n  /**\n   * Loads the Closure Dependency file.\n   *\n   * Exposed a public function so CLOSURE_NO_DEPS can be set to false, base\n   * loaded, setDependencyFactory called, and then this called. i.e. allows\n   * custom loading of the deps file.\n   */\n  goog.DebugLoader_.prototype.loadClosureDeps = function() {\n    // Circumvent addDependency, which would try to transpile deps.js if\n    // transpile is set to always.\n    var relPath = 'deps.js';\n    this.depsToLoad_.push(this.factory_.createDependency(\n        goog.normalizePath_(goog.basePath + relPath), relPath, [], [], {},\n        false));\n    this.loadDeps_();\n  };\n\n\n  /**\n   * Notifies the debug loader when a dependency has been requested.\n   *\n   * @param {string} absPathOrId Path of the dependency or goog id.\n   * @param {boolean=} opt_force\n   */\n  goog.DebugLoader_.prototype.requested = function(absPathOrId, opt_force) {\n    var path = this.getPathFromDeps_(absPathOrId);\n    if (path &&\n        (opt_force || this.areDepsLoaded_(this.dependencies_[path].requires))) {\n      var callback = this.deferredCallbacks_[path];\n      if (callback) {\n        delete this.deferredCallbacks_[path];\n        callback();\n      }\n    }\n  };\n\n\n  /**\n   * Sets the dependency factory, which can be used to create custom\n   * goog.Dependency implementations to control how dependencies are loaded.\n   *\n   * @param {!goog.DependencyFactory} factory\n   */\n  goog.DebugLoader_.prototype.setDependencyFactory = function(factory) {\n    this.factory_ = factory;\n  };\n\n\n  /**\n   * Travserses the dependency graph and queues the given dependency, and all of\n   * its transitive dependencies, for loading and then starts loading if not\n   * paused.\n   *\n   * @param {string} namespace\n   * @private\n   */\n  goog.DebugLoader_.prototype.load_ = function(namespace) {\n    if (!this.getPathFromDeps_(namespace)) {\n      var errorMessage = 'goog.require could not find: ' + namespace;\n\n      goog.logToConsole_(errorMessage);\n      throw Error(errorMessage);\n    } else {\n      var loader = this;\n\n      var deps = [];\n\n      /** @param {string} namespace */\n      var visit = function(namespace) {\n        var path = loader.getPathFromDeps_(namespace);\n\n        if (!path) {\n          throw new Error('Bad dependency path or symbol: ' + namespace);\n        }\n\n        if (loader.written_[path]) {\n          return;\n        }\n\n        loader.written_[path] = true;\n\n        var dep = loader.dependencies_[path];\n        for (var i = 0; i < dep.requires.length; i++) {\n          if (!goog.isProvided_(dep.requires[i])) {\n            visit(dep.requires[i]);\n          }\n        }\n\n        deps.push(dep);\n      };\n\n      visit(namespace);\n\n      var wasLoading = !!this.depsToLoad_.length;\n      this.depsToLoad_ = this.depsToLoad_.concat(deps);\n\n      if (!this.paused_ && !wasLoading) {\n        this.loadDeps_();\n      }\n    }\n  };\n\n\n  /**\n   * Loads any queued dependencies until they are all loaded or paused.\n   *\n   * @private\n   */\n  goog.DebugLoader_.prototype.loadDeps_ = function() {\n    var loader = this;\n    var paused = this.paused_;\n\n    while (this.depsToLoad_.length && !paused) {\n      (function() {\n        var loadCallDone = false;\n        var dep = loader.depsToLoad_.shift();\n\n        var loaded = false;\n        loader.loading_(dep);\n\n        var controller = {\n          pause: function() {\n            if (loadCallDone) {\n              throw new Error('Cannot call pause after the call to load.');\n            } else {\n              paused = true;\n            }\n          },\n          resume: function() {\n            if (loadCallDone) {\n              loader.resume_();\n            } else {\n              // Some dep called pause and then resume in the same load call.\n              // Just keep running this same loop.\n              paused = false;\n            }\n          },\n          loaded: function() {\n            if (loaded) {\n              throw new Error('Double call to loaded.');\n            }\n\n            loaded = true;\n            loader.loaded_(dep);\n          },\n          pending: function() {\n            // Defensive copy.\n            var pending = [];\n            for (var i = 0; i < loader.loadingDeps_.length; i++) {\n              pending.push(loader.loadingDeps_[i]);\n            }\n            return pending;\n          },\n          /**\n           * @param {goog.ModuleType} type\n           */\n          setModuleState: function(type) {\n            goog.moduleLoaderState_ = {\n              type: type,\n              moduleName: '',\n              declareLegacyNamespace: false\n            };\n          },\n          /** @type {function(string, string, string=)} */\n          registerEs6ModuleExports: function(\n              path, exports, opt_closureNamespace) {\n            if (opt_closureNamespace) {\n              goog.loadedModules_[opt_closureNamespace] = {\n                exports: exports,\n                type: goog.ModuleType.ES6,\n                moduleId: opt_closureNamespace || ''\n              };\n            }\n          },\n          /** @type {function(string, ?)} */\n          registerGoogModuleExports: function(moduleId, exports) {\n            goog.loadedModules_[moduleId] = {\n              exports: exports,\n              type: goog.ModuleType.GOOG,\n              moduleId: moduleId\n            };\n          },\n          clearModuleState: function() {\n            goog.moduleLoaderState_ = null;\n          },\n          defer: function(callback) {\n            if (loadCallDone) {\n              throw new Error(\n                  'Cannot register with defer after the call to load.');\n            }\n            loader.defer_(dep, callback);\n          },\n          areDepsLoaded: function() {\n            return loader.areDepsLoaded_(dep.requires);\n          }\n        };\n\n        try {\n          dep.load(controller);\n        } finally {\n          loadCallDone = true;\n        }\n      })();\n    }\n\n    if (paused) {\n      this.pause_();\n    }\n  };\n\n\n  /** @private */\n  goog.DebugLoader_.prototype.pause_ = function() {\n    this.paused_ = true;\n  };\n\n\n  /** @private */\n  goog.DebugLoader_.prototype.resume_ = function() {\n    if (this.paused_) {\n      this.paused_ = false;\n      this.loadDeps_();\n    }\n  };\n\n\n  /**\n   * Marks the given dependency as loading (load has been called but it has not\n   * yet marked itself as finished). Useful for dependencies that want to know\n   * what else is loading. Example: goog.modules cannot eval if there are\n   * loading dependencies.\n   *\n   * @param {!goog.Dependency} dep\n   * @private\n   */\n  goog.DebugLoader_.prototype.loading_ = function(dep) {\n    this.loadingDeps_.push(dep);\n  };\n\n\n  /**\n   * Marks the given dependency as having finished loading and being available\n   * for require.\n   *\n   * @param {!goog.Dependency} dep\n   * @private\n   */\n  goog.DebugLoader_.prototype.loaded_ = function(dep) {\n    for (var i = 0; i < this.loadingDeps_.length; i++) {\n      if (this.loadingDeps_[i] == dep) {\n        this.loadingDeps_.splice(i, 1);\n        break;\n      }\n    }\n\n    for (var i = 0; i < this.deferredQueue_.length; i++) {\n      if (this.deferredQueue_[i] == dep.path) {\n        this.deferredQueue_.splice(i, 1);\n        break;\n      }\n    }\n\n    if (this.loadingDeps_.length == this.deferredQueue_.length &&\n        !this.depsToLoad_.length) {\n      // Something has asked to load these, but they may not be directly\n      // required again later, so load them now that we know we're done loading\n      // everything else. e.g. a goog module entry point.\n      while (this.deferredQueue_.length) {\n        this.requested(this.deferredQueue_.shift(), true);\n      }\n    }\n\n    dep.loaded();\n  };\n\n\n  /**\n   * @param {!Array<string>} pathsOrIds\n   * @return {boolean}\n   * @private\n   */\n  goog.DebugLoader_.prototype.areDepsLoaded_ = function(pathsOrIds) {\n    for (var i = 0; i < pathsOrIds.length; i++) {\n      var path = this.getPathFromDeps_(pathsOrIds[i]);\n      if (!path ||\n          (!(path in this.deferredCallbacks_) &&\n           !goog.isProvided_(pathsOrIds[i]))) {\n        return false;\n      }\n    }\n\n    return true;\n  };\n\n\n  /**\n   * @param {string} absPathOrId\n   * @return {?string}\n   * @private\n   */\n  goog.DebugLoader_.prototype.getPathFromDeps_ = function(absPathOrId) {\n    if (absPathOrId in this.idToPath_) {\n      return this.idToPath_[absPathOrId];\n    } else if (absPathOrId in this.dependencies_) {\n      return absPathOrId;\n    } else {\n      return null;\n    }\n  };\n\n\n  /**\n   * @param {!goog.Dependency} dependency\n   * @param {!Function} callback\n   * @private\n   */\n  goog.DebugLoader_.prototype.defer_ = function(dependency, callback) {\n    this.deferredCallbacks_[dependency.path] = callback;\n    this.deferredQueue_.push(dependency.path);\n  };\n\n\n  /**\n   * Interface for goog.Dependency implementations to have some control over\n   * loading of dependencies.\n   *\n   * @record\n   */\n  goog.LoadController = function() {};\n\n\n  /**\n   * Tells the controller to halt loading of more dependencies.\n   */\n  goog.LoadController.prototype.pause = function() {};\n\n\n  /**\n   * Tells the controller to resume loading of more dependencies if paused.\n   */\n  goog.LoadController.prototype.resume = function() {};\n\n\n  /**\n   * Tells the controller that this dependency has finished loading.\n   *\n   * This causes this to be removed from pending() and any load callbacks to\n   * fire.\n   */\n  goog.LoadController.prototype.loaded = function() {};\n\n\n  /**\n   * List of dependencies on which load has been called but which have not\n   * called loaded on their controller. This includes the current dependency.\n   *\n   * @return {!Array<!goog.Dependency>}\n   */\n  goog.LoadController.prototype.pending = function() {};\n\n\n  /**\n   * Registers an object as an ES6 module's exports so that goog.modules may\n   * require it by path.\n   *\n   * @param {string} path Full path of the module.\n   * @param {?} exports\n   * @param {string=} opt_closureNamespace Closure namespace to associate with\n   *     this module.\n   */\n  goog.LoadController.prototype.registerEs6ModuleExports = function(\n      path, exports, opt_closureNamespace) {};\n\n\n  /**\n   * Sets the current module state.\n   *\n   * @param {goog.ModuleType} type Type of module.\n   */\n  goog.LoadController.prototype.setModuleState = function(type) {};\n\n\n  /**\n   * Clears the current module state.\n   */\n  goog.LoadController.prototype.clearModuleState = function() {};\n\n\n  /**\n   * Registers a callback to call once the dependency is actually requested\n   * via goog.require + all of the immediate dependencies have been loaded or\n   * all other files have been loaded. Allows for lazy loading until\n   * require'd without pausing dependency loading, which is needed on old IE.\n   *\n   * @param {!Function} callback\n   */\n  goog.LoadController.prototype.defer = function(callback) {};\n\n\n  /**\n   * @return {boolean}\n   */\n  goog.LoadController.prototype.areDepsLoaded = function() {};\n\n\n  /**\n   * Basic super class for all dependencies Closure Library can load.\n   *\n   * This default implementation is designed to load untranspiled, non-module\n   * scripts in a web broswer.\n   *\n   * For transpiled non-goog.module files {@see goog.TranspiledDependency}.\n   * For goog.modules see {@see goog.GoogModuleDependency}.\n   * For untranspiled ES6 modules {@see goog.Es6ModuleDependency}.\n   *\n   * @param {string} path Absolute path of this script.\n   * @param {string} relativePath Path of this script relative to goog.basePath.\n   * @param {!Array<string>} provides goog.provided or goog.module symbols\n   *     in this file.\n   * @param {!Array<string>} requires goog symbols or relative paths to Closure\n   *     this depends on.\n   * @param {!Object<string, string>} loadFlags\n   * @struct @constructor\n   */\n  goog.Dependency = function(\n      path, relativePath, provides, requires, loadFlags) {\n    /** @const */\n    this.path = path;\n    /** @const */\n    this.relativePath = relativePath;\n    /** @const */\n    this.provides = provides;\n    /** @const */\n    this.requires = requires;\n    /** @const */\n    this.loadFlags = loadFlags;\n    /** @private {boolean} */\n    this.loaded_ = false;\n    /** @private {!Array<function()>} */\n    this.loadCallbacks_ = [];\n  };\n\n\n  /**\n   * @return {string} The pathname part of this dependency's path if it is a\n   *     URI.\n   */\n  goog.Dependency.prototype.getPathName = function() {\n    var pathName = this.path;\n    var protocolIndex = pathName.indexOf('://');\n    if (protocolIndex >= 0) {\n      pathName = pathName.substring(protocolIndex + 3);\n      var slashIndex = pathName.indexOf('/');\n      if (slashIndex >= 0) {\n        pathName = pathName.substring(slashIndex + 1);\n      }\n    }\n    return pathName;\n  };\n\n\n  /**\n   * @param {function()} callback Callback to fire as soon as this has loaded.\n   * @final\n   */\n  goog.Dependency.prototype.onLoad = function(callback) {\n    if (this.loaded_) {\n      callback();\n    } else {\n      this.loadCallbacks_.push(callback);\n    }\n  };\n\n\n  /**\n   * Marks this dependency as loaded and fires any callbacks registered with\n   * onLoad.\n   * @final\n   */\n  goog.Dependency.prototype.loaded = function() {\n    this.loaded_ = true;\n    var callbacks = this.loadCallbacks_;\n    this.loadCallbacks_ = [];\n    for (var i = 0; i < callbacks.length; i++) {\n      callbacks[i]();\n    }\n  };\n\n\n  /**\n   * Whether or not document.written / appended script tags should be deferred.\n   *\n   * @private {boolean}\n   */\n  goog.Dependency.defer_ = false;\n\n\n  /**\n   * Map of script ready / state change callbacks. Old IE cannot handle putting\n   * these properties on goog.global.\n   *\n   * @private @const {!Object<string, function(?):undefined>}\n   */\n  goog.Dependency.callbackMap_ = {};\n\n\n  /**\n   * @param {function(...?):?} callback\n   * @return {string}\n   * @private\n   */\n  goog.Dependency.registerCallback_ = function(callback) {\n    var key = Math.random().toString(32);\n    goog.Dependency.callbackMap_[key] = callback;\n    return key;\n  };\n\n\n  /**\n   * @param {string} key\n   * @private\n   */\n  goog.Dependency.unregisterCallback_ = function(key) {\n    delete goog.Dependency.callbackMap_[key];\n  };\n\n\n  /**\n   * @param {string} key\n   * @param {...?} var_args\n   * @private\n   * @suppress {unusedPrivateMembers}\n   */\n  goog.Dependency.callback_ = function(key, var_args) {\n    if (key in goog.Dependency.callbackMap_) {\n      var callback = goog.Dependency.callbackMap_[key];\n      var args = [];\n      for (var i = 1; i < arguments.length; i++) {\n        args.push(arguments[i]);\n      }\n      callback.apply(undefined, args);\n    } else {\n      var errorMessage = 'Callback key ' + key +\n          ' does not exist (was base.js loaded more than once?).';\n      throw Error(errorMessage);\n    }\n  };\n\n\n  /**\n   * Starts loading this dependency. This dependency can pause loading if it\n   * needs to and resume it later via the controller interface.\n   *\n   * When this is loaded it should call controller.loaded(). Note that this will\n   * end up calling the loaded method of this dependency; there is no need to\n   * call it explicitly.\n   *\n   * @param {!goog.LoadController} controller\n   */\n  goog.Dependency.prototype.load = function(controller) {\n    if (goog.global.CLOSURE_IMPORT_SCRIPT) {\n      if (goog.global.CLOSURE_IMPORT_SCRIPT(this.path)) {\n        controller.loaded();\n      } else {\n        controller.pause();\n      }\n      return;\n    }\n\n    if (!goog.inHtmlDocument_()) {\n      goog.logToConsole_(\n          'Cannot use default debug loader outside of HTML documents.');\n      if (this.relativePath == 'deps.js') {\n        // Some old code is relying on base.js auto loading deps.js failing with\n        // no error before later setting CLOSURE_IMPORT_SCRIPT.\n        // CLOSURE_IMPORT_SCRIPT should be set *before* base.js is loaded, or\n        // CLOSURE_NO_DEPS set to true.\n        goog.logToConsole_(\n            'Consider setting CLOSURE_IMPORT_SCRIPT before loading base.js, ' +\n            'or setting CLOSURE_NO_DEPS to true.');\n        controller.loaded();\n      } else {\n        controller.pause();\n      }\n      return;\n    }\n\n    /** @type {!HTMLDocument} */\n    var doc = goog.global.document;\n\n    // If the user tries to require a new symbol after document load,\n    // something has gone terribly wrong. Doing a document.write would\n    // wipe out the page. This does not apply to the CSP-compliant method\n    // of writing script tags.\n    if (doc.readyState == 'complete' &&\n        !goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING) {\n      // Certain test frameworks load base.js multiple times, which tries\n      // to write deps.js each time. If that happens, just fail silently.\n      // These frameworks wipe the page between each load of base.js, so this\n      // is OK.\n      var isDeps = /\\bdeps.js$/.test(this.path);\n      if (isDeps) {\n        controller.loaded();\n        return;\n      } else {\n        throw Error('Cannot write \"' + this.path + '\" after document load');\n      }\n    }\n\n    var nonce = goog.getScriptNonce();\n    if (!goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING &&\n        goog.isDocumentLoading_()) {\n      var allowInlineEventHandlers = !nonce;\n      var key = goog.Dependency.registerCallback_(function(script) {\n        if (!goog.DebugLoader_.IS_OLD_IE_ || script.readyState == 'complete') {\n          goog.Dependency.unregisterCallback_(key);\n          controller.loaded();\n        }\n      });\n      var eventName =\n          goog.DebugLoader_.IS_OLD_IE_ ? 'onreadystatechange' : 'onload';\n      var event = '';\n      if (allowInlineEventHandlers) {\n        event = ' ' + eventName + '=\"goog.Dependency.callback_(\\'' + key +\n            '\\', this)\"';\n      }\n\n      var defer = goog.Dependency.defer_ ? ' defer' : '';\n      var nonceAttr = nonce ? ' nonce=\"' + nonce + '\"' : '';\n      var script = '<script src=\"' + this.path + '\"' + nonceAttr + event +\n          defer + '><\\/script>';\n\n      doc.write(\n          goog.TRUSTED_TYPES_POLICY_ ?\n              goog.TRUSTED_TYPES_POLICY_.createHTML(script) :\n              script);\n\n      if (!allowInlineEventHandlers) {\n        var elem = goog.getLastScript_();\n        elem.onload = /** @this {!HTMLScriptElement} */ function() {\n          goog.Dependency.callback_(key, this);\n        };\n      }\n    } else {\n      var scriptEl =\n          /** @type {!HTMLScriptElement} */ (doc.createElement('script'));\n      scriptEl.defer = goog.Dependency.defer_;\n      scriptEl.async = false;\n      scriptEl.type = 'text/javascript';\n\n      // If CSP nonces are used, propagate them to dynamically created scripts.\n      // This is necessary to allow nonce-based CSPs without 'strict-dynamic'.\n      if (nonce) {\n        scriptEl.setAttribute('nonce', nonce);\n      }\n\n      if (goog.DebugLoader_.IS_OLD_IE_) {\n        // Execution order is not guaranteed on old IE, halt loading and write\n        // these scripts one at a time, after each loads.\n        controller.pause();\n        scriptEl.onreadystatechange = function() {\n          if (scriptEl.readyState == 'loaded' ||\n              scriptEl.readyState == 'complete') {\n            controller.loaded();\n            controller.resume();\n          }\n        };\n      } else {\n        scriptEl.onload = function() {\n          scriptEl.onload = null;\n          controller.loaded();\n        };\n      }\n\n      scriptEl.src = goog.TRUSTED_TYPES_POLICY_ ?\n          goog.TRUSTED_TYPES_POLICY_.createScriptURL(this.path) :\n          this.path;\n      doc.head.appendChild(scriptEl);\n    }\n  };\n\n\n  /**\n   * @param {string} path Absolute path of this script.\n   * @param {string} relativePath Path of this script relative to goog.basePath.\n   * @param {!Array<string>} provides Should be an empty array.\n   *     TODO(johnplaisted) add support for adding closure namespaces to ES6\n   *     modules for interop purposes.\n   * @param {!Array<string>} requires goog symbols or relative paths to Closure\n   *     this depends on.\n   * @param {!Object<string, string>} loadFlags\n   * @struct @constructor\n   * @extends {goog.Dependency}\n   */\n  goog.Es6ModuleDependency = function(\n      path, relativePath, provides, requires, loadFlags) {\n    goog.Es6ModuleDependency.base(\n        this, 'constructor', path, relativePath, provides, requires, loadFlags);\n  };\n  goog.inherits(goog.Es6ModuleDependency, goog.Dependency);\n\n\n  /** @override */\n  goog.Es6ModuleDependency.prototype.load = function(controller) {\n    if (goog.global.CLOSURE_IMPORT_SCRIPT) {\n      if (goog.global.CLOSURE_IMPORT_SCRIPT(this.path)) {\n        controller.loaded();\n      } else {\n        controller.pause();\n      }\n      return;\n    }\n\n    if (!goog.inHtmlDocument_()) {\n      goog.logToConsole_(\n          'Cannot use default debug loader outside of HTML documents.');\n      controller.pause();\n      return;\n    }\n\n    /** @type {!HTMLDocument} */\n    var doc = goog.global.document;\n\n    var dep = this;\n\n    // TODO(johnplaisted): Does document.writing really speed up anything? Any\n    // difference between this and just waiting for interactive mode and then\n    // appending?\n    function write(src, contents) {\n      if (contents) {\n        var script = '<script type=\"module\" crossorigin>' + contents + '</' +\n            'script>';\n        doc.write(\n            goog.TRUSTED_TYPES_POLICY_ ?\n                goog.TRUSTED_TYPES_POLICY_.createHTML(script) :\n                script);\n      } else {\n        var script = '<script type=\"module\" crossorigin src=\"' + src + '\"></' +\n            'script>';\n        doc.write(\n            goog.TRUSTED_TYPES_POLICY_ ?\n                goog.TRUSTED_TYPES_POLICY_.createHTML(script) :\n                script);\n      }\n    }\n\n    function append(src, contents) {\n      var scriptEl =\n          /** @type {!HTMLScriptElement} */ (doc.createElement('script'));\n      scriptEl.defer = true;\n      scriptEl.async = false;\n      scriptEl.type = 'module';\n      scriptEl.setAttribute('crossorigin', true);\n\n      // If CSP nonces are used, propagate them to dynamically created scripts.\n      // This is necessary to allow nonce-based CSPs without 'strict-dynamic'.\n      var nonce = goog.getScriptNonce();\n      if (nonce) {\n        scriptEl.setAttribute('nonce', nonce);\n      }\n\n      if (contents) {\n        scriptEl.textContent = goog.TRUSTED_TYPES_POLICY_ ?\n            goog.TRUSTED_TYPES_POLICY_.createScript(contents) :\n            contents;\n      } else {\n        scriptEl.src = goog.TRUSTED_TYPES_POLICY_ ?\n            goog.TRUSTED_TYPES_POLICY_.createScriptURL(src) :\n            src;\n      }\n\n      doc.head.appendChild(scriptEl);\n    }\n\n    var create;\n\n    if (goog.isDocumentLoading_()) {\n      create = write;\n      // We can ONLY call document.write if we are guaranteed that any\n      // non-module script tags document.written after this are deferred.\n      // Small optimization, in theory document.writing is faster.\n      goog.Dependency.defer_ = true;\n    } else {\n      create = append;\n    }\n\n    // Write 4 separate tags here:\n    // 1) Sets the module state at the correct time (just before execution).\n    // 2) A src node for this, which just hopefully lets the browser load it a\n    //    little early (no need to parse #3).\n    // 3) Import the module and register it.\n    // 4) Clear the module state at the correct time. Guaranteed to run even\n    //    if there is an error in the module (#3 will not run if there is an\n    //    error in the module).\n    var beforeKey = goog.Dependency.registerCallback_(function() {\n      goog.Dependency.unregisterCallback_(beforeKey);\n      controller.setModuleState(goog.ModuleType.ES6);\n    });\n    create(undefined, 'goog.Dependency.callback_(\"' + beforeKey + '\")');\n\n    // TODO(johnplaisted): Does this really speed up anything?\n    create(this.path, undefined);\n\n    var registerKey = goog.Dependency.registerCallback_(function(exports) {\n      goog.Dependency.unregisterCallback_(registerKey);\n      controller.registerEs6ModuleExports(\n          dep.path, exports, goog.moduleLoaderState_.moduleName);\n    });\n    create(\n        undefined,\n        'import * as m from \"' + this.path + '\"; goog.Dependency.callback_(\"' +\n            registerKey + '\", m)');\n\n    var afterKey = goog.Dependency.registerCallback_(function() {\n      goog.Dependency.unregisterCallback_(afterKey);\n      controller.clearModuleState();\n      controller.loaded();\n    });\n    create(undefined, 'goog.Dependency.callback_(\"' + afterKey + '\")');\n  };\n\n\n  /**\n   * Superclass of any dependency that needs to be loaded into memory,\n   * transformed, and then eval'd (goog.modules and transpiled files).\n   *\n   * @param {string} path Absolute path of this script.\n   * @param {string} relativePath Path of this script relative to goog.basePath.\n   * @param {!Array<string>} provides goog.provided or goog.module symbols\n   *     in this file.\n   * @param {!Array<string>} requires goog symbols or relative paths to Closure\n   *     this depends on.\n   * @param {!Object<string, string>} loadFlags\n   * @struct @constructor @abstract\n   * @extends {goog.Dependency}\n   */\n  goog.TransformedDependency = function(\n      path, relativePath, provides, requires, loadFlags) {\n    goog.TransformedDependency.base(\n        this, 'constructor', path, relativePath, provides, requires, loadFlags);\n    /** @private {?string} */\n    this.contents_ = null;\n\n    /**\n     * Whether to lazily make the synchronous XHR (when goog.require'd) or make\n     * the synchronous XHR when initially loading. On FireFox 61 there is a bug\n     * where an ES6 module cannot make a synchronous XHR (rather, it can, but if\n     * it does then no other ES6 modules will load after).\n     *\n     * tl;dr we lazy load due to bugs on older browsers and eager load due to\n     * bugs on newer ones.\n     *\n     * https://bugzilla.mozilla.org/show_bug.cgi?id=1477090\n     *\n     * @private @const {boolean}\n     */\n    this.lazyFetch_ = !goog.inHtmlDocument_() ||\n        !('noModule' in goog.global.document.createElement('script'));\n  };\n  goog.inherits(goog.TransformedDependency, goog.Dependency);\n\n\n  /** @override */\n  goog.TransformedDependency.prototype.load = function(controller) {\n    var dep = this;\n\n    function fetch() {\n      dep.contents_ = goog.loadFileSync_(dep.path);\n\n      if (dep.contents_) {\n        dep.contents_ = dep.transform(dep.contents_);\n        if (dep.contents_) {\n          dep.contents_ += '\\n//# sourceURL=' + dep.path;\n        }\n      }\n    }\n\n    if (goog.global.CLOSURE_IMPORT_SCRIPT) {\n      fetch();\n      if (this.contents_ &&\n          goog.global.CLOSURE_IMPORT_SCRIPT('', this.contents_)) {\n        this.contents_ = null;\n        controller.loaded();\n      } else {\n        controller.pause();\n      }\n      return;\n    }\n\n\n    var isEs6 = this.loadFlags['module'] == goog.ModuleType.ES6;\n\n    if (!this.lazyFetch_) {\n      fetch();\n    }\n\n    function load() {\n      if (dep.lazyFetch_) {\n        fetch();\n      }\n\n      if (!dep.contents_) {\n        // loadFileSync_ or transform are responsible. Assume they logged an\n        // error.\n        return;\n      }\n\n      if (isEs6) {\n        controller.setModuleState(goog.ModuleType.ES6);\n      }\n\n      var namespace;\n\n      try {\n        var contents = dep.contents_;\n        dep.contents_ = null;\n        goog.globalEval(contents);\n        if (isEs6) {\n          namespace = goog.moduleLoaderState_.moduleName;\n        }\n      } finally {\n        if (isEs6) {\n          controller.clearModuleState();\n        }\n      }\n\n      if (isEs6) {\n        // Due to circular dependencies this may not be available for require\n        // right now.\n        goog.global['$jscomp']['require']['ensure'](\n            [dep.getPathName()], function() {\n              controller.registerEs6ModuleExports(\n                  dep.path,\n                  goog.global['$jscomp']['require'](dep.getPathName()),\n                  namespace);\n            });\n      }\n\n      controller.loaded();\n    }\n\n    // Do not fetch now; in FireFox 47 the synchronous XHR doesn't block all\n    // events. If we fetched now and then document.write'd the contents the\n    // document.write would be an eval and would execute too soon! Instead write\n    // a script tag to fetch and eval synchronously at the correct time.\n    function fetchInOwnScriptThenLoad() {\n      /** @type {!HTMLDocument} */\n      var doc = goog.global.document;\n\n      var key = goog.Dependency.registerCallback_(function() {\n        goog.Dependency.unregisterCallback_(key);\n        load();\n      });\n\n      var nonce = goog.getScriptNonce();\n      var nonceAttr = nonce ? ' nonce=\"' + nonce + '\"' : '';\n      var script = '<script' + nonceAttr + '>' +\n          goog.protectScriptTag_('goog.Dependency.callback_(\"' + key + '\");') +\n          '</' +\n          'script>';\n      doc.write(\n          goog.TRUSTED_TYPES_POLICY_ ?\n              goog.TRUSTED_TYPES_POLICY_.createHTML(script) :\n              script);\n    }\n\n    // If one thing is pending it is this.\n    var anythingElsePending = controller.pending().length > 1;\n\n    // If anything else is loading we need to lazy load due to bugs in old IE.\n    // Specifically script tags with src and script tags with contents could\n    // execute out of order if document.write is used, so we cannot use\n    // document.write. Do not pause here; it breaks old IE as well.\n    var useOldIeWorkAround =\n        anythingElsePending && goog.DebugLoader_.IS_OLD_IE_;\n\n    // Additionally if we are meant to defer scripts but the page is still\n    // loading (e.g. an ES6 module is loading) then also defer. Or if we are\n    // meant to defer and anything else is pending then defer (those may be\n    // scripts that did not need transformation and are just script tags with\n    // defer set to true, and we need to evaluate after that deferred script).\n    var needsAsyncLoading = goog.Dependency.defer_ &&\n        (anythingElsePending || goog.isDocumentLoading_());\n\n    if (useOldIeWorkAround || needsAsyncLoading) {\n      // Note that we only defer when we have to rather than 100% of the time.\n      // Always defering would work, but then in theory the order of\n      // goog.require calls would then matter. We want to enforce that most of\n      // the time the order of the require calls does not matter.\n      controller.defer(function() {\n        load();\n      });\n      return;\n    }\n    // TODO(johnplaisted): Externs are missing onreadystatechange for\n    // HTMLDocument.\n    /** @type {?} */\n    var doc = goog.global.document;\n\n    var isInternetExplorer =\n        goog.inHtmlDocument_() && 'ActiveXObject' in goog.global;\n\n    // Don't delay in any version of IE. There's bug around this that will\n    // cause out of order script execution. This means that on older IE ES6\n    // modules will load too early (while the document is still loading + the\n    // dom is not available). The other option is to load too late (when the\n    // document is complete and the onload even will never fire). This seems\n    // to be the lesser of two evils as scripts already act like the former.\n    if (isEs6 && goog.inHtmlDocument_() && goog.isDocumentLoading_() &&\n        !isInternetExplorer) {\n      goog.Dependency.defer_ = true;\n      // Transpiled ES6 modules still need to load like regular ES6 modules,\n      // aka only after the document is interactive.\n      controller.pause();\n      var oldCallback = doc.onreadystatechange;\n      doc.onreadystatechange = function() {\n        if (doc.readyState == 'interactive') {\n          doc.onreadystatechange = oldCallback;\n          load();\n          controller.resume();\n        }\n        if (goog.isFunction(oldCallback)) {\n          oldCallback.apply(undefined, arguments);\n        }\n      };\n    } else {\n      // Always eval on old IE.\n      if (goog.DebugLoader_.IS_OLD_IE_ || !goog.inHtmlDocument_() ||\n          !goog.isDocumentLoading_()) {\n        load();\n      } else {\n        fetchInOwnScriptThenLoad();\n      }\n    }\n  };\n\n\n  /**\n   * @param {string} contents\n   * @return {string}\n   * @abstract\n   */\n  goog.TransformedDependency.prototype.transform = function(contents) {};\n\n\n  /**\n   * Any non-goog.module dependency which needs to be transpiled before eval.\n   *\n   * @param {string} path Absolute path of this script.\n   * @param {string} relativePath Path of this script relative to goog.basePath.\n   * @param {!Array<string>} provides goog.provided or goog.module symbols\n   *     in this file.\n   * @param {!Array<string>} requires goog symbols or relative paths to Closure\n   *     this depends on.\n   * @param {!Object<string, string>} loadFlags\n   * @param {!goog.Transpiler} transpiler\n   * @struct @constructor\n   * @extends {goog.TransformedDependency}\n   */\n  goog.TranspiledDependency = function(\n      path, relativePath, provides, requires, loadFlags, transpiler) {\n    goog.TranspiledDependency.base(\n        this, 'constructor', path, relativePath, provides, requires, loadFlags);\n    /** @protected @const*/\n    this.transpiler = transpiler;\n  };\n  goog.inherits(goog.TranspiledDependency, goog.TransformedDependency);\n\n\n  /** @override */\n  goog.TranspiledDependency.prototype.transform = function(contents) {\n    // Transpile with the pathname so that ES6 modules are domain agnostic.\n    return this.transpiler.transpile(contents, this.getPathName());\n  };\n\n\n  /**\n   * An ES6 module dependency that was transpiled to a jscomp module outside\n   * of the debug loader, e.g. server side.\n   *\n   * @param {string} path Absolute path of this script.\n   * @param {string} relativePath Path of this script relative to goog.basePath.\n   * @param {!Array<string>} provides goog.provided or goog.module symbols\n   *     in this file.\n   * @param {!Array<string>} requires goog symbols or relative paths to Closure\n   *     this depends on.\n   * @param {!Object<string, string>} loadFlags\n   * @struct @constructor\n   * @extends {goog.TransformedDependency}\n   */\n  goog.PreTranspiledEs6ModuleDependency = function(\n      path, relativePath, provides, requires, loadFlags) {\n    goog.PreTranspiledEs6ModuleDependency.base(\n        this, 'constructor', path, relativePath, provides, requires, loadFlags);\n  };\n  goog.inherits(\n      goog.PreTranspiledEs6ModuleDependency, goog.TransformedDependency);\n\n\n  /** @override */\n  goog.PreTranspiledEs6ModuleDependency.prototype.transform = function(\n      contents) {\n    return contents;\n  };\n\n\n  /**\n   * A goog.module, transpiled or not. Will always perform some minimal\n   * transformation even when not transpiled to wrap in a goog.loadModule\n   * statement.\n   *\n   * @param {string} path Absolute path of this script.\n   * @param {string} relativePath Path of this script relative to goog.basePath.\n   * @param {!Array<string>} provides goog.provided or goog.module symbols\n   *     in this file.\n   * @param {!Array<string>} requires goog symbols or relative paths to Closure\n   *     this depends on.\n   * @param {!Object<string, string>} loadFlags\n   * @param {boolean} needsTranspile\n   * @param {!goog.Transpiler} transpiler\n   * @struct @constructor\n   * @extends {goog.TransformedDependency}\n   */\n  goog.GoogModuleDependency = function(\n      path, relativePath, provides, requires, loadFlags, needsTranspile,\n      transpiler) {\n    goog.GoogModuleDependency.base(\n        this, 'constructor', path, relativePath, provides, requires, loadFlags);\n    /** @private @const */\n    this.needsTranspile_ = needsTranspile;\n    /** @private @const */\n    this.transpiler_ = transpiler;\n  };\n  goog.inherits(goog.GoogModuleDependency, goog.TransformedDependency);\n\n\n  /** @override */\n  goog.GoogModuleDependency.prototype.transform = function(contents) {\n    if (this.needsTranspile_) {\n      contents = this.transpiler_.transpile(contents, this.getPathName());\n    }\n\n    if (!goog.LOAD_MODULE_USING_EVAL || goog.global.JSON === undefined) {\n      return '' +\n          'goog.loadModule(function(exports) {' +\n          '\"use strict\";' + contents +\n          '\\n' +  // terminate any trailing single line comment.\n          ';return exports' +\n          '});' +\n          '\\n//# sourceURL=' + this.path + '\\n';\n    } else {\n      return '' +\n          'goog.loadModule(' +\n          goog.global.JSON.stringify(\n              contents + '\\n//# sourceURL=' + this.path + '\\n') +\n          ');';\n    }\n  };\n\n\n  /**\n   * Whether the browser is IE9 or earlier, which needs special handling\n   * for deferred modules.\n   * @const @private {boolean}\n   */\n  goog.DebugLoader_.IS_OLD_IE_ = !!(\n      !goog.global.atob && goog.global.document && goog.global.document['all']);\n\n\n  /**\n   * @param {string} relPath\n   * @param {!Array<string>|undefined} provides\n   * @param {!Array<string>} requires\n   * @param {boolean|!Object<string>=} opt_loadFlags\n   * @see goog.addDependency\n   */\n  goog.DebugLoader_.prototype.addDependency = function(\n      relPath, provides, requires, opt_loadFlags) {\n    provides = provides || [];\n    relPath = relPath.replace(/\\\\/g, '/');\n    var path = goog.normalizePath_(goog.basePath + relPath);\n    if (!opt_loadFlags || typeof opt_loadFlags === 'boolean') {\n      opt_loadFlags = opt_loadFlags ? {'module': goog.ModuleType.GOOG} : {};\n    }\n    var dep = this.factory_.createDependency(\n        path, relPath, provides, requires, opt_loadFlags,\n        goog.transpiler_.needsTranspile(\n            opt_loadFlags['lang'] || 'es3', opt_loadFlags['module']));\n    this.dependencies_[path] = dep;\n    for (var i = 0; i < provides.length; i++) {\n      this.idToPath_[provides[i]] = path;\n    }\n    this.idToPath_[relPath] = path;\n  };\n\n\n  /**\n   * Creates goog.Dependency instances for the debug loader to load.\n   *\n   * Should be overridden to have the debug loader use custom subclasses of\n   * goog.Dependency.\n   *\n   * @param {!goog.Transpiler} transpiler\n   * @struct @constructor\n   */\n  goog.DependencyFactory = function(transpiler) {\n    /** @protected @const */\n    this.transpiler = transpiler;\n  };\n\n\n  /**\n   * @param {string} path Absolute path of the file.\n   * @param {string} relativePath Path relative to closure’s base.js.\n   * @param {!Array<string>} provides Array of provided goog.provide/module ids.\n   * @param {!Array<string>} requires Array of required goog.provide/module /\n   *     relative ES6 module paths.\n   * @param {!Object<string, string>} loadFlags\n   * @param {boolean} needsTranspile True if the file needs to be transpiled\n   *     per the goog.Transpiler.\n   * @return {!goog.Dependency}\n   */\n  goog.DependencyFactory.prototype.createDependency = function(\n      path, relativePath, provides, requires, loadFlags, needsTranspile) {\n\n    if (loadFlags['module'] == goog.ModuleType.GOOG) {\n      return new goog.GoogModuleDependency(\n          path, relativePath, provides, requires, loadFlags, needsTranspile,\n          this.transpiler);\n    } else if (needsTranspile) {\n      return new goog.TranspiledDependency(\n          path, relativePath, provides, requires, loadFlags, this.transpiler);\n    } else {\n      if (loadFlags['module'] == goog.ModuleType.ES6) {\n        if (goog.TRANSPILE == 'never' && goog.ASSUME_ES_MODULES_TRANSPILED) {\n          return new goog.PreTranspiledEs6ModuleDependency(\n              path, relativePath, provides, requires, loadFlags);\n        } else {\n          return new goog.Es6ModuleDependency(\n              path, relativePath, provides, requires, loadFlags);\n        }\n      } else {\n        return new goog.Dependency(\n            path, relativePath, provides, requires, loadFlags);\n      }\n    }\n  };\n\n\n  /** @private @const */\n  goog.debugLoader_ = new goog.DebugLoader_();\n\n\n  /**\n   * Loads the Closure Dependency file.\n   *\n   * Exposed a public function so CLOSURE_NO_DEPS can be set to false, base\n   * loaded, setDependencyFactory called, and then this called. i.e. allows\n   * custom loading of the deps file.\n   */\n  goog.loadClosureDeps = function() {\n    goog.debugLoader_.loadClosureDeps();\n  };\n\n\n  /**\n   * Sets the dependency factory, which can be used to create custom\n   * goog.Dependency implementations to control how dependencies are loaded.\n   *\n   * Note: if you wish to call this function and provide your own implemnetation\n   * it is a wise idea to set CLOSURE_NO_DEPS to true, otherwise the dependency\n   * file and all of its goog.addDependency calls will use the default factory.\n   * You can call goog.loadClosureDeps to load the Closure dependency file\n   * later, after your factory is injected.\n   *\n   * @param {!goog.DependencyFactory} factory\n   */\n  goog.setDependencyFactory = function(factory) {\n    goog.debugLoader_.setDependencyFactory(factory);\n  };\n\n\n  /**\n   * Trusted Types policy for the debug loader.\n   * @private @const {?TrustedTypePolicy}\n   */\n  goog.TRUSTED_TYPES_POLICY_ = goog.TRUSTED_TYPES_POLICY_NAME ?\n      goog.createTrustedTypesPolicy(goog.TRUSTED_TYPES_POLICY_NAME + '#base') :\n      null;\n\n  if (!goog.global.CLOSURE_NO_DEPS) {\n    goog.debugLoader_.loadClosureDeps();\n  }\n\n\n  /**\n   * Bootstraps the given namespaces and calls the callback once they are\n   * available either via goog.require. This is a replacement for using\n   * `goog.require` to bootstrap Closure JavaScript. Previously a `goog.require`\n   * in an HTML file would guarantee that the require'd namespace was available\n   * in the next immediate script tag. With ES6 modules this no longer a\n   * guarantee.\n   *\n   * @param {!Array<string>} namespaces\n   * @param {function(): ?} callback Function to call once all the namespaces\n   *     have loaded. Always called asynchronously.\n   */\n  goog.bootstrap = function(namespaces, callback) {\n    goog.debugLoader_.bootstrap(namespaces, callback);\n  };\n}\n\n\n/**\n * @define {string} Trusted Types policy name. If non-empty then Closure will\n * use Trusted Types.\n */\ngoog.TRUSTED_TYPES_POLICY_NAME =\n    goog.define('goog.TRUSTED_TYPES_POLICY_NAME', 'goog');\n\n\n/**\n * Returns the parameter.\n * @param {string} s\n * @return {string}\n * @private\n */\ngoog.identity_ = function(s) {\n  return s;\n};\n\n\n/**\n * Creates Trusted Types policy if Trusted Types are supported by the browser.\n * The policy just blesses any string as a Trusted Type. It is not visibility\n * restricted because anyone can also call trustedTypes.createPolicy directly.\n * However, the allowed names should be restricted by a HTTP header and the\n * reference to the created policy should be visibility restricted.\n * @param {string} name\n * @return {?TrustedTypePolicy}\n */\ngoog.createTrustedTypesPolicy = function(name) {\n  var policy = null;\n  var policyFactory = goog.global.trustedTypes;\n  if (!policyFactory || !policyFactory.createPolicy) {\n    return policy;\n  }\n  // trustedTypes.createPolicy throws if called with a name that is already\n  // registered, even in report-only mode. Until the API changes, catch the\n  // error not to break the applications functionally. In such case, the code\n  // will fall back to using regular Safe Types.\n  // TODO(koto): Remove catching once createPolicy API stops throwing.\n  try {\n    policy = policyFactory.createPolicy(name, {\n      createHTML: goog.identity_,\n      createScript: goog.identity_,\n      createScriptURL: goog.identity_\n    });\n  } catch (e) {\n    goog.logToConsole_(e.message);\n  }\n  return policy;\n};\n",null,null,null,null,null,null,"/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines developer-visible errors for Firebase Auth APIs.\n */\n\n\ngoog.provide('fireauth.AuthError');\ngoog.provide('fireauth.authenum');\ngoog.provide('fireauth.authenum.Error');\n\n\n\n/**\n * Error that can be returned to the developer.\n * @param {!fireauth.authenum.Error} code The short error code.\n * @param {?string=} message The human-readable message.\n * @param {?Object=} serverResponse The raw server response.\n * @constructor\n * @extends {Error}\n */\nfireauth.AuthError = function(code, message, serverResponse) {\n  this['code'] = fireauth.AuthError.ERROR_CODE_PREFIX + code;\n  this.message = message || fireauth.AuthError.MESSAGES_[code] || '';\n  this.serverResponse = serverResponse || null;\n};\ngoog.inherits(fireauth.AuthError, Error);\n\n\n/**\n * @return {!Object} The plain object form of the error.\n */\nfireauth.AuthError.prototype.toPlainObject = function() {\n  var obj = {\n    'code': this['code'],\n    'message': this.message\n  };\n  if (this.serverResponse) {\n    obj['serverResponse'] = this.serverResponse;\n  }\n  return obj;\n};\n\n\n/**\n * @return {!Object} The plain object form of the error. This is used by\n *     JSON.toStringify() to return the stringified representation of the error;\n * @override\n */\nfireauth.AuthError.prototype.toJSON = function() {\n  // Return the plain object representation in case JSON.stringify is called on\n  // an auth error instance.\n  return this.toPlainObject();\n};\n\n\n/**\n * @param {?Object|undefined} response The object response to convert to a\n *     fireauth.AuthError.\n * @return {?fireauth.AuthError} The error representation of the response.\n */\nfireauth.AuthError.fromPlainObject = function(response) {\n  var fullCode = response && response['code'];\n  if (fullCode) {\n    // Remove prefix from name.\n    var code = fullCode.substring(\n        fireauth.AuthError.ERROR_CODE_PREFIX.length);\n    return new fireauth.AuthError(\n        /** @type {!fireauth.authenum.Error} */ (code),\n        response['message'],\n        response['serverResponse']);\n  }\n  return null;\n};\n\n\n/**\n * Takes in an error and translates a specific error code to another one if\n * found in the current error.\n * @param {*} error The error thrown.\n * @param {!fireauth.authenum.Error} fromCode The error code to translate from.\n * @param {!fireauth.authenum.Error} toCode The error code to translate to.\n * @return {*} The mapped error message.\n */\nfireauth.AuthError.translateError = function(error, fromCode, toCode) {\n  if (error &&\n      error['code'] &&\n      error['code'] == fireauth.AuthError.ERROR_CODE_PREFIX + fromCode) {\n    // Translate the error to the new one.\n    return new fireauth.AuthError(toCode);\n  }\n  // Return the same error if the fromCode is not found.\n  return error;\n};\n\n\n/**\n * The error prefix for fireauth.Auth errors.\n * @protected {string}\n */\nfireauth.AuthError.ERROR_CODE_PREFIX = 'auth/';\n\n\n/**\n * Developer facing Firebase Auth error codes.\n * @enum {string}\n */\nfireauth.authenum.Error = {\n  ADMIN_ONLY_OPERATION: 'admin-restricted-operation',\n  ARGUMENT_ERROR: 'argument-error',\n  APP_NOT_AUTHORIZED: 'app-not-authorized',\n  APP_NOT_INSTALLED: 'app-not-installed',\n  CAPTCHA_CHECK_FAILED: 'captcha-check-failed',\n  CODE_EXPIRED: 'code-expired',\n  CORDOVA_NOT_READY: 'cordova-not-ready',\n  CORS_UNSUPPORTED: 'cors-unsupported',\n  CREDENTIAL_ALREADY_IN_USE: 'credential-already-in-use',\n  CREDENTIAL_MISMATCH: 'custom-token-mismatch',\n  CREDENTIAL_TOO_OLD_LOGIN_AGAIN: 'requires-recent-login',\n  DYNAMIC_LINK_NOT_ACTIVATED: 'dynamic-link-not-activated',\n  EMAIL_CHANGE_NEEDS_VERIFICATION: 'email-change-needs-verification',\n  EMAIL_EXISTS: 'email-already-in-use',\n  EXPIRED_OOB_CODE: 'expired-action-code',\n  EXPIRED_POPUP_REQUEST: 'cancelled-popup-request',\n  INTERNAL_ERROR: 'internal-error',\n  INVALID_API_KEY: 'invalid-api-key',\n  INVALID_APP_CREDENTIAL: 'invalid-app-credential',\n  INVALID_APP_ID: 'invalid-app-id',\n  INVALID_AUTH: 'invalid-user-token',\n  INVALID_AUTH_EVENT: 'invalid-auth-event',\n  INVALID_CERT_HASH: 'invalid-cert-hash',\n  INVALID_CODE: 'invalid-verification-code',\n  INVALID_CONTINUE_URI: 'invalid-continue-uri',\n  INVALID_CORDOVA_CONFIGURATION: 'invalid-cordova-configuration',\n  INVALID_CUSTOM_TOKEN: 'invalid-custom-token',\n  INVALID_DYNAMIC_LINK_DOMAIN: 'invalid-dynamic-link-domain',\n  INVALID_EMAIL: 'invalid-email',\n  INVALID_IDP_RESPONSE: 'invalid-credential',\n  INVALID_MESSAGE_PAYLOAD: 'invalid-message-payload',\n  INVALID_MFA_PENDING_CREDENTIAL: 'invalid-multi-factor-session',\n  INVALID_OAUTH_CLIENT_ID: 'invalid-oauth-client-id',\n  INVALID_OAUTH_PROVIDER: 'invalid-oauth-provider',\n  INVALID_OOB_CODE: 'invalid-action-code',\n  INVALID_ORIGIN: 'unauthorized-domain',\n  INVALID_PASSWORD: 'wrong-password',\n  INVALID_PERSISTENCE: 'invalid-persistence-type',\n  INVALID_PHONE_NUMBER: 'invalid-phone-number',\n  INVALID_PROVIDER_ID: 'invalid-provider-id',\n  INVALID_RECIPIENT_EMAIL: 'invalid-recipient-email',\n  INVALID_SENDER: 'invalid-sender',\n  INVALID_SESSION_INFO: 'invalid-verification-id',\n  INVALID_TENANT_ID: 'invalid-tenant-id',\n  MFA_ENROLLMENT_NOT_FOUND: 'multi-factor-info-not-found',\n  MFA_REQUIRED: 'multi-factor-auth-required',\n  MISSING_ANDROID_PACKAGE_NAME: 'missing-android-pkg-name',\n  MISSING_APP_CREDENTIAL: 'missing-app-credential',\n  MISSING_AUTH_DOMAIN: 'auth-domain-config-required',\n  MISSING_CODE: 'missing-verification-code',\n  MISSING_CONTINUE_URI: 'missing-continue-uri',\n  MISSING_IFRAME_START: 'missing-iframe-start',\n  MISSING_IOS_BUNDLE_ID: 'missing-ios-bundle-id',\n  MISSING_MFA_ENROLLMENT_ID: 'missing-multi-factor-info',\n  MISSING_MFA_PENDING_CREDENTIAL: 'missing-multi-factor-session',\n  MISSING_OR_INVALID_NONCE: 'missing-or-invalid-nonce',\n  MISSING_PHONE_NUMBER: 'missing-phone-number',\n  MISSING_SESSION_INFO: 'missing-verification-id',\n  MODULE_DESTROYED: 'app-deleted',\n  NEED_CONFIRMATION: 'account-exists-with-different-credential',\n  NETWORK_REQUEST_FAILED: 'network-request-failed',\n  NULL_USER: 'null-user',\n  NO_AUTH_EVENT: 'no-auth-event',\n  NO_SUCH_PROVIDER: 'no-such-provider',\n  OPERATION_NOT_ALLOWED: 'operation-not-allowed',\n  OPERATION_NOT_SUPPORTED: 'operation-not-supported-in-this-environment',\n  POPUP_BLOCKED: 'popup-blocked',\n  POPUP_CLOSED_BY_USER: 'popup-closed-by-user',\n  PROVIDER_ALREADY_LINKED: 'provider-already-linked',\n  QUOTA_EXCEEDED: 'quota-exceeded',\n  REDIRECT_CANCELLED_BY_USER: 'redirect-cancelled-by-user',\n  REDIRECT_OPERATION_PENDING: 'redirect-operation-pending',\n  REJECTED_CREDENTIAL: 'rejected-credential',\n  SECOND_FACTOR_EXISTS: 'second-factor-already-in-use',\n  SECOND_FACTOR_LIMIT_EXCEEDED: 'maximum-second-factor-count-exceeded',\n  TENANT_ID_MISMATCH: 'tenant-id-mismatch',\n  TIMEOUT: 'timeout',\n  TOKEN_EXPIRED: 'user-token-expired',\n  TOO_MANY_ATTEMPTS_TRY_LATER: 'too-many-requests',\n  UNAUTHORIZED_DOMAIN: 'unauthorized-continue-uri',\n  UNSUPPORTED_FIRST_FACTOR: 'unsupported-first-factor',\n  UNSUPPORTED_PERSISTENCE: 'unsupported-persistence-type',\n  UNSUPPORTED_TENANT_OPERATION: 'unsupported-tenant-operation',\n  UNVERIFIED_EMAIL: 'unverified-email',\n  USER_CANCELLED: 'user-cancelled',\n  USER_DELETED: 'user-not-found',\n  USER_DISABLED: 'user-disabled',\n  USER_MISMATCH: 'user-mismatch',\n  USER_SIGNED_OUT: 'user-signed-out',\n  WEAK_PASSWORD: 'weak-password',\n  WEB_STORAGE_UNSUPPORTED: 'web-storage-unsupported'\n};\n\n\n/**\n * Map from developer error codes to human-readable error messages.\n * @private {!Object<string, string>}\n */\nfireauth.AuthError.MESSAGES_ = {};\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.ADMIN_ONLY_OPERATION] =\n    'This operation is restricted to administrators only.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.ARGUMENT_ERROR] = '';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.APP_NOT_AUTHORIZED] =\n    'This app, identified by the domain where it\\'s hosted, is not ' +\n    'authorized to use Firebase Authentication with the provided API key. ' +\n    'Review your key configuration in the Google API console.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.APP_NOT_INSTALLED] =\n    'The requested mobile application corresponding to the identifier (' +\n    'Android package name or iOS bundle ID) provided is not installed on ' +\n    'this device.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.CAPTCHA_CHECK_FAILED] =\n    'The reCAPTCHA response token provided is either invalid, expired, ' +\n    'already used or the domain associated with it does not match the list ' +\n    'of whitelisted domains.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.CODE_EXPIRED] =\n    'The SMS code has expired. Please re-send the verification code to try ' +\n    'again.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.CORDOVA_NOT_READY] =\n    'Cordova framework is not ready.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.CORS_UNSUPPORTED] =\n    'This browser is not supported.';\nfireauth.AuthError.MESSAGES_[\n    fireauth.authenum.Error.CREDENTIAL_ALREADY_IN_USE] =\n    'This credential is already associated with a different user account.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.CREDENTIAL_MISMATCH] =\n    'The custom token corresponds to a different audience.';\nfireauth.AuthError.MESSAGES_[\n    fireauth.authenum.Error.CREDENTIAL_TOO_OLD_LOGIN_AGAIN] =\n    'This operation is sensitive and requires recent authentication. Log in ' +\n    'again before retrying this request.';\nfireauth.AuthError.MESSAGES_[\n    fireauth.authenum.Error.DYNAMIC_LINK_NOT_ACTIVATED] = 'Please activate ' +\n    'Dynamic Links in the Firebase Console and agree to the terms and ' +\n    'conditions.';\nfireauth.AuthError.MESSAGES_[\n    fireauth.authenum.Error.EMAIL_CHANGE_NEEDS_VERIFICATION] =\n    'Multi-factor users must always have a verified email.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.EMAIL_EXISTS] =\n    'The email address is already in use by another account.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.EXPIRED_OOB_CODE] =\n    'The action code has expired. ';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.EXPIRED_POPUP_REQUEST] =\n    'This operation has been cancelled due to another conflicting popup ' +\n    'being opened.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.INTERNAL_ERROR] =\n    'An internal error has occurred.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.INVALID_APP_CREDENTIAL] =\n    'The phone verification request contains an invalid application verifier.' +\n    ' The reCAPTCHA token response is either invalid or expired.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.INVALID_APP_ID] =\n    'The mobile app identifier is not registed for the current project.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.INVALID_AUTH] =\n    'This user\\'s credential isn\\'t valid for this project. This can happen ' +\n    'if the user\\'s token has been tampered with, or if the user isn\\'t for ' +\n    'the project associated with this API key.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.INVALID_AUTH_EVENT] =\n    'An internal error has occurred.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.INVALID_CODE] =\n    'The SMS verification code used to create the phone auth credential is ' +\n    'invalid. Please resend the verification code sms and be sure to use the ' +\n    'verification code provided by the user.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.INVALID_CONTINUE_URI] =\n    'The continue URL provided in the request is invalid.';\nfireauth.AuthError.MESSAGES_[\n     fireauth.authenum.Error.INVALID_CORDOVA_CONFIGURATION] = 'The following' +\n    ' Cordova plugins must be installed to enable OAuth sign-in: ' +\n    'cordova-plugin-buildinfo, cordova-universal-links-plugin, ' +\n    'cordova-plugin-browsertab, cordova-plugin-inappbrowser and ' +\n    'cordova-plugin-customurlscheme.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.INVALID_CUSTOM_TOKEN] =\n    'The custom token format is incorrect. Please check the documentation.';\nfireauth.AuthError.MESSAGES_[\n    fireauth.authenum.Error.INVALID_DYNAMIC_LINK_DOMAIN] = 'The provided ' +\n    'dynamic link domain is not configured or authorized for the current ' +\n    'project.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.INVALID_EMAIL] =\n    'The email address is badly formatted.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.INVALID_API_KEY] =\n    'Your API key is invalid, please check you have copied it correctly.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.INVALID_CERT_HASH] =\n    'The SHA-1 certificate hash provided is invalid.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.INVALID_IDP_RESPONSE] =\n    'The supplied auth credential is malformed or has expired.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.INVALID_MESSAGE_PAYLOAD] =\n    'The email template corresponding to this action contains invalid charac' +\n    'ters in its message. Please fix by going to the Auth email templates se' +\n    'ction in the Firebase Console.';\nfireauth.AuthError.MESSAGES_[\n    fireauth.authenum.Error.INVALID_MFA_PENDING_CREDENTIAL] =\n    'The request does not contain a valid proof of first factor successful ' +\n    'sign-in.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.INVALID_OAUTH_PROVIDER] =\n    'EmailAuthProvider is not supported for this operation. This operation ' +\n    'only supports OAuth providers.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.INVALID_OAUTH_CLIENT_ID] =\n    'The OAuth client ID provided is either invalid or does not match the ' +\n    'specified API key.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.INVALID_ORIGIN] =\n    'This domain is not authorized for OAuth operations for your Firebase ' +\n    'project. Edit the list of authorized domains from the Firebase console.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.INVALID_OOB_CODE] =\n    'The action code is invalid. This can happen if the code is malformed, ' +\n    'expired, or has already been used.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.INVALID_PASSWORD] =\n    'The password is invalid or the user does not have a password.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.INVALID_PERSISTENCE] =\n    'The specified persistence type is invalid. It can only be local, ' +\n    'session or none.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.INVALID_PHONE_NUMBER] =\n    'The format of the phone number provided is incorrect. Please enter the ' +\n    'phone number in a format that can be parsed into E.164 format. E.164 ' +\n    'phone numbers are written in the format [+][country code][subscriber ' +\n    'number including area code].';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.INVALID_PROVIDER_ID] =\n    'The specified provider ID is invalid.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.INVALID_RECIPIENT_EMAIL] =\n    'The email corresponding to this action failed to send as the provided ' +\n    'recipient email address is invalid.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.INVALID_SENDER] =\n    'The email template corresponding to this action contains an invalid sen' +\n    'der email or name. Please fix by going to the Auth email templates sect' +\n    'ion in the Firebase Console.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.INVALID_SESSION_INFO] =\n    'The verification ID used to create the phone auth credential is invalid.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.INVALID_TENANT_ID] =\n    'The Auth instance\\'s tenant ID is invalid.';\nfireauth.AuthError.MESSAGES_[\n    fireauth.authenum.Error.MFA_ENROLLMENT_NOT_FOUND] = 'The user does not ' +\n    'have a second factor matching the identifier provided.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.MFA_REQUIRED] =\n    'Proof of ownership of a second factor is required to complete sign-in.';\nfireauth.AuthError.MESSAGES_[\n    fireauth.authenum.Error.MISSING_ANDROID_PACKAGE_NAME] = 'An Android ' +\n    'Package Name must be provided if the Android App is required to be ' +\n    'installed.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.MISSING_AUTH_DOMAIN] =\n    'Be sure to include authDomain when calling firebase.initializeApp(), ' +\n    'by following the instructions in the Firebase console.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.MISSING_APP_CREDENTIAL] =\n    'The phone verification request is missing an application verifier ' +\n    'assertion. A reCAPTCHA response token needs to be provided.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.MISSING_CODE] =\n    'The phone auth credential was created with an empty SMS verification ' +\n    'code.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.MISSING_CONTINUE_URI] =\n    'A continue URL must be provided in the request.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.MISSING_IFRAME_START] =\n    'An internal error has occurred.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.MISSING_IOS_BUNDLE_ID] =\n    'An iOS Bundle ID must be provided if an App Store ID is provided.';\nfireauth.AuthError.MESSAGES_[\n    fireauth.authenum.Error.MISSING_MFA_ENROLLMENT_ID] =\n    'No second factor identifier is provided.';\nfireauth.AuthError.MESSAGES_[\n    fireauth.authenum.Error.MISSING_MFA_PENDING_CREDENTIAL] =\n    'The request is missing proof of first factor successful sign-in.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.MISSING_OR_INVALID_NONCE] =\n    'The request does not contain a valid nonce. This can occur if the ' +\n    'SHA-256 hash of the provided raw nonce does not match the hashed nonce ' +\n    'in the ID token payload.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.MISSING_PHONE_NUMBER] =\n    'To send verification codes, provide a phone number for the recipient.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.MISSING_SESSION_INFO] =\n    'The phone auth credential was created with an empty verification ID.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.MODULE_DESTROYED] =\n    'This instance of FirebaseApp has been deleted.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.NEED_CONFIRMATION] =\n    'An account already exists with the same email address but different ' +\n    'sign-in credentials. Sign in using a provider associated with this ' +\n    'email address.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.NETWORK_REQUEST_FAILED] =\n    'A network error (such as timeout, interrupted connection or ' +\n    'unreachable host) has occurred.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.NO_AUTH_EVENT] =\n    'An internal error has occurred.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.NO_SUCH_PROVIDER] =\n    'User was not linked to an account with the given provider.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.NULL_USER] =\n    'A null user object was provided as the argument for an operation which ' +\n    'requires a non-null user object.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.OPERATION_NOT_ALLOWED] =\n    'The given sign-in provider is disabled for this Firebase project. ' +\n    'Enable it in the Firebase console, under the sign-in method tab of the ' +\n    'Auth section.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.OPERATION_NOT_SUPPORTED] =\n    'This operation is not supported in the environment this application is ' +\n    'running on. \"location.protocol\" must be http, https or chrome-extension' +\n    ' and web storage must be enabled.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.POPUP_BLOCKED] =\n    'Unable to establish a connection with the popup. It may have been ' +\n    'blocked by the browser.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.POPUP_CLOSED_BY_USER] =\n    'The popup has been closed by the user before finalizing the operation.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.PROVIDER_ALREADY_LINKED] =\n    'User can only be linked to one identity for the given provider.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.QUOTA_EXCEEDED] =\n    'The project\\'s quota for this operation has been exceeded.';\nfireauth.AuthError.MESSAGES_[\n    fireauth.authenum.Error.REDIRECT_CANCELLED_BY_USER] =\n    'The redirect operation has been cancelled by the user before finalizing.';\nfireauth.AuthError.MESSAGES_[\n    fireauth.authenum.Error.REDIRECT_OPERATION_PENDING] =\n    'A redirect sign-in operation is already pending.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.REJECTED_CREDENTIAL] =\n    'The request contains malformed or mismatching credentials.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.SECOND_FACTOR_EXISTS] =\n    'The second factor is already enrolled on this account.';\nfireauth.AuthError.MESSAGES_[\n    fireauth.authenum.Error.SECOND_FACTOR_LIMIT_EXCEEDED] =\n    'The maximum allowed number of second factors on a user has been exceeded.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.TENANT_ID_MISMATCH] =\n    'The provided tenant ID does not match the Auth instance\\'s tenant ID';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.TIMEOUT] =\n    'The operation has timed out.';\nfireauth.AuthError.MESSAGES_[\n    fireauth.authenum.Error.TOKEN_EXPIRED] =\n    'The user\\'s credential is no longer valid. The user must sign in again.';\nfireauth.AuthError.MESSAGES_[\n    fireauth.authenum.Error.TOO_MANY_ATTEMPTS_TRY_LATER] =\n    'We have blocked all requests from this device due to unusual activity. ' +\n    'Try again later.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.UNAUTHORIZED_DOMAIN] =\n    'The domain of the continue URL is not whitelisted.  Please whitelist ' +\n    'the domain in the Firebase console.';\nfireauth.AuthError.MESSAGES_[\n    fireauth.authenum.Error.UNSUPPORTED_FIRST_FACTOR] = 'Enrolling a second ' +\n    'factor or signing in with a multi-factor account requires sign-in with ' +\n    'a supported first factor.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.UNSUPPORTED_PERSISTENCE] =\n    'The current environment does not support the specified persistence type.';\nfireauth.AuthError.MESSAGES_[\n    fireauth.authenum.Error.UNSUPPORTED_TENANT_OPERATION] =\n    'This operation is not supported in a multi-tenant context.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.UNVERIFIED_EMAIL] =\n    'The operation requires a verified email.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.USER_CANCELLED] =\n    'The user did not grant your application the permissions it requested.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.USER_DELETED] =\n    'There is no user record corresponding to this identifier. The user may ' +\n    'have been deleted.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.USER_DISABLED] =\n    'The user account has been disabled by an administrator.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.USER_MISMATCH] =\n    'The supplied credentials do not correspond to the previously signed in ' +\n    'user.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.USER_SIGNED_OUT] = '';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.WEAK_PASSWORD] =\n    'The password must be 6 characters long or more.';\nfireauth.AuthError.MESSAGES_[fireauth.authenum.Error.WEB_STORAGE_UNSUPPORTED] =\n    'This browser is not supported or 3rd party cookies and data may be ' +\n    'disabled.';\n","/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines all common constants and enums used by firebase-auth.\n */\n\ngoog.provide('fireauth.constants');\ngoog.provide('fireauth.constants.AuthEventType');\n\n\n/**\n * Enums for authentication operation types.\n * @enum {string}\n */\nfireauth.constants.OperationType = {\n  LINK: 'link',\n  REAUTHENTICATE: 'reauthenticate',\n  SIGN_IN: 'signIn'\n};\n\n\n/**\n * Events dispatched firebase.auth.Auth.\n * @enum {string}\n */\nfireauth.constants.AuthEventType = {\n  /** Dispatched when emulator config is changed. */\n  EMULATOR_CONFIG_CHANGED: 'emulatorConfigChanged',\n  /** Dispatched when Firebase framework is changed. */\n  FRAMEWORK_CHANGED: 'frameworkChanged',\n  /** Dispatched when language code is changed. */\n  LANGUAGE_CODE_CHANGED: 'languageCodeChanged'\n};\n\n\n/**\n * Enums for all second factor types.\n * @enum {string}\n */\nfireauth.constants.SecondFactorType = {\n  PHONE: 'phone'\n};\n\n\n/**\n * The settings of an Auth endpoint. The fields are:\n * <ul>\n * <li>firebaseAuthEndpoint: defines the Firebase Auth backend endpoint for\n *     specified endpoint type.</li>\n * <li>secureTokenEndpoint: defines the secure token backend endpoint for\n *     specified endpoint type.</li>\n * <li>identityPlatformEndpoint: defines the Identity Platform backend endpoint\n *     for specified endpoint type.</li>\n * <li>id: defines the endpoint identifier.</li>\n * </ul>\n * @typedef {{\n *   firebaseAuthEndpoint: string,\n *   secureTokenEndpoint: string,\n *   identityPlatformEndpoint: string,\n *   id: string\n * }}\n */\nfireauth.constants.EndpointSettings;\n\n\n/**\n * The different endpoints for Firebase Auth backend.\n * @enum {!fireauth.constants.EndpointSettings}\n */\nfireauth.constants.Endpoint = {\n  // TODO: this is no longer needed now that client endpoint migration is\n  // completed.\n  BOQ: {\n    firebaseAuthEndpoint: 'https://staging-identitytoolkit.sandbox.googleapi' +\n        's.com/identitytoolkit/v3/relyingparty/',\n    secureTokenEndpoint: 'https://staging-securetoken.sandbox.googleapis.com' +\n        '/v1/token',\n    identityPlatformEndpoint:\n        'https://staging-identitytoolkit.sandbox.googleapis.com/v2/',\n    id: 'b'\n  },\n  PRODUCTION: {\n    firebaseAuthEndpoint: 'https://www.googleapis.com/identitytoolkit/v3/' +\n        'relyingparty/',\n    secureTokenEndpoint: 'https://securetoken.googleapis.com/v1/token',\n    identityPlatformEndpoint:\n        'https://identitytoolkit.googleapis.com/v2/',\n    id: 'p'\n  },\n  STAGING: {\n    firebaseAuthEndpoint: 'https://staging-www.sandbox.googleapis.com/' +\n        'identitytoolkit/v3/relyingparty/',\n    secureTokenEndpoint: 'https://staging-securetoken.sandbox.googleapis.com' +\n        '/v1/token',\n    identityPlatformEndpoint:\n        'https://staging-identitytoolkit.sandbox.googleapis.com/v2/',\n    id: 's'\n  },\n  TEST: {\n    firebaseAuthEndpoint: 'https://www-googleapis-test.sandbox.google.com/' +\n        'identitytoolkit/v3/relyingparty/',\n    secureTokenEndpoint: 'https://test-securetoken.sandbox.googleapis.com/v1' +\n        '/token',\n    identityPlatformEndpoint:\n        'https://test-identitytoolkit.sandbox.googleapis.com/v2/',\n    id: 't'\n  }\n};\n\n\n/**\n * Returns the endpoint specific RpcHandler configuration.\n * @param {?string=} opt_id The identifier of the endpoint type if available.\n * @return {?Object|undefined} The RpcHandler endpoint configuration object.\n */\nfireauth.constants.getEndpointConfig = function(opt_id) {\n  for (var endpointKey in fireauth.constants.Endpoint) {\n    if (fireauth.constants.Endpoint[endpointKey].id === opt_id) {\n      var endpoint = fireauth.constants.Endpoint[endpointKey];\n      return {\n        'firebaseEndpoint': endpoint.firebaseAuthEndpoint,\n        'secureTokenEndpoint': endpoint.secureTokenEndpoint,\n        'identityPlatformEndpoint': endpoint.identityPlatformEndpoint\n      };\n    }\n  }\n  return null;\n};\n\n\n/**\n * Returns the validated endpoint identifier. Undefined if the provided one is\n * invalid.\n * @param {?string=} opt_id The identifier of the endpoint type if available.\n * @return {string|undefined} The validated endpoint ID. If not valid,\n *     undefined.\n */\nfireauth.constants.getEndpointId = function(opt_id) {\n  if (opt_id && fireauth.constants.getEndpointConfig(opt_id)) {\n    return opt_id;\n  }\n  return undefined;\n};\n\n\n/** @const {string|undefined} The current client endpoint. */\nfireauth.constants.clientEndpoint = fireauth.constants.getEndpointId('__EID__');\n\n\n/** @const {string} The required SAML provider ID prefix. */\nfireauth.constants.SAML_PREFIX = 'saml.';\n\n\n/** @const {string} The required OIDC provider ID prefix. */\nfireauth.constants.OIDC_PREFIX = 'oidc.';\n\n/**\n * The settings of an Auth emulator. The fields are:\n * <ul>\n * <li>url: defines the URL where the emulator is running.</li>\n * <li>disableWarnings: if true, banner is not shown on the page.</li>\n * </ul>\n * @typedef {{\n *   url: string,\n *   disableWarnings: boolean,\n * }}\n */\nfireauth.constants.EmulatorSettings;\n \n \n/**\n * The (externally visible) emulator configuration, used for\n * getEmulatorConfig(). The fields are:\n * <ul>\n * <li>protocol: the protocol used by the emulator (http or https).</li>\n * <li>host: the host used to reach the emulator.</li>\n * <li>port: the port used to reach the emulator.</li>\n * <li>options: a list of options used to configure the SDK's interaction with\n * the emulator.</li>\n * </ul>\n * @typedef {{\n *   protocol: string,\n *   host: string,\n *   port: (number|null),\n *   options: {\n *     disableWarnings: boolean,\n *   }\n * }}\n */\nfireauth.constants.EmulatorConfig;","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\ngoog.provide('goog.Thenable');\n\n/** @suppress {extraRequire} used in complex type */\ngoog.requireType('goog.Promise');  // for the type reference.\n\n\n\n/**\n * Provides a more strict interface for Thenables in terms of\n * http://promisesaplus.com for interop with {@see goog.Promise}.\n *\n * @interface\n * @extends {IThenable<TYPE>}\n * @template TYPE\n */\ngoog.Thenable = function() {};\n\n\n/**\n * Adds callbacks that will operate on the result of the Thenable, returning a\n * new child Promise.\n *\n * If the Thenable is fulfilled, the `onFulfilled` callback will be\n * invoked with the fulfillment value as argument, and the child Promise will\n * be fulfilled with the return value of the callback. If the callback throws\n * an exception, the child Promise will be rejected with the thrown value\n * instead.\n *\n * If the Thenable is rejected, the `onRejected` callback will be invoked with\n * the rejection reason as argument. Similar to the fulfilled case, the child\n * Promise will then be resolved with the return value of the callback, or\n * rejected with the thrown value if the callback throws an exception.\n *\n * @param {?(function(this:THIS, TYPE): VALUE)=} opt_onFulfilled A\n *     function that will be invoked with the fulfillment value if the Promise\n *     is fulfilled.\n * @param {?(function(this:THIS, *): *)=} opt_onRejected A function that will\n *     be invoked with the rejection reason if the Promise is rejected.\n * @param {THIS=} opt_context An optional context object that will be the\n *     execution context for the callbacks. By default, functions are executed\n *     with the default this.\n *\n * @return {RESULT} A new Promise that will receive the result\n *     of the fulfillment or rejection callback.\n * @template VALUE\n * @template THIS\n *\n * When a Promise (or thenable) is returned from the fulfilled callback,\n * the result is the payload of that promise, not the promise itself.\n *\n * @template RESULT := type('goog.Promise',\n *     cond(isUnknown(VALUE), unknown(),\n *       mapunion(VALUE, (V) =>\n *         cond(isTemplatized(V) && sub(rawTypeOf(V), 'IThenable'),\n *           templateTypeOf(V, 0),\n *           cond(sub(V, 'Thenable'),\n *              unknown(),\n *              V)))))\n *  =:\n *\n */\ngoog.Thenable.prototype.then = function(\n    opt_onFulfilled, opt_onRejected, opt_context) {};\n\n\n/**\n * An expando property to indicate that an object implements\n * `goog.Thenable`.\n *\n * {@see addImplementation}.\n *\n * @const\n */\ngoog.Thenable.IMPLEMENTED_BY_PROP = '$goog_Thenable';\n\n\n/**\n * Marks a given class (constructor) as an implementation of Thenable, so\n * that we can query that fact at runtime. The class must have already\n * implemented the interface.\n * Exports a 'then' method on the constructor prototype, so that the objects\n * also implement the extern {@see goog.Thenable} interface for interop with\n * other Promise implementations.\n * @param {function(new:goog.Thenable,...?)} ctor The class constructor. The\n *     corresponding class must have already implemented the interface.\n */\ngoog.Thenable.addImplementation = function(ctor) {\n  if (COMPILED) {\n    ctor.prototype[goog.Thenable.IMPLEMENTED_BY_PROP] = true;\n  } else {\n    // Avoids dictionary access in uncompiled mode.\n    ctor.prototype.$goog_Thenable = true;\n  }\n};\n\n\n/**\n * @param {?} object\n * @return {boolean} Whether a given instance implements `goog.Thenable`.\n *     The class/superclass of the instance must call `addImplementation`.\n */\ngoog.Thenable.isImplementedBy = function(object) {\n  if (!object) {\n    return false;\n  }\n  try {\n    if (COMPILED) {\n      return !!object[goog.Thenable.IMPLEMENTED_BY_PROP];\n    }\n    return !!object.$goog_Thenable;\n  } catch (e) {\n    // Property access seems to be forbidden.\n    return false;\n  }\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Provides a base class for custom Error objects such that the\n * stack is correctly maintained.\n *\n * You should never need to throw goog.debug.Error(msg) directly, Error(msg) is\n * sufficient.\n */\n\ngoog.provide('goog.debug.Error');\n\n\n\n/**\n * Base class for custom error objects.\n * @param {*=} opt_msg The message associated with the error.\n * @constructor\n * @extends {Error}\n */\ngoog.debug.Error = function(opt_msg) {\n\n  // Attempt to ensure there is a stack trace.\n  if (Error.captureStackTrace) {\n    Error.captureStackTrace(this, goog.debug.Error);\n  } else {\n    const stack = new Error().stack;\n    if (stack) {\n      /** @override */\n      this.stack = stack;\n    }\n  }\n\n  if (opt_msg) {\n    /** @override */\n    this.message = String(opt_msg);\n  }\n\n  /**\n   * Whether to report this error to the server. Setting this to false will\n   * cause the error reporter to not report the error back to the server,\n   * which can be useful if the client knows that the error has already been\n   * logged on the server.\n   * @type {boolean}\n   */\n  this.reportErrorToServer = true;\n};\ngoog.inherits(goog.debug.Error, Error);\n\n\n/** @override */\ngoog.debug.Error.prototype.name = 'CustomError';\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Utilities to check the preconditions, postconditions and\n * invariants runtime.\n *\n * Methods in this package are given special treatment by the compiler\n * for type-inference. For example, <code>goog.asserts.assert(foo)</code>\n * will make the compiler treat <code>foo</code> as non-nullable. Similarly,\n * <code>goog.asserts.assertNumber(foo)</code> informs the compiler about the\n * type of <code>foo</code>. Where applicable, such assertions are preferable to\n * casts by jsdoc with <code>@type</code>.\n *\n * The compiler has an option to disable asserts. So code like:\n * <code>\n * var x = goog.asserts.assert(foo());\n * goog.asserts.assert(bar());\n * </code>\n * will be transformed into:\n * <code>\n * var x = foo();\n * </code>\n * The compiler will leave in foo() (because its return value is used),\n * but it will remove bar() because it assumes it does not have side-effects.\n *\n * Additionally, note the compiler will consider the type to be \"tightened\" for\n * all statements <em>after</em> the assertion. For example:\n * <code>\n * const /** ?Object &#ast;/ value = foo();\n * goog.asserts.assert(value);\n * // \"value\" is of type {!Object} at this point.\n * </code>\n */\n\ngoog.provide('goog.asserts');\ngoog.provide('goog.asserts.AssertionError');\n\ngoog.require('goog.debug.Error');\ngoog.require('goog.dom.NodeType');\n\n\n/**\n * @define {boolean} Whether to strip out asserts or to leave them in.\n */\ngoog.asserts.ENABLE_ASSERTS =\n    goog.define('goog.asserts.ENABLE_ASSERTS', goog.DEBUG);\n\n\n\n/**\n * Error object for failed assertions.\n * @param {string} messagePattern The pattern that was used to form message.\n * @param {!Array<*>} messageArgs The items to substitute into the pattern.\n * @constructor\n * @extends {goog.debug.Error}\n * @final\n */\ngoog.asserts.AssertionError = function(messagePattern, messageArgs) {\n  goog.debug.Error.call(this, goog.asserts.subs_(messagePattern, messageArgs));\n\n  /**\n   * The message pattern used to format the error message. Error handlers can\n   * use this to uniquely identify the assertion.\n   * @type {string}\n   */\n  this.messagePattern = messagePattern;\n};\ngoog.inherits(goog.asserts.AssertionError, goog.debug.Error);\n\n\n/** @override */\ngoog.asserts.AssertionError.prototype.name = 'AssertionError';\n\n\n/**\n * The default error handler.\n * @param {!goog.asserts.AssertionError} e The exception to be handled.\n */\ngoog.asserts.DEFAULT_ERROR_HANDLER = function(e) {\n  throw e;\n};\n\n\n/**\n * The handler responsible for throwing or logging assertion errors.\n * @private {function(!goog.asserts.AssertionError)}\n */\ngoog.asserts.errorHandler_ = goog.asserts.DEFAULT_ERROR_HANDLER;\n\n\n/**\n * Does simple python-style string substitution.\n * subs(\"foo%s hot%s\", \"bar\", \"dog\") becomes \"foobar hotdog\".\n * @param {string} pattern The string containing the pattern.\n * @param {!Array<*>} subs The items to substitute into the pattern.\n * @return {string} A copy of `str` in which each occurrence of\n *     {@code %s} has been replaced an argument from `var_args`.\n * @private\n */\ngoog.asserts.subs_ = function(pattern, subs) {\n  var splitParts = pattern.split('%s');\n  var returnString = '';\n\n  // Replace up to the last split part. We are inserting in the\n  // positions between split parts.\n  var subLast = splitParts.length - 1;\n  for (var i = 0; i < subLast; i++) {\n    // keep unsupplied as '%s'\n    var sub = (i < subs.length) ? subs[i] : '%s';\n    returnString += splitParts[i] + sub;\n  }\n  return returnString + splitParts[subLast];\n};\n\n\n/**\n * Throws an exception with the given message and \"Assertion failed\" prefixed\n * onto it.\n * @param {string} defaultMessage The message to use if givenMessage is empty.\n * @param {Array<*>} defaultArgs The substitution arguments for defaultMessage.\n * @param {string|undefined} givenMessage Message supplied by the caller.\n * @param {Array<*>} givenArgs The substitution arguments for givenMessage.\n * @throws {goog.asserts.AssertionError} When the value is not a number.\n * @private\n */\ngoog.asserts.doAssertFailure_ = function(\n    defaultMessage, defaultArgs, givenMessage, givenArgs) {\n  var message = 'Assertion failed';\n  if (givenMessage) {\n    message += ': ' + givenMessage;\n    var args = givenArgs;\n  } else if (defaultMessage) {\n    message += ': ' + defaultMessage;\n    args = defaultArgs;\n  }\n  // The '' + works around an Opera 10 bug in the unit tests. Without it,\n  // a stack trace is added to var message above. With this, a stack trace is\n  // not added until this line (it causes the extra garbage to be added after\n  // the assertion message instead of in the middle of it).\n  var e = new goog.asserts.AssertionError('' + message, args || []);\n  goog.asserts.errorHandler_(e);\n};\n\n\n/**\n * Sets a custom error handler that can be used to customize the behavior of\n * assertion failures, for example by turning all assertion failures into log\n * messages.\n * @param {function(!goog.asserts.AssertionError)} errorHandler\n */\ngoog.asserts.setErrorHandler = function(errorHandler) {\n  if (goog.asserts.ENABLE_ASSERTS) {\n    goog.asserts.errorHandler_ = errorHandler;\n  }\n};\n\n\n/**\n * Checks if the condition evaluates to true if goog.asserts.ENABLE_ASSERTS is\n * true.\n * @template T\n * @param {T} condition The condition to check.\n * @param {string=} opt_message Error message in case of failure.\n * @param {...*} var_args The items to substitute into the failure message.\n * @return {T} The value of the condition.\n * @throws {goog.asserts.AssertionError} When the condition evaluates to false.\n * @closurePrimitive {asserts.truthy}\n */\ngoog.asserts.assert = function(condition, opt_message, var_args) {\n  if (goog.asserts.ENABLE_ASSERTS && !condition) {\n    goog.asserts.doAssertFailure_(\n        '', null, opt_message, Array.prototype.slice.call(arguments, 2));\n  }\n  return condition;\n};\n\n\n/**\n * Checks if `value` is `null` or `undefined` if goog.asserts.ENABLE_ASSERTS is\n * true.\n *\n * @param {T} value The value to check.\n * @param {string=} opt_message Error message in case of failure.\n * @param {...*} var_args The items to substitute into the failure message.\n * @return {R} `value` with its type narrowed to exclude `null` and `undefined`.\n *\n * @template T\n * @template R :=\n *     mapunion(T, (V) =>\n *         cond(eq(V, 'null'),\n *             none(),\n *             cond(eq(V, 'undefined'),\n *                 none(),\n *                 V)))\n *  =:\n *\n * @throws {!goog.asserts.AssertionError} When `value` is `null` or `undefined`.\n * @closurePrimitive {asserts.matchesReturn}\n */\ngoog.asserts.assertExists = function(value, opt_message, var_args) {\n  if (goog.asserts.ENABLE_ASSERTS && value == null) {\n    goog.asserts.doAssertFailure_(\n        'Expected to exist: %s.', [value], opt_message,\n        Array.prototype.slice.call(arguments, 2));\n  }\n  return value;\n};\n\n\n/**\n * Fails if goog.asserts.ENABLE_ASSERTS is true. This function is useful in case\n * when we want to add a check in the unreachable area like switch-case\n * statement:\n *\n * <pre>\n *  switch(type) {\n *    case FOO: doSomething(); break;\n *    case BAR: doSomethingElse(); break;\n *    default: goog.asserts.fail('Unrecognized type: ' + type);\n *      // We have only 2 types - \"default:\" section is unreachable code.\n *  }\n * </pre>\n *\n * @param {string=} opt_message Error message in case of failure.\n * @param {...*} var_args The items to substitute into the failure message.\n * @throws {goog.asserts.AssertionError} Failure.\n * @closurePrimitive {asserts.fail}\n */\ngoog.asserts.fail = function(opt_message, var_args) {\n  if (goog.asserts.ENABLE_ASSERTS) {\n    goog.asserts.errorHandler_(new goog.asserts.AssertionError(\n        'Failure' + (opt_message ? ': ' + opt_message : ''),\n        Array.prototype.slice.call(arguments, 1)));\n  }\n};\n\n\n/**\n * Checks if the value is a number if goog.asserts.ENABLE_ASSERTS is true.\n * @param {*} value The value to check.\n * @param {string=} opt_message Error message in case of failure.\n * @param {...*} var_args The items to substitute into the failure message.\n * @return {number} The value, guaranteed to be a number when asserts enabled.\n * @throws {goog.asserts.AssertionError} When the value is not a number.\n * @closurePrimitive {asserts.matchesReturn}\n */\ngoog.asserts.assertNumber = function(value, opt_message, var_args) {\n  if (goog.asserts.ENABLE_ASSERTS && typeof value !== 'number') {\n    goog.asserts.doAssertFailure_(\n        'Expected number but got %s: %s.', [goog.typeOf(value), value],\n        opt_message, Array.prototype.slice.call(arguments, 2));\n  }\n  return /** @type {number} */ (value);\n};\n\n\n/**\n * Checks if the value is a string if goog.asserts.ENABLE_ASSERTS is true.\n * @param {*} value The value to check.\n * @param {string=} opt_message Error message in case of failure.\n * @param {...*} var_args The items to substitute into the failure message.\n * @return {string} The value, guaranteed to be a string when asserts enabled.\n * @throws {goog.asserts.AssertionError} When the value is not a string.\n * @closurePrimitive {asserts.matchesReturn}\n */\ngoog.asserts.assertString = function(value, opt_message, var_args) {\n  if (goog.asserts.ENABLE_ASSERTS && typeof value !== 'string') {\n    goog.asserts.doAssertFailure_(\n        'Expected string but got %s: %s.', [goog.typeOf(value), value],\n        opt_message, Array.prototype.slice.call(arguments, 2));\n  }\n  return /** @type {string} */ (value);\n};\n\n\n/**\n * Checks if the value is a function if goog.asserts.ENABLE_ASSERTS is true.\n * @param {*} value The value to check.\n * @param {string=} opt_message Error message in case of failure.\n * @param {...*} var_args The items to substitute into the failure message.\n * @return {!Function} The value, guaranteed to be a function when asserts\n *     enabled.\n * @throws {goog.asserts.AssertionError} When the value is not a function.\n * @closurePrimitive {asserts.matchesReturn}\n */\ngoog.asserts.assertFunction = function(value, opt_message, var_args) {\n  if (goog.asserts.ENABLE_ASSERTS && !goog.isFunction(value)) {\n    goog.asserts.doAssertFailure_(\n        'Expected function but got %s: %s.', [goog.typeOf(value), value],\n        opt_message, Array.prototype.slice.call(arguments, 2));\n  }\n  return /** @type {!Function} */ (value);\n};\n\n\n/**\n * Checks if the value is an Object if goog.asserts.ENABLE_ASSERTS is true.\n * @param {*} value The value to check.\n * @param {string=} opt_message Error message in case of failure.\n * @param {...*} var_args The items to substitute into the failure message.\n * @return {!Object} The value, guaranteed to be a non-null object.\n * @throws {goog.asserts.AssertionError} When the value is not an object.\n * @closurePrimitive {asserts.matchesReturn}\n */\ngoog.asserts.assertObject = function(value, opt_message, var_args) {\n  if (goog.asserts.ENABLE_ASSERTS && !goog.isObject(value)) {\n    goog.asserts.doAssertFailure_(\n        'Expected object but got %s: %s.', [goog.typeOf(value), value],\n        opt_message, Array.prototype.slice.call(arguments, 2));\n  }\n  return /** @type {!Object} */ (value);\n};\n\n\n/**\n * Checks if the value is an Array if goog.asserts.ENABLE_ASSERTS is true.\n * @param {*} value The value to check.\n * @param {string=} opt_message Error message in case of failure.\n * @param {...*} var_args The items to substitute into the failure message.\n * @return {!Array<?>} The value, guaranteed to be a non-null array.\n * @throws {goog.asserts.AssertionError} When the value is not an array.\n * @closurePrimitive {asserts.matchesReturn}\n */\ngoog.asserts.assertArray = function(value, opt_message, var_args) {\n  if (goog.asserts.ENABLE_ASSERTS && !Array.isArray(value)) {\n    goog.asserts.doAssertFailure_(\n        'Expected array but got %s: %s.', [goog.typeOf(value), value],\n        opt_message, Array.prototype.slice.call(arguments, 2));\n  }\n  return /** @type {!Array<?>} */ (value);\n};\n\n\n/**\n * Checks if the value is a boolean if goog.asserts.ENABLE_ASSERTS is true.\n * @param {*} value The value to check.\n * @param {string=} opt_message Error message in case of failure.\n * @param {...*} var_args The items to substitute into the failure message.\n * @return {boolean} The value, guaranteed to be a boolean when asserts are\n *     enabled.\n * @throws {goog.asserts.AssertionError} When the value is not a boolean.\n * @closurePrimitive {asserts.matchesReturn}\n */\ngoog.asserts.assertBoolean = function(value, opt_message, var_args) {\n  if (goog.asserts.ENABLE_ASSERTS && typeof value !== 'boolean') {\n    goog.asserts.doAssertFailure_(\n        'Expected boolean but got %s: %s.', [goog.typeOf(value), value],\n        opt_message, Array.prototype.slice.call(arguments, 2));\n  }\n  return /** @type {boolean} */ (value);\n};\n\n\n/**\n * Checks if the value is a DOM Element if goog.asserts.ENABLE_ASSERTS is true.\n * @param {*} value The value to check.\n * @param {string=} opt_message Error message in case of failure.\n * @param {...*} var_args The items to substitute into the failure message.\n * @return {!Element} The value, likely to be a DOM Element when asserts are\n *     enabled.\n * @throws {goog.asserts.AssertionError} When the value is not an Element.\n * @closurePrimitive {asserts.matchesReturn}\n * @deprecated Use goog.asserts.dom.assertIsElement instead.\n */\ngoog.asserts.assertElement = function(value, opt_message, var_args) {\n  if (goog.asserts.ENABLE_ASSERTS &&\n      (!goog.isObject(value) ||\n       /** @type {!Node} */ (value).nodeType != goog.dom.NodeType.ELEMENT)) {\n    goog.asserts.doAssertFailure_(\n        'Expected Element but got %s: %s.', [goog.typeOf(value), value],\n        opt_message, Array.prototype.slice.call(arguments, 2));\n  }\n  return /** @type {!Element} */ (value);\n};\n\n\n/**\n * Checks if the value is an instance of the user-defined type if\n * goog.asserts.ENABLE_ASSERTS is true.\n *\n * The compiler may tighten the type returned by this function.\n *\n * Do not use this to ensure a value is an HTMLElement or a subclass! Cross-\n * document DOM inherits from separate - though identical - browser classes, and\n * such a check will unexpectedly fail. Please use the methods in\n * goog.asserts.dom for these purposes.\n *\n * @param {?} value The value to check.\n * @param {function(new: T, ...)} type A user-defined constructor.\n * @param {string=} opt_message Error message in case of failure.\n * @param {...*} var_args The items to substitute into the failure message.\n * @throws {goog.asserts.AssertionError} When the value is not an instance of\n *     type.\n * @return {T}\n * @template T\n * @closurePrimitive {asserts.matchesReturn}\n */\ngoog.asserts.assertInstanceof = function(value, type, opt_message, var_args) {\n  if (goog.asserts.ENABLE_ASSERTS && !(value instanceof type)) {\n    goog.asserts.doAssertFailure_(\n        'Expected instanceof %s but got %s.',\n        [goog.asserts.getType_(type), goog.asserts.getType_(value)],\n        opt_message, Array.prototype.slice.call(arguments, 3));\n  }\n  return value;\n};\n\n\n/**\n * Checks whether the value is a finite number, if goog.asserts.ENABLE_ASSERTS\n * is true.\n *\n * @param {*} value The value to check.\n * @param {string=} opt_message Error message in case of failure.\n * @param {...*} var_args The items to substitute into the failure message.\n * @throws {goog.asserts.AssertionError} When the value is not a number, or is\n *     a non-finite number such as NaN, Infinity or -Infinity.\n * @return {number} The value initially passed in.\n */\ngoog.asserts.assertFinite = function(value, opt_message, var_args) {\n  if (goog.asserts.ENABLE_ASSERTS &&\n      (typeof value != 'number' || !isFinite(value))) {\n    goog.asserts.doAssertFailure_(\n        'Expected %s to be a finite number but it is not.', [value],\n        opt_message, Array.prototype.slice.call(arguments, 2));\n  }\n  return /** @type {number} */ (value);\n};\n\n/**\n * Checks that no enumerable keys are present in Object.prototype. Such keys\n * would break most code that use {@code for (var ... in ...)} loops.\n */\ngoog.asserts.assertObjectPrototypeIsIntact = function() {\n  for (var key in Object.prototype) {\n    goog.asserts.fail(key + ' should not be enumerable in Object.prototype.');\n  }\n};\n\n\n/**\n * Returns the type of a value. If a constructor is passed, and a suitable\n * string cannot be found, 'unknown type name' will be returned.\n * @param {*} value A constructor, object, or primitive.\n * @return {string} The best display name for the value, or 'unknown type name'.\n * @private\n */\ngoog.asserts.getType_ = function(value) {\n  if (value instanceof Function) {\n    return value.displayName || value.name || 'unknown type name';\n  } else if (value instanceof Object) {\n    return /** @type {string} */ (value.constructor.displayName) ||\n        value.constructor.name || Object.prototype.toString.call(value);\n  } else {\n    return value === null ? 'null' : typeof value;\n  }\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Simple freelist.\n *\n * An anterative to goog.structs.SimplePool, it imposes the requirement that the\n * objects in the list contain a \"next\" property that can be used to maintain\n * the pool.\n */\n\ngoog.provide('goog.async.FreeList');\n\n\n/**\n * @template ITEM\n */\ngoog.async.FreeList = class {\n  /**\n   * @param {function():ITEM} create\n   * @param {function(ITEM):void} reset\n   * @param {number} limit\n   */\n  constructor(create, reset, limit) {\n    /** @private @const {number} */\n    this.limit_ = limit;\n    /** @private @const {function()} */\n    this.create_ = create;\n    /** @private @const {function(ITEM):void} */\n    this.reset_ = reset;\n\n    /** @private {number} */\n    this.occupants_ = 0;\n    /** @private {ITEM} */\n    this.head_ = null;\n  }\n\n  /**\n   * @return {ITEM}\n   */\n  get() {\n    let item;\n    if (this.occupants_ > 0) {\n      this.occupants_--;\n      item = this.head_;\n      this.head_ = item.next;\n      item.next = null;\n    } else {\n      item = this.create_();\n    }\n    return item;\n  }\n\n  /**\n   * @param {ITEM} item An item available for possible future reuse.\n   */\n  put(item) {\n    this.reset_(item);\n    if (this.occupants_ < this.limit_) {\n      this.occupants_++;\n      item.next = this.head_;\n      this.head_ = item;\n    }\n  }\n\n  /**\n   * Visible for testing.\n   * @package\n   * @return {number}\n   */\n  occupants() {\n    return this.occupants_;\n  }\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\ngoog.provide('goog.async.WorkItem');\ngoog.provide('goog.async.WorkQueue');\n\ngoog.require('goog.asserts');\ngoog.require('goog.async.FreeList');\n\n\n// TODO(johnlenz): generalize the WorkQueue if this is used by more\n// than goog.async.run.\n\n\n\n/**\n * A low GC workqueue. The key elements of this design:\n *   - avoids the need for goog.bind or equivalent by carrying scope\n *   - avoids the need for array reallocation by using a linked list\n *   - minimizes work entry objects allocation by recycling objects\n * @constructor\n * @final\n * @struct\n */\ngoog.async.WorkQueue = function() {\n  this.workHead_ = null;\n  this.workTail_ = null;\n};\n\n\n/** @define {number} The maximum number of entries to keep for recycling. */\ngoog.async.WorkQueue.DEFAULT_MAX_UNUSED =\n    goog.define('goog.async.WorkQueue.DEFAULT_MAX_UNUSED', 100);\n\n\n/** @const @private {goog.async.FreeList<goog.async.WorkItem>} */\ngoog.async.WorkQueue.freelist_ = new goog.async.FreeList(\n    function() { return new goog.async.WorkItem(); },\n    function(item) { item.reset(); }, goog.async.WorkQueue.DEFAULT_MAX_UNUSED);\n\n\n/**\n * @param {function()} fn\n * @param {Object|null|undefined} scope\n */\ngoog.async.WorkQueue.prototype.add = function(fn, scope) {\n  var item = this.getUnusedItem_();\n  item.set(fn, scope);\n\n  if (this.workTail_) {\n    this.workTail_.next = item;\n    this.workTail_ = item;\n  } else {\n    goog.asserts.assert(!this.workHead_);\n    this.workHead_ = item;\n    this.workTail_ = item;\n  }\n};\n\n\n/**\n * @return {goog.async.WorkItem}\n */\ngoog.async.WorkQueue.prototype.remove = function() {\n  var item = null;\n\n  if (this.workHead_) {\n    item = this.workHead_;\n    this.workHead_ = this.workHead_.next;\n    if (!this.workHead_) {\n      this.workTail_ = null;\n    }\n    item.next = null;\n  }\n  return item;\n};\n\n\n/**\n * @param {goog.async.WorkItem} item\n */\ngoog.async.WorkQueue.prototype.returnUnused = function(item) {\n  goog.async.WorkQueue.freelist_.put(item);\n};\n\n\n/**\n * @return {goog.async.WorkItem}\n * @private\n */\ngoog.async.WorkQueue.prototype.getUnusedItem_ = function() {\n  return goog.async.WorkQueue.freelist_.get();\n};\n\n\n\n/**\n * @constructor\n * @final\n * @struct\n */\ngoog.async.WorkItem = function() {\n  /** @type {?function()} */\n  this.fn = null;\n  /** @type {?Object|null|undefined} */\n  this.scope = null;\n  /** @type {?goog.async.WorkItem} */\n  this.next = null;\n};\n\n\n/**\n * @param {function()} fn\n * @param {Object|null|undefined} scope\n */\ngoog.async.WorkItem.prototype.set = function(fn, scope) {\n  this.fn = fn;\n  this.scope = scope;\n  this.next = null;\n};\n\n\n/** Reset the work item so they don't prevent GC before reuse */\ngoog.async.WorkItem.prototype.reset = function() {\n  this.fn = null;\n  this.scope = null;\n  this.next = null;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Utilities for manipulating arrays.\n */\n\n\ngoog.module('goog.array');\ngoog.module.declareLegacyNamespace();\n\nconst asserts = goog.require('goog.asserts');\n\n\n/**\n * @define {boolean} NATIVE_ARRAY_PROTOTYPES indicates whether the code should\n * rely on Array.prototype functions, if available.\n *\n * The Array.prototype functions can be defined by external libraries like\n * Prototype and setting this flag to false forces closure to use its own\n * goog.array implementation.\n *\n * If your javascript can be loaded by a third party site and you are wary about\n * relying on the prototype functions, specify\n * \"--define goog.NATIVE_ARRAY_PROTOTYPES=false\" to the JSCompiler.\n *\n * Setting goog.TRUSTED_SITE to false will automatically set\n * NATIVE_ARRAY_PROTOTYPES to false.\n */\ngoog.NATIVE_ARRAY_PROTOTYPES =\n    goog.define('goog.NATIVE_ARRAY_PROTOTYPES', goog.TRUSTED_SITE);\n\n\n/**\n * @define {boolean} If true, JSCompiler will use the native implementation of\n * array functions where appropriate (e.g., `Array#filter`) and remove the\n * unused pure JS implementation.\n */\nconst ASSUME_NATIVE_FUNCTIONS = goog.define(\n    'goog.array.ASSUME_NATIVE_FUNCTIONS', goog.FEATURESET_YEAR > 2012);\nexports.ASSUME_NATIVE_FUNCTIONS = ASSUME_NATIVE_FUNCTIONS;\n\n\n/**\n * Returns the last element in an array without removing it.\n * Same as {@link goog.array.last}.\n * @param {IArrayLike<T>|string} array The array.\n * @return {T} Last item in array.\n * @template T\n */\nfunction peek(array) {\n  return array[array.length - 1];\n}\nexports.peek = peek;\n\n\n/**\n * Returns the last element in an array without removing it.\n * Same as {@link goog.array.peek}.\n * @param {IArrayLike<T>|string} array The array.\n * @return {T} Last item in array.\n * @template T\n */\nexports.last = peek;\n\n// NOTE(arv): Since most of the array functions are generic it allows you to\n// pass an array-like object. Strings have a length and are considered array-\n// like. However, the 'in' operator does not work on strings so we cannot just\n// use the array path even if the browser supports indexing into strings. We\n// therefore end up splitting the string.\n\n\n/**\n * Returns the index of the first element of an array with a specified value, or\n * -1 if the element is not present in the array.\n *\n * See {@link http://tinyurl.com/developer-mozilla-org-array-indexof}\n *\n * @param {IArrayLike<T>|string} arr The array to be searched.\n * @param {T} obj The object for which we are searching.\n * @param {number=} opt_fromIndex The index at which to start the search. If\n *     omitted the search starts at index 0.\n * @return {number} The index of the first matching array element.\n * @template T\n */\nconst indexOf = goog.NATIVE_ARRAY_PROTOTYPES &&\n        (ASSUME_NATIVE_FUNCTIONS || Array.prototype.indexOf) ?\n    function(arr, obj, opt_fromIndex) {\n      asserts.assert(arr.length != null);\n\n      return Array.prototype.indexOf.call(arr, obj, opt_fromIndex);\n    } :\n    function(arr, obj, opt_fromIndex) {\n      var fromIndex = opt_fromIndex == null ?\n          0 :\n          (opt_fromIndex < 0 ? Math.max(0, arr.length + opt_fromIndex) :\n                               opt_fromIndex);\n\n      if (typeof arr === 'string') {\n        // Array.prototype.indexOf uses === so only strings should be found.\n        if (typeof obj !== 'string' || obj.length != 1) {\n          return -1;\n        }\n        return arr.indexOf(obj, fromIndex);\n      }\n\n      for (var i = fromIndex; i < arr.length; i++) {\n        if (i in arr && arr[i] === obj) return i;\n      }\n      return -1;\n    };\nexports.indexOf = indexOf;\n\n\n/**\n * Returns the index of the last element of an array with a specified value, or\n * -1 if the element is not present in the array.\n *\n * See {@link http://tinyurl.com/developer-mozilla-org-array-lastindexof}\n *\n * @param {!IArrayLike<T>|string} arr The array to be searched.\n * @param {T} obj The object for which we are searching.\n * @param {?number=} opt_fromIndex The index at which to start the search. If\n *     omitted the search starts at the end of the array.\n * @return {number} The index of the last matching array element.\n * @template T\n */\nconst lastIndexOf = goog.NATIVE_ARRAY_PROTOTYPES &&\n        (ASSUME_NATIVE_FUNCTIONS || Array.prototype.lastIndexOf) ?\n    function(arr, obj, opt_fromIndex) {\n      asserts.assert(arr.length != null);\n\n      // Firefox treats undefined and null as 0 in the fromIndex argument which\n      // leads it to always return -1\n      var fromIndex = opt_fromIndex == null ? arr.length - 1 : opt_fromIndex;\n      return Array.prototype.lastIndexOf.call(arr, obj, fromIndex);\n    } :\n    function(arr, obj, opt_fromIndex) {\n      var fromIndex = opt_fromIndex == null ? arr.length - 1 : opt_fromIndex;\n\n      if (fromIndex < 0) {\n        fromIndex = Math.max(0, arr.length + fromIndex);\n      }\n\n      if (typeof arr === 'string') {\n        // Array.prototype.lastIndexOf uses === so only strings should be found.\n        if (typeof obj !== 'string' || obj.length != 1) {\n          return -1;\n        }\n        return arr.lastIndexOf(obj, fromIndex);\n      }\n\n      for (var i = fromIndex; i >= 0; i--) {\n        if (i in arr && arr[i] === obj) return i;\n      }\n      return -1;\n    };\nexports.lastIndexOf = lastIndexOf;\n\n\n/**\n * Calls a function for each element in an array. Skips holes in the array.\n * See {@link http://tinyurl.com/developer-mozilla-org-array-foreach}\n *\n * @param {IArrayLike<T>|string} arr Array or array like object over\n *     which to iterate.\n * @param {?function(this: S, T, number, ?): ?} f The function to call for every\n *     element. This function takes 3 arguments (the element, the index and the\n *     array). The return value is ignored.\n * @param {S=} opt_obj The object to be used as the value of 'this' within f.\n * @template T,S\n */\nconst forEach = goog.NATIVE_ARRAY_PROTOTYPES &&\n        (ASSUME_NATIVE_FUNCTIONS || Array.prototype.forEach) ?\n    function(arr, f, opt_obj) {\n      asserts.assert(arr.length != null);\n\n      Array.prototype.forEach.call(arr, f, opt_obj);\n    } :\n    function(arr, f, opt_obj) {\n      var l = arr.length;  // must be fixed during loop... see docs\n      var arr2 = (typeof arr === 'string') ? arr.split('') : arr;\n      for (var i = 0; i < l; i++) {\n        if (i in arr2) {\n          f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr);\n        }\n      }\n    };\nexports.forEach = forEach;\n\n\n/**\n * Calls a function for each element in an array, starting from the last\n * element rather than the first.\n *\n * @param {IArrayLike<T>|string} arr Array or array\n *     like object over which to iterate.\n * @param {?function(this: S, T, number, ?): ?} f The function to call for every\n *     element. This function\n *     takes 3 arguments (the element, the index and the array). The return\n *     value is ignored.\n * @param {S=} opt_obj The object to be used as the value of 'this'\n *     within f.\n * @template T,S\n */\nfunction forEachRight(arr, f, opt_obj) {\n  var l = arr.length;  // must be fixed during loop... see docs\n  var arr2 = (typeof arr === 'string') ? arr.split('') : arr;\n  for (var i = l - 1; i >= 0; --i) {\n    if (i in arr2) {\n      f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr);\n    }\n  }\n}\nexports.forEachRight = forEachRight;\n\n\n/**\n * Calls a function for each element in an array, and if the function returns\n * true adds the element to a new array.\n *\n * See {@link http://tinyurl.com/developer-mozilla-org-array-filter}\n *\n * @param {IArrayLike<T>|string} arr Array or array\n *     like object over which to iterate.\n * @param {?function(this:S, T, number, ?):boolean} f The function to call for\n *     every element. This function\n *     takes 3 arguments (the element, the index and the array) and must\n *     return a Boolean. If the return value is true the element is added to the\n *     result array. If it is false the element is not included.\n * @param {S=} opt_obj The object to be used as the value of 'this'\n *     within f.\n * @return {!Array<T>} a new array in which only elements that passed the test\n *     are present.\n * @template T,S\n */\nconst filter = goog.NATIVE_ARRAY_PROTOTYPES &&\n        (ASSUME_NATIVE_FUNCTIONS || Array.prototype.filter) ?\n    function(arr, f, opt_obj) {\n      asserts.assert(arr.length != null);\n\n      return Array.prototype.filter.call(arr, f, opt_obj);\n    } :\n    function(arr, f, opt_obj) {\n      var l = arr.length;  // must be fixed during loop... see docs\n      var res = [];\n      var resLength = 0;\n      var arr2 = (typeof arr === 'string') ? arr.split('') : arr;\n      for (var i = 0; i < l; i++) {\n        if (i in arr2) {\n          var val = arr2[i];  // in case f mutates arr2\n          if (f.call(/** @type {?} */ (opt_obj), val, i, arr)) {\n            res[resLength++] = val;\n          }\n        }\n      }\n      return res;\n    };\nexports.filter = filter;\n\n\n/**\n * Calls a function for each element in an array and inserts the result into a\n * new array.\n *\n * See {@link http://tinyurl.com/developer-mozilla-org-array-map}\n *\n * @param {IArrayLike<VALUE>|string} arr Array or array like object\n *     over which to iterate.\n * @param {function(this:THIS, VALUE, number, ?): RESULT} f The function to call\n *     for every element. This function takes 3 arguments (the element,\n *     the index and the array) and should return something. The result will be\n *     inserted into a new array.\n * @param {THIS=} opt_obj The object to be used as the value of 'this' within f.\n * @return {!Array<RESULT>} a new array with the results from f.\n * @template THIS, VALUE, RESULT\n */\nconst map = goog.NATIVE_ARRAY_PROTOTYPES &&\n        (ASSUME_NATIVE_FUNCTIONS || Array.prototype.map) ?\n    function(arr, f, opt_obj) {\n      asserts.assert(arr.length != null);\n\n      return Array.prototype.map.call(arr, f, opt_obj);\n    } :\n    function(arr, f, opt_obj) {\n      var l = arr.length;  // must be fixed during loop... see docs\n      var res = new Array(l);\n      var arr2 = (typeof arr === 'string') ? arr.split('') : arr;\n      for (var i = 0; i < l; i++) {\n        if (i in arr2) {\n          res[i] = f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr);\n        }\n      }\n      return res;\n    };\nexports.map = map;\n\n\n/**\n * Passes every element of an array into a function and accumulates the result.\n *\n * See {@link http://tinyurl.com/developer-mozilla-org-array-reduce}\n * Note that this implementation differs from the native Array.prototype.reduce\n * in that the initial value is assumed to be defined (the MDN docs linked above\n * recommend not omitting this parameter, although it is technically optional).\n *\n * For example:\n * var a = [1, 2, 3, 4];\n * reduce(a, function(r, v, i, arr) {return r + v;}, 0);\n * returns 10\n *\n * @param {IArrayLike<T>|string} arr Array or array\n *     like object over which to iterate.\n * @param {function(this:S, R, T, number, ?) : R} f The function to call for\n *     every element. This function\n *     takes 4 arguments (the function's previous result or the initial value,\n *     the value of the current array element, the current array index, and the\n *     array itself)\n *     function(previousValue, currentValue, index, array).\n * @param {?} val The initial value to pass into the function on the first call.\n * @param {S=} opt_obj  The object to be used as the value of 'this'\n *     within f.\n * @return {R} Result of evaluating f repeatedly across the values of the array.\n * @template T,S,R\n */\nconst reduce = goog.NATIVE_ARRAY_PROTOTYPES &&\n        (ASSUME_NATIVE_FUNCTIONS || Array.prototype.reduce) ?\n    function(arr, f, val, opt_obj) {\n      asserts.assert(arr.length != null);\n      if (opt_obj) {\n        f = goog.bind(f, opt_obj);\n      }\n      return Array.prototype.reduce.call(arr, f, val);\n    } :\n    function(arr, f, val, opt_obj) {\n      var rval = val;\n      forEach(arr, function(val, index) {\n        rval = f.call(/** @type {?} */ (opt_obj), rval, val, index, arr);\n      });\n      return rval;\n    };\nexports.reduce = reduce;\n\n\n/**\n * Passes every element of an array into a function and accumulates the result,\n * starting from the last element and working towards the first.\n *\n * See {@link http://tinyurl.com/developer-mozilla-org-array-reduceright}\n *\n * For example:\n * var a = ['a', 'b', 'c'];\n * reduceRight(a, function(r, v, i, arr) {return r + v;}, '');\n * returns 'cba'\n *\n * @param {IArrayLike<T>|string} arr Array or array\n *     like object over which to iterate.\n * @param {?function(this:S, R, T, number, ?) : R} f The function to call for\n *     every element. This function\n *     takes 4 arguments (the function's previous result or the initial value,\n *     the value of the current array element, the current array index, and the\n *     array itself)\n *     function(previousValue, currentValue, index, array).\n * @param {?} val The initial value to pass into the function on the first call.\n * @param {S=} opt_obj The object to be used as the value of 'this'\n *     within f.\n * @return {R} Object returned as a result of evaluating f repeatedly across the\n *     values of the array.\n * @template T,S,R\n */\nconst reduceRight = goog.NATIVE_ARRAY_PROTOTYPES &&\n        (ASSUME_NATIVE_FUNCTIONS || Array.prototype.reduceRight) ?\n    function(arr, f, val, opt_obj) {\n      asserts.assert(arr.length != null);\n      asserts.assert(f != null);\n      if (opt_obj) {\n        f = goog.bind(f, opt_obj);\n      }\n      return Array.prototype.reduceRight.call(arr, f, val);\n    } :\n    function(arr, f, val, opt_obj) {\n      var rval = val;\n      forEachRight(arr, function(val, index) {\n        rval = f.call(/** @type {?} */ (opt_obj), rval, val, index, arr);\n      });\n      return rval;\n    };\nexports.reduceRight = reduceRight;\n\n\n/**\n * Calls f for each element of an array. If any call returns true, some()\n * returns true (without checking the remaining elements). If all calls\n * return false, some() returns false.\n *\n * See {@link http://tinyurl.com/developer-mozilla-org-array-some}\n *\n * @param {IArrayLike<T>|string} arr Array or array\n *     like object over which to iterate.\n * @param {?function(this:S, T, number, ?) : boolean} f The function to call for\n *     for every element. This function takes 3 arguments (the element, the\n *     index and the array) and should return a boolean.\n * @param {S=} opt_obj  The object to be used as the value of 'this'\n *     within f.\n * @return {boolean} true if any element passes the test.\n * @template T,S\n */\nconst some = goog.NATIVE_ARRAY_PROTOTYPES &&\n        (ASSUME_NATIVE_FUNCTIONS || Array.prototype.some) ?\n    function(arr, f, opt_obj) {\n      asserts.assert(arr.length != null);\n\n      return Array.prototype.some.call(arr, f, opt_obj);\n    } :\n    function(arr, f, opt_obj) {\n      var l = arr.length;  // must be fixed during loop... see docs\n      var arr2 = (typeof arr === 'string') ? arr.split('') : arr;\n      for (var i = 0; i < l; i++) {\n        if (i in arr2 && f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) {\n          return true;\n        }\n      }\n      return false;\n    };\nexports.some = some;\n\n\n/**\n * Call f for each element of an array. If all calls return true, every()\n * returns true. If any call returns false, every() returns false and\n * does not continue to check the remaining elements.\n *\n * See {@link http://tinyurl.com/developer-mozilla-org-array-every}\n *\n * @param {IArrayLike<T>|string} arr Array or array\n *     like object over which to iterate.\n * @param {?function(this:S, T, number, ?) : boolean} f The function to call for\n *     for every element. This function takes 3 arguments (the element, the\n *     index and the array) and should return a boolean.\n * @param {S=} opt_obj The object to be used as the value of 'this'\n *     within f.\n * @return {boolean} false if any element fails the test.\n * @template T,S\n */\nconst every = goog.NATIVE_ARRAY_PROTOTYPES &&\n        (ASSUME_NATIVE_FUNCTIONS || Array.prototype.every) ?\n    function(arr, f, opt_obj) {\n      asserts.assert(arr.length != null);\n\n      return Array.prototype.every.call(arr, f, opt_obj);\n    } :\n    function(arr, f, opt_obj) {\n      var l = arr.length;  // must be fixed during loop... see docs\n      var arr2 = (typeof arr === 'string') ? arr.split('') : arr;\n      for (var i = 0; i < l; i++) {\n        if (i in arr2 && !f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) {\n          return false;\n        }\n      }\n      return true;\n    };\nexports.every = every;\n\n\n/**\n * Counts the array elements that fulfill the predicate, i.e. for which the\n * callback function returns true. Skips holes in the array.\n *\n * @param {!IArrayLike<T>|string} arr Array or array like object\n *     over which to iterate.\n * @param {function(this: S, T, number, ?): boolean} f The function to call for\n *     every element. Takes 3 arguments (the element, the index and the array).\n * @param {S=} opt_obj The object to be used as the value of 'this' within f.\n * @return {number} The number of the matching elements.\n * @template T,S\n */\nfunction count(arr, f, opt_obj) {\n  var count = 0;\n  forEach(arr, function(element, index, arr) {\n    if (f.call(/** @type {?} */ (opt_obj), element, index, arr)) {\n      ++count;\n    }\n  }, opt_obj);\n  return count;\n}\nexports.count = count;\n\n\n/**\n * Search an array for the first element that satisfies a given condition and\n * return that element.\n * @param {IArrayLike<T>|string} arr Array or array\n *     like object over which to iterate.\n * @param {?function(this:S, T, number, ?) : boolean} f The function to call\n *     for every element. This function takes 3 arguments (the element, the\n *     index and the array) and should return a boolean.\n * @param {S=} opt_obj An optional \"this\" context for the function.\n * @return {T|null} The first array element that passes the test, or null if no\n *     element is found.\n * @template T,S\n */\nfunction find(arr, f, opt_obj) {\n  var i = findIndex(arr, f, opt_obj);\n  return i < 0 ? null : typeof arr === 'string' ? arr.charAt(i) : arr[i];\n}\nexports.find = find;\n\n\n/**\n * Search an array for the first element that satisfies a given condition and\n * return its index.\n * @param {IArrayLike<T>|string} arr Array or array\n *     like object over which to iterate.\n * @param {?function(this:S, T, number, ?) : boolean} f The function to call for\n *     every element. This function\n *     takes 3 arguments (the element, the index and the array) and should\n *     return a boolean.\n * @param {S=} opt_obj An optional \"this\" context for the function.\n * @return {number} The index of the first array element that passes the test,\n *     or -1 if no element is found.\n * @template T,S\n */\nfunction findIndex(arr, f, opt_obj) {\n  var l = arr.length;  // must be fixed during loop... see docs\n  var arr2 = (typeof arr === 'string') ? arr.split('') : arr;\n  for (var i = 0; i < l; i++) {\n    if (i in arr2 && f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) {\n      return i;\n    }\n  }\n  return -1;\n}\nexports.findIndex = findIndex;\n\n\n/**\n * Search an array (in reverse order) for the last element that satisfies a\n * given condition and return that element.\n * @param {IArrayLike<T>|string} arr Array or array\n *     like object over which to iterate.\n * @param {?function(this:S, T, number, ?) : boolean} f The function to call\n *     for every element. This function\n *     takes 3 arguments (the element, the index and the array) and should\n *     return a boolean.\n * @param {S=} opt_obj An optional \"this\" context for the function.\n * @return {T|null} The last array element that passes the test, or null if no\n *     element is found.\n * @template T,S\n */\nfunction findRight(arr, f, opt_obj) {\n  var i = findIndexRight(arr, f, opt_obj);\n  return i < 0 ? null : typeof arr === 'string' ? arr.charAt(i) : arr[i];\n}\nexports.findRight = findRight;\n\n\n/**\n * Search an array (in reverse order) for the last element that satisfies a\n * given condition and return its index.\n * @param {IArrayLike<T>|string} arr Array or array\n *     like object over which to iterate.\n * @param {?function(this:S, T, number, ?) : boolean} f The function to call\n *     for every element. This function\n *     takes 3 arguments (the element, the index and the array) and should\n *     return a boolean.\n * @param {S=} opt_obj An optional \"this\" context for the function.\n * @return {number} The index of the last array element that passes the test,\n *     or -1 if no element is found.\n * @template T,S\n */\nfunction findIndexRight(arr, f, opt_obj) {\n  var l = arr.length;  // must be fixed during loop... see docs\n  var arr2 = (typeof arr === 'string') ? arr.split('') : arr;\n  for (var i = l - 1; i >= 0; i--) {\n    if (i in arr2 && f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) {\n      return i;\n    }\n  }\n  return -1;\n}\nexports.findIndexRight = findIndexRight;\n\n\n/**\n * Whether the array contains the given object.\n * @param {IArrayLike<?>|string} arr The array to test for the presence of the\n *     element.\n * @param {*} obj The object for which to test.\n * @return {boolean} true if obj is present.\n */\nfunction contains(arr, obj) {\n  return indexOf(arr, obj) >= 0;\n}\nexports.contains = contains;\n\n\n/**\n * Whether the array is empty.\n * @param {IArrayLike<?>|string} arr The array to test.\n * @return {boolean} true if empty.\n */\nfunction isEmpty(arr) {\n  return arr.length == 0;\n}\nexports.isEmpty = isEmpty;\n\n\n/**\n * Clears the array.\n * @param {IArrayLike<?>} arr Array or array like object to clear.\n */\nfunction clear(arr) {\n  // For non real arrays we don't have the magic length so we delete the\n  // indices.\n  if (!Array.isArray(arr)) {\n    for (var i = arr.length - 1; i >= 0; i--) {\n      delete arr[i];\n    }\n  }\n  arr.length = 0;\n}\nexports.clear = clear;\n\n\n/**\n * Pushes an item into an array, if it's not already in the array.\n * @param {Array<T>} arr Array into which to insert the item.\n * @param {T} obj Value to add.\n * @template T\n */\nfunction insert(arr, obj) {\n  if (!contains(arr, obj)) {\n    arr.push(obj);\n  }\n}\nexports.insert = insert;\n\n\n/**\n * Inserts an object at the given index of the array.\n * @param {IArrayLike<?>} arr The array to modify.\n * @param {*} obj The object to insert.\n * @param {number=} opt_i The index at which to insert the object. If omitted,\n *      treated as 0. A negative index is counted from the end of the array.\n */\nfunction insertAt(arr, obj, opt_i) {\n  splice(arr, opt_i, 0, obj);\n}\nexports.insertAt = insertAt;\n\n\n/**\n * Inserts at the given index of the array, all elements of another array.\n * @param {IArrayLike<?>} arr The array to modify.\n * @param {IArrayLike<?>} elementsToAdd The array of elements to add.\n * @param {number=} opt_i The index at which to insert the object. If omitted,\n *      treated as 0. A negative index is counted from the end of the array.\n */\nfunction insertArrayAt(arr, elementsToAdd, opt_i) {\n  goog.partial(splice, arr, opt_i, 0).apply(null, elementsToAdd);\n}\nexports.insertArrayAt = insertArrayAt;\n\n\n/**\n * Inserts an object into an array before a specified object.\n * @param {Array<T>} arr The array to modify.\n * @param {T} obj The object to insert.\n * @param {T=} opt_obj2 The object before which obj should be inserted. If obj2\n *     is omitted or not found, obj is inserted at the end of the array.\n * @template T\n */\nfunction insertBefore(arr, obj, opt_obj2) {\n  var i;\n  if (arguments.length == 2 || (i = indexOf(arr, opt_obj2)) < 0) {\n    arr.push(obj);\n  } else {\n    insertAt(arr, obj, i);\n  }\n}\nexports.insertBefore = insertBefore;\n\n\n/**\n * Removes the first occurrence of a particular value from an array.\n * @param {IArrayLike<T>} arr Array from which to remove\n *     value.\n * @param {T} obj Object to remove.\n * @return {boolean} True if an element was removed.\n * @template T\n */\nfunction remove(arr, obj) {\n  var i = indexOf(arr, obj);\n  var rv;\n  if ((rv = i >= 0)) {\n    removeAt(arr, i);\n  }\n  return rv;\n}\nexports.remove = remove;\n\n\n/**\n * Removes the last occurrence of a particular value from an array.\n * @param {!IArrayLike<T>} arr Array from which to remove value.\n * @param {T} obj Object to remove.\n * @return {boolean} True if an element was removed.\n * @template T\n */\nfunction removeLast(arr, obj) {\n  var i = lastIndexOf(arr, obj);\n  if (i >= 0) {\n    removeAt(arr, i);\n    return true;\n  }\n  return false;\n}\nexports.removeLast = removeLast;\n\n\n/**\n * Removes from an array the element at index i\n * @param {IArrayLike<?>} arr Array or array like object from which to\n *     remove value.\n * @param {number} i The index to remove.\n * @return {boolean} True if an element was removed.\n */\nfunction removeAt(arr, i) {\n  asserts.assert(arr.length != null);\n\n  // use generic form of splice\n  // splice returns the removed items and if successful the length of that\n  // will be 1\n  return Array.prototype.splice.call(arr, i, 1).length == 1;\n}\nexports.removeAt = removeAt;\n\n\n/**\n * Removes the first value that satisfies the given condition.\n * @param {IArrayLike<T>} arr Array or array\n *     like object over which to iterate.\n * @param {?function(this:S, T, number, ?) : boolean} f The function to call\n *     for every element. This function\n *     takes 3 arguments (the element, the index and the array) and should\n *     return a boolean.\n * @param {S=} opt_obj An optional \"this\" context for the function.\n * @return {boolean} True if an element was removed.\n * @template T,S\n */\nfunction removeIf(arr, f, opt_obj) {\n  var i = findIndex(arr, f, opt_obj);\n  if (i >= 0) {\n    removeAt(arr, i);\n    return true;\n  }\n  return false;\n}\nexports.removeIf = removeIf;\n\n\n/**\n * Removes all values that satisfy the given condition.\n * @param {IArrayLike<T>} arr Array or array\n *     like object over which to iterate.\n * @param {?function(this:S, T, number, ?) : boolean} f The function to call\n *     for every element. This function\n *     takes 3 arguments (the element, the index and the array) and should\n *     return a boolean.\n * @param {S=} opt_obj An optional \"this\" context for the function.\n * @return {number} The number of items removed\n * @template T,S\n */\nfunction removeAllIf(arr, f, opt_obj) {\n  var removedCount = 0;\n  forEachRight(arr, function(val, index) {\n    if (f.call(/** @type {?} */ (opt_obj), val, index, arr)) {\n      if (removeAt(arr, index)) {\n        removedCount++;\n      }\n    }\n  });\n  return removedCount;\n}\nexports.removeAllIf = removeAllIf;\n\n\n/**\n * Returns a new array that is the result of joining the arguments.  If arrays\n * are passed then their items are added, however, if non-arrays are passed they\n * will be added to the return array as is.\n *\n * Note that ArrayLike objects will be added as is, rather than having their\n * items added.\n *\n * concat([1, 2], [3, 4]) -> [1, 2, 3, 4]\n * concat(0, [1, 2]) -> [0, 1, 2]\n * concat([1, 2], null) -> [1, 2, null]\n *\n * There is bug in all current versions of IE (6, 7 and 8) where arrays created\n * in an iframe become corrupted soon (not immediately) after the iframe is\n * destroyed. This is common if loading data via goog.net.IframeIo, for example.\n * This corruption only affects the concat method which will start throwing\n * Catastrophic Errors (#-2147418113).\n *\n * See http://endoflow.com/scratch/corrupted-arrays.html for a test case.\n *\n * Internally goog.array should use this, so that all methods will continue to\n * work on these broken array objects.\n *\n * @param {...*} var_args Items to concatenate.  Arrays will have each item\n *     added, while primitives and objects will be added as is.\n * @return {!Array<?>} The new resultant array.\n */\nfunction concat(var_args) {\n  return Array.prototype.concat.apply([], arguments);\n}\nexports.concat = concat;\n\n\n/**\n * Returns a new array that contains the contents of all the arrays passed.\n * @param {...!Array<T>} var_args\n * @return {!Array<T>}\n * @template T\n */\nfunction join(var_args) {\n  return Array.prototype.concat.apply([], arguments);\n}\nexports.join = join;\n\n\n/**\n * Converts an object to an array.\n * @param {IArrayLike<T>|string} object  The object to convert to an\n *     array.\n * @return {!Array<T>} The object converted into an array. If object has a\n *     length property, every property indexed with a non-negative number\n *     less than length will be included in the result. If object does not\n *     have a length property, an empty array will be returned.\n * @template T\n */\nfunction toArray(object) {\n  var length = object.length;\n\n  // If length is not a number the following is false. This case is kept for\n  // backwards compatibility since there are callers that pass objects that are\n  // not array like.\n  if (length > 0) {\n    var rv = new Array(length);\n    for (var i = 0; i < length; i++) {\n      rv[i] = object[i];\n    }\n    return rv;\n  }\n  return [];\n}\nexports.toArray = toArray;\n\n\n/**\n * Does a shallow copy of an array.\n * @param {IArrayLike<T>|string} arr  Array or array-like object to\n *     clone.\n * @return {!Array<T>} Clone of the input array.\n * @template T\n */\nconst clone = toArray;\nexports.clone = clone;\n\n\n/**\n * Extends an array with another array, element, or \"array like\" object.\n * This function operates 'in-place', it does not create a new Array.\n *\n * Example:\n * var a = [];\n * extend(a, [0, 1]);\n * a; // [0, 1]\n * extend(a, 2);\n * a; // [0, 1, 2]\n *\n * @param {Array<VALUE>} arr1  The array to modify.\n * @param {...(IArrayLike<VALUE>|VALUE)} var_args The elements or arrays of\n *     elements to add to arr1.\n * @template VALUE\n */\nfunction extend(arr1, var_args) {\n  for (var i = 1; i < arguments.length; i++) {\n    var arr2 = arguments[i];\n    if (goog.isArrayLike(arr2)) {\n      var len1 = arr1.length || 0;\n      var len2 = arr2.length || 0;\n      arr1.length = len1 + len2;\n      for (var j = 0; j < len2; j++) {\n        arr1[len1 + j] = arr2[j];\n      }\n    } else {\n      arr1.push(arr2);\n    }\n  }\n}\nexports.extend = extend;\n\n\n/**\n * Adds or removes elements from an array. This is a generic version of Array\n * splice. This means that it might work on other objects similar to arrays,\n * such as the arguments object.\n *\n * @param {IArrayLike<T>} arr The array to modify.\n * @param {number|undefined} index The index at which to start changing the\n *     array. If not defined, treated as 0.\n * @param {number} howMany How many elements to remove (0 means no removal. A\n *     value below 0 is treated as zero and so is any other non number. Numbers\n *     are floored).\n * @param {...T} var_args Optional, additional elements to insert into the\n *     array.\n * @return {!Array<T>} the removed elements.\n * @template T\n */\nfunction splice(arr, index, howMany, var_args) {\n  asserts.assert(arr.length != null);\n\n  return Array.prototype.splice.apply(arr, slice(arguments, 1));\n}\nexports.splice = splice;\n\n\n/**\n * Returns a new array from a segment of an array. This is a generic version of\n * Array slice. This means that it might work on other objects similar to\n * arrays, such as the arguments object.\n *\n * @param {IArrayLike<T>|string} arr The array from\n * which to copy a segment.\n * @param {number} start The index of the first element to copy.\n * @param {number=} opt_end The index after the last element to copy.\n * @return {!Array<T>} A new array containing the specified segment of the\n *     original array.\n * @template T\n */\nfunction slice(arr, start, opt_end) {\n  asserts.assert(arr.length != null);\n\n  // passing 1 arg to slice is not the same as passing 2 where the second is\n  // null or undefined (in that case the second argument is treated as 0).\n  // we could use slice on the arguments object and then use apply instead of\n  // testing the length\n  if (arguments.length <= 2) {\n    return Array.prototype.slice.call(arr, start);\n  } else {\n    return Array.prototype.slice.call(arr, start, opt_end);\n  }\n}\nexports.slice = slice;\n\n\n/**\n * Removes all duplicates from an array (retaining only the first\n * occurrence of each array element).  This function modifies the\n * array in place and doesn't change the order of the non-duplicate items.\n *\n * For objects, duplicates are identified as having the same unique ID as\n * defined by {@link goog.getUid}.\n *\n * Alternatively you can specify a custom hash function that returns a unique\n * value for each item in the array it should consider unique.\n *\n * Runtime: N,\n * Worstcase space: 2N (no dupes)\n *\n * @param {IArrayLike<T>} arr The array from which to remove\n *     duplicates.\n * @param {Array=} opt_rv An optional array in which to return the results,\n *     instead of performing the removal inplace.  If specified, the original\n *     array will remain unchanged.\n * @param {function(T):string=} opt_hashFn An optional function to use to\n *     apply to every item in the array. This function should return a unique\n *     value for each item in the array it should consider unique.\n * @template T\n */\nfunction removeDuplicates(arr, opt_rv, opt_hashFn) {\n  var returnArray = opt_rv || arr;\n  var defaultHashFn = function(item) {\n    // Prefix each type with a single character representing the type to\n    // prevent conflicting keys (e.g. true and 'true').\n    return goog.isObject(item) ? 'o' + goog.getUid(item) :\n                                 (typeof item).charAt(0) + item;\n  };\n  var hashFn = opt_hashFn || defaultHashFn;\n\n  var seen = {}, cursorInsert = 0, cursorRead = 0;\n  while (cursorRead < arr.length) {\n    var current = arr[cursorRead++];\n    var key = hashFn(current);\n    if (!Object.prototype.hasOwnProperty.call(seen, key)) {\n      seen[key] = true;\n      returnArray[cursorInsert++] = current;\n    }\n  }\n  returnArray.length = cursorInsert;\n}\nexports.removeDuplicates = removeDuplicates;\n\n\n/**\n * Searches the specified array for the specified target using the binary\n * search algorithm.  If no opt_compareFn is specified, elements are compared\n * using <code>defaultCompare</code>, which compares the elements\n * using the built in < and > operators.  This will produce the expected\n * behavior for homogeneous arrays of String(s) and Number(s). The array\n * specified <b>must</b> be sorted in ascending order (as defined by the\n * comparison function).  If the array is not sorted, results are undefined.\n * If the array contains multiple instances of the specified target value, the\n * left-most instance will be found.\n *\n * Runtime: O(log n)\n *\n * @param {IArrayLike<VALUE>} arr The array to be searched.\n * @param {TARGET} target The sought value.\n * @param {function(TARGET, VALUE): number=} opt_compareFn Optional comparison\n *     function by which the array is ordered. Should take 2 arguments to\n *     compare, the target value and an element from your array, and return a\n *     negative number, zero, or a positive number depending on whether the\n *     first argument is less than, equal to, or greater than the second.\n * @return {number} Lowest index of the target value if found, otherwise\n *     (-(insertion point) - 1). The insertion point is where the value should\n *     be inserted into arr to preserve the sorted property.  Return value >= 0\n *     iff target is found.\n * @template TARGET, VALUE\n */\nfunction binarySearch(arr, target, opt_compareFn) {\n  return binarySearch_(\n      arr, opt_compareFn || defaultCompare, false /* isEvaluator */, target);\n}\nexports.binarySearch = binarySearch;\n\n\n/**\n * Selects an index in the specified array using the binary search algorithm.\n * The evaluator receives an element and determines whether the desired index\n * is before, at, or after it.  The evaluator must be consistent (formally,\n * map(map(arr, evaluator, opt_obj), goog.math.sign)\n * must be monotonically non-increasing).\n *\n * Runtime: O(log n)\n *\n * @param {IArrayLike<VALUE>} arr The array to be searched.\n * @param {function(this:THIS, VALUE, number, ?): number} evaluator\n *     Evaluator function that receives 3 arguments (the element, the index and\n *     the array). Should return a negative number, zero, or a positive number\n *     depending on whether the desired index is before, at, or after the\n *     element passed to it.\n * @param {THIS=} opt_obj The object to be used as the value of 'this'\n *     within evaluator.\n * @return {number} Index of the leftmost element matched by the evaluator, if\n *     such exists; otherwise (-(insertion point) - 1). The insertion point is\n *     the index of the first element for which the evaluator returns negative,\n *     or arr.length if no such element exists. The return value is non-negative\n *     iff a match is found.\n * @template THIS, VALUE\n */\nfunction binarySelect(arr, evaluator, opt_obj) {\n  return binarySearch_(\n      arr, evaluator, true /* isEvaluator */, undefined /* opt_target */,\n      opt_obj);\n}\nexports.binarySelect = binarySelect;\n\n\n/**\n * Implementation of a binary search algorithm which knows how to use both\n * comparison functions and evaluators. If an evaluator is provided, will call\n * the evaluator with the given optional data object, conforming to the\n * interface defined in binarySelect. Otherwise, if a comparison function is\n * provided, will call the comparison function against the given data object.\n *\n * This implementation purposefully does not use goog.bind or goog.partial for\n * performance reasons.\n *\n * Runtime: O(log n)\n *\n * @param {IArrayLike<?>} arr The array to be searched.\n * @param {function(?, ?, ?): number | function(?, ?): number} compareFn\n *     Either an evaluator or a comparison function, as defined by binarySearch\n *     and binarySelect above.\n * @param {boolean} isEvaluator Whether the function is an evaluator or a\n *     comparison function.\n * @param {?=} opt_target If the function is a comparison function, then\n *     this is the target to binary search for.\n * @param {Object=} opt_selfObj If the function is an evaluator, this is an\n *     optional this object for the evaluator.\n * @return {number} Lowest index of the target value if found, otherwise\n *     (-(insertion point) - 1). The insertion point is where the value should\n *     be inserted into arr to preserve the sorted property.  Return value >= 0\n *     iff target is found.\n * @private\n */\nfunction binarySearch_(arr, compareFn, isEvaluator, opt_target, opt_selfObj) {\n  var left = 0;            // inclusive\n  var right = arr.length;  // exclusive\n  var found;\n  while (left < right) {\n    var middle = left + ((right - left) >>> 1);\n    var compareResult;\n    if (isEvaluator) {\n      compareResult = compareFn.call(opt_selfObj, arr[middle], middle, arr);\n    } else {\n      // NOTE(dimvar): To avoid this cast, we'd have to use function overloading\n      // for the type of binarySearch_, which the type system can't express yet.\n      compareResult = /** @type {function(?, ?): number} */ (compareFn)(\n          opt_target, arr[middle]);\n    }\n    if (compareResult > 0) {\n      left = middle + 1;\n    } else {\n      right = middle;\n      // We are looking for the lowest index so we can't return immediately.\n      found = !compareResult;\n    }\n  }\n  // left is the index if found, or the insertion point otherwise.\n  // Avoiding bitwise not operator, as that causes a loss in precision for array\n  // indexes outside the bounds of a 32-bit signed integer.  Array indexes have\n  // a maximum value of 2^32-2 https://tc39.es/ecma262/#array-index\n  return found ? left : -left - 1;\n}\n\n\n/**\n * Sorts the specified array into ascending order.  If no opt_compareFn is\n * specified, elements are compared using\n * <code>defaultCompare</code>, which compares the elements using\n * the built in < and > operators.  This will produce the expected behavior\n * for homogeneous arrays of String(s) and Number(s), unlike the native sort,\n * but will give unpredictable results for heterogeneous lists of strings and\n * numbers with different numbers of digits.\n *\n * This sort is not guaranteed to be stable.\n *\n * Runtime: Same as `Array.prototype.sort`\n *\n * @param {Array<T>} arr The array to be sorted.\n * @param {?function(T,T):number=} opt_compareFn Optional comparison\n *     function by which the\n *     array is to be ordered. Should take 2 arguments to compare, and return a\n *     negative number, zero, or a positive number depending on whether the\n *     first argument is less than, equal to, or greater than the second.\n * @template T\n */\nfunction sort(arr, opt_compareFn) {\n  // TODO(arv): Update type annotation since null is not accepted.\n  arr.sort(opt_compareFn || defaultCompare);\n}\nexports.sort = sort;\n\n\n/**\n * Sorts the specified array into ascending order in a stable way.  If no\n * opt_compareFn is specified, elements are compared using\n * <code>defaultCompare</code>, which compares the elements using\n * the built in < and > operators.  This will produce the expected behavior\n * for homogeneous arrays of String(s) and Number(s).\n *\n * Runtime: Same as `Array.prototype.sort`, plus an additional\n * O(n) overhead of copying the array twice.\n *\n * @param {Array<T>} arr The array to be sorted.\n * @param {?function(T, T): number=} opt_compareFn Optional comparison function\n *     by which the array is to be ordered. Should take 2 arguments to compare,\n *     and return a negative number, zero, or a positive number depending on\n *     whether the first argument is less than, equal to, or greater than the\n *     second.\n * @template T\n */\nfunction stableSort(arr, opt_compareFn) {\n  var compArr = new Array(arr.length);\n  for (var i = 0; i < arr.length; i++) {\n    compArr[i] = {index: i, value: arr[i]};\n  }\n  var valueCompareFn = opt_compareFn || defaultCompare;\n  function stableCompareFn(obj1, obj2) {\n    return valueCompareFn(obj1.value, obj2.value) || obj1.index - obj2.index;\n  }\n  sort(compArr, stableCompareFn);\n  for (var i = 0; i < arr.length; i++) {\n    arr[i] = compArr[i].value;\n  }\n}\nexports.stableSort = stableSort;\n\n\n/**\n * Sort the specified array into ascending order based on item keys\n * returned by the specified key function.\n * If no opt_compareFn is specified, the keys are compared in ascending order\n * using <code>defaultCompare</code>.\n *\n * Runtime: O(S(f(n)), where S is runtime of <code>sort</code>\n * and f(n) is runtime of the key function.\n *\n * @param {Array<T>} arr The array to be sorted.\n * @param {function(T): K} keyFn Function taking array element and returning\n *     a key used for sorting this element.\n * @param {?function(K, K): number=} opt_compareFn Optional comparison function\n *     by which the keys are to be ordered. Should take 2 arguments to compare,\n *     and return a negative number, zero, or a positive number depending on\n *     whether the first argument is less than, equal to, or greater than the\n *     second.\n * @template T,K\n */\nfunction sortByKey(arr, keyFn, opt_compareFn) {\n  var keyCompareFn = opt_compareFn || defaultCompare;\n  sort(arr, function(a, b) {\n    return keyCompareFn(keyFn(a), keyFn(b));\n  });\n}\nexports.sortByKey = sortByKey;\n\n\n/**\n * Sorts an array of objects by the specified object key and compare\n * function. If no compare function is provided, the key values are\n * compared in ascending order using <code>defaultCompare</code>.\n * This won't work for keys that get renamed by the compiler. So use\n * {'foo': 1, 'bar': 2} rather than {foo: 1, bar: 2}.\n * @param {Array<Object>} arr An array of objects to sort.\n * @param {string} key The object key to sort by.\n * @param {Function=} opt_compareFn The function to use to compare key\n *     values.\n */\nfunction sortObjectsByKey(arr, key, opt_compareFn) {\n  sortByKey(arr, function(obj) {\n    return obj[key];\n  }, opt_compareFn);\n}\nexports.sortObjectsByKey = sortObjectsByKey;\n\n\n/**\n * Tells if the array is sorted.\n * @param {!IArrayLike<T>} arr The array.\n * @param {?function(T,T):number=} opt_compareFn Function to compare the\n *     array elements.\n *     Should take 2 arguments to compare, and return a negative number, zero,\n *     or a positive number depending on whether the first argument is less\n *     than, equal to, or greater than the second.\n * @param {boolean=} opt_strict If true no equal elements are allowed.\n * @return {boolean} Whether the array is sorted.\n * @template T\n */\nfunction isSorted(arr, opt_compareFn, opt_strict) {\n  var compare = opt_compareFn || defaultCompare;\n  for (var i = 1; i < arr.length; i++) {\n    var compareResult = compare(arr[i - 1], arr[i]);\n    if (compareResult > 0 || compareResult == 0 && opt_strict) {\n      return false;\n    }\n  }\n  return true;\n}\nexports.isSorted = isSorted;\n\n\n/**\n * Compares two arrays for equality. Two arrays are considered equal if they\n * have the same length and their corresponding elements are equal according to\n * the comparison function.\n *\n * @param {IArrayLike<A>} arr1 The first array to compare.\n * @param {IArrayLike<B>} arr2 The second array to compare.\n * @param {?function(A,B):boolean=} opt_equalsFn Optional comparison function.\n *     Should take 2 arguments to compare, and return true if the arguments\n *     are equal. Defaults to {@link goog.array.defaultCompareEquality} which\n *     compares the elements using the built-in '===' operator.\n * @return {boolean} Whether the two arrays are equal.\n * @template A\n * @template B\n */\nfunction equals(arr1, arr2, opt_equalsFn) {\n  if (!goog.isArrayLike(arr1) || !goog.isArrayLike(arr2) ||\n      arr1.length != arr2.length) {\n    return false;\n  }\n  var l = arr1.length;\n  var equalsFn = opt_equalsFn || defaultCompareEquality;\n  for (var i = 0; i < l; i++) {\n    if (!equalsFn(arr1[i], arr2[i])) {\n      return false;\n    }\n  }\n  return true;\n}\nexports.equals = equals;\n\n\n/**\n * 3-way array compare function.\n * @param {!IArrayLike<VALUE>} arr1 The first array to\n *     compare.\n * @param {!IArrayLike<VALUE>} arr2 The second array to\n *     compare.\n * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison\n *     function by which the array is to be ordered. Should take 2 arguments to\n *     compare, and return a negative number, zero, or a positive number\n *     depending on whether the first argument is less than, equal to, or\n *     greater than the second.\n * @return {number} Negative number, zero, or a positive number depending on\n *     whether the first argument is less than, equal to, or greater than the\n *     second.\n * @template VALUE\n */\nfunction compare3(arr1, arr2, opt_compareFn) {\n  var compare = opt_compareFn || defaultCompare;\n  var l = Math.min(arr1.length, arr2.length);\n  for (var i = 0; i < l; i++) {\n    var result = compare(arr1[i], arr2[i]);\n    if (result != 0) {\n      return result;\n    }\n  }\n  return defaultCompare(arr1.length, arr2.length);\n}\nexports.compare3 = compare3;\n\n\n/**\n * Compares its two arguments for order, using the built in < and >\n * operators.\n * @param {VALUE} a The first object to be compared.\n * @param {VALUE} b The second object to be compared.\n * @return {number} A negative number, zero, or a positive number as the first\n *     argument is less than, equal to, or greater than the second,\n *     respectively.\n * @template VALUE\n */\nfunction defaultCompare(a, b) {\n  return a > b ? 1 : a < b ? -1 : 0;\n}\nexports.defaultCompare = defaultCompare;\n\n\n/**\n * Compares its two arguments for inverse order, using the built in < and >\n * operators.\n * @param {VALUE} a The first object to be compared.\n * @param {VALUE} b The second object to be compared.\n * @return {number} A negative number, zero, or a positive number as the first\n *     argument is greater than, equal to, or less than the second,\n *     respectively.\n * @template VALUE\n */\nfunction inverseDefaultCompare(a, b) {\n  return -defaultCompare(a, b);\n}\nexports.inverseDefaultCompare = inverseDefaultCompare;\n\n\n/**\n * Compares its two arguments for equality, using the built in === operator.\n * @param {*} a The first object to compare.\n * @param {*} b The second object to compare.\n * @return {boolean} True if the two arguments are equal, false otherwise.\n */\nfunction defaultCompareEquality(a, b) {\n  return a === b;\n}\nexports.defaultCompareEquality = defaultCompareEquality;\n\n\n/**\n * Inserts a value into a sorted array. The array is not modified if the\n * value is already present.\n * @param {IArrayLike<VALUE>} array The array to modify.\n * @param {VALUE} value The object to insert.\n * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison\n *     function by which the array is ordered. Should take 2 arguments to\n *     compare, and return a negative number, zero, or a positive number\n *     depending on whether the first argument is less than, equal to, or\n *     greater than the second.\n * @return {boolean} True if an element was inserted.\n * @template VALUE\n */\nfunction binaryInsert(array, value, opt_compareFn) {\n  var index = binarySearch(array, value, opt_compareFn);\n  if (index < 0) {\n    insertAt(array, value, -(index + 1));\n    return true;\n  }\n  return false;\n}\nexports.binaryInsert = binaryInsert;\n\n\n/**\n * Removes a value from a sorted array.\n * @param {!IArrayLike<VALUE>} array The array to modify.\n * @param {VALUE} value The object to remove.\n * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison\n *     function by which the array is ordered. Should take 2 arguments to\n *     compare, and return a negative number, zero, or a positive number\n *     depending on whether the first argument is less than, equal to, or\n *     greater than the second.\n * @return {boolean} True if an element was removed.\n * @template VALUE\n */\nfunction binaryRemove(array, value, opt_compareFn) {\n  var index = binarySearch(array, value, opt_compareFn);\n  return (index >= 0) ? removeAt(array, index) : false;\n}\nexports.binaryRemove = binaryRemove;\n\n\n/**\n * Splits an array into disjoint buckets according to a splitting function.\n * @param {IArrayLike<T>} array The array.\n * @param {function(this:S, T, number, !IArrayLike<T>):?} sorter Function to\n *     call for every element.  This takes 3 arguments (the element, the index\n *     and the array) and must return a valid object key (a string, number,\n *     etc), or undefined, if that object should not be placed in a bucket.\n * @param {S=} opt_obj The object to be used as the value of 'this' within\n *     sorter.\n * @return {!Object<!Array<T>>} An object, with keys being all of the unique\n *     return values of sorter, and values being arrays containing the items for\n *     which the splitter returned that key.\n * @template T,S\n */\nfunction bucket(array, sorter, opt_obj) {\n  var buckets = {};\n\n  for (var i = 0; i < array.length; i++) {\n    var value = array[i];\n    var key = sorter.call(/** @type {?} */ (opt_obj), value, i, array);\n    if (key !== undefined) {\n      // Push the value to the right bucket, creating it if necessary.\n      var bucket = buckets[key] || (buckets[key] = []);\n      bucket.push(value);\n    }\n  }\n\n  return buckets;\n}\nexports.bucket = bucket;\n\n\n/**\n * Creates a new object built from the provided array and the key-generation\n * function.\n * @param {IArrayLike<T>} arr Array or array like object over\n *     which to iterate whose elements will be the values in the new object.\n * @param {?function(this:S, T, number, ?) : string} keyFunc The function to\n *     call for every element. This function takes 3 arguments (the element, the\n *     index and the array) and should return a string that will be used as the\n *     key for the element in the new object. If the function returns the same\n *     key for more than one element, the value for that key is\n *     implementation-defined.\n * @param {S=} opt_obj The object to be used as the value of 'this'\n *     within keyFunc.\n * @return {!Object<T>} The new object.\n * @template T,S\n */\nfunction toObject(arr, keyFunc, opt_obj) {\n  var ret = {};\n  forEach(arr, function(element, index) {\n    ret[keyFunc.call(/** @type {?} */ (opt_obj), element, index, arr)] =\n        element;\n  });\n  return ret;\n}\nexports.toObject = toObject;\n\n\n/**\n * Creates a range of numbers in an arithmetic progression.\n *\n * Range takes 1, 2, or 3 arguments:\n * <pre>\n * range(5) is the same as range(0, 5, 1) and produces [0, 1, 2, 3, 4]\n * range(2, 5) is the same as range(2, 5, 1) and produces [2, 3, 4]\n * range(-2, -5, -1) produces [-2, -3, -4]\n * range(-2, -5, 1) produces [], since stepping by 1 wouldn't ever reach -5.\n * </pre>\n *\n * @param {number} startOrEnd The starting value of the range if an end argument\n *     is provided. Otherwise, the start value is 0, and this is the end value.\n * @param {number=} opt_end The optional end value of the range.\n * @param {number=} opt_step The step size between range values. Defaults to 1\n *     if opt_step is undefined or 0.\n * @return {!Array<number>} An array of numbers for the requested range. May be\n *     an empty array if adding the step would not converge toward the end\n *     value.\n */\nfunction range(startOrEnd, opt_end, opt_step) {\n  var array = [];\n  var start = 0;\n  var end = startOrEnd;\n  var step = opt_step || 1;\n  if (opt_end !== undefined) {\n    start = startOrEnd;\n    end = opt_end;\n  }\n\n  if (step * (end - start) < 0) {\n    // Sign mismatch: start + step will never reach the end value.\n    return [];\n  }\n\n  if (step > 0) {\n    for (var i = start; i < end; i += step) {\n      array.push(i);\n    }\n  } else {\n    for (var i = start; i > end; i += step) {\n      array.push(i);\n    }\n  }\n  return array;\n}\nexports.range = range;\n\n\n/**\n * Returns an array consisting of the given value repeated N times.\n *\n * @param {VALUE} value The value to repeat.\n * @param {number} n The repeat count.\n * @return {!Array<VALUE>} An array with the repeated value.\n * @template VALUE\n */\nfunction repeat(value, n) {\n  var array = [];\n  for (var i = 0; i < n; i++) {\n    array[i] = value;\n  }\n  return array;\n}\nexports.repeat = repeat;\n\n\n/**\n * Returns an array consisting of every argument with all arrays\n * expanded in-place recursively.\n *\n * @param {...*} var_args The values to flatten.\n * @return {!Array<?>} An array containing the flattened values.\n */\nfunction flatten(var_args) {\n  var CHUNK_SIZE = 8192;\n\n  var result = [];\n  for (var i = 0; i < arguments.length; i++) {\n    var element = arguments[i];\n    if (Array.isArray(element)) {\n      for (var c = 0; c < element.length; c += CHUNK_SIZE) {\n        var chunk = slice(element, c, c + CHUNK_SIZE);\n        var recurseResult = flatten.apply(null, chunk);\n        for (var r = 0; r < recurseResult.length; r++) {\n          result.push(recurseResult[r]);\n        }\n      }\n    } else {\n      result.push(element);\n    }\n  }\n  return result;\n}\nexports.flatten = flatten;\n\n\n/**\n * Rotates an array in-place. After calling this method, the element at\n * index i will be the element previously at index (i - n) %\n * array.length, for all values of i between 0 and array.length - 1,\n * inclusive.\n *\n * For example, suppose list comprises [t, a, n, k, s]. After invoking\n * rotate(array, 1) (or rotate(array, -4)), array will comprise [s, t, a, n, k].\n *\n * @param {!Array<T>} array The array to rotate.\n * @param {number} n The amount to rotate.\n * @return {!Array<T>} The array.\n * @template T\n */\nfunction rotate(array, n) {\n  asserts.assert(array.length != null);\n\n  if (array.length) {\n    n %= array.length;\n    if (n > 0) {\n      Array.prototype.unshift.apply(array, array.splice(-n, n));\n    } else if (n < 0) {\n      Array.prototype.push.apply(array, array.splice(0, -n));\n    }\n  }\n  return array;\n}\nexports.rotate = rotate;\n\n\n/**\n * Moves one item of an array to a new position keeping the order of the rest\n * of the items. Example use case: keeping a list of JavaScript objects\n * synchronized with the corresponding list of DOM elements after one of the\n * elements has been dragged to a new position.\n * @param {!IArrayLike<?>} arr The array to modify.\n * @param {number} fromIndex Index of the item to move between 0 and\n *     `arr.length - 1`.\n * @param {number} toIndex Target index between 0 and `arr.length - 1`.\n */\nfunction moveItem(arr, fromIndex, toIndex) {\n  asserts.assert(fromIndex >= 0 && fromIndex < arr.length);\n  asserts.assert(toIndex >= 0 && toIndex < arr.length);\n  // Remove 1 item at fromIndex.\n  var removedItems = Array.prototype.splice.call(arr, fromIndex, 1);\n  // Insert the removed item at toIndex.\n  Array.prototype.splice.call(arr, toIndex, 0, removedItems[0]);\n  // We don't use goog.array.insertAt and goog.array.removeAt, because they're\n  // significantly slower than splice.\n}\nexports.moveItem = moveItem;\n\n\n/**\n * Creates a new array for which the element at position i is an array of the\n * ith element of the provided arrays.  The returned array will only be as long\n * as the shortest array provided; additional values are ignored.  For example,\n * the result of zipping [1, 2] and [3, 4, 5] is [[1,3], [2, 4]].\n *\n * This is similar to the zip() function in Python.  See {@link\n * http://docs.python.org/library/functions.html#zip}\n *\n * @param {...!IArrayLike<?>} var_args Arrays to be combined.\n * @return {!Array<!Array<?>>} A new array of arrays created from\n *     provided arrays.\n */\nfunction zip(var_args) {\n  if (!arguments.length) {\n    return [];\n  }\n  var result = [];\n  var minLen = arguments[0].length;\n  for (var i = 1; i < arguments.length; i++) {\n    if (arguments[i].length < minLen) {\n      minLen = arguments[i].length;\n    }\n  }\n  for (var i = 0; i < minLen; i++) {\n    var value = [];\n    for (var j = 0; j < arguments.length; j++) {\n      value.push(arguments[j][i]);\n    }\n    result.push(value);\n  }\n  return result;\n}\nexports.zip = zip;\n\n\n/**\n * Shuffles the values in the specified array using the Fisher-Yates in-place\n * shuffle (also known as the Knuth Shuffle). By default, calls Math.random()\n * and so resets the state of that random number generator. Similarly, may reset\n * the state of any other specified random number generator.\n *\n * Runtime: O(n)\n *\n * @param {!Array<?>} arr The array to be shuffled.\n * @param {function():number=} opt_randFn Optional random function to use for\n *     shuffling.\n *     Takes no arguments, and returns a random number on the interval [0, 1).\n *     Defaults to Math.random() using JavaScript's built-in Math library.\n */\nfunction shuffle(arr, opt_randFn) {\n  var randFn = opt_randFn || Math.random;\n\n  for (var i = arr.length - 1; i > 0; i--) {\n    // Choose a random array index in [0, i] (inclusive with i).\n    var j = Math.floor(randFn() * (i + 1));\n\n    var tmp = arr[i];\n    arr[i] = arr[j];\n    arr[j] = tmp;\n  }\n}\nexports.shuffle = shuffle;\n\n\n/**\n * Returns a new array of elements from arr, based on the indexes of elements\n * provided by index_arr. For example, the result of index copying\n * ['a', 'b', 'c'] with index_arr [1,0,0,2] is ['b', 'a', 'a', 'c'].\n *\n * @param {!IArrayLike<T>} arr The array to get a indexed copy from.\n * @param {!IArrayLike<number>} index_arr An array of indexes to get from arr.\n * @return {!Array<T>} A new array of elements from arr in index_arr order.\n * @template T\n */\nfunction copyByIndex(arr, index_arr) {\n  var result = [];\n  forEach(index_arr, function(index) {\n    result.push(arr[index]);\n  });\n  return result;\n}\nexports.copyByIndex = copyByIndex;\n\n\n/**\n * Maps each element of the input array into zero or more elements of the output\n * array.\n *\n * @param {!IArrayLike<VALUE>|string} arr Array or array like object\n *     over which to iterate.\n * @param {function(this:THIS, VALUE, number, ?): !Array<RESULT>} f The function\n *     to call for every element. This function takes 3 arguments (the element,\n *     the index and the array) and should return an array. The result will be\n *     used to extend a new array.\n * @param {THIS=} opt_obj The object to be used as the value of 'this' within f.\n * @return {!Array<RESULT>} a new array with the concatenation of all arrays\n *     returned from f.\n * @template THIS, VALUE, RESULT\n */\nfunction concatMap(arr, f, opt_obj) {\n  return concat.apply([], map(arr, f, opt_obj));\n}\nexports.concatMap = concatMap;\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview String functions called from Closure packages that couldn't\n * depend on each other. Outside Closure, use goog.string function which\n * delegate to these.\n */\n\n\ngoog.provide('goog.string.internal');\n\n\n/**\n * Fast prefix-checker.\n * @param {string} str The string to check.\n * @param {string} prefix A string to look for at the start of `str`.\n * @return {boolean} True if `str` begins with `prefix`.\n * @see goog.string.startsWith\n */\ngoog.string.internal.startsWith = function(str, prefix) {\n  'use strict';\n  return str.lastIndexOf(prefix, 0) == 0;\n};\n\n\n/**\n * Fast suffix-checker.\n * @param {string} str The string to check.\n * @param {string} suffix A string to look for at the end of `str`.\n * @return {boolean} True if `str` ends with `suffix`.\n * @see goog.string.endsWith\n */\ngoog.string.internal.endsWith = function(str, suffix) {\n  'use strict';\n  const l = str.length - suffix.length;\n  return l >= 0 && str.indexOf(suffix, l) == l;\n};\n\n\n/**\n * Case-insensitive prefix-checker.\n * @param {string} str The string to check.\n * @param {string} prefix  A string to look for at the end of `str`.\n * @return {boolean} True if `str` begins with `prefix` (ignoring\n *     case).\n * @see goog.string.caseInsensitiveStartsWith\n */\ngoog.string.internal.caseInsensitiveStartsWith = function(str, prefix) {\n  'use strict';\n  return goog.string.internal.caseInsensitiveCompare(\n             prefix, str.substr(0, prefix.length)) == 0;\n};\n\n\n/**\n * Case-insensitive suffix-checker.\n * @param {string} str The string to check.\n * @param {string} suffix A string to look for at the end of `str`.\n * @return {boolean} True if `str` ends with `suffix` (ignoring\n *     case).\n * @see goog.string.caseInsensitiveEndsWith\n */\ngoog.string.internal.caseInsensitiveEndsWith = function(str, suffix) {\n  'use strict';\n  return (\n      goog.string.internal.caseInsensitiveCompare(\n          suffix, str.substr(str.length - suffix.length, suffix.length)) == 0);\n};\n\n\n/**\n * Case-insensitive equality checker.\n * @param {string} str1 First string to check.\n * @param {string} str2 Second string to check.\n * @return {boolean} True if `str1` and `str2` are the same string,\n *     ignoring case.\n * @see goog.string.caseInsensitiveEquals\n */\ngoog.string.internal.caseInsensitiveEquals = function(str1, str2) {\n  'use strict';\n  return str1.toLowerCase() == str2.toLowerCase();\n};\n\n\n/**\n * Checks if a string is empty or contains only whitespaces.\n * @param {string} str The string to check.\n * @return {boolean} Whether `str` is empty or whitespace only.\n * @see goog.string.isEmptyOrWhitespace\n */\ngoog.string.internal.isEmptyOrWhitespace = function(str) {\n  'use strict';\n  // testing length == 0 first is actually slower in all browsers (about the\n  // same in Opera).\n  // Since IE doesn't include non-breaking-space (0xa0) in their \\s character\n  // class (as required by section 7.2 of the ECMAScript spec), we explicitly\n  // include it in the regexp to enforce consistent cross-browser behavior.\n  return /^[\\s\\xa0]*$/.test(str);\n};\n\n\n/**\n * Trims white spaces to the left and right of a string.\n * @param {string} str The string to trim.\n * @return {string} A trimmed copy of `str`.\n */\ngoog.string.internal.trim =\n    (goog.TRUSTED_SITE && String.prototype.trim) ? function(str) {\n      'use strict';\n      return str.trim();\n    } : function(str) {\n      'use strict';\n      // Since IE doesn't include non-breaking-space (0xa0) in their \\s\n      // character class (as required by section 7.2 of the ECMAScript spec),\n      // we explicitly include it in the regexp to enforce consistent\n      // cross-browser behavior.\n      // NOTE: We don't use String#replace because it might have side effects\n      // causing this function to not compile to 0 bytes.\n      return /^[\\s\\xa0]*([\\s\\S]*?)[\\s\\xa0]*$/.exec(str)[1];\n    };\n\n\n/**\n * A string comparator that ignores case.\n * -1 = str1 less than str2\n *  0 = str1 equals str2\n *  1 = str1 greater than str2\n *\n * @param {string} str1 The string to compare.\n * @param {string} str2 The string to compare `str1` to.\n * @return {number} The comparator result, as described above.\n * @see goog.string.caseInsensitiveCompare\n */\ngoog.string.internal.caseInsensitiveCompare = function(str1, str2) {\n  'use strict';\n  const test1 = String(str1).toLowerCase();\n  const test2 = String(str2).toLowerCase();\n\n  if (test1 < test2) {\n    return -1;\n  } else if (test1 == test2) {\n    return 0;\n  } else {\n    return 1;\n  }\n};\n\n\n/**\n * Converts \\n to <br>s or <br />s.\n * @param {string} str The string in which to convert newlines.\n * @param {boolean=} opt_xml Whether to use XML compatible tags.\n * @return {string} A copy of `str` with converted newlines.\n * @see goog.string.newLineToBr\n */\ngoog.string.internal.newLineToBr = function(str, opt_xml) {\n  'use strict';\n  return str.replace(/(\\r\\n|\\r|\\n)/g, opt_xml ? '<br />' : '<br>');\n};\n\n\n/**\n * Escapes double quote '\"' and single quote '\\'' characters in addition to\n * '&', '<', and '>' so that a string can be included in an HTML tag attribute\n * value within double or single quotes.\n * @param {string} str string to be escaped.\n * @param {boolean=} opt_isLikelyToContainHtmlChars\n * @return {string} An escaped copy of `str`.\n * @see goog.string.htmlEscape\n */\ngoog.string.internal.htmlEscape = function(\n    str, opt_isLikelyToContainHtmlChars) {\n  'use strict';\n  if (opt_isLikelyToContainHtmlChars) {\n    str = str.replace(goog.string.internal.AMP_RE_, '&amp;')\n              .replace(goog.string.internal.LT_RE_, '&lt;')\n              .replace(goog.string.internal.GT_RE_, '&gt;')\n              .replace(goog.string.internal.QUOT_RE_, '&quot;')\n              .replace(goog.string.internal.SINGLE_QUOTE_RE_, '&#39;')\n              .replace(goog.string.internal.NULL_RE_, '&#0;');\n    return str;\n\n  } else {\n    // quick test helps in the case when there are no chars to replace, in\n    // worst case this makes barely a difference to the time taken\n    if (!goog.string.internal.ALL_RE_.test(str)) return str;\n\n    // str.indexOf is faster than regex.test in this case\n    if (str.indexOf('&') != -1) {\n      str = str.replace(goog.string.internal.AMP_RE_, '&amp;');\n    }\n    if (str.indexOf('<') != -1) {\n      str = str.replace(goog.string.internal.LT_RE_, '&lt;');\n    }\n    if (str.indexOf('>') != -1) {\n      str = str.replace(goog.string.internal.GT_RE_, '&gt;');\n    }\n    if (str.indexOf('\"') != -1) {\n      str = str.replace(goog.string.internal.QUOT_RE_, '&quot;');\n    }\n    if (str.indexOf('\\'') != -1) {\n      str = str.replace(goog.string.internal.SINGLE_QUOTE_RE_, '&#39;');\n    }\n    if (str.indexOf('\\x00') != -1) {\n      str = str.replace(goog.string.internal.NULL_RE_, '&#0;');\n    }\n    return str;\n  }\n};\n\n\n/**\n * Regular expression that matches an ampersand, for use in escaping.\n * @const {!RegExp}\n * @private\n */\ngoog.string.internal.AMP_RE_ = /&/g;\n\n\n/**\n * Regular expression that matches a less than sign, for use in escaping.\n * @const {!RegExp}\n * @private\n */\ngoog.string.internal.LT_RE_ = /</g;\n\n\n/**\n * Regular expression that matches a greater than sign, for use in escaping.\n * @const {!RegExp}\n * @private\n */\ngoog.string.internal.GT_RE_ = />/g;\n\n\n/**\n * Regular expression that matches a double quote, for use in escaping.\n * @const {!RegExp}\n * @private\n */\ngoog.string.internal.QUOT_RE_ = /\"/g;\n\n\n/**\n * Regular expression that matches a single quote, for use in escaping.\n * @const {!RegExp}\n * @private\n */\ngoog.string.internal.SINGLE_QUOTE_RE_ = /'/g;\n\n\n/**\n * Regular expression that matches null character, for use in escaping.\n * @const {!RegExp}\n * @private\n */\ngoog.string.internal.NULL_RE_ = /\\x00/g;\n\n\n/**\n * Regular expression that matches any character that needs to be escaped.\n * @const {!RegExp}\n * @private\n */\ngoog.string.internal.ALL_RE_ = /[\\x00&<>\"']/;\n\n\n/**\n * Do escaping of whitespace to preserve spatial formatting. We use character\n * entity #160 to make it safer for xml.\n * @param {string} str The string in which to escape whitespace.\n * @param {boolean=} opt_xml Whether to use XML compatible tags.\n * @return {string} An escaped copy of `str`.\n * @see goog.string.whitespaceEscape\n */\ngoog.string.internal.whitespaceEscape = function(str, opt_xml) {\n  'use strict';\n  // This doesn't use goog.string.preserveSpaces for backwards compatibility.\n  return goog.string.internal.newLineToBr(\n      str.replace(/  /g, ' &#160;'), opt_xml);\n};\n\n\n/**\n * Determines whether a string contains a substring.\n * @param {string} str The string to search.\n * @param {string} subString The substring to search for.\n * @return {boolean} Whether `str` contains `subString`.\n * @see goog.string.contains\n */\ngoog.string.internal.contains = function(str, subString) {\n  'use strict';\n  return str.indexOf(subString) != -1;\n};\n\n\n/**\n * Determines whether a string contains a substring, ignoring case.\n * @param {string} str The string to search.\n * @param {string} subString The substring to search for.\n * @return {boolean} Whether `str` contains `subString`.\n * @see goog.string.caseInsensitiveContains\n */\ngoog.string.internal.caseInsensitiveContains = function(str, subString) {\n  'use strict';\n  return goog.string.internal.contains(\n      str.toLowerCase(), subString.toLowerCase());\n};\n\n\n/**\n * Compares two version numbers.\n *\n * @param {string|number} version1 Version of first item.\n * @param {string|number} version2 Version of second item.\n *\n * @return {number}  1 if `version1` is higher.\n *                   0 if arguments are equal.\n *                  -1 if `version2` is higher.\n * @see goog.string.compareVersions\n */\ngoog.string.internal.compareVersions = function(version1, version2) {\n  'use strict';\n  let order = 0;\n  // Trim leading and trailing whitespace and split the versions into\n  // subversions.\n  const v1Subs = goog.string.internal.trim(String(version1)).split('.');\n  const v2Subs = goog.string.internal.trim(String(version2)).split('.');\n  const subCount = Math.max(v1Subs.length, v2Subs.length);\n\n  // Iterate over the subversions, as long as they appear to be equivalent.\n  for (let subIdx = 0; order == 0 && subIdx < subCount; subIdx++) {\n    let v1Sub = v1Subs[subIdx] || '';\n    let v2Sub = v2Subs[subIdx] || '';\n\n    do {\n      // Split the subversions into pairs of numbers and qualifiers (like 'b').\n      // Two different RegExp objects are use to make it clear the code\n      // is side-effect free\n      const v1Comp = /(\\d*)(\\D*)(.*)/.exec(v1Sub) || ['', '', '', ''];\n      const v2Comp = /(\\d*)(\\D*)(.*)/.exec(v2Sub) || ['', '', '', ''];\n      // Break if there are no more matches.\n      if (v1Comp[0].length == 0 && v2Comp[0].length == 0) {\n        break;\n      }\n\n      // Parse the numeric part of the subversion. A missing number is\n      // equivalent to 0.\n      const v1CompNum = v1Comp[1].length == 0 ? 0 : parseInt(v1Comp[1], 10);\n      const v2CompNum = v2Comp[1].length == 0 ? 0 : parseInt(v2Comp[1], 10);\n\n      // Compare the subversion components. The number has the highest\n      // precedence. Next, if the numbers are equal, a subversion without any\n      // qualifier is always higher than a subversion with any qualifier. Next,\n      // the qualifiers are compared as strings.\n      order = goog.string.internal.compareElements_(v1CompNum, v2CompNum) ||\n          goog.string.internal.compareElements_(\n              v1Comp[2].length == 0, v2Comp[2].length == 0) ||\n          goog.string.internal.compareElements_(v1Comp[2], v2Comp[2]);\n      // Stop as soon as an inequality is discovered.\n\n      v1Sub = v1Comp[3];\n      v2Sub = v2Comp[3];\n    } while (order == 0);\n  }\n\n  return order;\n};\n\n\n/**\n * Compares elements of a version number.\n *\n * @param {string|number|boolean} left An element from a version number.\n * @param {string|number|boolean} right An element from a version number.\n *\n * @return {number}  1 if `left` is higher.\n *                   0 if arguments are equal.\n *                  -1 if `right` is higher.\n * @private\n */\ngoog.string.internal.compareElements_ = function(left, right) {\n  'use strict';\n  if (left < right) {\n    return -1;\n  } else if (left > right) {\n    return 1;\n  }\n  return 0;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Utilities used by goog.labs.userAgent tools. These functions\n * should not be used outside of goog.labs.userAgent.*.\n *\n */\n\ngoog.provide('goog.labs.userAgent.util');\n\ngoog.require('goog.string.internal');\n\n\n/**\n * Gets the native userAgent string from navigator if it exists.\n * If navigator or navigator.userAgent string is missing, returns an empty\n * string.\n * @return {string}\n * @private\n */\ngoog.labs.userAgent.util.getNativeUserAgentString_ = function() {\n  'use strict';\n  var navigator = goog.labs.userAgent.util.getNavigator_();\n  if (navigator) {\n    var userAgent = navigator.userAgent;\n    if (userAgent) {\n      return userAgent;\n    }\n  }\n  return '';\n};\n\n\n/**\n * Getter for the native navigator.\n * This is a separate function so it can be stubbed out in testing.\n * @return {!Navigator}\n * @private\n */\ngoog.labs.userAgent.util.getNavigator_ = function() {\n  'use strict';\n  return goog.global.navigator;\n};\n\n\n/**\n * A possible override for applications which wish to not check\n * navigator.userAgent but use a specified value for detection instead.\n * @private {string}\n */\ngoog.labs.userAgent.util.userAgent_ =\n    goog.labs.userAgent.util.getNativeUserAgentString_();\n\n\n/**\n * Applications may override browser detection on the built in\n * navigator.userAgent object by setting this string. Set to null to use the\n * browser object instead.\n * @param {?string=} opt_userAgent The User-Agent override.\n */\ngoog.labs.userAgent.util.setUserAgent = function(opt_userAgent) {\n  'use strict';\n  goog.labs.userAgent.util.userAgent_ =\n      opt_userAgent || goog.labs.userAgent.util.getNativeUserAgentString_();\n};\n\n\n/**\n * @return {string} The user agent string.\n */\ngoog.labs.userAgent.util.getUserAgent = function() {\n  'use strict';\n  return goog.labs.userAgent.util.userAgent_;\n};\n\n\n/**\n * @param {string} str\n * @return {boolean} Whether the user agent contains the given string.\n */\ngoog.labs.userAgent.util.matchUserAgent = function(str) {\n  'use strict';\n  var userAgent = goog.labs.userAgent.util.getUserAgent();\n  return goog.string.internal.contains(userAgent, str);\n};\n\n\n/**\n * @param {string} str\n * @return {boolean} Whether the user agent contains the given string, ignoring\n *     case.\n */\ngoog.labs.userAgent.util.matchUserAgentIgnoreCase = function(str) {\n  'use strict';\n  var userAgent = goog.labs.userAgent.util.getUserAgent();\n  return goog.string.internal.caseInsensitiveContains(userAgent, str);\n};\n\n\n/**\n * Parses the user agent into tuples for each section.\n * @param {string} userAgent\n * @return {!Array<!Array<string>>} Tuples of key, version, and the contents\n *     of the parenthetical.\n */\ngoog.labs.userAgent.util.extractVersionTuples = function(userAgent) {\n  'use strict';\n  // Matches each section of a user agent string.\n  // Example UA:\n  // Mozilla/5.0 (iPad; U; CPU OS 3_2_1 like Mac OS X; en-us)\n  // AppleWebKit/531.21.10 (KHTML, like Gecko) Mobile/7B405\n  // This has three version tuples: Mozilla, AppleWebKit, and Mobile.\n\n  var versionRegExp = new RegExp(\n      // Key. Note that a key may have a space.\n      // (i.e. 'Mobile Safari' in 'Mobile Safari/5.0')\n      '(\\\\w[\\\\w ]+)' +\n\n          '/' +                // slash\n          '([^\\\\s]+)' +        // version (i.e. '5.0b')\n          '\\\\s*' +             // whitespace\n          '(?:\\\\((.*?)\\\\))?',  // parenthetical info. parentheses not matched.\n      'g');\n\n  var data = [];\n  var match;\n\n  // Iterate and collect the version tuples.  Each iteration will be the\n  // next regex match.\n  while (match = versionRegExp.exec(userAgent)) {\n    data.push([\n      match[1],  // key\n      match[2],  // value\n      // || undefined as this is not undefined in IE7 and IE8\n      match[3] || undefined  // info\n    ]);\n  }\n\n  return data;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Utilities for manipulating objects/maps/hashes.\n */\n\ngoog.provide('goog.object');\n\n\n/**\n * Calls a function for each element in an object/map/hash.\n *\n * @param {Object<K,V>} obj The object over which to iterate.\n * @param {function(this:T,V,?,Object<K,V>):?} f The function to call\n *     for every element. This function takes 3 arguments (the value, the\n *     key and the object) and the return value is ignored.\n * @param {T=} opt_obj This is used as the 'this' object within f.\n * @template T,K,V\n */\ngoog.object.forEach = function(obj, f, opt_obj) {\n  for (const key in obj) {\n    f.call(/** @type {?} */ (opt_obj), obj[key], key, obj);\n  }\n};\n\n\n/**\n * Calls a function for each element in an object/map/hash. If that call returns\n * true, adds the element to a new object.\n *\n * @param {Object<K,V>} obj The object over which to iterate.\n * @param {function(this:T,V,?,Object<K,V>):boolean} f The function to call\n *     for every element. This\n *     function takes 3 arguments (the value, the key and the object)\n *     and should return a boolean. If the return value is true the\n *     element is added to the result object. If it is false the\n *     element is not included.\n * @param {T=} opt_obj This is used as the 'this' object within f.\n * @return {!Object<K,V>} a new object in which only elements that passed the\n *     test are present.\n * @template T,K,V\n */\ngoog.object.filter = function(obj, f, opt_obj) {\n  const res = {};\n  for (const key in obj) {\n    if (f.call(/** @type {?} */ (opt_obj), obj[key], key, obj)) {\n      res[key] = obj[key];\n    }\n  }\n  return res;\n};\n\n\n/**\n * For every element in an object/map/hash calls a function and inserts the\n * result into a new object.\n *\n * @param {Object<K,V>} obj The object over which to iterate.\n * @param {function(this:T,V,?,Object<K,V>):R} f The function to call\n *     for every element. This function\n *     takes 3 arguments (the value, the key and the object)\n *     and should return something. The result will be inserted\n *     into a new object.\n * @param {T=} opt_obj This is used as the 'this' object within f.\n * @return {!Object<K,R>} a new object with the results from f.\n * @template T,K,V,R\n */\ngoog.object.map = function(obj, f, opt_obj) {\n  const res = {};\n  for (const key in obj) {\n    res[key] = f.call(/** @type {?} */ (opt_obj), obj[key], key, obj);\n  }\n  return res;\n};\n\n\n/**\n * Calls a function for each element in an object/map/hash. If any\n * call returns true, returns true (without checking the rest). If\n * all calls return false, returns false.\n *\n * @param {Object<K,V>} obj The object to check.\n * @param {function(this:T,V,?,Object<K,V>):boolean} f The function to\n *     call for every element. This function\n *     takes 3 arguments (the value, the key and the object) and should\n *     return a boolean.\n * @param {T=} opt_obj This is used as the 'this' object within f.\n * @return {boolean} true if any element passes the test.\n * @template T,K,V\n */\ngoog.object.some = function(obj, f, opt_obj) {\n  for (const key in obj) {\n    if (f.call(/** @type {?} */ (opt_obj), obj[key], key, obj)) {\n      return true;\n    }\n  }\n  return false;\n};\n\n\n/**\n * Calls a function for each element in an object/map/hash. If\n * all calls return true, returns true. If any call returns false, returns\n * false at this point and does not continue to check the remaining elements.\n *\n * @param {Object<K,V>} obj The object to check.\n * @param {?function(this:T,V,?,Object<K,V>):boolean} f The function to\n *     call for every element. This function\n *     takes 3 arguments (the value, the key and the object) and should\n *     return a boolean.\n * @param {T=} opt_obj This is used as the 'this' object within f.\n * @return {boolean} false if any element fails the test.\n * @template T,K,V\n */\ngoog.object.every = function(obj, f, opt_obj) {\n  for (const key in obj) {\n    if (!f.call(/** @type {?} */ (opt_obj), obj[key], key, obj)) {\n      return false;\n    }\n  }\n  return true;\n};\n\n\n/**\n * Returns the number of key-value pairs in the object map.\n *\n * @param {Object} obj The object for which to get the number of key-value\n *     pairs.\n * @return {number} The number of key-value pairs in the object map.\n */\ngoog.object.getCount = function(obj) {\n  let rv = 0;\n  for (const key in obj) {\n    rv++;\n  }\n  return rv;\n};\n\n\n/**\n * Returns one key from the object map, if any exists.\n * For map literals the returned key will be the first one in most of the\n * browsers (a know exception is Konqueror).\n *\n * @param {Object} obj The object to pick a key from.\n * @return {string|undefined} The key or undefined if the object is empty.\n */\ngoog.object.getAnyKey = function(obj) {\n  for (const key in obj) {\n    return key;\n  }\n};\n\n\n/**\n * Returns one value from the object map, if any exists.\n * For map literals the returned value will be the first one in most of the\n * browsers (a know exception is Konqueror).\n *\n * @param {Object<K,V>} obj The object to pick a value from.\n * @return {V|undefined} The value or undefined if the object is empty.\n * @template K,V\n */\ngoog.object.getAnyValue = function(obj) {\n  for (const key in obj) {\n    return obj[key];\n  }\n};\n\n\n/**\n * Whether the object/hash/map contains the given object as a value.\n * An alias for goog.object.containsValue(obj, val).\n *\n * @param {Object<K,V>} obj The object in which to look for val.\n * @param {V} val The object for which to check.\n * @return {boolean} true if val is present.\n * @template K,V\n */\ngoog.object.contains = function(obj, val) {\n  return goog.object.containsValue(obj, val);\n};\n\n\n/**\n * Returns the values of the object/map/hash.\n *\n * @param {Object<K,V>} obj The object from which to get the values.\n * @return {!Array<V>} The values in the object/map/hash.\n * @template K,V\n */\ngoog.object.getValues = function(obj) {\n  const res = [];\n  let i = 0;\n  for (const key in obj) {\n    res[i++] = obj[key];\n  }\n  return res;\n};\n\n\n/**\n * Returns the keys of the object/map/hash.\n *\n * @param {Object} obj The object from which to get the keys.\n * @return {!Array<string>} Array of property keys.\n */\ngoog.object.getKeys = function(obj) {\n  const res = [];\n  let i = 0;\n  for (const key in obj) {\n    res[i++] = key;\n  }\n  return res;\n};\n\n\n/**\n * Get a value from an object multiple levels deep.  This is useful for\n * pulling values from deeply nested objects, such as JSON responses.\n * Example usage: getValueByKeys(jsonObj, 'foo', 'entries', 3)\n *\n * @param {!Object} obj An object to get the value from.  Can be array-like.\n * @param {...(string|number|!IArrayLike<number|string>)}\n *     var_args A number of keys\n *     (as strings, or numbers, for array-like objects).  Can also be\n *     specified as a single array of keys.\n * @return {*} The resulting value.  If, at any point, the value for a key\n *     in the current object is null or undefined, returns undefined.\n */\ngoog.object.getValueByKeys = function(obj, var_args) {\n  const isArrayLike = goog.isArrayLike(var_args);\n  const keys = isArrayLike ?\n      /** @type {!IArrayLike<number|string>} */ (var_args) :\n      arguments;\n\n  // Start with the 2nd parameter for the variable parameters syntax.\n  for (let i = isArrayLike ? 0 : 1; i < keys.length; i++) {\n    if (obj == null) return undefined;\n    obj = obj[keys[i]];\n  }\n\n  return obj;\n};\n\n\n/**\n * Whether the object/map/hash contains the given key.\n *\n * @param {Object} obj The object in which to look for key.\n * @param {?} key The key for which to check.\n * @return {boolean} true If the map contains the key.\n */\ngoog.object.containsKey = function(obj, key) {\n  return obj !== null && key in obj;\n};\n\n\n/**\n * Whether the object/map/hash contains the given value. This is O(n).\n *\n * @param {Object<K,V>} obj The object in which to look for val.\n * @param {V} val The value for which to check.\n * @return {boolean} true If the map contains the value.\n * @template K,V\n */\ngoog.object.containsValue = function(obj, val) {\n  for (const key in obj) {\n    if (obj[key] == val) {\n      return true;\n    }\n  }\n  return false;\n};\n\n\n/**\n * Searches an object for an element that satisfies the given condition and\n * returns its key.\n * @param {Object<K,V>} obj The object to search in.\n * @param {function(this:T,V,string,Object<K,V>):boolean} f The\n *      function to call for every element. Takes 3 arguments (the value,\n *     the key and the object) and should return a boolean.\n * @param {T=} opt_this An optional \"this\" context for the function.\n * @return {string|undefined} The key of an element for which the function\n *     returns true or undefined if no such element is found.\n * @template T,K,V\n */\ngoog.object.findKey = function(obj, f, opt_this) {\n  for (const key in obj) {\n    if (f.call(/** @type {?} */ (opt_this), obj[key], key, obj)) {\n      return key;\n    }\n  }\n  return undefined;\n};\n\n\n/**\n * Searches an object for an element that satisfies the given condition and\n * returns its value.\n * @param {Object<K,V>} obj The object to search in.\n * @param {function(this:T,V,string,Object<K,V>):boolean} f The function\n *     to call for every element. Takes 3 arguments (the value, the key\n *     and the object) and should return a boolean.\n * @param {T=} opt_this An optional \"this\" context for the function.\n * @return {V} The value of an element for which the function returns true or\n *     undefined if no such element is found.\n * @template T,K,V\n */\ngoog.object.findValue = function(obj, f, opt_this) {\n  const key = goog.object.findKey(obj, f, opt_this);\n  return key && obj[key];\n};\n\n\n/**\n * Whether the object/map/hash is empty.\n *\n * @param {Object} obj The object to test.\n * @return {boolean} true if obj is empty.\n */\ngoog.object.isEmpty = function(obj) {\n  for (const key in obj) {\n    return false;\n  }\n  return true;\n};\n\n\n/**\n * Removes all key value pairs from the object/map/hash.\n *\n * @param {Object} obj The object to clear.\n */\ngoog.object.clear = function(obj) {\n  for (const i in obj) {\n    delete obj[i];\n  }\n};\n\n\n/**\n * Removes a key-value pair based on the key.\n *\n * @param {Object} obj The object from which to remove the key.\n * @param {?} key The key to remove.\n * @return {boolean} Whether an element was removed.\n */\ngoog.object.remove = function(obj, key) {\n  let rv;\n  if (rv = key in /** @type {!Object} */ (obj)) {\n    delete obj[key];\n  }\n  return rv;\n};\n\n\n/**\n * Adds a key-value pair to the object. Throws an exception if the key is\n * already in use. Use set if you want to change an existing pair.\n *\n * @param {Object<K,V>} obj The object to which to add the key-value pair.\n * @param {string} key The key to add.\n * @param {V} val The value to add.\n * @template K,V\n */\ngoog.object.add = function(obj, key, val) {\n  if (obj !== null && key in obj) {\n    throw new Error('The object already contains the key \"' + key + '\"');\n  }\n  goog.object.set(obj, key, val);\n};\n\n\n/**\n * Returns the value for the given key.\n *\n * @param {Object<K,V>} obj The object from which to get the value.\n * @param {string} key The key for which to get the value.\n * @param {R=} opt_val The value to return if no item is found for the given\n *     key (default is undefined).\n * @return {V|R|undefined} The value for the given key.\n * @template K,V,R\n */\ngoog.object.get = function(obj, key, opt_val) {\n  if (obj !== null && key in obj) {\n    return obj[key];\n  }\n  return opt_val;\n};\n\n\n/**\n * Adds a key-value pair to the object/map/hash.\n *\n * @param {Object<K,V>} obj The object to which to add the key-value pair.\n * @param {string} key The key to add.\n * @param {V} value The value to add.\n * @template K,V\n */\ngoog.object.set = function(obj, key, value) {\n  obj[key] = value;\n};\n\n\n/**\n * Adds a key-value pair to the object/map/hash if it doesn't exist yet.\n *\n * @param {Object<K,V>} obj The object to which to add the key-value pair.\n * @param {string} key The key to add.\n * @param {V} value The value to add if the key wasn't present.\n * @return {V} The value of the entry at the end of the function.\n * @template K,V\n */\ngoog.object.setIfUndefined = function(obj, key, value) {\n  return key in /** @type {!Object} */ (obj) ? obj[key] : (obj[key] = value);\n};\n\n\n/**\n * Sets a key and value to an object if the key is not set. The value will be\n * the return value of the given function. If the key already exists, the\n * object will not be changed and the function will not be called (the function\n * will be lazily evaluated -- only called if necessary).\n *\n * This function is particularly useful when used with an `Object` which is\n * acting as a cache.\n *\n * @param {!Object<K,V>} obj The object to which to add the key-value pair.\n * @param {string} key The key to add.\n * @param {function():V} f The value to add if the key wasn't present.\n * @return {V} The value of the entry at the end of the function.\n * @template K,V\n */\ngoog.object.setWithReturnValueIfNotSet = function(obj, key, f) {\n  if (key in obj) {\n    return obj[key];\n  }\n\n  const val = f();\n  obj[key] = val;\n  return val;\n};\n\n\n/**\n * Compares two objects for equality using === on the values.\n *\n * @param {!Object<K,V>} a\n * @param {!Object<K,V>} b\n * @return {boolean}\n * @template K,V\n */\ngoog.object.equals = function(a, b) {\n  for (const k in a) {\n    if (!(k in b) || a[k] !== b[k]) {\n      return false;\n    }\n  }\n  for (const k in b) {\n    if (!(k in a)) {\n      return false;\n    }\n  }\n  return true;\n};\n\n\n/**\n * Returns a shallow clone of the object.\n *\n * @param {Object<K,V>} obj Object to clone.\n * @return {!Object<K,V>} Clone of the input object.\n * @template K,V\n */\ngoog.object.clone = function(obj) {\n  const res = {};\n  for (const key in obj) {\n    res[key] = obj[key];\n  }\n  return res;\n};\n\n\n/**\n * Clones a value. The input may be an Object, Array, or basic type. Objects and\n * arrays will be cloned recursively.\n *\n * WARNINGS:\n * <code>goog.object.unsafeClone</code> does not detect reference loops. Objects\n * that refer to themselves will cause infinite recursion.\n *\n * <code>goog.object.unsafeClone</code> is unaware of unique identifiers, and\n * copies UIDs created by <code>getUid</code> into cloned results.\n *\n * @param {T} obj The value to clone.\n * @return {T} A clone of the input value.\n * @template T\n */\ngoog.object.unsafeClone = function(obj) {\n  if (!obj || typeof obj !== 'object') return obj;\n  if (typeof obj.clone === 'function') return obj.clone();\n  const clone = Array.isArray(obj) ? [] :\n      typeof ArrayBuffer === 'function' &&\n          typeof ArrayBuffer.isView === 'function' && ArrayBuffer.isView(obj) &&\n          !(obj instanceof DataView) ?\n                                     new obj.constructor(obj.length) :\n                                     {};\n  for (const key in obj) {\n    clone[key] = goog.object.unsafeClone(obj[key]);\n  }\n  return clone;\n};\n\n\n/**\n * Returns a new object in which all the keys and values are interchanged\n * (keys become values and values become keys). If multiple keys map to the\n * same value, the chosen transposed value is implementation-dependent.\n *\n * @param {Object} obj The object to transpose.\n * @return {!Object} The transposed object.\n */\ngoog.object.transpose = function(obj) {\n  const transposed = {};\n  for (const key in obj) {\n    transposed[obj[key]] = key;\n  }\n  return transposed;\n};\n\n\n/**\n * The names of the fields that are defined on Object.prototype.\n * @type {Array<string>}\n * @private\n */\ngoog.object.PROTOTYPE_FIELDS_ = [\n  'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',\n  'toLocaleString', 'toString', 'valueOf'\n];\n\n\n/**\n * Extends an object with another object.\n * This operates 'in-place'; it does not create a new Object.\n *\n * Example:\n * var o = {};\n * goog.object.extend(o, {a: 0, b: 1});\n * o; // {a: 0, b: 1}\n * goog.object.extend(o, {b: 2, c: 3});\n * o; // {a: 0, b: 2, c: 3}\n *\n * @param {Object} target The object to modify. Existing properties will be\n *     overwritten if they are also present in one of the objects in\n *     `var_args`.\n * @param {...(Object|null|undefined)} var_args The objects from which values\n *     will be copied.\n * @deprecated Prefer Object.assign\n */\ngoog.object.extend = function(target, var_args) {\n  let key;\n  let source;\n  for (let i = 1; i < arguments.length; i++) {\n    source = arguments[i];\n    for (key in source) {\n      target[key] = source[key];\n    }\n\n    // For IE the for-in-loop does not contain any properties that are not\n    // enumerable on the prototype object (for example isPrototypeOf from\n    // Object.prototype) and it will also not include 'replace' on objects that\n    // extend String and change 'replace' (not that it is common for anyone to\n    // extend anything except Object).\n\n    for (let j = 0; j < goog.object.PROTOTYPE_FIELDS_.length; j++) {\n      key = goog.object.PROTOTYPE_FIELDS_[j];\n      if (Object.prototype.hasOwnProperty.call(source, key)) {\n        target[key] = source[key];\n      }\n    }\n  }\n};\n\n\n/**\n * Creates a new object built from the key-value pairs provided as arguments.\n * @param {...*} var_args If only one argument is provided and it is an array\n *     then this is used as the arguments, otherwise even arguments are used as\n *     the property names and odd arguments are used as the property values.\n * @return {!Object} The new object.\n * @throws {Error} If there are uneven number of arguments or there is only one\n *     non array argument.\n */\ngoog.object.create = function(var_args) {\n  const argLength = arguments.length;\n  if (argLength == 1 && Array.isArray(arguments[0])) {\n    return goog.object.create.apply(null, arguments[0]);\n  }\n\n  if (argLength % 2) {\n    throw new Error('Uneven number of arguments');\n  }\n\n  const rv = {};\n  for (let i = 0; i < argLength; i += 2) {\n    rv[arguments[i]] = arguments[i + 1];\n  }\n  return rv;\n};\n\n\n/**\n * Creates a new object where the property names come from the arguments but\n * the value is always set to true\n * @param {...*} var_args If only one argument is provided and it is an array\n *     then this is used as the arguments, otherwise the arguments are used\n *     as the property names.\n * @return {!Object} The new object.\n */\ngoog.object.createSet = function(var_args) {\n  const argLength = arguments.length;\n  if (argLength == 1 && Array.isArray(arguments[0])) {\n    return goog.object.createSet.apply(null, arguments[0]);\n  }\n\n  const rv = {};\n  for (let i = 0; i < argLength; i++) {\n    rv[arguments[i]] = true;\n  }\n  return rv;\n};\n\n\n/**\n * Creates an immutable view of the underlying object, if the browser\n * supports immutable objects.\n *\n * In default mode, writes to this view will fail silently. In strict mode,\n * they will throw an error.\n *\n * @param {!Object<K,V>} obj An object.\n * @return {!Object<K,V>} An immutable view of that object, or the\n *     original object if this browser does not support immutables.\n * @template K,V\n */\ngoog.object.createImmutableView = function(obj) {\n  let result = obj;\n  if (Object.isFrozen && !Object.isFrozen(obj)) {\n    result = Object.create(obj);\n    Object.freeze(result);\n  }\n  return result;\n};\n\n\n/**\n * @param {!Object} obj An object.\n * @return {boolean} Whether this is an immutable view of the object.\n */\ngoog.object.isImmutableView = function(obj) {\n  return !!Object.isFrozen && Object.isFrozen(obj);\n};\n\n\n/**\n * Get all properties names on a given Object regardless of enumerability.\n *\n * <p> If the browser does not support `Object.getOwnPropertyNames` nor\n * `Object.getPrototypeOf` then this is equivalent to using\n * `goog.object.getKeys`\n *\n * @param {?Object} obj The object to get the properties of.\n * @param {boolean=} opt_includeObjectPrototype Whether properties defined on\n *     `Object.prototype` should be included in the result.\n * @param {boolean=} opt_includeFunctionPrototype Whether properties defined on\n *     `Function.prototype` should be included in the result.\n * @return {!Array<string>}\n * @public\n */\ngoog.object.getAllPropertyNames = function(\n    obj, opt_includeObjectPrototype, opt_includeFunctionPrototype) {\n  if (!obj) {\n    return [];\n  }\n\n  // Naively use a for..in loop to get the property names if the browser doesn't\n  // support any other APIs for getting it.\n  if (!Object.getOwnPropertyNames || !Object.getPrototypeOf) {\n    return goog.object.getKeys(obj);\n  }\n\n  const visitedSet = {};\n\n  // Traverse the prototype chain and add all properties to the visited set.\n  let proto = obj;\n  while (proto &&\n         (proto !== Object.prototype || !!opt_includeObjectPrototype) &&\n         (proto !== Function.prototype || !!opt_includeFunctionPrototype)) {\n    const names = Object.getOwnPropertyNames(proto);\n    for (let i = 0; i < names.length; i++) {\n      visitedSet[names[i]] = true;\n    }\n    proto = Object.getPrototypeOf(proto);\n  }\n\n  return goog.object.getKeys(visitedSet);\n};\n\n\n/**\n * Given a ES5 or ES6 class reference, return its super class / super\n * constructor.\n *\n * This should be used in rare cases where you need to walk up the inheritance\n * tree (this is generally a bad idea). But this work with ES5 and ES6 classes,\n * unlike relying on the superClass_ property.\n *\n * Note: To start walking up the hierarchy from an instance call this with its\n * `constructor` property; e.g. `getSuperClass(instance.constructor)`.\n *\n * @param {function(new: ?)} constructor\n * @return {?Object}\n */\ngoog.object.getSuperClass = function(constructor) {\n  var proto = Object.getPrototypeOf(constructor.prototype);\n  return proto && proto.constructor;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\ngoog.provide('goog.dom.asserts');\n\ngoog.require('goog.asserts');\n\n/**\n * @fileoverview Custom assertions to ensure that an element has the appropriate\n * type.\n *\n * Using a goog.dom.safe wrapper on an object on the incorrect type (via an\n * incorrect static type cast) can result in security bugs: For instance,\n * g.d.s.setAnchorHref ensures that the URL assigned to the .href attribute\n * satisfies the SafeUrl contract, i.e., is safe to dereference as a hyperlink.\n * However, the value assigned to a HTMLLinkElement's .href property requires\n * the stronger TrustedResourceUrl contract, since it can refer to a stylesheet.\n * Thus, using g.d.s.setAnchorHref on an (incorrectly statically typed) object\n * of type HTMLLinkElement can result in a security vulnerability.\n * Assertions of the correct run-time type help prevent such incorrect use.\n *\n * In some cases, code using the DOM API is tested using mock objects (e.g., a\n * plain object such as {'href': url} instead of an actual Location object).\n * To allow such mocking, the assertions permit objects of types that are not\n * relevant DOM API objects at all (for instance, not Element or Location).\n *\n * Note that instanceof checks don't work straightforwardly in older versions of\n * IE, or across frames (see,\n * http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object,\n * http://stackoverflow.com/questions/26248599/instanceof-htmlelement-in-iframe-is-not-element-or-object).\n *\n * Hence, these assertions may pass vacuously in such scenarios. The resulting\n * risk of security bugs is limited by the following factors:\n *  - A bug can only arise in scenarios involving incorrect static typing (the\n *    wrapper methods are statically typed to demand objects of the appropriate,\n *    precise type).\n *  - Typically, code is tested and exercised in multiple browsers.\n */\n\n/**\n * Asserts that a given object is a Location.\n *\n * To permit this assertion to pass in the context of tests where DOM APIs might\n * be mocked, also accepts any other type except for subtypes of {!Element}.\n * This is to ensure that, for instance, HTMLLinkElement is not being used in\n * place of a Location, since this could result in security bugs due to stronger\n * contracts required for assignments to the href property of the latter.\n *\n * @param {?Object} o The object whose type to assert.\n * @return {!Location}\n */\ngoog.dom.asserts.assertIsLocation = function(o) {\n  if (goog.asserts.ENABLE_ASSERTS) {\n    var win = goog.dom.asserts.getWindow_(o);\n    if (win) {\n      if (!o || (!(o instanceof win.Location) && o instanceof win.Element)) {\n        goog.asserts.fail(\n            'Argument is not a Location (or a non-Element mock); got: %s',\n            goog.dom.asserts.debugStringForType_(o));\n      }\n    }\n  }\n  return /** @type {!Location} */ (o);\n};\n\n\n/**\n * Asserts that a given object is either the given subtype of Element\n * or a non-Element, non-Location Mock.\n *\n * To permit this assertion to pass in the context of tests where DOM\n * APIs might be mocked, also accepts any other type except for\n * subtypes of {!Element}.  This is to ensure that, for instance,\n * HTMLScriptElement is not being used in place of a HTMLImageElement,\n * since this could result in security bugs due to stronger contracts\n * required for assignments to the src property of the latter.\n *\n * The DOM type is looked up in the window the object belongs to.  In\n * some contexts, this might not be possible (e.g. when running tests\n * outside a browser, cross-domain lookup). In this case, the\n * assertions are skipped.\n *\n * @param {?Object} o The object whose type to assert.\n * @param {string} typename The name of the DOM type.\n * @return {!Element} The object.\n * @private\n */\n// TODO(bangert): Make an analog of goog.dom.TagName to correctly handle casts?\ngoog.dom.asserts.assertIsElementType_ = function(o, typename) {\n  if (goog.asserts.ENABLE_ASSERTS) {\n    var win = goog.dom.asserts.getWindow_(o);\n    if (win && typeof win[typename] != 'undefined') {\n      if (!o ||\n          (!(o instanceof win[typename]) &&\n           (o instanceof win.Location || o instanceof win.Element))) {\n        goog.asserts.fail(\n            'Argument is not a %s (or a non-Element, non-Location mock); ' +\n                'got: %s',\n            typename, goog.dom.asserts.debugStringForType_(o));\n      }\n    }\n  }\n  return /** @type {!Element} */ (o);\n};\n\n/**\n * Asserts that a given object is a HTMLAnchorElement.\n *\n * To permit this assertion to pass in the context of tests where elements might\n * be mocked, also accepts objects that are not of type Location nor a subtype\n * of Element.\n *\n * @param {?Object} o The object whose type to assert.\n * @return {!HTMLAnchorElement}\n * @deprecated Use goog.asserts.dom.assertIsHtmlAnchorElement instead.\n */\ngoog.dom.asserts.assertIsHTMLAnchorElement = function(o) {\n  return /** @type {!HTMLAnchorElement} */ (\n      goog.dom.asserts.assertIsElementType_(o, 'HTMLAnchorElement'));\n};\n\n/**\n * Asserts that a given object is a HTMLButtonElement.\n *\n * To permit this assertion to pass in the context of tests where elements might\n * be mocked, also accepts objects that are not a subtype of Element.\n *\n * @param {?Object} o The object whose type to assert.\n * @return {!HTMLButtonElement}\n * @deprecated Use goog.asserts.dom.assertIsHtmlButtonElement instead.\n */\ngoog.dom.asserts.assertIsHTMLButtonElement = function(o) {\n  return /** @type {!HTMLButtonElement} */ (\n      goog.dom.asserts.assertIsElementType_(o, 'HTMLButtonElement'));\n};\n\n/**\n * Asserts that a given object is a HTMLLinkElement.\n *\n * To permit this assertion to pass in the context of tests where elements might\n * be mocked, also accepts objects that are not a subtype of Element.\n *\n * @param {?Object} o The object whose type to assert.\n * @return {!HTMLLinkElement}\n * @deprecated Use goog.asserts.dom.assertIsHtmlLinkElement instead.\n */\ngoog.dom.asserts.assertIsHTMLLinkElement = function(o) {\n  return /** @type {!HTMLLinkElement} */ (\n      goog.dom.asserts.assertIsElementType_(o, 'HTMLLinkElement'));\n};\n\n/**\n * Asserts that a given object is a HTMLImageElement.\n *\n * To permit this assertion to pass in the context of tests where elements might\n * be mocked, also accepts objects that are not a subtype of Element.\n *\n * @param {?Object} o The object whose type to assert.\n * @return {!HTMLImageElement}\n * @deprecated Use goog.asserts.dom.assertIsHtmlImageElement instead.\n */\ngoog.dom.asserts.assertIsHTMLImageElement = function(o) {\n  return /** @type {!HTMLImageElement} */ (\n      goog.dom.asserts.assertIsElementType_(o, 'HTMLImageElement'));\n};\n\n/**\n * Asserts that a given object is a HTMLAudioElement.\n *\n * To permit this assertion to pass in the context of tests where elements might\n * be mocked, also accepts objects that are not a subtype of Element.\n *\n * @param {?Object} o The object whose type to assert.\n * @return {!HTMLAudioElement}\n * @deprecated Use goog.asserts.dom.assertIsHtmlAudioElement instead.\n */\ngoog.dom.asserts.assertIsHTMLAudioElement = function(o) {\n  return /** @type {!HTMLAudioElement} */ (\n      goog.dom.asserts.assertIsElementType_(o, 'HTMLAudioElement'));\n};\n\n/**\n * Asserts that a given object is a HTMLVideoElement.\n *\n * To permit this assertion to pass in the context of tests where elements might\n * be mocked, also accepts objects that are not a subtype of Element.\n *\n * @param {?Object} o The object whose type to assert.\n * @return {!HTMLVideoElement}\n * @deprecated Use goog.asserts.dom.assertIsHtmlVideoElement instead.\n */\ngoog.dom.asserts.assertIsHTMLVideoElement = function(o) {\n  return /** @type {!HTMLVideoElement} */ (\n      goog.dom.asserts.assertIsElementType_(o, 'HTMLVideoElement'));\n};\n\n/**\n * Asserts that a given object is a HTMLInputElement.\n *\n * To permit this assertion to pass in the context of tests where elements might\n * be mocked, also accepts objects that are not a subtype of Element.\n *\n * @param {?Object} o The object whose type to assert.\n * @return {!HTMLInputElement}\n * @deprecated Use goog.asserts.dom.assertIsHtmlInputElement instead.\n */\ngoog.dom.asserts.assertIsHTMLInputElement = function(o) {\n  return /** @type {!HTMLInputElement} */ (\n      goog.dom.asserts.assertIsElementType_(o, 'HTMLInputElement'));\n};\n\n/**\n * Asserts that a given object is a HTMLTextAreaElement.\n *\n * To permit this assertion to pass in the context of tests where elements might\n * be mocked, also accepts objects that are not a subtype of Element.\n *\n * @param {?Object} o The object whose type to assert.\n * @return {!HTMLTextAreaElement}\n * @deprecated Use goog.asserts.dom.assertIsHtmlTextAreaElement instead.\n */\ngoog.dom.asserts.assertIsHTMLTextAreaElement = function(o) {\n  return /** @type {!HTMLTextAreaElement} */ (\n      goog.dom.asserts.assertIsElementType_(o, 'HTMLTextAreaElement'));\n};\n\n/**\n * Asserts that a given object is a HTMLCanvasElement.\n *\n * To permit this assertion to pass in the context of tests where elements might\n * be mocked, also accepts objects that are not a subtype of Element.\n *\n * @param {?Object} o The object whose type to assert.\n * @return {!HTMLCanvasElement}\n * @deprecated Use goog.asserts.dom.assertIsHtmlCanvasElement instead.\n */\ngoog.dom.asserts.assertIsHTMLCanvasElement = function(o) {\n  return /** @type {!HTMLCanvasElement} */ (\n      goog.dom.asserts.assertIsElementType_(o, 'HTMLCanvasElement'));\n};\n\n/**\n * Asserts that a given object is a HTMLEmbedElement.\n *\n * To permit this assertion to pass in the context of tests where elements might\n * be mocked, also accepts objects that are not a subtype of Element.\n *\n * @param {?Object} o The object whose type to assert.\n * @return {!HTMLEmbedElement}\n * @deprecated Use goog.asserts.dom.assertIsHtmlEmbedElement instead.\n */\ngoog.dom.asserts.assertIsHTMLEmbedElement = function(o) {\n  return /** @type {!HTMLEmbedElement} */ (\n      goog.dom.asserts.assertIsElementType_(o, 'HTMLEmbedElement'));\n};\n\n/**\n * Asserts that a given object is a HTMLFormElement.\n *\n * To permit this assertion to pass in the context of tests where elements might\n * be mocked, also accepts objects that are not a subtype of Element.\n *\n * @param {?Object} o The object whose type to assert.\n * @return {!HTMLFormElement}\n * @deprecated Use goog.asserts.dom.assertIsHtmlFormElement instead.\n */\ngoog.dom.asserts.assertIsHTMLFormElement = function(o) {\n  return /** @type {!HTMLFormElement} */ (\n      goog.dom.asserts.assertIsElementType_(o, 'HTMLFormElement'));\n};\n\n/**\n * Asserts that a given object is a HTMLFrameElement.\n *\n * To permit this assertion to pass in the context of tests where elements might\n * be mocked, also accepts objects that are not a subtype of Element.\n *\n * @param {?Object} o The object whose type to assert.\n * @return {!HTMLFrameElement}\n * @deprecated Use goog.asserts.dom.assertIsHtmlFrameElement instead.\n */\ngoog.dom.asserts.assertIsHTMLFrameElement = function(o) {\n  return /** @type {!HTMLFrameElement} */ (\n      goog.dom.asserts.assertIsElementType_(o, 'HTMLFrameElement'));\n};\n\n/**\n * Asserts that a given object is a HTMLIFrameElement.\n *\n * To permit this assertion to pass in the context of tests where elements might\n * be mocked, also accepts objects that are not a subtype of Element.\n *\n * @param {?Object} o The object whose type to assert.\n * @return {!HTMLIFrameElement}\n * @deprecated Use goog.asserts.dom.assertIsHtmlIFrameElement instead.\n */\ngoog.dom.asserts.assertIsHTMLIFrameElement = function(o) {\n  return /** @type {!HTMLIFrameElement} */ (\n      goog.dom.asserts.assertIsElementType_(o, 'HTMLIFrameElement'));\n};\n\n/**\n * Asserts that a given object is a HTMLObjectElement.\n *\n * To permit this assertion to pass in the context of tests where elements might\n * be mocked, also accepts objects that are not a subtype of Element.\n *\n * @param {?Object} o The object whose type to assert.\n * @return {!HTMLObjectElement}\n * @deprecated Use goog.asserts.dom.assertIsHtmlObjectElement instead.\n */\ngoog.dom.asserts.assertIsHTMLObjectElement = function(o) {\n  return /** @type {!HTMLObjectElement} */ (\n      goog.dom.asserts.assertIsElementType_(o, 'HTMLObjectElement'));\n};\n\n/**\n * Asserts that a given object is a HTMLScriptElement.\n *\n * To permit this assertion to pass in the context of tests where elements might\n * be mocked, also accepts objects that are not a subtype of Element.\n *\n * @param {?Object} o The object whose type to assert.\n * @return {!HTMLScriptElement}\n * @deprecated Use goog.asserts.dom.assertIsHtmlScriptElement instead.\n */\ngoog.dom.asserts.assertIsHTMLScriptElement = function(o) {\n  return /** @type {!HTMLScriptElement} */ (\n      goog.dom.asserts.assertIsElementType_(o, 'HTMLScriptElement'));\n};\n\n/**\n * Returns a string representation of a value's type.\n *\n * @param {*} value An object, or primitive.\n * @return {string} The best display name for the value.\n * @private\n */\ngoog.dom.asserts.debugStringForType_ = function(value) {\n  if (goog.isObject(value)) {\n    try {\n      return /** @type {string|undefined} */ (value.constructor.displayName) ||\n          value.constructor.name || Object.prototype.toString.call(value);\n    } catch (e) {\n      return '<object could not be stringified>';\n    }\n  } else {\n    return value === undefined ? 'undefined' :\n                                 value === null ? 'null' : typeof value;\n  }\n};\n\n/**\n * Gets window of element.\n * @param {?Object} o\n * @return {?Window}\n * @private\n * @suppress {strictMissingProperties} ownerDocument not defined on Object\n */\ngoog.dom.asserts.getWindow_ = function(o) {\n  try {\n    var doc = o && o.ownerDocument;\n    // This can throw “Blocked a frame with origin \"chrome-extension://...\" from\n    // accessing a cross-origin frame” in Chrome extension.\n    var win =\n        doc && /** @type {?Window} */ (doc.defaultView || doc.parentWindow);\n    win = win || /** @type {!Window} */ (goog.global);\n    // This can throw “Permission denied to access property \"Element\" on\n    // cross-origin object”.\n    if (win.Element && win.Location) {\n      return win;\n    }\n  } catch (ex) {\n  }\n  return null;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\ngoog.provide('goog.string.Const');\n\ngoog.require('goog.asserts');\ngoog.require('goog.string.TypedString');\n\n\n\n/**\n * Wrapper for compile-time-constant strings.\n *\n * Const is a wrapper for strings that can only be created from program\n * constants (i.e., string literals).  This property relies on a custom Closure\n * compiler check that `goog.string.Const.from` is only invoked on\n * compile-time-constant expressions.\n *\n * Const is useful in APIs whose correct and secure use requires that certain\n * arguments are not attacker controlled: Compile-time constants are inherently\n * under the control of the application and not under control of external\n * attackers, and hence are safe to use in such contexts.\n *\n * Instances of this type must be created via its factory method\n * `goog.string.Const.from` and not by invoking its constructor.  The\n * constructor intentionally takes no parameters and the type is immutable;\n * hence only a default instance corresponding to the empty string can be\n * obtained via constructor invocation.  Use goog.string.Const.EMPTY\n * instead of using this constructor to get an empty Const string.\n *\n * @see goog.string.Const#from\n * @constructor\n * @final\n * @struct\n * @implements {goog.string.TypedString}\n * @param {Object=} opt_token package-internal implementation detail.\n * @param {string=} opt_content package-internal implementation detail.\n */\ngoog.string.Const = function(opt_token, opt_content) {\n  'use strict';\n  /**\n   * The wrapped value of this Const object.  The field has a purposely ugly\n   * name to make (non-compiled) code that attempts to directly access this\n   * field stand out.\n   * @private {string}\n   */\n  this.stringConstValueWithSecurityContract__googStringSecurityPrivate_ =\n      ((opt_token ===\n        goog.string.Const.GOOG_STRING_CONSTRUCTOR_TOKEN_PRIVATE_) &&\n       opt_content) ||\n      '';\n\n  /**\n   * A type marker used to implement additional run-time type checking.\n   * @see goog.string.Const#unwrap\n   * @const {!Object}\n   * @private\n   */\n  this.STRING_CONST_TYPE_MARKER__GOOG_STRING_SECURITY_PRIVATE_ =\n      goog.string.Const.TYPE_MARKER_;\n};\n\n\n/**\n * @override\n * @const\n */\ngoog.string.Const.prototype.implementsGoogStringTypedString = true;\n\n\n/**\n * Returns this Const's value as a string.\n *\n * IMPORTANT: In code where it is security-relevant that an object's type is\n * indeed `goog.string.Const`, use `goog.string.Const.unwrap`\n * instead of this method.\n *\n * @see goog.string.Const#unwrap\n * @override\n */\ngoog.string.Const.prototype.getTypedStringValue = function() {\n  'use strict';\n  return this.stringConstValueWithSecurityContract__googStringSecurityPrivate_;\n};\n\n\nif (goog.DEBUG) {\n  /**\n   * Returns a debug-string representation of this value.\n   *\n   * To obtain the actual string value wrapped inside an object of this type,\n   * use `goog.string.Const.unwrap`.\n   *\n   * @see goog.string.Const#unwrap\n   * @override\n   */\n  goog.string.Const.prototype.toString = function() {\n    'use strict';\n    return 'Const{' +\n        this.stringConstValueWithSecurityContract__googStringSecurityPrivate_ +\n        '}';\n  };\n}\n\n\n/**\n * Performs a runtime check that the provided object is indeed an instance\n * of `goog.string.Const`, and returns its value.\n * @param {!goog.string.Const} stringConst The object to extract from.\n * @return {string} The Const object's contained string, unless the run-time\n *     type check fails. In that case, `unwrap` returns an innocuous\n *     string, or, if assertions are enabled, throws\n *     `goog.asserts.AssertionError`.\n */\ngoog.string.Const.unwrap = function(stringConst) {\n  'use strict';\n  // Perform additional run-time type-checking to ensure that stringConst is\n  // indeed an instance of the expected type.  This provides some additional\n  // protection against security bugs due to application code that disables type\n  // checks.\n  if (stringConst instanceof goog.string.Const &&\n      stringConst.constructor === goog.string.Const &&\n      stringConst.STRING_CONST_TYPE_MARKER__GOOG_STRING_SECURITY_PRIVATE_ ===\n          goog.string.Const.TYPE_MARKER_) {\n    return stringConst\n        .stringConstValueWithSecurityContract__googStringSecurityPrivate_;\n  } else {\n    goog.asserts.fail(\n        'expected object of type Const, got \\'' + stringConst + '\\'');\n    return 'type_error:Const';\n  }\n};\n\n\n/**\n * Creates a Const object from a compile-time constant string.\n *\n * It is illegal to invoke this function on an expression whose\n * compile-time-constant value cannot be determined by the Closure compiler.\n *\n * Correct invocations include,\n * <pre>\n *   var s = goog.string.Const.from('hello');\n *   var t = goog.string.Const.from('hello' + 'world');\n * </pre>\n *\n * In contrast, the following are illegal:\n * <pre>\n *   var s = goog.string.Const.from(getHello());\n *   var t = goog.string.Const.from('hello' + world);\n * </pre>\n *\n * @param {string} s A constant string from which to create a Const.\n * @return {!goog.string.Const} A Const object initialized to stringConst.\n */\ngoog.string.Const.from = function(s) {\n  'use strict';\n  return new goog.string.Const(\n      goog.string.Const.GOOG_STRING_CONSTRUCTOR_TOKEN_PRIVATE_, s);\n};\n\n/**\n * Type marker for the Const type, used to implement additional run-time\n * type checking.\n * @const {!Object}\n * @private\n */\ngoog.string.Const.TYPE_MARKER_ = {};\n\n/**\n * @type {!Object}\n * @private\n * @const\n */\ngoog.string.Const.GOOG_STRING_CONSTRUCTOR_TOKEN_PRIVATE_ = {};\n\n/**\n * A Const instance wrapping the empty string.\n * @const {!goog.string.Const}\n */\ngoog.string.Const.EMPTY = goog.string.Const.from('');\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Policy to convert strings to Trusted Types. See\n * https://github.com/WICG/trusted-types for details.\n */\n\ngoog.provide('goog.html.trustedtypes');\n\n\n/**\n * Cached result of goog.createTrustedTypesPolicy.\n * @type {?TrustedTypePolicy|undefined}\n * @private\n */\ngoog.html.trustedtypes.cachedPolicy_;\n\n\n/**\n * Creates a (singleton) Trusted Type Policy for Safe HTML Types.\n * @return {?TrustedTypePolicy}\n * @package\n */\ngoog.html.trustedtypes.getPolicyPrivateDoNotAccessOrElse = function() {\n  if (!goog.TRUSTED_TYPES_POLICY_NAME) {\n    // Binary not configured for Trusted Types.\n    return null;\n  }\n\n  if (goog.html.trustedtypes.cachedPolicy_ === undefined) {\n    goog.html.trustedtypes.cachedPolicy_ =\n        goog.createTrustedTypesPolicy(goog.TRUSTED_TYPES_POLICY_NAME + '#html');\n  }\n\n  return goog.html.trustedtypes.cachedPolicy_;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview The TrustedResourceUrl type and its builders.\n *\n * TODO(xtof): Link to document stating type contract.\n */\n\ngoog.provide('goog.html.TrustedResourceUrl');\n\ngoog.require('goog.asserts');\ngoog.require('goog.fs.blob');\ngoog.require('goog.fs.url');\ngoog.require('goog.html.SafeScript');\ngoog.require('goog.html.trustedtypes');\ngoog.require('goog.i18n.bidi.Dir');\ngoog.require('goog.i18n.bidi.DirectionalString');\ngoog.require('goog.string.Const');\ngoog.require('goog.string.TypedString');\n\n\n\n/**\n * A URL which is under application control and from which script, CSS, and\n * other resources that represent executable code, can be fetched.\n *\n * Given that the URL can only be constructed from strings under application\n * control and is used to load resources, bugs resulting in a malformed URL\n * should not have a security impact and are likely to be easily detectable\n * during testing. Given the wide number of non-RFC compliant URLs in use,\n * stricter validation could prevent some applications from being able to use\n * this type.\n *\n * Instances of this type must be created via the factory method,\n * (`fromConstant`, `fromConstants`, `format` or `formatWithParams`), and not by\n * invoking its constructor. The constructor intentionally takes an extra\n * parameter that cannot be constructed outside of this file and the type is\n * immutable; hence only a default instance corresponding to the empty string\n * can be obtained via constructor invocation.\n *\n * Creating TrustedResourceUrl objects HAS SIDE-EFFECTS due to calling\n * Trusted Types Web API.\n *\n * @see goog.html.TrustedResourceUrl#fromConstant\n * @final\n * @struct\n * @implements {goog.i18n.bidi.DirectionalString}\n * @implements {goog.string.TypedString}\n */\ngoog.html.TrustedResourceUrl = class {\n  /**\n   * @param {!TrustedScriptURL|string} value\n   * @param {!Object} token package-internal implementation detail.\n   */\n  constructor(value, token) {\n    /**\n     * The contained value of this TrustedResourceUrl.  The field has a\n     * purposely ugly name to make (non-compiled) code that attempts to directly\n     * access this field stand out.\n     * @const\n     * @private {!TrustedScriptURL|string}\n     */\n    this.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_ =\n        (token === goog.html.TrustedResourceUrl.CONSTRUCTOR_TOKEN_PRIVATE_) ?\n        value :\n        '';\n  }\n};\n\n\n/**\n * @override\n * @const\n */\ngoog.html.TrustedResourceUrl.prototype.implementsGoogStringTypedString = true;\n\n\n/**\n * Returns this TrustedResourceUrl's value as a string.\n *\n * IMPORTANT: In code where it is security relevant that an object's type is\n * indeed `TrustedResourceUrl`, use\n * `goog.html.TrustedResourceUrl.unwrap` instead of this method. If in\n * doubt, assume that it's security relevant. In particular, note that\n * goog.html functions which return a goog.html type do not guarantee that\n * the returned instance is of the right type. For example:\n *\n * <pre>\n * var fakeSafeHtml = new String('fake');\n * fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;\n * var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);\n * // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by\n * // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml instanceof\n * // goog.html.SafeHtml.\n * </pre>\n *\n * @see goog.html.TrustedResourceUrl#unwrap\n * @override\n */\ngoog.html.TrustedResourceUrl.prototype.getTypedStringValue = function() {\n  return this.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_\n      .toString();\n};\n\n\n/**\n * @override\n * @const\n */\ngoog.html.TrustedResourceUrl.prototype.implementsGoogI18nBidiDirectionalString =\n    true;\n\n\n/**\n * Returns this URLs directionality, which is always `LTR`.\n * @override\n */\ngoog.html.TrustedResourceUrl.prototype.getDirection = function() {\n  return goog.i18n.bidi.Dir.LTR;\n};\n\n\n/**\n * Creates a new TrustedResourceUrl with params added to URL. Both search and\n * hash params can be specified.\n *\n * @param {string|?Object<string, *>|undefined} searchParams Search parameters\n *     to add to URL. See goog.html.TrustedResourceUrl.stringifyParams_ for\n *     exact format definition.\n * @param {(string|?Object<string, *>)=} opt_hashParams Hash parameters to add\n *     to URL. See goog.html.TrustedResourceUrl.stringifyParams_ for exact\n *     format definition.\n * @return {!goog.html.TrustedResourceUrl} New TrustedResourceUrl with params.\n */\ngoog.html.TrustedResourceUrl.prototype.cloneWithParams = function(\n    searchParams, opt_hashParams) {\n  var url = goog.html.TrustedResourceUrl.unwrap(this);\n  var parts = goog.html.TrustedResourceUrl.URL_PARAM_PARSER_.exec(url);\n  var urlBase = parts[1];\n  var urlSearch = parts[2] || '';\n  var urlHash = parts[3] || '';\n\n  return goog.html.TrustedResourceUrl\n      .createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(\n          urlBase +\n          goog.html.TrustedResourceUrl.stringifyParams_(\n              '?', urlSearch, searchParams) +\n          goog.html.TrustedResourceUrl.stringifyParams_(\n              '#', urlHash, opt_hashParams));\n};\n\n\nif (goog.DEBUG) {\n  /**\n   * Returns a debug string-representation of this value.\n   *\n   * To obtain the actual string value wrapped in a TrustedResourceUrl, use\n   * `goog.html.TrustedResourceUrl.unwrap`.\n   *\n   * @see goog.html.TrustedResourceUrl#unwrap\n   * @override\n   */\n  goog.html.TrustedResourceUrl.prototype.toString = function() {\n    return 'TrustedResourceUrl{' +\n        this.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_ + '}';\n  };\n}\n\n\n/**\n * Performs a runtime check that the provided object is indeed a\n * TrustedResourceUrl object, and returns its value.\n *\n * @param {!goog.html.TrustedResourceUrl} trustedResourceUrl The object to\n *     extract from.\n * @return {string} The trustedResourceUrl object's contained string, unless\n *     the run-time type check fails. In that case, `unwrap` returns an\n *     innocuous string, or, if assertions are enabled, throws\n *     `goog.asserts.AssertionError`.\n */\ngoog.html.TrustedResourceUrl.unwrap = function(trustedResourceUrl) {\n  return goog.html.TrustedResourceUrl.unwrapTrustedScriptURL(trustedResourceUrl)\n      .toString();\n};\n\n\n/**\n * Unwraps value as TrustedScriptURL if supported or as a string if not.\n * @param {!goog.html.TrustedResourceUrl} trustedResourceUrl\n * @return {!TrustedScriptURL|string}\n * @see goog.html.TrustedResourceUrl.unwrap\n */\ngoog.html.TrustedResourceUrl.unwrapTrustedScriptURL = function(\n    trustedResourceUrl) {\n  // Perform additional Run-time type-checking to ensure that\n  // trustedResourceUrl is indeed an instance of the expected type.  This\n  // provides some additional protection against security bugs due to\n  // application code that disables type checks.\n  // Specifically, the following checks are performed:\n  // 1. The object is an instance of the expected type.\n  // 2. The object is not an instance of a subclass.\n  if (trustedResourceUrl instanceof goog.html.TrustedResourceUrl &&\n      trustedResourceUrl.constructor === goog.html.TrustedResourceUrl) {\n    return trustedResourceUrl\n        .privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_;\n  } else {\n    goog.asserts.fail('expected object of type TrustedResourceUrl, got \\'' +\n        trustedResourceUrl + '\\' of type ' + goog.typeOf(trustedResourceUrl));\n    return 'type_error:TrustedResourceUrl';\n  }\n};\n\n\n/**\n * Creates a TrustedResourceUrl from a format string and arguments.\n *\n * The arguments for interpolation into the format string map labels to values.\n * Values of type `goog.string.Const` are interpolated without modifcation.\n * Values of other types are cast to string and encoded with\n * encodeURIComponent.\n *\n * `%{<label>}` markers are used in the format string to indicate locations\n * to be interpolated with the valued mapped to the given label. `<label>`\n * must contain only alphanumeric and `_` characters.\n *\n * The format string must match goog.html.TrustedResourceUrl.BASE_URL_.\n *\n * Example usage:\n *\n *    var url = goog.html.TrustedResourceUrl.format(goog.string.Const.from(\n *        'https://www.google.com/search?q=%{query}'), {'query': searchTerm});\n *\n *    var url = goog.html.TrustedResourceUrl.format(goog.string.Const.from(\n *        '//www.youtube.com/v/%{videoId}?hl=en&fs=1%{autoplay}'), {\n *        'videoId': videoId,\n *        'autoplay': opt_autoplay ?\n *            goog.string.Const.from('&autoplay=1') : goog.string.Const.EMPTY\n *    });\n *\n * While this function can be used to create a TrustedResourceUrl from only\n * constants, fromConstant() and fromConstants() are generally preferable for\n * that purpose.\n *\n * @param {!goog.string.Const} format The format string.\n * @param {!Object<string, (string|number|!goog.string.Const)>} args Mapping\n *     of labels to values to be interpolated into the format string.\n *     goog.string.Const values are interpolated without encoding.\n * @return {!goog.html.TrustedResourceUrl}\n * @throws {!Error} On an invalid format string or if a label used in the\n *     the format string is not present in args.\n */\ngoog.html.TrustedResourceUrl.format = function(format, args) {\n  var formatStr = goog.string.Const.unwrap(format);\n  if (!goog.html.TrustedResourceUrl.BASE_URL_.test(formatStr)) {\n    throw new Error('Invalid TrustedResourceUrl format: ' + formatStr);\n  }\n  var result = formatStr.replace(\n      goog.html.TrustedResourceUrl.FORMAT_MARKER_, function(match, id) {\n        if (!Object.prototype.hasOwnProperty.call(args, id)) {\n          throw new Error(\n              'Found marker, \"' + id + '\", in format string, \"' + formatStr +\n              '\", but no valid label mapping found ' +\n              'in args: ' + JSON.stringify(args));\n        }\n        var arg = args[id];\n        if (arg instanceof goog.string.Const) {\n          return goog.string.Const.unwrap(arg);\n        } else {\n          return encodeURIComponent(String(arg));\n        }\n      });\n  return goog.html.TrustedResourceUrl\n      .createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(result);\n};\n\n\n/**\n * @private @const {!RegExp}\n */\ngoog.html.TrustedResourceUrl.FORMAT_MARKER_ = /%{(\\w+)}/g;\n\n\n/**\n * The URL must be absolute, scheme-relative or path-absolute. So it must\n * start with:\n * - https:// followed by allowed origin characters.\n * - // followed by allowed origin characters.\n * - Any absolute or relative path.\n *\n * Based on\n * https://url.spec.whatwg.org/commit-snapshots/56b74ce7cca8883eab62e9a12666e2fac665d03d/#url-parsing\n * an initial / which is not followed by another / or \\ will end up in the \"path\n * state\" and from there it can only go to \"fragment state\" and \"query state\".\n *\n * We don't enforce a well-formed domain name. So '.' or '1.2' are valid.\n * That's ok because the origin comes from a compile-time constant.\n *\n * A regular expression is used instead of goog.uri for several reasons:\n * - Strictness. E.g. we don't want any userinfo component and we don't\n *   want '/./, nor \\' in the first path component.\n * - Small trusted base. goog.uri is generic and might need to change,\n *   reasoning about all the ways it can parse a URL now and in the future\n *   is error-prone.\n * - Code size. We expect many calls to .format(), many of which might\n *   not be using goog.uri.\n * - Simplicity. Using goog.uri would likely not result in simpler nor shorter\n *   code.\n * @private @const {!RegExp}\n */\ngoog.html.TrustedResourceUrl.BASE_URL_ = new RegExp(\n    '^((https:)?//[0-9a-z.:[\\\\]-]+/'  // Origin.\n        + '|/[^/\\\\\\\\]'                // Absolute path.\n        + '|[^:/\\\\\\\\%]+/'             // Relative path.\n        + '|[^:/\\\\\\\\%]*[?#]'          // Query string or fragment.\n        + '|about:blank#'             // about:blank with fragment.\n        + ')',\n    'i');\n\n/**\n * RegExp for splitting a URL into the base, search field, and hash field.\n *\n * @private @const {!RegExp}\n */\ngoog.html.TrustedResourceUrl.URL_PARAM_PARSER_ =\n    /^([^?#]*)(\\?[^#]*)?(#[\\s\\S]*)?/;\n\n\n/**\n * Formats the URL same as TrustedResourceUrl.format and then adds extra URL\n * parameters.\n *\n * Example usage:\n *\n *     // Creates '//www.youtube.com/v/abc?autoplay=1' for videoId='abc' and\n *     // opt_autoplay=1. Creates '//www.youtube.com/v/abc' for videoId='abc'\n *     // and opt_autoplay=undefined.\n *     var url = goog.html.TrustedResourceUrl.formatWithParams(\n *         goog.string.Const.from('//www.youtube.com/v/%{videoId}'),\n *         {'videoId': videoId},\n *         {'autoplay': opt_autoplay});\n *\n * @param {!goog.string.Const} format The format string.\n * @param {!Object<string, (string|number|!goog.string.Const)>} args Mapping\n *     of labels to values to be interpolated into the format string.\n *     goog.string.Const values are interpolated without encoding.\n * @param {string|?Object<string, *>|undefined} searchParams Parameters to add\n *     to URL. See goog.html.TrustedResourceUrl.stringifyParams_ for exact\n *     format definition.\n * @param {(string|?Object<string, *>)=} opt_hashParams Hash parameters to add\n *     to URL. See goog.html.TrustedResourceUrl.stringifyParams_ for exact\n *     format definition.\n * @return {!goog.html.TrustedResourceUrl}\n * @throws {!Error} On an invalid format string or if a label used in the\n *     the format string is not present in args.\n */\ngoog.html.TrustedResourceUrl.formatWithParams = function(\n    format, args, searchParams, opt_hashParams) {\n  var url = goog.html.TrustedResourceUrl.format(format, args);\n  return url.cloneWithParams(searchParams, opt_hashParams);\n};\n\n\n/**\n * Creates a TrustedResourceUrl object from a compile-time constant string.\n *\n * Compile-time constant strings are inherently program-controlled and hence\n * trusted.\n *\n * @param {!goog.string.Const} url A compile-time-constant string from which to\n *     create a TrustedResourceUrl.\n * @return {!goog.html.TrustedResourceUrl} A TrustedResourceUrl object\n *     initialized to `url`.\n */\ngoog.html.TrustedResourceUrl.fromConstant = function(url) {\n  return goog.html.TrustedResourceUrl\n      .createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(\n          goog.string.Const.unwrap(url));\n};\n\n\n/**\n * Creates a TrustedResourceUrl object from a compile-time constant strings.\n *\n * Compile-time constant strings are inherently program-controlled and hence\n * trusted.\n *\n * @param {!Array<!goog.string.Const>} parts Compile-time-constant strings from\n *     which to create a TrustedResourceUrl.\n * @return {!goog.html.TrustedResourceUrl} A TrustedResourceUrl object\n *     initialized to concatenation of `parts`.\n */\ngoog.html.TrustedResourceUrl.fromConstants = function(parts) {\n  var unwrapped = '';\n  for (var i = 0; i < parts.length; i++) {\n    unwrapped += goog.string.Const.unwrap(parts[i]);\n  }\n  return goog.html.TrustedResourceUrl\n      .createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(unwrapped);\n};\n\n/**\n * Creates a TrustedResourceUrl object by generating a Blob from a SafeScript\n * object and then calling createObjectURL with that blob.\n *\n * SafeScript objects are trusted to contain executable JavaScript code.\n *\n * Caller must call goog.fs.url.revokeObjectUrl() on the unwrapped url to\n * release the underlying blob.\n *\n * Throws if browser doesn't support blob construction.\n *\n * @param {!goog.html.SafeScript} safeScript A script from which to create a\n *     TrustedResourceUrl.\n * @return {!goog.html.TrustedResourceUrl} A TrustedResourceUrl object\n *     initialized to a new blob URL.\n */\ngoog.html.TrustedResourceUrl.fromSafeScript = function(safeScript) {\n  var blob = goog.fs.blob.getBlobWithProperties(\n      [goog.html.SafeScript.unwrap(safeScript)], 'text/javascript');\n  var url = goog.fs.url.createObjectUrl(blob);\n  return goog.html.TrustedResourceUrl\n      .createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(url);\n};\n\n\n/**\n * Token used to ensure that object is created only from this file. No code\n * outside of this file can access this token.\n * @private {!Object}\n * @const\n */\ngoog.html.TrustedResourceUrl.CONSTRUCTOR_TOKEN_PRIVATE_ = {};\n\n\n/**\n * Package-internal utility method to create TrustedResourceUrl instances.\n *\n * @param {string} url The string to initialize the TrustedResourceUrl object\n *     with.\n * @return {!goog.html.TrustedResourceUrl} The initialized TrustedResourceUrl\n *     object.\n * @package\n */\ngoog.html.TrustedResourceUrl\n    .createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse = function(url) {\n  const policy = goog.html.trustedtypes.getPolicyPrivateDoNotAccessOrElse();\n  var value = policy ? policy.createScriptURL(url) : url;\n  return new goog.html.TrustedResourceUrl(\n      value, goog.html.TrustedResourceUrl.CONSTRUCTOR_TOKEN_PRIVATE_);\n};\n\n\n/**\n * Stringifies the passed params to be used as either a search or hash field of\n * a URL.\n *\n * @param {string} prefix The prefix character for the given field ('?' or '#').\n * @param {string} currentString The existing field value (including the prefix\n *     character, if the field is present).\n * @param {string|?Object<string, *>|undefined} params The params to set or\n *     append to the field.\n * - If `undefined` or `null`, the field remains unchanged.\n * - If a string, then the string will be escaped and the field will be\n *   overwritten with that value.\n * - If an Object, that object is treated as a set of key-value pairs to be\n *   appended to the current field. Note that JavaScript doesn't guarantee the\n *   order of values in an object which might result in non-deterministic order\n *   of the parameters. However, browsers currently preserve the order. The\n *   rules for each entry:\n *   - If an array, it will be processed as if each entry were an additional\n *     parameter with exactly the same key, following the same logic below.\n *   - If `undefined` or `null`, it will be skipped.\n *   - Otherwise, it will be turned into a string, escaped, and appended.\n * @return {string}\n * @private\n */\ngoog.html.TrustedResourceUrl.stringifyParams_ = function(\n    prefix, currentString, params) {\n  if (params == null) {\n    // Do not modify the field.\n    return currentString;\n  }\n  if (typeof params === 'string') {\n    // Set field to the passed string.\n    return params ? prefix + encodeURIComponent(params) : '';\n  }\n  // Add on parameters to field from key-value object.\n  for (var key in params) {\n    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty#Using_hasOwnProperty_as_a_property_name\n    if (Object.prototype.hasOwnProperty.call(params, key)) {\n      var value = params[key];\n      var outputValues = Array.isArray(value) ? value : [value];\n      for (var i = 0; i < outputValues.length; i++) {\n        var outputValue = outputValues[i];\n        if (outputValue != null) {\n          if (!currentString) {\n            currentString = prefix;\n          }\n          currentString += (currentString.length > prefix.length ? '&' : '') +\n              encodeURIComponent(key) + '=' +\n              encodeURIComponent(String(outputValue));\n        }\n      }\n    }\n  }\n  return currentString;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview The SafeUrl type and its builders.\n *\n * TODO(xtof): Link to document stating type contract.\n */\n\ngoog.provide('goog.html.SafeUrl');\n\ngoog.require('goog.asserts');\ngoog.require('goog.fs.url');\ngoog.require('goog.html.TrustedResourceUrl');\ngoog.require('goog.i18n.bidi.Dir');\ngoog.require('goog.i18n.bidi.DirectionalString');\ngoog.require('goog.string.Const');\ngoog.require('goog.string.TypedString');\ngoog.require('goog.string.internal');\n\n\n\n/**\n * A string that is safe to use in URL context in DOM APIs and HTML documents.\n *\n * A SafeUrl is a string-like object that carries the security type contract\n * that its value as a string will not cause untrusted script execution\n * when evaluated as a hyperlink URL in a browser.\n *\n * Values of this type are guaranteed to be safe to use in URL/hyperlink\n * contexts, such as assignment to URL-valued DOM properties, in the sense that\n * the use will not result in a Cross-Site-Scripting vulnerability. Similarly,\n * SafeUrls can be interpolated into the URL context of an HTML template (e.g.,\n * inside a href attribute). However, appropriate HTML-escaping must still be\n * applied.\n *\n * Note that, as documented in `goog.html.SafeUrl.unwrap`, this type's\n * contract does not guarantee that instances are safe to interpolate into HTML\n * without appropriate escaping.\n *\n * Note also that this type's contract does not imply any guarantees regarding\n * the resource the URL refers to.  In particular, SafeUrls are <b>not</b>\n * safe to use in a context where the referred-to resource is interpreted as\n * trusted code, e.g., as the src of a script tag.\n *\n * Instances of this type must be created via the factory methods\n * (`goog.html.SafeUrl.fromConstant`, `goog.html.SafeUrl.sanitize`),\n * etc and not by invoking its constructor. The constructor intentionally takes\n * an extra parameter that cannot be constructed outside of this file and the\n * type is immutable; hence only a default instance corresponding to the empty\n * string can be obtained via constructor invocation.\n *\n * @see goog.html.SafeUrl#fromConstant\n * @see goog.html.SafeUrl#from\n * @see goog.html.SafeUrl#sanitize\n * @final\n * @struct\n * @implements {goog.i18n.bidi.DirectionalString}\n * @implements {goog.string.TypedString}\n */\ngoog.html.SafeUrl = class {\n  /**\n   * @param {string} value\n   * @param {!Object} token package-internal implementation detail.\n   */\n  constructor(value, token) {\n    /**\n     * The contained value of this SafeUrl.  The field has a purposely ugly\n     * name to make (non-compiled) code that attempts to directly access this\n     * field stand out.\n     * @private {string}\n     */\n    this.privateDoNotAccessOrElseSafeUrlWrappedValue_ =\n        (token === goog.html.SafeUrl.CONSTRUCTOR_TOKEN_PRIVATE_) ? value : '';\n  };\n};\n\n\n/**\n * The innocuous string generated by goog.html.SafeUrl.sanitize when passed\n * an unsafe URL.\n *\n * about:invalid is registered in\n * http://www.w3.org/TR/css3-values/#about-invalid.\n * http://tools.ietf.org/html/rfc6694#section-2.2.1 permits about URLs to\n * contain a fragment, which is not to be considered when determining if an\n * about URL is well-known.\n *\n * Using about:invalid seems preferable to using a fixed data URL, since\n * browsers might choose to not report CSP violations on it, as legitimate\n * CSS function calls to attr() can result in this URL being produced. It is\n * also a standard URL which matches exactly the semantics we need:\n * \"The about:invalid URI references a non-existent document with a generic\n * error condition. It can be used when a URI is necessary, but the default\n * value shouldn't be resolveable as any type of document\".\n *\n * @const {string}\n */\ngoog.html.SafeUrl.INNOCUOUS_STRING = 'about:invalid#zClosurez';\n\n\n/**\n * @override\n * @const\n */\ngoog.html.SafeUrl.prototype.implementsGoogStringTypedString = true;\n\n\n/**\n * Returns this SafeUrl's value as a string.\n *\n * IMPORTANT: In code where it is security relevant that an object's type is\n * indeed `SafeUrl`, use `goog.html.SafeUrl.unwrap` instead of this\n * method. If in doubt, assume that it's security relevant. In particular, note\n * that goog.html functions which return a goog.html type do not guarantee that\n * the returned instance is of the right type.\n *\n * IMPORTANT: The guarantees of the SafeUrl type contract only extend to the\n * behavior of browsers when interpreting URLs. Values of SafeUrl objects MUST\n * be appropriately escaped before embedding in a HTML document. Note that the\n * required escaping is context-sensitive (e.g. a different escaping is\n * required for embedding a URL in a style property within a style\n * attribute, as opposed to embedding in a href attribute).\n *\n * @see goog.html.SafeUrl#unwrap\n * @override\n */\ngoog.html.SafeUrl.prototype.getTypedStringValue = function() {\n  return this.privateDoNotAccessOrElseSafeUrlWrappedValue_.toString();\n};\n\n\n/**\n * @override\n * @const\n */\ngoog.html.SafeUrl.prototype.implementsGoogI18nBidiDirectionalString = true;\n\n\n/**\n * Returns this URLs directionality, which is always `LTR`.\n * @override\n */\ngoog.html.SafeUrl.prototype.getDirection = function() {\n  return goog.i18n.bidi.Dir.LTR;\n};\n\n\nif (goog.DEBUG) {\n  /**\n   * Returns a debug string-representation of this value.\n   *\n   * To obtain the actual string value wrapped in a SafeUrl, use\n   * `goog.html.SafeUrl.unwrap`.\n   *\n   * @see goog.html.SafeUrl#unwrap\n   * @override\n   */\n  goog.html.SafeUrl.prototype.toString = function() {\n    return 'SafeUrl{' + this.privateDoNotAccessOrElseSafeUrlWrappedValue_ + '}';\n  };\n}\n\n\n/**\n * Performs a runtime check that the provided object is indeed a SafeUrl\n * object, and returns its value.\n *\n * IMPORTANT: The guarantees of the SafeUrl type contract only extend to the\n * behavior of  browsers when interpreting URLs. Values of SafeUrl objects MUST\n * be appropriately escaped before embedding in a HTML document. Note that the\n * required escaping is context-sensitive (e.g. a different escaping is\n * required for embedding a URL in a style property within a style\n * attribute, as opposed to embedding in a href attribute).\n *\n * @param {!goog.html.SafeUrl} safeUrl The object to extract from.\n * @return {string} The SafeUrl object's contained string, unless the run-time\n *     type check fails. In that case, `unwrap` returns an innocuous\n *     string, or, if assertions are enabled, throws\n *     `goog.asserts.AssertionError`.\n */\ngoog.html.SafeUrl.unwrap = function(safeUrl) {\n  // Perform additional Run-time type-checking to ensure that safeUrl is indeed\n  // an instance of the expected type.  This provides some additional protection\n  // against security bugs due to application code that disables type checks.\n  // Specifically, the following checks are performed:\n  // 1. The object is an instance of the expected type.\n  // 2. The object is not an instance of a subclass.\n  if (safeUrl instanceof goog.html.SafeUrl &&\n      safeUrl.constructor === goog.html.SafeUrl) {\n    return safeUrl.privateDoNotAccessOrElseSafeUrlWrappedValue_;\n  } else {\n    goog.asserts.fail('expected object of type SafeUrl, got \\'' +\n        safeUrl + '\\' of type ' + goog.typeOf(safeUrl));\n    return 'type_error:SafeUrl';\n  }\n};\n\n\n/**\n * Creates a SafeUrl object from a compile-time constant string.\n *\n * Compile-time constant strings are inherently program-controlled and hence\n * trusted.\n *\n * @param {!goog.string.Const} url A compile-time-constant string from which to\n *         create a SafeUrl.\n * @return {!goog.html.SafeUrl} A SafeUrl object initialized to `url`.\n */\ngoog.html.SafeUrl.fromConstant = function(url) {\n  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(\n      goog.string.Const.unwrap(url));\n};\n\n\n/**\n * A pattern that matches Blob or data types that can have SafeUrls created\n * from URL.createObjectURL(blob) or via a data: URI.\n *\n * This has some parameter support (most notably, we haven't implemented the\n * more complex parts like %-encoded characters or non-alphanumerical ones for\n * simplicity's sake). The specs are fairly complex, and they don't\n * always match Chrome's behavior: we settled on a subset where we're confident\n * all parties involved agree.\n *\n * The spec is available at https://mimesniff.spec.whatwg.org/ (and see\n * https://tools.ietf.org/html/rfc2397 for data: urls, which override some of\n * it).\n * @const\n * @private\n */\ngoog.html.SAFE_MIME_TYPE_PATTERN_ = new RegExp(\n    // Note: Due to content-sniffing concerns, only add MIME types for\n    // media formats.\n    '^(?:audio/(?:3gpp2|3gpp|aac|L16|midi|mp3|mp4|mpeg|oga|ogg|opus|x-m4a|x-matroska|x-wav|wav|webm)|' +\n        'font/\\\\w+|' +\n        'image/(?:bmp|gif|jpeg|jpg|png|tiff|webp|x-icon)|' +\n        // TODO(user): Due to content-sniffing concerns, text/csv should\n        // be removed from the whitelist.\n        'text/csv|' +\n        'video/(?:mpeg|mp4|ogg|webm|quicktime|x-matroska))' +\n        '(?:;\\\\w+=(?:\\\\w+|\"[\\\\w;,= ]+\"))*$',  // MIME type parameters\n    'i');\n\n\n/**\n * @param {string} mimeType The MIME type to check if safe.\n * @return {boolean} True if the MIME type is safe and creating a Blob via\n *   `SafeUrl.fromBlob()` with that type will not fail due to the type. False\n *   otherwise.\n */\ngoog.html.SafeUrl.isSafeMimeType = function(mimeType) {\n  return goog.html.SAFE_MIME_TYPE_PATTERN_.test(mimeType);\n};\n\n\n/**\n * Creates a SafeUrl wrapping a blob URL for the given `blob`.\n *\n * The blob URL is created with `URL.createObjectURL`. If the MIME type\n * for `blob` is not of a known safe audio, image or video MIME type,\n * then the SafeUrl will wrap {@link #INNOCUOUS_STRING}.\n *\n * Note: Call {@link revokeObjectUrl} on the URL after it's used\n * to prevent memory leaks.\n *\n * @see http://www.w3.org/TR/FileAPI/#url\n * @param {!Blob} blob\n * @return {!goog.html.SafeUrl} The blob URL, or an innocuous string wrapped\n *   as a SafeUrl.\n */\ngoog.html.SafeUrl.fromBlob = function(blob) {\n  var url = goog.html.SafeUrl.isSafeMimeType(blob.type) ?\n      goog.fs.url.createObjectUrl(blob) :\n      goog.html.SafeUrl.INNOCUOUS_STRING;\n  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);\n};\n\n\n/**\n * Revokes an object URL created for a safe URL created {@link fromBlob()}.\n * @param {!goog.html.SafeUrl} safeUrl SafeUrl wrapping a blob object.\n */\ngoog.html.SafeUrl.revokeObjectUrl = function(safeUrl) {\n  var url = safeUrl.getTypedStringValue();\n  if (url !== goog.html.SafeUrl.INNOCUOUS_STRING) {\n    goog.fs.url.revokeObjectUrl(url);\n  }\n};\n\n\n/**\n * Creates a SafeUrl wrapping a blob URL created for a MediaSource.\n * @param {!MediaSource} mediaSource\n * @return {!goog.html.SafeUrl} The blob URL.\n */\ngoog.html.SafeUrl.fromMediaSource = function(mediaSource) {\n  goog.asserts.assert(\n      'MediaSource' in goog.global, 'No support for MediaSource');\n  const url = mediaSource instanceof MediaSource ?\n      goog.fs.url.createObjectUrl(mediaSource) :\n      goog.html.SafeUrl.INNOCUOUS_STRING;\n  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);\n};\n\n\n/**\n * Matches a base-64 data URL, with the first match group being the MIME type.\n * @const\n * @private\n */\ngoog.html.DATA_URL_PATTERN_ = /^data:(.*);base64,[a-z0-9+\\/]+=*$/i;\n\n\n/**\n * Attempts to create a SafeUrl wrapping a `data:` URL, after validating it\n * matches a known-safe media MIME type. If it doesn't match, return `null`.\n *\n * @param {string} dataUrl A valid base64 data URL with one of the whitelisted\n *     media MIME types.\n * @return {?goog.html.SafeUrl} A matching safe URL, or `null` if it does not\n *     pass.\n */\ngoog.html.SafeUrl.tryFromDataUrl = function(dataUrl) {\n  // For defensive purposes, in case users cast around the parameter type.\n  dataUrl = String(dataUrl);\n  // RFC4648 suggest to ignore CRLF in base64 encoding.\n  // See https://tools.ietf.org/html/rfc4648.\n  // Remove the CR (%0D) and LF (%0A) from the dataUrl.\n  var filteredDataUrl = dataUrl.replace(/(%0A|%0D)/g, '');\n  var match = filteredDataUrl.match(goog.html.DATA_URL_PATTERN_);\n  // Note: The only risk of XSS here is if the `data:` URL results in a\n  // same-origin document. In which case content-sniffing might cause the\n  // browser to interpret the contents as html.\n  // All modern browsers consider `data:` URL documents to have unique empty\n  // origins. Only Firefox for versions prior to v57 behaves differently:\n  // https://blog.mozilla.org/security/2017/10/04/treating-data-urls-unique-origins-firefox-57/\n  // Older versions of IE don't understand `data:` urls, so it is not an issue.\n  var valid = match && goog.html.SafeUrl.isSafeMimeType(match[1]);\n  if (valid) {\n    return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(\n        filteredDataUrl);\n  }\n  return null;\n};\n\n\n/**\n * Creates a SafeUrl wrapping a `data:` URL, after validating it matches a\n * known-safe media MIME type. If it doesn't match, return\n * `goog.html.SafeUrl.INNOCUOUS_URL`.\n *\n * @param {string} dataUrl A valid base64 data URL with one of the whitelisted\n *     media MIME types.\n * @return {!goog.html.SafeUrl} A matching safe URL, or\n *     `goog.html.SafeUrl.INNOCUOUS_URL` if it does not pass.\n */\ngoog.html.SafeUrl.fromDataUrl = function(dataUrl) {\n  return goog.html.SafeUrl.tryFromDataUrl(dataUrl) ||\n      goog.html.SafeUrl.INNOCUOUS_URL;\n};\n\n\n/**\n * Creates a SafeUrl wrapping a tel: URL.\n *\n * @param {string} telUrl A tel URL.\n * @return {!goog.html.SafeUrl} A matching safe URL, or {@link INNOCUOUS_STRING}\n *     wrapped as a SafeUrl if it does not pass.\n */\ngoog.html.SafeUrl.fromTelUrl = function(telUrl) {\n  // There's a risk that a tel: URL could immediately place a call once\n  // clicked, without requiring user confirmation. For that reason it is\n  // handled in this separate function.\n  if (!goog.string.internal.caseInsensitiveStartsWith(telUrl, 'tel:')) {\n    telUrl = goog.html.SafeUrl.INNOCUOUS_STRING;\n  }\n  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(\n      telUrl);\n};\n\n\n/**\n * Matches a sip/sips URL. We only allow urls that consist of an email address.\n * The characters '?' and '#' are not allowed in the local part of the email\n * address.\n * @const\n * @private\n */\ngoog.html.SIP_URL_PATTERN_ = new RegExp(\n    '^sip[s]?:[+a-z0-9_.!$%&\\'*\\\\/=^`{|}~-]+@([a-z0-9-]+\\\\.)+[a-z0-9]{2,63}$',\n    'i');\n\n\n/**\n * Creates a SafeUrl wrapping a sip: URL. We only allow urls that consist of an\n * email address. The characters '?' and '#' are not allowed in the local part\n * of the email address.\n *\n * @param {string} sipUrl A sip URL.\n * @return {!goog.html.SafeUrl} A matching safe URL, or {@link INNOCUOUS_STRING}\n *     wrapped as a SafeUrl if it does not pass.\n */\ngoog.html.SafeUrl.fromSipUrl = function(sipUrl) {\n  if (!goog.html.SIP_URL_PATTERN_.test(decodeURIComponent(sipUrl))) {\n    sipUrl = goog.html.SafeUrl.INNOCUOUS_STRING;\n  }\n  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(\n      sipUrl);\n};\n\n\n/**\n * Creates a SafeUrl wrapping a fb-messenger://share URL.\n *\n * @param {string} facebookMessengerUrl A facebook messenger URL.\n * @return {!goog.html.SafeUrl} A matching safe URL, or {@link INNOCUOUS_STRING}\n *     wrapped as a SafeUrl if it does not pass.\n */\ngoog.html.SafeUrl.fromFacebookMessengerUrl = function(facebookMessengerUrl) {\n  if (!goog.string.internal.caseInsensitiveStartsWith(\n          facebookMessengerUrl, 'fb-messenger://share')) {\n    facebookMessengerUrl = goog.html.SafeUrl.INNOCUOUS_STRING;\n  }\n  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(\n      facebookMessengerUrl);\n};\n\n/**\n * Creates a SafeUrl wrapping a whatsapp://send URL.\n *\n * @param {string} whatsAppUrl A WhatsApp URL.\n * @return {!goog.html.SafeUrl} A matching safe URL, or {@link INNOCUOUS_STRING}\n *     wrapped as a SafeUrl if it does not pass.\n */\ngoog.html.SafeUrl.fromWhatsAppUrl = function(whatsAppUrl) {\n  if (!goog.string.internal.caseInsensitiveStartsWith(\n          whatsAppUrl, 'whatsapp://send')) {\n    whatsAppUrl = goog.html.SafeUrl.INNOCUOUS_STRING;\n  }\n  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(\n      whatsAppUrl);\n};\n\n/**\n * Creates a SafeUrl wrapping a sms: URL.\n *\n * @param {string} smsUrl A sms URL.\n * @return {!goog.html.SafeUrl} A matching safe URL, or {@link INNOCUOUS_STRING}\n *     wrapped as a SafeUrl if it does not pass.\n */\ngoog.html.SafeUrl.fromSmsUrl = function(smsUrl) {\n  if (!goog.string.internal.caseInsensitiveStartsWith(smsUrl, 'sms:') ||\n      !goog.html.SafeUrl.isSmsUrlBodyValid_(smsUrl)) {\n    smsUrl = goog.html.SafeUrl.INNOCUOUS_STRING;\n  }\n  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(\n      smsUrl);\n};\n\n\n/**\n * Validates SMS URL `body` parameter, which is optional and should appear at\n * most once and should be percent-encoded if present. Rejects many malformed\n * bodies, but may spuriously reject some URLs and does not reject all malformed\n * sms: URLs.\n *\n * @param {string} smsUrl A sms URL.\n * @return {boolean} Whether SMS URL has a valid `body` parameter if it exists.\n * @private\n */\ngoog.html.SafeUrl.isSmsUrlBodyValid_ = function(smsUrl) {\n  var hash = smsUrl.indexOf('#');\n  if (hash > 0) {\n    smsUrl = smsUrl.substring(0, hash);\n  }\n  var bodyParams = smsUrl.match(/[?&]body=/gi);\n  // \"body\" param is optional\n  if (!bodyParams) {\n    return true;\n  }\n  // \"body\" MUST only appear once\n  if (bodyParams.length > 1) {\n    return false;\n  }\n  // Get the encoded `body` parameter value.\n  var bodyValue = smsUrl.match(/[?&]body=([^&]*)/)[1];\n  if (!bodyValue) {\n    return true;\n  }\n  try {\n    decodeURIComponent(bodyValue);\n  } catch (error) {\n    return false;\n  }\n  return /^(?:[a-z0-9\\-_.~]|%[0-9a-f]{2})+$/i.test(bodyValue);\n};\n\n\n/**\n * Creates a SafeUrl wrapping a ssh: URL.\n *\n * @param {string} sshUrl A ssh URL.\n * @return {!goog.html.SafeUrl} A matching safe URL, or {@link INNOCUOUS_STRING}\n *     wrapped as a SafeUrl if it does not pass.\n */\ngoog.html.SafeUrl.fromSshUrl = function(sshUrl) {\n  if (!goog.string.internal.caseInsensitiveStartsWith(sshUrl, 'ssh://')) {\n    sshUrl = goog.html.SafeUrl.INNOCUOUS_STRING;\n  }\n  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(\n      sshUrl);\n};\n\n/**\n * Sanitizes a Chrome extension URL to SafeUrl, given a compile-time-constant\n * extension identifier. Can also be restricted to chrome extensions.\n *\n * @param {string} url The url to sanitize. Should start with the extension\n *     scheme and the extension identifier.\n * @param {!goog.string.Const|!Array<!goog.string.Const>} extensionId The\n *     extension id to accept, as a compile-time constant or an array of those.\n *\n * @return {!goog.html.SafeUrl} Either `url` if it's deemed safe, or\n *     `INNOCUOUS_STRING` if it's not.\n */\ngoog.html.SafeUrl.sanitizeChromeExtensionUrl = function(url, extensionId) {\n  return goog.html.SafeUrl.sanitizeExtensionUrl_(\n      /^chrome-extension:\\/\\/([^\\/]+)\\//, url, extensionId);\n};\n\n/**\n * Sanitizes a Firefox extension URL to SafeUrl, given a compile-time-constant\n * extension identifier. Can also be restricted to chrome extensions.\n *\n * @param {string} url The url to sanitize. Should start with the extension\n *     scheme and the extension identifier.\n * @param {!goog.string.Const|!Array<!goog.string.Const>} extensionId The\n *     extension id to accept, as a compile-time constant or an array of those.\n *\n * @return {!goog.html.SafeUrl} Either `url` if it's deemed safe, or\n *     `INNOCUOUS_STRING` if it's not.\n */\ngoog.html.SafeUrl.sanitizeFirefoxExtensionUrl = function(url, extensionId) {\n  return goog.html.SafeUrl.sanitizeExtensionUrl_(\n      /^moz-extension:\\/\\/([^\\/]+)\\//, url, extensionId);\n};\n\n/**\n * Sanitizes a Edge extension URL to SafeUrl, given a compile-time-constant\n * extension identifier. Can also be restricted to chrome extensions.\n *\n * @param {string} url The url to sanitize. Should start with the extension\n *     scheme and the extension identifier.\n * @param {!goog.string.Const|!Array<!goog.string.Const>} extensionId The\n *     extension id to accept, as a compile-time constant or an array of those.\n *\n * @return {!goog.html.SafeUrl} Either `url` if it's deemed safe, or\n *     `INNOCUOUS_STRING` if it's not.\n */\ngoog.html.SafeUrl.sanitizeEdgeExtensionUrl = function(url, extensionId) {\n  return goog.html.SafeUrl.sanitizeExtensionUrl_(\n      /^ms-browser-extension:\\/\\/([^\\/]+)\\//, url, extensionId);\n};\n\n/**\n * Private helper for converting extension URLs to SafeUrl, given the scheme for\n * that particular extension type. Use the sanitizeFirefoxExtensionUrl,\n * sanitizeChromeExtensionUrl or sanitizeEdgeExtensionUrl unless you're building\n * new helpers.\n *\n * @private\n * @param {!RegExp} scheme The scheme to accept as a RegExp extracting the\n *     extension identifier.\n * @param {string} url The url to sanitize. Should start with the extension\n *     scheme and the extension identifier.\n * @param {!goog.string.Const|!Array<!goog.string.Const>} extensionId The\n *     extension id to accept, as a compile-time constant or an array of those.\n *\n * @return {!goog.html.SafeUrl} Either `url` if it's deemed safe, or\n *     `INNOCUOUS_STRING` if it's not.\n */\ngoog.html.SafeUrl.sanitizeExtensionUrl_ = function(scheme, url, extensionId) {\n  var matches = scheme.exec(url);\n  if (!matches) {\n    url = goog.html.SafeUrl.INNOCUOUS_STRING;\n  } else {\n    var extractedExtensionId = matches[1];\n    var acceptedExtensionIds;\n    if (extensionId instanceof goog.string.Const) {\n      acceptedExtensionIds = [goog.string.Const.unwrap(extensionId)];\n    } else {\n      acceptedExtensionIds = extensionId.map(function unwrap(x) {\n        return goog.string.Const.unwrap(x);\n      });\n    }\n    if (acceptedExtensionIds.indexOf(extractedExtensionId) == -1) {\n      url = goog.html.SafeUrl.INNOCUOUS_STRING;\n    }\n  }\n  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);\n};\n\n\n/**\n * Creates a SafeUrl from TrustedResourceUrl. This is safe because\n * TrustedResourceUrl is more tightly restricted than SafeUrl.\n *\n * @param {!goog.html.TrustedResourceUrl} trustedResourceUrl\n * @return {!goog.html.SafeUrl}\n */\ngoog.html.SafeUrl.fromTrustedResourceUrl = function(trustedResourceUrl) {\n  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(\n      goog.html.TrustedResourceUrl.unwrap(trustedResourceUrl));\n};\n\n\n/**\n * A pattern that recognizes a commonly useful subset of URLs that satisfy\n * the SafeUrl contract.\n *\n * This regular expression matches a subset of URLs that will not cause script\n * execution if used in URL context within a HTML document. Specifically, this\n * regular expression matches if (comment from here on and regex copied from\n * Soy's EscapingConventions):\n * (1) Either a protocol in a whitelist (http, https, mailto or ftp).\n * (2) or no protocol.  A protocol must be followed by a colon. The below\n *     allows that by allowing colons only after one of the characters [/?#].\n *     A colon after a hash (#) must be in the fragment.\n *     Otherwise, a colon after a (?) must be in a query.\n *     Otherwise, a colon after a single solidus (/) must be in a path.\n *     Otherwise, a colon after a double solidus (//) must be in the authority\n *     (before port).\n *\n * @private\n * @const {!RegExp}\n */\ngoog.html.SAFE_URL_PATTERN_ =\n    /^(?:(?:https?|mailto|ftp):|[^:/?#]*(?:[/?#]|$))/i;\n\n/**\n * Public version of goog.html.SAFE_URL_PATTERN_. Updating\n * goog.html.SAFE_URL_PATTERN_ doesn't seem to be backward compatible.\n * Namespace is also changed to goog.html.SafeUrl so it can be imported using\n * goog.require('goog.dom.SafeUrl').\n *\n * TODO(bangert): Remove SAFE_URL_PATTERN_\n * @const {!RegExp}\n */\ngoog.html.SafeUrl.SAFE_URL_PATTERN = goog.html.SAFE_URL_PATTERN_;\n\n/**\n * Attempts to create a SafeUrl object from `url`. The input string is validated\n * to match a pattern of commonly used safe URLs. If validation fails, `null` is\n * returned.\n *\n * `url` may be a URL with the `http:`, `https:`, `mailto:`, `ftp:` or `data`\n * scheme, or a relative URL (i.e., a URL without a scheme; specifically, a\n * scheme-relative, absolute-path-relative, or path-relative URL).\n *\n * @see http://url.spec.whatwg.org/#concept-relative-url\n * @param {string|!goog.string.TypedString} url The URL to validate.\n * @return {?goog.html.SafeUrl} The validated URL, wrapped as a SafeUrl, or null\n *     if validation fails.\n */\ngoog.html.SafeUrl.trySanitize = function(url) {\n  if (url instanceof goog.html.SafeUrl) {\n    return url;\n  }\n  if (typeof url == 'object' && url.implementsGoogStringTypedString) {\n    url = /** @type {!goog.string.TypedString} */ (url).getTypedStringValue();\n  } else {\n    // For defensive purposes, in case users cast around the parameter type.\n    url = String(url);\n  }\n  if (!goog.html.SAFE_URL_PATTERN_.test(url)) {\n    return goog.html.SafeUrl.tryFromDataUrl(url);\n  }\n  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);\n};\n\n/**\n * Creates a SafeUrl object from `url`. If `url` is a\n * `goog.html.SafeUrl` then it is simply returned. Otherwise the input string is\n * validated to match a pattern of commonly used safe URLs. If validation fails,\n * `goog.html.SafeUrl.INNOCUOUS_URL` is returned.\n *\n * `url` may be a URL with the `http:`, `https:`, `mailto:`, `ftp:` or `data`\n * scheme, or a relative URL (i.e., a URL without a scheme; specifically, a\n * scheme-relative, absolute-path-relative, or path-relative URL).\n *\n * @see http://url.spec.whatwg.org/#concept-relative-url\n * @param {string|!goog.string.TypedString} url The URL to validate.\n * @return {!goog.html.SafeUrl} The validated URL, wrapped as a SafeUrl.\n */\ngoog.html.SafeUrl.sanitize = function(url) {\n  return goog.html.SafeUrl.trySanitize(url) || goog.html.SafeUrl.INNOCUOUS_URL;\n};\n\n/**\n * Creates a SafeUrl object from `url`. If `url` is a\n * `goog.html.SafeUrl` then it is simply returned. Otherwise the input string is\n * validated to match a pattern of commonly used safe URLs.\n *\n * `url` may be a URL with the http, https, mailto or ftp scheme,\n * or a relative URL (i.e., a URL without a scheme; specifically, a\n * scheme-relative, absolute-path-relative, or path-relative URL).\n *\n * This function asserts (using goog.asserts) that the URL matches this pattern.\n * If it does not, in addition to failing the assert, an innocuous URL will be\n * returned.\n *\n * @see http://url.spec.whatwg.org/#concept-relative-url\n * @param {string|!goog.string.TypedString} url The URL to validate.\n * @param {boolean=} opt_allowDataUrl Whether to allow valid data: URLs.\n * @return {!goog.html.SafeUrl} The validated URL, wrapped as a SafeUrl.\n */\ngoog.html.SafeUrl.sanitizeAssertUnchanged = function(url, opt_allowDataUrl) {\n  if (url instanceof goog.html.SafeUrl) {\n    return url;\n  } else if (typeof url == 'object' && url.implementsGoogStringTypedString) {\n    url = /** @type {!goog.string.TypedString} */ (url).getTypedStringValue();\n  } else {\n    url = String(url);\n  }\n  if (opt_allowDataUrl && /^data:/i.test(url)) {\n    var safeUrl = goog.html.SafeUrl.fromDataUrl(url);\n    if (safeUrl.getTypedStringValue() == url) {\n      return safeUrl;\n    }\n  }\n  if (!goog.asserts.assert(\n          goog.html.SAFE_URL_PATTERN_.test(url),\n          '%s does not match the safe URL pattern', url)) {\n    url = goog.html.SafeUrl.INNOCUOUS_STRING;\n  }\n  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);\n};\n\n/**\n * Token used to ensure that object is created only from this file. No code\n * outside of this file can access this token.\n * @private {!Object}\n * @const\n */\ngoog.html.SafeUrl.CONSTRUCTOR_TOKEN_PRIVATE_ = {};\n\n/**\n * Package-internal utility method to create SafeUrl instances.\n *\n * @param {string} url The string to initialize the SafeUrl object with.\n * @return {!goog.html.SafeUrl} The initialized SafeUrl object.\n * @package\n */\ngoog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse = function(\n    url) {\n  return new goog.html.SafeUrl(\n      url, goog.html.SafeUrl.CONSTRUCTOR_TOKEN_PRIVATE_);\n};\n\n\n/**\n * `INNOCUOUS_STRING` wrapped in a `SafeUrl`.\n * @const {!goog.html.SafeUrl}\n */\ngoog.html.SafeUrl.INNOCUOUS_URL =\n    goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(\n        goog.html.SafeUrl.INNOCUOUS_STRING);\n\n\n/**\n * A SafeUrl corresponding to the special about:blank url.\n * @const {!goog.html.SafeUrl}\n */\ngoog.html.SafeUrl.ABOUT_BLANK =\n    goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(\n        'about:blank');\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n\n/**\n * @fileoverview The SafeHtml type and its builders.\n *\n * TODO(xtof): Link to document stating type contract.\n */\n\ngoog.provide('goog.html.SafeHtml');\n\ngoog.require('goog.array');\ngoog.require('goog.asserts');\ngoog.require('goog.dom.TagName');\ngoog.require('goog.dom.tags');\ngoog.require('goog.html.SafeScript');\ngoog.require('goog.html.SafeStyle');\ngoog.require('goog.html.SafeStyleSheet');\ngoog.require('goog.html.SafeUrl');\ngoog.require('goog.html.TrustedResourceUrl');\ngoog.require('goog.html.trustedtypes');\ngoog.require('goog.i18n.bidi.Dir');\ngoog.require('goog.i18n.bidi.DirectionalString');\ngoog.require('goog.labs.userAgent.browser');\ngoog.require('goog.object');\ngoog.require('goog.string.Const');\ngoog.require('goog.string.TypedString');\ngoog.require('goog.string.internal');\n\n\n\n/**\n * A string that is safe to use in HTML context in DOM APIs and HTML documents.\n *\n * A SafeHtml is a string-like object that carries the security type contract\n * that its value as a string will not cause untrusted script execution when\n * evaluated as HTML in a browser.\n *\n * Values of this type are guaranteed to be safe to use in HTML contexts,\n * such as, assignment to the innerHTML DOM property, or interpolation into\n * a HTML template in HTML PC_DATA context, in the sense that the use will not\n * result in a Cross-Site-Scripting vulnerability.\n *\n * Instances of this type must be created via the factory methods\n * (`goog.html.SafeHtml.create`, `goog.html.SafeHtml.htmlEscape`),\n * etc and not by invoking its constructor. The constructor intentionally takes\n * an extra parameter that cannot be constructed outside of this file and the\n * type is immutable; hence only a default instance corresponding to the empty\n * string can be obtained via constructor invocation.\n *\n * Creating SafeHtml objects HAS SIDE-EFFECTS due to calling Trusted Types Web\n * API.\n *\n * Note that there is no `goog.html.SafeHtml.fromConstant`. The reason is that\n * the following code would create an unsafe HTML:\n *\n * ```\n * goog.html.SafeHtml.concat(\n *     goog.html.SafeHtml.fromConstant(goog.string.Const.from('<script>')),\n *     goog.html.SafeHtml.htmlEscape(userInput),\n *     goog.html.SafeHtml.fromConstant(goog.string.Const.from('<\\/script>')));\n * ```\n *\n * There's `goog.dom.constHtmlToNode` to create a node from constant strings\n * only.\n *\n * @see goog.html.SafeHtml.create\n * @see goog.html.SafeHtml.htmlEscape\n * @final\n * @struct\n * @implements {goog.i18n.bidi.DirectionalString}\n * @implements {goog.string.TypedString}\n */\ngoog.html.SafeHtml = class {\n  /**\n   * @param {!TrustedHTML|string} value\n   * @param {?goog.i18n.bidi.Dir} dir\n   * @param {!Object} token package-internal implementation detail.\n   */\n  constructor(value, dir, token) {\n    /**\n     * The contained value of this SafeHtml.  The field has a purposely ugly\n     * name to make (non-compiled) code that attempts to directly access this\n     * field stand out.\n     * @private {!TrustedHTML|string}\n     */\n    this.privateDoNotAccessOrElseSafeHtmlWrappedValue_ =\n        (token === goog.html.SafeHtml.CONSTRUCTOR_TOKEN_PRIVATE_) ? value : '';\n\n    /**\n     * This SafeHtml's directionality, or null if unknown.\n     * @private {?goog.i18n.bidi.Dir}\n     */\n    this.dir_ = dir;\n  }\n};\n\n\n/**\n * @define {boolean} Whether to strip out error messages or to leave them in.\n */\ngoog.html.SafeHtml.ENABLE_ERROR_MESSAGES =\n    goog.define('goog.html.SafeHtml.ENABLE_ERROR_MESSAGES', goog.DEBUG);\n\n\n/**\n * Whether the `style` attribute is supported. Set to false to avoid the byte\n * weight of `goog.html.SafeStyle` where unneeded. An error will be thrown if\n * the `style` attribute is used.\n * @define {boolean}\n */\ngoog.html.SafeHtml.SUPPORT_STYLE_ATTRIBUTE =\n    goog.define('goog.html.SafeHtml.SUPPORT_STYLE_ATTRIBUTE', true);\n\n\n/**\n * @override\n * @const\n */\ngoog.html.SafeHtml.prototype.implementsGoogI18nBidiDirectionalString = true;\n\n\n/** @override */\ngoog.html.SafeHtml.prototype.getDirection = function() {\n  return this.dir_;\n};\n\n\n/**\n * @override\n * @const\n */\ngoog.html.SafeHtml.prototype.implementsGoogStringTypedString = true;\n\n\n/**\n * Returns this SafeHtml's value as string.\n *\n * IMPORTANT: In code where it is security relevant that an object's type is\n * indeed `SafeHtml`, use `goog.html.SafeHtml.unwrap` instead of\n * this method. If in doubt, assume that it's security relevant. In particular,\n * note that goog.html functions which return a goog.html type do not guarantee\n * that the returned instance is of the right type. For example:\n *\n * <pre>\n * var fakeSafeHtml = new String('fake');\n * fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;\n * var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);\n * // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by\n * // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml\n * // instanceof goog.html.SafeHtml.\n * </pre>\n *\n * @see goog.html.SafeHtml.unwrap\n * @override\n */\ngoog.html.SafeHtml.prototype.getTypedStringValue = function() {\n  return this.privateDoNotAccessOrElseSafeHtmlWrappedValue_.toString();\n};\n\n\nif (goog.DEBUG) {\n  /**\n   * Returns a debug string-representation of this value.\n   *\n   * To obtain the actual string value wrapped in a SafeHtml, use\n   * `goog.html.SafeHtml.unwrap`.\n   *\n   * @see goog.html.SafeHtml.unwrap\n   * @override\n   */\n  goog.html.SafeHtml.prototype.toString = function() {\n    return 'SafeHtml{' + this.privateDoNotAccessOrElseSafeHtmlWrappedValue_ +\n        '}';\n  };\n}\n\n\n/**\n * Performs a runtime check that the provided object is indeed a SafeHtml\n * object, and returns its value.\n * @param {!goog.html.SafeHtml} safeHtml The object to extract from.\n * @return {string} The SafeHtml object's contained string, unless the run-time\n *     type check fails. In that case, `unwrap` returns an innocuous\n *     string, or, if assertions are enabled, throws\n *     `goog.asserts.AssertionError`.\n */\ngoog.html.SafeHtml.unwrap = function(safeHtml) {\n  return goog.html.SafeHtml.unwrapTrustedHTML(safeHtml).toString();\n};\n\n\n/**\n * Unwraps value as TrustedHTML if supported or as a string if not.\n * @param {!goog.html.SafeHtml} safeHtml\n * @return {!TrustedHTML|string}\n * @see goog.html.SafeHtml.unwrap\n */\ngoog.html.SafeHtml.unwrapTrustedHTML = function(safeHtml) {\n  // Perform additional run-time type-checking to ensure that safeHtml is indeed\n  // an instance of the expected type.  This provides some additional protection\n  // against security bugs due to application code that disables type checks.\n  // Specifically, the following checks are performed:\n  // 1. The object is an instance of the expected type.\n  // 2. The object is not an instance of a subclass.\n  if (safeHtml instanceof goog.html.SafeHtml &&\n      safeHtml.constructor === goog.html.SafeHtml) {\n    return safeHtml.privateDoNotAccessOrElseSafeHtmlWrappedValue_;\n  } else {\n    goog.asserts.fail(\n        'expected object of type SafeHtml, got \\'' + safeHtml + '\\' of type ' +\n        goog.typeOf(safeHtml));\n    return 'type_error:SafeHtml';\n  }\n};\n\n\n/**\n * Shorthand for union of types that can sensibly be converted to strings\n * or might already be SafeHtml (as SafeHtml is a goog.string.TypedString).\n * @private\n * @typedef {string|number|boolean|!goog.string.TypedString|\n *           !goog.i18n.bidi.DirectionalString}\n */\ngoog.html.SafeHtml.TextOrHtml_;\n\n\n/**\n * Returns HTML-escaped text as a SafeHtml object.\n *\n * If text is of a type that implements\n * `goog.i18n.bidi.DirectionalString`, the directionality of the new\n * `SafeHtml` object is set to `text`'s directionality, if known.\n * Otherwise, the directionality of the resulting SafeHtml is unknown (i.e.,\n * `null`).\n *\n * @param {!goog.html.SafeHtml.TextOrHtml_} textOrHtml The text to escape. If\n *     the parameter is of type SafeHtml it is returned directly (no escaping\n *     is done).\n * @return {!goog.html.SafeHtml} The escaped text, wrapped as a SafeHtml.\n */\ngoog.html.SafeHtml.htmlEscape = function(textOrHtml) {\n  if (textOrHtml instanceof goog.html.SafeHtml) {\n    return textOrHtml;\n  }\n  var textIsObject = typeof textOrHtml == 'object';\n  var dir = null;\n  if (textIsObject && textOrHtml.implementsGoogI18nBidiDirectionalString) {\n    dir = /** @type {!goog.i18n.bidi.DirectionalString} */ (textOrHtml)\n              .getDirection();\n  }\n  var textAsString;\n  if (textIsObject && textOrHtml.implementsGoogStringTypedString) {\n    textAsString = /** @type {!goog.string.TypedString} */ (textOrHtml)\n                       .getTypedStringValue();\n  } else {\n    textAsString = String(textOrHtml);\n  }\n  return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(\n      goog.string.internal.htmlEscape(textAsString), dir);\n};\n\n\n/**\n * Returns HTML-escaped text as a SafeHtml object, with newlines changed to\n * &lt;br&gt;.\n * @param {!goog.html.SafeHtml.TextOrHtml_} textOrHtml The text to escape. If\n *     the parameter is of type SafeHtml it is returned directly (no escaping\n *     is done).\n * @return {!goog.html.SafeHtml} The escaped text, wrapped as a SafeHtml.\n */\ngoog.html.SafeHtml.htmlEscapePreservingNewlines = function(textOrHtml) {\n  if (textOrHtml instanceof goog.html.SafeHtml) {\n    return textOrHtml;\n  }\n  var html = goog.html.SafeHtml.htmlEscape(textOrHtml);\n  return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(\n      goog.string.internal.newLineToBr(goog.html.SafeHtml.unwrap(html)),\n      html.getDirection());\n};\n\n\n/**\n * Returns HTML-escaped text as a SafeHtml object, with newlines changed to\n * &lt;br&gt; and escaping whitespace to preserve spatial formatting. Character\n * entity #160 is used to make it safer for XML.\n * @param {!goog.html.SafeHtml.TextOrHtml_} textOrHtml The text to escape. If\n *     the parameter is of type SafeHtml it is returned directly (no escaping\n *     is done).\n * @return {!goog.html.SafeHtml} The escaped text, wrapped as a SafeHtml.\n */\ngoog.html.SafeHtml.htmlEscapePreservingNewlinesAndSpaces = function(\n    textOrHtml) {\n  if (textOrHtml instanceof goog.html.SafeHtml) {\n    return textOrHtml;\n  }\n  var html = goog.html.SafeHtml.htmlEscape(textOrHtml);\n  return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(\n      goog.string.internal.whitespaceEscape(goog.html.SafeHtml.unwrap(html)),\n      html.getDirection());\n};\n\n\n/**\n * Coerces an arbitrary object into a SafeHtml object.\n *\n * If `textOrHtml` is already of type `goog.html.SafeHtml`, the same\n * object is returned. Otherwise, `textOrHtml` is coerced to string, and\n * HTML-escaped. If `textOrHtml` is of a type that implements\n * `goog.i18n.bidi.DirectionalString`, its directionality, if known, is\n * preserved.\n *\n * @param {!goog.html.SafeHtml.TextOrHtml_} textOrHtml The text or SafeHtml to\n *     coerce.\n * @return {!goog.html.SafeHtml} The resulting SafeHtml object.\n * @deprecated Use goog.html.SafeHtml.htmlEscape.\n */\ngoog.html.SafeHtml.from = goog.html.SafeHtml.htmlEscape;\n\n\n/**\n * Converts an arbitrary string into an HTML comment by HTML-escaping the\n * contents and embedding the result between HTML comment markers.\n *\n * Escaping is needed because Internet Explorer supports conditional comments\n * and so may render HTML markup within comments.\n *\n * @param {string} text\n * @return {!goog.html.SafeHtml}\n */\ngoog.html.SafeHtml.comment = function(text) {\n  return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(\n      '<!--' + goog.string.internal.htmlEscape(text) + '-->', null);\n};\n\n\n/**\n * @const\n * @private\n */\ngoog.html.SafeHtml.VALID_NAMES_IN_TAG_ = /^[a-zA-Z0-9-]+$/;\n\n\n/**\n * Set of attributes containing URL as defined at\n * http://www.w3.org/TR/html5/index.html#attributes-1.\n * @private @const {!Object<string,boolean>}\n */\ngoog.html.SafeHtml.URL_ATTRIBUTES_ = goog.object.createSet(\n    'action', 'cite', 'data', 'formaction', 'href', 'manifest', 'poster',\n    'src');\n\n\n/**\n * Tags which are unsupported via create(). They might be supported via a\n * tag-specific create method. These are tags which might require a\n * TrustedResourceUrl in one of their attributes or a restricted type for\n * their content.\n * @private @const {!Object<string,boolean>}\n */\ngoog.html.SafeHtml.NOT_ALLOWED_TAG_NAMES_ = goog.object.createSet(\n    goog.dom.TagName.APPLET, goog.dom.TagName.BASE, goog.dom.TagName.EMBED,\n    goog.dom.TagName.IFRAME, goog.dom.TagName.LINK, goog.dom.TagName.MATH,\n    goog.dom.TagName.META, goog.dom.TagName.OBJECT, goog.dom.TagName.SCRIPT,\n    goog.dom.TagName.STYLE, goog.dom.TagName.SVG, goog.dom.TagName.TEMPLATE);\n\n\n/**\n * @typedef {string|number|!goog.string.TypedString|\n *     !goog.html.SafeStyle.PropertyMap|undefined|null}\n */\ngoog.html.SafeHtml.AttributeValue;\n\n\n/**\n * Creates a SafeHtml content consisting of a tag with optional attributes and\n * optional content.\n *\n * For convenience tag names and attribute names are accepted as regular\n * strings, instead of goog.string.Const. Nevertheless, you should not pass\n * user-controlled values to these parameters. Note that these parameters are\n * syntactically validated at runtime, and invalid values will result in\n * an exception.\n *\n * Example usage:\n *\n * goog.html.SafeHtml.create('br');\n * goog.html.SafeHtml.create('div', {'class': 'a'});\n * goog.html.SafeHtml.create('p', {}, 'a');\n * goog.html.SafeHtml.create('p', {}, goog.html.SafeHtml.create('br'));\n *\n * goog.html.SafeHtml.create('span', {\n *   'style': {'margin': '0'}\n * });\n *\n * To guarantee SafeHtml's type contract is upheld there are restrictions on\n * attribute values and tag names.\n *\n * - For attributes which contain script code (on*), a goog.string.Const is\n *   required.\n * - For attributes which contain style (style), a goog.html.SafeStyle or a\n *   goog.html.SafeStyle.PropertyMap is required.\n * - For attributes which are interpreted as URLs (e.g. src, href) a\n *   goog.html.SafeUrl, goog.string.Const or string is required. If a string\n *   is passed, it will be sanitized with SafeUrl.sanitize().\n * - For tags which can load code or set security relevant page metadata,\n *   more specific goog.html.SafeHtml.create*() functions must be used. Tags\n *   which are not supported by this function are applet, base, embed, iframe,\n *   link, math, object, script, style, svg, and template.\n *\n * @param {!goog.dom.TagName|string} tagName The name of the tag. Only tag names\n *     consisting of [a-zA-Z0-9-] are allowed. Tag names documented above are\n *     disallowed.\n * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes\n *     Mapping from attribute names to their values. Only attribute names\n *     consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined causes\n *     the attribute to be omitted.\n * @param {!goog.html.SafeHtml.TextOrHtml_|\n *     !Array<!goog.html.SafeHtml.TextOrHtml_>=} opt_content Content to\n *     HTML-escape and put inside the tag. This must be empty for void tags\n *     like <br>. Array elements are concatenated.\n * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.\n * @throws {!Error} If invalid tag name, attribute name, or attribute value is\n *     provided.\n * @throws {!goog.asserts.AssertionError} If content for void tag is provided.\n */\ngoog.html.SafeHtml.create = function(tagName, opt_attributes, opt_content) {\n  goog.html.SafeHtml.verifyTagName(String(tagName));\n  return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(\n      String(tagName), opt_attributes, opt_content);\n};\n\n\n/**\n * Verifies if the tag name is valid and if it doesn't change the context.\n * E.g. STRONG is fine but SCRIPT throws because it changes context. See\n * goog.html.SafeHtml.create for an explanation of allowed tags.\n * @param {string} tagName\n * @throws {!Error} If invalid tag name is provided.\n * @package\n */\ngoog.html.SafeHtml.verifyTagName = function(tagName) {\n  if (!goog.html.SafeHtml.VALID_NAMES_IN_TAG_.test(tagName)) {\n    throw new Error(\n        goog.html.SafeHtml.ENABLE_ERROR_MESSAGES ?\n            'Invalid tag name <' + tagName + '>.' :\n            '');\n  }\n  if (tagName.toUpperCase() in goog.html.SafeHtml.NOT_ALLOWED_TAG_NAMES_) {\n    throw new Error(\n        goog.html.SafeHtml.ENABLE_ERROR_MESSAGES ?\n\n            'Tag name <' + tagName + '> is not allowed for SafeHtml.' :\n            '');\n  }\n};\n\n\n/**\n * Creates a SafeHtml representing an iframe tag.\n *\n * This by default restricts the iframe as much as possible by setting the\n * sandbox attribute to the empty string. If the iframe requires less\n * restrictions, set the sandbox attribute as tight as possible, but do not rely\n * on the sandbox as a security feature because it is not supported by older\n * browsers. If a sandbox is essential to security (e.g. for third-party\n * frames), use createSandboxIframe which checks for browser support.\n *\n * @see https://developer.mozilla.org/en/docs/Web/HTML/Element/iframe#attr-sandbox\n *\n * @param {?goog.html.TrustedResourceUrl=} opt_src The value of the src\n *     attribute. If null or undefined src will not be set.\n * @param {?goog.html.SafeHtml=} opt_srcdoc The value of the srcdoc attribute.\n *     If null or undefined srcdoc will not be set.\n * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes\n *     Mapping from attribute names to their values. Only attribute names\n *     consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined causes\n *     the attribute to be omitted.\n * @param {!goog.html.SafeHtml.TextOrHtml_|\n *     !Array<!goog.html.SafeHtml.TextOrHtml_>=} opt_content Content to\n *     HTML-escape and put inside the tag. Array elements are concatenated.\n * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.\n * @throws {!Error} If invalid tag name, attribute name, or attribute value is\n *     provided. If opt_attributes contains the src or srcdoc attributes.\n */\ngoog.html.SafeHtml.createIframe = function(\n    opt_src, opt_srcdoc, opt_attributes, opt_content) {\n  if (opt_src) {\n    // Check whether this is really TrustedResourceUrl.\n    goog.html.TrustedResourceUrl.unwrap(opt_src);\n  }\n\n  var fixedAttributes = {};\n  fixedAttributes['src'] = opt_src || null;\n  fixedAttributes['srcdoc'] =\n      opt_srcdoc && goog.html.SafeHtml.unwrap(opt_srcdoc);\n  var defaultAttributes = {'sandbox': ''};\n  var attributes = goog.html.SafeHtml.combineAttributes(\n      fixedAttributes, defaultAttributes, opt_attributes);\n  return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(\n      'iframe', attributes, opt_content);\n};\n\n\n/**\n * Creates a SafeHtml representing a sandboxed iframe tag.\n *\n * The sandbox attribute is enforced in its most restrictive mode, an empty\n * string. Consequently, the security requirements for the src and srcdoc\n * attributes are relaxed compared to SafeHtml.createIframe. This function\n * will throw on browsers that do not support the sandbox attribute, as\n * determined by SafeHtml.canUseSandboxIframe.\n *\n * The SafeHtml returned by this function can trigger downloads with no\n * user interaction on Chrome (though only a few, further attempts are blocked).\n * Firefox and IE will block all downloads from the sandbox.\n *\n * @see https://developer.mozilla.org/en/docs/Web/HTML/Element/iframe#attr-sandbox\n * @see https://lists.w3.org/Archives/Public/public-whatwg-archive/2013Feb/0112.html\n *\n * @param {string|!goog.html.SafeUrl=} opt_src The value of the src\n *     attribute. If null or undefined src will not be set.\n * @param {string=} opt_srcdoc The value of the srcdoc attribute.\n *     If null or undefined srcdoc will not be set. Will not be sanitized.\n * @param {!Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes\n *     Mapping from attribute names to their values. Only attribute names\n *     consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined causes\n *     the attribute to be omitted.\n * @param {!goog.html.SafeHtml.TextOrHtml_|\n *     !Array<!goog.html.SafeHtml.TextOrHtml_>=} opt_content Content to\n *     HTML-escape and put inside the tag. Array elements are concatenated.\n * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.\n * @throws {!Error} If invalid tag name, attribute name, or attribute value is\n *     provided. If opt_attributes contains the src, srcdoc or sandbox\n *     attributes. If browser does not support the sandbox attribute on iframe.\n */\ngoog.html.SafeHtml.createSandboxIframe = function(\n    opt_src, opt_srcdoc, opt_attributes, opt_content) {\n  if (!goog.html.SafeHtml.canUseSandboxIframe()) {\n    throw new Error(\n        goog.html.SafeHtml.ENABLE_ERROR_MESSAGES ?\n            'The browser does not support sandboxed iframes.' :\n            '');\n  }\n\n  var fixedAttributes = {};\n  if (opt_src) {\n    // Note that sanitize is a no-op on SafeUrl.\n    fixedAttributes['src'] =\n        goog.html.SafeUrl.unwrap(goog.html.SafeUrl.sanitize(opt_src));\n  } else {\n    fixedAttributes['src'] = null;\n  }\n  fixedAttributes['srcdoc'] = opt_srcdoc || null;\n  fixedAttributes['sandbox'] = '';\n  var attributes =\n      goog.html.SafeHtml.combineAttributes(fixedAttributes, {}, opt_attributes);\n  return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(\n      'iframe', attributes, opt_content);\n};\n\n\n/**\n * Checks if the user agent supports sandboxed iframes.\n * @return {boolean}\n */\ngoog.html.SafeHtml.canUseSandboxIframe = function() {\n  return goog.global['HTMLIFrameElement'] &&\n      ('sandbox' in goog.global['HTMLIFrameElement'].prototype);\n};\n\n\n/**\n * Creates a SafeHtml representing a script tag with the src attribute.\n * @param {!goog.html.TrustedResourceUrl} src The value of the src\n * attribute.\n * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=}\n * opt_attributes\n *     Mapping from attribute names to their values. Only attribute names\n *     consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined\n *     causes the attribute to be omitted.\n * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.\n * @throws {!Error} If invalid attribute name or value is provided. If\n *     opt_attributes contains the src attribute.\n */\ngoog.html.SafeHtml.createScriptSrc = function(src, opt_attributes) {\n  // TODO(mlourenco): The charset attribute should probably be blocked. If\n  // its value is attacker controlled, the script contains attacker controlled\n  // sub-strings (even if properly escaped) and the server does not set charset\n  // then XSS is likely possible.\n  // https://html.spec.whatwg.org/multipage/scripting.html#dom-script-charset\n\n  // Check whether this is really TrustedResourceUrl.\n  goog.html.TrustedResourceUrl.unwrap(src);\n\n  var fixedAttributes = {'src': src};\n  var defaultAttributes = {};\n  var attributes = goog.html.SafeHtml.combineAttributes(\n      fixedAttributes, defaultAttributes, opt_attributes);\n  return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(\n      'script', attributes);\n};\n\n\n/**\n * Creates a SafeHtml representing a script tag. Does not allow the language,\n * src, text or type attributes to be set.\n * @param {!goog.html.SafeScript|!Array<!goog.html.SafeScript>}\n *     script Content to put inside the tag. Array elements are\n *     concatenated.\n * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes\n *     Mapping from attribute names to their values. Only attribute names\n *     consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined causes\n *     the attribute to be omitted.\n * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.\n * @throws {!Error} If invalid attribute name or attribute value is provided. If\n *     opt_attributes contains the language, src, text or type attribute.\n */\ngoog.html.SafeHtml.createScript = function(script, opt_attributes) {\n  for (var attr in opt_attributes) {\n    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty#Using_hasOwnProperty_as_a_property_name\n    if (Object.prototype.hasOwnProperty.call(opt_attributes, attr)) {\n      var attrLower = attr.toLowerCase();\n      if (attrLower == 'language' || attrLower == 'src' ||\n          attrLower == 'text' || attrLower == 'type') {\n        throw new Error(\n            goog.html.SafeHtml.ENABLE_ERROR_MESSAGES ?\n                'Cannot set \"' + attrLower + '\" attribute' :\n                '');\n      }\n    }\n  }\n\n  var content = '';\n  script = goog.array.concat(script);\n  for (var i = 0; i < script.length; i++) {\n    content += goog.html.SafeScript.unwrap(script[i]);\n  }\n  // Convert to SafeHtml so that it's not HTML-escaped. This is safe because\n  // as part of its contract, SafeScript should have no dangerous '<'.\n  var htmlContent =\n      goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(\n          content, goog.i18n.bidi.Dir.NEUTRAL);\n  return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(\n      'script', opt_attributes, htmlContent);\n};\n\n\n/**\n * Creates a SafeHtml representing a style tag. The type attribute is set\n * to \"text/css\".\n * @param {!goog.html.SafeStyleSheet|!Array<!goog.html.SafeStyleSheet>}\n *     styleSheet Content to put inside the tag. Array elements are\n *     concatenated.\n * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes\n *     Mapping from attribute names to their values. Only attribute names\n *     consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined causes\n *     the attribute to be omitted.\n * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.\n * @throws {!Error} If invalid attribute name or attribute value is provided. If\n *     opt_attributes contains the type attribute.\n */\ngoog.html.SafeHtml.createStyle = function(styleSheet, opt_attributes) {\n  var fixedAttributes = {'type': 'text/css'};\n  var defaultAttributes = {};\n  var attributes = goog.html.SafeHtml.combineAttributes(\n      fixedAttributes, defaultAttributes, opt_attributes);\n\n  var content = '';\n  styleSheet = goog.array.concat(styleSheet);\n  for (var i = 0; i < styleSheet.length; i++) {\n    content += goog.html.SafeStyleSheet.unwrap(styleSheet[i]);\n  }\n  // Convert to SafeHtml so that it's not HTML-escaped. This is safe because\n  // as part of its contract, SafeStyleSheet should have no dangerous '<'.\n  var htmlContent =\n      goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(\n          content, goog.i18n.bidi.Dir.NEUTRAL);\n  return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(\n      'style', attributes, htmlContent);\n};\n\n\n/**\n * Creates a SafeHtml representing a meta refresh tag.\n * @param {!goog.html.SafeUrl|string} url Where to redirect. If a string is\n *     passed, it will be sanitized with SafeUrl.sanitize().\n * @param {number=} opt_secs Number of seconds until the page should be\n *     reloaded. Will be set to 0 if unspecified.\n * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.\n */\ngoog.html.SafeHtml.createMetaRefresh = function(url, opt_secs) {\n  // Note that sanitize is a no-op on SafeUrl.\n  var unwrappedUrl = goog.html.SafeUrl.unwrap(goog.html.SafeUrl.sanitize(url));\n\n  if (goog.labs.userAgent.browser.isIE() ||\n      goog.labs.userAgent.browser.isEdge()) {\n    // IE/EDGE can't parse the content attribute if the url contains a\n    // semicolon. We can fix this by adding quotes around the url, but then we\n    // can't parse quotes in the URL correctly. Also, it seems that IE/EDGE\n    // did not unescape semicolons in these URLs at some point in the past. We\n    // take a best-effort approach.\n    //\n    // If the URL has semicolons (which may happen in some cases, see\n    // http://www.w3.org/TR/1999/REC-html401-19991224/appendix/notes.html#h-B.2\n    // for instance), wrap it in single quotes to protect the semicolons.\n    // If the URL has semicolons and single quotes, url-encode the single quotes\n    // as well.\n    //\n    // This is imperfect. Notice that both ' and ; are reserved characters in\n    // URIs, so this could do the wrong thing, but at least it will do the wrong\n    // thing in only rare cases.\n    if (goog.string.internal.contains(unwrappedUrl, ';')) {\n      unwrappedUrl = '\\'' + unwrappedUrl.replace(/'/g, '%27') + '\\'';\n    }\n  }\n  var attributes = {\n    'http-equiv': 'refresh',\n    'content': (opt_secs || 0) + '; url=' + unwrappedUrl\n  };\n\n  // This function will handle the HTML escaping for attributes.\n  return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(\n      'meta', attributes);\n};\n\n\n/**\n * @param {string} tagName The tag name.\n * @param {string} name The attribute name.\n * @param {!goog.html.SafeHtml.AttributeValue} value The attribute value.\n * @return {string} A \"name=value\" string.\n * @throws {!Error} If attribute value is unsafe for the given tag and\n *     attribute.\n * @private\n */\ngoog.html.SafeHtml.getAttrNameAndValue_ = function(tagName, name, value) {\n  // If it's goog.string.Const, allow any valid attribute name.\n  if (value instanceof goog.string.Const) {\n    value = goog.string.Const.unwrap(value);\n  } else if (name.toLowerCase() == 'style') {\n    if (goog.html.SafeHtml.SUPPORT_STYLE_ATTRIBUTE) {\n      value = goog.html.SafeHtml.getStyleValue_(value);\n    } else {\n      throw new Error(\n          goog.html.SafeHtml.ENABLE_ERROR_MESSAGES ?\n              'Attribute \"style\" not supported.' :\n              '');\n    }\n  } else if (/^on/i.test(name)) {\n    // TODO(jakubvrana): Disallow more attributes with a special meaning.\n    throw new Error(\n        goog.html.SafeHtml.ENABLE_ERROR_MESSAGES ? 'Attribute \"' + name +\n                '\" requires goog.string.Const value, \"' + value + '\" given.' :\n                                                   '');\n    // URL attributes handled differently according to tag.\n  } else if (name.toLowerCase() in goog.html.SafeHtml.URL_ATTRIBUTES_) {\n    if (value instanceof goog.html.TrustedResourceUrl) {\n      value = goog.html.TrustedResourceUrl.unwrap(value);\n    } else if (value instanceof goog.html.SafeUrl) {\n      value = goog.html.SafeUrl.unwrap(value);\n    } else if (typeof value === 'string') {\n      value = goog.html.SafeUrl.sanitize(value).getTypedStringValue();\n    } else {\n      throw new Error(\n          goog.html.SafeHtml.ENABLE_ERROR_MESSAGES ?\n              'Attribute \"' + name + '\" on tag \"' + tagName +\n                  '\" requires goog.html.SafeUrl, goog.string.Const, or' +\n                  ' string, value \"' + value + '\" given.' :\n              '');\n    }\n  }\n\n  // Accept SafeUrl, TrustedResourceUrl, etc. for attributes which only require\n  // HTML-escaping.\n  if (value.implementsGoogStringTypedString) {\n    // Ok to call getTypedStringValue() since there's no reliance on the type\n    // contract for security here.\n    value =\n        /** @type {!goog.string.TypedString} */ (value).getTypedStringValue();\n  }\n\n  goog.asserts.assert(\n      typeof value === 'string' || typeof value === 'number',\n      'String or number value expected, got ' + (typeof value) +\n          ' with value: ' + value);\n  return name + '=\"' + goog.string.internal.htmlEscape(String(value)) + '\"';\n};\n\n\n/**\n * Gets value allowed in \"style\" attribute.\n * @param {!goog.html.SafeHtml.AttributeValue} value It could be SafeStyle or a\n *     map which will be passed to goog.html.SafeStyle.create.\n * @return {string} Unwrapped value.\n * @throws {!Error} If string value is given.\n * @private\n */\ngoog.html.SafeHtml.getStyleValue_ = function(value) {\n  if (!goog.isObject(value)) {\n    throw new Error(\n        goog.html.SafeHtml.ENABLE_ERROR_MESSAGES ?\n            'The \"style\" attribute requires goog.html.SafeStyle or map ' +\n                'of style properties, ' + (typeof value) + ' given: ' + value :\n            '');\n  }\n  if (!(value instanceof goog.html.SafeStyle)) {\n    // Process the property bag into a style object.\n    value = goog.html.SafeStyle.create(value);\n  }\n  return goog.html.SafeStyle.unwrap(value);\n};\n\n\n/**\n * Creates a SafeHtml content with known directionality consisting of a tag with\n * optional attributes and optional content.\n * @param {!goog.i18n.bidi.Dir} dir Directionality.\n * @param {string} tagName\n * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes\n * @param {!goog.html.SafeHtml.TextOrHtml_|\n *     !Array<!goog.html.SafeHtml.TextOrHtml_>=} opt_content\n * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.\n */\ngoog.html.SafeHtml.createWithDir = function(\n    dir, tagName, opt_attributes, opt_content) {\n  var html = goog.html.SafeHtml.create(tagName, opt_attributes, opt_content);\n  html.dir_ = dir;\n  return html;\n};\n\n\n/**\n * Creates a new SafeHtml object by joining the parts with separator.\n * @param {!goog.html.SafeHtml.TextOrHtml_} separator\n * @param {!Array<!goog.html.SafeHtml.TextOrHtml_|\n *     !Array<!goog.html.SafeHtml.TextOrHtml_>>} parts Parts to join. If a part\n *     contains an array then each member of this array is also joined with the\n *     separator.\n * @return {!goog.html.SafeHtml}\n */\ngoog.html.SafeHtml.join = function(separator, parts) {\n  var separatorHtml = goog.html.SafeHtml.htmlEscape(separator);\n  var dir = separatorHtml.getDirection();\n  var content = [];\n\n  /**\n   * @param {!goog.html.SafeHtml.TextOrHtml_|\n   *     !Array<!goog.html.SafeHtml.TextOrHtml_>} argument\n   */\n  var addArgument = function(argument) {\n    if (Array.isArray(argument)) {\n      goog.array.forEach(argument, addArgument);\n    } else {\n      var html = goog.html.SafeHtml.htmlEscape(argument);\n      content.push(goog.html.SafeHtml.unwrap(html));\n      var htmlDir = html.getDirection();\n      if (dir == goog.i18n.bidi.Dir.NEUTRAL) {\n        dir = htmlDir;\n      } else if (htmlDir != goog.i18n.bidi.Dir.NEUTRAL && dir != htmlDir) {\n        dir = null;\n      }\n    }\n  };\n\n  goog.array.forEach(parts, addArgument);\n  return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(\n      content.join(goog.html.SafeHtml.unwrap(separatorHtml)), dir);\n};\n\n\n/**\n * Creates a new SafeHtml object by concatenating values.\n * @param {...(!goog.html.SafeHtml.TextOrHtml_|\n *     !Array<!goog.html.SafeHtml.TextOrHtml_>)} var_args Values to concatenate.\n * @return {!goog.html.SafeHtml}\n */\ngoog.html.SafeHtml.concat = function(var_args) {\n  return goog.html.SafeHtml.join(\n      goog.html.SafeHtml.EMPTY, Array.prototype.slice.call(arguments));\n};\n\n\n/**\n * Creates a new SafeHtml object with known directionality by concatenating the\n * values.\n * @param {!goog.i18n.bidi.Dir} dir Directionality.\n * @param {...(!goog.html.SafeHtml.TextOrHtml_|\n *     !Array<!goog.html.SafeHtml.TextOrHtml_>)} var_args Elements of array\n *     arguments would be processed recursively.\n * @return {!goog.html.SafeHtml}\n */\ngoog.html.SafeHtml.concatWithDir = function(dir, var_args) {\n  var html = goog.html.SafeHtml.concat(goog.array.slice(arguments, 1));\n  html.dir_ = dir;\n  return html;\n};\n\n\n/**\n * Token used to ensure that object is created only from this file. No code\n * outside of this file can access this token.\n * @private {!Object}\n * @const\n */\ngoog.html.SafeHtml.CONSTRUCTOR_TOKEN_PRIVATE_ = {};\n\n\n/**\n * Package-internal utility method to create SafeHtml instances.\n *\n * @param {string} html The string to initialize the SafeHtml object with.\n * @param {?goog.i18n.bidi.Dir} dir The directionality of the SafeHtml to be\n *     constructed, or null if unknown.\n * @return {!goog.html.SafeHtml} The initialized SafeHtml object.\n * @package\n */\ngoog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse = function(\n    html, dir) {\n  const policy = goog.html.trustedtypes.getPolicyPrivateDoNotAccessOrElse();\n  const trustedHtml = policy ? policy.createHTML(html) : html;\n  return new goog.html.SafeHtml(\n      trustedHtml, dir, goog.html.SafeHtml.CONSTRUCTOR_TOKEN_PRIVATE_);\n};\n\n\n/**\n * Like create() but does not restrict which tags can be constructed.\n *\n * @param {string} tagName Tag name. Set or validated by caller.\n * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes\n * @param {(!goog.html.SafeHtml.TextOrHtml_|\n *     !Array<!goog.html.SafeHtml.TextOrHtml_>)=} opt_content\n * @return {!goog.html.SafeHtml}\n * @throws {!Error} If invalid or unsafe attribute name or value is provided.\n * @throws {!goog.asserts.AssertionError} If content for void tag is provided.\n * @package\n */\ngoog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse = function(\n    tagName, opt_attributes, opt_content) {\n  var dir = null;\n  var result = '<' + tagName;\n  result += goog.html.SafeHtml.stringifyAttributes(tagName, opt_attributes);\n\n  var content = opt_content;\n  if (content == null) {\n    content = [];\n  } else if (!Array.isArray(content)) {\n    content = [content];\n  }\n\n  if (goog.dom.tags.isVoidTag(tagName.toLowerCase())) {\n    goog.asserts.assert(\n        !content.length, 'Void tag <' + tagName + '> does not allow content.');\n    result += '>';\n  } else {\n    var html = goog.html.SafeHtml.concat(content);\n    result += '>' + goog.html.SafeHtml.unwrap(html) + '</' + tagName + '>';\n    dir = html.getDirection();\n  }\n\n  var dirAttribute = opt_attributes && opt_attributes['dir'];\n  if (dirAttribute) {\n    if (/^(ltr|rtl|auto)$/i.test(dirAttribute)) {\n      // If the tag has the \"dir\" attribute specified then its direction is\n      // neutral because it can be safely used in any context.\n      dir = goog.i18n.bidi.Dir.NEUTRAL;\n    } else {\n      dir = null;\n    }\n  }\n\n  return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(\n      result, dir);\n};\n\n\n/**\n * Creates a string with attributes to insert after tagName.\n * @param {string} tagName\n * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes\n * @return {string} Returns an empty string if there are no attributes, returns\n *     a string starting with a space otherwise.\n * @throws {!Error} If attribute value is unsafe for the given tag and\n *     attribute.\n * @package\n */\ngoog.html.SafeHtml.stringifyAttributes = function(tagName, opt_attributes) {\n  var result = '';\n  if (opt_attributes) {\n    for (var name in opt_attributes) {\n      // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty#Using_hasOwnProperty_as_a_property_name\n      if (Object.prototype.hasOwnProperty.call(opt_attributes, name)) {\n        if (!goog.html.SafeHtml.VALID_NAMES_IN_TAG_.test(name)) {\n          throw new Error(\n              goog.html.SafeHtml.ENABLE_ERROR_MESSAGES ?\n                  'Invalid attribute name \"' + name + '\".' :\n                  '');\n        }\n        var value = opt_attributes[name];\n        if (value == null) {\n          continue;\n        }\n        result +=\n            ' ' + goog.html.SafeHtml.getAttrNameAndValue_(tagName, name, value);\n      }\n    }\n  }\n  return result;\n};\n\n\n/**\n * @param {!Object<string, ?goog.html.SafeHtml.AttributeValue>} fixedAttributes\n * @param {!Object<string, string>} defaultAttributes\n * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes\n *     Optional attributes passed to create*().\n * @return {!Object<string, ?goog.html.SafeHtml.AttributeValue>}\n * @throws {!Error} If opt_attributes contains an attribute with the same name\n *     as an attribute in fixedAttributes.\n * @package\n */\ngoog.html.SafeHtml.combineAttributes = function(\n    fixedAttributes, defaultAttributes, opt_attributes) {\n  var combinedAttributes = {};\n  var name;\n\n  for (name in fixedAttributes) {\n    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty#Using_hasOwnProperty_as_a_property_name\n    if (Object.prototype.hasOwnProperty.call(fixedAttributes, name)) {\n      goog.asserts.assert(name.toLowerCase() == name, 'Must be lower case');\n      combinedAttributes[name] = fixedAttributes[name];\n    }\n  }\n  for (name in defaultAttributes) {\n    if (Object.prototype.hasOwnProperty.call(defaultAttributes, name)) {\n      goog.asserts.assert(name.toLowerCase() == name, 'Must be lower case');\n      combinedAttributes[name] = defaultAttributes[name];\n    }\n  }\n\n  if (opt_attributes) {\n    for (name in opt_attributes) {\n      if (Object.prototype.hasOwnProperty.call(opt_attributes, name)) {\n        var nameLower = name.toLowerCase();\n        if (nameLower in fixedAttributes) {\n          throw new Error(\n              goog.html.SafeHtml.ENABLE_ERROR_MESSAGES ?\n                  'Cannot override \"' + nameLower + '\" attribute, got \"' +\n                      name + '\" with value \"' + opt_attributes[name] + '\"' :\n                  '');\n        }\n        if (nameLower in defaultAttributes) {\n          delete combinedAttributes[nameLower];\n        }\n        combinedAttributes[name] = opt_attributes[name];\n      }\n    }\n  }\n\n  return combinedAttributes;\n};\n\n\n/**\n * A SafeHtml instance corresponding to the HTML doctype: \"<!DOCTYPE html>\".\n * @const {!goog.html.SafeHtml}\n */\ngoog.html.SafeHtml.DOCTYPE_HTML = /** @type {!goog.html.SafeHtml} */ ({\n  // NOTE: this compiles to nothing, but hides the possible side effect of\n  // SafeHtml creation (due to calling trustedTypes.createPolicy) from the\n  // compiler so that the entire call can be removed if the result is not used.\n  valueOf: function() {\n    return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(\n        '<!DOCTYPE html>', goog.i18n.bidi.Dir.NEUTRAL);\n  },\n}.valueOf());\n\n/**\n * A SafeHtml instance corresponding to the empty string.\n * @const {!goog.html.SafeHtml}\n */\ngoog.html.SafeHtml.EMPTY = new goog.html.SafeHtml(\n    (goog.global.trustedTypes && goog.global.trustedTypes.emptyHTML) || '',\n    goog.i18n.bidi.Dir.NEUTRAL, goog.html.SafeHtml.CONSTRUCTOR_TOKEN_PRIVATE_);\n\n/**\n * A SafeHtml instance corresponding to the <br> tag.\n * @const {!goog.html.SafeHtml}\n */\ngoog.html.SafeHtml.BR = /** @type {!goog.html.SafeHtml} */ ({\n  // NOTE: this compiles to nothing, but hides the possible side effect of\n  // SafeHtml creation (due to calling trustedTypes.createPolicy) from the\n  // compiler so that the entire call can be removed if the result is not used.\n  valueOf: function() {\n    return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(\n        '<br>', goog.i18n.bidi.Dir.NEUTRAL);\n  },\n}.valueOf());\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Type-safe wrappers for unsafe DOM APIs.\n *\n * This file provides type-safe wrappers for DOM APIs that can result in\n * cross-site scripting (XSS) vulnerabilities, if the API is supplied with\n * untrusted (attacker-controlled) input.  Instead of plain strings, the type\n * safe wrappers consume values of types from the goog.html package whose\n * contract promises that values are safe to use in the corresponding context.\n *\n * Hence, a program that exclusively uses the wrappers in this file (i.e., whose\n * only reference to security-sensitive raw DOM APIs are in this file) is\n * guaranteed to be free of XSS due to incorrect use of such DOM APIs (modulo\n * correctness of code that produces values of the respective goog.html types,\n * and absent code that violates type safety).\n *\n * For example, assigning to an element's .innerHTML property a string that is\n * derived (even partially) from untrusted input typically results in an XSS\n * vulnerability. The type-safe wrapper goog.dom.safe.setInnerHtml consumes a\n * value of type goog.html.SafeHtml, whose contract states that using its values\n * in a HTML context will not result in XSS. Hence a program that is free of\n * direct assignments to any element's innerHTML property (with the exception of\n * the assignment to .innerHTML in this file) is guaranteed to be free of XSS\n * due to assignment of untrusted strings to the innerHTML property.\n */\n\ngoog.provide('goog.dom.safe');\ngoog.provide('goog.dom.safe.InsertAdjacentHtmlPosition');\n\ngoog.require('goog.asserts');\ngoog.require('goog.dom.asserts');\ngoog.require('goog.functions');\ngoog.require('goog.html.SafeHtml');\ngoog.require('goog.html.SafeScript');\ngoog.require('goog.html.SafeStyle');\ngoog.require('goog.html.SafeUrl');\ngoog.require('goog.html.TrustedResourceUrl');\ngoog.require('goog.html.uncheckedconversions');\ngoog.require('goog.string.Const');\ngoog.require('goog.string.internal');\n\n\n/** @enum {string} */\ngoog.dom.safe.InsertAdjacentHtmlPosition = {\n  AFTERBEGIN: 'afterbegin',\n  AFTEREND: 'afterend',\n  BEFOREBEGIN: 'beforebegin',\n  BEFOREEND: 'beforeend'\n};\n\n\n/**\n * Inserts known-safe HTML into a Node, at the specified position.\n * @param {!Node} node The node on which to call insertAdjacentHTML.\n * @param {!goog.dom.safe.InsertAdjacentHtmlPosition} position Position where\n *     to insert the HTML.\n * @param {!goog.html.SafeHtml} html The known-safe HTML to insert.\n */\ngoog.dom.safe.insertAdjacentHtml = function(node, position, html) {\n  node.insertAdjacentHTML(position, goog.html.SafeHtml.unwrapTrustedHTML(html));\n};\n\n\n/**\n * Tags not allowed in goog.dom.safe.setInnerHtml.\n * @private @const {!Object<string, boolean>}\n */\ngoog.dom.safe.SET_INNER_HTML_DISALLOWED_TAGS_ = {\n  'MATH': true,\n  'SCRIPT': true,\n  'STYLE': true,\n  'SVG': true,\n  'TEMPLATE': true\n};\n\n\n/**\n * Whether assigning to innerHTML results in a non-spec-compliant clean-up. Used\n * to define goog.dom.safe.unsafeSetInnerHtmlDoNotUseOrElse.\n *\n * <p>As mentioned in https://stackoverflow.com/questions/28741528, re-rendering\n * an element in IE by setting innerHTML causes IE to recursively disconnect all\n * parent/children connections that were in the previous contents of the\n * element. Unfortunately, this can unexpectedly result in confusing cases where\n * a function is run (typically asynchronously) on element that has since\n * disconnected from the DOM but assumes the presence of its children. A simple\n * workaround is to remove all children first. Testing on IE11 via\n * https://jsperf.com/innerhtml-vs-removechild/239, removeChild seems to be\n * ~10x faster than innerHTML='' for a large number of children (perhaps due\n * to the latter's recursive behavior), implying that this workaround would\n * not hurt performance and might actually improve it.\n * @return {boolean}\n * @private\n */\ngoog.dom.safe.isInnerHtmlCleanupRecursive_ =\n    goog.functions.cacheReturnValue(function() {\n      // `document` missing in some test frameworks.\n      if (goog.DEBUG && typeof document === 'undefined') {\n        return false;\n      }\n      // Create 3 nested <div>s without using innerHTML.\n      // We're not chaining the appendChilds in one call,  as this breaks\n      // in a DocumentFragment.\n      var div = document.createElement('div');\n      var childDiv = document.createElement('div');\n      childDiv.appendChild(document.createElement('div'));\n      div.appendChild(childDiv);\n      // `firstChild` is null in Google Js Test.\n      if (goog.DEBUG && !div.firstChild) {\n        return false;\n      }\n      var innerChild = div.firstChild.firstChild;\n      div.innerHTML =\n          goog.html.SafeHtml.unwrapTrustedHTML(goog.html.SafeHtml.EMPTY);\n      return !innerChild.parentElement;\n    });\n\n\n/**\n * Assigns HTML to an element's innerHTML property. Helper to use only here and\n * in soy.js.\n * @param {?Element|?ShadowRoot} elem The element whose innerHTML is to be\n *     assigned to.\n * @param {!goog.html.SafeHtml} html\n */\ngoog.dom.safe.unsafeSetInnerHtmlDoNotUseOrElse = function(elem, html) {\n  // See comment above goog.dom.safe.isInnerHtmlCleanupRecursive_.\n  if (goog.dom.safe.isInnerHtmlCleanupRecursive_()) {\n    while (elem.lastChild) {\n      elem.removeChild(elem.lastChild);\n    }\n  }\n  elem.innerHTML = goog.html.SafeHtml.unwrapTrustedHTML(html);\n};\n\n\n/**\n * Assigns known-safe HTML to an element's innerHTML property.\n * @param {!Element|!ShadowRoot} elem The element whose innerHTML is to be\n *     assigned to.\n * @param {!goog.html.SafeHtml} html The known-safe HTML to assign.\n * @throws {Error} If called with one of these tags: math, script, style, svg,\n *     template.\n */\ngoog.dom.safe.setInnerHtml = function(elem, html) {\n  if (goog.asserts.ENABLE_ASSERTS && elem.tagName) {\n    var tagName = elem.tagName.toUpperCase();\n    if (goog.dom.safe.SET_INNER_HTML_DISALLOWED_TAGS_[tagName]) {\n      throw new Error(\n          'goog.dom.safe.setInnerHtml cannot be used to set content of ' +\n          elem.tagName + '.');\n    }\n  }\n\n  goog.dom.safe.unsafeSetInnerHtmlDoNotUseOrElse(elem, html);\n};\n\n\n/**\n * Assigns constant HTML to an element's innerHTML property.\n * @param {!Element} element The element whose innerHTML is to be assigned to.\n * @param {!goog.string.Const} constHtml The known-safe HTML to assign.\n * @throws {!Error} If called with one of these tags: math, script, style, svg,\n *     template.\n */\ngoog.dom.safe.setInnerHtmlFromConstant = function(element, constHtml) {\n  goog.dom.safe.setInnerHtml(\n      element,\n      goog.html.uncheckedconversions\n          .safeHtmlFromStringKnownToSatisfyTypeContract(\n              goog.string.Const.from('Constant HTML to be immediatelly used.'),\n              goog.string.Const.unwrap(constHtml)));\n};\n\n\n/**\n * Assigns known-safe HTML to an element's outerHTML property.\n * @param {!Element} elem The element whose outerHTML is to be assigned to.\n * @param {!goog.html.SafeHtml} html The known-safe HTML to assign.\n */\ngoog.dom.safe.setOuterHtml = function(elem, html) {\n  elem.outerHTML = goog.html.SafeHtml.unwrapTrustedHTML(html);\n};\n\n\n/**\n * Safely assigns a URL a form element's action property.\n *\n * If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to\n * form's action property.  If url is of type string however, it is first\n * sanitized using goog.html.SafeUrl.sanitize.\n *\n * Example usage:\n *   goog.dom.safe.setFormElementAction(formEl, url);\n * which is a safe alternative to\n *   formEl.action = url;\n * The latter can result in XSS vulnerabilities if url is a\n * user-/attacker-controlled value.\n *\n * @param {!Element} form The form element whose action property\n *     is to be assigned to.\n * @param {string|!goog.html.SafeUrl} url The URL to assign.\n * @see goog.html.SafeUrl#sanitize\n */\ngoog.dom.safe.setFormElementAction = function(form, url) {\n  /** @type {!goog.html.SafeUrl} */\n  var safeUrl;\n  if (url instanceof goog.html.SafeUrl) {\n    safeUrl = url;\n  } else {\n    safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url);\n  }\n  goog.dom.asserts.assertIsHTMLFormElement(form).action =\n      goog.html.SafeUrl.unwrap(safeUrl);\n};\n\n/**\n * Safely assigns a URL to a button element's formaction property.\n *\n * If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to\n * button's formaction property.  If url is of type string however, it is first\n * sanitized using goog.html.SafeUrl.sanitize.\n *\n * Example usage:\n *   goog.dom.safe.setButtonFormAction(buttonEl, url);\n * which is a safe alternative to\n *   buttonEl.action = url;\n * The latter can result in XSS vulnerabilities if url is a\n * user-/attacker-controlled value.\n *\n * @param {!Element} button The button element whose action property\n *     is to be assigned to.\n * @param {string|!goog.html.SafeUrl} url The URL to assign.\n * @see goog.html.SafeUrl#sanitize\n */\ngoog.dom.safe.setButtonFormAction = function(button, url) {\n  /** @type {!goog.html.SafeUrl} */\n  var safeUrl;\n  if (url instanceof goog.html.SafeUrl) {\n    safeUrl = url;\n  } else {\n    safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url);\n  }\n  goog.dom.asserts.assertIsHTMLButtonElement(button).formAction =\n      goog.html.SafeUrl.unwrap(safeUrl);\n};\n/**\n * Safely assigns a URL to an input element's formaction property.\n *\n * If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to\n * input's formaction property.  If url is of type string however, it is first\n * sanitized using goog.html.SafeUrl.sanitize.\n *\n * Example usage:\n *   goog.dom.safe.setInputFormAction(inputEl, url);\n * which is a safe alternative to\n *   inputEl.action = url;\n * The latter can result in XSS vulnerabilities if url is a\n * user-/attacker-controlled value.\n *\n * @param {!Element} input The input element whose action property\n *     is to be assigned to.\n * @param {string|!goog.html.SafeUrl} url The URL to assign.\n * @see goog.html.SafeUrl#sanitize\n */\ngoog.dom.safe.setInputFormAction = function(input, url) {\n  /** @type {!goog.html.SafeUrl} */\n  var safeUrl;\n  if (url instanceof goog.html.SafeUrl) {\n    safeUrl = url;\n  } else {\n    safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url);\n  }\n  goog.dom.asserts.assertIsHTMLInputElement(input).formAction =\n      goog.html.SafeUrl.unwrap(safeUrl);\n};\n\n/**\n * Sets the given element's style property to the contents of the provided\n * SafeStyle object.\n * @param {!Element} elem\n * @param {!goog.html.SafeStyle} style\n */\ngoog.dom.safe.setStyle = function(elem, style) {\n  elem.style.cssText = goog.html.SafeStyle.unwrap(style);\n};\n\n\n/**\n * Writes known-safe HTML to a document.\n * @param {!Document} doc The document to be written to.\n * @param {!goog.html.SafeHtml} html The known-safe HTML to assign.\n */\ngoog.dom.safe.documentWrite = function(doc, html) {\n  doc.write(goog.html.SafeHtml.unwrapTrustedHTML(html));\n};\n\n\n/**\n * Safely assigns a URL to an anchor element's href property.\n *\n * If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to\n * anchor's href property.  If url is of type string however, it is first\n * sanitized using goog.html.SafeUrl.sanitize.\n *\n * Example usage:\n *   goog.dom.safe.setAnchorHref(anchorEl, url);\n * which is a safe alternative to\n *   anchorEl.href = url;\n * The latter can result in XSS vulnerabilities if url is a\n * user-/attacker-controlled value.\n *\n * @param {!HTMLAnchorElement} anchor The anchor element whose href property\n *     is to be assigned to.\n * @param {string|!goog.html.SafeUrl} url The URL to assign.\n * @see goog.html.SafeUrl#sanitize\n */\ngoog.dom.safe.setAnchorHref = function(anchor, url) {\n  goog.dom.asserts.assertIsHTMLAnchorElement(anchor);\n  /** @type {!goog.html.SafeUrl} */\n  var safeUrl;\n  if (url instanceof goog.html.SafeUrl) {\n    safeUrl = url;\n  } else {\n    safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url);\n  }\n  anchor.href = goog.html.SafeUrl.unwrap(safeUrl);\n};\n\n\n/**\n * Safely assigns a URL to an image element's src property.\n *\n * If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to\n * image's src property.  If url is of type string however, it is first\n * sanitized using goog.html.SafeUrl.sanitize.\n *\n * @param {!HTMLImageElement} imageElement The image element whose src property\n *     is to be assigned to.\n * @param {string|!goog.html.SafeUrl} url The URL to assign.\n * @see goog.html.SafeUrl#sanitize\n */\ngoog.dom.safe.setImageSrc = function(imageElement, url) {\n  goog.dom.asserts.assertIsHTMLImageElement(imageElement);\n  /** @type {!goog.html.SafeUrl} */\n  var safeUrl;\n  if (url instanceof goog.html.SafeUrl) {\n    safeUrl = url;\n  } else {\n    var allowDataUrl = /^data:image\\//i.test(url);\n    safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url, allowDataUrl);\n  }\n  imageElement.src = goog.html.SafeUrl.unwrap(safeUrl);\n};\n\n/**\n * Safely assigns a URL to a audio element's src property.\n *\n * If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to\n * audio's src property.  If url is of type string however, it is first\n * sanitized using goog.html.SafeUrl.sanitize.\n *\n * @param {!HTMLAudioElement} audioElement The audio element whose src property\n *     is to be assigned to.\n * @param {string|!goog.html.SafeUrl} url The URL to assign.\n * @see goog.html.SafeUrl#sanitize\n */\ngoog.dom.safe.setAudioSrc = function(audioElement, url) {\n  goog.dom.asserts.assertIsHTMLAudioElement(audioElement);\n  /** @type {!goog.html.SafeUrl} */\n  var safeUrl;\n  if (url instanceof goog.html.SafeUrl) {\n    safeUrl = url;\n  } else {\n    var allowDataUrl = /^data:audio\\//i.test(url);\n    safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url, allowDataUrl);\n  }\n  audioElement.src = goog.html.SafeUrl.unwrap(safeUrl);\n};\n\n/**\n * Safely assigns a URL to a video element's src property.\n *\n * If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to\n * video's src property.  If url is of type string however, it is first\n * sanitized using goog.html.SafeUrl.sanitize.\n *\n * @param {!HTMLVideoElement} videoElement The video element whose src property\n *     is to be assigned to.\n * @param {string|!goog.html.SafeUrl} url The URL to assign.\n * @see goog.html.SafeUrl#sanitize\n */\ngoog.dom.safe.setVideoSrc = function(videoElement, url) {\n  goog.dom.asserts.assertIsHTMLVideoElement(videoElement);\n  /** @type {!goog.html.SafeUrl} */\n  var safeUrl;\n  if (url instanceof goog.html.SafeUrl) {\n    safeUrl = url;\n  } else {\n    var allowDataUrl = /^data:video\\//i.test(url);\n    safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url, allowDataUrl);\n  }\n  videoElement.src = goog.html.SafeUrl.unwrap(safeUrl);\n};\n\n/**\n * Safely assigns a URL to an embed element's src property.\n *\n * Example usage:\n *   goog.dom.safe.setEmbedSrc(embedEl, url);\n * which is a safe alternative to\n *   embedEl.src = url;\n * The latter can result in loading untrusted code unless it is ensured that\n * the URL refers to a trustworthy resource.\n *\n * @param {!HTMLEmbedElement} embed The embed element whose src property\n *     is to be assigned to.\n * @param {!goog.html.TrustedResourceUrl} url The URL to assign.\n */\ngoog.dom.safe.setEmbedSrc = function(embed, url) {\n  goog.dom.asserts.assertIsHTMLEmbedElement(embed);\n  embed.src = goog.html.TrustedResourceUrl.unwrapTrustedScriptURL(url);\n};\n\n\n/**\n * Safely assigns a URL to a frame element's src property.\n *\n * Example usage:\n *   goog.dom.safe.setFrameSrc(frameEl, url);\n * which is a safe alternative to\n *   frameEl.src = url;\n * The latter can result in loading untrusted code unless it is ensured that\n * the URL refers to a trustworthy resource.\n *\n * @param {!HTMLFrameElement} frame The frame element whose src property\n *     is to be assigned to.\n * @param {!goog.html.TrustedResourceUrl} url The URL to assign.\n */\ngoog.dom.safe.setFrameSrc = function(frame, url) {\n  goog.dom.asserts.assertIsHTMLFrameElement(frame);\n  frame.src = goog.html.TrustedResourceUrl.unwrap(url);\n};\n\n\n/**\n * Safely assigns a URL to an iframe element's src property.\n *\n * Example usage:\n *   goog.dom.safe.setIframeSrc(iframeEl, url);\n * which is a safe alternative to\n *   iframeEl.src = url;\n * The latter can result in loading untrusted code unless it is ensured that\n * the URL refers to a trustworthy resource.\n *\n * @param {!HTMLIFrameElement} iframe The iframe element whose src property\n *     is to be assigned to.\n * @param {!goog.html.TrustedResourceUrl} url The URL to assign.\n */\ngoog.dom.safe.setIframeSrc = function(iframe, url) {\n  goog.dom.asserts.assertIsHTMLIFrameElement(iframe);\n  iframe.src = goog.html.TrustedResourceUrl.unwrap(url);\n};\n\n\n/**\n * Safely assigns HTML to an iframe element's srcdoc property.\n *\n * Example usage:\n *   goog.dom.safe.setIframeSrcdoc(iframeEl, safeHtml);\n * which is a safe alternative to\n *   iframeEl.srcdoc = html;\n * The latter can result in loading untrusted code.\n *\n * @param {!HTMLIFrameElement} iframe The iframe element whose srcdoc property\n *     is to be assigned to.\n * @param {!goog.html.SafeHtml} html The HTML to assign.\n */\ngoog.dom.safe.setIframeSrcdoc = function(iframe, html) {\n  goog.dom.asserts.assertIsHTMLIFrameElement(iframe);\n  iframe.srcdoc = goog.html.SafeHtml.unwrapTrustedHTML(html);\n};\n\n\n/**\n * Safely sets a link element's href and rel properties. Whether or not\n * the URL assigned to href has to be a goog.html.TrustedResourceUrl\n * depends on the value of the rel property. If rel contains \"stylesheet\"\n * then a TrustedResourceUrl is required.\n *\n * Example usage:\n *   goog.dom.safe.setLinkHrefAndRel(linkEl, url, 'stylesheet');\n * which is a safe alternative to\n *   linkEl.rel = 'stylesheet';\n *   linkEl.href = url;\n * The latter can result in loading untrusted code unless it is ensured that\n * the URL refers to a trustworthy resource.\n *\n * @param {!HTMLLinkElement} link The link element whose href property\n *     is to be assigned to.\n * @param {string|!goog.html.SafeUrl|!goog.html.TrustedResourceUrl} url The URL\n *     to assign to the href property. Must be a TrustedResourceUrl if the\n *     value assigned to rel contains \"stylesheet\". A string value is\n *     sanitized with goog.html.SafeUrl.sanitize.\n * @param {string} rel The value to assign to the rel property.\n * @throws {Error} if rel contains \"stylesheet\" and url is not a\n *     TrustedResourceUrl\n * @see goog.html.SafeUrl#sanitize\n */\ngoog.dom.safe.setLinkHrefAndRel = function(link, url, rel) {\n  goog.dom.asserts.assertIsHTMLLinkElement(link);\n  link.rel = rel;\n  if (goog.string.internal.caseInsensitiveContains(rel, 'stylesheet')) {\n    goog.asserts.assert(\n        url instanceof goog.html.TrustedResourceUrl,\n        'URL must be TrustedResourceUrl because \"rel\" contains \"stylesheet\"');\n    link.href = goog.html.TrustedResourceUrl.unwrap(url);\n  } else if (url instanceof goog.html.TrustedResourceUrl) {\n    link.href = goog.html.TrustedResourceUrl.unwrap(url);\n  } else if (url instanceof goog.html.SafeUrl) {\n    link.href = goog.html.SafeUrl.unwrap(url);\n  } else {  // string\n    // SafeUrl.sanitize must return legitimate SafeUrl when passed a string.\n    link.href = goog.html.SafeUrl.unwrap(\n        goog.html.SafeUrl.sanitizeAssertUnchanged(url));\n  }\n};\n\n\n/**\n * Safely assigns a URL to an object element's data property.\n *\n * Example usage:\n *   goog.dom.safe.setObjectData(objectEl, url);\n * which is a safe alternative to\n *   objectEl.data = url;\n * The latter can result in loading untrusted code unless setit is ensured that\n * the URL refers to a trustworthy resource.\n *\n * @param {!HTMLObjectElement} object The object element whose data property\n *     is to be assigned to.\n * @param {!goog.html.TrustedResourceUrl} url The URL to assign.\n */\ngoog.dom.safe.setObjectData = function(object, url) {\n  goog.dom.asserts.assertIsHTMLObjectElement(object);\n  object.data = goog.html.TrustedResourceUrl.unwrapTrustedScriptURL(url);\n};\n\n\n/**\n * Safely assigns a URL to a script element's src property.\n *\n * Example usage:\n *   goog.dom.safe.setScriptSrc(scriptEl, url);\n * which is a safe alternative to\n *   scriptEl.src = url;\n * The latter can result in loading untrusted code unless it is ensured that\n * the URL refers to a trustworthy resource.\n *\n * @param {!HTMLScriptElement} script The script element whose src property\n *     is to be assigned to.\n * @param {!goog.html.TrustedResourceUrl} url The URL to assign.\n */\ngoog.dom.safe.setScriptSrc = function(script, url) {\n  goog.dom.asserts.assertIsHTMLScriptElement(script);\n  script.src = goog.html.TrustedResourceUrl.unwrapTrustedScriptURL(url);\n  goog.dom.safe.setNonceForScriptElement_(script);\n};\n\n\n/**\n * Safely assigns a value to a script element's content.\n *\n * Example usage:\n *   goog.dom.safe.setScriptContent(scriptEl, content);\n * which is a safe alternative to\n *   scriptEl.text = content;\n * The latter can result in executing untrusted code unless it is ensured that\n * the code is loaded from a trustworthy resource.\n *\n * @param {!HTMLScriptElement} script The script element whose content is being\n *     set.\n * @param {!goog.html.SafeScript} content The content to assign.\n */\ngoog.dom.safe.setScriptContent = function(script, content) {\n  goog.dom.asserts.assertIsHTMLScriptElement(script);\n  script.textContent = goog.html.SafeScript.unwrapTrustedScript(content);\n  goog.dom.safe.setNonceForScriptElement_(script);\n};\n\n\n/**\n * Set nonce-based CSPs to dynamically created scripts.\n * @param {!HTMLScriptElement} script The script element whose nonce value\n *     is to be calculated\n * @private\n */\ngoog.dom.safe.setNonceForScriptElement_ = function(script) {\n  var win = script.ownerDocument && script.ownerDocument.defaultView;\n  var nonce = goog.getScriptNonce(win);\n  if (nonce) {\n    script.setAttribute('nonce', nonce);\n  }\n};\n\n\n/**\n * Safely assigns a URL to a Location object's href property.\n *\n * If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to\n * loc's href property.  If url is of type string however, it is first sanitized\n * using goog.html.SafeUrl.sanitize.\n *\n * Example usage:\n *   goog.dom.safe.setLocationHref(document.location, redirectUrl);\n * which is a safe alternative to\n *   document.location.href = redirectUrl;\n * The latter can result in XSS vulnerabilities if redirectUrl is a\n * user-/attacker-controlled value.\n *\n * @param {!Location} loc The Location object whose href property is to be\n *     assigned to.\n * @param {string|!goog.html.SafeUrl} url The URL to assign.\n * @see goog.html.SafeUrl#sanitize\n */\ngoog.dom.safe.setLocationHref = function(loc, url) {\n  goog.dom.asserts.assertIsLocation(loc);\n  /** @type {!goog.html.SafeUrl} */\n  var safeUrl;\n  if (url instanceof goog.html.SafeUrl) {\n    safeUrl = url;\n  } else {\n    safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url);\n  }\n  loc.href = goog.html.SafeUrl.unwrap(safeUrl);\n};\n\n/**\n * Safely assigns the URL of a Location object.\n *\n * If url is of type goog.html.SafeUrl, its value is unwrapped and\n * passed to Location#assign. If url is of type string however, it is\n * first sanitized using goog.html.SafeUrl.sanitize.\n *\n * Example usage:\n *   goog.dom.safe.assignLocation(document.location, newUrl);\n * which is a safe alternative to\n *   document.location.assign(newUrl);\n * The latter can result in XSS vulnerabilities if newUrl is a\n * user-/attacker-controlled value.\n *\n * This has the same behaviour as setLocationHref, however some test\n * mock Location.assign instead of a property assignment.\n *\n * @param {!Location} loc The Location object which is to be assigned.\n * @param {string|!goog.html.SafeUrl} url The URL to assign.\n * @see goog.html.SafeUrl#sanitize\n */\ngoog.dom.safe.assignLocation = function(loc, url) {\n  goog.dom.asserts.assertIsLocation(loc);\n  /** @type {!goog.html.SafeUrl} */\n  var safeUrl;\n  if (url instanceof goog.html.SafeUrl) {\n    safeUrl = url;\n  } else {\n    safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url);\n  }\n  loc.assign(goog.html.SafeUrl.unwrap(safeUrl));\n};\n\n\n/**\n * Safely replaces the URL of a Location object.\n *\n * If url is of type goog.html.SafeUrl, its value is unwrapped and\n * passed to Location#replace. If url is of type string however, it is\n * first sanitized using goog.html.SafeUrl.sanitize.\n *\n * Example usage:\n *   goog.dom.safe.replaceLocation(document.location, newUrl);\n * which is a safe alternative to\n *   document.location.replace(newUrl);\n * The latter can result in XSS vulnerabilities if newUrl is a\n * user-/attacker-controlled value.\n *\n * @param {!Location} loc The Location object which is to be replaced.\n * @param {string|!goog.html.SafeUrl} url The URL to assign.\n * @see goog.html.SafeUrl#sanitize\n */\ngoog.dom.safe.replaceLocation = function(loc, url) {\n  /** @type {!goog.html.SafeUrl} */\n  var safeUrl;\n  if (url instanceof goog.html.SafeUrl) {\n    safeUrl = url;\n  } else {\n    safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url);\n  }\n  loc.replace(goog.html.SafeUrl.unwrap(safeUrl));\n};\n\n\n/**\n * Safely opens a URL in a new window (via window.open).\n *\n * If url is of type goog.html.SafeUrl, its value is unwrapped and passed in to\n * window.open.  If url is of type string however, it is first sanitized\n * using goog.html.SafeUrl.sanitize.\n *\n * Note that this function does not prevent leakages via the referer that is\n * sent by window.open. It is advised to only use this to open 1st party URLs.\n *\n * Example usage:\n *   goog.dom.safe.openInWindow(url);\n * which is a safe alternative to\n *   window.open(url);\n * The latter can result in XSS vulnerabilities if url is a\n * user-/attacker-controlled value.\n *\n * @param {string|!goog.html.SafeUrl} url The URL to open.\n * @param {Window=} opt_openerWin Window of which to call the .open() method.\n *     Defaults to the global window.\n * @param {!goog.string.Const|string=} opt_name Name of the window to open in.\n *     Can be _top, etc as allowed by window.open(). This accepts string for\n *     legacy reasons. Pass goog.string.Const if possible.\n * @param {string=} opt_specs Comma-separated list of specifications, same as\n *     in window.open().\n * @param {boolean=} opt_replace Whether to replace the current entry in browser\n *     history, same as in window.open().\n * @return {Window} Window the url was opened in.\n */\ngoog.dom.safe.openInWindow = function(\n    url, opt_openerWin, opt_name, opt_specs, opt_replace) {\n  /** @type {!goog.html.SafeUrl} */\n  var safeUrl;\n  if (url instanceof goog.html.SafeUrl) {\n    safeUrl = url;\n  } else {\n    safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url);\n  }\n  var win = opt_openerWin || goog.global;\n  // If opt_name is undefined, simply passing that in to open() causes IE to\n  // reuse the current window instead of opening a new one. Thus we pass '' in\n  // instead, which according to spec opens a new window. See\n  // https://html.spec.whatwg.org/multipage/browsers.html#dom-open .\n  var name = opt_name instanceof goog.string.Const ?\n      goog.string.Const.unwrap(opt_name) :\n      opt_name || '';\n  return win.open(\n      goog.html.SafeUrl.unwrap(safeUrl), name, opt_specs, opt_replace);\n};\n\n\n/**\n * Parses the HTML as 'text/html'.\n * @param {!DOMParser} parser\n * @param {!goog.html.SafeHtml} html The HTML to be parsed.\n * @return {?Document}\n */\ngoog.dom.safe.parseFromStringHtml = function(parser, html) {\n  return goog.dom.safe.parseFromString(parser, html, 'text/html');\n};\n\n\n/**\n * Parses the string.\n * @param {!DOMParser} parser\n * @param {!goog.html.SafeHtml} content Note: We don't have a special type for\n *     XML od SVG supported by this function so we use SafeHtml.\n * @param {string} type\n * @return {?Document}\n */\ngoog.dom.safe.parseFromString = function(parser, content, type) {\n  return parser.parseFromString(\n      goog.html.SafeHtml.unwrapTrustedHTML(content), type);\n};\n\n\n/**\n * Safely creates an HTMLImageElement from a Blob.\n *\n * Example usage:\n *     goog.dom.safe.createImageFromBlob(blob);\n * which is a safe alternative to\n *     image.src = createObjectUrl(blob)\n * The latter can result in executing malicious same-origin scripts from a bad\n * Blob.\n * @param {!Blob} blob The blob to create the image from.\n * @return {!HTMLImageElement} The image element created from the blob.\n * @throws {!Error} If called with a Blob with a MIME type other than image/.*.\n */\ngoog.dom.safe.createImageFromBlob = function(blob) {\n  // Any image/* MIME type is accepted as safe.\n  if (!/^image\\/.*/g.test(blob.type)) {\n    throw new Error(\n        'goog.dom.safe.createImageFromBlob only accepts MIME type image/.*.');\n  }\n  var objectUrl = goog.global.URL.createObjectURL(blob);\n  var image = new goog.global.Image();\n  image.onload = function() {\n    goog.global.URL.revokeObjectURL(objectUrl);\n  };\n  goog.dom.safe.setImageSrc(\n      image,\n      goog.html.uncheckedconversions\n          .safeUrlFromStringKnownToSatisfyTypeContract(\n              goog.string.Const.from('Image blob URL.'), objectUrl));\n  return image;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Utilities for string manipulation.\n */\n\n\n/**\n * Namespace for string utilities\n */\ngoog.provide('goog.string');\ngoog.provide('goog.string.Unicode');\n\ngoog.require('goog.dom.safe');\ngoog.require('goog.html.uncheckedconversions');\ngoog.require('goog.string.Const');\ngoog.require('goog.string.internal');\n\n\n/**\n * @define {boolean} Enables HTML escaping of lowercase letter \"e\" which helps\n * with detection of double-escaping as this letter is frequently used.\n */\ngoog.string.DETECT_DOUBLE_ESCAPING =\n    goog.define('goog.string.DETECT_DOUBLE_ESCAPING', false);\n\n\n/**\n * @define {boolean} Whether to force non-dom html unescaping.\n */\ngoog.string.FORCE_NON_DOM_HTML_UNESCAPING =\n    goog.define('goog.string.FORCE_NON_DOM_HTML_UNESCAPING', false);\n\n\n/**\n * Common Unicode string characters.\n * @enum {string}\n */\ngoog.string.Unicode = {\n  NBSP: '\\xa0'\n};\n\n\n/**\n * Fast prefix-checker.\n * @param {string} str The string to check.\n * @param {string} prefix A string to look for at the start of `str`.\n * @return {boolean} True if `str` begins with `prefix`.\n */\ngoog.string.startsWith = goog.string.internal.startsWith;\n\n\n/**\n * Fast suffix-checker.\n * @param {string} str The string to check.\n * @param {string} suffix A string to look for at the end of `str`.\n * @return {boolean} True if `str` ends with `suffix`.\n */\ngoog.string.endsWith = goog.string.internal.endsWith;\n\n\n/**\n * Case-insensitive prefix-checker.\n * @param {string} str The string to check.\n * @param {string} prefix  A string to look for at the end of `str`.\n * @return {boolean} True if `str` begins with `prefix` (ignoring\n *     case).\n */\ngoog.string.caseInsensitiveStartsWith =\n    goog.string.internal.caseInsensitiveStartsWith;\n\n\n/**\n * Case-insensitive suffix-checker.\n * @param {string} str The string to check.\n * @param {string} suffix A string to look for at the end of `str`.\n * @return {boolean} True if `str` ends with `suffix` (ignoring\n *     case).\n */\ngoog.string.caseInsensitiveEndsWith =\n    goog.string.internal.caseInsensitiveEndsWith;\n\n\n/**\n * Case-insensitive equality checker.\n * @param {string} str1 First string to check.\n * @param {string} str2 Second string to check.\n * @return {boolean} True if `str1` and `str2` are the same string,\n *     ignoring case.\n */\ngoog.string.caseInsensitiveEquals = goog.string.internal.caseInsensitiveEquals;\n\n\n/**\n * Does simple python-style string substitution.\n * subs(\"foo%s hot%s\", \"bar\", \"dog\") becomes \"foobar hotdog\".\n * @param {string} str The string containing the pattern.\n * @param {...*} var_args The items to substitute into the pattern.\n * @return {string} A copy of `str` in which each occurrence of\n *     {@code %s} has been replaced an argument from `var_args`.\n */\ngoog.string.subs = function(str, var_args) {\n  'use strict';\n  var splitParts = str.split('%s');\n  var returnString = '';\n\n  var subsArguments = Array.prototype.slice.call(arguments, 1);\n  while (subsArguments.length &&\n         // Replace up to the last split part. We are inserting in the\n         // positions between split parts.\n         splitParts.length > 1) {\n    returnString += splitParts.shift() + subsArguments.shift();\n  }\n\n  return returnString + splitParts.join('%s');  // Join unused '%s'\n};\n\n\n/**\n * Converts multiple whitespace chars (spaces, non-breaking-spaces, new lines\n * and tabs) to a single space, and strips leading and trailing whitespace.\n * @param {string} str Input string.\n * @return {string} A copy of `str` with collapsed whitespace.\n */\ngoog.string.collapseWhitespace = function(str) {\n  'use strict';\n  // Since IE doesn't include non-breaking-space (0xa0) in their \\s character\n  // class (as required by section 7.2 of the ECMAScript spec), we explicitly\n  // include it in the regexp to enforce consistent cross-browser behavior.\n  return str.replace(/[\\s\\xa0]+/g, ' ').replace(/^\\s+|\\s+$/g, '');\n};\n\n\n/**\n * Checks if a string is empty or contains only whitespaces.\n * @param {string} str The string to check.\n * @return {boolean} Whether `str` is empty or whitespace only.\n */\ngoog.string.isEmptyOrWhitespace = goog.string.internal.isEmptyOrWhitespace;\n\n\n/**\n * Checks if a string is empty.\n * @param {string} str The string to check.\n * @return {boolean} Whether `str` is empty.\n */\ngoog.string.isEmptyString = function(str) {\n  'use strict';\n  return str.length == 0;\n};\n\n\n/**\n * Checks if a string is empty or contains only whitespaces.\n *\n * @param {string} str The string to check.\n * @return {boolean} Whether `str` is empty or whitespace only.\n * @deprecated Use goog.string.isEmptyOrWhitespace instead.\n */\ngoog.string.isEmpty = goog.string.isEmptyOrWhitespace;\n\n\n/**\n * Checks if a string is null, undefined, empty or contains only whitespaces.\n * @param {*} str The string to check.\n * @return {boolean} Whether `str` is null, undefined, empty, or\n *     whitespace only.\n * @deprecated Use goog.string.isEmptyOrWhitespace(goog.string.makeSafe(str))\n *     instead.\n */\ngoog.string.isEmptyOrWhitespaceSafe = function(str) {\n  'use strict';\n  return goog.string.isEmptyOrWhitespace(goog.string.makeSafe(str));\n};\n\n\n/**\n * Checks if a string is null, undefined, empty or contains only whitespaces.\n *\n * @param {*} str The string to check.\n * @return {boolean} Whether `str` is null, undefined, empty, or\n *     whitespace only.\n * @deprecated Use goog.string.isEmptyOrWhitespace instead.\n */\ngoog.string.isEmptySafe = goog.string.isEmptyOrWhitespaceSafe;\n\n\n/**\n * Checks if a string is all breaking whitespace.\n * @param {string} str The string to check.\n * @return {boolean} Whether the string is all breaking whitespace.\n */\ngoog.string.isBreakingWhitespace = function(str) {\n  'use strict';\n  return !/[^\\t\\n\\r ]/.test(str);\n};\n\n\n/**\n * Checks if a string contains all letters.\n * @param {string} str string to check.\n * @return {boolean} True if `str` consists entirely of letters.\n */\ngoog.string.isAlpha = function(str) {\n  'use strict';\n  return !/[^a-zA-Z]/.test(str);\n};\n\n\n/**\n * Checks if a string contains only numbers.\n * @param {*} str string to check. If not a string, it will be\n *     casted to one.\n * @return {boolean} True if `str` is numeric.\n */\ngoog.string.isNumeric = function(str) {\n  'use strict';\n  return !/[^0-9]/.test(str);\n};\n\n\n/**\n * Checks if a string contains only numbers or letters.\n * @param {string} str string to check.\n * @return {boolean} True if `str` is alphanumeric.\n */\ngoog.string.isAlphaNumeric = function(str) {\n  'use strict';\n  return !/[^a-zA-Z0-9]/.test(str);\n};\n\n\n/**\n * Checks if a character is a space character.\n * @param {string} ch Character to check.\n * @return {boolean} True if `ch` is a space.\n */\ngoog.string.isSpace = function(ch) {\n  'use strict';\n  return ch == ' ';\n};\n\n\n/**\n * Checks if a character is a valid unicode character.\n * @param {string} ch Character to check.\n * @return {boolean} True if `ch` is a valid unicode character.\n */\ngoog.string.isUnicodeChar = function(ch) {\n  'use strict';\n  return ch.length == 1 && ch >= ' ' && ch <= '~' ||\n      ch >= '\\u0080' && ch <= '\\uFFFD';\n};\n\n\n/**\n * Takes a string and replaces newlines with a space. Multiple lines are\n * replaced with a single space.\n * @param {string} str The string from which to strip newlines.\n * @return {string} A copy of `str` stripped of newlines.\n */\ngoog.string.stripNewlines = function(str) {\n  'use strict';\n  return str.replace(/(\\r\\n|\\r|\\n)+/g, ' ');\n};\n\n\n/**\n * Replaces Windows and Mac new lines with unix style: \\r or \\r\\n with \\n.\n * @param {string} str The string to in which to canonicalize newlines.\n * @return {string} `str` A copy of {@code} with canonicalized newlines.\n */\ngoog.string.canonicalizeNewlines = function(str) {\n  'use strict';\n  return str.replace(/(\\r\\n|\\r|\\n)/g, '\\n');\n};\n\n\n/**\n * Normalizes whitespace in a string, replacing all whitespace chars with\n * a space.\n * @param {string} str The string in which to normalize whitespace.\n * @return {string} A copy of `str` with all whitespace normalized.\n */\ngoog.string.normalizeWhitespace = function(str) {\n  'use strict';\n  return str.replace(/\\xa0|\\s/g, ' ');\n};\n\n\n/**\n * Normalizes spaces in a string, replacing all consecutive spaces and tabs\n * with a single space. Replaces non-breaking space with a space.\n * @param {string} str The string in which to normalize spaces.\n * @return {string} A copy of `str` with all consecutive spaces and tabs\n *    replaced with a single space.\n */\ngoog.string.normalizeSpaces = function(str) {\n  'use strict';\n  return str.replace(/\\xa0|[ \\t]+/g, ' ');\n};\n\n\n/**\n * Removes the breaking spaces from the left and right of the string and\n * collapses the sequences of breaking spaces in the middle into single spaces.\n * The original and the result strings render the same way in HTML.\n * @param {string} str A string in which to collapse spaces.\n * @return {string} Copy of the string with normalized breaking spaces.\n */\ngoog.string.collapseBreakingSpaces = function(str) {\n  'use strict';\n  return str.replace(/[\\t\\r\\n ]+/g, ' ')\n      .replace(/^[\\t\\r\\n ]+|[\\t\\r\\n ]+$/g, '');\n};\n\n\n/**\n * Trims white spaces to the left and right of a string.\n * @param {string} str The string to trim.\n * @return {string} A trimmed copy of `str`.\n */\ngoog.string.trim = goog.string.internal.trim;\n\n\n/**\n * Trims whitespaces at the left end of a string.\n * @param {string} str The string to left trim.\n * @return {string} A trimmed copy of `str`.\n */\ngoog.string.trimLeft = function(str) {\n  'use strict';\n  // Since IE doesn't include non-breaking-space (0xa0) in their \\s character\n  // class (as required by section 7.2 of the ECMAScript spec), we explicitly\n  // include it in the regexp to enforce consistent cross-browser behavior.\n  return str.replace(/^[\\s\\xa0]+/, '');\n};\n\n\n/**\n * Trims whitespaces at the right end of a string.\n * @param {string} str The string to right trim.\n * @return {string} A trimmed copy of `str`.\n */\ngoog.string.trimRight = function(str) {\n  'use strict';\n  // Since IE doesn't include non-breaking-space (0xa0) in their \\s character\n  // class (as required by section 7.2 of the ECMAScript spec), we explicitly\n  // include it in the regexp to enforce consistent cross-browser behavior.\n  return str.replace(/[\\s\\xa0]+$/, '');\n};\n\n\n/**\n * A string comparator that ignores case.\n * -1 = str1 less than str2\n *  0 = str1 equals str2\n *  1 = str1 greater than str2\n *\n * @param {string} str1 The string to compare.\n * @param {string} str2 The string to compare `str1` to.\n * @return {number} The comparator result, as described above.\n */\ngoog.string.caseInsensitiveCompare =\n    goog.string.internal.caseInsensitiveCompare;\n\n\n/**\n * Compares two strings interpreting their numeric substrings as numbers.\n *\n * @param {string} str1 First string.\n * @param {string} str2 Second string.\n * @param {!RegExp} tokenizerRegExp Splits a string into substrings of\n *     non-negative integers, non-numeric characters and optionally fractional\n *     numbers starting with a decimal point.\n * @return {number} Negative if str1 < str2, 0 is str1 == str2, positive if\n *     str1 > str2.\n * @private\n */\ngoog.string.numberAwareCompare_ = function(str1, str2, tokenizerRegExp) {\n  'use strict';\n  if (str1 == str2) {\n    return 0;\n  }\n  if (!str1) {\n    return -1;\n  }\n  if (!str2) {\n    return 1;\n  }\n\n  // Using match to split the entire string ahead of time turns out to be faster\n  // for most inputs than using RegExp.exec or iterating over each character.\n  var tokens1 = str1.toLowerCase().match(tokenizerRegExp);\n  var tokens2 = str2.toLowerCase().match(tokenizerRegExp);\n\n  var count = Math.min(tokens1.length, tokens2.length);\n\n  for (var i = 0; i < count; i++) {\n    var a = tokens1[i];\n    var b = tokens2[i];\n\n    // Compare pairs of tokens, returning if one token sorts before the other.\n    if (a != b) {\n      // Only if both tokens are integers is a special comparison required.\n      // Decimal numbers are sorted as strings (e.g., '.09' < '.1').\n      var num1 = parseInt(a, 10);\n      if (!isNaN(num1)) {\n        var num2 = parseInt(b, 10);\n        if (!isNaN(num2) && num1 - num2) {\n          return num1 - num2;\n        }\n      }\n      return a < b ? -1 : 1;\n    }\n  }\n\n  // If one string is a substring of the other, the shorter string sorts first.\n  if (tokens1.length != tokens2.length) {\n    return tokens1.length - tokens2.length;\n  }\n\n  // The two strings must be equivalent except for case (perfect equality is\n  // tested at the head of the function.) Revert to default ASCII string\n  // comparison to stabilize the sort.\n  return str1 < str2 ? -1 : 1;\n};\n\n\n/**\n * String comparison function that handles non-negative integer numbers in a\n * way humans might expect. Using this function, the string 'File 2.jpg' sorts\n * before 'File 10.jpg', and 'Version 1.9' before 'Version 1.10'. The comparison\n * is mostly case-insensitive, though strings that are identical except for case\n * are sorted with the upper-case strings before lower-case.\n *\n * This comparison function is up to 50x slower than either the default or the\n * case-insensitive compare. It should not be used in time-critical code, but\n * should be fast enough to sort several hundred short strings (like filenames)\n * with a reasonable delay.\n *\n * @param {string} str1 The string to compare in a numerically sensitive way.\n * @param {string} str2 The string to compare `str1` to.\n * @return {number} less than 0 if str1 < str2, 0 if str1 == str2, greater than\n *     0 if str1 > str2.\n */\ngoog.string.intAwareCompare = function(str1, str2) {\n  'use strict';\n  return goog.string.numberAwareCompare_(str1, str2, /\\d+|\\D+/g);\n};\n\n\n/**\n * String comparison function that handles non-negative integer and fractional\n * numbers in a way humans might expect. Using this function, the string\n * 'File 2.jpg' sorts before 'File 10.jpg', and '3.14' before '3.2'. Equivalent\n * to {@link goog.string.intAwareCompare} apart from the way how it interprets\n * dots.\n *\n * @param {string} str1 The string to compare in a numerically sensitive way.\n * @param {string} str2 The string to compare `str1` to.\n * @return {number} less than 0 if str1 < str2, 0 if str1 == str2, greater than\n *     0 if str1 > str2.\n */\ngoog.string.floatAwareCompare = function(str1, str2) {\n  'use strict';\n  return goog.string.numberAwareCompare_(str1, str2, /\\d+|\\.\\d+|\\D+/g);\n};\n\n\n/**\n * Alias for {@link goog.string.floatAwareCompare}.\n *\n * @param {string} str1\n * @param {string} str2\n * @return {number}\n */\ngoog.string.numerateCompare = goog.string.floatAwareCompare;\n\n\n/**\n * URL-encodes a string\n * @param {*} str The string to url-encode.\n * @return {string} An encoded copy of `str` that is safe for urls.\n *     Note that '#', ':', and other characters used to delimit portions\n *     of URLs *will* be encoded.\n */\ngoog.string.urlEncode = function(str) {\n  'use strict';\n  return encodeURIComponent(String(str));\n};\n\n\n/**\n * URL-decodes the string. We need to specially handle '+'s because\n * the javascript library doesn't convert them to spaces.\n * @param {string} str The string to url decode.\n * @return {string} The decoded `str`.\n */\ngoog.string.urlDecode = function(str) {\n  'use strict';\n  return decodeURIComponent(str.replace(/\\+/g, ' '));\n};\n\n\n/**\n * Converts \\n to <br>s or <br />s.\n * @param {string} str The string in which to convert newlines.\n * @param {boolean=} opt_xml Whether to use XML compatible tags.\n * @return {string} A copy of `str` with converted newlines.\n */\ngoog.string.newLineToBr = goog.string.internal.newLineToBr;\n\n\n/**\n * Escapes double quote '\"' and single quote '\\'' characters in addition to\n * '&', '<', and '>' so that a string can be included in an HTML tag attribute\n * value within double or single quotes.\n *\n * It should be noted that > doesn't need to be escaped for the HTML or XML to\n * be valid, but it has been decided to escape it for consistency with other\n * implementations.\n *\n * With goog.string.DETECT_DOUBLE_ESCAPING, this function escapes also the\n * lowercase letter \"e\".\n *\n * NOTE(user):\n * HtmlEscape is often called during the generation of large blocks of HTML.\n * Using statics for the regular expressions and strings is an optimization\n * that can more than half the amount of time IE spends in this function for\n * large apps, since strings and regexes both contribute to GC allocations.\n *\n * Testing for the presence of a character before escaping increases the number\n * of function calls, but actually provides a speed increase for the average\n * case -- since the average case often doesn't require the escaping of all 4\n * characters and indexOf() is much cheaper than replace().\n * The worst case does suffer slightly from the additional calls, therefore the\n * opt_isLikelyToContainHtmlChars option has been included for situations\n * where all 4 HTML entities are very likely to be present and need escaping.\n *\n * Some benchmarks (times tended to fluctuate +-0.05ms):\n *                                     FireFox                     IE6\n * (no chars / average (mix of cases) / all 4 chars)\n * no checks                     0.13 / 0.22 / 0.22         0.23 / 0.53 / 0.80\n * indexOf                       0.08 / 0.17 / 0.26         0.22 / 0.54 / 0.84\n * indexOf + re test             0.07 / 0.17 / 0.28         0.19 / 0.50 / 0.85\n *\n * An additional advantage of checking if replace actually needs to be called\n * is a reduction in the number of object allocations, so as the size of the\n * application grows the difference between the various methods would increase.\n *\n * @param {string} str string to be escaped.\n * @param {boolean=} opt_isLikelyToContainHtmlChars Don't perform a check to see\n *     if the character needs replacing - use this option if you expect each of\n *     the characters to appear often. Leave false if you expect few html\n *     characters to occur in your strings, such as if you are escaping HTML.\n * @return {string} An escaped copy of `str`.\n */\ngoog.string.htmlEscape = function(str, opt_isLikelyToContainHtmlChars) {\n  'use strict';\n  str = goog.string.internal.htmlEscape(str, opt_isLikelyToContainHtmlChars);\n  if (goog.string.DETECT_DOUBLE_ESCAPING) {\n    str = str.replace(goog.string.E_RE_, '&#101;');\n  }\n  return str;\n};\n\n\n/**\n * Regular expression that matches a lowercase letter \"e\", for use in escaping.\n * @const {!RegExp}\n * @private\n */\ngoog.string.E_RE_ = /e/g;\n\n\n/**\n * Unescapes an HTML string.\n *\n * @param {string} str The string to unescape.\n * @return {string} An unescaped copy of `str`.\n */\ngoog.string.unescapeEntities = function(str) {\n  'use strict';\n  if (goog.string.contains(str, '&')) {\n    // We are careful not to use a DOM if we do not have one or we explicitly\n    // requested non-DOM html unescaping.\n    if (!goog.string.FORCE_NON_DOM_HTML_UNESCAPING &&\n        'document' in goog.global) {\n      return goog.string.unescapeEntitiesUsingDom_(str);\n    } else {\n      // Fall back on pure XML entities\n      return goog.string.unescapePureXmlEntities_(str);\n    }\n  }\n  return str;\n};\n\n\n/**\n * Unescapes a HTML string using the provided document.\n *\n * @param {string} str The string to unescape.\n * @param {!Document} document A document to use in escaping the string.\n * @return {string} An unescaped copy of `str`.\n */\ngoog.string.unescapeEntitiesWithDocument = function(str, document) {\n  'use strict';\n  if (goog.string.contains(str, '&')) {\n    return goog.string.unescapeEntitiesUsingDom_(str, document);\n  }\n  return str;\n};\n\n\n/**\n * Unescapes an HTML string using a DOM to resolve non-XML, non-numeric\n * entities. This function is XSS-safe and whitespace-preserving.\n * @private\n * @param {string} str The string to unescape.\n * @param {Document=} opt_document An optional document to use for creating\n *     elements. If this is not specified then the default window.document\n *     will be used.\n * @return {string} The unescaped `str` string.\n */\ngoog.string.unescapeEntitiesUsingDom_ = function(str, opt_document) {\n  'use strict';\n  /** @type {!Object<string, string>} */\n  var seen = {'&amp;': '&', '&lt;': '<', '&gt;': '>', '&quot;': '\"'};\n  /** @type {!Element} */\n  var div;\n  if (opt_document) {\n    div = opt_document.createElement('div');\n  } else {\n    div = goog.global.document.createElement('div');\n  }\n  // Match as many valid entity characters as possible. If the actual entity\n  // happens to be shorter, it will still work as innerHTML will return the\n  // trailing characters unchanged. Since the entity characters do not include\n  // open angle bracket, there is no chance of XSS from the innerHTML use.\n  // Since no whitespace is passed to innerHTML, whitespace is preserved.\n  return str.replace(goog.string.HTML_ENTITY_PATTERN_, function(s, entity) {\n    'use strict';\n    // Check for cached entity.\n    var value = seen[s];\n    if (value) {\n      return value;\n    }\n    // Check for numeric entity.\n    if (entity.charAt(0) == '#') {\n      // Prefix with 0 so that hex entities (e.g. &#x10) parse as hex numbers.\n      var n = Number('0' + entity.substr(1));\n      if (!isNaN(n)) {\n        value = String.fromCharCode(n);\n      }\n    }\n    // Fall back to innerHTML otherwise.\n    if (!value) {\n      // Append a non-entity character to avoid a bug in Webkit that parses\n      // an invalid entity at the end of innerHTML text as the empty string.\n      goog.dom.safe.setInnerHtml(\n          div,\n          goog.html.uncheckedconversions\n              .safeHtmlFromStringKnownToSatisfyTypeContract(\n                  goog.string.Const.from('Single HTML entity.'), s + ' '));\n      // Then remove the trailing character from the result.\n      value = div.firstChild.nodeValue.slice(0, -1);\n    }\n    // Cache and return.\n    return seen[s] = value;\n  });\n};\n\n\n/**\n * Unescapes XML entities.\n * @private\n * @param {string} str The string to unescape.\n * @return {string} An unescaped copy of `str`.\n */\ngoog.string.unescapePureXmlEntities_ = function(str) {\n  'use strict';\n  return str.replace(/&([^;]+);/g, function(s, entity) {\n    'use strict';\n    switch (entity) {\n      case 'amp':\n        return '&';\n      case 'lt':\n        return '<';\n      case 'gt':\n        return '>';\n      case 'quot':\n        return '\"';\n      default:\n        if (entity.charAt(0) == '#') {\n          // Prefix with 0 so that hex entities (e.g. &#x10) parse as hex.\n          var n = Number('0' + entity.substr(1));\n          if (!isNaN(n)) {\n            return String.fromCharCode(n);\n          }\n        }\n        // For invalid entities we just return the entity\n        return s;\n    }\n  });\n};\n\n\n/**\n * Regular expression that matches an HTML entity.\n * See also HTML5: Tokenization / Tokenizing character references.\n * @private\n * @type {!RegExp}\n */\ngoog.string.HTML_ENTITY_PATTERN_ = /&([^;\\s<&]+);?/g;\n\n\n/**\n * Do escaping of whitespace to preserve spatial formatting. We use character\n * entity #160 to make it safer for xml.\n * @param {string} str The string in which to escape whitespace.\n * @param {boolean=} opt_xml Whether to use XML compatible tags.\n * @return {string} An escaped copy of `str`.\n */\ngoog.string.whitespaceEscape = function(str, opt_xml) {\n  'use strict';\n  // This doesn't use goog.string.preserveSpaces for backwards compatibility.\n  return goog.string.newLineToBr(str.replace(/  /g, ' &#160;'), opt_xml);\n};\n\n\n/**\n * Preserve spaces that would be otherwise collapsed in HTML by replacing them\n * with non-breaking space Unicode characters.\n * @param {string} str The string in which to preserve whitespace.\n * @return {string} A copy of `str` with preserved whitespace.\n */\ngoog.string.preserveSpaces = function(str) {\n  'use strict';\n  return str.replace(/(^|[\\n ]) /g, '$1' + goog.string.Unicode.NBSP);\n};\n\n\n/**\n * Strip quote characters around a string.  The second argument is a string of\n * characters to treat as quotes.  This can be a single character or a string of\n * multiple character and in that case each of those are treated as possible\n * quote characters. For example:\n *\n * <pre>\n * goog.string.stripQuotes('\"abc\"', '\"`') --> 'abc'\n * goog.string.stripQuotes('`abc`', '\"`') --> 'abc'\n * </pre>\n *\n * @param {string} str The string to strip.\n * @param {string} quoteChars The quote characters to strip.\n * @return {string} A copy of `str` without the quotes.\n */\ngoog.string.stripQuotes = function(str, quoteChars) {\n  'use strict';\n  var length = quoteChars.length;\n  for (var i = 0; i < length; i++) {\n    var quoteChar = length == 1 ? quoteChars : quoteChars.charAt(i);\n    if (str.charAt(0) == quoteChar && str.charAt(str.length - 1) == quoteChar) {\n      return str.substring(1, str.length - 1);\n    }\n  }\n  return str;\n};\n\n\n/**\n * Truncates a string to a certain length and adds '...' if necessary.  The\n * length also accounts for the ellipsis, so a maximum length of 10 and a string\n * 'Hello World!' produces 'Hello W...'.\n * @param {string} str The string to truncate.\n * @param {number} chars Max number of characters.\n * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped\n *     characters from being cut off in the middle.\n * @return {string} The truncated `str` string.\n */\ngoog.string.truncate = function(str, chars, opt_protectEscapedCharacters) {\n  'use strict';\n  if (opt_protectEscapedCharacters) {\n    str = goog.string.unescapeEntities(str);\n  }\n\n  if (str.length > chars) {\n    str = str.substring(0, chars - 3) + '...';\n  }\n\n  if (opt_protectEscapedCharacters) {\n    str = goog.string.htmlEscape(str);\n  }\n\n  return str;\n};\n\n\n/**\n * Truncate a string in the middle, adding \"...\" if necessary,\n * and favoring the beginning of the string.\n * @param {string} str The string to truncate the middle of.\n * @param {number} chars Max number of characters.\n * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped\n *     characters from being cutoff in the middle.\n * @param {number=} opt_trailingChars Optional number of trailing characters to\n *     leave at the end of the string, instead of truncating as close to the\n *     middle as possible.\n * @return {string} A truncated copy of `str`.\n */\ngoog.string.truncateMiddle = function(\n    str, chars, opt_protectEscapedCharacters, opt_trailingChars) {\n  'use strict';\n  if (opt_protectEscapedCharacters) {\n    str = goog.string.unescapeEntities(str);\n  }\n\n  if (opt_trailingChars && str.length > chars) {\n    if (opt_trailingChars > chars) {\n      opt_trailingChars = chars;\n    }\n    var endPoint = str.length - opt_trailingChars;\n    var startPoint = chars - opt_trailingChars;\n    str = str.substring(0, startPoint) + '...' + str.substring(endPoint);\n  } else if (str.length > chars) {\n    // Favor the beginning of the string:\n    var half = Math.floor(chars / 2);\n    var endPos = str.length - half;\n    half += chars % 2;\n    str = str.substring(0, half) + '...' + str.substring(endPos);\n  }\n\n  if (opt_protectEscapedCharacters) {\n    str = goog.string.htmlEscape(str);\n  }\n\n  return str;\n};\n\n\n/**\n * Special chars that need to be escaped for goog.string.quote.\n * @private {!Object<string, string>}\n */\ngoog.string.specialEscapeChars_ = {\n  '\\0': '\\\\0',\n  '\\b': '\\\\b',\n  '\\f': '\\\\f',\n  '\\n': '\\\\n',\n  '\\r': '\\\\r',\n  '\\t': '\\\\t',\n  '\\x0B': '\\\\x0B',  // '\\v' is not supported in JScript\n  '\"': '\\\\\"',\n  '\\\\': '\\\\\\\\',\n  // To support the use case of embedding quoted strings inside of script\n  // tags, we have to make sure HTML comments and opening/closing script tags do\n  // not appear in the resulting string. The specific strings that must be\n  // escaped are documented at:\n  // https://html.spec.whatwg.org/multipage/scripting.html#restrictions-for-contents-of-script-elements\n  '<': '\\\\u003C'  // NOTE: JSON.parse crashes on '\\\\x3c'.\n};\n\n\n/**\n * Character mappings used internally for goog.string.escapeChar.\n * @private {!Object<string, string>}\n */\ngoog.string.jsEscapeCache_ = {\n  '\\'': '\\\\\\''\n};\n\n\n/**\n * Encloses a string in double quotes and escapes characters so that the\n * string is a valid JS string. The resulting string is safe to embed in\n * `<script>` tags as \"<\" is escaped.\n * @param {string} s The string to quote.\n * @return {string} A copy of `s` surrounded by double quotes.\n */\ngoog.string.quote = function(s) {\n  'use strict';\n  s = String(s);\n  var sb = ['\"'];\n  for (var i = 0; i < s.length; i++) {\n    var ch = s.charAt(i);\n    var cc = ch.charCodeAt(0);\n    sb[i + 1] = goog.string.specialEscapeChars_[ch] ||\n        ((cc > 31 && cc < 127) ? ch : goog.string.escapeChar(ch));\n  }\n  sb.push('\"');\n  return sb.join('');\n};\n\n\n/**\n * Takes a string and returns the escaped string for that input string.\n * @param {string} str The string to escape.\n * @return {string} An escaped string representing `str`.\n */\ngoog.string.escapeString = function(str) {\n  'use strict';\n  var sb = [];\n  for (var i = 0; i < str.length; i++) {\n    sb[i] = goog.string.escapeChar(str.charAt(i));\n  }\n  return sb.join('');\n};\n\n\n/**\n * Takes a character and returns the escaped string for that character. For\n * example escapeChar(String.fromCharCode(15)) -> \"\\\\x0E\".\n * @param {string} c The character to escape.\n * @return {string} An escaped string representing `c`.\n */\ngoog.string.escapeChar = function(c) {\n  'use strict';\n  if (c in goog.string.jsEscapeCache_) {\n    return goog.string.jsEscapeCache_[c];\n  }\n\n  if (c in goog.string.specialEscapeChars_) {\n    return goog.string.jsEscapeCache_[c] = goog.string.specialEscapeChars_[c];\n  }\n\n  var rv = c;\n  var cc = c.charCodeAt(0);\n  if (cc > 31 && cc < 127) {\n    rv = c;\n  } else {\n    // tab is 9 but handled above\n    if (cc < 256) {\n      rv = '\\\\x';\n      if (cc < 16 || cc > 256) {\n        rv += '0';\n      }\n    } else {\n      rv = '\\\\u';\n      if (cc < 4096) {  // \\u1000\n        rv += '0';\n      }\n    }\n    rv += cc.toString(16).toUpperCase();\n  }\n\n  return goog.string.jsEscapeCache_[c] = rv;\n};\n\n\n/**\n * Determines whether a string contains a substring.\n * @param {string} str The string to search.\n * @param {string} subString The substring to search for.\n * @return {boolean} Whether `str` contains `subString`.\n */\ngoog.string.contains = goog.string.internal.contains;\n\n\n/**\n * Determines whether a string contains a substring, ignoring case.\n * @param {string} str The string to search.\n * @param {string} subString The substring to search for.\n * @return {boolean} Whether `str` contains `subString`.\n */\ngoog.string.caseInsensitiveContains =\n    goog.string.internal.caseInsensitiveContains;\n\n\n/**\n * Returns the non-overlapping occurrences of ss in s.\n * If either s or ss evalutes to false, then returns zero.\n * @param {string} s The string to look in.\n * @param {string} ss The string to look for.\n * @return {number} Number of occurrences of ss in s.\n */\ngoog.string.countOf = function(s, ss) {\n  'use strict';\n  return s && ss ? s.split(ss).length - 1 : 0;\n};\n\n\n/**\n * Removes a substring of a specified length at a specific\n * index in a string.\n * @param {string} s The base string from which to remove.\n * @param {number} index The index at which to remove the substring.\n * @param {number} stringLength The length of the substring to remove.\n * @return {string} A copy of `s` with the substring removed or the full\n *     string if nothing is removed or the input is invalid.\n */\ngoog.string.removeAt = function(s, index, stringLength) {\n  'use strict';\n  var resultStr = s;\n  // If the index is greater or equal to 0 then remove substring\n  if (index >= 0 && index < s.length && stringLength > 0) {\n    resultStr = s.substr(0, index) +\n        s.substr(index + stringLength, s.length - index - stringLength);\n  }\n  return resultStr;\n};\n\n\n/**\n * Removes the first occurrence of a substring from a string.\n * @param {string} str The base string from which to remove.\n * @param {string} substr The string to remove.\n * @return {string} A copy of `str` with `substr` removed or the\n *     full string if nothing is removed.\n */\ngoog.string.remove = function(str, substr) {\n  'use strict';\n  return str.replace(substr, '');\n};\n\n\n/**\n *  Removes all occurrences of a substring from a string.\n *  @param {string} s The base string from which to remove.\n *  @param {string} ss The string to remove.\n *  @return {string} A copy of `s` with `ss` removed or the full\n *      string if nothing is removed.\n */\ngoog.string.removeAll = function(s, ss) {\n  'use strict';\n  var re = new RegExp(goog.string.regExpEscape(ss), 'g');\n  return s.replace(re, '');\n};\n\n\n/**\n *  Replaces all occurrences of a substring of a string with a new substring.\n *  @param {string} s The base string from which to remove.\n *  @param {string} ss The string to replace.\n *  @param {string} replacement The replacement string.\n *  @return {string} A copy of `s` with `ss` replaced by\n *      `replacement` or the original string if nothing is replaced.\n */\ngoog.string.replaceAll = function(s, ss, replacement) {\n  'use strict';\n  var re = new RegExp(goog.string.regExpEscape(ss), 'g');\n  return s.replace(re, replacement.replace(/\\$/g, '$$$$'));\n};\n\n\n/**\n * Escapes characters in the string that are not safe to use in a RegExp.\n * @param {*} s The string to escape. If not a string, it will be casted\n *     to one.\n * @return {string} A RegExp safe, escaped copy of `s`.\n */\ngoog.string.regExpEscape = function(s) {\n  'use strict';\n  return String(s)\n      .replace(/([-()\\[\\]{}+?*.$\\^|,:#<!\\\\])/g, '\\\\$1')\n      .replace(/\\x08/g, '\\\\x08');\n};\n\n\n/**\n * Repeats a string n times.\n * @param {string} string The string to repeat.\n * @param {number} length The number of times to repeat.\n * @return {string} A string containing `length` repetitions of\n *     `string`.\n */\ngoog.string.repeat = (String.prototype.repeat) ? function(string, length) {\n  'use strict';\n  // The native method is over 100 times faster than the alternative.\n  return string.repeat(length);\n} : function(string, length) {\n  'use strict';\n  return new Array(length + 1).join(string);\n};\n\n\n/**\n * Pads number to given length and optionally rounds it to a given precision.\n * For example:\n * <pre>padNumber(1.25, 2, 3) -> '01.250'\n * padNumber(1.25, 2) -> '01.25'\n * padNumber(1.25, 2, 1) -> '01.3'\n * padNumber(1.25, 0) -> '1.25'</pre>\n *\n * @param {number} num The number to pad.\n * @param {number} length The desired length.\n * @param {number=} opt_precision The desired precision.\n * @return {string} `num` as a string with the given options.\n */\ngoog.string.padNumber = function(num, length, opt_precision) {\n  'use strict';\n  var s =\n      (opt_precision !== undefined) ? num.toFixed(opt_precision) : String(num);\n  var index = s.indexOf('.');\n  if (index == -1) {\n    index = s.length;\n  }\n  return goog.string.repeat('0', Math.max(0, length - index)) + s;\n};\n\n\n/**\n * Returns a string representation of the given object, with\n * null and undefined being returned as the empty string.\n *\n * @param {*} obj The object to convert.\n * @return {string} A string representation of the `obj`.\n */\ngoog.string.makeSafe = function(obj) {\n  'use strict';\n  return obj == null ? '' : String(obj);\n};\n\n\n/**\n * Concatenates string expressions. This is useful\n * since some browsers are very inefficient when it comes to using plus to\n * concat strings. Be careful when using null and undefined here since\n * these will not be included in the result. If you need to represent these\n * be sure to cast the argument to a String first.\n * For example:\n * <pre>buildString('a', 'b', 'c', 'd') -> 'abcd'\n * buildString(null, undefined) -> ''\n * </pre>\n * @param {...*} var_args A list of strings to concatenate. If not a string,\n *     it will be casted to one.\n * @return {string} The concatenation of `var_args`.\n */\ngoog.string.buildString = function(var_args) {\n  'use strict';\n  return Array.prototype.join.call(arguments, '');\n};\n\n\n/**\n * Returns a string with at least 64-bits of randomness.\n *\n * Doesn't trust JavaScript's random function entirely. Uses a combination of\n * random and current timestamp, and then encodes the string in base-36 to\n * make it shorter.\n *\n * @return {string} A random string, e.g. sn1s7vb4gcic.\n */\ngoog.string.getRandomString = function() {\n  'use strict';\n  var x = 2147483648;\n  return Math.floor(Math.random() * x).toString(36) +\n      Math.abs(Math.floor(Math.random() * x) ^ goog.now()).toString(36);\n};\n\n\n/**\n * Compares two version numbers.\n *\n * @param {string|number} version1 Version of first item.\n * @param {string|number} version2 Version of second item.\n *\n * @return {number}  1 if `version1` is higher.\n *                   0 if arguments are equal.\n *                  -1 if `version2` is higher.\n */\ngoog.string.compareVersions = goog.string.internal.compareVersions;\n\n\n/**\n * String hash function similar to java.lang.String.hashCode().\n * The hash code for a string is computed as\n * s[0] * 31 ^ (n - 1) + s[1] * 31 ^ (n - 2) + ... + s[n - 1],\n * where s[i] is the ith character of the string and n is the length of\n * the string. We mod the result to make it between 0 (inclusive) and 2^32\n * (exclusive).\n * @param {string} str A string.\n * @return {number} Hash value for `str`, between 0 (inclusive) and 2^32\n *  (exclusive). The empty string returns 0.\n */\ngoog.string.hashCode = function(str) {\n  'use strict';\n  var result = 0;\n  for (var i = 0; i < str.length; ++i) {\n    // Normalize to 4 byte range, 0 ... 2^32.\n    result = (31 * result + str.charCodeAt(i)) >>> 0;\n  }\n  return result;\n};\n\n\n/**\n * The most recent unique ID. |0 is equivalent to Math.floor in this case.\n * @type {number}\n * @private\n */\ngoog.string.uniqueStringCounter_ = Math.random() * 0x80000000 | 0;\n\n\n/**\n * Generates and returns a string which is unique in the current document.\n * This is useful, for example, to create unique IDs for DOM elements.\n * @return {string} A unique id.\n */\ngoog.string.createUniqueString = function() {\n  'use strict';\n  return 'goog_' + goog.string.uniqueStringCounter_++;\n};\n\n\n/**\n * Converts the supplied string to a number, which may be Infinity or NaN.\n * This function strips whitespace: (toNumber(' 123') === 123)\n * This function accepts scientific notation: (toNumber('1e1') === 10)\n *\n * This is better than JavaScript's built-in conversions because, sadly:\n *     (Number(' ') === 0) and (parseFloat('123a') === 123)\n *\n * @param {string} str The string to convert.\n * @return {number} The number the supplied string represents, or NaN.\n */\ngoog.string.toNumber = function(str) {\n  'use strict';\n  var num = Number(str);\n  if (num == 0 && goog.string.isEmptyOrWhitespace(str)) {\n    return NaN;\n  }\n  return num;\n};\n\n\n/**\n * Returns whether the given string is lower camel case (e.g. \"isFooBar\").\n *\n * Note that this assumes the string is entirely letters.\n * @see http://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms\n *\n * @param {string} str String to test.\n * @return {boolean} Whether the string is lower camel case.\n */\ngoog.string.isLowerCamelCase = function(str) {\n  'use strict';\n  return /^[a-z]+([A-Z][a-z]*)*$/.test(str);\n};\n\n\n/**\n * Returns whether the given string is upper camel case (e.g. \"FooBarBaz\").\n *\n * Note that this assumes the string is entirely letters.\n * @see http://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms\n *\n * @param {string} str String to test.\n * @return {boolean} Whether the string is upper camel case.\n */\ngoog.string.isUpperCamelCase = function(str) {\n  'use strict';\n  return /^([A-Z][a-z]*)+$/.test(str);\n};\n\n\n/**\n * Converts a string from selector-case to camelCase (e.g. from\n * \"multi-part-string\" to \"multiPartString\"), useful for converting\n * CSS selectors and HTML dataset keys to their equivalent JS properties.\n * @param {string} str The string in selector-case form.\n * @return {string} The string in camelCase form.\n */\ngoog.string.toCamelCase = function(str) {\n  'use strict';\n  return String(str).replace(/\\-([a-z])/g, function(all, match) {\n    'use strict';\n    return match.toUpperCase();\n  });\n};\n\n\n/**\n * Converts a string from camelCase to selector-case (e.g. from\n * \"multiPartString\" to \"multi-part-string\"), useful for converting JS\n * style and dataset properties to equivalent CSS selectors and HTML keys.\n * @param {string} str The string in camelCase form.\n * @return {string} The string in selector-case form.\n */\ngoog.string.toSelectorCase = function(str) {\n  'use strict';\n  return String(str).replace(/([A-Z])/g, '-$1').toLowerCase();\n};\n\n\n/**\n * Converts a string into TitleCase. First character of the string is always\n * capitalized in addition to the first letter of every subsequent word.\n * Words are delimited by one or more whitespaces by default. Custom delimiters\n * can optionally be specified to replace the default, which doesn't preserve\n * whitespace delimiters and instead must be explicitly included if needed.\n *\n * Default delimiter => \" \":\n *    goog.string.toTitleCase('oneTwoThree')    => 'OneTwoThree'\n *    goog.string.toTitleCase('one two three')  => 'One Two Three'\n *    goog.string.toTitleCase('  one   two   ') => '  One   Two   '\n *    goog.string.toTitleCase('one_two_three')  => 'One_two_three'\n *    goog.string.toTitleCase('one-two-three')  => 'One-two-three'\n *\n * Custom delimiter => \"_-.\":\n *    goog.string.toTitleCase('oneTwoThree', '_-.')       => 'OneTwoThree'\n *    goog.string.toTitleCase('one two three', '_-.')     => 'One two three'\n *    goog.string.toTitleCase('  one   two   ', '_-.')    => '  one   two   '\n *    goog.string.toTitleCase('one_two_three', '_-.')     => 'One_Two_Three'\n *    goog.string.toTitleCase('one-two-three', '_-.')     => 'One-Two-Three'\n *    goog.string.toTitleCase('one...two...three', '_-.') => 'One...Two...Three'\n *    goog.string.toTitleCase('one. two. three', '_-.')   => 'One. two. three'\n *    goog.string.toTitleCase('one-two.three', '_-.')     => 'One-Two.Three'\n *\n * @param {string} str String value in camelCase form.\n * @param {string=} opt_delimiters Custom delimiter character set used to\n *      distinguish words in the string value. Each character represents a\n *      single delimiter. When provided, default whitespace delimiter is\n *      overridden and must be explicitly included if needed.\n * @return {string} String value in TitleCase form.\n */\ngoog.string.toTitleCase = function(str, opt_delimiters) {\n  'use strict';\n  var delimiters = (typeof opt_delimiters === 'string') ?\n      goog.string.regExpEscape(opt_delimiters) :\n      '\\\\s';\n\n  // For IE8, we need to prevent using an empty character set. Otherwise,\n  // incorrect matching will occur.\n  delimiters = delimiters ? '|[' + delimiters + ']+' : '';\n\n  var regexp = new RegExp('(^' + delimiters + ')([a-z])', 'g');\n  return str.replace(regexp, function(all, p1, p2) {\n    'use strict';\n    return p1 + p2.toUpperCase();\n  });\n};\n\n\n/**\n * Capitalizes a string, i.e. converts the first letter to uppercase\n * and all other letters to lowercase, e.g.:\n *\n * goog.string.capitalize('one')     => 'One'\n * goog.string.capitalize('ONE')     => 'One'\n * goog.string.capitalize('one two') => 'One two'\n *\n * Note that this function does not trim initial whitespace.\n *\n * @param {string} str String value to capitalize.\n * @return {string} String value with first letter in uppercase.\n */\ngoog.string.capitalize = function(str) {\n  'use strict';\n  return String(str.charAt(0)).toUpperCase() +\n      String(str.substr(1)).toLowerCase();\n};\n\n\n/**\n * Parse a string in decimal or hexidecimal ('0xFFFF') form.\n *\n * To parse a particular radix, please use parseInt(string, radix) directly. See\n * https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/parseInt\n *\n * This is a wrapper for the built-in parseInt function that will only parse\n * numbers as base 10 or base 16.  Some JS implementations assume strings\n * starting with \"0\" are intended to be octal. ES3 allowed but discouraged\n * this behavior. ES5 forbids it.  This function emulates the ES5 behavior.\n *\n * For more information, see Mozilla JS Reference: http://goo.gl/8RiFj\n *\n * @param {string|number|null|undefined} value The value to be parsed.\n * @return {number} The number, parsed. If the string failed to parse, this\n *     will be NaN.\n */\ngoog.string.parseInt = function(value) {\n  'use strict';\n  // Force finite numbers to strings.\n  if (isFinite(value)) {\n    value = String(value);\n  }\n\n  if (typeof value === 'string') {\n    // If the string starts with '0x' or '-0x', parse as hex.\n    return /^\\s*-?0x/i.test(value) ? parseInt(value, 16) : parseInt(value, 10);\n  }\n\n  return NaN;\n};\n\n\n/**\n * Splits a string on a separator a limited number of times.\n *\n * This implementation is more similar to Python or Java, where the limit\n * parameter specifies the maximum number of splits rather than truncating\n * the number of results.\n *\n * See http://docs.python.org/2/library/stdtypes.html#str.split\n * See JavaDoc: http://goo.gl/F2AsY\n * See Mozilla reference: http://goo.gl/dZdZs\n *\n * @param {string} str String to split.\n * @param {string} separator The separator.\n * @param {number} limit The limit to the number of splits. The resulting array\n *     will have a maximum length of limit+1.  Negative numbers are the same\n *     as zero.\n * @return {!Array<string>} The string, split.\n */\ngoog.string.splitLimit = function(str, separator, limit) {\n  'use strict';\n  var parts = str.split(separator);\n  var returnVal = [];\n\n  // Only continue doing this while we haven't hit the limit and we have\n  // parts left.\n  while (limit > 0 && parts.length) {\n    returnVal.push(parts.shift());\n    limit--;\n  }\n\n  // If there are remaining parts, append them to the end.\n  if (parts.length) {\n    returnVal.push(parts.join(separator));\n  }\n\n  return returnVal;\n};\n\n\n/**\n * Finds the characters to the right of the last instance of any separator\n *\n * This function is similar to goog.string.path.baseName, except it can take a\n * list of characters to split the string on. It will return the rightmost\n * grouping of characters to the right of any separator as a left-to-right\n * oriented string.\n *\n * @see goog.string.path.baseName\n * @param {string} str The string\n * @param {string|!Array<string>} separators A list of separator characters\n * @return {string} The last part of the string with respect to the separators\n */\ngoog.string.lastComponent = function(str, separators) {\n  'use strict';\n  if (!separators) {\n    return str;\n  } else if (typeof separators == 'string') {\n    separators = [separators];\n  }\n\n  var lastSeparatorIndex = -1;\n  for (var i = 0; i < separators.length; i++) {\n    if (separators[i] == '') {\n      continue;\n    }\n    var currentSeparatorIndex = str.lastIndexOf(separators[i]);\n    if (currentSeparatorIndex > lastSeparatorIndex) {\n      lastSeparatorIndex = currentSeparatorIndex;\n    }\n  }\n  if (lastSeparatorIndex == -1) {\n    return str;\n  }\n  return str.slice(lastSeparatorIndex + 1);\n};\n\n\n/**\n * Computes the Levenshtein edit distance between two strings.\n * @param {string} a\n * @param {string} b\n * @return {number} The edit distance between the two strings.\n */\ngoog.string.editDistance = function(a, b) {\n  'use strict';\n  var v0 = [];\n  var v1 = [];\n\n  if (a == b) {\n    return 0;\n  }\n\n  if (!a.length || !b.length) {\n    return Math.max(a.length, b.length);\n  }\n\n  for (var i = 0; i < b.length + 1; i++) {\n    v0[i] = i;\n  }\n\n  for (var i = 0; i < a.length; i++) {\n    v1[0] = i + 1;\n\n    for (var j = 0; j < b.length; j++) {\n      var cost = Number(a[i] != b[j]);\n      // Cost for the substring is the minimum of adding one character, removing\n      // one character, or a swap.\n      v1[j + 1] = Math.min(v1[j] + 1, v0[j + 1] + 1, v0[j] + cost);\n    }\n\n    for (var j = 0; j < v0.length; j++) {\n      v0[j] = v1[j];\n    }\n  }\n\n  return v1[b.length];\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Useful compiler idioms.\n */\n\ngoog.provide('goog.reflect');\n\n\n/**\n * Syntax for object literal casts.\n * @see http://go/jscompiler-renaming\n * @see https://goo.gl/CRs09P\n *\n * Use this if you have an object literal whose keys need to have the same names\n * as the properties of some class even after they are renamed by the compiler.\n *\n * @param {!Function} type Type to cast to.\n * @param {Object} object Object literal to cast.\n * @return {Object} The object literal.\n */\ngoog.reflect.object = function(type, object) {\n  return object;\n};\n\n/**\n * Syntax for renaming property strings.\n * @see http://go/jscompiler-renaming\n * @see https://goo.gl/CRs09P\n *\n * Use this if you have an need to access a property as a string, but want\n * to also have the property renamed by the compiler. In contrast to\n * goog.reflect.object, this method takes an instance of an object.\n *\n * Properties must be simple names (not qualified names).\n *\n * @param {string} prop Name of the property\n * @param {!Object} object Instance of the object whose type will be used\n *     for renaming\n * @return {string} The renamed property.\n */\ngoog.reflect.objectProperty = function(prop, object) {\n  return prop;\n};\n\n/**\n * To assert to the compiler that an operation is needed when it would\n * otherwise be stripped. For example:\n * <code>\n *     // Force a layout\n *     goog.reflect.sinkValue(dialog.offsetHeight);\n * </code>\n * @param {T} x\n * @return {T}\n * @template T\n */\ngoog.reflect.sinkValue = function(x) {\n  goog.reflect.sinkValue[' '](x);\n  return x;\n};\n\n\n/**\n * The compiler should optimize this function away iff no one ever uses\n * goog.reflect.sinkValue.\n */\ngoog.reflect.sinkValue[' '] = goog.nullFunction;\n\n\n/**\n * Check if a property can be accessed without throwing an exception.\n * @param {Object} obj The owner of the property.\n * @param {string} prop The property name.\n * @return {boolean} Whether the property is accessible. Will also return true\n *     if obj is null.\n */\ngoog.reflect.canAccessProperty = function(obj, prop) {\n\n  try {\n    goog.reflect.sinkValue(obj[prop]);\n    return true;\n  } catch (e) {\n  }\n  return false;\n};\n\n\n/**\n * Retrieves a value from a cache given a key. The compiler provides special\n * consideration for this call such that it is generally considered side-effect\n * free. However, if the `opt_keyFn` or `valueFn` have side-effects\n * then the entire call is considered to have side-effects.\n *\n * Conventionally storing the value on the cache would be considered a\n * side-effect and preclude unused calls from being pruned, ie. even if\n * the value was never used, it would still always be stored in the cache.\n *\n * Providing a side-effect free `valueFn` and `opt_keyFn`\n * allows unused calls to `goog.reflect.cache` to be pruned.\n *\n * @param {!Object<K, V>} cacheObj The object that contains the cached values.\n * @param {?} key The key to lookup in the cache. If it is not string or number\n *     then a `opt_keyFn` should be provided. The key is also used as the\n *     parameter to the `valueFn`.\n * @param {function(?):V} valueFn The value provider to use to calculate the\n *     value to store in the cache. This function should be side-effect free\n *     to take advantage of the optimization.\n * @param {function(?):K=} opt_keyFn The key provider to determine the cache\n *     map key. This should be used if the given key is not a string or number.\n *     If not provided then the given key is used. This function should be\n *     side-effect free to take advantage of the optimization.\n * @return {V} The cached or calculated value.\n * @template K\n * @template V\n */\ngoog.reflect.cache = function(cacheObj, key, valueFn, opt_keyFn) {\n  const storedKey = opt_keyFn ? opt_keyFn(key) : key;\n\n  if (Object.prototype.hasOwnProperty.call(cacheObj, storedKey)) {\n    return cacheObj[storedKey];\n  }\n\n  return (cacheObj[storedKey] = valueFn(key));\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Rendering engine detection.\n * @see <a href=\"http://www.useragentstring.com/\">User agent strings</a>\n * For information on the browser brand (such as Safari versus Chrome), see\n * goog.userAgent.product.\n * @see ../demos/useragent.html\n */\n\ngoog.provide('goog.userAgent');\n\ngoog.require('goog.labs.userAgent.browser');\ngoog.require('goog.labs.userAgent.engine');\ngoog.require('goog.labs.userAgent.platform');\ngoog.require('goog.labs.userAgent.util');\ngoog.require('goog.reflect');\ngoog.require('goog.string');\n\n\n/**\n * @define {boolean} Whether we know at compile-time that the browser is IE.\n */\ngoog.userAgent.ASSUME_IE = goog.define('goog.userAgent.ASSUME_IE', false);\n\n\n/**\n * @define {boolean} Whether we know at compile-time that the browser is EDGE,\n * referring to EdgeHTML based Edge.\n */\ngoog.userAgent.ASSUME_EDGE = goog.define('goog.userAgent.ASSUME_EDGE', false);\n\n\n/**\n * @define {boolean} Whether we know at compile-time that the browser is GECKO.\n */\ngoog.userAgent.ASSUME_GECKO = goog.define('goog.userAgent.ASSUME_GECKO', false);\n\n\n/**\n * @define {boolean} Whether we know at compile-time that the browser is WEBKIT.\n */\ngoog.userAgent.ASSUME_WEBKIT =\n    goog.define('goog.userAgent.ASSUME_WEBKIT', false);\n\n\n/**\n * @define {boolean} Whether we know at compile-time that the browser is a\n *     mobile device running WebKit e.g. iPhone or Android.\n */\ngoog.userAgent.ASSUME_MOBILE_WEBKIT =\n    goog.define('goog.userAgent.ASSUME_MOBILE_WEBKIT', false);\n\n\n/**\n * @define {boolean} Whether we know at compile-time that the browser is OPERA,\n * referring to Presto-based Opera.\n */\ngoog.userAgent.ASSUME_OPERA = goog.define('goog.userAgent.ASSUME_OPERA', false);\n\n\n/**\n * @define {boolean} Whether the\n *     `goog.userAgent.isVersionOrHigher`\n *     function will return true for any version.\n */\ngoog.userAgent.ASSUME_ANY_VERSION =\n    goog.define('goog.userAgent.ASSUME_ANY_VERSION', false);\n\n\n/**\n * Whether we know the browser engine at compile-time.\n * @type {boolean}\n * @private\n */\ngoog.userAgent.BROWSER_KNOWN_ = goog.userAgent.ASSUME_IE ||\n    goog.userAgent.ASSUME_EDGE || goog.userAgent.ASSUME_GECKO ||\n    goog.userAgent.ASSUME_MOBILE_WEBKIT || goog.userAgent.ASSUME_WEBKIT ||\n    goog.userAgent.ASSUME_OPERA;\n\n\n/**\n * Returns the userAgent string for the current browser.\n *\n * @return {string} The userAgent string.\n */\ngoog.userAgent.getUserAgentString = function() {\n  return goog.labs.userAgent.util.getUserAgent();\n};\n\n\n/**\n * @return {?Navigator} The native navigator object.\n */\ngoog.userAgent.getNavigatorTyped = function() {\n  // Need a local navigator reference instead of using the global one,\n  // to avoid the rare case where they reference different objects.\n  // (in a WorkerPool, for example).\n  return goog.global['navigator'] || null;\n};\n\n\n/**\n * TODO(nnaze): Change type to \"Navigator\" and update compilation targets.\n * @return {?Object} The native navigator object.\n */\ngoog.userAgent.getNavigator = function() {\n  return goog.userAgent.getNavigatorTyped();\n};\n\n\n/**\n * Whether the user agent is Presto-based Opera.\n * @type {boolean}\n */\ngoog.userAgent.OPERA = goog.userAgent.BROWSER_KNOWN_ ?\n    goog.userAgent.ASSUME_OPERA :\n    goog.labs.userAgent.browser.isOpera();\n\n\n/**\n * Whether the user agent is Internet Explorer.\n * @type {boolean}\n */\ngoog.userAgent.IE = goog.userAgent.BROWSER_KNOWN_ ?\n    goog.userAgent.ASSUME_IE :\n    goog.labs.userAgent.browser.isIE();\n\n\n/**\n * Whether the user agent is Microsoft Edge (EdgeHTML based).\n * @type {boolean}\n */\ngoog.userAgent.EDGE = goog.userAgent.BROWSER_KNOWN_ ?\n    goog.userAgent.ASSUME_EDGE :\n    goog.labs.userAgent.engine.isEdge();\n\n\n/**\n * Whether the user agent is MS Internet Explorer or MS Edge (EdgeHTML based).\n * @type {boolean}\n */\ngoog.userAgent.EDGE_OR_IE = goog.userAgent.EDGE || goog.userAgent.IE;\n\n\n/**\n * Whether the user agent is Gecko. Gecko is the rendering engine used by\n * Mozilla, Firefox, and others.\n * @type {boolean}\n */\ngoog.userAgent.GECKO = goog.userAgent.BROWSER_KNOWN_ ?\n    goog.userAgent.ASSUME_GECKO :\n    goog.labs.userAgent.engine.isGecko();\n\n\n/**\n * Whether the user agent is WebKit. WebKit is the rendering engine that\n * Safari, Edge Chromium, Opera Chromium, Android and others use.\n * @type {boolean}\n */\ngoog.userAgent.WEBKIT = goog.userAgent.BROWSER_KNOWN_ ?\n    goog.userAgent.ASSUME_WEBKIT || goog.userAgent.ASSUME_MOBILE_WEBKIT :\n    goog.labs.userAgent.engine.isWebKit();\n\n\n/**\n * Whether the user agent is running on a mobile device.\n *\n * This is a separate function so that the logic can be tested.\n *\n * TODO(nnaze): Investigate swapping in goog.labs.userAgent.device.isMobile().\n *\n * @return {boolean} Whether the user agent is running on a mobile device.\n * @private\n */\ngoog.userAgent.isMobile_ = function() {\n  return goog.userAgent.WEBKIT &&\n      goog.labs.userAgent.util.matchUserAgent('Mobile');\n};\n\n\n/**\n * Whether the user agent is running on a mobile device.\n *\n * TODO(nnaze): Consider deprecating MOBILE when labs.userAgent\n *   is promoted as the gecko/webkit logic is likely inaccurate.\n *\n * @type {boolean}\n */\ngoog.userAgent.MOBILE =\n    goog.userAgent.ASSUME_MOBILE_WEBKIT || goog.userAgent.isMobile_();\n\n\n/**\n * Used while transitioning code to use WEBKIT instead.\n * @type {boolean}\n * @deprecated Use {@link goog.userAgent.product.SAFARI} instead.\n * TODO(nicksantos): Delete this from goog.userAgent.\n */\ngoog.userAgent.SAFARI = goog.userAgent.WEBKIT;\n\n\n/**\n * @return {string} the platform (operating system) the user agent is running\n *     on. Default to empty string because navigator.platform may not be defined\n *     (on Rhino, for example).\n * @private\n */\ngoog.userAgent.determinePlatform_ = function() {\n  var navigator = goog.userAgent.getNavigatorTyped();\n  return navigator && navigator.platform || '';\n};\n\n\n/**\n * The platform (operating system) the user agent is running on. Default to\n * empty string because navigator.platform may not be defined (on Rhino, for\n * example).\n * @type {string}\n */\ngoog.userAgent.PLATFORM = goog.userAgent.determinePlatform_();\n\n\n/**\n * @define {boolean} Whether the user agent is running on a Macintosh operating\n *     system.\n */\ngoog.userAgent.ASSUME_MAC = goog.define('goog.userAgent.ASSUME_MAC', false);\n\n\n/**\n * @define {boolean} Whether the user agent is running on a Windows operating\n *     system.\n */\ngoog.userAgent.ASSUME_WINDOWS =\n    goog.define('goog.userAgent.ASSUME_WINDOWS', false);\n\n\n/**\n * @define {boolean} Whether the user agent is running on a Linux operating\n *     system.\n */\ngoog.userAgent.ASSUME_LINUX = goog.define('goog.userAgent.ASSUME_LINUX', false);\n\n\n/**\n * @define {boolean} Whether the user agent is running on a X11 windowing\n *     system.\n */\ngoog.userAgent.ASSUME_X11 = goog.define('goog.userAgent.ASSUME_X11', false);\n\n\n/**\n * @define {boolean} Whether the user agent is running on Android.\n */\ngoog.userAgent.ASSUME_ANDROID =\n    goog.define('goog.userAgent.ASSUME_ANDROID', false);\n\n\n/**\n * @define {boolean} Whether the user agent is running on an iPhone.\n */\ngoog.userAgent.ASSUME_IPHONE =\n    goog.define('goog.userAgent.ASSUME_IPHONE', false);\n\n\n/**\n * @define {boolean} Whether the user agent is running on an iPad.\n */\ngoog.userAgent.ASSUME_IPAD = goog.define('goog.userAgent.ASSUME_IPAD', false);\n\n\n/**\n * @define {boolean} Whether the user agent is running on an iPod.\n */\ngoog.userAgent.ASSUME_IPOD = goog.define('goog.userAgent.ASSUME_IPOD', false);\n\n\n/**\n * @define {boolean} Whether the user agent is running on KaiOS.\n */\ngoog.userAgent.ASSUME_KAIOS = goog.define('goog.userAgent.ASSUME_KAIOS', false);\n\n\n/**\n * @type {boolean}\n * @private\n */\ngoog.userAgent.PLATFORM_KNOWN_ = goog.userAgent.ASSUME_MAC ||\n    goog.userAgent.ASSUME_WINDOWS || goog.userAgent.ASSUME_LINUX ||\n    goog.userAgent.ASSUME_X11 || goog.userAgent.ASSUME_ANDROID ||\n    goog.userAgent.ASSUME_IPHONE || goog.userAgent.ASSUME_IPAD ||\n    goog.userAgent.ASSUME_IPOD;\n\n\n/**\n * Whether the user agent is running on a Macintosh operating system.\n * @type {boolean}\n */\ngoog.userAgent.MAC = goog.userAgent.PLATFORM_KNOWN_ ?\n    goog.userAgent.ASSUME_MAC :\n    goog.labs.userAgent.platform.isMacintosh();\n\n\n/**\n * Whether the user agent is running on a Windows operating system.\n * @type {boolean}\n */\ngoog.userAgent.WINDOWS = goog.userAgent.PLATFORM_KNOWN_ ?\n    goog.userAgent.ASSUME_WINDOWS :\n    goog.labs.userAgent.platform.isWindows();\n\n\n/**\n * Whether the user agent is Linux per the legacy behavior of\n * goog.userAgent.LINUX, which considered ChromeOS to also be\n * Linux.\n * @return {boolean}\n * @private\n */\ngoog.userAgent.isLegacyLinux_ = function() {\n  return goog.labs.userAgent.platform.isLinux() ||\n      goog.labs.userAgent.platform.isChromeOS();\n};\n\n\n/**\n * Whether the user agent is running on a Linux operating system.\n *\n * Note that goog.userAgent.LINUX considers ChromeOS to be Linux,\n * while goog.labs.userAgent.platform considers ChromeOS and\n * Linux to be different OSes.\n *\n * @type {boolean}\n */\ngoog.userAgent.LINUX = goog.userAgent.PLATFORM_KNOWN_ ?\n    goog.userAgent.ASSUME_LINUX :\n    goog.userAgent.isLegacyLinux_();\n\n\n/**\n * @return {boolean} Whether the user agent is an X11 windowing system.\n * @private\n */\ngoog.userAgent.isX11_ = function() {\n  var navigator = goog.userAgent.getNavigatorTyped();\n  return !!navigator &&\n      goog.string.contains(navigator['appVersion'] || '', 'X11');\n};\n\n\n/**\n * Whether the user agent is running on a X11 windowing system.\n * @type {boolean}\n */\ngoog.userAgent.X11 = goog.userAgent.PLATFORM_KNOWN_ ?\n    goog.userAgent.ASSUME_X11 :\n    goog.userAgent.isX11_();\n\n\n/**\n * Whether the user agent is running on Android.\n * @type {boolean}\n */\ngoog.userAgent.ANDROID = goog.userAgent.PLATFORM_KNOWN_ ?\n    goog.userAgent.ASSUME_ANDROID :\n    goog.labs.userAgent.platform.isAndroid();\n\n\n/**\n * Whether the user agent is running on an iPhone.\n * @type {boolean}\n */\ngoog.userAgent.IPHONE = goog.userAgent.PLATFORM_KNOWN_ ?\n    goog.userAgent.ASSUME_IPHONE :\n    goog.labs.userAgent.platform.isIphone();\n\n\n/**\n * Whether the user agent is running on an iPad.\n * @type {boolean}\n */\ngoog.userAgent.IPAD = goog.userAgent.PLATFORM_KNOWN_ ?\n    goog.userAgent.ASSUME_IPAD :\n    goog.labs.userAgent.platform.isIpad();\n\n\n/**\n * Whether the user agent is running on an iPod.\n * @type {boolean}\n */\ngoog.userAgent.IPOD = goog.userAgent.PLATFORM_KNOWN_ ?\n    goog.userAgent.ASSUME_IPOD :\n    goog.labs.userAgent.platform.isIpod();\n\n\n/**\n * Whether the user agent is running on iOS.\n * @type {boolean}\n */\ngoog.userAgent.IOS = goog.userAgent.PLATFORM_KNOWN_ ?\n    (goog.userAgent.ASSUME_IPHONE || goog.userAgent.ASSUME_IPAD ||\n     goog.userAgent.ASSUME_IPOD) :\n    goog.labs.userAgent.platform.isIos();\n\n/**\n * Whether the user agent is running on KaiOS.\n * @type {boolean}\n */\ngoog.userAgent.KAIOS = goog.userAgent.PLATFORM_KNOWN_ ?\n    goog.userAgent.ASSUME_KAIOS :\n    goog.labs.userAgent.platform.isKaiOS();\n\n\n/**\n * @return {string} The string that describes the version number of the user\n *     agent.\n * @private\n */\ngoog.userAgent.determineVersion_ = function() {\n  // All browsers have different ways to detect the version and they all have\n  // different naming schemes.\n  // version is a string rather than a number because it may contain 'b', 'a',\n  // and so on.\n  var version = '';\n  var arr = goog.userAgent.getVersionRegexResult_();\n  if (arr) {\n    version = arr ? arr[1] : '';\n  }\n\n  if (goog.userAgent.IE) {\n    // IE9 can be in document mode 9 but be reporting an inconsistent user agent\n    // version.  If it is identifying as a version lower than 9 we take the\n    // documentMode as the version instead.  IE8 has similar behavior.\n    // It is recommended to set the X-UA-Compatible header to ensure that IE9\n    // uses documentMode 9.\n    var docMode = goog.userAgent.getDocumentMode_();\n    if (docMode != null && docMode > parseFloat(version)) {\n      return String(docMode);\n    }\n  }\n\n  return version;\n};\n\n\n/**\n * @return {?IArrayLike<string>|undefined} The version regex matches from\n *     parsing the user\n *     agent string. These regex statements must be executed inline so they can\n *     be compiled out by the closure compiler with the rest of the useragent\n *     detection logic when ASSUME_* is specified.\n * @private\n */\ngoog.userAgent.getVersionRegexResult_ = function() {\n  var userAgent = goog.userAgent.getUserAgentString();\n  if (goog.userAgent.GECKO) {\n    return /rv\\:([^\\);]+)(\\)|;)/.exec(userAgent);\n  }\n  if (goog.userAgent.EDGE) {\n    return /Edge\\/([\\d\\.]+)/.exec(userAgent);\n  }\n  if (goog.userAgent.IE) {\n    return /\\b(?:MSIE|rv)[: ]([^\\);]+)(\\)|;)/.exec(userAgent);\n  }\n  if (goog.userAgent.WEBKIT) {\n    // WebKit/125.4\n    return /WebKit\\/(\\S+)/.exec(userAgent);\n  }\n  if (goog.userAgent.OPERA) {\n    // If none of the above browsers were detected but the browser is Opera, the\n    // only string that is of interest is 'Version/<number>'.\n    return /(?:Version)[ \\/]?(\\S+)/.exec(userAgent);\n  }\n  return undefined;\n};\n\n\n/**\n * @return {number|undefined} Returns the document mode (for testing).\n * @private\n */\ngoog.userAgent.getDocumentMode_ = function() {\n  // NOTE(user): goog.userAgent may be used in context where there is no DOM.\n  var doc = goog.global['document'];\n  return doc ? doc['documentMode'] : undefined;\n};\n\n\n/**\n * The version of the user agent. This is a string because it might contain\n * 'b' (as in beta) as well as multiple dots.\n * @type {string}\n */\ngoog.userAgent.VERSION = goog.userAgent.determineVersion_();\n\n\n/**\n * Compares two version numbers.\n *\n * @param {string} v1 Version of first item.\n * @param {string} v2 Version of second item.\n *\n * @return {number}  1 if first argument is higher\n *                   0 if arguments are equal\n *                  -1 if second argument is higher.\n * @deprecated Use goog.string.compareVersions.\n */\ngoog.userAgent.compare = function(v1, v2) {\n  return goog.string.compareVersions(v1, v2);\n};\n\n\n/**\n * Cache for {@link goog.userAgent.isVersionOrHigher}.\n * Calls to compareVersions are surprisingly expensive and, as a browser's\n * version number is unlikely to change during a session, we cache the results.\n * @const\n * @private\n */\ngoog.userAgent.isVersionOrHigherCache_ = {};\n\n\n/**\n * Whether the user agent version is higher or the same as the given version.\n * NOTE: When checking the version numbers for Firefox and Safari, be sure to\n * use the engine's version, not the browser's version number.  For example,\n * Firefox 3.0 corresponds to Gecko 1.9 and Safari 3.0 to Webkit 522.11.\n * Opera and Internet Explorer versions match the product release number.<br>\n * @see <a href=\"http://en.wikipedia.org/wiki/Safari_version_history\">\n *     Webkit</a>\n * @see <a href=\"http://en.wikipedia.org/wiki/Gecko_engine\">Gecko</a>\n *\n * @param {string|number} version The version to check.\n * @return {boolean} Whether the user agent version is higher or the same as\n *     the given version.\n */\ngoog.userAgent.isVersionOrHigher = function(version) {\n  return goog.userAgent.ASSUME_ANY_VERSION ||\n      goog.reflect.cache(\n          goog.userAgent.isVersionOrHigherCache_, version, function() {\n            return goog.string.compareVersions(\n                       goog.userAgent.VERSION, version) >= 0;\n          });\n};\n\n\n/**\n * Deprecated alias to `goog.userAgent.isVersionOrHigher`.\n * @param {string|number} version The version to check.\n * @return {boolean} Whether the user agent version is higher or the same as\n *     the given version.\n * @deprecated Use goog.userAgent.isVersionOrHigher().\n */\ngoog.userAgent.isVersion = goog.userAgent.isVersionOrHigher;\n\n\n/**\n * Whether the IE effective document mode is higher or the same as the given\n * document mode version.\n * NOTE: Only for IE, return false for another browser.\n *\n * @param {number} documentMode The document mode version to check.\n * @return {boolean} Whether the IE effective document mode is higher or the\n *     same as the given version.\n */\ngoog.userAgent.isDocumentModeOrHigher = function(documentMode) {\n  return Number(goog.userAgent.DOCUMENT_MODE) >= documentMode;\n};\n\n\n/**\n * Deprecated alias to `goog.userAgent.isDocumentModeOrHigher`.\n * @param {number} version The version to check.\n * @return {boolean} Whether the IE effective document mode is higher or the\n *      same as the given version.\n * @deprecated Use goog.userAgent.isDocumentModeOrHigher().\n */\ngoog.userAgent.isDocumentMode = goog.userAgent.isDocumentModeOrHigher;\n\n\n/**\n * For IE version < 7, documentMode is undefined, so attempt to use the\n * CSS1Compat property to see if we are in standards mode. If we are in\n * standards mode, treat the browser version as the document mode. Otherwise,\n * IE is emulating version 5.\n *\n * NOTE(user): Support for IE < 7 is long gone, so this is now simplified.\n * It returns document.documentMode for IE and undefined for everything else.\n *\n * @type {number|undefined}\n * @const\n */\ngoog.userAgent.DOCUMENT_MODE = (function() {\n  var doc = goog.global['document'];\n  if (!doc || !goog.userAgent.IE) return undefined;\n  // This must be an IE user agent.\n  var documentMode = goog.userAgent.getDocumentMode_();\n  if (documentMode) return documentMode;\n  // The user agent version string begins with the major version.\n  // Parse the major version and truncate anything following.\n  var ieVersion = parseInt(goog.userAgent.VERSION, 10);\n  return ieVersion || undefined;\n})();\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Closure user agent detection (Browser).\n * @see <a href=\"http://www.useragentstring.com/\">User agent strings</a>\n * For more information on rendering engine, platform, or device see the other\n * sub-namespaces in goog.labs.userAgent, goog.labs.userAgent.platform,\n * goog.labs.userAgent.device respectively.)\n */\n\ngoog.provide('goog.labs.userAgent.browser');\n\ngoog.require('goog.array');\ngoog.require('goog.labs.userAgent.util');\ngoog.require('goog.object');\ngoog.require('goog.string.internal');\n\n\n// TODO(nnaze): Refactor to remove excessive exclusion logic in matching\n// functions.\n\n\n/**\n * @return {boolean} Whether the user's browser is Opera.  Note: Chromium\n *     based Opera (Opera 15+) is detected as Chrome to avoid unnecessary\n *     special casing.\n * @private\n */\ngoog.labs.userAgent.browser.matchOpera_ = function() {\n  'use strict';\n  return goog.labs.userAgent.util.matchUserAgent('Opera');\n};\n\n\n/**\n * @return {boolean} Whether the user's browser is IE.\n * @private\n */\ngoog.labs.userAgent.browser.matchIE_ = function() {\n  'use strict';\n  return goog.labs.userAgent.util.matchUserAgent('Trident') ||\n      goog.labs.userAgent.util.matchUserAgent('MSIE');\n};\n\n\n/**\n * @return {boolean} Whether the user's browser is Edge. This refers to EdgeHTML\n * based Edge.\n * @private\n */\ngoog.labs.userAgent.browser.matchEdgeHtml_ = function() {\n  'use strict';\n  return goog.labs.userAgent.util.matchUserAgent('Edge');\n};\n\n\n/**\n * @return {boolean} Whether the user's browser is Chromium based Edge.\n * @private\n */\ngoog.labs.userAgent.browser.matchEdgeChromium_ = function() {\n  'use strict';\n  return goog.labs.userAgent.util.matchUserAgent('Edg/');\n};\n\n\n/**\n * @return {boolean} Whether the user's browser is Chromium based Opera.\n * @private\n */\ngoog.labs.userAgent.browser.matchOperaChromium_ = function() {\n  'use strict';\n  return goog.labs.userAgent.util.matchUserAgent('OPR');\n};\n\n\n/**\n * @return {boolean} Whether the user's browser is Firefox.\n * @private\n */\ngoog.labs.userAgent.browser.matchFirefox_ = function() {\n  'use strict';\n  return goog.labs.userAgent.util.matchUserAgent('Firefox') ||\n      goog.labs.userAgent.util.matchUserAgent('FxiOS');\n};\n\n\n/**\n * @return {boolean} Whether the user's browser is Safari.\n * @private\n */\ngoog.labs.userAgent.browser.matchSafari_ = function() {\n  'use strict';\n  return goog.labs.userAgent.util.matchUserAgent('Safari') &&\n      !(goog.labs.userAgent.browser.matchChrome_() ||\n        goog.labs.userAgent.browser.matchCoast_() ||\n        goog.labs.userAgent.browser.matchOpera_() ||\n        goog.labs.userAgent.browser.matchEdgeHtml_() ||\n        goog.labs.userAgent.browser.matchEdgeChromium_() ||\n        goog.labs.userAgent.browser.matchOperaChromium_() ||\n        goog.labs.userAgent.browser.matchFirefox_() ||\n        goog.labs.userAgent.browser.isSilk() ||\n        goog.labs.userAgent.util.matchUserAgent('Android'));\n};\n\n\n/**\n * @return {boolean} Whether the user's browser is Coast (Opera's Webkit-based\n *     iOS browser).\n * @private\n */\ngoog.labs.userAgent.browser.matchCoast_ = function() {\n  'use strict';\n  return goog.labs.userAgent.util.matchUserAgent('Coast');\n};\n\n\n/**\n * @return {boolean} Whether the user's browser is iOS Webview.\n * @private\n */\ngoog.labs.userAgent.browser.matchIosWebview_ = function() {\n  'use strict';\n  // iOS Webview does not show up as Chrome or Safari. Also check for Opera's\n  // WebKit-based iOS browser, Coast.\n  return (goog.labs.userAgent.util.matchUserAgent('iPad') ||\n          goog.labs.userAgent.util.matchUserAgent('iPhone')) &&\n      !goog.labs.userAgent.browser.matchSafari_() &&\n      !goog.labs.userAgent.browser.matchChrome_() &&\n      !goog.labs.userAgent.browser.matchCoast_() &&\n      !goog.labs.userAgent.browser.matchFirefox_() &&\n      goog.labs.userAgent.util.matchUserAgent('AppleWebKit');\n};\n\n\n/**\n * @return {boolean} Whether the user's browser is any Chromium browser. This\n * returns true for Chrome, Opera 15+, and Edge Chromium.\n * @private\n */\ngoog.labs.userAgent.browser.matchChrome_ = function() {\n  'use strict';\n  return (goog.labs.userAgent.util.matchUserAgent('Chrome') ||\n          goog.labs.userAgent.util.matchUserAgent('CriOS')) &&\n      !goog.labs.userAgent.browser.matchEdgeHtml_();\n};\n\n\n/**\n * @return {boolean} Whether the user's browser is the Android browser.\n * @private\n */\ngoog.labs.userAgent.browser.matchAndroidBrowser_ = function() {\n  'use strict';\n  // Android can appear in the user agent string for Chrome on Android.\n  // This is not the Android standalone browser if it does.\n  return goog.labs.userAgent.util.matchUserAgent('Android') &&\n      !(goog.labs.userAgent.browser.isChrome() ||\n        goog.labs.userAgent.browser.isFirefox() ||\n        goog.labs.userAgent.browser.isOpera() ||\n        goog.labs.userAgent.browser.isSilk());\n};\n\n\n/**\n * @return {boolean} Whether the user's browser is Opera.\n */\ngoog.labs.userAgent.browser.isOpera = goog.labs.userAgent.browser.matchOpera_;\n\n\n/**\n * @return {boolean} Whether the user's browser is IE.\n */\ngoog.labs.userAgent.browser.isIE = goog.labs.userAgent.browser.matchIE_;\n\n\n/**\n * @return {boolean} Whether the user's browser is EdgeHTML based Edge.\n */\ngoog.labs.userAgent.browser.isEdge = goog.labs.userAgent.browser.matchEdgeHtml_;\n\n\n/**\n * @return {boolean} Whether the user's browser is Chromium based Edge.\n */\ngoog.labs.userAgent.browser.isEdgeChromium =\n    goog.labs.userAgent.browser.matchEdgeChromium_;\n\n/**\n * @return {boolean} Whether the user's browser is Chromium based Opera.\n */\ngoog.labs.userAgent.browser.isOperaChromium =\n    goog.labs.userAgent.browser.matchOperaChromium_;\n\n/**\n * @return {boolean} Whether the user's browser is Firefox.\n */\ngoog.labs.userAgent.browser.isFirefox =\n    goog.labs.userAgent.browser.matchFirefox_;\n\n\n/**\n * @return {boolean} Whether the user's browser is Safari.\n */\ngoog.labs.userAgent.browser.isSafari = goog.labs.userAgent.browser.matchSafari_;\n\n\n/**\n * @return {boolean} Whether the user's browser is Coast (Opera's Webkit-based\n *     iOS browser).\n */\ngoog.labs.userAgent.browser.isCoast = goog.labs.userAgent.browser.matchCoast_;\n\n\n/**\n * @return {boolean} Whether the user's browser is iOS Webview.\n */\ngoog.labs.userAgent.browser.isIosWebview =\n    goog.labs.userAgent.browser.matchIosWebview_;\n\n\n/**\n * @return {boolean} Whether the user's browser is any Chromium based browser (\n * Chrome, Blink-based Opera (15+) and Edge Chromium).\n */\ngoog.labs.userAgent.browser.isChrome = goog.labs.userAgent.browser.matchChrome_;\n\n\n/**\n * @return {boolean} Whether the user's browser is the Android browser.\n */\ngoog.labs.userAgent.browser.isAndroidBrowser =\n    goog.labs.userAgent.browser.matchAndroidBrowser_;\n\n\n/**\n * For more information, see:\n * http://docs.aws.amazon.com/silk/latest/developerguide/user-agent.html\n * @return {boolean} Whether the user's browser is Silk.\n */\ngoog.labs.userAgent.browser.isSilk = function() {\n  'use strict';\n  return goog.labs.userAgent.util.matchUserAgent('Silk');\n};\n\n\n/**\n * @return {string} The browser version or empty string if version cannot be\n *     determined. Note that for Internet Explorer, this returns the version of\n *     the browser, not the version of the rendering engine. (IE 8 in\n *     compatibility mode will return 8.0 rather than 7.0. To determine the\n *     rendering engine version, look at document.documentMode instead. See\n *     http://msdn.microsoft.com/en-us/library/cc196988(v=vs.85).aspx for more\n *     details.)\n */\ngoog.labs.userAgent.browser.getVersion = function() {\n  'use strict';\n  var userAgentString = goog.labs.userAgent.util.getUserAgent();\n  // Special case IE since IE's version is inside the parenthesis and\n  // without the '/'.\n  if (goog.labs.userAgent.browser.isIE()) {\n    return goog.labs.userAgent.browser.getIEVersion_(userAgentString);\n  }\n\n  var versionTuples =\n      goog.labs.userAgent.util.extractVersionTuples(userAgentString);\n\n  // Construct a map for easy lookup.\n  var versionMap = {};\n  goog.array.forEach(versionTuples, function(tuple) {\n    'use strict';\n    // Note that the tuple is of length three, but we only care about the\n    // first two.\n    var key = tuple[0];\n    var value = tuple[1];\n    versionMap[key] = value;\n  });\n\n  var versionMapHasKey = goog.partial(goog.object.containsKey, versionMap);\n\n  // Gives the value with the first key it finds, otherwise empty string.\n  function lookUpValueWithKeys(keys) {\n    var key = goog.array.find(keys, versionMapHasKey);\n    return versionMap[key] || '';\n  }\n\n  // Check Opera before Chrome since Opera 15+ has \"Chrome\" in the string.\n  // See\n  // http://my.opera.com/ODIN/blog/2013/07/15/opera-user-agent-strings-opera-15-and-beyond\n  if (goog.labs.userAgent.browser.isOpera()) {\n    // Opera 10 has Version/10.0 but Opera/9.8, so look for \"Version\" first.\n    // Opera uses 'OPR' for more recent UAs.\n    return lookUpValueWithKeys(['Version', 'Opera']);\n  }\n\n  // Check Edge before Chrome since it has Chrome in the string.\n  if (goog.labs.userAgent.browser.isEdge()) {\n    return lookUpValueWithKeys(['Edge']);\n  }\n\n  // Check Chromium Edge before Chrome since it has Chrome in the string.\n  if (goog.labs.userAgent.browser.isEdgeChromium()) {\n    return lookUpValueWithKeys(['Edg']);\n  }\n\n  if (goog.labs.userAgent.browser.isChrome()) {\n    return lookUpValueWithKeys(['Chrome', 'CriOS', 'HeadlessChrome']);\n  }\n\n  // Usually products browser versions are in the third tuple after \"Mozilla\"\n  // and the engine.\n  var tuple = versionTuples[2];\n  return tuple && tuple[1] || '';\n};\n\n\n/**\n * @param {string|number} version The version to check.\n * @return {boolean} Whether the browser version is higher or the same as the\n *     given version.\n */\ngoog.labs.userAgent.browser.isVersionOrHigher = function(version) {\n  'use strict';\n  return goog.string.internal.compareVersions(\n             goog.labs.userAgent.browser.getVersion(), version) >= 0;\n};\n\n\n/**\n * Determines IE version. More information:\n * http://msdn.microsoft.com/en-us/library/ie/bg182625(v=vs.85).aspx#uaString\n * http://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx\n * http://blogs.msdn.com/b/ie/archive/2010/03/23/introducing-ie9-s-user-agent-string.aspx\n * http://blogs.msdn.com/b/ie/archive/2009/01/09/the-internet-explorer-8-user-agent-string-updated-edition.aspx\n *\n * @param {string} userAgent the User-Agent.\n * @return {string}\n * @private\n */\ngoog.labs.userAgent.browser.getIEVersion_ = function(userAgent) {\n  'use strict';\n  // IE11 may identify itself as MSIE 9.0 or MSIE 10.0 due to an IE 11 upgrade\n  // bug. Example UA:\n  // Mozilla/5.0 (MSIE 9.0; Windows NT 6.1; WOW64; Trident/7.0; rv:11.0)\n  // like Gecko.\n  // See http://www.whatismybrowser.com/developers/unknown-user-agent-fragments.\n  var rv = /rv: *([\\d\\.]*)/.exec(userAgent);\n  if (rv && rv[1]) {\n    return rv[1];\n  }\n\n  var version = '';\n  var msie = /MSIE +([\\d\\.]+)/.exec(userAgent);\n  if (msie && msie[1]) {\n    // IE in compatibility mode usually identifies itself as MSIE 7.0; in this\n    // case, use the Trident version to determine the version of IE. For more\n    // details, see the links above.\n    var tridentVersion = /Trident\\/(\\d.\\d)/.exec(userAgent);\n    if (msie[1] == '7.0') {\n      if (tridentVersion && tridentVersion[1]) {\n        switch (tridentVersion[1]) {\n          case '4.0':\n            version = '8.0';\n            break;\n          case '5.0':\n            version = '9.0';\n            break;\n          case '6.0':\n            version = '10.0';\n            break;\n          case '7.0':\n            version = '11.0';\n            break;\n        }\n      } else {\n        version = '7.0';\n      }\n    } else {\n      version = msie[1];\n    }\n  }\n  return version;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Closure user agent detection.\n * @see http://en.wikipedia.org/wiki/User_agent\n * For more information on browser brand, platform, or device see the other\n * sub-namespaces in goog.labs.userAgent (browser, platform, and device).\n */\n\ngoog.provide('goog.labs.userAgent.engine');\n\ngoog.require('goog.array');\ngoog.require('goog.labs.userAgent.util');\ngoog.require('goog.string');\n\n\n/**\n * @return {boolean} Whether the rendering engine is Presto.\n */\ngoog.labs.userAgent.engine.isPresto = function() {\n  'use strict';\n  return goog.labs.userAgent.util.matchUserAgent('Presto');\n};\n\n\n/**\n * @return {boolean} Whether the rendering engine is Trident.\n */\ngoog.labs.userAgent.engine.isTrident = function() {\n  'use strict';\n  // IE only started including the Trident token in IE8.\n  return goog.labs.userAgent.util.matchUserAgent('Trident') ||\n      goog.labs.userAgent.util.matchUserAgent('MSIE');\n};\n\n\n/**\n * @return {boolean} Whether the rendering engine is EdgeHTML.\n */\ngoog.labs.userAgent.engine.isEdge = function() {\n  'use strict';\n  return goog.labs.userAgent.util.matchUserAgent('Edge');\n};\n\n\n/**\n * @return {boolean} Whether the rendering engine is WebKit. This will return\n * true for Chrome, Blink-based Opera (15+), Edge Chromium and Safari.\n */\ngoog.labs.userAgent.engine.isWebKit = function() {\n  'use strict';\n  return goog.labs.userAgent.util.matchUserAgentIgnoreCase('WebKit') &&\n      !goog.labs.userAgent.engine.isEdge();\n};\n\n\n/**\n * @return {boolean} Whether the rendering engine is Gecko.\n */\ngoog.labs.userAgent.engine.isGecko = function() {\n  'use strict';\n  return goog.labs.userAgent.util.matchUserAgent('Gecko') &&\n      !goog.labs.userAgent.engine.isWebKit() &&\n      !goog.labs.userAgent.engine.isTrident() &&\n      !goog.labs.userAgent.engine.isEdge();\n};\n\n\n/**\n * @return {string} The rendering engine's version or empty string if version\n *     can't be determined.\n */\ngoog.labs.userAgent.engine.getVersion = function() {\n  'use strict';\n  var userAgentString = goog.labs.userAgent.util.getUserAgent();\n  if (userAgentString) {\n    var tuples = goog.labs.userAgent.util.extractVersionTuples(userAgentString);\n\n    var engineTuple = goog.labs.userAgent.engine.getEngineTuple_(tuples);\n    if (engineTuple) {\n      // In Gecko, the version string is either in the browser info or the\n      // Firefox version.  See Gecko user agent string reference:\n      // http://goo.gl/mULqa\n      if (engineTuple[0] == 'Gecko') {\n        return goog.labs.userAgent.engine.getVersionForKey_(tuples, 'Firefox');\n      }\n\n      return engineTuple[1];\n    }\n\n    // MSIE has only one version identifier, and the Trident version is\n    // specified in the parenthetical. IE Edge is covered in the engine tuple\n    // detection.\n    var browserTuple = tuples[0];\n    var info;\n    if (browserTuple && (info = browserTuple[2])) {\n      var match = /Trident\\/([^\\s;]+)/.exec(info);\n      if (match) {\n        return match[1];\n      }\n    }\n  }\n  return '';\n};\n\n\n/**\n * @param {!Array<!Array<string>>} tuples Extracted version tuples.\n * @return {!Array<string>|undefined} The engine tuple or undefined if not\n *     found.\n * @private\n */\ngoog.labs.userAgent.engine.getEngineTuple_ = function(tuples) {\n  'use strict';\n  if (!goog.labs.userAgent.engine.isEdge()) {\n    return tuples[1];\n  }\n  for (var i = 0; i < tuples.length; i++) {\n    var tuple = tuples[i];\n    if (tuple[0] == 'Edge') {\n      return tuple;\n    }\n  }\n};\n\n\n/**\n * @param {string|number} version The version to check.\n * @return {boolean} Whether the rendering engine version is higher or the same\n *     as the given version.\n */\ngoog.labs.userAgent.engine.isVersionOrHigher = function(version) {\n  'use strict';\n  return goog.string.compareVersions(\n             goog.labs.userAgent.engine.getVersion(), version) >= 0;\n};\n\n\n/**\n * @param {!Array<!Array<string>>} tuples Version tuples.\n * @param {string} key The key to look for.\n * @return {string} The version string of the given key, if present.\n *     Otherwise, the empty string.\n * @private\n */\ngoog.labs.userAgent.engine.getVersionForKey_ = function(tuples, key) {\n  'use strict';\n  // TODO(nnaze): Move to util if useful elsewhere.\n\n  var pair = goog.array.find(tuples, function(pair) {\n    'use strict';\n    return key == pair[0];\n  });\n\n  return pair && pair[1] || '';\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Browser capability checks for the dom package.\n */\n\n\ngoog.provide('goog.dom.BrowserFeature');\n\ngoog.require('goog.userAgent');\n\n\n/**\n * @define {boolean} Whether we know at compile time that the browser doesn't\n * support OffscreenCanvas.\n */\ngoog.dom.BrowserFeature.ASSUME_NO_OFFSCREEN_CANVAS =\n    goog.define('goog.dom.ASSUME_NO_OFFSCREEN_CANVAS', false);\n\n/**\n * @define {boolean} Whether we know at compile time that the browser supports\n * all OffscreenCanvas contexts.\n */\n// TODO(user): Eventually this should default to \"FEATURESET_YEAR >= 202X\".\ngoog.dom.BrowserFeature.ASSUME_OFFSCREEN_CANVAS =\n    goog.define('goog.dom.ASSUME_OFFSCREEN_CANVAS', false);\n\n/**\n * Detects if a particular OffscreenCanvas context is supported.\n * @param {string} contextName name of the context to test.\n * @return {boolean} Whether the browser supports this OffscreenCanvas context.\n * @private\n */\ngoog.dom.BrowserFeature.detectOffscreenCanvas_ = function(contextName) {\n  // This code only gets removed because we forced @nosideeffects on\n  // the functions. See: b/138802376\n  try {\n    return Boolean(new self.OffscreenCanvas(0, 0).getContext(contextName));\n  } catch (ex) {\n  }\n  return false;\n};\n\n/**\n * Whether the browser supports OffscreenCanvas 2D context.\n * @const {boolean}\n */\ngoog.dom.BrowserFeature.OFFSCREEN_CANVAS_2D =\n    !goog.dom.BrowserFeature.ASSUME_NO_OFFSCREEN_CANVAS &&\n    (goog.dom.BrowserFeature.ASSUME_OFFSCREEN_CANVAS ||\n     goog.dom.BrowserFeature.detectOffscreenCanvas_('2d'));\n\n/**\n * Whether attributes 'name' and 'type' can be added to an element after it's\n * created. False in Internet Explorer prior to version 9.\n * @const {boolean}\n */\ngoog.dom.BrowserFeature.CAN_ADD_NAME_OR_TYPE_ATTRIBUTES =\n    !goog.userAgent.IE || goog.userAgent.isDocumentModeOrHigher(9);\n\n/**\n * Whether we can use element.children to access an element's Element\n * children. Available since Gecko 1.9.1, IE 9. (IE<9 also includes comment\n * nodes in the collection.)\n * @const {boolean}\n */\ngoog.dom.BrowserFeature.CAN_USE_CHILDREN_ATTRIBUTE =\n    !goog.userAgent.GECKO && !goog.userAgent.IE ||\n    goog.userAgent.IE && goog.userAgent.isDocumentModeOrHigher(9) ||\n    goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher('1.9.1');\n\n/**\n * Opera, Safari 3, and Internet Explorer 9 all support innerText but they\n * include text nodes in script and style tags. Not document-mode-dependent.\n * @const {boolean}\n */\ngoog.dom.BrowserFeature.CAN_USE_INNER_TEXT =\n    (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9'));\n\n/**\n * MSIE, Opera, and Safari>=4 support element.parentElement to access an\n * element's parent if it is an Element.\n * @const {boolean}\n */\ngoog.dom.BrowserFeature.CAN_USE_PARENT_ELEMENT_PROPERTY =\n    goog.userAgent.IE || goog.userAgent.OPERA || goog.userAgent.WEBKIT;\n\n/**\n * Whether NoScope elements need a scoped element written before them in\n * innerHTML.\n * MSDN: http://msdn.microsoft.com/en-us/library/ms533897(VS.85).aspx#1\n * @const {boolean}\n */\ngoog.dom.BrowserFeature.INNER_HTML_NEEDS_SCOPED_ELEMENT = goog.userAgent.IE;\n\n/**\n * Whether we use legacy IE range API.\n * @const {boolean}\n */\ngoog.dom.BrowserFeature.LEGACY_IE_RANGES =\n    goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9);\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Utilities for manipulating the browser's Document Object Model\n * Inspiration taken *heavily* from mochikit (http://mochikit.com/).\n *\n * You can use {@link goog.dom.DomHelper} to create new dom helpers that refer\n * to a different document object.  This is useful if you are working with\n * frames or multiple windows.\n *\n * @suppress {strictMissingProperties}\n */\n\n\n// TODO(arv): Rename/refactor getTextContent and getRawTextContent. The problem\n// is that getTextContent should mimic the DOM3 textContent. We should add a\n// getInnerText (or getText) which tries to return the visible text, innerText.\n\n\ngoog.provide('goog.dom');\ngoog.provide('goog.dom.Appendable');\ngoog.provide('goog.dom.DomHelper');\n\ngoog.require('goog.array');\ngoog.require('goog.asserts');\ngoog.require('goog.dom.BrowserFeature');\ngoog.require('goog.dom.NodeType');\ngoog.require('goog.dom.TagName');\ngoog.require('goog.dom.safe');\ngoog.require('goog.html.SafeHtml');\ngoog.require('goog.html.uncheckedconversions');\ngoog.require('goog.math.Coordinate');\ngoog.require('goog.math.Size');\ngoog.require('goog.object');\ngoog.require('goog.string');\ngoog.require('goog.string.Const');\ngoog.require('goog.string.Unicode');\ngoog.require('goog.userAgent');\n\n\n/**\n * @define {boolean} Whether we know at compile time that the browser is in\n * quirks mode.\n */\ngoog.dom.ASSUME_QUIRKS_MODE = goog.define('goog.dom.ASSUME_QUIRKS_MODE', false);\n\n\n/**\n * @define {boolean} Whether we know at compile time that the browser is in\n * standards compliance mode.\n */\ngoog.dom.ASSUME_STANDARDS_MODE =\n    goog.define('goog.dom.ASSUME_STANDARDS_MODE', false);\n\n\n/**\n * Whether we know the compatibility mode at compile time.\n * @type {boolean}\n * @private\n */\ngoog.dom.COMPAT_MODE_KNOWN_ =\n    goog.dom.ASSUME_QUIRKS_MODE || goog.dom.ASSUME_STANDARDS_MODE;\n\n\n/**\n * Gets the DomHelper object for the document where the element resides.\n * @param {(Node|Window)=} opt_element If present, gets the DomHelper for this\n *     element.\n * @return {!goog.dom.DomHelper} The DomHelper.\n */\ngoog.dom.getDomHelper = function(opt_element) {\n  return opt_element ?\n      new goog.dom.DomHelper(goog.dom.getOwnerDocument(opt_element)) :\n      (goog.dom.defaultDomHelper_ ||\n       (goog.dom.defaultDomHelper_ = new goog.dom.DomHelper()));\n};\n\n\n/**\n * Cached default DOM helper.\n * @type {!goog.dom.DomHelper|undefined}\n * @private\n */\ngoog.dom.defaultDomHelper_;\n\n\n/**\n * Gets the document object being used by the dom library.\n * @return {!Document} Document object.\n */\ngoog.dom.getDocument = function() {\n  return document;\n};\n\n\n/**\n * Gets an element from the current document by element id.\n *\n * If an Element is passed in, it is returned.\n *\n * @param {string|Element} element Element ID or a DOM node.\n * @return {Element} The element with the given ID, or the node passed in.\n */\ngoog.dom.getElement = function(element) {\n  return goog.dom.getElementHelper_(document, element);\n};\n\n\n/**\n * Gets an element by id from the given document (if present).\n * If an element is given, it is returned.\n * @param {!Document} doc\n * @param {string|Element} element Element ID or a DOM node.\n * @return {Element} The resulting element.\n * @private\n */\ngoog.dom.getElementHelper_ = function(doc, element) {\n  return typeof element === 'string' ? doc.getElementById(element) : element;\n};\n\n\n/**\n * Gets an element by id, asserting that the element is found.\n *\n * This is used when an element is expected to exist, and should fail with\n * an assertion error if it does not (if assertions are enabled).\n *\n * @param {string} id Element ID.\n * @return {!Element} The element with the given ID, if it exists.\n */\ngoog.dom.getRequiredElement = function(id) {\n  return goog.dom.getRequiredElementHelper_(document, id);\n};\n\n\n/**\n * Helper function for getRequiredElementHelper functions, both static and\n * on DomHelper.  Asserts the element with the given id exists.\n * @param {!Document} doc\n * @param {string} id\n * @return {!Element} The element with the given ID, if it exists.\n * @private\n */\ngoog.dom.getRequiredElementHelper_ = function(doc, id) {\n  // To prevent users passing in Elements as is permitted in getElement().\n  goog.asserts.assertString(id);\n  var element = goog.dom.getElementHelper_(doc, id);\n  element =\n      goog.asserts.assertElement(element, 'No element found with id: ' + id);\n  return element;\n};\n\n\n/**\n * Alias for getElement.\n * @param {string|Element} element Element ID or a DOM node.\n * @return {Element} The element with the given ID, or the node passed in.\n * @deprecated Use {@link goog.dom.getElement} instead.\n */\ngoog.dom.$ = goog.dom.getElement;\n\n\n/**\n * Gets elements by tag name.\n * @param {!goog.dom.TagName<T>} tagName\n * @param {(!Document|!Element)=} opt_parent Parent element or document where to\n *     look for elements. Defaults to document.\n * @return {!NodeList<R>} List of elements. The members of the list are\n *     {!Element} if tagName is not a member of goog.dom.TagName or more\n *     specific types if it is (e.g. {!HTMLAnchorElement} for\n *     goog.dom.TagName.A).\n * @template T\n * @template R := cond(isUnknown(T), 'Element', T) =:\n */\ngoog.dom.getElementsByTagName = function(tagName, opt_parent) {\n  var parent = opt_parent || document;\n  return parent.getElementsByTagName(String(tagName));\n};\n\n\n/**\n * Looks up elements by both tag and class name, using browser native functions\n * (`querySelectorAll`, `getElementsByTagName` or\n * `getElementsByClassName`) where possible. This function\n * is a useful, if limited, way of collecting a list of DOM elements\n * with certain characteristics.  `querySelectorAll` offers a\n * more powerful and general solution which allows matching on CSS3\n * selector expressions.\n *\n * Note that tag names are case sensitive in the SVG namespace, and this\n * function converts opt_tag to uppercase for comparisons. For queries in the\n * SVG namespace you should use querySelector or querySelectorAll instead.\n * https://bugzilla.mozilla.org/show_bug.cgi?id=963870\n * https://bugs.webkit.org/show_bug.cgi?id=83438\n *\n * @see {https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll}\n *\n * @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name.\n * @param {?string=} opt_class Optional class name.\n * @param {(Document|Element)=} opt_el Optional element to look in.\n * @return {!IArrayLike<R>} Array-like list of elements (only a length property\n *     and numerical indices are guaranteed to exist). The members of the array\n *     are {!Element} if opt_tag is not a member of goog.dom.TagName or more\n *     specific types if it is (e.g. {!HTMLAnchorElement} for\n *     goog.dom.TagName.A).\n * @template T\n * @template R := cond(isUnknown(T), 'Element', T) =:\n */\ngoog.dom.getElementsByTagNameAndClass = function(opt_tag, opt_class, opt_el) {\n  return goog.dom.getElementsByTagNameAndClass_(\n      document, opt_tag, opt_class, opt_el);\n};\n\n\n/**\n * Gets the first element matching the tag and the class.\n *\n * @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name.\n * @param {?string=} opt_class Optional class name.\n * @param {(Document|Element)=} opt_el Optional element to look in.\n * @return {?R} Reference to a DOM node. The return type is {?Element} if\n *     tagName is a string or a more specific type if it is a member of\n *     goog.dom.TagName (e.g. {?HTMLAnchorElement} for goog.dom.TagName.A).\n * @template T\n * @template R := cond(isUnknown(T), 'Element', T) =:\n */\ngoog.dom.getElementByTagNameAndClass = function(opt_tag, opt_class, opt_el) {\n  return goog.dom.getElementByTagNameAndClass_(\n      document, opt_tag, opt_class, opt_el);\n};\n\n\n/**\n * Returns a static, array-like list of the elements with the provided\n * className.\n *\n * @param {string} className the name of the class to look for.\n * @param {(Document|Element)=} opt_el Optional element to look in.\n * @return {!IArrayLike<!Element>} The items found with the class name provided.\n */\ngoog.dom.getElementsByClass = function(className, opt_el) {\n  var parent = opt_el || document;\n  if (goog.dom.canUseQuerySelector_(parent)) {\n    return parent.querySelectorAll('.' + className);\n  }\n  return goog.dom.getElementsByTagNameAndClass_(\n      document, '*', className, opt_el);\n};\n\n\n/**\n * Returns the first element with the provided className.\n *\n * @param {string} className the name of the class to look for.\n * @param {Element|Document=} opt_el Optional element to look in.\n * @return {Element} The first item with the class name provided.\n */\ngoog.dom.getElementByClass = function(className, opt_el) {\n  var parent = opt_el || document;\n  var retVal = null;\n  if (parent.getElementsByClassName) {\n    retVal = parent.getElementsByClassName(className)[0];\n  } else {\n    retVal =\n        goog.dom.getElementByTagNameAndClass_(document, '*', className, opt_el);\n  }\n  return retVal || null;\n};\n\n\n/**\n * Ensures an element with the given className exists, and then returns the\n * first element with the provided className.\n *\n * @param {string} className the name of the class to look for.\n * @param {!Element|!Document=} opt_root Optional element or document to look\n *     in.\n * @return {!Element} The first item with the class name provided.\n * @throws {goog.asserts.AssertionError} Thrown if no element is found.\n */\ngoog.dom.getRequiredElementByClass = function(className, opt_root) {\n  var retValue = goog.dom.getElementByClass(className, opt_root);\n  return goog.asserts.assert(\n      retValue, 'No element found with className: ' + className);\n};\n\n\n/**\n * Prefer the standardized (http://www.w3.org/TR/selectors-api/), native and\n * fast W3C Selectors API.\n * @param {!(Element|Document)} parent The parent document object.\n * @return {boolean} whether or not we can use parent.querySelector* APIs.\n * @private\n */\ngoog.dom.canUseQuerySelector_ = function(parent) {\n  return !!(parent.querySelectorAll && parent.querySelector);\n};\n\n\n/**\n * Helper for `getElementsByTagNameAndClass`.\n * @param {!Document} doc The document to get the elements in.\n * @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name.\n * @param {?string=} opt_class Optional class name.\n * @param {(Document|Element)=} opt_el Optional element to look in.\n * @return {!IArrayLike<R>} Array-like list of elements (only a length property\n *     and numerical indices are guaranteed to exist). The members of the array\n *     are {!Element} if opt_tag is not a member of goog.dom.TagName or more\n *     specific types if it is (e.g. {!HTMLAnchorElement} for\n *     goog.dom.TagName.A).\n * @template T\n * @template R := cond(isUnknown(T), 'Element', T) =:\n * @private\n */\ngoog.dom.getElementsByTagNameAndClass_ = function(\n    doc, opt_tag, opt_class, opt_el) {\n  var parent = opt_el || doc;\n  var tagName =\n      (opt_tag && opt_tag != '*') ? String(opt_tag).toUpperCase() : '';\n\n  if (goog.dom.canUseQuerySelector_(parent) && (tagName || opt_class)) {\n    var query = tagName + (opt_class ? '.' + opt_class : '');\n    return parent.querySelectorAll(query);\n  }\n\n  // Use the native getElementsByClassName if available, under the assumption\n  // that even when the tag name is specified, there will be fewer elements to\n  // filter through when going by class than by tag name\n  if (opt_class && parent.getElementsByClassName) {\n    var els = parent.getElementsByClassName(opt_class);\n\n    if (tagName) {\n      var arrayLike = {};\n      var len = 0;\n\n      // Filter for specific tags if requested.\n      for (var i = 0, el; el = els[i]; i++) {\n        if (tagName == el.nodeName) {\n          arrayLike[len++] = el;\n        }\n      }\n      arrayLike.length = len;\n\n      return /** @type {!IArrayLike<!Element>} */ (arrayLike);\n    } else {\n      return els;\n    }\n  }\n\n  var els = parent.getElementsByTagName(tagName || '*');\n\n  if (opt_class) {\n    var arrayLike = {};\n    var len = 0;\n    for (var i = 0, el; el = els[i]; i++) {\n      var className = el.className;\n      // Check if className has a split function since SVG className does not.\n      if (typeof className.split == 'function' &&\n          goog.array.contains(className.split(/\\s+/), opt_class)) {\n        arrayLike[len++] = el;\n      }\n    }\n    arrayLike.length = len;\n    return /** @type {!IArrayLike<!Element>} */ (arrayLike);\n  } else {\n    return els;\n  }\n};\n\n\n/**\n * Helper for goog.dom.getElementByTagNameAndClass.\n *\n * @param {!Document} doc The document to get the elements in.\n * @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name.\n * @param {?string=} opt_class Optional class name.\n * @param {(Document|Element)=} opt_el Optional element to look in.\n * @return {?R} Reference to a DOM node. The return type is {?Element} if\n *     tagName is a string or a more specific type if it is a member of\n *     goog.dom.TagName (e.g. {?HTMLAnchorElement} for goog.dom.TagName.A).\n * @template T\n * @template R := cond(isUnknown(T), 'Element', T) =:\n * @private\n */\ngoog.dom.getElementByTagNameAndClass_ = function(\n    doc, opt_tag, opt_class, opt_el) {\n  var parent = opt_el || doc;\n  var tag = (opt_tag && opt_tag != '*') ? String(opt_tag).toUpperCase() : '';\n  if (goog.dom.canUseQuerySelector_(parent) && (tag || opt_class)) {\n    return parent.querySelector(tag + (opt_class ? '.' + opt_class : ''));\n  }\n  var elements =\n      goog.dom.getElementsByTagNameAndClass_(doc, opt_tag, opt_class, opt_el);\n  return elements[0] || null;\n};\n\n\n\n/**\n * Alias for `getElementsByTagNameAndClass`.\n * @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name.\n * @param {?string=} opt_class Optional class name.\n * @param {Element=} opt_el Optional element to look in.\n * @return {!IArrayLike<R>} Array-like list of elements (only a length property\n *     and numerical indices are guaranteed to exist). The members of the array\n *     are {!Element} if opt_tag is not a member of goog.dom.TagName or more\n *     specific types if it is (e.g. {!HTMLAnchorElement} for\n *     goog.dom.TagName.A).\n * @template T\n * @template R := cond(isUnknown(T), 'Element', T) =:\n * @deprecated Use {@link goog.dom.getElementsByTagNameAndClass} instead.\n */\ngoog.dom.$$ = goog.dom.getElementsByTagNameAndClass;\n\n\n/**\n * Sets multiple properties, and sometimes attributes, on an element. Note that\n * properties are simply object properties on the element instance, while\n * attributes are visible in the DOM. Many properties map to attributes with the\n * same names, some with different names, and there are also unmappable cases.\n *\n * This method sets properties by default (which means that custom attributes\n * are not supported). These are the exeptions (some of which is legacy):\n * - \"style\": Even though this is an attribute name, it is translated to a\n *   property, \"style.cssText\". Note that this property sanitizes and formats\n *   its value, unlike the attribute.\n * - \"class\": This is an attribute name, it is translated to the \"className\"\n *   property.\n * - \"for\": This is an attribute name, it is translated to the \"htmlFor\"\n *   property.\n * - Entries in {@see goog.dom.DIRECT_ATTRIBUTE_MAP_} are set as attributes,\n *   this is probably due to browser quirks.\n * - \"aria-*\", \"data-*\": Always set as attributes, they have no property\n *   counterparts.\n *\n * @param {Element} element DOM node to set properties on.\n * @param {Object} properties Hash of property:value pairs.\n *     Property values can be strings or goog.string.TypedString values (such as\n *     goog.html.SafeUrl).\n */\ngoog.dom.setProperties = function(element, properties) {\n  goog.object.forEach(properties, function(val, key) {\n    if (val && typeof val == 'object' && val.implementsGoogStringTypedString) {\n      val = val.getTypedStringValue();\n    }\n    if (key == 'style') {\n      element.style.cssText = val;\n    } else if (key == 'class') {\n      element.className = val;\n    } else if (key == 'for') {\n      element.htmlFor = val;\n    } else if (goog.dom.DIRECT_ATTRIBUTE_MAP_.hasOwnProperty(key)) {\n      element.setAttribute(goog.dom.DIRECT_ATTRIBUTE_MAP_[key], val);\n    } else if (\n        goog.string.startsWith(key, 'aria-') ||\n        goog.string.startsWith(key, 'data-')) {\n      element.setAttribute(key, val);\n    } else {\n      element[key] = val;\n    }\n  });\n};\n\n\n/**\n * Map of attributes that should be set using\n * element.setAttribute(key, val) instead of element[key] = val.  Used\n * by goog.dom.setProperties.\n *\n * @private {!Object<string, string>}\n * @const\n */\ngoog.dom.DIRECT_ATTRIBUTE_MAP_ = {\n  'cellpadding': 'cellPadding',\n  'cellspacing': 'cellSpacing',\n  'colspan': 'colSpan',\n  'frameborder': 'frameBorder',\n  'height': 'height',\n  'maxlength': 'maxLength',\n  'nonce': 'nonce',\n  'role': 'role',\n  'rowspan': 'rowSpan',\n  'type': 'type',\n  'usemap': 'useMap',\n  'valign': 'vAlign',\n  'width': 'width'\n};\n\n\n/**\n * Gets the dimensions of the viewport.\n *\n * Gecko Standards mode:\n * docEl.clientWidth  Width of viewport excluding scrollbar.\n * win.innerWidth     Width of viewport including scrollbar.\n * body.clientWidth   Width of body element.\n *\n * docEl.clientHeight Height of viewport excluding scrollbar.\n * win.innerHeight    Height of viewport including scrollbar.\n * body.clientHeight  Height of document.\n *\n * Gecko Backwards compatible mode:\n * docEl.clientWidth  Width of viewport excluding scrollbar.\n * win.innerWidth     Width of viewport including scrollbar.\n * body.clientWidth   Width of viewport excluding scrollbar.\n *\n * docEl.clientHeight Height of document.\n * win.innerHeight    Height of viewport including scrollbar.\n * body.clientHeight  Height of viewport excluding scrollbar.\n *\n * IE6/7 Standards mode:\n * docEl.clientWidth  Width of viewport excluding scrollbar.\n * win.innerWidth     Undefined.\n * body.clientWidth   Width of body element.\n *\n * docEl.clientHeight Height of viewport excluding scrollbar.\n * win.innerHeight    Undefined.\n * body.clientHeight  Height of document element.\n *\n * IE5 + IE6/7 Backwards compatible mode:\n * docEl.clientWidth  0.\n * win.innerWidth     Undefined.\n * body.clientWidth   Width of viewport excluding scrollbar.\n *\n * docEl.clientHeight 0.\n * win.innerHeight    Undefined.\n * body.clientHeight  Height of viewport excluding scrollbar.\n *\n * Opera 9 Standards and backwards compatible mode:\n * docEl.clientWidth  Width of viewport excluding scrollbar.\n * win.innerWidth     Width of viewport including scrollbar.\n * body.clientWidth   Width of viewport excluding scrollbar.\n *\n * docEl.clientHeight Height of document.\n * win.innerHeight    Height of viewport including scrollbar.\n * body.clientHeight  Height of viewport excluding scrollbar.\n *\n * WebKit:\n * Safari 2\n * docEl.clientHeight Same as scrollHeight.\n * docEl.clientWidth  Same as innerWidth.\n * win.innerWidth     Width of viewport excluding scrollbar.\n * win.innerHeight    Height of the viewport including scrollbar.\n * frame.innerHeight  Height of the viewport exluding scrollbar.\n *\n * Safari 3 (tested in 522)\n *\n * docEl.clientWidth  Width of viewport excluding scrollbar.\n * docEl.clientHeight Height of viewport excluding scrollbar in strict mode.\n * body.clientHeight  Height of viewport excluding scrollbar in quirks mode.\n *\n * @param {Window=} opt_window Optional window element to test.\n * @return {!goog.math.Size} Object with values 'width' and 'height'.\n */\ngoog.dom.getViewportSize = function(opt_window) {\n  // TODO(arv): This should not take an argument\n  return goog.dom.getViewportSize_(opt_window || window);\n};\n\n\n/**\n * Helper for `getViewportSize`.\n * @param {Window} win The window to get the view port size for.\n * @return {!goog.math.Size} Object with values 'width' and 'height'.\n * @private\n */\ngoog.dom.getViewportSize_ = function(win) {\n  var doc = win.document;\n  var el = goog.dom.isCss1CompatMode_(doc) ? doc.documentElement : doc.body;\n  return new goog.math.Size(el.clientWidth, el.clientHeight);\n};\n\n\n/**\n * Calculates the height of the document.\n *\n * @return {number} The height of the current document.\n */\ngoog.dom.getDocumentHeight = function() {\n  return goog.dom.getDocumentHeight_(window);\n};\n\n/**\n * Calculates the height of the document of the given window.\n *\n * @param {!Window} win The window whose document height to retrieve.\n * @return {number} The height of the document of the given window.\n */\ngoog.dom.getDocumentHeightForWindow = function(win) {\n  return goog.dom.getDocumentHeight_(win);\n};\n\n/**\n * Calculates the height of the document of the given window.\n *\n * Function code copied from the opensocial gadget api:\n *   gadgets.window.adjustHeight(opt_height)\n *\n * @private\n * @param {!Window} win The window whose document height to retrieve.\n * @return {number} The height of the document of the given window.\n */\ngoog.dom.getDocumentHeight_ = function(win) {\n  // NOTE(eae): This method will return the window size rather than the document\n  // size in webkit quirks mode.\n  var doc = win.document;\n  var height = 0;\n\n  if (doc) {\n    // Calculating inner content height is hard and different between\n    // browsers rendering in Strict vs. Quirks mode.  We use a combination of\n    // three properties within document.body and document.documentElement:\n    // - scrollHeight\n    // - offsetHeight\n    // - clientHeight\n    // These values differ significantly between browsers and rendering modes.\n    // But there are patterns.  It just takes a lot of time and persistence\n    // to figure out.\n\n    var body = doc.body;\n    var docEl = /** @type {!HTMLElement} */ (doc.documentElement);\n    if (!(docEl && body)) {\n      return 0;\n    }\n\n    // Get the height of the viewport\n    var vh = goog.dom.getViewportSize_(win).height;\n    if (goog.dom.isCss1CompatMode_(doc) && docEl.scrollHeight) {\n      // In Strict mode:\n      // The inner content height is contained in either:\n      //    document.documentElement.scrollHeight\n      //    document.documentElement.offsetHeight\n      // Based on studying the values output by different browsers,\n      // use the value that's NOT equal to the viewport height found above.\n      height =\n          docEl.scrollHeight != vh ? docEl.scrollHeight : docEl.offsetHeight;\n    } else {\n      // In Quirks mode:\n      // documentElement.clientHeight is equal to documentElement.offsetHeight\n      // except in IE.  In most browsers, document.documentElement can be used\n      // to calculate the inner content height.\n      // However, in other browsers (e.g. IE), document.body must be used\n      // instead.  How do we know which one to use?\n      // If document.documentElement.clientHeight does NOT equal\n      // document.documentElement.offsetHeight, then use document.body.\n      var sh = docEl.scrollHeight;\n      var oh = docEl.offsetHeight;\n      if (docEl.clientHeight != oh) {\n        sh = body.scrollHeight;\n        oh = body.offsetHeight;\n      }\n\n      // Detect whether the inner content height is bigger or smaller\n      // than the bounding box (viewport).  If bigger, take the larger\n      // value.  If smaller, take the smaller value.\n      if (sh > vh) {\n        // Content is larger\n        height = sh > oh ? sh : oh;\n      } else {\n        // Content is smaller\n        height = sh < oh ? sh : oh;\n      }\n    }\n  }\n\n  return height;\n};\n\n\n/**\n * Gets the page scroll distance as a coordinate object.\n *\n * @param {Window=} opt_window Optional window element to test.\n * @return {!goog.math.Coordinate} Object with values 'x' and 'y'.\n * @deprecated Use {@link goog.dom.getDocumentScroll} instead.\n */\ngoog.dom.getPageScroll = function(opt_window) {\n  var win = opt_window || goog.global || window;\n  return goog.dom.getDomHelper(win.document).getDocumentScroll();\n};\n\n\n/**\n * Gets the document scroll distance as a coordinate object.\n *\n * @return {!goog.math.Coordinate} Object with values 'x' and 'y'.\n */\ngoog.dom.getDocumentScroll = function() {\n  return goog.dom.getDocumentScroll_(document);\n};\n\n\n/**\n * Helper for `getDocumentScroll`.\n *\n * @param {!Document} doc The document to get the scroll for.\n * @return {!goog.math.Coordinate} Object with values 'x' and 'y'.\n * @private\n */\ngoog.dom.getDocumentScroll_ = function(doc) {\n  var el = goog.dom.getDocumentScrollElement_(doc);\n  var win = goog.dom.getWindow_(doc);\n  if (goog.userAgent.IE && goog.userAgent.isVersionOrHigher('10') &&\n      win.pageYOffset != el.scrollTop) {\n    // The keyboard on IE10 touch devices shifts the page using the pageYOffset\n    // without modifying scrollTop. For this case, we want the body scroll\n    // offsets.\n    return new goog.math.Coordinate(el.scrollLeft, el.scrollTop);\n  }\n  return new goog.math.Coordinate(\n      win.pageXOffset || el.scrollLeft, win.pageYOffset || el.scrollTop);\n};\n\n\n/**\n * Gets the document scroll element.\n * @return {!Element} Scrolling element.\n */\ngoog.dom.getDocumentScrollElement = function() {\n  return goog.dom.getDocumentScrollElement_(document);\n};\n\n\n/**\n * Helper for `getDocumentScrollElement`.\n * @param {!Document} doc The document to get the scroll element for.\n * @return {!Element} Scrolling element.\n * @private\n */\ngoog.dom.getDocumentScrollElement_ = function(doc) {\n  // Old WebKit needs body.scrollLeft in both quirks mode and strict mode. We\n  // also default to the documentElement if the document does not have a body\n  // (e.g. a SVG document).\n  // Uses http://dev.w3.org/csswg/cssom-view/#dom-document-scrollingelement to\n  // avoid trying to guess about browser behavior from the UA string.\n  if (doc.scrollingElement) {\n    return doc.scrollingElement;\n  }\n  if (!goog.userAgent.WEBKIT && goog.dom.isCss1CompatMode_(doc)) {\n    return doc.documentElement;\n  }\n  return doc.body || doc.documentElement;\n};\n\n\n/**\n * Gets the window object associated with the given document.\n *\n * @param {Document=} opt_doc  Document object to get window for.\n * @return {!Window} The window associated with the given document.\n */\ngoog.dom.getWindow = function(opt_doc) {\n  // TODO(arv): This should not take an argument.\n  return opt_doc ? goog.dom.getWindow_(opt_doc) : window;\n};\n\n\n/**\n * Helper for `getWindow`.\n *\n * @param {!Document} doc  Document object to get window for.\n * @return {!Window} The window associated with the given document.\n * @private\n */\ngoog.dom.getWindow_ = function(doc) {\n  return /** @type {!Window} */ (doc.parentWindow || doc.defaultView);\n};\n\n\n/**\n * Returns a dom node with a set of attributes.  This function accepts varargs\n * for subsequent nodes to be added.  Subsequent nodes will be added to the\n * first node as childNodes.\n *\n * So:\n * <code>createDom(goog.dom.TagName.DIV, null, createDom(goog.dom.TagName.P),\n * createDom(goog.dom.TagName.P));</code> would return a div with two child\n * paragraphs\n *\n * This function uses {@link goog.dom.setProperties} to set attributes: the\n * `opt_attributes` parameter follows the same rules.\n *\n * @param {string|!goog.dom.TagName<T>} tagName Tag to create.\n * @param {?Object|?Array<string>|string=} opt_attributes If object, then a map\n *     of name-value pairs for attributes. If a string, then this is the\n *     className of the new element. If an array, the elements will be joined\n *     together as the className of the new element.\n * @param {...(Object|string|Array|NodeList|null|undefined)} var_args Further\n *     DOM nodes or strings for text nodes. If one of the var_args is an array\n *     or NodeList, its elements will be added as childNodes instead.\n * @return {R} Reference to a DOM node. The return type is {!Element} if tagName\n *     is a string or a more specific type if it is a member of\n *     goog.dom.TagName (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).\n * @template T\n * @template R := cond(isUnknown(T), 'Element', T) =:\n */\ngoog.dom.createDom = function(tagName, opt_attributes, var_args) {\n  return goog.dom.createDom_(document, arguments);\n};\n\n\n/**\n * Helper for `createDom`.\n * @param {!Document} doc The document to create the DOM in.\n * @param {!Arguments} args Argument object passed from the callers. See\n *     `goog.dom.createDom` for details.\n * @return {!Element} Reference to a DOM node.\n * @private\n */\ngoog.dom.createDom_ = function(doc, args) {\n  var tagName = String(args[0]);\n  var attributes = args[1];\n\n  // Internet Explorer is dumb:\n  // name: https://msdn.microsoft.com/en-us/library/ms534184(v=vs.85).aspx\n  // type: https://msdn.microsoft.com/en-us/library/ms534700(v=vs.85).aspx\n  // Also does not allow setting of 'type' attribute on 'input' or 'button'.\n  if (!goog.dom.BrowserFeature.CAN_ADD_NAME_OR_TYPE_ATTRIBUTES && attributes &&\n      (attributes.name || attributes.type)) {\n    var tagNameArr = ['<', tagName];\n    if (attributes.name) {\n      tagNameArr.push(' name=\"', goog.string.htmlEscape(attributes.name), '\"');\n    }\n    if (attributes.type) {\n      tagNameArr.push(' type=\"', goog.string.htmlEscape(attributes.type), '\"');\n\n      // Clone attributes map to remove 'type' without mutating the input.\n      var clone = {};\n      goog.object.extend(clone, attributes);\n\n      // JSCompiler can't see how goog.object.extend added this property,\n      // because it was essentially added by reflection.\n      // So it needs to be quoted.\n      delete clone['type'];\n\n      attributes = clone;\n    }\n    tagNameArr.push('>');\n    tagName = tagNameArr.join('');\n  }\n\n  var element = goog.dom.createElement_(doc, tagName);\n\n  if (attributes) {\n    if (typeof attributes === 'string') {\n      element.className = attributes;\n    } else if (Array.isArray(attributes)) {\n      element.className = attributes.join(' ');\n    } else {\n      goog.dom.setProperties(element, attributes);\n    }\n  }\n\n  if (args.length > 2) {\n    goog.dom.append_(doc, element, args, 2);\n  }\n\n  return element;\n};\n\n\n/**\n * Appends a node with text or other nodes.\n * @param {!Document} doc The document to create new nodes in.\n * @param {!Node} parent The node to append nodes to.\n * @param {!Arguments} args The values to add. See `goog.dom.append`.\n * @param {number} startIndex The index of the array to start from.\n * @private\n */\ngoog.dom.append_ = function(doc, parent, args, startIndex) {\n  function childHandler(child) {\n    // TODO(user): More coercion, ala MochiKit?\n    if (child) {\n      parent.appendChild(\n          typeof child === 'string' ? doc.createTextNode(child) : child);\n    }\n  }\n\n  for (var i = startIndex; i < args.length; i++) {\n    var arg = args[i];\n    // TODO(attila): Fix isArrayLike to return false for a text node.\n    if (goog.isArrayLike(arg) && !goog.dom.isNodeLike(arg)) {\n      // If the argument is a node list, not a real array, use a clone,\n      // because forEach can't be used to mutate a NodeList.\n      goog.array.forEach(\n          goog.dom.isNodeList(arg) ? goog.array.toArray(arg) : arg,\n          childHandler);\n    } else {\n      childHandler(arg);\n    }\n  }\n};\n\n\n/**\n * Alias for `createDom`.\n * @param {string|!goog.dom.TagName<T>} tagName Tag to create.\n * @param {?Object|?Array<string>|string=} opt_attributes If object, then a map\n *     of name-value pairs for attributes. If a string, then this is the\n *     className of the new element. If an array, the elements will be joined\n *     together as the className of the new element.\n * @param {...(Object|string|Array|NodeList|null|undefined)} var_args Further\n *     DOM nodes or strings for text nodes. If one of the var_args is an array,\n *     its children will be added as childNodes instead.\n * @return {R} Reference to a DOM node. The return type is {!Element} if tagName\n *     is a string or a more specific type if it is a member of\n *     goog.dom.TagName (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).\n * @template T\n * @template R := cond(isUnknown(T), 'Element', T) =:\n * @deprecated Use {@link goog.dom.createDom} instead.\n */\ngoog.dom.$dom = goog.dom.createDom;\n\n\n/**\n * Creates a new element.\n * @param {string|!goog.dom.TagName<T>} name Tag to create.\n * @return {R} The new element. The return type is {!Element} if name is\n *     a string or a more specific type if it is a member of goog.dom.TagName\n *     (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).\n * @template T\n * @template R := cond(isUnknown(T), 'Element', T) =:\n */\ngoog.dom.createElement = function(name) {\n  return goog.dom.createElement_(document, name);\n};\n\n\n/**\n * Creates a new element.\n * @param {!Document} doc The document to create the element in.\n * @param {string|!goog.dom.TagName<T>} name Tag to create.\n * @return {R} The new element. The return type is {!Element} if name is\n *     a string or a more specific type if it is a member of goog.dom.TagName\n *     (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).\n * @template T\n * @template R := cond(isUnknown(T), 'Element', T) =:\n * @private\n */\ngoog.dom.createElement_ = function(doc, name) {\n  name = String(name);\n  if (doc.contentType === 'application/xhtml+xml') name = name.toLowerCase();\n  return doc.createElement(name);\n};\n\n\n/**\n * Creates a new text node.\n * @param {number|string} content Content.\n * @return {!Text} The new text node.\n */\ngoog.dom.createTextNode = function(content) {\n  return document.createTextNode(String(content));\n};\n\n\n/**\n * Create a table.\n * @param {number} rows The number of rows in the table.  Must be >= 1.\n * @param {number} columns The number of columns in the table.  Must be >= 1.\n * @param {boolean=} opt_fillWithNbsp If true, fills table entries with\n *     `goog.string.Unicode.NBSP` characters.\n * @return {!Element} The created table.\n */\ngoog.dom.createTable = function(rows, columns, opt_fillWithNbsp) {\n  // TODO(mlourenco): Return HTMLTableElement, also in prototype function.\n  // Callers need to be updated to e.g. not assign numbers to table.cellSpacing.\n  return goog.dom.createTable_(document, rows, columns, !!opt_fillWithNbsp);\n};\n\n\n/**\n * Create a table.\n * @param {!Document} doc Document object to use to create the table.\n * @param {number} rows The number of rows in the table.  Must be >= 1.\n * @param {number} columns The number of columns in the table.  Must be >= 1.\n * @param {boolean} fillWithNbsp If true, fills table entries with\n *     `goog.string.Unicode.NBSP` characters.\n * @return {!HTMLTableElement} The created table.\n * @private\n */\ngoog.dom.createTable_ = function(doc, rows, columns, fillWithNbsp) {\n  var table = goog.dom.createElement_(doc, goog.dom.TagName.TABLE);\n  var tbody =\n      table.appendChild(goog.dom.createElement_(doc, goog.dom.TagName.TBODY));\n  for (var i = 0; i < rows; i++) {\n    var tr = goog.dom.createElement_(doc, goog.dom.TagName.TR);\n    for (var j = 0; j < columns; j++) {\n      var td = goog.dom.createElement_(doc, goog.dom.TagName.TD);\n      // IE <= 9 will create a text node if we set text content to the empty\n      // string, so we avoid doing it unless necessary. This ensures that the\n      // same DOM tree is returned on all browsers.\n      if (fillWithNbsp) {\n        goog.dom.setTextContent(td, goog.string.Unicode.NBSP);\n      }\n      tr.appendChild(td);\n    }\n    tbody.appendChild(tr);\n  }\n  return table;\n};\n\n\n\n/**\n * Creates a new Node from constant strings of HTML markup.\n * @param {...!goog.string.Const} var_args The HTML strings to concatenate then\n *     convert into a node.\n * @return {!Node}\n */\ngoog.dom.constHtmlToNode = function(var_args) {\n  var stringArray = goog.array.map(arguments, goog.string.Const.unwrap);\n  var safeHtml =\n      goog.html.uncheckedconversions\n          .safeHtmlFromStringKnownToSatisfyTypeContract(\n              goog.string.Const.from(\n                  'Constant HTML string, that gets turned into a ' +\n                  'Node later, so it will be automatically balanced.'),\n              stringArray.join(''));\n  return goog.dom.safeHtmlToNode(safeHtml);\n};\n\n\n/**\n * Converts HTML markup into a node. This is a safe version of\n * `goog.dom.htmlToDocumentFragment` which is now deleted.\n * @param {!goog.html.SafeHtml} html The HTML markup to convert.\n * @return {!Node} The resulting node.\n */\ngoog.dom.safeHtmlToNode = function(html) {\n  return goog.dom.safeHtmlToNode_(document, html);\n};\n\n\n/**\n * Helper for `safeHtmlToNode`.\n * @param {!Document} doc The document.\n * @param {!goog.html.SafeHtml} html The HTML markup to convert.\n * @return {!Node} The resulting node.\n * @private\n */\ngoog.dom.safeHtmlToNode_ = function(doc, html) {\n  var tempDiv = goog.dom.createElement_(doc, goog.dom.TagName.DIV);\n  if (goog.dom.BrowserFeature.INNER_HTML_NEEDS_SCOPED_ELEMENT) {\n    goog.dom.safe.setInnerHtml(\n        tempDiv, goog.html.SafeHtml.concat(goog.html.SafeHtml.BR, html));\n    tempDiv.removeChild(goog.asserts.assert(tempDiv.firstChild));\n  } else {\n    goog.dom.safe.setInnerHtml(tempDiv, html);\n  }\n  return goog.dom.childrenToNode_(doc, tempDiv);\n};\n\n\n/**\n * Helper for `safeHtmlToNode_`.\n * @param {!Document} doc The document.\n * @param {!Node} tempDiv The input node.\n * @return {!Node} The resulting node.\n * @private\n */\ngoog.dom.childrenToNode_ = function(doc, tempDiv) {\n  if (tempDiv.childNodes.length == 1) {\n    return tempDiv.removeChild(goog.asserts.assert(tempDiv.firstChild));\n  } else {\n    var fragment = doc.createDocumentFragment();\n    while (tempDiv.firstChild) {\n      fragment.appendChild(tempDiv.firstChild);\n    }\n    return fragment;\n  }\n};\n\n\n/**\n * Returns true if the browser is in \"CSS1-compatible\" (standards-compliant)\n * mode, false otherwise.\n * @return {boolean} True if in CSS1-compatible mode.\n */\ngoog.dom.isCss1CompatMode = function() {\n  return goog.dom.isCss1CompatMode_(document);\n};\n\n\n/**\n * Returns true if the browser is in \"CSS1-compatible\" (standards-compliant)\n * mode, false otherwise.\n * @param {!Document} doc The document to check.\n * @return {boolean} True if in CSS1-compatible mode.\n * @private\n */\ngoog.dom.isCss1CompatMode_ = function(doc) {\n  if (goog.dom.COMPAT_MODE_KNOWN_) {\n    return goog.dom.ASSUME_STANDARDS_MODE;\n  }\n\n  return doc.compatMode == 'CSS1Compat';\n};\n\n\n/**\n * Determines if the given node can contain children, intended to be used for\n * HTML generation.\n *\n * IE natively supports node.canHaveChildren but has inconsistent behavior.\n * Prior to IE8 the base tag allows children and in IE9 all nodes return true\n * for canHaveChildren.\n *\n * In practice all non-IE browsers allow you to add children to any node, but\n * the behavior is inconsistent:\n *\n * <pre>\n *   var a = goog.dom.createElement(goog.dom.TagName.BR);\n *   a.appendChild(document.createTextNode('foo'));\n *   a.appendChild(document.createTextNode('bar'));\n *   console.log(a.childNodes.length);  // 2\n *   console.log(a.innerHTML);  // Chrome: \"\", IE9: \"foobar\", FF3.5: \"foobar\"\n * </pre>\n *\n * For more information, see:\n * http://dev.w3.org/html5/markup/syntax.html#syntax-elements\n *\n * TODO(user): Rename shouldAllowChildren() ?\n *\n * @param {Node} node The node to check.\n * @return {boolean} Whether the node can contain children.\n */\ngoog.dom.canHaveChildren = function(node) {\n  if (node.nodeType != goog.dom.NodeType.ELEMENT) {\n    return false;\n  }\n  switch (/** @type {!Element} */ (node).tagName) {\n    case String(goog.dom.TagName.APPLET):\n    case String(goog.dom.TagName.AREA):\n    case String(goog.dom.TagName.BASE):\n    case String(goog.dom.TagName.BR):\n    case String(goog.dom.TagName.COL):\n    case String(goog.dom.TagName.COMMAND):\n    case String(goog.dom.TagName.EMBED):\n    case String(goog.dom.TagName.FRAME):\n    case String(goog.dom.TagName.HR):\n    case String(goog.dom.TagName.IMG):\n    case String(goog.dom.TagName.INPUT):\n    case String(goog.dom.TagName.IFRAME):\n    case String(goog.dom.TagName.ISINDEX):\n    case String(goog.dom.TagName.KEYGEN):\n    case String(goog.dom.TagName.LINK):\n    case String(goog.dom.TagName.NOFRAMES):\n    case String(goog.dom.TagName.NOSCRIPT):\n    case String(goog.dom.TagName.META):\n    case String(goog.dom.TagName.OBJECT):\n    case String(goog.dom.TagName.PARAM):\n    case String(goog.dom.TagName.SCRIPT):\n    case String(goog.dom.TagName.SOURCE):\n    case String(goog.dom.TagName.STYLE):\n    case String(goog.dom.TagName.TRACK):\n    case String(goog.dom.TagName.WBR):\n      return false;\n  }\n  return true;\n};\n\n\n/**\n * Appends a child to a node.\n * @param {Node} parent Parent.\n * @param {Node} child Child.\n */\ngoog.dom.appendChild = function(parent, child) {\n  goog.asserts.assert(\n      parent != null && child != null,\n      'goog.dom.appendChild expects non-null arguments');\n  parent.appendChild(child);\n};\n\n\n/**\n * Appends a node with text or other nodes.\n * @param {!Node} parent The node to append nodes to.\n * @param {...goog.dom.Appendable} var_args The things to append to the node.\n *     If this is a Node it is appended as is.\n *     If this is a string then a text node is appended.\n *     If this is an array like object then fields 0 to length - 1 are appended.\n */\ngoog.dom.append = function(parent, var_args) {\n  goog.dom.append_(goog.dom.getOwnerDocument(parent), parent, arguments, 1);\n};\n\n\n/**\n * Removes all the child nodes on a DOM node.\n * @param {Node} node Node to remove children from.\n */\ngoog.dom.removeChildren = function(node) {\n  // Note: Iterations over live collections can be slow, this is the fastest\n  // we could find. The double parenthesis are used to prevent JsCompiler and\n  // strict warnings.\n  var child;\n  while ((child = node.firstChild)) {\n    node.removeChild(child);\n  }\n};\n\n\n/**\n * Inserts a new node before an existing reference node (i.e. as the previous\n * sibling). If the reference node has no parent, then does nothing.\n * @param {Node} newNode Node to insert.\n * @param {Node} refNode Reference node to insert before.\n */\ngoog.dom.insertSiblingBefore = function(newNode, refNode) {\n  goog.asserts.assert(\n      newNode != null && refNode != null,\n      'goog.dom.insertSiblingBefore expects non-null arguments');\n  if (refNode.parentNode) {\n    refNode.parentNode.insertBefore(newNode, refNode);\n  }\n};\n\n\n/**\n * Inserts a new node after an existing reference node (i.e. as the next\n * sibling). If the reference node has no parent, then does nothing.\n * @param {Node} newNode Node to insert.\n * @param {Node} refNode Reference node to insert after.\n */\ngoog.dom.insertSiblingAfter = function(newNode, refNode) {\n  goog.asserts.assert(\n      newNode != null && refNode != null,\n      'goog.dom.insertSiblingAfter expects non-null arguments');\n  if (refNode.parentNode) {\n    refNode.parentNode.insertBefore(newNode, refNode.nextSibling);\n  }\n};\n\n\n/**\n * Insert a child at a given index. If index is larger than the number of child\n * nodes that the parent currently has, the node is inserted as the last child\n * node.\n * @param {Element} parent The element into which to insert the child.\n * @param {Node} child The element to insert.\n * @param {number} index The index at which to insert the new child node. Must\n *     not be negative.\n */\ngoog.dom.insertChildAt = function(parent, child, index) {\n  // Note that if the second argument is null, insertBefore\n  // will append the child at the end of the list of children.\n  goog.asserts.assert(\n      parent != null, 'goog.dom.insertChildAt expects a non-null parent');\n  parent.insertBefore(\n      /** @type {!Node} */ (child), parent.childNodes[index] || null);\n};\n\n\n/**\n * Removes a node from its parent.\n * @param {Node} node The node to remove.\n * @return {Node} The node removed if removed; else, null.\n */\ngoog.dom.removeNode = function(node) {\n  return node && node.parentNode ? node.parentNode.removeChild(node) : null;\n};\n\n\n/**\n * Replaces a node in the DOM tree. Will do nothing if `oldNode` has no\n * parent.\n * @param {Node} newNode Node to insert.\n * @param {Node} oldNode Node to replace.\n */\ngoog.dom.replaceNode = function(newNode, oldNode) {\n  goog.asserts.assert(\n      newNode != null && oldNode != null,\n      'goog.dom.replaceNode expects non-null arguments');\n  var parent = oldNode.parentNode;\n  if (parent) {\n    parent.replaceChild(newNode, oldNode);\n  }\n};\n\n\n/**\n * Replaces child nodes of `target` with child nodes of `source`. This is\n * roughly equivalent to `target.innerHTML = source.innerHTML` which is not\n * compatible with Trusted Types.\n * @param {?Node} target Node to clean and replace its children.\n * @param {?Node} source Node to get the children from. The nodes will be cloned\n *     so they will stay in source.\n */\ngoog.dom.copyContents = function(target, source) {\n  goog.asserts.assert(\n      target != null && source != null,\n      'goog.dom.copyContents expects non-null arguments');\n  var childNodes = source.cloneNode(/* deep= */ true).childNodes;\n  goog.dom.removeChildren(target);\n  while (childNodes.length) {\n    target.appendChild(childNodes[0]);\n  }\n};\n\n\n/**\n * Flattens an element. That is, removes it and replace it with its children.\n * Does nothing if the element is not in the document.\n * @param {Element} element The element to flatten.\n * @return {Element|undefined} The original element, detached from the document\n *     tree, sans children; or undefined, if the element was not in the document\n *     to begin with.\n */\ngoog.dom.flattenElement = function(element) {\n  var child, parent = element.parentNode;\n  if (parent && parent.nodeType != goog.dom.NodeType.DOCUMENT_FRAGMENT) {\n    // Use IE DOM method (supported by Opera too) if available\n    if (element.removeNode) {\n      return /** @type {Element} */ (element.removeNode(false));\n    } else {\n      // Move all children of the original node up one level.\n      while ((child = element.firstChild)) {\n        parent.insertBefore(child, element);\n      }\n\n      // Detach the original element.\n      return /** @type {Element} */ (goog.dom.removeNode(element));\n    }\n  }\n};\n\n\n/**\n * Returns an array containing just the element children of the given element.\n * @param {Element} element The element whose element children we want.\n * @return {!(Array<!Element>|NodeList<!Element>)} An array or array-like list\n *     of just the element children of the given element.\n */\ngoog.dom.getChildren = function(element) {\n  // We check if the children attribute is supported for child elements\n  // since IE8 misuses the attribute by also including comments.\n  if (goog.dom.BrowserFeature.CAN_USE_CHILDREN_ATTRIBUTE &&\n      element.children != undefined) {\n    return element.children;\n  }\n  // Fall back to manually filtering the element's child nodes.\n  return goog.array.filter(element.childNodes, function(node) {\n    return node.nodeType == goog.dom.NodeType.ELEMENT;\n  });\n};\n\n\n/**\n * Returns the first child node that is an element.\n * @param {Node} node The node to get the first child element of.\n * @return {Element} The first child node of `node` that is an element.\n */\ngoog.dom.getFirstElementChild = function(node) {\n  if (node.firstElementChild !== undefined) {\n    return /** @type {!Element} */ (node).firstElementChild;\n  }\n  return goog.dom.getNextElementNode_(node.firstChild, true);\n};\n\n\n/**\n * Returns the last child node that is an element.\n * @param {Node} node The node to get the last child element of.\n * @return {Element} The last child node of `node` that is an element.\n */\ngoog.dom.getLastElementChild = function(node) {\n  if (node.lastElementChild !== undefined) {\n    return /** @type {!Element} */ (node).lastElementChild;\n  }\n  return goog.dom.getNextElementNode_(node.lastChild, false);\n};\n\n\n/**\n * Returns the first next sibling that is an element.\n * @param {Node} node The node to get the next sibling element of.\n * @return {Element} The next sibling of `node` that is an element.\n */\ngoog.dom.getNextElementSibling = function(node) {\n  if (node.nextElementSibling !== undefined) {\n    return /** @type {!Element} */ (node).nextElementSibling;\n  }\n  return goog.dom.getNextElementNode_(node.nextSibling, true);\n};\n\n\n/**\n * Returns the first previous sibling that is an element.\n * @param {Node} node The node to get the previous sibling element of.\n * @return {Element} The first previous sibling of `node` that is\n *     an element.\n */\ngoog.dom.getPreviousElementSibling = function(node) {\n  if (node.previousElementSibling !== undefined) {\n    return /** @type {!Element} */ (node).previousElementSibling;\n  }\n  return goog.dom.getNextElementNode_(node.previousSibling, false);\n};\n\n\n/**\n * Returns the first node that is an element in the specified direction,\n * starting with `node`.\n * @param {Node} node The node to get the next element from.\n * @param {boolean} forward Whether to look forwards or backwards.\n * @return {Element} The first element.\n * @private\n */\ngoog.dom.getNextElementNode_ = function(node, forward) {\n  while (node && node.nodeType != goog.dom.NodeType.ELEMENT) {\n    node = forward ? node.nextSibling : node.previousSibling;\n  }\n\n  return /** @type {Element} */ (node);\n};\n\n\n/**\n * Returns the next node in source order from the given node.\n * @param {Node} node The node.\n * @return {Node} The next node in the DOM tree, or null if this was the last\n *     node.\n */\ngoog.dom.getNextNode = function(node) {\n  if (!node) {\n    return null;\n  }\n\n  if (node.firstChild) {\n    return node.firstChild;\n  }\n\n  while (node && !node.nextSibling) {\n    node = node.parentNode;\n  }\n\n  return node ? node.nextSibling : null;\n};\n\n\n/**\n * Returns the previous node in source order from the given node.\n * @param {Node} node The node.\n * @return {Node} The previous node in the DOM tree, or null if this was the\n *     first node.\n */\ngoog.dom.getPreviousNode = function(node) {\n  if (!node) {\n    return null;\n  }\n\n  if (!node.previousSibling) {\n    return node.parentNode;\n  }\n\n  node = node.previousSibling;\n  while (node && node.lastChild) {\n    node = node.lastChild;\n  }\n\n  return node;\n};\n\n\n/**\n * Whether the object looks like a DOM node.\n * @param {?} obj The object being tested for node likeness.\n * @return {boolean} Whether the object looks like a DOM node.\n */\ngoog.dom.isNodeLike = function(obj) {\n  return goog.isObject(obj) && obj.nodeType > 0;\n};\n\n\n/**\n * Whether the object looks like an Element.\n * @param {?} obj The object being tested for Element likeness.\n * @return {boolean} Whether the object looks like an Element.\n */\ngoog.dom.isElement = function(obj) {\n  return goog.isObject(obj) && obj.nodeType == goog.dom.NodeType.ELEMENT;\n};\n\n\n/**\n * Returns true if the specified value is a Window object. This includes the\n * global window for HTML pages, and iframe windows.\n * @param {?} obj Variable to test.\n * @return {boolean} Whether the variable is a window.\n */\ngoog.dom.isWindow = function(obj) {\n  return goog.isObject(obj) && obj['window'] == obj;\n};\n\n\n/**\n * Returns an element's parent, if it's an Element.\n * @param {Element} element The DOM element.\n * @return {Element} The parent, or null if not an Element.\n */\ngoog.dom.getParentElement = function(element) {\n  var parent;\n  if (goog.dom.BrowserFeature.CAN_USE_PARENT_ELEMENT_PROPERTY) {\n    var isIe9 = goog.userAgent.IE && goog.userAgent.isVersionOrHigher('9') &&\n        !goog.userAgent.isVersionOrHigher('10');\n    // SVG elements in IE9 can't use the parentElement property.\n    // goog.global['SVGElement'] is not defined in IE9 quirks mode.\n    if (!(isIe9 && goog.global['SVGElement'] &&\n          element instanceof goog.global['SVGElement'])) {\n      parent = element.parentElement;\n      if (parent) {\n        return parent;\n      }\n    }\n  }\n  parent = element.parentNode;\n  return goog.dom.isElement(parent) ? /** @type {!Element} */ (parent) : null;\n};\n\n\n/**\n * Whether a node contains another node.\n * @param {?Node|undefined} parent The node that should contain the other node.\n * @param {?Node|undefined} descendant The node to test presence of.\n * @return {boolean} Whether the parent node contains the descendant node.\n */\ngoog.dom.contains = function(parent, descendant) {\n  if (!parent || !descendant) {\n    return false;\n  }\n  // We use browser specific methods for this if available since it is faster\n  // that way.\n\n  // IE DOM\n  if (parent.contains && descendant.nodeType == goog.dom.NodeType.ELEMENT) {\n    return parent == descendant || parent.contains(descendant);\n  }\n\n  // W3C DOM Level 3\n  if (typeof parent.compareDocumentPosition != 'undefined') {\n    return parent == descendant ||\n        Boolean(parent.compareDocumentPosition(descendant) & 16);\n  }\n\n  // W3C DOM Level 1\n  while (descendant && parent != descendant) {\n    descendant = descendant.parentNode;\n  }\n  return descendant == parent;\n};\n\n\n/**\n * Compares the document order of two nodes, returning 0 if they are the same\n * node, a negative number if node1 is before node2, and a positive number if\n * node2 is before node1.  Note that we compare the order the tags appear in the\n * document so in the tree <b><i>text</i></b> the B node is considered to be\n * before the I node.\n *\n * @param {Node} node1 The first node to compare.\n * @param {Node} node2 The second node to compare.\n * @return {number} 0 if the nodes are the same node, a negative number if node1\n *     is before node2, and a positive number if node2 is before node1.\n */\ngoog.dom.compareNodeOrder = function(node1, node2) {\n  // Fall out quickly for equality.\n  if (node1 == node2) {\n    return 0;\n  }\n\n  // Use compareDocumentPosition where available\n  if (node1.compareDocumentPosition) {\n    // 4 is the bitmask for FOLLOWS.\n    return node1.compareDocumentPosition(node2) & 2 ? 1 : -1;\n  }\n\n  // Special case for document nodes on IE 7 and 8.\n  if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) {\n    if (node1.nodeType == goog.dom.NodeType.DOCUMENT) {\n      return -1;\n    }\n    if (node2.nodeType == goog.dom.NodeType.DOCUMENT) {\n      return 1;\n    }\n  }\n\n  // Process in IE using sourceIndex - we check to see if the first node has\n  // a source index or if its parent has one.\n  if ('sourceIndex' in node1 ||\n      (node1.parentNode && 'sourceIndex' in node1.parentNode)) {\n    var isElement1 = node1.nodeType == goog.dom.NodeType.ELEMENT;\n    var isElement2 = node2.nodeType == goog.dom.NodeType.ELEMENT;\n\n    if (isElement1 && isElement2) {\n      return node1.sourceIndex - node2.sourceIndex;\n    } else {\n      var parent1 = node1.parentNode;\n      var parent2 = node2.parentNode;\n\n      if (parent1 == parent2) {\n        return goog.dom.compareSiblingOrder_(node1, node2);\n      }\n\n      if (!isElement1 && goog.dom.contains(parent1, node2)) {\n        return -1 * goog.dom.compareParentsDescendantNodeIe_(node1, node2);\n      }\n\n\n      if (!isElement2 && goog.dom.contains(parent2, node1)) {\n        return goog.dom.compareParentsDescendantNodeIe_(node2, node1);\n      }\n\n      return (isElement1 ? node1.sourceIndex : parent1.sourceIndex) -\n          (isElement2 ? node2.sourceIndex : parent2.sourceIndex);\n    }\n  }\n\n  // For Safari, we compare ranges.\n  var doc = goog.dom.getOwnerDocument(node1);\n\n  var range1, range2;\n  range1 = doc.createRange();\n  range1.selectNode(node1);\n  range1.collapse(true);\n\n  range2 = doc.createRange();\n  range2.selectNode(node2);\n  range2.collapse(true);\n\n  return range1.compareBoundaryPoints(\n      goog.global['Range'].START_TO_END, range2);\n};\n\n\n/**\n * Utility function to compare the position of two nodes, when\n * `textNode`'s parent is an ancestor of `node`.  If this entry\n * condition is not met, this function will attempt to reference a null object.\n * @param {!Node} textNode The textNode to compare.\n * @param {Node} node The node to compare.\n * @return {number} -1 if node is before textNode, +1 otherwise.\n * @private\n */\ngoog.dom.compareParentsDescendantNodeIe_ = function(textNode, node) {\n  var parent = textNode.parentNode;\n  if (parent == node) {\n    // If textNode is a child of node, then node comes first.\n    return -1;\n  }\n  var sibling = node;\n  while (sibling.parentNode != parent) {\n    sibling = sibling.parentNode;\n  }\n  return goog.dom.compareSiblingOrder_(sibling, textNode);\n};\n\n\n/**\n * Utility function to compare the position of two nodes known to be non-equal\n * siblings.\n * @param {Node} node1 The first node to compare.\n * @param {!Node} node2 The second node to compare.\n * @return {number} -1 if node1 is before node2, +1 otherwise.\n * @private\n */\ngoog.dom.compareSiblingOrder_ = function(node1, node2) {\n  var s = node2;\n  while ((s = s.previousSibling)) {\n    if (s == node1) {\n      // We just found node1 before node2.\n      return -1;\n    }\n  }\n\n  // Since we didn't find it, node1 must be after node2.\n  return 1;\n};\n\n\n/**\n * Find the deepest common ancestor of the given nodes.\n * @param {...Node} var_args The nodes to find a common ancestor of.\n * @return {Node} The common ancestor of the nodes, or null if there is none.\n *     null will only be returned if two or more of the nodes are from different\n *     documents.\n */\ngoog.dom.findCommonAncestor = function(var_args) {\n  var i, count = arguments.length;\n  if (!count) {\n    return null;\n  } else if (count == 1) {\n    return arguments[0];\n  }\n\n  var paths = [];\n  var minLength = Infinity;\n  for (i = 0; i < count; i++) {\n    // Compute the list of ancestors.\n    var ancestors = [];\n    var node = arguments[i];\n    while (node) {\n      ancestors.unshift(node);\n      node = node.parentNode;\n    }\n\n    // Save the list for comparison.\n    paths.push(ancestors);\n    minLength = Math.min(minLength, ancestors.length);\n  }\n  var output = null;\n  for (i = 0; i < minLength; i++) {\n    var first = paths[0][i];\n    for (var j = 1; j < count; j++) {\n      if (first != paths[j][i]) {\n        return output;\n      }\n    }\n    output = first;\n  }\n  return output;\n};\n\n\n/**\n * Returns whether node is in a document or detached. Throws an error if node\n * itself is a document. This specifically handles two cases beyond naive use of\n * builtins: (1) it works correctly in IE, and (2) it works for elements from\n * different documents/iframes. If neither of these considerations are relevant\n * then a simple `document.contains(node)` may be used instead.\n * @param {!Node} node\n * @return {boolean}\n */\ngoog.dom.isInDocument = function(node) {\n  return (node.ownerDocument.compareDocumentPosition(node) & 16) == 16;\n};\n\n\n/**\n * Returns the owner document for a node.\n * @param {Node|Window} node The node to get the document for.\n * @return {!Document} The document owning the node.\n */\ngoog.dom.getOwnerDocument = function(node) {\n  // TODO(nnaze): Update param signature to be non-nullable.\n  goog.asserts.assert(node, 'Node cannot be null or undefined.');\n  return /** @type {!Document} */ (\n      node.nodeType == goog.dom.NodeType.DOCUMENT ? node : node.ownerDocument ||\n              node.document);\n};\n\n\n/**\n * Cross-browser function for getting the document element of a frame or iframe.\n * @param {Element} frame Frame element.\n * @return {!Document} The frame content document.\n */\ngoog.dom.getFrameContentDocument = function(frame) {\n  return frame.contentDocument ||\n      /** @type {!HTMLFrameElement} */ (frame).contentWindow.document;\n};\n\n\n/**\n * Cross-browser function for getting the window of a frame or iframe.\n * @param {Element} frame Frame element.\n * @return {Window} The window associated with the given frame, or null if none\n *     exists.\n */\ngoog.dom.getFrameContentWindow = function(frame) {\n  try {\n    return frame.contentWindow ||\n        (frame.contentDocument ? goog.dom.getWindow(frame.contentDocument) :\n                                 null);\n  } catch (e) {\n    // NOTE(user): In IE8, checking the contentWindow or contentDocument\n    // properties will throw a \"Unspecified Error\" exception if the iframe is\n    // not inserted in the DOM. If we get this we can be sure that no window\n    // exists, so return null.\n  }\n  return null;\n};\n\n\n/**\n * Sets the text content of a node, with cross-browser support.\n * @param {Node} node The node to change the text content of.\n * @param {string|number} text The value that should replace the node's content.\n */\ngoog.dom.setTextContent = function(node, text) {\n  goog.asserts.assert(\n      node != null,\n      'goog.dom.setTextContent expects a non-null value for node');\n\n  if ('textContent' in node) {\n    node.textContent = text;\n  } else if (node.nodeType == goog.dom.NodeType.TEXT) {\n    /** @type {!Text} */ (node).data = String(text);\n  } else if (\n      node.firstChild && node.firstChild.nodeType == goog.dom.NodeType.TEXT) {\n    // If the first child is a text node we just change its data and remove the\n    // rest of the children.\n    while (node.lastChild != node.firstChild) {\n      node.removeChild(goog.asserts.assert(node.lastChild));\n    }\n    /** @type {!Text} */ (node.firstChild).data = String(text);\n  } else {\n    goog.dom.removeChildren(node);\n    var doc = goog.dom.getOwnerDocument(node);\n    node.appendChild(doc.createTextNode(String(text)));\n  }\n};\n\n\n/**\n * Gets the outerHTML of a node, which is like innerHTML, except that it\n * actually contains the HTML of the node itself.\n * @param {Element} element The element to get the HTML of.\n * @return {string} The outerHTML of the given element.\n */\ngoog.dom.getOuterHtml = function(element) {\n  goog.asserts.assert(\n      element !== null,\n      'goog.dom.getOuterHtml expects a non-null value for element');\n  // IE, Opera and WebKit all have outerHTML.\n  if ('outerHTML' in element) {\n    return element.outerHTML;\n  } else {\n    var doc = goog.dom.getOwnerDocument(element);\n    var div = goog.dom.createElement_(doc, goog.dom.TagName.DIV);\n    div.appendChild(element.cloneNode(true));\n    return div.innerHTML;\n  }\n};\n\n\n/**\n * Finds the first descendant node that matches the filter function, using depth\n * first search. This function offers the most general purpose way of finding a\n * matching element.\n *\n * Prefer using `querySelector` if the matching criteria can be expressed as a\n * CSS selector, or `goog.dom.findElement` if you would filter for `nodeType ==\n * Node.ELEMENT_NODE`.\n *\n * @param {Node} root The root of the tree to search.\n * @param {function(Node) : boolean} p The filter function.\n * @return {Node|undefined} The found node or undefined if none is found.\n */\ngoog.dom.findNode = function(root, p) {\n  var rv = [];\n  var found = goog.dom.findNodes_(root, p, rv, true);\n  return found ? rv[0] : undefined;\n};\n\n\n/**\n * Finds all the descendant nodes that match the filter function, using depth\n * first search. This function offers the most general-purpose way\n * of finding a set of matching elements.\n *\n * Prefer using `querySelectorAll` if the matching criteria can be expressed as\n * a CSS selector, or `goog.dom.findElements` if you would filter for\n * `nodeType == Node.ELEMENT_NODE`.\n *\n * @param {Node} root The root of the tree to search.\n * @param {function(Node) : boolean} p The filter function.\n * @return {!Array<!Node>} The found nodes or an empty array if none are found.\n */\ngoog.dom.findNodes = function(root, p) {\n  var rv = [];\n  goog.dom.findNodes_(root, p, rv, false);\n  return rv;\n};\n\n\n/**\n * Finds the first or all the descendant nodes that match the filter function,\n * using a depth first search.\n * @param {Node} root The root of the tree to search.\n * @param {function(Node) : boolean} p The filter function.\n * @param {!Array<!Node>} rv The found nodes are added to this array.\n * @param {boolean} findOne If true we exit after the first found node.\n * @return {boolean} Whether the search is complete or not. True in case findOne\n *     is true and the node is found. False otherwise.\n * @private\n */\ngoog.dom.findNodes_ = function(root, p, rv, findOne) {\n  if (root != null) {\n    var child = root.firstChild;\n    while (child) {\n      if (p(child)) {\n        rv.push(child);\n        if (findOne) {\n          return true;\n        }\n      }\n      if (goog.dom.findNodes_(child, p, rv, findOne)) {\n        return true;\n      }\n      child = child.nextSibling;\n    }\n  }\n  return false;\n};\n\n\n/**\n * Finds the first descendant element (excluding `root`) that matches the filter\n * function, using depth first search. Prefer using `querySelector` if the\n * matching criteria can be expressed as a CSS selector.\n *\n * @param {!Element | !Document} root\n * @param {function(!Element): boolean} pred Filter function.\n * @return {?Element} First matching element or null if there is none.\n */\ngoog.dom.findElement = function(root, pred) {\n  var stack = goog.dom.getChildrenReverse_(root);\n  while (stack.length > 0) {\n    var next = stack.pop();\n    if (pred(next)) return next;\n    for (var c = next.lastElementChild; c; c = c.previousElementSibling) {\n      stack.push(c);\n    }\n  }\n  return null;\n};\n\n\n/**\n * Finds all the descendant elements (excluding `root`) that match the filter\n * function, using depth first search. Prefer using `querySelectorAll` if the\n * matching criteria can be expressed as a CSS selector.\n *\n * @param {!Element | !Document} root\n * @param {function(!Element): boolean} pred Filter function.\n * @return {!Array<!Element>}\n */\ngoog.dom.findElements = function(root, pred) {\n  var result = [], stack = goog.dom.getChildrenReverse_(root);\n  while (stack.length > 0) {\n    var next = stack.pop();\n    if (pred(next)) result.push(next);\n    for (var c = next.lastElementChild; c; c = c.previousElementSibling) {\n      stack.push(c);\n    }\n  }\n  return result;\n};\n\n\n/**\n * @param {!Element | !Document} node\n * @return {!Array<!Element>} node's child elements in reverse order.\n * @private\n */\ngoog.dom.getChildrenReverse_ = function(node) {\n  // document.lastElementChild doesn't exist in IE9; fall back to\n  // documentElement.\n  if (node.nodeType == goog.dom.NodeType.DOCUMENT) {\n    return [node.documentElement];\n  } else {\n    var children = [];\n    for (var c = node.lastElementChild; c; c = c.previousElementSibling) {\n      children.push(c);\n    }\n    return children;\n  }\n};\n\n\n/**\n * Map of tags whose content to ignore when calculating text length.\n * @private {!Object<string, number>}\n * @const\n */\ngoog.dom.TAGS_TO_IGNORE_ = {\n  'SCRIPT': 1,\n  'STYLE': 1,\n  'HEAD': 1,\n  'IFRAME': 1,\n  'OBJECT': 1\n};\n\n\n/**\n * Map of tags which have predefined values with regard to whitespace.\n * @private {!Object<string, string>}\n * @const\n */\ngoog.dom.PREDEFINED_TAG_VALUES_ = {\n  'IMG': ' ',\n  'BR': '\\n'\n};\n\n\n/**\n * Returns true if the element has a tab index that allows it to receive\n * keyboard focus (tabIndex >= 0), false otherwise.  Note that some elements\n * natively support keyboard focus, even if they have no tab index.\n * @param {!Element} element Element to check.\n * @return {boolean} Whether the element has a tab index that allows keyboard\n *     focus.\n */\ngoog.dom.isFocusableTabIndex = function(element) {\n  return goog.dom.hasSpecifiedTabIndex_(element) &&\n      goog.dom.isTabIndexFocusable_(element);\n};\n\n\n/**\n * Enables or disables keyboard focus support on the element via its tab index.\n * Only elements for which {@link goog.dom.isFocusableTabIndex} returns true\n * (or elements that natively support keyboard focus, like form elements) can\n * receive keyboard focus.  See http://go/tabindex for more info.\n * @param {Element} element Element whose tab index is to be changed.\n * @param {boolean} enable Whether to set or remove a tab index on the element\n *     that supports keyboard focus.\n */\ngoog.dom.setFocusableTabIndex = function(element, enable) {\n  if (enable) {\n    element.tabIndex = 0;\n  } else {\n    // Set tabIndex to -1 first, then remove it. This is a workaround for\n    // Safari (confirmed in version 4 on Windows). When removing the attribute\n    // without setting it to -1 first, the element remains keyboard focusable\n    // despite not having a tabIndex attribute anymore.\n    element.tabIndex = -1;\n    element.removeAttribute('tabIndex');  // Must be camelCase!\n  }\n};\n\n\n/**\n * Returns true if the element can be focused, i.e. it has a tab index that\n * allows it to receive keyboard focus (tabIndex >= 0), or it is an element\n * that natively supports keyboard focus.\n * @param {!Element} element Element to check.\n * @return {boolean} Whether the element allows keyboard focus.\n */\ngoog.dom.isFocusable = function(element) {\n  var focusable;\n  // Some elements can have unspecified tab index and still receive focus.\n  if (goog.dom.nativelySupportsFocus_(element)) {\n    // Make sure the element is not disabled ...\n    focusable = !element.disabled &&\n        // ... and if a tab index is specified, it allows focus.\n        (!goog.dom.hasSpecifiedTabIndex_(element) ||\n         goog.dom.isTabIndexFocusable_(element));\n  } else {\n    focusable = goog.dom.isFocusableTabIndex(element);\n  }\n\n  // IE requires elements to be visible in order to focus them.\n  return focusable && goog.userAgent.IE ?\n      goog.dom.hasNonZeroBoundingRect_(/** @type {!HTMLElement} */ (element)) :\n      focusable;\n};\n\n\n/**\n * Returns true if the element has a specified tab index.\n * @param {!Element} element Element to check.\n * @return {boolean} Whether the element has a specified tab index.\n * @private\n */\ngoog.dom.hasSpecifiedTabIndex_ = function(element) {\n  // IE8 and below don't support hasAttribute(), instead check whether the\n  // 'tabindex' attributeNode is specified. Otherwise check hasAttribute().\n  if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9')) {\n    var attrNode = element.getAttributeNode('tabindex');  // Must be lowercase!\n    return attrNode != null && attrNode.specified;\n  } else {\n    return element.hasAttribute('tabindex');\n  }\n};\n\n\n/**\n * Returns true if the element's tab index allows the element to be focused.\n * @param {!Element} element Element to check.\n * @return {boolean} Whether the element's tab index allows focus.\n * @private\n */\ngoog.dom.isTabIndexFocusable_ = function(element) {\n  var index = /** @type {!HTMLElement} */ (element).tabIndex;\n  // NOTE: IE9 puts tabIndex in 16-bit int, e.g. -2 is 65534.\n  return typeof (index) === 'number' && index >= 0 && index < 32768;\n};\n\n\n/**\n * Returns true if the element is focusable even when tabIndex is not set.\n * @param {!Element} element Element to check.\n * @return {boolean} Whether the element natively supports focus.\n * @private\n */\ngoog.dom.nativelySupportsFocus_ = function(element) {\n  return (\n      element.tagName == goog.dom.TagName.A && element.hasAttribute('href') ||\n      element.tagName == goog.dom.TagName.INPUT ||\n      element.tagName == goog.dom.TagName.TEXTAREA ||\n      element.tagName == goog.dom.TagName.SELECT ||\n      element.tagName == goog.dom.TagName.BUTTON);\n};\n\n\n/**\n * Returns true if the element has a bounding rectangle that would be visible\n * (i.e. its width and height are greater than zero).\n * @param {!HTMLElement} element Element to check.\n * @return {boolean} Whether the element has a non-zero bounding rectangle.\n * @private\n */\ngoog.dom.hasNonZeroBoundingRect_ = function(element) {\n  var rect;\n  if (!goog.isFunction(element['getBoundingClientRect']) ||\n      // In IE, getBoundingClientRect throws on detached nodes.\n      (goog.userAgent.IE && element.parentElement == null)) {\n    rect = {'height': element.offsetHeight, 'width': element.offsetWidth};\n  } else {\n    rect = element.getBoundingClientRect();\n  }\n  return rect != null && rect.height > 0 && rect.width > 0;\n};\n\n\n/**\n * Returns the text content of the current node, without markup and invisible\n * symbols. New lines are stripped and whitespace is collapsed,\n * such that each character would be visible.\n *\n * In browsers that support it, innerText is used.  Other browsers attempt to\n * simulate it via node traversal.  Line breaks are canonicalized in IE.\n *\n * @param {Node} node The node from which we are getting content.\n * @return {string} The text content.\n */\ngoog.dom.getTextContent = function(node) {\n  var textContent;\n  // Note(arv): IE9, Opera, and Safari 3 support innerText but they include\n  // text nodes in script tags. So we revert to use a user agent test here.\n  if (goog.dom.BrowserFeature.CAN_USE_INNER_TEXT && node !== null &&\n      ('innerText' in node)) {\n    textContent = goog.string.canonicalizeNewlines(node.innerText);\n    // Unfortunately .innerText() returns text with &shy; symbols\n    // We need to filter it out and then remove duplicate whitespaces\n  } else {\n    var buf = [];\n    goog.dom.getTextContent_(node, buf, true);\n    textContent = buf.join('');\n  }\n\n  // Strip &shy; entities. goog.format.insertWordBreaks inserts them in Opera.\n  textContent = textContent.replace(/ \\xAD /g, ' ').replace(/\\xAD/g, '');\n  // Strip &#8203; entities. goog.format.insertWordBreaks inserts them in IE8.\n  textContent = textContent.replace(/\\u200B/g, '');\n\n  // Skip this replacement on old browsers with working innerText, which\n  // automatically turns &nbsp; into ' ' and / +/ into ' ' when reading\n  // innerText.\n  if (!goog.dom.BrowserFeature.CAN_USE_INNER_TEXT) {\n    textContent = textContent.replace(/ +/g, ' ');\n  }\n  if (textContent != ' ') {\n    textContent = textContent.replace(/^\\s*/, '');\n  }\n\n  return textContent;\n};\n\n\n/**\n * Returns the text content of the current node, without markup.\n *\n * Unlike `getTextContent` this method does not collapse whitespaces\n * or normalize lines breaks.\n *\n * @param {Node} node The node from which we are getting content.\n * @return {string} The raw text content.\n */\ngoog.dom.getRawTextContent = function(node) {\n  var buf = [];\n  goog.dom.getTextContent_(node, buf, false);\n\n  return buf.join('');\n};\n\n\n/**\n * Recursive support function for text content retrieval.\n *\n * @param {Node} node The node from which we are getting content.\n * @param {Array<string>} buf string buffer.\n * @param {boolean} normalizeWhitespace Whether to normalize whitespace.\n * @private\n */\ngoog.dom.getTextContent_ = function(node, buf, normalizeWhitespace) {\n  if (node.nodeName in goog.dom.TAGS_TO_IGNORE_) {\n    // ignore certain tags\n  } else if (node.nodeType == goog.dom.NodeType.TEXT) {\n    if (normalizeWhitespace) {\n      buf.push(String(node.nodeValue).replace(/(\\r\\n|\\r|\\n)/g, ''));\n    } else {\n      buf.push(node.nodeValue);\n    }\n  } else if (node.nodeName in goog.dom.PREDEFINED_TAG_VALUES_) {\n    buf.push(goog.dom.PREDEFINED_TAG_VALUES_[node.nodeName]);\n  } else {\n    var child = node.firstChild;\n    while (child) {\n      goog.dom.getTextContent_(child, buf, normalizeWhitespace);\n      child = child.nextSibling;\n    }\n  }\n};\n\n\n/**\n * Returns the text length of the text contained in a node, without markup. This\n * is equivalent to the selection length if the node was selected, or the number\n * of cursor movements to traverse the node. Images & BRs take one space.  New\n * lines are ignored.\n *\n * @param {Node} node The node whose text content length is being calculated.\n * @return {number} The length of `node`'s text content.\n */\ngoog.dom.getNodeTextLength = function(node) {\n  return goog.dom.getTextContent(node).length;\n};\n\n\n/**\n * Returns the text offset of a node relative to one of its ancestors. The text\n * length is the same as the length calculated by goog.dom.getNodeTextLength.\n *\n * @param {Node} node The node whose offset is being calculated.\n * @param {Node=} opt_offsetParent The node relative to which the offset will\n *     be calculated. Defaults to the node's owner document's body.\n * @return {number} The text offset.\n */\ngoog.dom.getNodeTextOffset = function(node, opt_offsetParent) {\n  var root = opt_offsetParent || goog.dom.getOwnerDocument(node).body;\n  var buf = [];\n  while (node && node != root) {\n    var cur = node;\n    while ((cur = cur.previousSibling)) {\n      buf.unshift(goog.dom.getTextContent(cur));\n    }\n    node = node.parentNode;\n  }\n  // Trim left to deal with FF cases when there might be line breaks and empty\n  // nodes at the front of the text\n  return goog.string.trimLeft(buf.join('')).replace(/ +/g, ' ').length;\n};\n\n\n/**\n * Returns the node at a given offset in a parent node.  If an object is\n * provided for the optional third parameter, the node and the remainder of the\n * offset will stored as properties of this object.\n * @param {Node} parent The parent node.\n * @param {number} offset The offset into the parent node.\n * @param {Object=} opt_result Object to be used to store the return value. The\n *     return value will be stored in the form {node: Node, remainder: number}\n *     if this object is provided.\n * @return {Node} The node at the given offset.\n */\ngoog.dom.getNodeAtOffset = function(parent, offset, opt_result) {\n  var stack = [parent], pos = 0, cur = null;\n  while (stack.length > 0 && pos < offset) {\n    cur = stack.pop();\n    if (cur.nodeName in goog.dom.TAGS_TO_IGNORE_) {\n      // ignore certain tags\n    } else if (cur.nodeType == goog.dom.NodeType.TEXT) {\n      var text = cur.nodeValue.replace(/(\\r\\n|\\r|\\n)/g, '').replace(/ +/g, ' ');\n      pos += text.length;\n    } else if (cur.nodeName in goog.dom.PREDEFINED_TAG_VALUES_) {\n      pos += goog.dom.PREDEFINED_TAG_VALUES_[cur.nodeName].length;\n    } else {\n      for (var i = cur.childNodes.length - 1; i >= 0; i--) {\n        stack.push(cur.childNodes[i]);\n      }\n    }\n  }\n  if (goog.isObject(opt_result)) {\n    opt_result.remainder = cur ? cur.nodeValue.length + offset - pos - 1 : 0;\n    opt_result.node = cur;\n  }\n\n  return cur;\n};\n\n\n/**\n * Returns true if the object is a `NodeList`.  To qualify as a NodeList,\n * the object must have a numeric length property and an item function (which\n * has type 'string' on IE for some reason).\n * @param {Object} val Object to test.\n * @return {boolean} Whether the object is a NodeList.\n */\ngoog.dom.isNodeList = function(val) {\n  // TODO(attila): Now the isNodeList is part of goog.dom we can use\n  // goog.userAgent to make this simpler.\n  // A NodeList must have a length property of type 'number' on all platforms.\n  if (val && typeof val.length == 'number') {\n    // A NodeList is an object everywhere except Safari, where it's a function.\n    if (goog.isObject(val)) {\n      // A NodeList must have an item function (on non-IE platforms) or an item\n      // property of type 'string' (on IE).\n      return typeof val.item == 'function' || typeof val.item == 'string';\n    } else if (goog.isFunction(val)) {\n      // On Safari, a NodeList is a function with an item property that is also\n      // a function.\n      return typeof /** @type {?} */ (val.item) == 'function';\n    }\n  }\n\n  // Not a NodeList.\n  return false;\n};\n\n\n/**\n * Walks up the DOM hierarchy returning the first ancestor that has the passed\n * tag name and/or class name. If the passed element matches the specified\n * criteria, the element itself is returned.\n * @param {Node} element The DOM node to start with.\n * @param {?(goog.dom.TagName<T>|string)=} opt_tag The tag name to match (or\n *     null/undefined to match only based on class name).\n * @param {?string=} opt_class The class name to match (or null/undefined to\n *     match only based on tag name).\n * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the\n *     dom.\n * @return {?R} The first ancestor that matches the passed criteria, or\n *     null if no match is found. The return type is {?Element} if opt_tag is\n *     not a member of goog.dom.TagName or a more specific type if it is (e.g.\n *     {?HTMLAnchorElement} for goog.dom.TagName.A).\n * @template T\n * @template R := cond(isUnknown(T), 'Element', T) =:\n */\ngoog.dom.getAncestorByTagNameAndClass = function(\n    element, opt_tag, opt_class, opt_maxSearchSteps) {\n  if (!opt_tag && !opt_class) {\n    return null;\n  }\n  var tagName = opt_tag ? String(opt_tag).toUpperCase() : null;\n  return /** @type {Element} */ (goog.dom.getAncestor(element, function(node) {\n    return (!tagName || node.nodeName == tagName) &&\n        (!opt_class ||\n         typeof node.className === 'string' &&\n             goog.array.contains(node.className.split(/\\s+/), opt_class));\n  }, true, opt_maxSearchSteps));\n};\n\n\n/**\n * Walks up the DOM hierarchy returning the first ancestor that has the passed\n * class name. If the passed element matches the specified criteria, the\n * element itself is returned.\n * @param {Node} element The DOM node to start with.\n * @param {string} className The class name to match.\n * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the\n *     dom.\n * @return {Element} The first ancestor that matches the passed criteria, or\n *     null if none match.\n */\ngoog.dom.getAncestorByClass = function(element, className, opt_maxSearchSteps) {\n  return goog.dom.getAncestorByTagNameAndClass(\n      element, null, className, opt_maxSearchSteps);\n};\n\n\n/**\n * Walks up the DOM hierarchy returning the first ancestor that passes the\n * matcher function.\n * @param {Node} element The DOM node to start with.\n * @param {function(Node) : boolean} matcher A function that returns true if the\n *     passed node matches the desired criteria.\n * @param {boolean=} opt_includeNode If true, the node itself is included in\n *     the search (the first call to the matcher will pass startElement as\n *     the node to test).\n * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the\n *     dom.\n * @return {Node} DOM node that matched the matcher, or null if there was\n *     no match.\n */\ngoog.dom.getAncestor = function(\n    element, matcher, opt_includeNode, opt_maxSearchSteps) {\n  if (element && !opt_includeNode) {\n    element = element.parentNode;\n  }\n  var steps = 0;\n  while (element &&\n         (opt_maxSearchSteps == null || steps <= opt_maxSearchSteps)) {\n    goog.asserts.assert(element.name != 'parentNode');\n    if (matcher(element)) {\n      return element;\n    }\n    element = element.parentNode;\n    steps++;\n  }\n  // Reached the root of the DOM without a match\n  return null;\n};\n\n\n/**\n * Determines the active element in the given document.\n * @param {Document} doc The document to look in.\n * @return {Element} The active element.\n */\ngoog.dom.getActiveElement = function(doc) {\n  // While in an iframe, IE9 will throw \"Unspecified error\" when accessing\n  // activeElement.\n  try {\n    var activeElement = doc && doc.activeElement;\n    // While not in an iframe, IE9-11 sometimes gives null.\n    // While in an iframe, IE11 sometimes returns an empty object.\n    return activeElement && activeElement.nodeName ? activeElement : null;\n  } catch (e) {\n    return null;\n  }\n};\n\n\n/**\n * Gives the current devicePixelRatio.\n *\n * By default, this is the value of window.devicePixelRatio (which should be\n * preferred if present).\n *\n * If window.devicePixelRatio is not present, the ratio is calculated with\n * window.matchMedia, if present. Otherwise, gives 1.0.\n *\n * Some browsers (including Chrome) consider the browser zoom level in the pixel\n * ratio, so the value may change across multiple calls.\n *\n * @return {number} The number of actual pixels per virtual pixel.\n */\ngoog.dom.getPixelRatio = function() {\n  var win = goog.dom.getWindow();\n  if (win.devicePixelRatio !== undefined) {\n    return win.devicePixelRatio;\n  } else if (win.matchMedia) {\n    // Should be for IE10 and FF6-17 (this basically clamps to lower)\n    // Note that the order of these statements is important\n    return goog.dom.matchesPixelRatio_(3) || goog.dom.matchesPixelRatio_(2) ||\n           goog.dom.matchesPixelRatio_(1.5) || goog.dom.matchesPixelRatio_(1) ||\n           .75;\n  }\n  return 1;\n};\n\n\n/**\n * Calculates a mediaQuery to check if the current device supports the\n * given actual to virtual pixel ratio.\n * @param {number} pixelRatio The ratio of actual pixels to virtual pixels.\n * @return {number} pixelRatio if applicable, otherwise 0.\n * @private\n */\ngoog.dom.matchesPixelRatio_ = function(pixelRatio) {\n  var win = goog.dom.getWindow();\n  /**\n   * Due to the 1:96 fixed ratio of CSS in to CSS px, 1dppx is equivalent to\n   * 96dpi.\n   * @const {number}\n   */\n  var dpiPerDppx = 96;\n  var query =\n      // FF16-17\n      '(min-resolution: ' + pixelRatio + 'dppx),' +\n      // FF6-15\n      '(min--moz-device-pixel-ratio: ' + pixelRatio + '),' +\n      // IE10 (this works for the two browsers above too but I don't want to\n      // trust the 1:96 fixed ratio magic)\n      '(min-resolution: ' + (pixelRatio * dpiPerDppx) + 'dpi)';\n  return win.matchMedia(query).matches ? pixelRatio : 0;\n};\n\n\n/**\n * Gets '2d' context of a canvas. Shortcut for canvas.getContext('2d') with a\n * type information.\n * @param {!HTMLCanvasElement|!OffscreenCanvas} canvas\n * @return {!CanvasRenderingContext2D}\n */\ngoog.dom.getCanvasContext2D = function(canvas) {\n  return /** @type {!CanvasRenderingContext2D} */ (canvas.getContext('2d'));\n};\n\n\n\n/**\n * Create an instance of a DOM helper with a new document object.\n * @param {Document=} opt_document Document object to associate with this\n *     DOM helper.\n * @constructor\n */\ngoog.dom.DomHelper = function(opt_document) {\n  /**\n   * Reference to the document object to use\n   * @type {!Document}\n   * @private\n   */\n  this.document_ = opt_document || goog.global.document || document;\n};\n\n\n/**\n * Gets the dom helper object for the document where the element resides.\n * @param {Node=} opt_node If present, gets the DomHelper for this node.\n * @return {!goog.dom.DomHelper} The DomHelper.\n */\ngoog.dom.DomHelper.prototype.getDomHelper = goog.dom.getDomHelper;\n\n\n/**\n * Sets the document object.\n * @param {!Document} document Document object.\n */\ngoog.dom.DomHelper.prototype.setDocument = function(document) {\n  this.document_ = document;\n};\n\n\n/**\n * Gets the document object being used by the dom library.\n * @return {!Document} Document object.\n */\ngoog.dom.DomHelper.prototype.getDocument = function() {\n  return this.document_;\n};\n\n\n/**\n * Alias for `getElementById`. If a DOM node is passed in then we just\n * return that.\n * @param {string|Element} element Element ID or a DOM node.\n * @return {Element} The element with the given ID, or the node passed in.\n */\ngoog.dom.DomHelper.prototype.getElement = function(element) {\n  return goog.dom.getElementHelper_(this.document_, element);\n};\n\n\n/**\n * Gets an element by id, asserting that the element is found.\n *\n * This is used when an element is expected to exist, and should fail with\n * an assertion error if it does not (if assertions are enabled).\n *\n * @param {string} id Element ID.\n * @return {!Element} The element with the given ID, if it exists.\n */\ngoog.dom.DomHelper.prototype.getRequiredElement = function(id) {\n  return goog.dom.getRequiredElementHelper_(this.document_, id);\n};\n\n\n/**\n * Alias for `getElement`.\n * @param {string|Element} element Element ID or a DOM node.\n * @return {Element} The element with the given ID, or the node passed in.\n * @deprecated Use {@link goog.dom.DomHelper.prototype.getElement} instead.\n */\ngoog.dom.DomHelper.prototype.$ = goog.dom.DomHelper.prototype.getElement;\n\n\n/**\n * Gets elements by tag name.\n * @param {!goog.dom.TagName<T>} tagName\n * @param {(!Document|!Element)=} opt_parent Parent element or document where to\n *     look for elements. Defaults to document of this DomHelper.\n * @return {!NodeList<R>} List of elements. The members of the list are\n *     {!Element} if tagName is not a member of goog.dom.TagName or more\n *     specific types if it is (e.g. {!HTMLAnchorElement} for\n *     goog.dom.TagName.A).\n * @template T\n * @template R := cond(isUnknown(T), 'Element', T) =:\n */\ngoog.dom.DomHelper.prototype.getElementsByTagName =\n    function(tagName, opt_parent) {\n  var parent = opt_parent || this.document_;\n  return parent.getElementsByTagName(String(tagName));\n};\n\n\n/**\n * Looks up elements by both tag and class name, using browser native functions\n * (`querySelectorAll`, `getElementsByTagName` or\n * `getElementsByClassName`) where possible. The returned array is a live\n * NodeList or a static list depending on the code path taken.\n *\n * @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name or * for all\n *     tags.\n * @param {?string=} opt_class Optional class name.\n * @param {(Document|Element)=} opt_el Optional element to look in.\n * @return {!IArrayLike<R>} Array-like list of elements (only a length property\n *     and numerical indices are guaranteed to exist). The members of the array\n *     are {!Element} if opt_tag is not a member of goog.dom.TagName or more\n *     specific types if it is (e.g. {!HTMLAnchorElement} for\n *     goog.dom.TagName.A).\n * @template T\n * @template R := cond(isUnknown(T), 'Element', T) =:\n */\ngoog.dom.DomHelper.prototype.getElementsByTagNameAndClass = function(\n    opt_tag, opt_class, opt_el) {\n  return goog.dom.getElementsByTagNameAndClass_(\n      this.document_, opt_tag, opt_class, opt_el);\n};\n\n\n/**\n * Gets the first element matching the tag and the class.\n *\n * @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name.\n * @param {?string=} opt_class Optional class name.\n * @param {(Document|Element)=} opt_el Optional element to look in.\n * @return {?R} Reference to a DOM node. The return type is {?Element} if\n *     tagName is a string or a more specific type if it is a member of\n *     goog.dom.TagName (e.g. {?HTMLAnchorElement} for goog.dom.TagName.A).\n * @template T\n * @template R := cond(isUnknown(T), 'Element', T) =:\n */\ngoog.dom.DomHelper.prototype.getElementByTagNameAndClass = function(\n    opt_tag, opt_class, opt_el) {\n  return goog.dom.getElementByTagNameAndClass_(\n      this.document_, opt_tag, opt_class, opt_el);\n};\n\n\n/**\n * Returns an array of all the elements with the provided className.\n * @param {string} className the name of the class to look for.\n * @param {Element|Document=} opt_el Optional element to look in.\n * @return {!IArrayLike<!Element>} The items found with the class name provided.\n */\ngoog.dom.DomHelper.prototype.getElementsByClass = function(className, opt_el) {\n  var doc = opt_el || this.document_;\n  return goog.dom.getElementsByClass(className, doc);\n};\n\n\n/**\n * Returns the first element we find matching the provided class name.\n * @param {string} className the name of the class to look for.\n * @param {(Element|Document)=} opt_el Optional element to look in.\n * @return {Element} The first item found with the class name provided.\n */\ngoog.dom.DomHelper.prototype.getElementByClass = function(className, opt_el) {\n  var doc = opt_el || this.document_;\n  return goog.dom.getElementByClass(className, doc);\n};\n\n\n/**\n * Ensures an element with the given className exists, and then returns the\n * first element with the provided className.\n * @param {string} className the name of the class to look for.\n * @param {(!Element|!Document)=} opt_root Optional element or document to look\n *     in.\n * @return {!Element} The first item found with the class name provided.\n * @throws {goog.asserts.AssertionError} Thrown if no element is found.\n */\ngoog.dom.DomHelper.prototype.getRequiredElementByClass = function(\n    className, opt_root) {\n  var root = opt_root || this.document_;\n  return goog.dom.getRequiredElementByClass(className, root);\n};\n\n\n/**\n * Alias for `getElementsByTagNameAndClass`.\n * @deprecated Use DomHelper getElementsByTagNameAndClass.\n *\n * @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name.\n * @param {?string=} opt_class Optional class name.\n * @param {Element=} opt_el Optional element to look in.\n * @return {!IArrayLike<R>} Array-like list of elements (only a length property\n *     and numerical indices are guaranteed to exist). The members of the array\n *     are {!Element} if opt_tag is a string or more specific types if it is\n *     a member of goog.dom.TagName (e.g. {!HTMLAnchorElement} for\n *     goog.dom.TagName.A).\n * @template T\n * @template R := cond(isUnknown(T), 'Element', T) =:\n */\ngoog.dom.DomHelper.prototype.$$ =\n    goog.dom.DomHelper.prototype.getElementsByTagNameAndClass;\n\n\n/**\n * Sets a number of properties on a node.\n * @param {Element} element DOM node to set properties on.\n * @param {Object} properties Hash of property:value pairs.\n */\ngoog.dom.DomHelper.prototype.setProperties = goog.dom.setProperties;\n\n\n/**\n * Gets the dimensions of the viewport.\n * @param {Window=} opt_window Optional window element to test. Defaults to\n *     the window of the Dom Helper.\n * @return {!goog.math.Size} Object with values 'width' and 'height'.\n */\ngoog.dom.DomHelper.prototype.getViewportSize = function(opt_window) {\n  // TODO(arv): This should not take an argument. That breaks the rule of a\n  // a DomHelper representing a single frame/window/document.\n  return goog.dom.getViewportSize(opt_window || this.getWindow());\n};\n\n\n/**\n * Calculates the height of the document.\n *\n * @return {number} The height of the document.\n */\ngoog.dom.DomHelper.prototype.getDocumentHeight = function() {\n  return goog.dom.getDocumentHeight_(this.getWindow());\n};\n\n\n/**\n * Typedef for use with goog.dom.createDom and goog.dom.append.\n * @typedef {Object|string|Array|NodeList}\n */\ngoog.dom.Appendable;\n\n\n/**\n * Returns a dom node with a set of attributes.  This function accepts varargs\n * for subsequent nodes to be added.  Subsequent nodes will be added to the\n * first node as childNodes.\n *\n * So:\n * <code>createDom(goog.dom.TagName.DIV, null, createDom(goog.dom.TagName.P),\n * createDom(goog.dom.TagName.P));</code> would return a div with two child\n * paragraphs\n *\n * An easy way to move all child nodes of an existing element to a new parent\n * element is:\n * <code>createDom(goog.dom.TagName.DIV, null, oldElement.childNodes);</code>\n * which will remove all child nodes from the old element and add them as\n * child nodes of the new DIV.\n *\n * @param {string|!goog.dom.TagName<T>} tagName Tag to create.\n * @param {?Object|?Array<string>|string=} opt_attributes If object, then a map\n *     of name-value pairs for attributes. If a string, then this is the\n *     className of the new element. If an array, the elements will be joined\n *     together as the className of the new element.\n * @param {...(goog.dom.Appendable|undefined)} var_args Further DOM nodes or\n *     strings for text nodes. If one of the var_args is an array or\n *     NodeList, its elements will be added as childNodes instead.\n * @return {R} Reference to a DOM node. The return type is {!Element} if tagName\n *     is a string or a more specific type if it is a member of\n *     goog.dom.TagName (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).\n * @template T\n * @template R := cond(isUnknown(T), 'Element', T) =:\n */\ngoog.dom.DomHelper.prototype.createDom = function(\n    tagName, opt_attributes, var_args) {\n  return goog.dom.createDom_(this.document_, arguments);\n};\n\n\n/**\n * Alias for `createDom`.\n * @param {string|!goog.dom.TagName<T>} tagName Tag to create.\n * @param {?Object|?Array<string>|string=} opt_attributes If object, then a map\n *     of name-value pairs for attributes. If a string, then this is the\n *     className of the new element. If an array, the elements will be joined\n *     together as the className of the new element.\n * @param {...(goog.dom.Appendable|undefined)} var_args Further DOM nodes or\n *     strings for text nodes.  If one of the var_args is an array, its children\n *     will be added as childNodes instead.\n * @return {R} Reference to a DOM node. The return type is {!Element} if tagName\n *     is a string or a more specific type if it is a member of\n *     goog.dom.TagName (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).\n * @template T\n * @template R := cond(isUnknown(T), 'Element', T) =:\n * @deprecated Use {@link goog.dom.DomHelper.prototype.createDom} instead.\n */\ngoog.dom.DomHelper.prototype.$dom = goog.dom.DomHelper.prototype.createDom;\n\n\n/**\n * Creates a new element.\n * @param {string|!goog.dom.TagName<T>} name Tag to create.\n * @return {R} The new element. The return type is {!Element} if name is\n *     a string or a more specific type if it is a member of goog.dom.TagName\n *     (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).\n * @template T\n * @template R := cond(isUnknown(T), 'Element', T) =:\n */\ngoog.dom.DomHelper.prototype.createElement = function(name) {\n  return goog.dom.createElement_(this.document_, name);\n};\n\n\n/**\n * Creates a new text node.\n * @param {number|string} content Content.\n * @return {!Text} The new text node.\n */\ngoog.dom.DomHelper.prototype.createTextNode = function(content) {\n  return this.document_.createTextNode(String(content));\n};\n\n\n/**\n * Create a table.\n * @param {number} rows The number of rows in the table.  Must be >= 1.\n * @param {number} columns The number of columns in the table.  Must be >= 1.\n * @param {boolean=} opt_fillWithNbsp If true, fills table entries with\n *     `goog.string.Unicode.NBSP` characters.\n * @return {!HTMLElement} The created table.\n */\ngoog.dom.DomHelper.prototype.createTable = function(\n    rows, columns, opt_fillWithNbsp) {\n  return goog.dom.createTable_(\n      this.document_, rows, columns, !!opt_fillWithNbsp);\n};\n\n\n/**\n * Converts an HTML into a node or a document fragment. A single Node is used if\n * `html` only generates a single node. If `html` generates multiple\n * nodes then these are put inside a `DocumentFragment`. This is a safe\n * version of `goog.dom.DomHelper#htmlToDocumentFragment` which is now\n * deleted.\n * @param {!goog.html.SafeHtml} html The HTML markup to convert.\n * @return {!Node} The resulting node.\n */\ngoog.dom.DomHelper.prototype.safeHtmlToNode = function(html) {\n  return goog.dom.safeHtmlToNode_(this.document_, html);\n};\n\n\n/**\n * Returns true if the browser is in \"CSS1-compatible\" (standards-compliant)\n * mode, false otherwise.\n * @return {boolean} True if in CSS1-compatible mode.\n */\ngoog.dom.DomHelper.prototype.isCss1CompatMode = function() {\n  return goog.dom.isCss1CompatMode_(this.document_);\n};\n\n\n/**\n * Gets the window object associated with the document.\n * @return {!Window} The window associated with the given document.\n */\ngoog.dom.DomHelper.prototype.getWindow = function() {\n  return goog.dom.getWindow_(this.document_);\n};\n\n\n/**\n * Gets the document scroll element.\n * @return {!Element} Scrolling element.\n */\ngoog.dom.DomHelper.prototype.getDocumentScrollElement = function() {\n  return goog.dom.getDocumentScrollElement_(this.document_);\n};\n\n\n/**\n * Gets the document scroll distance as a coordinate object.\n * @return {!goog.math.Coordinate} Object with properties 'x' and 'y'.\n */\ngoog.dom.DomHelper.prototype.getDocumentScroll = function() {\n  return goog.dom.getDocumentScroll_(this.document_);\n};\n\n\n/**\n * Determines the active element in the given document.\n * @param {Document=} opt_doc The document to look in.\n * @return {Element} The active element.\n */\ngoog.dom.DomHelper.prototype.getActiveElement = function(opt_doc) {\n  return goog.dom.getActiveElement(opt_doc || this.document_);\n};\n\n\n/**\n * Appends a child to a node.\n * @param {Node} parent Parent.\n * @param {Node} child Child.\n */\ngoog.dom.DomHelper.prototype.appendChild = goog.dom.appendChild;\n\n\n/**\n * Appends a node with text or other nodes.\n * @param {!Node} parent The node to append nodes to.\n * @param {...goog.dom.Appendable} var_args The things to append to the node.\n *     If this is a Node it is appended as is.\n *     If this is a string then a text node is appended.\n *     If this is an array like object then fields 0 to length - 1 are appended.\n */\ngoog.dom.DomHelper.prototype.append = goog.dom.append;\n\n\n/**\n * Determines if the given node can contain children, intended to be used for\n * HTML generation.\n *\n * @param {Node} node The node to check.\n * @return {boolean} Whether the node can contain children.\n */\ngoog.dom.DomHelper.prototype.canHaveChildren = goog.dom.canHaveChildren;\n\n\n/**\n * Removes all the child nodes on a DOM node.\n * @param {Node} node Node to remove children from.\n */\ngoog.dom.DomHelper.prototype.removeChildren = goog.dom.removeChildren;\n\n\n/**\n * Inserts a new node before an existing reference node (i.e., as the previous\n * sibling). If the reference node has no parent, then does nothing.\n * @param {Node} newNode Node to insert.\n * @param {Node} refNode Reference node to insert before.\n */\ngoog.dom.DomHelper.prototype.insertSiblingBefore = goog.dom.insertSiblingBefore;\n\n\n/**\n * Inserts a new node after an existing reference node (i.e., as the next\n * sibling). If the reference node has no parent, then does nothing.\n * @param {Node} newNode Node to insert.\n * @param {Node} refNode Reference node to insert after.\n */\ngoog.dom.DomHelper.prototype.insertSiblingAfter = goog.dom.insertSiblingAfter;\n\n\n/**\n * Insert a child at a given index. If index is larger than the number of child\n * nodes that the parent currently has, the node is inserted as the last child\n * node.\n * @param {Element} parent The element into which to insert the child.\n * @param {Node} child The element to insert.\n * @param {number} index The index at which to insert the new child node. Must\n *     not be negative.\n */\ngoog.dom.DomHelper.prototype.insertChildAt = goog.dom.insertChildAt;\n\n\n/**\n * Removes a node from its parent.\n * @param {Node} node The node to remove.\n * @return {Node} The node removed if removed; else, null.\n */\ngoog.dom.DomHelper.prototype.removeNode = goog.dom.removeNode;\n\n\n/**\n * Replaces a node in the DOM tree. Will do nothing if `oldNode` has no\n * parent.\n * @param {Node} newNode Node to insert.\n * @param {Node} oldNode Node to replace.\n */\ngoog.dom.DomHelper.prototype.replaceNode = goog.dom.replaceNode;\n\n\n/**\n * Replaces child nodes of `target` with child nodes of `source`. This is\n * roughly equivalent to `target.innerHTML = source.innerHTML` which is not\n * compatible with Trusted Types.\n * @param {?Node} target Node to clean and replace its children.\n * @param {?Node} source Node to get the children from. The nodes will be cloned\n *     so they will stay in source.\n */\ngoog.dom.DomHelper.prototype.copyContents = goog.dom.copyContents;\n\n\n/**\n * Flattens an element. That is, removes it and replace it with its children.\n * @param {Element} element The element to flatten.\n * @return {Element|undefined} The original element, detached from the document\n *     tree, sans children, or undefined if the element was already not in the\n *     document.\n */\ngoog.dom.DomHelper.prototype.flattenElement = goog.dom.flattenElement;\n\n\n/**\n * Returns an array containing just the element children of the given element.\n * @param {Element} element The element whose element children we want.\n * @return {!(Array<!Element>|NodeList<!Element>)} An array or array-like list\n *     of just the element children of the given element.\n */\ngoog.dom.DomHelper.prototype.getChildren = goog.dom.getChildren;\n\n\n/**\n * Returns the first child node that is an element.\n * @param {Node} node The node to get the first child element of.\n * @return {Element} The first child node of `node` that is an element.\n */\ngoog.dom.DomHelper.prototype.getFirstElementChild =\n    goog.dom.getFirstElementChild;\n\n\n/**\n * Returns the last child node that is an element.\n * @param {Node} node The node to get the last child element of.\n * @return {Element} The last child node of `node` that is an element.\n */\ngoog.dom.DomHelper.prototype.getLastElementChild = goog.dom.getLastElementChild;\n\n\n/**\n * Returns the first next sibling that is an element.\n * @param {Node} node The node to get the next sibling element of.\n * @return {Element} The next sibling of `node` that is an element.\n */\ngoog.dom.DomHelper.prototype.getNextElementSibling =\n    goog.dom.getNextElementSibling;\n\n\n/**\n * Returns the first previous sibling that is an element.\n * @param {Node} node The node to get the previous sibling element of.\n * @return {Element} The first previous sibling of `node` that is\n *     an element.\n */\ngoog.dom.DomHelper.prototype.getPreviousElementSibling =\n    goog.dom.getPreviousElementSibling;\n\n\n/**\n * Returns the next node in source order from the given node.\n * @param {Node} node The node.\n * @return {Node} The next node in the DOM tree, or null if this was the last\n *     node.\n */\ngoog.dom.DomHelper.prototype.getNextNode = goog.dom.getNextNode;\n\n\n/**\n * Returns the previous node in source order from the given node.\n * @param {Node} node The node.\n * @return {Node} The previous node in the DOM tree, or null if this was the\n *     first node.\n */\ngoog.dom.DomHelper.prototype.getPreviousNode = goog.dom.getPreviousNode;\n\n\n/**\n * Whether the object looks like a DOM node.\n * @param {?} obj The object being tested for node likeness.\n * @return {boolean} Whether the object looks like a DOM node.\n */\ngoog.dom.DomHelper.prototype.isNodeLike = goog.dom.isNodeLike;\n\n\n/**\n * Whether the object looks like an Element.\n * @param {?} obj The object being tested for Element likeness.\n * @return {boolean} Whether the object looks like an Element.\n */\ngoog.dom.DomHelper.prototype.isElement = goog.dom.isElement;\n\n\n/**\n * Returns true if the specified value is a Window object. This includes the\n * global window for HTML pages, and iframe windows.\n * @param {?} obj Variable to test.\n * @return {boolean} Whether the variable is a window.\n */\ngoog.dom.DomHelper.prototype.isWindow = goog.dom.isWindow;\n\n\n/**\n * Returns an element's parent, if it's an Element.\n * @param {Element} element The DOM element.\n * @return {Element} The parent, or null if not an Element.\n */\ngoog.dom.DomHelper.prototype.getParentElement = goog.dom.getParentElement;\n\n\n/**\n * Whether a node contains another node.\n * @param {Node} parent The node that should contain the other node.\n * @param {Node} descendant The node to test presence of.\n * @return {boolean} Whether the parent node contains the descendant node.\n */\ngoog.dom.DomHelper.prototype.contains = goog.dom.contains;\n\n\n/**\n * Compares the document order of two nodes, returning 0 if they are the same\n * node, a negative number if node1 is before node2, and a positive number if\n * node2 is before node1.  Note that we compare the order the tags appear in the\n * document so in the tree <b><i>text</i></b> the B node is considered to be\n * before the I node.\n *\n * @param {Node} node1 The first node to compare.\n * @param {Node} node2 The second node to compare.\n * @return {number} 0 if the nodes are the same node, a negative number if node1\n *     is before node2, and a positive number if node2 is before node1.\n */\ngoog.dom.DomHelper.prototype.compareNodeOrder = goog.dom.compareNodeOrder;\n\n\n/**\n * Find the deepest common ancestor of the given nodes.\n * @param {...Node} var_args The nodes to find a common ancestor of.\n * @return {Node} The common ancestor of the nodes, or null if there is none.\n *     null will only be returned if two or more of the nodes are from different\n *     documents.\n */\ngoog.dom.DomHelper.prototype.findCommonAncestor = goog.dom.findCommonAncestor;\n\n\n/**\n * Returns the owner document for a node.\n * @param {Node} node The node to get the document for.\n * @return {!Document} The document owning the node.\n */\ngoog.dom.DomHelper.prototype.getOwnerDocument = goog.dom.getOwnerDocument;\n\n\n/**\n * Cross browser function for getting the document element of an iframe.\n * @param {Element} iframe Iframe element.\n * @return {!Document} The frame content document.\n */\ngoog.dom.DomHelper.prototype.getFrameContentDocument =\n    goog.dom.getFrameContentDocument;\n\n\n/**\n * Cross browser function for getting the window of a frame or iframe.\n * @param {Element} frame Frame element.\n * @return {Window} The window associated with the given frame.\n */\ngoog.dom.DomHelper.prototype.getFrameContentWindow =\n    goog.dom.getFrameContentWindow;\n\n\n/**\n * Sets the text content of a node, with cross-browser support.\n * @param {Node} node The node to change the text content of.\n * @param {string|number} text The value that should replace the node's content.\n */\ngoog.dom.DomHelper.prototype.setTextContent = goog.dom.setTextContent;\n\n\n/**\n * Gets the outerHTML of a node, which islike innerHTML, except that it\n * actually contains the HTML of the node itself.\n * @param {Element} element The element to get the HTML of.\n * @return {string} The outerHTML of the given element.\n */\ngoog.dom.DomHelper.prototype.getOuterHtml = goog.dom.getOuterHtml;\n\n\n/**\n * Finds the first descendant node that matches the filter function. This does\n * a depth first search.\n * @param {Node} root The root of the tree to search.\n * @param {function(Node) : boolean} p The filter function.\n * @return {Node|undefined} The found node or undefined if none is found.\n */\ngoog.dom.DomHelper.prototype.findNode = goog.dom.findNode;\n\n\n/**\n * Finds all the descendant nodes that matches the filter function. This does a\n * depth first search.\n * @param {Node} root The root of the tree to search.\n * @param {function(Node) : boolean} p The filter function.\n * @return {Array<Node>} The found nodes or an empty array if none are found.\n */\ngoog.dom.DomHelper.prototype.findNodes = goog.dom.findNodes;\n\n\n/**\n * Returns true if the element has a tab index that allows it to receive\n * keyboard focus (tabIndex >= 0), false otherwise.  Note that some elements\n * natively support keyboard focus, even if they have no tab index.\n * @param {!Element} element Element to check.\n * @return {boolean} Whether the element has a tab index that allows keyboard\n *     focus.\n */\ngoog.dom.DomHelper.prototype.isFocusableTabIndex = goog.dom.isFocusableTabIndex;\n\n\n/**\n * Enables or disables keyboard focus support on the element via its tab index.\n * Only elements for which {@link goog.dom.isFocusableTabIndex} returns true\n * (or elements that natively support keyboard focus, like form elements) can\n * receive keyboard focus.  See http://go/tabindex for more info.\n * @param {Element} element Element whose tab index is to be changed.\n * @param {boolean} enable Whether to set or remove a tab index on the element\n *     that supports keyboard focus.\n */\ngoog.dom.DomHelper.prototype.setFocusableTabIndex =\n    goog.dom.setFocusableTabIndex;\n\n\n/**\n * Returns true if the element can be focused, i.e. it has a tab index that\n * allows it to receive keyboard focus (tabIndex >= 0), or it is an element\n * that natively supports keyboard focus.\n * @param {!Element} element Element to check.\n * @return {boolean} Whether the element allows keyboard focus.\n */\ngoog.dom.DomHelper.prototype.isFocusable = goog.dom.isFocusable;\n\n\n/**\n * Returns the text contents of the current node, without markup. New lines are\n * stripped and whitespace is collapsed, such that each character would be\n * visible.\n *\n * In browsers that support it, innerText is used.  Other browsers attempt to\n * simulate it via node traversal.  Line breaks are canonicalized in IE.\n *\n * @param {Node} node The node from which we are getting content.\n * @return {string} The text content.\n */\ngoog.dom.DomHelper.prototype.getTextContent = goog.dom.getTextContent;\n\n\n/**\n * Returns the text length of the text contained in a node, without markup. This\n * is equivalent to the selection length if the node was selected, or the number\n * of cursor movements to traverse the node. Images & BRs take one space.  New\n * lines are ignored.\n *\n * @param {Node} node The node whose text content length is being calculated.\n * @return {number} The length of `node`'s text content.\n */\ngoog.dom.DomHelper.prototype.getNodeTextLength = goog.dom.getNodeTextLength;\n\n\n/**\n * Returns the text offset of a node relative to one of its ancestors. The text\n * length is the same as the length calculated by\n * `goog.dom.getNodeTextLength`.\n *\n * @param {Node} node The node whose offset is being calculated.\n * @param {Node=} opt_offsetParent Defaults to the node's owner document's body.\n * @return {number} The text offset.\n */\ngoog.dom.DomHelper.prototype.getNodeTextOffset = goog.dom.getNodeTextOffset;\n\n\n/**\n * Returns the node at a given offset in a parent node.  If an object is\n * provided for the optional third parameter, the node and the remainder of the\n * offset will stored as properties of this object.\n * @param {Node} parent The parent node.\n * @param {number} offset The offset into the parent node.\n * @param {Object=} opt_result Object to be used to store the return value. The\n *     return value will be stored in the form {node: Node, remainder: number}\n *     if this object is provided.\n * @return {Node} The node at the given offset.\n */\ngoog.dom.DomHelper.prototype.getNodeAtOffset = goog.dom.getNodeAtOffset;\n\n\n/**\n * Returns true if the object is a `NodeList`.  To qualify as a NodeList,\n * the object must have a numeric length property and an item function (which\n * has type 'string' on IE for some reason).\n * @param {Object} val Object to test.\n * @return {boolean} Whether the object is a NodeList.\n */\ngoog.dom.DomHelper.prototype.isNodeList = goog.dom.isNodeList;\n\n\n/**\n * Walks up the DOM hierarchy returning the first ancestor that has the passed\n * tag name and/or class name. If the passed element matches the specified\n * criteria, the element itself is returned.\n * @param {Node} element The DOM node to start with.\n * @param {?(goog.dom.TagName<T>|string)=} opt_tag The tag name to match (or\n *     null/undefined to match only based on class name).\n * @param {?string=} opt_class The class name to match (or null/undefined to\n *     match only based on tag name).\n * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the\n *     dom.\n * @return {?R} The first ancestor that matches the passed criteria, or\n *     null if no match is found. The return type is {?Element} if opt_tag is\n *     not a member of goog.dom.TagName or a more specific type if it is (e.g.\n *     {?HTMLAnchorElement} for goog.dom.TagName.A).\n * @template T\n * @template R := cond(isUnknown(T), 'Element', T) =:\n */\ngoog.dom.DomHelper.prototype.getAncestorByTagNameAndClass =\n    goog.dom.getAncestorByTagNameAndClass;\n\n\n/**\n * Walks up the DOM hierarchy returning the first ancestor that has the passed\n * class name. If the passed element matches the specified criteria, the\n * element itself is returned.\n * @param {Node} element The DOM node to start with.\n * @param {string} class The class name to match.\n * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the\n *     dom.\n * @return {Element} The first ancestor that matches the passed criteria, or\n *     null if none match.\n */\ngoog.dom.DomHelper.prototype.getAncestorByClass = goog.dom.getAncestorByClass;\n\n\n/**\n * Walks up the DOM hierarchy returning the first ancestor that passes the\n * matcher function.\n * @param {Node} element The DOM node to start with.\n * @param {function(Node) : boolean} matcher A function that returns true if the\n *     passed node matches the desired criteria.\n * @param {boolean=} opt_includeNode If true, the node itself is included in\n *     the search (the first call to the matcher will pass startElement as\n *     the node to test).\n * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the\n *     dom.\n * @return {Node} DOM node that matched the matcher, or null if there was\n *     no match.\n */\ngoog.dom.DomHelper.prototype.getAncestor = goog.dom.getAncestor;\n\n\n/**\n * Gets '2d' context of a canvas. Shortcut for canvas.getContext('2d') with a\n * type information.\n * @param {!HTMLCanvasElement} canvas\n * @return {!CanvasRenderingContext2D}\n */\ngoog.dom.DomHelper.prototype.getCanvasContext2D = goog.dom.getCanvasContext2D;\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Provides a function to schedule running a function as soon\n * as possible after the current JS execution stops and yields to the event\n * loop.\n */\n\ngoog.provide('goog.async.nextTick');\ngoog.provide('goog.async.throwException');\n\ngoog.require('goog.debug.entryPointRegistry');\ngoog.require('goog.dom');\ngoog.require('goog.dom.TagName');\ngoog.require('goog.functions');\ngoog.require('goog.labs.userAgent.browser');\ngoog.require('goog.labs.userAgent.engine');\n\n\n/**\n * Throw an item without interrupting the current execution context.  For\n * example, if processing a group of items in a loop, sometimes it is useful\n * to report an error while still allowing the rest of the batch to be\n * processed.\n * @param {*} exception\n */\ngoog.async.throwException = function(exception) {\n  // Each throw needs to be in its own context.\n  goog.global.setTimeout(function() { throw exception; }, 0);\n};\n\n\n/**\n * Fires the provided callbacks as soon as possible after the current JS\n * execution context. setTimeout(…, 0) takes at least 4ms when called from\n * within another setTimeout(…, 0) for legacy reasons.\n *\n * This will not schedule the callback as a microtask (i.e. a task that can\n * preempt user input or networking callbacks). It is meant to emulate what\n * setTimeout(_, 0) would do if it were not throttled. If you desire microtask\n * behavior, use {@see goog.Promise} instead.\n *\n * @param {function(this:SCOPE)} callback Callback function to fire as soon as\n *     possible.\n * @param {SCOPE=} opt_context Object in whose scope to call the listener.\n * @param {boolean=} opt_useSetImmediate Avoid the IE workaround that\n *     ensures correctness at the cost of speed. See comments for details.\n * @template SCOPE\n */\ngoog.async.nextTick = function(callback, opt_context, opt_useSetImmediate) {\n  var cb = callback;\n  if (opt_context) {\n    cb = goog.bind(callback, opt_context);\n  }\n  cb = goog.async.nextTick.wrapCallback_(cb);\n  // Note we do allow callers to also request setImmediate if they are willing\n  // to accept the possible tradeoffs of incorrectness in exchange for speed.\n  // The IE fallback of readystate change is much slower. See useSetImmediate_\n  // for details.\n  if (goog.isFunction(goog.global.setImmediate) &&\n      (opt_useSetImmediate || goog.async.nextTick.useSetImmediate_())) {\n    goog.global.setImmediate(cb);\n    return;\n  }\n\n  // Look for and cache the custom fallback version of setImmediate.\n  if (!goog.async.nextTick.setImmediate_) {\n    goog.async.nextTick.setImmediate_ =\n        goog.async.nextTick.getSetImmediateEmulator_();\n  }\n  goog.async.nextTick.setImmediate_(cb);\n};\n\n\n/**\n * Returns whether should use setImmediate implementation currently on window.\n *\n * window.setImmediate was introduced and currently only supported by IE10+,\n * but due to a bug in the implementation it is not guaranteed that\n * setImmediate is faster than setTimeout nor that setImmediate N is before\n * setImmediate N+1. That is why we do not use the native version if\n * available. We do, however, call setImmediate if it is a non-native function\n * because that indicates that it has been replaced by goog.testing.MockClock\n * which we do want to support.\n * See\n * http://connect.microsoft.com/IE/feedback/details/801823/setimmediate-and-messagechannel-are-broken-in-ie10\n *\n * @return {boolean} Whether to use the implementation of setImmediate defined\n *     on Window.\n * @private\n * @suppress {missingProperties} For \"Window.prototype.setImmediate\"\n */\ngoog.async.nextTick.useSetImmediate_ = function() {\n  // Not a browser environment.\n  if (!goog.global.Window || !goog.global.Window.prototype) {\n    return true;\n  }\n\n  // MS Edge has window.setImmediate natively, but it's not on Window.prototype.\n  // Also, there's no clean way to detect if the goog.global.setImmediate has\n  // been replaced by mockClock as its replacement also shows up as \"[native\n  // code]\" when using toString. Therefore, just always use\n  // goog.global.setImmediate for Edge. It's unclear if it suffers the same\n  // issues as IE10/11, but based on\n  // https://dev.modern.ie/testdrive/demos/setimmediatesorting/\n  // it seems they've been working to ensure it's WAI.\n  if (goog.labs.userAgent.browser.isEdge() ||\n      goog.global.Window.prototype.setImmediate != goog.global.setImmediate) {\n    // Something redefined setImmediate in which case we decide to use it (This\n    // is so that we use the mockClock setImmediate).\n    return true;\n  }\n\n  return false;\n};\n\n\n/**\n * Cache for the setImmediate implementation.\n * @type {function(function())}\n * @private\n */\ngoog.async.nextTick.setImmediate_;\n\n\n/**\n * Determines the best possible implementation to run a function as soon as\n * the JS event loop is idle.\n * @return {function(function())} The \"setImmediate\" implementation.\n * @private\n */\ngoog.async.nextTick.getSetImmediateEmulator_ = function() {\n  // Create a private message channel and use it to postMessage empty messages\n  // to ourselves.\n  /** @type {!Function|undefined} */\n  var Channel = goog.global['MessageChannel'];\n  // If MessageChannel is not available and we are in a browser, implement\n  // an iframe based polyfill in browsers that have postMessage and\n  // document.addEventListener. The latter excludes IE8 because it has a\n  // synchronous postMessage implementation.\n  if (typeof Channel === 'undefined' && typeof window !== 'undefined' &&\n      window.postMessage && window.addEventListener &&\n      // Presto (The old pre-blink Opera engine) has problems with iframes\n      // and contentWindow.\n      !goog.labs.userAgent.engine.isPresto()) {\n    /** @constructor */\n    Channel = function() {\n      // Make an empty, invisible iframe.\n      var iframe = goog.dom.createElement(goog.dom.TagName.IFRAME);\n      iframe.style.display = 'none';\n      document.documentElement.appendChild(iframe);\n      var win = iframe.contentWindow;\n      var doc = win.document;\n      doc.open();\n      doc.close();\n      // Do not post anything sensitive over this channel, as the workaround for\n      // pages with file: origin could allow that information to be modified or\n      // intercepted.\n      var message = 'callImmediate' + Math.random();\n      // The same origin policy rejects attempts to postMessage from file: urls\n      // unless the origin is '*'.\n      var origin = win.location.protocol == 'file:' ?\n          '*' :\n          win.location.protocol + '//' + win.location.host;\n      var onmessage = goog.bind(function(e) {\n        // Validate origin and message to make sure that this message was\n        // intended for us. If the origin is set to '*' (see above) only the\n        // message needs to match since, for example, '*' != 'file://'. Allowing\n        // the wildcard is ok, as we are not concerned with security here.\n        if ((origin != '*' && e.origin != origin) || e.data != message) {\n          return;\n        }\n        this['port1'].onmessage();\n      }, this);\n      win.addEventListener('message', onmessage, false);\n      this['port1'] = {};\n      this['port2'] = {\n        postMessage: function() { win.postMessage(message, origin); }\n      };\n    };\n  }\n  if (typeof Channel !== 'undefined' && !goog.labs.userAgent.browser.isIE()) {\n    // Exclude all of IE due to\n    // http://codeforhire.com/2013/09/21/setimmediate-and-messagechannel-broken-on-internet-explorer-10/\n    // which allows starving postMessage with a busy setTimeout loop.\n    // This currently affects IE10 and IE11 which would otherwise be able\n    // to use the postMessage based fallbacks.\n    var channel = new Channel();\n    // Use a fifo linked list to call callbacks in the right order.\n    var head = {};\n    var tail = head;\n    channel['port1'].onmessage = function() {\n      if (head.next !== undefined) {\n        head = head.next;\n        var cb = head.cb;\n        head.cb = null;\n        cb();\n      }\n    };\n    return function(cb) {\n      tail.next = {cb: cb};\n      tail = tail.next;\n      channel['port2'].postMessage(0);\n    };\n  }\n  // Fall back to setTimeout with 0. In browsers this creates a delay of 5ms\n  // or more.\n  // NOTE(user): This fallback is used for IE.\n  return function(cb) {\n    goog.global.setTimeout(/** @type {function()} */ (cb), 0);\n  };\n};\n\n\n/**\n * Helper function that is overrided to protect callbacks with entry point\n * monitor if the application monitors entry points.\n * @param {function()} callback Callback function to fire as soon as possible.\n * @return {function()} The wrapped callback.\n * @private\n */\ngoog.async.nextTick.wrapCallback_ = goog.functions.identity;\n\n\n// Register the callback function as an entry point, so that it can be\n// monitored for exception handling, etc. This has to be done in this file\n// since it requires special code to handle all browsers.\ngoog.debug.entryPointRegistry.register(\n    /**\n     * @param {function(!Function): !Function} transformer The transforming\n     *     function.\n     */\n    function(transformer) { goog.async.nextTick.wrapCallback_ = transformer; });\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\ngoog.provide('goog.async.run');\n\ngoog.require('goog.async.WorkQueue');\ngoog.require('goog.async.nextTick');\ngoog.require('goog.async.throwException');\n\n/**\n * @define {boolean} If true, use the global Promise to implement goog.async.run\n * assuming either the native, or polyfill version will be used. Does still\n * permit tests to use forceNextTick.\n */\ngoog.ASSUME_NATIVE_PROMISE = goog.define('goog.ASSUME_NATIVE_PROMISE', false);\n\n/**\n * Fires the provided callback just before the current callstack unwinds, or as\n * soon as possible after the current JS execution context.\n * @param {function(this:THIS)} callback\n * @param {THIS=} opt_context Object to use as the \"this value\" when calling\n *     the provided function.\n * @template THIS\n */\ngoog.async.run = function(callback, opt_context) {\n  if (!goog.async.run.schedule_) {\n    goog.async.run.initializeRunner_();\n  }\n  if (!goog.async.run.workQueueScheduled_) {\n    // Nothing is currently scheduled, schedule it now.\n    goog.async.run.schedule_();\n    goog.async.run.workQueueScheduled_ = true;\n  }\n\n  goog.async.run.workQueue_.add(callback, opt_context);\n};\n\n\n/**\n * Initializes the function to use to process the work queue.\n * @private\n */\ngoog.async.run.initializeRunner_ = function() {\n  if (goog.ASSUME_NATIVE_PROMISE ||\n      (goog.global.Promise && goog.global.Promise.resolve)) {\n    // Use goog.global.Promise instead of just Promise because the relevant\n    // externs may be missing, and don't alias it because this could confuse the\n    // compiler into thinking the polyfill is required when it should be treated\n    // as optional.\n    var promise = goog.global.Promise.resolve(undefined);\n    goog.async.run.schedule_ = function() {\n      promise.then(goog.async.run.processWorkQueue);\n    };\n  } else {\n    goog.async.run.schedule_ = function() {\n      goog.async.nextTick(goog.async.run.processWorkQueue);\n    };\n  }\n};\n\n\n/**\n * Forces goog.async.run to use nextTick instead of Promise.\n *\n * This should only be done in unit tests. It's useful because MockClock\n * replaces nextTick, but not the browser Promise implementation, so it allows\n * Promise-based code to be tested with MockClock.\n *\n * However, we also want to run promises if the MockClock is no longer in\n * control so we schedule a backup \"setTimeout\" to the unmocked timeout if\n * provided.\n *\n * @param {function(function())=} opt_realSetTimeout\n */\ngoog.async.run.forceNextTick = function(opt_realSetTimeout) {\n  goog.async.run.schedule_ = function() {\n    goog.async.nextTick(goog.async.run.processWorkQueue);\n    if (opt_realSetTimeout) {\n      opt_realSetTimeout(goog.async.run.processWorkQueue);\n    }\n  };\n};\n\n\n/**\n * The function used to schedule work asynchronousely.\n * @private {function()}\n */\ngoog.async.run.schedule_;\n\n\n/** @private {boolean} */\ngoog.async.run.workQueueScheduled_ = false;\n\n\n/** @private {!goog.async.WorkQueue} */\ngoog.async.run.workQueue_ = new goog.async.WorkQueue();\n\n\nif (goog.DEBUG) {\n  /**\n   * Reset the work queue. Only available for tests in debug mode.\n   */\n  goog.async.run.resetQueue = function() {\n    goog.async.run.workQueueScheduled_ = false;\n    goog.async.run.workQueue_ = new goog.async.WorkQueue();\n  };\n}\n\n\n/**\n * Run any pending goog.async.run work items. This function is not intended\n * for general use, but for use by entry point handlers to run items ahead of\n * goog.async.nextTick.\n */\ngoog.async.run.processWorkQueue = function() {\n  // NOTE: additional work queue items may be added while processing.\n  var item = null;\n  while (item = goog.async.run.workQueue_.remove()) {\n    try {\n      item.fn.call(item.scope);\n    } catch (e) {\n      goog.async.throwException(e);\n    }\n    goog.async.run.workQueue_.returnUnused(item);\n  }\n\n  // There are no more work items, allow processing to be scheduled again.\n  goog.async.run.workQueueScheduled_ = false;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Defines the goog.dom.TagName class. Its constants enumerate\n * all HTML tag names specified in either the W3C HTML 4.01 index of elements\n * or the HTML5.1 specification.\n *\n * References:\n * https://www.w3.org/TR/html401/index/elements.html\n * https://www.w3.org/TR/html51/dom.html#elements\n */\ngoog.provide('goog.dom.TagName');\n\ngoog.require('goog.dom.HtmlElement');\n\n/**\n * A tag name for an HTML element.\n *\n * This type is a lie. All instances are actually strings. Do not implement it.\n *\n * It exists because we need an object type to host the template type parameter,\n * and that's not possible with literal or enum types. It is a record type so\n * that runtime type checks don't try to validate the lie.\n *\n * Closure Compiler unconditionally converts the following constants to their\n * string value (goog.dom.TagName.A -> 'A'). These are the consequences:\n * 1. Don't add any members or static members to goog.dom.TagName as they\n *    couldn't be accessed after this optimization.\n * 2. Keep the constant name and its string value the same:\n *    goog.dom.TagName.X = new goog.dom.TagName('Y');\n *    is converted to 'X', not 'Y'.\n *\n * @template T\n * @record\n */\ngoog.dom.TagName = class {\n  /**\n   * Cast a string into the tagname for the associated constructor.\n   *\n   * @template T\n   * @param {string} name\n   * @param {function(new:T, ...?)} type\n   * @return {!goog.dom.TagName<T>}\n   */\n  static cast(name, type) {\n    return /** @type {?} */ (name);\n  }\n\n  /** @suppress {unusedPrivateMembers} */\n  constructor() {\n    /** @private {null} */\n    this.googDomTagName_doNotImplementThisTypeOrElse_;\n\n    /** @private {T} */\n    this.ensureTypeScriptRemembersTypeT_;\n  }\n\n  /**\n   * Appease the compiler that instances are stringafiable for the\n   * purpose of being a dictionary key.\n   *\n   * Never implemented; always backed by `String::toString`.\n   *\n   * @override\n   * @return {string}\n   */\n  toString() {}\n};\n\n\n\n/** @const {!goog.dom.TagName<!HTMLAnchorElement>} */\ngoog.dom.TagName.A = /** @type {?} */ ('A');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.ABBR = /** @type {?} */ ('ABBR');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.ACRONYM = /** @type {?} */ ('ACRONYM');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.ADDRESS = /** @type {?} */ ('ADDRESS');\n\n/** @const {!goog.dom.TagName<!HTMLAppletElement>} */\ngoog.dom.TagName.APPLET = /** @type {?} */ ('APPLET');\n\n/** @const {!goog.dom.TagName<!HTMLAreaElement>} */\ngoog.dom.TagName.AREA = /** @type {?} */ ('AREA');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.ARTICLE = /** @type {?} */ ('ARTICLE');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.ASIDE = /** @type {?} */ ('ASIDE');\n\n/** @const {!goog.dom.TagName<!HTMLAudioElement>} */\ngoog.dom.TagName.AUDIO = /** @type {?} */ ('AUDIO');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.B = /** @type {?} */ ('B');\n\n/** @const {!goog.dom.TagName<!HTMLBaseElement>} */\ngoog.dom.TagName.BASE = /** @type {?} */ ('BASE');\n\n/** @const {!goog.dom.TagName<!HTMLBaseFontElement>} */\ngoog.dom.TagName.BASEFONT = /** @type {?} */ ('BASEFONT');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.BDI = /** @type {?} */ ('BDI');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.BDO = /** @type {?} */ ('BDO');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.BIG = /** @type {?} */ ('BIG');\n\n/** @const {!goog.dom.TagName<!HTMLQuoteElement>} */\ngoog.dom.TagName.BLOCKQUOTE = /** @type {?} */ ('BLOCKQUOTE');\n\n/** @const {!goog.dom.TagName<!HTMLBodyElement>} */\ngoog.dom.TagName.BODY = /** @type {?} */ ('BODY');\n\n/** @const {!goog.dom.TagName<!HTMLBRElement>} */\ngoog.dom.TagName.BR = /** @type {?} */ ('BR');\n\n/** @const {!goog.dom.TagName<!HTMLButtonElement>} */\ngoog.dom.TagName.BUTTON = /** @type {?} */ ('BUTTON');\n\n/** @const {!goog.dom.TagName<!HTMLCanvasElement>} */\ngoog.dom.TagName.CANVAS = /** @type {?} */ ('CANVAS');\n\n/** @const {!goog.dom.TagName<!HTMLTableCaptionElement>} */\ngoog.dom.TagName.CAPTION = /** @type {?} */ ('CAPTION');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.CENTER = /** @type {?} */ ('CENTER');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.CITE = /** @type {?} */ ('CITE');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.CODE = /** @type {?} */ ('CODE');\n\n/** @const {!goog.dom.TagName<!HTMLTableColElement>} */\ngoog.dom.TagName.COL = /** @type {?} */ ('COL');\n\n/** @const {!goog.dom.TagName<!HTMLTableColElement>} */\ngoog.dom.TagName.COLGROUP = /** @type {?} */ ('COLGROUP');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.COMMAND = /** @type {?} */ ('COMMAND');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.DATA = /** @type {?} */ ('DATA');\n\n/** @const {!goog.dom.TagName<!HTMLDataListElement>} */\ngoog.dom.TagName.DATALIST = /** @type {?} */ ('DATALIST');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.DD = /** @type {?} */ ('DD');\n\n/** @const {!goog.dom.TagName<!HTMLModElement>} */\ngoog.dom.TagName.DEL = /** @type {?} */ ('DEL');\n\n/** @const {!goog.dom.TagName<!HTMLDetailsElement>} */\ngoog.dom.TagName.DETAILS = /** @type {?} */ ('DETAILS');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.DFN = /** @type {?} */ ('DFN');\n\n/** @const {!goog.dom.TagName<!HTMLDialogElement>} */\ngoog.dom.TagName.DIALOG = /** @type {?} */ ('DIALOG');\n\n/** @const {!goog.dom.TagName<!HTMLDirectoryElement>} */\ngoog.dom.TagName.DIR = /** @type {?} */ ('DIR');\n\n/** @const {!goog.dom.TagName<!HTMLDivElement>} */\ngoog.dom.TagName.DIV = /** @type {?} */ ('DIV');\n\n/** @const {!goog.dom.TagName<!HTMLDListElement>} */\ngoog.dom.TagName.DL = /** @type {?} */ ('DL');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.DT = /** @type {?} */ ('DT');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.EM = /** @type {?} */ ('EM');\n\n/** @const {!goog.dom.TagName<!HTMLEmbedElement>} */\ngoog.dom.TagName.EMBED = /** @type {?} */ ('EMBED');\n\n/** @const {!goog.dom.TagName<!HTMLFieldSetElement>} */\ngoog.dom.TagName.FIELDSET = /** @type {?} */ ('FIELDSET');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.FIGCAPTION = /** @type {?} */ ('FIGCAPTION');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.FIGURE = /** @type {?} */ ('FIGURE');\n\n/** @const {!goog.dom.TagName<!HTMLFontElement>} */\ngoog.dom.TagName.FONT = /** @type {?} */ ('FONT');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.FOOTER = /** @type {?} */ ('FOOTER');\n\n/** @const {!goog.dom.TagName<!HTMLFormElement>} */\ngoog.dom.TagName.FORM = /** @type {?} */ ('FORM');\n\n/** @const {!goog.dom.TagName<!HTMLFrameElement>} */\ngoog.dom.TagName.FRAME = /** @type {?} */ ('FRAME');\n\n/** @const {!goog.dom.TagName<!HTMLFrameSetElement>} */\ngoog.dom.TagName.FRAMESET = /** @type {?} */ ('FRAMESET');\n\n/** @const {!goog.dom.TagName<!HTMLHeadingElement>} */\ngoog.dom.TagName.H1 = /** @type {?} */ ('H1');\n\n/** @const {!goog.dom.TagName<!HTMLHeadingElement>} */\ngoog.dom.TagName.H2 = /** @type {?} */ ('H2');\n\n/** @const {!goog.dom.TagName<!HTMLHeadingElement>} */\ngoog.dom.TagName.H3 = /** @type {?} */ ('H3');\n\n/** @const {!goog.dom.TagName<!HTMLHeadingElement>} */\ngoog.dom.TagName.H4 = /** @type {?} */ ('H4');\n\n/** @const {!goog.dom.TagName<!HTMLHeadingElement>} */\ngoog.dom.TagName.H5 = /** @type {?} */ ('H5');\n\n/** @const {!goog.dom.TagName<!HTMLHeadingElement>} */\ngoog.dom.TagName.H6 = /** @type {?} */ ('H6');\n\n/** @const {!goog.dom.TagName<!HTMLHeadElement>} */\ngoog.dom.TagName.HEAD = /** @type {?} */ ('HEAD');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.HEADER = /** @type {?} */ ('HEADER');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.HGROUP = /** @type {?} */ ('HGROUP');\n\n/** @const {!goog.dom.TagName<!HTMLHRElement>} */\ngoog.dom.TagName.HR = /** @type {?} */ ('HR');\n\n/** @const {!goog.dom.TagName<!HTMLHtmlElement>} */\ngoog.dom.TagName.HTML = /** @type {?} */ ('HTML');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.I = /** @type {?} */ ('I');\n\n/** @const {!goog.dom.TagName<!HTMLIFrameElement>} */\ngoog.dom.TagName.IFRAME = /** @type {?} */ ('IFRAME');\n\n/** @const {!goog.dom.TagName<!HTMLImageElement>} */\ngoog.dom.TagName.IMG = /** @type {?} */ ('IMG');\n\n/** @const {!goog.dom.TagName<!HTMLInputElement>} */\ngoog.dom.TagName.INPUT = /** @type {?} */ ('INPUT');\n\n/** @const {!goog.dom.TagName<!HTMLModElement>} */\ngoog.dom.TagName.INS = /** @type {?} */ ('INS');\n\n/** @const {!goog.dom.TagName<!HTMLIsIndexElement>} */\ngoog.dom.TagName.ISINDEX = /** @type {?} */ ('ISINDEX');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.KBD = /** @type {?} */ ('KBD');\n\n// HTMLKeygenElement is deprecated.\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.KEYGEN = /** @type {?} */ ('KEYGEN');\n\n/** @const {!goog.dom.TagName<!HTMLLabelElement>} */\ngoog.dom.TagName.LABEL = /** @type {?} */ ('LABEL');\n\n/** @const {!goog.dom.TagName<!HTMLLegendElement>} */\ngoog.dom.TagName.LEGEND = /** @type {?} */ ('LEGEND');\n\n/** @const {!goog.dom.TagName<!HTMLLIElement>} */\ngoog.dom.TagName.LI = /** @type {?} */ ('LI');\n\n/** @const {!goog.dom.TagName<!HTMLLinkElement>} */\ngoog.dom.TagName.LINK = /** @type {?} */ ('LINK');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.MAIN = /** @type {?} */ ('MAIN');\n\n/** @const {!goog.dom.TagName<!HTMLMapElement>} */\ngoog.dom.TagName.MAP = /** @type {?} */ ('MAP');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.MARK = /** @type {?} */ ('MARK');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.MATH = /** @type {?} */ ('MATH');\n\n/** @const {!goog.dom.TagName<!HTMLMenuElement>} */\ngoog.dom.TagName.MENU = /** @type {?} */ ('MENU');\n\n/** @const {!goog.dom.TagName<!HTMLMenuItemElement>} */\ngoog.dom.TagName.MENUITEM = /** @type {?} */ ('MENUITEM');\n\n/** @const {!goog.dom.TagName<!HTMLMetaElement>} */\ngoog.dom.TagName.META = /** @type {?} */ ('META');\n\n/** @const {!goog.dom.TagName<!HTMLMeterElement>} */\ngoog.dom.TagName.METER = /** @type {?} */ ('METER');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.NAV = /** @type {?} */ ('NAV');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.NOFRAMES = /** @type {?} */ ('NOFRAMES');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.NOSCRIPT = /** @type {?} */ ('NOSCRIPT');\n\n/** @const {!goog.dom.TagName<!HTMLObjectElement>} */\ngoog.dom.TagName.OBJECT = /** @type {?} */ ('OBJECT');\n\n/** @const {!goog.dom.TagName<!HTMLOListElement>} */\ngoog.dom.TagName.OL = /** @type {?} */ ('OL');\n\n/** @const {!goog.dom.TagName<!HTMLOptGroupElement>} */\ngoog.dom.TagName.OPTGROUP = /** @type {?} */ ('OPTGROUP');\n\n/** @const {!goog.dom.TagName<!HTMLOptionElement>} */\ngoog.dom.TagName.OPTION = /** @type {?} */ ('OPTION');\n\n/** @const {!goog.dom.TagName<!HTMLOutputElement>} */\ngoog.dom.TagName.OUTPUT = /** @type {?} */ ('OUTPUT');\n\n/** @const {!goog.dom.TagName<!HTMLParagraphElement>} */\ngoog.dom.TagName.P = /** @type {?} */ ('P');\n\n/** @const {!goog.dom.TagName<!HTMLParamElement>} */\ngoog.dom.TagName.PARAM = /** @type {?} */ ('PARAM');\n\n/** @const {!goog.dom.TagName<!HTMLPictureElement>} */\ngoog.dom.TagName.PICTURE = /** @type {?} */ ('PICTURE');\n\n/** @const {!goog.dom.TagName<!HTMLPreElement>} */\ngoog.dom.TagName.PRE = /** @type {?} */ ('PRE');\n\n/** @const {!goog.dom.TagName<!HTMLProgressElement>} */\ngoog.dom.TagName.PROGRESS = /** @type {?} */ ('PROGRESS');\n\n/** @const {!goog.dom.TagName<!HTMLQuoteElement>} */\ngoog.dom.TagName.Q = /** @type {?} */ ('Q');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.RP = /** @type {?} */ ('RP');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.RT = /** @type {?} */ ('RT');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.RTC = /** @type {?} */ ('RTC');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.RUBY = /** @type {?} */ ('RUBY');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.S = /** @type {?} */ ('S');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.SAMP = /** @type {?} */ ('SAMP');\n\n/** @const {!goog.dom.TagName<!HTMLScriptElement>} */\ngoog.dom.TagName.SCRIPT = /** @type {?} */ ('SCRIPT');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.SECTION = /** @type {?} */ ('SECTION');\n\n/** @const {!goog.dom.TagName<!HTMLSelectElement>} */\ngoog.dom.TagName.SELECT = /** @type {?} */ ('SELECT');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.SMALL = /** @type {?} */ ('SMALL');\n\n/** @const {!goog.dom.TagName<!HTMLSourceElement>} */\ngoog.dom.TagName.SOURCE = /** @type {?} */ ('SOURCE');\n\n/** @const {!goog.dom.TagName<!HTMLSpanElement>} */\ngoog.dom.TagName.SPAN = /** @type {?} */ ('SPAN');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.STRIKE = /** @type {?} */ ('STRIKE');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.STRONG = /** @type {?} */ ('STRONG');\n\n/** @const {!goog.dom.TagName<!HTMLStyleElement>} */\ngoog.dom.TagName.STYLE = /** @type {?} */ ('STYLE');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.SUB = /** @type {?} */ ('SUB');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.SUMMARY = /** @type {?} */ ('SUMMARY');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.SUP = /** @type {?} */ ('SUP');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.SVG = /** @type {?} */ ('SVG');\n\n/** @const {!goog.dom.TagName<!HTMLTableElement>} */\ngoog.dom.TagName.TABLE = /** @type {?} */ ('TABLE');\n\n/** @const {!goog.dom.TagName<!HTMLTableSectionElement>} */\ngoog.dom.TagName.TBODY = /** @type {?} */ ('TBODY');\n\n/** @const {!goog.dom.TagName<!HTMLTableCellElement>} */\ngoog.dom.TagName.TD = /** @type {?} */ ('TD');\n\n/** @const {!goog.dom.TagName<!HTMLTemplateElement>} */\ngoog.dom.TagName.TEMPLATE = /** @type {?} */ ('TEMPLATE');\n\n/** @const {!goog.dom.TagName<!HTMLTextAreaElement>} */\ngoog.dom.TagName.TEXTAREA = /** @type {?} */ ('TEXTAREA');\n\n/** @const {!goog.dom.TagName<!HTMLTableSectionElement>} */\ngoog.dom.TagName.TFOOT = /** @type {?} */ ('TFOOT');\n\n/** @const {!goog.dom.TagName<!HTMLTableCellElement>} */\ngoog.dom.TagName.TH = /** @type {?} */ ('TH');\n\n/** @const {!goog.dom.TagName<!HTMLTableSectionElement>} */\ngoog.dom.TagName.THEAD = /** @type {?} */ ('THEAD');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.TIME = /** @type {?} */ ('TIME');\n\n/** @const {!goog.dom.TagName<!HTMLTitleElement>} */\ngoog.dom.TagName.TITLE = /** @type {?} */ ('TITLE');\n\n/** @const {!goog.dom.TagName<!HTMLTableRowElement>} */\ngoog.dom.TagName.TR = /** @type {?} */ ('TR');\n\n/** @const {!goog.dom.TagName<!HTMLTrackElement>} */\ngoog.dom.TagName.TRACK = /** @type {?} */ ('TRACK');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.TT = /** @type {?} */ ('TT');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.U = /** @type {?} */ ('U');\n\n/** @const {!goog.dom.TagName<!HTMLUListElement>} */\ngoog.dom.TagName.UL = /** @type {?} */ ('UL');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.VAR = /** @type {?} */ ('VAR');\n\n/** @const {!goog.dom.TagName<!HTMLVideoElement>} */\ngoog.dom.TagName.VIDEO = /** @type {?} */ ('VIDEO');\n\n/** @const {!goog.dom.TagName<!goog.dom.HtmlElement>} */\ngoog.dom.TagName.WBR = /** @type {?} */ ('WBR');\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\ngoog.provide('goog.Promise');\n\ngoog.require('goog.Thenable');\ngoog.require('goog.asserts');\ngoog.require('goog.async.FreeList');\ngoog.require('goog.async.run');\ngoog.require('goog.async.throwException');\ngoog.require('goog.debug.Error');\ngoog.require('goog.promise.Resolver');\n\n\n\n/**\n * NOTE: This class was created in anticipation of the built-in Promise type\n * being standardized and implemented across browsers. Now that Promise is\n * available in modern browsers, and is automatically polyfilled by the Closure\n * Compiler, by default, most new code should use native `Promise`\n * instead of `goog.Promise`. However, `goog.Promise` has the\n * concept of cancellation which native Promises do not yet have. So code\n * needing cancellation may still want to use `goog.Promise`.\n *\n * Promises provide a result that may be resolved asynchronously. A Promise may\n * be resolved by being fulfilled with a fulfillment value, rejected with a\n * rejection reason, or blocked by another Promise. A Promise is said to be\n * settled if it is either fulfilled or rejected. Once settled, the Promise\n * result is immutable.\n *\n * Promises may represent results of any type, including undefined. Rejection\n * reasons are typically Errors, but may also be of any type. Closure Promises\n * allow for optional type annotations that enforce that fulfillment values are\n * of the appropriate types at compile time.\n *\n * The result of a Promise is accessible by calling `then` and registering\n * `onFulfilled` and `onRejected` callbacks. Once the Promise\n * is settled, the relevant callbacks are invoked with the fulfillment value or\n * rejection reason as argument. Callbacks are always invoked in the order they\n * were registered, even when additional `then` calls are made from inside\n * another callback. A callback is always run asynchronously sometime after the\n * scope containing the registering `then` invocation has returned.\n *\n * If a Promise is resolved with another Promise, the first Promise will block\n * until the second is settled, and then assumes the same result as the second\n * Promise. This allows Promises to depend on the results of other Promises,\n * linking together multiple asynchronous operations.\n *\n * This implementation is compatible with the Promises/A+ specification and\n * passes that specification's conformance test suite. A Closure Promise may be\n * resolved with a Promise instance (or sufficiently compatible Promise-like\n * object) created by other Promise implementations. From the specification,\n * Promise-like objects are known as \"Thenables\".\n *\n * @see http://promisesaplus.com/\n *\n * @param {function(\n *             this:RESOLVER_CONTEXT,\n *             function((TYPE|IThenable<TYPE>|Thenable)=),\n *             function(*=)): void} resolver\n *     Initialization function that is invoked immediately with `resolve`\n *     and `reject` functions as arguments. The Promise is resolved or\n *     rejected with the first argument passed to either function.\n * @param {RESOLVER_CONTEXT=} opt_context An optional context for executing the\n *     resolver function. If unspecified, the resolver function will be executed\n *     in the default scope.\n * @constructor\n * @struct\n * @final\n * @implements {goog.Thenable<TYPE>}\n * @template TYPE,RESOLVER_CONTEXT\n */\ngoog.Promise = function(resolver, opt_context) {\n  /**\n   * The internal state of this Promise. Either PENDING, FULFILLED, REJECTED, or\n   * BLOCKED.\n   * @private {goog.Promise.State_}\n   */\n  this.state_ = goog.Promise.State_.PENDING;\n\n  /**\n   * The settled result of the Promise. Immutable once set with either a\n   * fulfillment value or rejection reason.\n   * @private {*}\n   */\n  this.result_ = undefined;\n\n  /**\n   * For Promises created by calling `then()`, the originating parent.\n   * @private {?goog.Promise}\n   */\n  this.parent_ = null;\n\n  /**\n   * The linked list of `onFulfilled` and `onRejected` callbacks\n   * added to this Promise by calls to `then()`.\n   * @private {?goog.Promise.CallbackEntry_}\n   */\n  this.callbackEntries_ = null;\n\n  /**\n   * The tail of the linked list of `onFulfilled` and `onRejected`\n   * callbacks added to this Promise by calls to `then()`.\n   * @private {?goog.Promise.CallbackEntry_}\n   */\n  this.callbackEntriesTail_ = null;\n\n  /**\n   * Whether the Promise is in the queue of Promises to execute.\n   * @private {boolean}\n   */\n  this.executing_ = false;\n\n  if (goog.Promise.UNHANDLED_REJECTION_DELAY > 0) {\n    /**\n     * A timeout ID used when the `UNHANDLED_REJECTION_DELAY` is greater\n     * than 0 milliseconds. The ID is set when the Promise is rejected, and\n     * cleared only if an `onRejected` callback is invoked for the\n     * Promise (or one of its descendants) before the delay is exceeded.\n     *\n     * If the rejection is not handled before the timeout completes, the\n     * rejection reason is passed to the unhandled rejection handler.\n     * @private {number}\n     */\n    this.unhandledRejectionId_ = 0;\n  } else if (goog.Promise.UNHANDLED_REJECTION_DELAY == 0) {\n    /**\n     * When the `UNHANDLED_REJECTION_DELAY` is set to 0 milliseconds, a\n     * boolean that is set if the Promise is rejected, and reset to false if an\n     * `onRejected` callback is invoked for the Promise (or one of its\n     * descendants). If the rejection is not handled before the next timestep,\n     * the rejection reason is passed to the unhandled rejection handler.\n     * @private {boolean}\n     */\n    this.hadUnhandledRejection_ = false;\n  }\n\n  if (goog.Promise.LONG_STACK_TRACES) {\n    /**\n     * A list of stack trace frames pointing to the locations where this Promise\n     * was created or had callbacks added to it. Saved to add additional context\n     * to stack traces when an exception is thrown.\n     * @private {!Array<string>}\n     */\n    this.stack_ = [];\n    this.addStackTrace_(new Error('created'));\n\n    /**\n     * Index of the most recently executed stack frame entry.\n     * @private {number}\n     */\n    this.currentStep_ = 0;\n  }\n\n  // As an optimization, we can skip this if resolver is goog.nullFunction.\n  // This value is passed internally when creating a promise which will be\n  // resolved through a more optimized path.\n  if (resolver != goog.nullFunction) {\n    try {\n      var self = this;\n      resolver.call(\n          opt_context,\n          function(value) {\n            self.resolve_(goog.Promise.State_.FULFILLED, value);\n          },\n          function(reason) {\n            if (goog.DEBUG &&\n                !(reason instanceof goog.Promise.CancellationError)) {\n              try {\n                // Promise was rejected. Step up one call frame to see why.\n                if (reason instanceof Error) {\n                  throw reason;\n                } else {\n                  throw new Error('Promise rejected.');\n                }\n              } catch (e) {\n                // Only thrown so browser dev tools can catch rejections of\n                // promises when the option to break on caught exceptions is\n                // activated.\n              }\n            }\n            self.resolve_(goog.Promise.State_.REJECTED, reason);\n          });\n    } catch (e) {\n      this.resolve_(goog.Promise.State_.REJECTED, e);\n    }\n  }\n};\n\n\n/**\n * @define {boolean} Whether traces of `then` calls should be included in\n * exceptions thrown\n */\ngoog.Promise.LONG_STACK_TRACES =\n    goog.define('goog.Promise.LONG_STACK_TRACES', false);\n\n\n/**\n * @define {number} The delay in milliseconds before a rejected Promise's reason\n * is passed to the rejection handler. By default, the rejection handler\n * rethrows the rejection reason so that it appears in the developer console or\n * `window.onerror` handler.\n *\n * Rejections are rethrown as quickly as possible by default. A negative value\n * disables rejection handling entirely.\n */\ngoog.Promise.UNHANDLED_REJECTION_DELAY =\n    goog.define('goog.Promise.UNHANDLED_REJECTION_DELAY', 0);\n\n\n/**\n * The possible internal states for a Promise. These states are not directly\n * observable to external callers.\n * @enum {number}\n * @private\n */\ngoog.Promise.State_ = {\n  /** The Promise is waiting for resolution. */\n  PENDING: 0,\n\n  /** The Promise is blocked waiting for the result of another Thenable. */\n  BLOCKED: 1,\n\n  /** The Promise has been resolved with a fulfillment value. */\n  FULFILLED: 2,\n\n  /** The Promise has been resolved with a rejection reason. */\n  REJECTED: 3\n};\n\n\n\n/**\n * Entries in the callback chain. Each call to `then`,\n * `thenCatch`, or `thenAlways` creates an entry containing the\n * functions that may be invoked once the Promise is settled.\n *\n * @private @final @struct @constructor\n */\ngoog.Promise.CallbackEntry_ = function() {\n  /** @type {?goog.Promise} */\n  this.child = null;\n  /** @type {?Function} */\n  this.onFulfilled = null;\n  /** @type {?Function} */\n  this.onRejected = null;\n  /** @type {?} */\n  this.context = null;\n  /** @type {?goog.Promise.CallbackEntry_} */\n  this.next = null;\n\n  /**\n   * A boolean value to indicate this is a \"thenAlways\" callback entry.\n   * Unlike a normal \"then/thenVoid\" a \"thenAlways doesn't participate\n   * in \"cancel\" considerations but is simply an observer and requires\n   * special handling.\n   * @type {boolean}\n   */\n  this.always = false;\n};\n\n\n/** clear the object prior to reuse */\ngoog.Promise.CallbackEntry_.prototype.reset = function() {\n  this.child = null;\n  this.onFulfilled = null;\n  this.onRejected = null;\n  this.context = null;\n  this.always = false;\n};\n\n\n/**\n * @define {number} The number of currently unused objects to keep around for\n *    reuse.\n */\ngoog.Promise.DEFAULT_MAX_UNUSED =\n    goog.define('goog.Promise.DEFAULT_MAX_UNUSED', 100);\n\n\n/** @const @private {goog.async.FreeList<!goog.Promise.CallbackEntry_>} */\ngoog.Promise.freelist_ = new goog.async.FreeList(\n    function() { return new goog.Promise.CallbackEntry_(); },\n    function(item) { item.reset(); }, goog.Promise.DEFAULT_MAX_UNUSED);\n\n\n/**\n * @param {Function} onFulfilled\n * @param {Function} onRejected\n * @param {?} context\n * @return {!goog.Promise.CallbackEntry_}\n * @private\n */\ngoog.Promise.getCallbackEntry_ = function(onFulfilled, onRejected, context) {\n  var entry = goog.Promise.freelist_.get();\n  entry.onFulfilled = onFulfilled;\n  entry.onRejected = onRejected;\n  entry.context = context;\n  return entry;\n};\n\n\n/**\n * @param {!goog.Promise.CallbackEntry_} entry\n * @private\n */\ngoog.Promise.returnEntry_ = function(entry) {\n  goog.Promise.freelist_.put(entry);\n};\n\n\n// NOTE: this is the same template expression as is used for\n// goog.IThenable.prototype.then\n\n\n/**\n * @param {VALUE=} opt_value\n * @return {RESULT} A new Promise that is immediately resolved\n *     with the given value. If the input value is already a goog.Promise, it\n *     will be returned immediately without creating a new instance.\n * @template VALUE\n * @template RESULT := type('goog.Promise',\n *     cond(isUnknown(VALUE), unknown(),\n *       mapunion(VALUE, (V) =>\n *         cond(isTemplatized(V) && sub(rawTypeOf(V), 'IThenable'),\n *           templateTypeOf(V, 0),\n *           cond(sub(V, 'Thenable'),\n *              unknown(),\n *              V)))))\n * =:\n */\ngoog.Promise.resolve = function(opt_value) {\n  if (opt_value instanceof goog.Promise) {\n    // Avoid creating a new object if we already have a promise object\n    // of the correct type.\n    return opt_value;\n  }\n\n  // Passing goog.nullFunction will cause the constructor to take an optimized\n  // path that skips calling the resolver function.\n  var promise = new goog.Promise(goog.nullFunction);\n  promise.resolve_(goog.Promise.State_.FULFILLED, opt_value);\n  return promise;\n};\n\n\n/**\n * @param {*=} opt_reason\n * @return {!goog.Promise} A new Promise that is immediately rejected with the\n *     given reason.\n */\ngoog.Promise.reject = function(opt_reason) {\n  return new goog.Promise(function(resolve, reject) { reject(opt_reason); });\n};\n\n\n/**\n * This is identical to\n * {@code goog.Promise.resolve(value).then(onFulfilled, onRejected)}, but it\n * avoids creating an unnecessary wrapper Promise when `value` is already\n * thenable.\n *\n * @param {?(goog.Thenable<TYPE>|Thenable|TYPE)} value\n * @param {function(TYPE): ?} onFulfilled\n * @param {function(*): *} onRejected\n * @template TYPE\n * @private\n */\ngoog.Promise.resolveThen_ = function(value, onFulfilled, onRejected) {\n  var isThenable =\n      goog.Promise.maybeThen_(value, onFulfilled, onRejected, null);\n  if (!isThenable) {\n    goog.async.run(goog.partial(onFulfilled, value));\n  }\n};\n\n\n/**\n * @param {!Array<?(goog.Promise<TYPE>|goog.Thenable<TYPE>|Thenable|*)>}\n *     promises\n * @return {!goog.Promise<TYPE>} A Promise that receives the result of the\n *     first Promise (or Promise-like) input to settle immediately after it\n *     settles.\n * @template TYPE\n */\ngoog.Promise.race = function(promises) {\n  return new goog.Promise(function(resolve, reject) {\n    if (!promises.length) {\n      resolve(undefined);\n    }\n    for (var i = 0, promise; i < promises.length; i++) {\n      promise = promises[i];\n      goog.Promise.resolveThen_(promise, resolve, reject);\n    }\n  });\n};\n\n\n/**\n * @param {!Array<?(goog.Promise<TYPE>|goog.Thenable<TYPE>|Thenable|*)>}\n *     promises\n * @return {!goog.Promise<!Array<TYPE>>} A Promise that receives a list of\n *     every fulfilled value once every input Promise (or Promise-like) is\n *     successfully fulfilled, or is rejected with the first rejection reason\n *     immediately after it is rejected.\n * @template TYPE\n */\ngoog.Promise.all = function(promises) {\n  return new goog.Promise(function(resolve, reject) {\n    var toFulfill = promises.length;\n    var values = [];\n\n    if (!toFulfill) {\n      resolve(values);\n      return;\n    }\n\n    var onFulfill = function(index, value) {\n      toFulfill--;\n      values[index] = value;\n      if (toFulfill == 0) {\n        resolve(values);\n      }\n    };\n\n    var onReject = function(reason) { reject(reason); };\n\n    for (var i = 0, promise; i < promises.length; i++) {\n      promise = promises[i];\n      goog.Promise.resolveThen_(promise, goog.partial(onFulfill, i), onReject);\n    }\n  });\n};\n\n\n/**\n * @param {!Array<?(goog.Promise<TYPE>|goog.Thenable<TYPE>|Thenable|*)>}\n *     promises\n * @return {!goog.Promise<!Array<{\n *     fulfilled: boolean,\n *     value: (TYPE|undefined),\n *     reason: (*|undefined)}>>} A Promise that resolves with a list of\n *         result objects once all input Promises (or Promise-like) have\n *         settled. Each result object contains a 'fulfilled' boolean indicating\n *         whether an input Promise was fulfilled or rejected. For fulfilled\n *         Promises, the resulting value is stored in the 'value' field. For\n *         rejected Promises, the rejection reason is stored in the 'reason'\n *         field.\n * @template TYPE\n */\ngoog.Promise.allSettled = function(promises) {\n  return new goog.Promise(function(resolve, reject) {\n    var toSettle = promises.length;\n    var results = [];\n\n    if (!toSettle) {\n      resolve(results);\n      return;\n    }\n\n    var onSettled = function(index, fulfilled, result) {\n      toSettle--;\n      results[index] = fulfilled ? {fulfilled: true, value: result} :\n                                   {fulfilled: false, reason: result};\n      if (toSettle == 0) {\n        resolve(results);\n      }\n    };\n\n    for (var i = 0, promise; i < promises.length; i++) {\n      promise = promises[i];\n      goog.Promise.resolveThen_(\n          promise, goog.partial(onSettled, i, true /* fulfilled */),\n          goog.partial(onSettled, i, false /* fulfilled */));\n    }\n  });\n};\n\n\n/**\n * @param {!Array<?(goog.Promise<TYPE>|goog.Thenable<TYPE>|Thenable|*)>}\n *     promises\n * @return {!goog.Promise<TYPE>} A Promise that receives the value of the first\n *     input to be fulfilled, or is rejected with a list of every rejection\n *     reason if all inputs are rejected.\n * @template TYPE\n */\ngoog.Promise.firstFulfilled = function(promises) {\n  return new goog.Promise(function(resolve, reject) {\n    var toReject = promises.length;\n    var reasons = [];\n\n    if (!toReject) {\n      resolve(undefined);\n      return;\n    }\n\n    var onFulfill = function(value) { resolve(value); };\n\n    var onReject = function(index, reason) {\n      toReject--;\n      reasons[index] = reason;\n      if (toReject == 0) {\n        reject(reasons);\n      }\n    };\n\n    for (var i = 0, promise; i < promises.length; i++) {\n      promise = promises[i];\n      goog.Promise.resolveThen_(promise, onFulfill, goog.partial(onReject, i));\n    }\n  });\n};\n\n\n/**\n * @return {!goog.promise.Resolver<TYPE>} Resolver wrapping the promise and its\n *     resolve / reject functions. Resolving or rejecting the resolver\n *     resolves or rejects the promise.\n * @template TYPE\n */\ngoog.Promise.withResolver = function() {\n  var resolve, reject;\n  var promise = new goog.Promise(function(rs, rj) {\n    resolve = rs;\n    reject = rj;\n  });\n  return new goog.Promise.Resolver_(promise, resolve, reject);\n};\n\n\n/**\n * Adds callbacks that will operate on the result of the Promise, returning a\n * new child Promise.\n *\n * If the Promise is fulfilled, the `onFulfilled` callback will be invoked\n * with the fulfillment value as argument, and the child Promise will be\n * fulfilled with the return value of the callback. If the callback throws an\n * exception, the child Promise will be rejected with the thrown value instead.\n *\n * If the Promise is rejected, the `onRejected` callback will be invoked\n * with the rejection reason as argument, and the child Promise will be resolved\n * with the return value or rejected with the thrown value of the callback.\n *\n * @override\n */\ngoog.Promise.prototype.then = function(\n    opt_onFulfilled, opt_onRejected, opt_context) {\n\n  if (opt_onFulfilled != null) {\n    goog.asserts.assertFunction(\n        opt_onFulfilled, 'opt_onFulfilled should be a function.');\n  }\n  if (opt_onRejected != null) {\n    goog.asserts.assertFunction(\n        opt_onRejected,\n        'opt_onRejected should be a function. Did you pass opt_context ' +\n            'as the second argument instead of the third?');\n  }\n\n  if (goog.Promise.LONG_STACK_TRACES) {\n    this.addStackTrace_(new Error('then'));\n  }\n\n  return this.addChildPromise_(\n      goog.isFunction(opt_onFulfilled) ? opt_onFulfilled : null,\n      goog.isFunction(opt_onRejected) ? opt_onRejected : null, opt_context);\n};\ngoog.Thenable.addImplementation(goog.Promise);\n\n\n/**\n * Adds callbacks that will operate on the result of the Promise without\n * returning a child Promise (unlike \"then\").\n *\n * If the Promise is fulfilled, the `onFulfilled` callback will be invoked\n * with the fulfillment value as argument.\n *\n * If the Promise is rejected, the `onRejected` callback will be invoked\n * with the rejection reason as argument.\n *\n * @param {?(function(this:THIS, TYPE):?)=} opt_onFulfilled A\n *     function that will be invoked with the fulfillment value if the Promise\n *     is fulfilled.\n * @param {?(function(this:THIS, *): *)=} opt_onRejected A function that will\n *     be invoked with the rejection reason if the Promise is rejected.\n * @param {THIS=} opt_context An optional context object that will be the\n *     execution context for the callbacks. By default, functions are executed\n *     with the default this.\n * @package\n * @template THIS\n */\ngoog.Promise.prototype.thenVoid = function(\n    opt_onFulfilled, opt_onRejected, opt_context) {\n\n  if (opt_onFulfilled != null) {\n    goog.asserts.assertFunction(\n        opt_onFulfilled, 'opt_onFulfilled should be a function.');\n  }\n  if (opt_onRejected != null) {\n    goog.asserts.assertFunction(\n        opt_onRejected,\n        'opt_onRejected should be a function. Did you pass opt_context ' +\n            'as the second argument instead of the third?');\n  }\n\n  if (goog.Promise.LONG_STACK_TRACES) {\n    this.addStackTrace_(new Error('then'));\n  }\n\n  // Note: no default rejection handler is provided here as we need to\n  // distinguish unhandled rejections.\n  this.addCallbackEntry_(\n      goog.Promise.getCallbackEntry_(\n          opt_onFulfilled || goog.nullFunction, opt_onRejected || null,\n          opt_context));\n};\n\n\n/**\n * Adds a callback that will be invoked when the Promise is settled (fulfilled\n * or rejected). The callback receives no argument, and no new child Promise is\n * created. This is useful for ensuring that cleanup takes place after certain\n * asynchronous operations. Callbacks added with `thenAlways` will be\n * executed in the same order with other calls to `then`,\n * `thenAlways`, or `thenCatch`.\n *\n * Since it does not produce a new child Promise, cancellation propagation is\n * not prevented by adding callbacks with `thenAlways`. A Promise that has\n * a cleanup handler added with `thenAlways` will be canceled if all of\n * its children created by `then` (or `thenCatch`) are canceled.\n * Additionally, since any rejections are not passed to the callback, it does\n * not stop the unhandled rejection handler from running.\n *\n * @param {function(this:THIS): void} onSettled A function that will be invoked\n *     when the Promise is settled (fulfilled or rejected).\n * @param {THIS=} opt_context An optional context object that will be the\n *     execution context for the callbacks. By default, functions are executed\n *     in the global scope.\n * @return {!goog.Promise<TYPE>} This Promise, for chaining additional calls.\n * @template THIS\n */\ngoog.Promise.prototype.thenAlways = function(onSettled, opt_context) {\n  if (goog.Promise.LONG_STACK_TRACES) {\n    this.addStackTrace_(new Error('thenAlways'));\n  }\n\n  var entry = goog.Promise.getCallbackEntry_(onSettled, onSettled, opt_context);\n  entry.always = true;\n  this.addCallbackEntry_(entry);\n  return this;\n};\n\n\n/**\n * Adds a callback that will be invoked only if the Promise is rejected. This\n * is equivalent to `then(null, onRejected)`.\n *\n * @param {function(this:THIS, *): *} onRejected A function that will be\n *     invoked with the rejection reason if this Promise is rejected.\n * @param {THIS=} opt_context An optional context object that will be the\n *     execution context for the callbacks. By default, functions are executed\n *     in the global scope.\n * @return {!goog.Promise} A new Promise that will resolve either to the\n *     value of this promise, or if this promise is rejected, the result of\n *     `onRejected`. The returned Promise will reject if `onRejected` throws.\n * @template THIS\n */\ngoog.Promise.prototype.thenCatch = function(onRejected, opt_context) {\n  if (goog.Promise.LONG_STACK_TRACES) {\n    this.addStackTrace_(new Error('thenCatch'));\n  }\n  return this.addChildPromise_(null, onRejected, opt_context);\n};\n\n\n/**\n * Cancels the Promise if it is still pending by rejecting it with a cancel\n * Error. No action is performed if the Promise is already resolved.\n *\n * All child Promises of the canceled Promise will be rejected with the same\n * cancel error, as with normal Promise rejection. If the Promise to be canceled\n * is the only child of a pending Promise, the parent Promise will also be\n * canceled. Cancellation may propagate upward through multiple generations.\n *\n * @param {string=} opt_message An optional debugging message for describing the\n *     cancellation reason.\n */\ngoog.Promise.prototype.cancel = function(opt_message) {\n  if (this.state_ == goog.Promise.State_.PENDING) {\n    // Instantiate Error object synchronously. This ensures Error::stack points\n    // to the cancel() callsite.\n    var err = new goog.Promise.CancellationError(opt_message);\n    goog.async.run(function() {\n      this.cancelInternal_(err);\n    }, this);\n  }\n};\n\n\n/**\n * Cancels this Promise with the given error.\n *\n * @param {!Error} err The cancellation error.\n * @private\n */\ngoog.Promise.prototype.cancelInternal_ = function(err) {\n  if (this.state_ == goog.Promise.State_.PENDING) {\n    if (this.parent_) {\n      // Cancel the Promise and remove it from the parent's child list.\n      this.parent_.cancelChild_(this, err);\n      this.parent_ = null;\n    } else {\n      this.resolve_(goog.Promise.State_.REJECTED, err);\n    }\n  }\n};\n\n\n/**\n * Cancels a child Promise from the list of callback entries. If the Promise has\n * not already been resolved, reject it with a cancel error. If there are no\n * other children in the list of callback entries, propagate the cancellation\n * by canceling this Promise as well.\n *\n * @param {!goog.Promise} childPromise The Promise to cancel.\n * @param {!Error} err The cancel error to use for rejecting the Promise.\n * @private\n */\ngoog.Promise.prototype.cancelChild_ = function(childPromise, err) {\n  if (!this.callbackEntries_) {\n    return;\n  }\n  var childCount = 0;\n  var childEntry = null;\n  var beforeChildEntry = null;\n\n  // Find the callback entry for the childPromise, and count whether there are\n  // additional child Promises.\n  for (var entry = this.callbackEntries_; entry; entry = entry.next) {\n    if (!entry.always) {\n      childCount++;\n      if (entry.child == childPromise) {\n        childEntry = entry;\n      }\n      if (childEntry && childCount > 1) {\n        break;\n      }\n    }\n    if (!childEntry) {\n      beforeChildEntry = entry;\n    }\n  }\n\n  // Can a child entry be missing?\n\n  // If the child Promise was the only child, cancel this Promise as well.\n  // Otherwise, reject only the child Promise with the cancel error.\n  if (childEntry) {\n    if (this.state_ == goog.Promise.State_.PENDING && childCount == 1) {\n      this.cancelInternal_(err);\n    } else {\n      if (beforeChildEntry) {\n        this.removeEntryAfter_(beforeChildEntry);\n      } else {\n        this.popEntry_();\n      }\n\n      this.executeCallback_(childEntry, goog.Promise.State_.REJECTED, err);\n    }\n  }\n};\n\n\n/**\n * Adds a callback entry to the current Promise, and schedules callback\n * execution if the Promise has already been settled.\n *\n * @param {goog.Promise.CallbackEntry_} callbackEntry Record containing\n *     `onFulfilled` and `onRejected` callbacks to execute after\n *     the Promise is settled.\n * @private\n */\ngoog.Promise.prototype.addCallbackEntry_ = function(callbackEntry) {\n  if (!this.hasEntry_() && (this.state_ == goog.Promise.State_.FULFILLED ||\n                            this.state_ == goog.Promise.State_.REJECTED)) {\n    this.scheduleCallbacks_();\n  }\n  this.queueEntry_(callbackEntry);\n};\n\n\n/**\n * Creates a child Promise and adds it to the callback entry list. The result of\n * the child Promise is determined by the state of the parent Promise and the\n * result of the `onFulfilled` or `onRejected` callbacks as\n * specified in the Promise resolution procedure.\n *\n * @see http://promisesaplus.com/#the__method\n *\n * @param {?function(this:THIS, TYPE):\n *          (RESULT|goog.Promise<RESULT>|Thenable)} onFulfilled A callback that\n *     will be invoked if the Promise is fulfilled, or null.\n * @param {?function(this:THIS, *): *} onRejected A callback that will be\n *     invoked if the Promise is rejected, or null.\n * @param {THIS=} opt_context An optional execution context for the callbacks.\n *     in the default calling context.\n * @return {!goog.Promise} The child Promise.\n * @template RESULT,THIS\n * @private\n */\ngoog.Promise.prototype.addChildPromise_ = function(\n    onFulfilled, onRejected, opt_context) {\n\n  /** @type {goog.Promise.CallbackEntry_} */\n  var callbackEntry = goog.Promise.getCallbackEntry_(null, null, null);\n\n  callbackEntry.child = new goog.Promise(function(resolve, reject) {\n    // Invoke onFulfilled, or resolve with the parent's value if absent.\n    callbackEntry.onFulfilled = onFulfilled ? function(value) {\n      try {\n        var result = onFulfilled.call(opt_context, value);\n        resolve(result);\n      } catch (err) {\n        reject(err);\n      }\n    } : resolve;\n\n    // Invoke onRejected, or reject with the parent's reason if absent.\n    callbackEntry.onRejected = onRejected ? function(reason) {\n      try {\n        var result = onRejected.call(opt_context, reason);\n        if (result === undefined &&\n            reason instanceof goog.Promise.CancellationError) {\n          // Propagate cancellation to children if no other result is returned.\n          reject(reason);\n        } else {\n          resolve(result);\n        }\n      } catch (err) {\n        reject(err);\n      }\n    } : reject;\n  });\n\n  callbackEntry.child.parent_ = this;\n  this.addCallbackEntry_(callbackEntry);\n  return callbackEntry.child;\n};\n\n\n/**\n * Unblocks the Promise and fulfills it with the given value.\n *\n * @param {TYPE} value\n * @private\n */\ngoog.Promise.prototype.unblockAndFulfill_ = function(value) {\n  goog.asserts.assert(this.state_ == goog.Promise.State_.BLOCKED);\n  this.state_ = goog.Promise.State_.PENDING;\n  this.resolve_(goog.Promise.State_.FULFILLED, value);\n};\n\n\n/**\n * Unblocks the Promise and rejects it with the given rejection reason.\n *\n * @param {*} reason\n * @private\n */\ngoog.Promise.prototype.unblockAndReject_ = function(reason) {\n  goog.asserts.assert(this.state_ == goog.Promise.State_.BLOCKED);\n  this.state_ = goog.Promise.State_.PENDING;\n  this.resolve_(goog.Promise.State_.REJECTED, reason);\n};\n\n\n/**\n * Attempts to resolve a Promise with a given resolution state and value. This\n * is a no-op if the given Promise has already been resolved.\n *\n * If the given result is a Thenable (such as another Promise), the Promise will\n * be settled with the same state and result as the Thenable once it is itself\n * settled.\n *\n * If the given result is not a Thenable, the Promise will be settled (fulfilled\n * or rejected) with that result based on the given state.\n *\n * @see http://promisesaplus.com/#the_promise_resolution_procedure\n *\n * @param {goog.Promise.State_} state\n * @param {*} x The result to apply to the Promise.\n * @private\n */\ngoog.Promise.prototype.resolve_ = function(state, x) {\n  if (this.state_ != goog.Promise.State_.PENDING) {\n    return;\n  }\n\n  if (this === x) {\n    state = goog.Promise.State_.REJECTED;\n    x = new TypeError('Promise cannot resolve to itself');\n  }\n\n  this.state_ = goog.Promise.State_.BLOCKED;\n  var isThenable = goog.Promise.maybeThen_(\n      x, this.unblockAndFulfill_, this.unblockAndReject_, this);\n  if (isThenable) {\n    return;\n  }\n\n  this.result_ = x;\n  this.state_ = state;\n  // Since we can no longer be canceled, remove link to parent, so that the\n  // child promise does not keep the parent promise alive.\n  this.parent_ = null;\n  this.scheduleCallbacks_();\n\n  if (state == goog.Promise.State_.REJECTED &&\n      !(x instanceof goog.Promise.CancellationError)) {\n    goog.Promise.addUnhandledRejection_(this, x);\n  }\n};\n\n\n/**\n * Invokes the \"then\" method of an input value if that value is a Thenable. This\n * is a no-op if the value is not thenable.\n *\n * @param {?} value A potentially thenable value.\n * @param {!Function} onFulfilled\n * @param {!Function} onRejected\n * @param {?} context\n * @return {boolean} Whether the input value was thenable.\n * @private\n */\ngoog.Promise.maybeThen_ = function(value, onFulfilled, onRejected, context) {\n  if (value instanceof goog.Promise) {\n    value.thenVoid(onFulfilled, onRejected, context);\n    return true;\n  } else if (goog.Thenable.isImplementedBy(value)) {\n    value = /** @type {!goog.Thenable} */ (value);\n    value.then(onFulfilled, onRejected, context);\n    return true;\n  } else if (goog.isObject(value)) {\n    const thenable = /** @type {!Thenable} */ (value);\n    try {\n      var then = thenable.then;\n      if (goog.isFunction(then)) {\n        goog.Promise.tryThen_(thenable, then, onFulfilled, onRejected, context);\n        return true;\n      }\n    } catch (e) {\n      onRejected.call(context, e);\n      return true;\n    }\n  }\n\n  return false;\n};\n\n\n/**\n * Attempts to call the `then` method on an object in the hopes that it is\n * a Promise-compatible instance. This allows interoperation between different\n * Promise implementations, however a non-compliant object may cause a Promise\n * to hang indefinitely. If the `then` method throws an exception, the\n * dependent Promise will be rejected with the thrown value.\n *\n * @see http://promisesaplus.com/#point-70\n *\n * @param {Thenable} thenable An object with a `then` method that may be\n *     compatible with the Promise/A+ specification.\n * @param {!Function} then The `then` method of the Thenable object.\n * @param {!Function} onFulfilled\n * @param {!Function} onRejected\n * @param {*} context\n * @private\n */\ngoog.Promise.tryThen_ = function(\n    thenable, then, onFulfilled, onRejected, context) {\n\n  var called = false;\n  var resolve = function(value) {\n    if (!called) {\n      called = true;\n      onFulfilled.call(context, value);\n    }\n  };\n\n  var reject = function(reason) {\n    if (!called) {\n      called = true;\n      onRejected.call(context, reason);\n    }\n  };\n\n  try {\n    then.call(thenable, resolve, reject);\n  } catch (e) {\n    reject(e);\n  }\n};\n\n\n/**\n * Executes the pending callbacks of a settled Promise after a timeout.\n *\n * Section 2.2.4 of the Promises/A+ specification requires that Promise\n * callbacks must only be invoked from a call stack that only contains Promise\n * implementation code, which we accomplish by invoking callback execution after\n * a timeout. If `startExecution_` is called multiple times for the same\n * Promise, the callback chain will be evaluated only once. Additional callbacks\n * may be added during the evaluation phase, and will be executed in the same\n * event loop.\n *\n * All Promises added to the waiting list during the same browser event loop\n * will be executed in one batch to avoid using a separate timeout per Promise.\n *\n * @private\n */\ngoog.Promise.prototype.scheduleCallbacks_ = function() {\n  if (!this.executing_) {\n    this.executing_ = true;\n    goog.async.run(this.executeCallbacks_, this);\n  }\n};\n\n\n/**\n * @return {boolean} Whether there are any pending callbacks queued.\n * @private\n */\ngoog.Promise.prototype.hasEntry_ = function() {\n  return !!this.callbackEntries_;\n};\n\n\n/**\n * @param {goog.Promise.CallbackEntry_} entry\n * @private\n */\ngoog.Promise.prototype.queueEntry_ = function(entry) {\n  goog.asserts.assert(entry.onFulfilled != null);\n\n  if (this.callbackEntriesTail_) {\n    this.callbackEntriesTail_.next = entry;\n    this.callbackEntriesTail_ = entry;\n  } else {\n    // It the work queue was empty set the head too.\n    this.callbackEntries_ = entry;\n    this.callbackEntriesTail_ = entry;\n  }\n};\n\n\n/**\n * @return {goog.Promise.CallbackEntry_} entry\n * @private\n */\ngoog.Promise.prototype.popEntry_ = function() {\n  var entry = null;\n  if (this.callbackEntries_) {\n    entry = this.callbackEntries_;\n    this.callbackEntries_ = entry.next;\n    entry.next = null;\n  }\n  // It the work queue is empty clear the tail too.\n  if (!this.callbackEntries_) {\n    this.callbackEntriesTail_ = null;\n  }\n\n  if (entry != null) {\n    goog.asserts.assert(entry.onFulfilled != null);\n  }\n  return entry;\n};\n\n\n/**\n * @param {goog.Promise.CallbackEntry_} previous\n * @private\n */\ngoog.Promise.prototype.removeEntryAfter_ = function(previous) {\n  goog.asserts.assert(this.callbackEntries_);\n  goog.asserts.assert(previous != null);\n  // If the last entry is being removed, update the tail\n  if (previous.next == this.callbackEntriesTail_) {\n    this.callbackEntriesTail_ = previous;\n  }\n\n  previous.next = previous.next.next;\n};\n\n\n/**\n * Executes all pending callbacks for this Promise.\n *\n * @private\n */\ngoog.Promise.prototype.executeCallbacks_ = function() {\n  var entry = null;\n  while (entry = this.popEntry_()) {\n    if (goog.Promise.LONG_STACK_TRACES) {\n      this.currentStep_++;\n    }\n    this.executeCallback_(entry, this.state_, this.result_);\n  }\n  this.executing_ = false;\n};\n\n\n/**\n * Executes a pending callback for this Promise. Invokes an `onFulfilled`\n * or `onRejected` callback based on the settled state of the Promise.\n *\n * @param {!goog.Promise.CallbackEntry_} callbackEntry An entry containing the\n *     onFulfilled and/or onRejected callbacks for this step.\n * @param {goog.Promise.State_} state The resolution status of the Promise,\n *     either FULFILLED or REJECTED.\n * @param {*} result The settled result of the Promise.\n * @private\n */\ngoog.Promise.prototype.executeCallback_ = function(\n    callbackEntry, state, result) {\n  // Cancel an unhandled rejection if the then/thenVoid call had an onRejected.\n  if (state == goog.Promise.State_.REJECTED && callbackEntry.onRejected &&\n      !callbackEntry.always) {\n    this.removeUnhandledRejection_();\n  }\n\n  if (callbackEntry.child) {\n    // When the parent is settled, the child no longer needs to hold on to it,\n    // as the parent can no longer be canceled.\n    callbackEntry.child.parent_ = null;\n    goog.Promise.invokeCallback_(callbackEntry, state, result);\n  } else {\n    // Callbacks created with thenAlways or thenVoid do not have the rejection\n    // handling code normally set up in the child Promise.\n    try {\n      callbackEntry.always ?\n          callbackEntry.onFulfilled.call(callbackEntry.context) :\n          goog.Promise.invokeCallback_(callbackEntry, state, result);\n    } catch (err) {\n      goog.Promise.handleRejection_.call(null, err);\n    }\n  }\n  goog.Promise.returnEntry_(callbackEntry);\n};\n\n\n/**\n * Executes the onFulfilled or onRejected callback for a callbackEntry.\n *\n * @param {!goog.Promise.CallbackEntry_} callbackEntry\n * @param {goog.Promise.State_} state\n * @param {*} result\n * @private\n */\ngoog.Promise.invokeCallback_ = function(callbackEntry, state, result) {\n  if (state == goog.Promise.State_.FULFILLED) {\n    callbackEntry.onFulfilled.call(callbackEntry.context, result);\n  } else if (callbackEntry.onRejected) {\n    callbackEntry.onRejected.call(callbackEntry.context, result);\n  }\n};\n\n\n/**\n * Records a stack trace entry for functions that call `then` or the\n * Promise constructor. May be disabled by unsetting `LONG_STACK_TRACES`.\n *\n * @param {!Error} err An Error object created by the calling function for\n *     providing a stack trace.\n * @private\n */\ngoog.Promise.prototype.addStackTrace_ = function(err) {\n  if (goog.Promise.LONG_STACK_TRACES && typeof err.stack === 'string') {\n    // Extract the third line of the stack trace, which is the entry for the\n    // user function that called into Promise code.\n    var trace = err.stack.split('\\n', 4)[3];\n    var message = err.message;\n\n    // Pad the message to align the traces.\n    message += Array(11 - message.length).join(' ');\n    this.stack_.push(message + trace);\n  }\n};\n\n\n/**\n * Adds extra stack trace information to an exception for the list of\n * asynchronous `then` calls that have been run for this Promise. Stack\n * trace information is recorded in {@see #addStackTrace_}, and appended to\n * rethrown errors when `LONG_STACK_TRACES` is enabled.\n *\n * @param {?} err An unhandled exception captured during callback execution.\n * @private\n */\ngoog.Promise.prototype.appendLongStack_ = function(err) {\n  if (goog.Promise.LONG_STACK_TRACES && err && typeof err.stack === 'string' &&\n      this.stack_.length) {\n    var longTrace = ['Promise trace:'];\n\n    for (var promise = this; promise; promise = promise.parent_) {\n      for (var i = this.currentStep_; i >= 0; i--) {\n        longTrace.push(promise.stack_[i]);\n      }\n      longTrace.push(\n          'Value: ' +\n          '[' + (promise.state_ == goog.Promise.State_.REJECTED ? 'REJECTED' :\n                                                                  'FULFILLED') +\n          '] ' +\n          '<' + String(promise.result_) + '>');\n    }\n    err.stack += '\\n\\n' + longTrace.join('\\n');\n  }\n};\n\n\n/**\n * Marks this rejected Promise as having being handled. Also marks any parent\n * Promises in the rejected state as handled. The rejection handler will no\n * longer be invoked for this Promise (if it has not been called already).\n *\n * @private\n */\ngoog.Promise.prototype.removeUnhandledRejection_ = function() {\n  if (goog.Promise.UNHANDLED_REJECTION_DELAY > 0) {\n    for (var p = this; p && p.unhandledRejectionId_; p = p.parent_) {\n      goog.global.clearTimeout(p.unhandledRejectionId_);\n      p.unhandledRejectionId_ = 0;\n    }\n  } else if (goog.Promise.UNHANDLED_REJECTION_DELAY == 0) {\n    for (var p = this; p && p.hadUnhandledRejection_; p = p.parent_) {\n      p.hadUnhandledRejection_ = false;\n    }\n  }\n};\n\n\n/**\n * Marks this rejected Promise as unhandled. If no `onRejected` callback\n * is called for this Promise before the `UNHANDLED_REJECTION_DELAY`\n * expires, the reason will be passed to the unhandled rejection handler. The\n * handler typically rethrows the rejection reason so that it becomes visible in\n * the developer console.\n *\n * @param {!goog.Promise} promise The rejected Promise.\n * @param {*} reason The Promise rejection reason.\n * @private\n */\ngoog.Promise.addUnhandledRejection_ = function(promise, reason) {\n  if (goog.Promise.UNHANDLED_REJECTION_DELAY > 0) {\n    promise.unhandledRejectionId_ = goog.global.setTimeout(function() {\n      promise.appendLongStack_(reason);\n      goog.Promise.handleRejection_.call(null, reason);\n    }, goog.Promise.UNHANDLED_REJECTION_DELAY);\n\n  } else if (goog.Promise.UNHANDLED_REJECTION_DELAY == 0) {\n    promise.hadUnhandledRejection_ = true;\n    goog.async.run(function() {\n      if (promise.hadUnhandledRejection_) {\n        promise.appendLongStack_(reason);\n        goog.Promise.handleRejection_.call(null, reason);\n      }\n    });\n  }\n};\n\n\n/**\n * A method that is invoked with the rejection reasons for Promises that are\n * rejected but have no `onRejected` callbacks registered yet.\n * @type {function(*)}\n * @private\n */\ngoog.Promise.handleRejection_ = goog.async.throwException;\n\n\n/**\n * Sets a handler that will be called with reasons from unhandled rejected\n * Promises. If the rejected Promise (or one of its descendants) has an\n * `onRejected` callback registered, the rejection will be considered\n * handled, and the rejection handler will not be called.\n *\n * By default, unhandled rejections are rethrown so that the error may be\n * captured by the developer console or a `window.onerror` handler.\n *\n * @param {function(*)} handler A function that will be called with reasons from\n *     rejected Promises. Defaults to `goog.async.throwException`.\n */\ngoog.Promise.setUnhandledRejectionHandler = function(handler) {\n  goog.Promise.handleRejection_ = handler;\n};\n\n\n\n/**\n * Error used as a rejection reason for canceled Promises.\n *\n * @param {string=} opt_message\n * @constructor\n * @extends {goog.debug.Error}\n * @final\n */\ngoog.Promise.CancellationError = function(opt_message) {\n  goog.Promise.CancellationError.base(this, 'constructor', opt_message);\n};\ngoog.inherits(goog.Promise.CancellationError, goog.debug.Error);\n\n\n/** @override */\ngoog.Promise.CancellationError.prototype.name = 'cancel';\n\n\n\n/**\n * Internal implementation of the resolver interface.\n *\n * @param {!goog.Promise<TYPE>} promise\n * @param {function((TYPE|goog.Promise<TYPE>|Thenable)=)} resolve\n * @param {function(*=): void} reject\n * @implements {goog.promise.Resolver<TYPE>}\n * @final @struct\n * @constructor\n * @private\n * @template TYPE\n */\ngoog.Promise.Resolver_ = function(promise, resolve, reject) {\n  /** @const */\n  this.promise = promise;\n\n  /** @const */\n  this.resolve = resolve;\n\n  /** @const */\n  this.reject = reject;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Implements the disposable interface. The dispose method is used\n * to clean up references and resources.\n */\n\n\ngoog.provide('goog.Disposable');\ngoog.provide('goog.dispose');\ngoog.provide('goog.disposeAll');\n\ngoog.require('goog.disposable.IDisposable');\n\n\n\n/**\n * Class that provides the basic implementation for disposable objects. If your\n * class holds references or resources that can't be collected by standard GC,\n * it should extend this class or implement the disposable interface (defined\n * in goog.disposable.IDisposable). See description of\n * goog.disposable.IDisposable for examples of cleanup.\n * @constructor\n * @implements {goog.disposable.IDisposable}\n */\ngoog.Disposable = function() {\n  /**\n   * If monitoring the goog.Disposable instances is enabled, stores the creation\n   * stack trace of the Disposable instance.\n   * @type {string|undefined}\n   */\n  this.creationStack;\n\n  if (goog.Disposable.MONITORING_MODE != goog.Disposable.MonitoringMode.OFF) {\n    if (goog.Disposable.INCLUDE_STACK_ON_CREATION) {\n      this.creationStack = new Error().stack;\n    }\n    goog.Disposable.instances_[goog.getUid(this)] = this;\n  }\n  // Support sealing\n  this.disposed_ = this.disposed_;\n  this.onDisposeCallbacks_ = this.onDisposeCallbacks_;\n};\n\n\n/**\n * @enum {number} Different monitoring modes for Disposable.\n */\ngoog.Disposable.MonitoringMode = {\n  /**\n   * No monitoring.\n   */\n  OFF: 0,\n  /**\n   * Creating and disposing the goog.Disposable instances is monitored. All\n   * disposable objects need to call the `goog.Disposable` base\n   * constructor. The PERMANENT mode must be switched on before creating any\n   * goog.Disposable instances.\n   */\n  PERMANENT: 1,\n  /**\n   * INTERACTIVE mode can be switched on and off on the fly without producing\n   * errors. It also doesn't warn if the disposable objects don't call the\n   * `goog.Disposable` base constructor.\n   */\n  INTERACTIVE: 2\n};\n\n\n/**\n * @define {number} The monitoring mode of the goog.Disposable\n *     instances. Default is OFF. Switching on the monitoring is only\n *     recommended for debugging because it has a significant impact on\n *     performance and memory usage. If switched off, the monitoring code\n *     compiles down to 0 bytes.\n */\ngoog.Disposable.MONITORING_MODE =\n    goog.define('goog.Disposable.MONITORING_MODE', 0);\n\n\n/**\n * @define {boolean} Whether to attach creation stack to each created disposable\n *     instance; This is only relevant for when MonitoringMode != OFF.\n */\ngoog.Disposable.INCLUDE_STACK_ON_CREATION =\n    goog.define('goog.Disposable.INCLUDE_STACK_ON_CREATION', true);\n\n\n/**\n * Maps the unique ID of every undisposed `goog.Disposable` object to\n * the object itself.\n * @type {!Object<number, !goog.Disposable>}\n * @private\n */\ngoog.Disposable.instances_ = {};\n\n\n/**\n * @return {!Array<!goog.Disposable>} All `goog.Disposable` objects that\n *     haven't been disposed of.\n */\ngoog.Disposable.getUndisposedObjects = function() {\n  var ret = [];\n  for (var id in goog.Disposable.instances_) {\n    if (goog.Disposable.instances_.hasOwnProperty(id)) {\n      ret.push(goog.Disposable.instances_[Number(id)]);\n    }\n  }\n  return ret;\n};\n\n\n/**\n * Clears the registry of undisposed objects but doesn't dispose of them.\n */\ngoog.Disposable.clearUndisposedObjects = function() {\n  goog.Disposable.instances_ = {};\n};\n\n\n/**\n * Whether the object has been disposed of.\n * @type {boolean}\n * @private\n */\ngoog.Disposable.prototype.disposed_ = false;\n\n\n/**\n * Callbacks to invoke when this object is disposed.\n * @type {Array<!Function>}\n * @private\n */\ngoog.Disposable.prototype.onDisposeCallbacks_;\n\n\n/**\n * @return {boolean} Whether the object has been disposed of.\n * @override\n */\ngoog.Disposable.prototype.isDisposed = function() {\n  return this.disposed_;\n};\n\n\n/**\n * @return {boolean} Whether the object has been disposed of.\n * @deprecated Use {@link #isDisposed} instead.\n */\ngoog.Disposable.prototype.getDisposed = goog.Disposable.prototype.isDisposed;\n\n\n/**\n * Disposes of the object. If the object hasn't already been disposed of, calls\n * {@link #disposeInternal}. Classes that extend `goog.Disposable` should\n * override {@link #disposeInternal} in order to cleanup references, resources\n * and other disposable objects. Reentrant.\n *\n * @return {void} Nothing.\n * @override\n */\ngoog.Disposable.prototype.dispose = function() {\n  if (!this.disposed_) {\n    // Set disposed_ to true first, in case during the chain of disposal this\n    // gets disposed recursively.\n    this.disposed_ = true;\n    this.disposeInternal();\n    if (goog.Disposable.MONITORING_MODE != goog.Disposable.MonitoringMode.OFF) {\n      var uid = goog.getUid(this);\n      if (goog.Disposable.MONITORING_MODE ==\n              goog.Disposable.MonitoringMode.PERMANENT &&\n          !goog.Disposable.instances_.hasOwnProperty(uid)) {\n        throw new Error(\n            this + ' did not call the goog.Disposable base ' +\n            'constructor or was disposed of after a clearUndisposedObjects ' +\n            'call');\n      }\n      if (goog.Disposable.MONITORING_MODE !=\n              goog.Disposable.MonitoringMode.OFF &&\n          this.onDisposeCallbacks_ && this.onDisposeCallbacks_.length > 0) {\n        throw new Error(\n            this + ' did not empty its onDisposeCallbacks queue. This ' +\n            'probably means it overrode dispose() or disposeInternal() ' +\n            'without calling the superclass\\' method.');\n      }\n      delete goog.Disposable.instances_[uid];\n    }\n  }\n};\n\n\n/**\n * Associates a disposable object with this object so that they will be disposed\n * together.\n * @param {goog.disposable.IDisposable} disposable that will be disposed when\n *     this object is disposed.\n */\ngoog.Disposable.prototype.registerDisposable = function(disposable) {\n  this.addOnDisposeCallback(goog.partial(goog.dispose, disposable));\n};\n\n\n/**\n * Invokes a callback function when this object is disposed. Callbacks are\n * invoked in the order in which they were added. If a callback is added to\n * an already disposed Disposable, it will be called immediately.\n * @param {function(this:T):?} callback The callback function.\n * @param {T=} opt_scope An optional scope to call the callback in.\n * @template T\n */\ngoog.Disposable.prototype.addOnDisposeCallback = function(callback, opt_scope) {\n  if (this.disposed_) {\n    opt_scope !== undefined ? callback.call(opt_scope) : callback();\n    return;\n  }\n  if (!this.onDisposeCallbacks_) {\n    this.onDisposeCallbacks_ = [];\n  }\n\n  this.onDisposeCallbacks_.push(\n      opt_scope !== undefined ? goog.bind(callback, opt_scope) : callback);\n};\n\n\n/**\n * Performs appropriate cleanup. See description of goog.disposable.IDisposable\n * for examples. Classes that extend `goog.Disposable` should override this\n * method. Not reentrant. To avoid calling it twice, it must only be called from\n * the subclass' `disposeInternal` method. Everywhere else the public `dispose`\n * method must be used. For example:\n *\n * <pre>\n * mypackage.MyClass = function() {\n * mypackage.MyClass.base(this, 'constructor');\n *     // Constructor logic specific to MyClass.\n *     ...\n *   };\n *   goog.inherits(mypackage.MyClass, goog.Disposable);\n *\n *   mypackage.MyClass.prototype.disposeInternal = function() {\n *     // Dispose logic specific to MyClass.\n *     ...\n *     // Call superclass's disposeInternal at the end of the subclass's, like\n *     // in C++, to avoid hard-to-catch issues.\n *     mypackage.MyClass.base(this, 'disposeInternal');\n *   };\n * </pre>\n *\n * @protected\n */\ngoog.Disposable.prototype.disposeInternal = function() {\n  if (this.onDisposeCallbacks_) {\n    while (this.onDisposeCallbacks_.length) {\n      this.onDisposeCallbacks_.shift()();\n    }\n  }\n};\n\n\n/**\n * Returns True if we can verify the object is disposed.\n * Calls `isDisposed` on the argument if it supports it.  If obj\n * is not an object with an isDisposed() method, return false.\n * @param {*} obj The object to investigate.\n * @return {boolean} True if we can verify the object is disposed.\n */\ngoog.Disposable.isDisposed = function(obj) {\n  if (obj && typeof obj.isDisposed == 'function') {\n    return obj.isDisposed();\n  }\n  return false;\n};\n\n\n/**\n * Calls `dispose` on the argument if it supports it. If obj is not an\n *     object with a dispose() method, this is a no-op.\n * @param {*} obj The object to dispose of.\n */\ngoog.dispose = function(obj) {\n  if (obj && typeof obj.dispose == 'function') {\n    obj.dispose();\n  }\n};\n\n\n/**\n * Calls `dispose` on each member of the list that supports it. (If the\n * member is an ArrayLike, then `goog.disposeAll()` will be called\n * recursively on each of its members.) If the member is not an object with a\n * `dispose()` method, then it is ignored.\n * @param {...*} var_args The list.\n */\ngoog.disposeAll = function(var_args) {\n  for (var i = 0, len = arguments.length; i < len; ++i) {\n    var disposable = arguments[i];\n    if (goog.isArrayLike(disposable)) {\n      goog.disposeAll.apply(null, disposable);\n    } else {\n      goog.dispose(disposable);\n    }\n  }\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Logging and debugging utilities.\n *\n * @see ../demos/debug.html\n */\n\ngoog.provide('goog.debug');\n\ngoog.require('goog.array');\ngoog.require('goog.debug.errorcontext');\ngoog.require('goog.userAgent');\n\n\n/** @define {boolean} Whether logging should be enabled. */\ngoog.debug.LOGGING_ENABLED =\n    goog.define('goog.debug.LOGGING_ENABLED', goog.DEBUG);\n\n\n/** @define {boolean} Whether to force \"sloppy\" stack building. */\ngoog.debug.FORCE_SLOPPY_STACKS =\n    goog.define('goog.debug.FORCE_SLOPPY_STACKS', false);\n\n\n/**\n * @define {boolean} TODO(user): Remove this hack once bug is resolved.\n */\ngoog.debug.CHECK_FOR_THROWN_EVENT =\n    goog.define('goog.debug.CHECK_FOR_THROWN_EVENT', false);\n\n\n\n/**\n * Catches onerror events fired by windows and similar objects.\n * @param {function(Object)} logFunc The function to call with the error\n *    information.\n * @param {boolean=} opt_cancel Whether to stop the error from reaching the\n *    browser.\n * @param {Object=} opt_target Object that fires onerror events.\n * @suppress {strictMissingProperties} onerror is not defined as a property\n *    on Object.\n */\ngoog.debug.catchErrors = function(logFunc, opt_cancel, opt_target) {\n  var target = opt_target || goog.global;\n  var oldErrorHandler = target.onerror;\n  var retVal = !!opt_cancel;\n\n  // Chrome interprets onerror return value backwards (http://crbug.com/92062)\n  // until it was fixed in webkit revision r94061 (Webkit 535.3). This\n  // workaround still needs to be skipped in Safari after the webkit change\n  // gets pushed out in Safari.\n  // See https://bugs.webkit.org/show_bug.cgi?id=67119\n  if (goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('535.3')) {\n    retVal = !retVal;\n  }\n\n  /**\n   * New onerror handler for this target. This onerror handler follows the spec\n   * according to\n   * http://www.whatwg.org/specs/web-apps/current-work/#runtime-script-errors\n   * The spec was changed in August 2013 to support receiving column information\n   * and an error object for all scripts on the same origin or cross origin\n   * scripts with the proper headers. See\n   * https://mikewest.org/2013/08/debugging-runtime-errors-with-window-onerror\n   *\n   * @param {string} message The error message. For cross-origin errors, this\n   *     will be scrubbed to just \"Script error.\". For new browsers that have\n   *     updated to follow the latest spec, errors that come from origins that\n   *     have proper cross origin headers will not be scrubbed.\n   * @param {string} url The URL of the script that caused the error. The URL\n   *     will be scrubbed to \"\" for cross origin scripts unless the script has\n   *     proper cross origin headers and the browser has updated to the latest\n   *     spec.\n   * @param {number} line The line number in the script that the error\n   *     occurred on.\n   * @param {number=} opt_col The optional column number that the error\n   *     occurred on. Only browsers that have updated to the latest spec will\n   *     include this.\n   * @param {Error=} opt_error The optional actual error object for this\n   *     error that should include the stack. Only browsers that have updated\n   *     to the latest spec will inlude this parameter.\n   * @return {boolean} Whether to prevent the error from reaching the browser.\n   */\n  target.onerror = function(message, url, line, opt_col, opt_error) {\n    if (oldErrorHandler) {\n      oldErrorHandler(message, url, line, opt_col, opt_error);\n    }\n    logFunc({\n      message: message,\n      fileName: url,\n      line: line,\n      lineNumber: line,\n      col: opt_col,\n      error: opt_error\n    });\n    return retVal;\n  };\n};\n\n\n/**\n * Creates a string representing an object and all its properties.\n * @param {Object|null|undefined} obj Object to expose.\n * @param {boolean=} opt_showFn Show the functions as well as the properties,\n *     default is false.\n * @return {string} The string representation of `obj`.\n */\ngoog.debug.expose = function(obj, opt_showFn) {\n  if (typeof obj == 'undefined') {\n    return 'undefined';\n  }\n  if (obj == null) {\n    return 'NULL';\n  }\n  var str = [];\n\n  for (var x in obj) {\n    if (!opt_showFn && goog.isFunction(obj[x])) {\n      continue;\n    }\n    var s = x + ' = ';\n\n    try {\n      s += obj[x];\n    } catch (e) {\n      s += '*** ' + e + ' ***';\n    }\n    str.push(s);\n  }\n  return str.join('\\n');\n};\n\n\n/**\n * Creates a string representing a given primitive or object, and for an\n * object, all its properties and nested objects. NOTE: The output will include\n * Uids on all objects that were exposed. Any added Uids will be removed before\n * returning.\n * @param {*} obj Object to expose.\n * @param {boolean=} opt_showFn Also show properties that are functions (by\n *     default, functions are omitted).\n * @return {string} A string representation of `obj`.\n */\ngoog.debug.deepExpose = function(obj, opt_showFn) {\n  var str = [];\n\n  // Track any objects where deepExpose added a Uid, so they can be cleaned up\n  // before return. We do this globally, rather than only on ancestors so that\n  // if the same object appears in the output, you can see it.\n  var uidsToCleanup = [];\n  var ancestorUids = {};\n\n  var helper = function(obj, space) {\n    var nestspace = space + '  ';\n\n    var indentMultiline = function(str) {\n      return str.replace(/\\n/g, '\\n' + space);\n    };\n\n\n    try {\n      if (obj === undefined) {\n        str.push('undefined');\n      } else if (obj === null) {\n        str.push('NULL');\n      } else if (typeof obj === 'string') {\n        str.push('\"' + indentMultiline(obj) + '\"');\n      } else if (goog.isFunction(obj)) {\n        str.push(indentMultiline(String(obj)));\n      } else if (goog.isObject(obj)) {\n        // Add a Uid if needed. The struct calls implicitly adds them.\n        if (!goog.hasUid(obj)) {\n          uidsToCleanup.push(obj);\n        }\n        var uid = goog.getUid(obj);\n        if (ancestorUids[uid]) {\n          str.push('*** reference loop detected (id=' + uid + ') ***');\n        } else {\n          ancestorUids[uid] = true;\n          str.push('{');\n          for (var x in obj) {\n            if (!opt_showFn && goog.isFunction(obj[x])) {\n              continue;\n            }\n            str.push('\\n');\n            str.push(nestspace);\n            str.push(x + ' = ');\n            helper(obj[x], nestspace);\n          }\n          str.push('\\n' + space + '}');\n          delete ancestorUids[uid];\n        }\n      } else {\n        str.push(obj);\n      }\n    } catch (e) {\n      str.push('*** ' + e + ' ***');\n    }\n  };\n\n  helper(obj, '');\n\n  // Cleanup any Uids that were added by the deepExpose.\n  for (var i = 0; i < uidsToCleanup.length; i++) {\n    goog.removeUid(uidsToCleanup[i]);\n  }\n\n  return str.join('');\n};\n\n\n/**\n * Recursively outputs a nested array as a string.\n * @param {Array<?>} arr The array.\n * @return {string} String representing nested array.\n */\ngoog.debug.exposeArray = function(arr) {\n  var str = [];\n  for (var i = 0; i < arr.length; i++) {\n    if (Array.isArray(arr[i])) {\n      str.push(goog.debug.exposeArray(arr[i]));\n    } else {\n      str.push(arr[i]);\n    }\n  }\n  return '[ ' + str.join(', ') + ' ]';\n};\n\n\n/**\n * Normalizes the error/exception object between browsers.\n * @param {*} err Raw error object.\n * @return {{\n *    message: (?|undefined),\n *    name: (?|undefined),\n *    lineNumber: (?|undefined),\n *    fileName: (?|undefined),\n *    stack: (?|undefined)\n * }} Normalized error object.\n * @suppress {strictMissingProperties} properties not defined on err\n */\ngoog.debug.normalizeErrorObject = function(err) {\n  var href = goog.getObjectByName('window.location.href');\n  if (err == null) {\n    err = 'Unknown Error of type \"null/undefined\"';\n  }\n  if (typeof err === 'string') {\n    return {\n      'message': err,\n      'name': 'Unknown error',\n      'lineNumber': 'Not available',\n      'fileName': href,\n      'stack': 'Not available'\n    };\n  }\n\n  var lineNumber, fileName;\n  var threwError = false;\n\n  try {\n    lineNumber = err.lineNumber || err.line || 'Not available';\n  } catch (e) {\n    // Firefox 2 sometimes throws an error when accessing 'lineNumber':\n    // Message: Permission denied to get property UnnamedClass.lineNumber\n    lineNumber = 'Not available';\n    threwError = true;\n  }\n\n  try {\n    fileName = err.fileName || err.filename || err.sourceURL ||\n        // $googDebugFname may be set before a call to eval to set the filename\n        // that the eval is supposed to present.\n        goog.global['$googDebugFname'] || href;\n  } catch (e) {\n    // Firefox 2 may also throw an error when accessing 'filename'.\n    fileName = 'Not available';\n    threwError = true;\n  }\n\n  var stack = goog.debug.serializeErrorStack_(err);\n\n  // The IE Error object contains only the name and the message.\n  // The Safari Error object uses the line and sourceURL fields.\n  if (threwError || !err.lineNumber || !err.fileName || !err.stack ||\n      !err.message || !err.name) {\n    var message = err.message;\n    if (message == null) {\n      if (err.constructor && err.constructor instanceof Function) {\n        var ctorName = err.constructor.name ?\n            err.constructor.name :\n            goog.debug.getFunctionName(err.constructor);\n        message = 'Unknown Error of type \"' + ctorName + '\"';\n        // TODO(user): Remove this hack once bug is resolved.\n        if (goog.debug.CHECK_FOR_THROWN_EVENT && ctorName == 'Event') {\n          try {\n            message = message + ' with Event.type \"' + (err.type || '') + '\"';\n          } catch (e) {\n            // Just give up on getting more information out of the error object.\n          }\n        }\n      } else {\n        message = 'Unknown Error of unknown type';\n      }\n\n      // Avoid TypeError since toString could be missing from the instance\n      // (e.g. if created Object.create(null)).\n      if (typeof err.toString === 'function' &&\n          Object.prototype.toString !== err.toString) {\n        message += ': ' + err.toString();\n      }\n    }\n    return {\n      'message': message,\n      'name': err.name || 'UnknownError',\n      'lineNumber': lineNumber,\n      'fileName': fileName,\n      'stack': stack || 'Not available'\n    };\n  }\n  // Standards error object\n  // Typed !Object. Should be a subtype of the return type, but it's not.\n  err.stack = stack;\n  return /** @type {?} */ (err);\n};\n\n\n/**\n * Serialize stack by including the cause chain of the exception if it exists.\n *\n *\n * @param {*} e an exception that may have a cause\n * @param {!Object=} seen set of cause that have already been serialized\n * @return {string}\n * @private\n * @suppress {missingProperties} properties not defined on cause and e\n */\ngoog.debug.serializeErrorStack_ = function(e, seen) {\n  if (!seen) {\n    seen = {};\n  }\n  seen[goog.debug.serializeErrorAsKey_(e)] = true;\n\n  var stack = e['stack'] || '';\n\n  // Add cause if exists.\n  var cause = e.cause;\n  if (cause && !seen[goog.debug.serializeErrorAsKey_(cause)]) {\n    stack += '\\nCaused by: ';\n    // Some browsers like Chrome add the error message as the first frame of the\n    // stack, In this case we don't need to add it. Note: we don't use\n    // String.startsWith method because it might have to be polyfilled.\n    if (!cause.stack || cause.stack.indexOf(cause.toString()) != 0) {\n      stack += (typeof cause === 'string') ? cause : cause.message + '\\n';\n    }\n    stack += goog.debug.serializeErrorStack_(cause, seen);\n  }\n\n  return stack;\n};\n\n/**\n * Serialize an error to a string key.\n * @param {*} e an exception\n * @return {string}\n * @private\n */\ngoog.debug.serializeErrorAsKey_ = function(e) {\n  var keyPrefix = '';\n\n  if (typeof e.toString === 'function') {\n    keyPrefix = '' + e;\n  }\n\n  return keyPrefix + e['stack'];\n};\n\n\n/**\n * Converts an object to an Error using the object's toString if it's not\n * already an Error, adds a stacktrace if there isn't one, and optionally adds\n * an extra message.\n * @param {*} err The original thrown error, object, or string.\n * @param {string=} opt_message  optional additional message to add to the\n *     error.\n * @return {!Error} If err is an Error, it is enhanced and returned. Otherwise,\n *     it is converted to an Error which is enhanced and returned.\n */\ngoog.debug.enhanceError = function(err, opt_message) {\n  var error;\n  if (!(err instanceof Error)) {\n    error = Error(err);\n    if (Error.captureStackTrace) {\n      // Trim this function off the call stack, if we can.\n      Error.captureStackTrace(error, goog.debug.enhanceError);\n    }\n  } else {\n    error = err;\n  }\n\n  if (!error.stack) {\n    error.stack = goog.debug.getStacktrace(goog.debug.enhanceError);\n  }\n  if (opt_message) {\n    // find the first unoccupied 'messageX' property\n    var x = 0;\n    while (error['message' + x]) {\n      ++x;\n    }\n    error['message' + x] = String(opt_message);\n  }\n  return error;\n};\n\n\n/**\n * Converts an object to an Error using the object's toString if it's not\n * already an Error, adds a stacktrace if there isn't one, and optionally adds\n * context to the Error, which is reported by the closure error reporter.\n * @param {*} err The original thrown error, object, or string.\n * @param {!Object<string, string>=} opt_context Key-value context to add to the\n *     Error.\n * @return {!Error} If err is an Error, it is enhanced and returned. Otherwise,\n *     it is converted to an Error which is enhanced and returned.\n */\ngoog.debug.enhanceErrorWithContext = function(err, opt_context) {\n  var error = goog.debug.enhanceError(err);\n  if (opt_context) {\n    for (var key in opt_context) {\n      goog.debug.errorcontext.addErrorContext(error, key, opt_context[key]);\n    }\n  }\n  return error;\n};\n\n\n/**\n * Gets the current stack trace. Simple and iterative - doesn't worry about\n * catching circular references or getting the args.\n * @param {number=} opt_depth Optional maximum depth to trace back to.\n * @return {string} A string with the function names of all functions in the\n *     stack, separated by \\n.\n * @suppress {es5Strict}\n */\ngoog.debug.getStacktraceSimple = function(opt_depth) {\n  if (!goog.debug.FORCE_SLOPPY_STACKS) {\n    var stack = goog.debug.getNativeStackTrace_(goog.debug.getStacktraceSimple);\n    if (stack) {\n      return stack;\n    }\n    // NOTE: browsers that have strict mode support also have native \"stack\"\n    // properties.  Fall-through for legacy browser support.\n  }\n\n  var sb = [];\n  var fn = arguments.callee.caller;\n  var depth = 0;\n\n  while (fn && (!opt_depth || depth < opt_depth)) {\n    sb.push(goog.debug.getFunctionName(fn));\n    sb.push('()\\n');\n\n    try {\n      fn = fn.caller;\n    } catch (e) {\n      sb.push('[exception trying to get caller]\\n');\n      break;\n    }\n    depth++;\n    if (depth >= goog.debug.MAX_STACK_DEPTH) {\n      sb.push('[...long stack...]');\n      break;\n    }\n  }\n  if (opt_depth && depth >= opt_depth) {\n    sb.push('[...reached max depth limit...]');\n  } else {\n    sb.push('[end]');\n  }\n\n  return sb.join('');\n};\n\n\n/**\n * Max length of stack to try and output\n * @type {number}\n */\ngoog.debug.MAX_STACK_DEPTH = 50;\n\n\n/**\n * @param {Function} fn The function to start getting the trace from.\n * @return {?string}\n * @private\n */\ngoog.debug.getNativeStackTrace_ = function(fn) {\n  var tempErr = new Error();\n  if (Error.captureStackTrace) {\n    Error.captureStackTrace(tempErr, fn);\n    return String(tempErr.stack);\n  } else {\n    // IE10, only adds stack traces when an exception is thrown.\n    try {\n      throw tempErr;\n    } catch (e) {\n      tempErr = e;\n    }\n    var stack = tempErr.stack;\n    if (stack) {\n      return String(stack);\n    }\n  }\n  return null;\n};\n\n\n/**\n * Gets the current stack trace, either starting from the caller or starting\n * from a specified function that's currently on the call stack.\n * @param {?Function=} fn If provided, when collecting the stack trace all\n *     frames above the topmost call to this function, including that call,\n *     will be left out of the stack trace.\n * @return {string} Stack trace.\n * @suppress {es5Strict}\n */\ngoog.debug.getStacktrace = function(fn) {\n  var stack;\n  if (!goog.debug.FORCE_SLOPPY_STACKS) {\n    // Try to get the stack trace from the environment if it is available.\n    var contextFn = fn || goog.debug.getStacktrace;\n    stack = goog.debug.getNativeStackTrace_(contextFn);\n  }\n  if (!stack) {\n    // NOTE: browsers that have strict mode support also have native \"stack\"\n    // properties. This function will throw in strict mode.\n    stack = goog.debug.getStacktraceHelper_(fn || arguments.callee.caller, []);\n  }\n  return stack;\n};\n\n\n/**\n * Private helper for getStacktrace().\n * @param {?Function} fn If provided, when collecting the stack trace all\n *     frames above the topmost call to this function, including that call,\n *     will be left out of the stack trace.\n * @param {Array<!Function>} visited List of functions visited so far.\n * @return {string} Stack trace starting from function fn.\n * @suppress {es5Strict}\n * @private\n */\ngoog.debug.getStacktraceHelper_ = function(fn, visited) {\n  var sb = [];\n\n  // Circular reference, certain functions like bind seem to cause a recursive\n  // loop so we need to catch circular references\n  if (goog.array.contains(visited, fn)) {\n    sb.push('[...circular reference...]');\n\n    // Traverse the call stack until function not found or max depth is reached\n  } else if (fn && visited.length < goog.debug.MAX_STACK_DEPTH) {\n    sb.push(goog.debug.getFunctionName(fn) + '(');\n    var args = fn.arguments;\n    // Args may be null for some special functions such as host objects or eval.\n    for (var i = 0; args && i < args.length; i++) {\n      if (i > 0) {\n        sb.push(', ');\n      }\n      var argDesc;\n      var arg = args[i];\n      switch (typeof arg) {\n        case 'object':\n          argDesc = arg ? 'object' : 'null';\n          break;\n\n        case 'string':\n          argDesc = arg;\n          break;\n\n        case 'number':\n          argDesc = String(arg);\n          break;\n\n        case 'boolean':\n          argDesc = arg ? 'true' : 'false';\n          break;\n\n        case 'function':\n          argDesc = goog.debug.getFunctionName(arg);\n          argDesc = argDesc ? argDesc : '[fn]';\n          break;\n\n        case 'undefined':\n        default:\n          argDesc = typeof arg;\n          break;\n      }\n\n      if (argDesc.length > 40) {\n        argDesc = argDesc.substr(0, 40) + '...';\n      }\n      sb.push(argDesc);\n    }\n    visited.push(fn);\n    sb.push(')\\n');\n\n    try {\n      sb.push(goog.debug.getStacktraceHelper_(fn.caller, visited));\n    } catch (e) {\n      sb.push('[exception trying to get caller]\\n');\n    }\n\n  } else if (fn) {\n    sb.push('[...long stack...]');\n  } else {\n    sb.push('[end]');\n  }\n  return sb.join('');\n};\n\n\n/**\n * Gets a function name\n * @param {Function} fn Function to get name of.\n * @return {string} Function's name.\n */\ngoog.debug.getFunctionName = function(fn) {\n  if (goog.debug.fnNameCache_[fn]) {\n    return goog.debug.fnNameCache_[fn];\n  }\n\n  // Heuristically determine function name based on code.\n  var functionSource = String(fn);\n  if (!goog.debug.fnNameCache_[functionSource]) {\n    var matches = /function\\s+([^\\(]+)/m.exec(functionSource);\n    if (matches) {\n      var method = matches[1];\n      goog.debug.fnNameCache_[functionSource] = method;\n    } else {\n      goog.debug.fnNameCache_[functionSource] = '[Anonymous]';\n    }\n  }\n\n  return goog.debug.fnNameCache_[functionSource];\n};\n\n\n/**\n * Makes whitespace visible by replacing it with printable characters.\n * This is useful in finding diffrences between the expected and the actual\n * output strings of a testcase.\n * @param {string} string whose whitespace needs to be made visible.\n * @return {string} string whose whitespace is made visible.\n */\ngoog.debug.makeWhitespaceVisible = function(string) {\n  return string.replace(/ /g, '[_]')\n      .replace(/\\f/g, '[f]')\n      .replace(/\\n/g, '[n]\\n')\n      .replace(/\\r/g, '[r]')\n      .replace(/\\t/g, '[t]');\n};\n\n\n/**\n * Returns the type of a value. If a constructor is passed, and a suitable\n * string cannot be found, 'unknown type name' will be returned.\n *\n * <p>Forked rather than moved from {@link goog.asserts.getType_}\n * to avoid adding a dependency to goog.asserts.\n * @param {*} value A constructor, object, or primitive.\n * @return {string} The best display name for the value, or 'unknown type name'.\n */\ngoog.debug.runtimeType = function(value) {\n  if (value instanceof Function) {\n    return value.displayName || value.name || 'unknown type name';\n  } else if (value instanceof Object) {\n    return /** @type {string} */ (value.constructor.displayName) ||\n        value.constructor.name || Object.prototype.toString.call(value);\n  } else {\n    return value === null ? 'null' : typeof value;\n  }\n};\n\n\n/**\n * Hash map for storing function names that have already been looked up.\n * @type {Object}\n * @private\n */\ngoog.debug.fnNameCache_ = {};\n\n\n/**\n * Private internal function to support goog.debug.freeze.\n * @param {T} arg\n * @return {T}\n * @template T\n * @private\n */\ngoog.debug.freezeInternal_ = goog.DEBUG && Object.freeze || function(arg) {\n  return arg;\n};\n\n\n/**\n * Freezes the given object, but only in debug mode (and in browsers that\n * support it).  Note that this is a shallow freeze, so for deeply nested\n * objects it must be called at every level to ensure deep immutability.\n * @param {T} arg\n * @return {T}\n * @template T\n */\ngoog.debug.freeze = function(arg) {\n  // NOTE: this compiles to nothing, but hides the possible side effect of\n  // freezeInternal_ from the compiler so that the entire call can be\n  // removed if the result is not used.\n  return {\n    valueOf: function() {\n      return goog.debug.freezeInternal_(arg);\n    }\n  }.valueOf();\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Browser capability checks for the events package.\n */\n\ngoog.module('goog.events.BrowserFeature');\ngoog.module.declareLegacyNamespace();\n\nconst googUserAgent = goog.require('goog.userAgent');\n\n\n/**\n * Tricks Closure Compiler into believing that a function is pure.  The compiler\n * assumes that any `valueOf` function is pure, without analyzing its contents.\n *\n * @param {function(): T} fn\n * @return {T}\n * @template T\n */\nconst purify = (fn) => {\n  return ({valueOf: fn}).valueOf();\n};\n\n\n/**\n * Enum of browser capabilities.\n * @enum {boolean}\n */\nexports = {\n  /**\n   * Whether the button attribute of the event is W3C compliant.  False in\n   * Internet Explorer prior to version 9; document-version dependent.\n   */\n  HAS_W3C_BUTTON: !googUserAgent.IE || googUserAgent.isDocumentModeOrHigher(9),\n\n  /**\n   * Whether the browser supports full W3C event model.\n   */\n  HAS_W3C_EVENT_SUPPORT:\n      !googUserAgent.IE || googUserAgent.isDocumentModeOrHigher(9),\n\n  /**\n   * To prevent default in IE7-8 for certain keydown events we need set the\n   * keyCode to -1.\n   */\n  SET_KEY_CODE_TO_PREVENT_DEFAULT:\n      googUserAgent.IE && !googUserAgent.isVersionOrHigher('9'),\n\n  /**\n   * Whether the `navigator.onLine` property is supported.\n   */\n  HAS_NAVIGATOR_ONLINE_PROPERTY:\n      !googUserAgent.WEBKIT || googUserAgent.isVersionOrHigher('528'),\n\n  /**\n   * Whether HTML5 network online/offline events are supported.\n   */\n  HAS_HTML5_NETWORK_EVENT_SUPPORT:\n      googUserAgent.GECKO && googUserAgent.isVersionOrHigher('1.9b') ||\n      googUserAgent.IE && googUserAgent.isVersionOrHigher('8') ||\n      googUserAgent.OPERA && googUserAgent.isVersionOrHigher('9.5') ||\n      googUserAgent.WEBKIT && googUserAgent.isVersionOrHigher('528'),\n\n  /**\n   * Whether HTML5 network events fire on document.body, or otherwise the\n   * window.\n   */\n  HTML5_NETWORK_EVENTS_FIRE_ON_BODY:\n      googUserAgent.GECKO && !googUserAgent.isVersionOrHigher('8') ||\n      googUserAgent.IE && !googUserAgent.isVersionOrHigher('9'),\n\n  /**\n   * Whether touch is enabled in the browser.\n   */\n  TOUCH_ENABLED:\n      ('ontouchstart' in goog.global ||\n       !!(goog.global['document'] && document.documentElement &&\n          'ontouchstart' in document.documentElement) ||\n       // IE10 uses non-standard touch events, so it has a different check.\n       !!(goog.global['navigator'] &&\n          (goog.global['navigator']['maxTouchPoints'] ||\n           goog.global['navigator']['msMaxTouchPoints']))),\n\n  /**\n   * Whether addEventListener supports W3C standard pointer events.\n   * http://www.w3.org/TR/pointerevents/\n   */\n  POINTER_EVENTS: ('PointerEvent' in goog.global),\n\n  /**\n   * Whether addEventListener supports MSPointer events (only used in IE10).\n   * http://msdn.microsoft.com/en-us/library/ie/hh772103(v=vs.85).aspx\n   * http://msdn.microsoft.com/library/hh673557(v=vs.85).aspx\n   */\n  MSPOINTER_EVENTS:\n      ('MSPointerEvent' in goog.global &&\n       !!(goog.global['navigator'] &&\n          goog.global['navigator']['msPointerEnabled'])),\n\n  /**\n   * Whether addEventListener supports {passive: true}.\n   * https://developers.google.com/web/updates/2016/06/passive-event-listeners\n   */\n  PASSIVE_EVENTS: purify(function() {\n    // If we're in a web worker or other custom environment, we can't tell.\n    if (!goog.global.addEventListener || !Object.defineProperty) {  // IE 8\n      return false;\n    }\n\n    var passive = false;\n    var options = Object.defineProperty({}, 'passive', {\n      get: function() {\n        passive = true;\n      }\n    });\n    try {\n      goog.global.addEventListener('test', goog.nullFunction, options);\n      goog.global.removeEventListener('test', goog.nullFunction, options);\n    } catch (e) {\n    }\n\n    return passive;\n  })\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview A base class for event objects.\n */\n\n\ngoog.provide('goog.events.Event');\ngoog.provide('goog.events.EventLike');\n\n/**\n * goog.events.Event no longer depends on goog.Disposable. Keep requiring\n * goog.Disposable here to not break projects which assume this dependency.\n * @suppress {extraRequire}\n */\ngoog.require('goog.Disposable');\ngoog.require('goog.events.EventId');\n\n\n/**\n * A typedef for event like objects that are dispatchable via the\n * goog.events.dispatchEvent function. strings are treated as the type for a\n * goog.events.Event. Objects are treated as an extension of a new\n * goog.events.Event with the type property of the object being used as the type\n * of the Event.\n * @typedef {string|Object|goog.events.Event|goog.events.EventId}\n */\ngoog.events.EventLike;\n\n\n\n/**\n * A base class for event objects, so that they can support preventDefault and\n * stopPropagation.\n *\n * @param {string|!goog.events.EventId} type Event Type.\n * @param {Object=} opt_target Reference to the object that is the target of\n *     this event. It has to implement the `EventTarget` interface\n *     declared at {@link http://developer.mozilla.org/en/DOM/EventTarget}.\n * @constructor\n */\ngoog.events.Event = function(type, opt_target) {\n  /**\n   * Event type.\n   * @type {string}\n   */\n  this.type = type instanceof goog.events.EventId ? String(type) : type;\n\n  /**\n   * TODO(tbreisacher): The type should probably be\n   * EventTarget|goog.events.EventTarget.\n   *\n   * Target of the event.\n   * @type {Object|undefined}\n   */\n  this.target = opt_target;\n\n  /**\n   * Object that had the listener attached.\n   * @type {Object|undefined}\n   */\n  this.currentTarget = this.target;\n\n  /**\n   * Whether to cancel the event in internal capture/bubble processing for IE.\n   * @type {boolean}\n   * @private\n   */\n  this.propagationStopped_ = false;\n\n  /**\n   * Whether the default action has been prevented.\n   * This is a property to match the W3C specification at\n   * {@link http://www.w3.org/TR/DOM-Level-3-Events/\n   * #events-event-type-defaultPrevented}.\n   * Must be treated as read-only outside the class.\n   * @type {boolean}\n   */\n  this.defaultPrevented = false;\n};\n\n/**\n * @return {boolean} true iff internal propagation has been stopped.\n */\ngoog.events.Event.prototype.hasPropagationStopped = function() {\n  return this.propagationStopped_;\n};\n\n/**\n * Stops event propagation.\n */\ngoog.events.Event.prototype.stopPropagation = function() {\n  this.propagationStopped_ = true;\n};\n\n\n/**\n * Prevents the default action, for example a link redirecting to a url.\n */\ngoog.events.Event.prototype.preventDefault = function() {\n  this.defaultPrevented = true;\n};\n\n\n/**\n * Stops the propagation of the event. It is equivalent to\n * `e.stopPropagation()`, but can be used as the callback argument of\n * {@link goog.events.listen} without declaring another function.\n * @param {!goog.events.Event} e An event.\n */\ngoog.events.Event.stopPropagation = function(e) {\n  e.stopPropagation();\n};\n\n\n/**\n * Prevents the default action. It is equivalent to\n * `e.preventDefault()`, but can be used as the callback argument of\n * {@link goog.events.listen} without declaring another function.\n * @param {!goog.events.Event} e An event.\n */\ngoog.events.Event.preventDefault = function(e) {\n  e.preventDefault();\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview A patched, standardized event object for browser events.\n *\n * <pre>\n * The patched event object contains the following members:\n * - type           {string}    Event type, e.g. 'click'\n * - target         {Object}    The element that actually triggered the event\n * - currentTarget  {Object}    The element the listener is attached to\n * - relatedTarget  {Object}    For mouseover and mouseout, the previous object\n * - offsetX        {number}    X-coordinate relative to target\n * - offsetY        {number}    Y-coordinate relative to target\n * - clientX        {number}    X-coordinate relative to viewport\n * - clientY        {number}    Y-coordinate relative to viewport\n * - screenX        {number}    X-coordinate relative to the edge of the screen\n * - screenY        {number}    Y-coordinate relative to the edge of the screen\n * - button         {number}    Mouse button. Use isButton() to test.\n * - keyCode        {number}    Key-code\n * - ctrlKey        {boolean}   Was ctrl key depressed\n * - altKey         {boolean}   Was alt key depressed\n * - shiftKey       {boolean}   Was shift key depressed\n * - metaKey        {boolean}   Was meta key depressed\n * - pointerId      {number}    Pointer ID\n * - pointerType    {string}    Pointer type, e.g. 'mouse', 'pen', or 'touch'\n * - defaultPrevented {boolean} Whether the default action has been prevented\n * - state          {Object}    History state object\n *\n * NOTE: The keyCode member contains the raw browser keyCode. For normalized\n * key and character code use {@link goog.events.KeyHandler}.\n * </pre>\n */\n\ngoog.provide('goog.events.BrowserEvent');\ngoog.provide('goog.events.BrowserEvent.MouseButton');\ngoog.provide('goog.events.BrowserEvent.PointerType');\n\ngoog.require('goog.debug');\ngoog.require('goog.events.BrowserFeature');\ngoog.require('goog.events.Event');\ngoog.require('goog.events.EventType');\ngoog.require('goog.reflect');\ngoog.require('goog.userAgent');\n\n/**\n * @define {boolean} If true, use the layerX and layerY properties of a native\n * browser event over the offsetX and offsetY properties, which cause expensive\n * reflow. If layerX or layerY is not defined, offsetX and offsetY will be used\n * as usual.\n */\ngoog.events.USE_LAYER_XY_AS_OFFSET_XY =\n    goog.define('goog.events.USE_LAYER_XY_AS_OFFSET_XY', false);\n\n/**\n * Accepts a browser event object and creates a patched, cross browser event\n * object.\n * The content of this object will not be initialized if no event object is\n * provided. If this is the case, init() needs to be invoked separately.\n * @param {Event=} opt_e Browser event object.\n * @param {EventTarget=} opt_currentTarget Current target for event.\n * @constructor\n * @extends {goog.events.Event}\n */\ngoog.events.BrowserEvent = function(opt_e, opt_currentTarget) {\n  goog.events.BrowserEvent.base(this, 'constructor', opt_e ? opt_e.type : '');\n\n  /**\n   * Target that fired the event.\n   * @override\n   * @type {?Node}\n   */\n  this.target = null;\n\n  /**\n   * Node that had the listener attached.\n   * @override\n   * @type {?Node|undefined}\n   */\n  this.currentTarget = null;\n\n  /**\n   * For mouseover and mouseout events, the related object for the event.\n   * @type {?Node}\n   */\n  this.relatedTarget = null;\n\n  /**\n   * X-coordinate relative to target.\n   * @type {number}\n   */\n  this.offsetX = 0;\n\n  /**\n   * Y-coordinate relative to target.\n   * @type {number}\n   */\n  this.offsetY = 0;\n\n  /**\n   * X-coordinate relative to the window.\n   * @type {number}\n   */\n  this.clientX = 0;\n\n  /**\n   * Y-coordinate relative to the window.\n   * @type {number}\n   */\n  this.clientY = 0;\n\n  /**\n   * X-coordinate relative to the monitor.\n   * @type {number}\n   */\n  this.screenX = 0;\n\n  /**\n   * Y-coordinate relative to the monitor.\n   * @type {number}\n   */\n  this.screenY = 0;\n\n  /**\n   * Which mouse button was pressed.\n   * @type {number}\n   */\n  this.button = 0;\n\n  /**\n   * Key of key press.\n   * @type {string}\n   */\n  this.key = '';\n\n  /**\n   * Keycode of key press.\n   * @type {number}\n   */\n  this.keyCode = 0;\n\n  /**\n   * Keycode of key press.\n   * @type {number}\n   */\n  this.charCode = 0;\n\n  /**\n   * Whether control was pressed at time of event.\n   * @type {boolean}\n   */\n  this.ctrlKey = false;\n\n  /**\n   * Whether alt was pressed at time of event.\n   * @type {boolean}\n   */\n  this.altKey = false;\n\n  /**\n   * Whether shift was pressed at time of event.\n   * @type {boolean}\n   */\n  this.shiftKey = false;\n\n  /**\n   * Whether the meta key was pressed at time of event.\n   * @type {boolean}\n   */\n  this.metaKey = false;\n\n  /**\n   * History state object, only set for PopState events where it's a copy of the\n   * state object provided to pushState or replaceState.\n   * @type {?Object}\n   */\n  this.state = null;\n\n  /**\n   * Whether the default platform modifier key was pressed at time of event.\n   * (This is control for all platforms except Mac, where it's Meta.)\n   * @type {boolean}\n   */\n  this.platformModifierKey = false;\n\n  /**\n   * @type {number}\n   */\n  this.pointerId = 0;\n\n  /**\n   * @type {string}\n   */\n  this.pointerType = '';\n\n  /**\n   * The browser event object.\n   * @private {?Event}\n   */\n  this.event_ = null;\n\n  if (opt_e) {\n    this.init(opt_e, opt_currentTarget);\n  }\n};\ngoog.inherits(goog.events.BrowserEvent, goog.events.Event);\n\n\n/**\n * Normalized button constants for the mouse.\n * @enum {number}\n */\ngoog.events.BrowserEvent.MouseButton = {\n  LEFT: 0,\n  MIDDLE: 1,\n  RIGHT: 2\n};\n\n\n/**\n * Normalized pointer type constants for pointer events.\n * @enum {string}\n */\ngoog.events.BrowserEvent.PointerType = {\n  MOUSE: 'mouse',\n  PEN: 'pen',\n  TOUCH: 'touch'\n};\n\n\n/**\n * Static data for mapping mouse buttons.\n * @type {!Array<number>}\n * @deprecated Use `goog.events.BrowserEvent.IE_BUTTON_MAP` instead.\n */\ngoog.events.BrowserEvent.IEButtonMap = goog.debug.freeze([\n  1,  // LEFT\n  4,  // MIDDLE\n  2   // RIGHT\n]);\n\n\n/**\n * Static data for mapping mouse buttons.\n * @const {!Array<number>}\n */\ngoog.events.BrowserEvent.IE_BUTTON_MAP = goog.events.BrowserEvent.IEButtonMap;\n\n\n/**\n * Static data for mapping MSPointerEvent types to PointerEvent types.\n * @const {!Object<number, goog.events.BrowserEvent.PointerType>}\n */\ngoog.events.BrowserEvent.IE_POINTER_TYPE_MAP = goog.debug.freeze({\n  2: goog.events.BrowserEvent.PointerType.TOUCH,\n  3: goog.events.BrowserEvent.PointerType.PEN,\n  4: goog.events.BrowserEvent.PointerType.MOUSE\n});\n\n\n/**\n * Accepts a browser event object and creates a patched, cross browser event\n * object.\n * @param {Event} e Browser event object.\n * @param {EventTarget=} opt_currentTarget Current target for event.\n */\ngoog.events.BrowserEvent.prototype.init = function(e, opt_currentTarget) {\n  var type = this.type = e.type;\n\n  /**\n   * On touch devices use the first \"changed touch\" as the relevant touch.\n   * @type {?Touch}\n   */\n  var relevantTouch =\n      e.changedTouches && e.changedTouches.length ? e.changedTouches[0] : null;\n\n  // TODO(nicksantos): Change this.target to type EventTarget.\n  this.target = /** @type {Node} */ (e.target) || e.srcElement;\n\n  // TODO(nicksantos): Change this.currentTarget to type EventTarget.\n  this.currentTarget = /** @type {Node} */ (opt_currentTarget);\n\n  var relatedTarget = /** @type {Node} */ (e.relatedTarget);\n  if (relatedTarget) {\n    // There's a bug in FireFox where sometimes, relatedTarget will be a\n    // chrome element, and accessing any property of it will get a permission\n    // denied exception. See:\n    // https://bugzilla.mozilla.org/show_bug.cgi?id=497780\n    if (goog.userAgent.GECKO) {\n      if (!goog.reflect.canAccessProperty(relatedTarget, 'nodeName')) {\n        relatedTarget = null;\n      }\n    }\n  } else if (type == goog.events.EventType.MOUSEOVER) {\n    relatedTarget = e.fromElement;\n  } else if (type == goog.events.EventType.MOUSEOUT) {\n    relatedTarget = e.toElement;\n  }\n\n  this.relatedTarget = relatedTarget;\n\n  if (relevantTouch) {\n    this.clientX = relevantTouch.clientX !== undefined ? relevantTouch.clientX :\n                                                         relevantTouch.pageX;\n    this.clientY = relevantTouch.clientY !== undefined ? relevantTouch.clientY :\n                                                         relevantTouch.pageY;\n    this.screenX = relevantTouch.screenX || 0;\n    this.screenY = relevantTouch.screenY || 0;\n  } else {\n    if (goog.events.USE_LAYER_XY_AS_OFFSET_XY) {\n      this.offsetX = (e.layerX !== undefined) ? e.layerX : e.offsetX;\n      this.offsetY = (e.layerY !== undefined) ? e.layerY : e.offsetY;\n    } else {\n      // Webkit emits a lame warning whenever layerX/layerY is accessed.\n      // http://code.google.com/p/chromium/issues/detail?id=101733\n      this.offsetX = (goog.userAgent.WEBKIT || e.offsetX !== undefined) ?\n          e.offsetX :\n          e.layerX;\n      this.offsetY = (goog.userAgent.WEBKIT || e.offsetY !== undefined) ?\n          e.offsetY :\n          e.layerY;\n    }\n    this.clientX = e.clientX !== undefined ? e.clientX : e.pageX;\n    this.clientY = e.clientY !== undefined ? e.clientY : e.pageY;\n    this.screenX = e.screenX || 0;\n    this.screenY = e.screenY || 0;\n  }\n\n  this.button = e.button;\n\n  this.keyCode = e.keyCode || 0;\n  this.key = e.key || '';\n  this.charCode = e.charCode || (type == 'keypress' ? e.keyCode : 0);\n  this.ctrlKey = e.ctrlKey;\n  this.altKey = e.altKey;\n  this.shiftKey = e.shiftKey;\n  this.metaKey = e.metaKey;\n  this.platformModifierKey = goog.userAgent.MAC ? e.metaKey : e.ctrlKey;\n  this.pointerId = e.pointerId || 0;\n  this.pointerType = goog.events.BrowserEvent.getPointerType_(e);\n  this.state = e.state;\n  this.event_ = e;\n  if (e.defaultPrevented) {\n    this.preventDefault();\n  }\n};\n\n\n/**\n * Tests to see which button was pressed during the event. This is really only\n * useful in IE and Gecko browsers. And in IE, it's only useful for\n * mousedown/mouseup events, because click only fires for the left mouse button.\n *\n * Safari 2 only reports the left button being clicked, and uses the value '1'\n * instead of 0. Opera only reports a mousedown event for the middle button, and\n * no mouse events for the right button. Opera has default behavior for left and\n * middle click that can only be overridden via a configuration setting.\n *\n * There's a nice table of this mess at http://www.unixpapa.com/js/mouse.html.\n *\n * @param {goog.events.BrowserEvent.MouseButton} button The button\n *     to test for.\n * @return {boolean} True if button was pressed.\n */\ngoog.events.BrowserEvent.prototype.isButton = function(button) {\n  if (!goog.events.BrowserFeature.HAS_W3C_BUTTON) {\n    if (this.type == 'click') {\n      return button == goog.events.BrowserEvent.MouseButton.LEFT;\n    } else {\n      return !!(\n          this.event_.button & goog.events.BrowserEvent.IE_BUTTON_MAP[button]);\n    }\n  } else {\n    return this.event_.button == button;\n  }\n};\n\n\n/**\n * Whether this has an \"action\"-producing mouse button.\n *\n * By definition, this includes left-click on windows/linux, and left-click\n * without the ctrl key on Macs.\n *\n * @return {boolean} The result.\n */\ngoog.events.BrowserEvent.prototype.isMouseActionButton = function() {\n  // Ctrl+click should never behave like a left-click on mac, regardless of\n  // whether or not the browser will actually ever emit such an event.  If\n  // we see it, treat it like right-click always.\n  return this.isButton(goog.events.BrowserEvent.MouseButton.LEFT) &&\n      !(goog.userAgent.MAC && this.ctrlKey);\n};\n\n\n/**\n * @override\n */\ngoog.events.BrowserEvent.prototype.stopPropagation = function() {\n  goog.events.BrowserEvent.superClass_.stopPropagation.call(this);\n  if (this.event_.stopPropagation) {\n    this.event_.stopPropagation();\n  } else {\n    this.event_.cancelBubble = true;\n  }\n};\n\n\n/**\n * @override\n */\ngoog.events.BrowserEvent.prototype.preventDefault = function() {\n  goog.events.BrowserEvent.superClass_.preventDefault.call(this);\n  var be = this.event_;\n  if (!be.preventDefault) {\n    be.returnValue = false;\n    if (goog.events.BrowserFeature.SET_KEY_CODE_TO_PREVENT_DEFAULT) {\n\n      try {\n        // Most keys can be prevented using returnValue. Some special keys\n        // require setting the keyCode to -1 as well:\n        //\n        // In IE7:\n        // F3, F5, F10, F11, Ctrl+P, Crtl+O, Ctrl+F (these are taken from IE6)\n        //\n        // In IE8:\n        // Ctrl+P, Crtl+O, Ctrl+F (F1-F12 cannot be stopped through the event)\n        //\n        // We therefore do this for all function keys as well as when Ctrl key\n        // is pressed.\n        var VK_F1 = 112;\n        var VK_F12 = 123;\n        if (be.ctrlKey || be.keyCode >= VK_F1 && be.keyCode <= VK_F12) {\n          be.keyCode = -1;\n        }\n      } catch (ex) {\n        // IE throws an 'access denied' exception when trying to change\n        // keyCode in some situations (e.g. srcElement is input[type=file],\n        // or srcElement is an anchor tag rewritten by parent's innerHTML).\n        // Do nothing in this case.\n      }\n    }\n  } else {\n    be.preventDefault();\n  }\n};\n\n\n/**\n * @return {Event} The underlying browser event object.\n */\ngoog.events.BrowserEvent.prototype.getBrowserEvent = function() {\n  return this.event_;\n};\n\n\n/**\n * Extracts the pointer type from the given event.\n * @param {!Event} e\n * @return {string} The pointer type, e.g. 'mouse', 'pen', or 'touch'.\n * @private\n */\ngoog.events.BrowserEvent.getPointerType_ = function(e) {\n  if (typeof (e.pointerType) === 'string') {\n    return e.pointerType;\n  }\n  // IE10 uses integer codes for pointer type.\n  // https://msdn.microsoft.com/en-us/library/hh772359(v=vs.85).aspx\n  return goog.events.BrowserEvent.IE_POINTER_TYPE_MAP[e.pointerType] || '';\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Event Types.\n */\n\n\ngoog.provide('goog.events.EventType');\ngoog.provide('goog.events.MouseAsMouseEventType');\ngoog.provide('goog.events.MouseEvents');\ngoog.provide('goog.events.PointerAsMouseEventType');\ngoog.provide('goog.events.PointerAsTouchEventType');\ngoog.provide('goog.events.PointerFallbackEventType');\ngoog.provide('goog.events.PointerTouchFallbackEventType');\n\ngoog.require('goog.events.BrowserFeature');\ngoog.require('goog.userAgent');\n\n\n/**\n * Returns a prefixed event name for the current browser.\n * @param {string} eventName The name of the event.\n * @return {string} The prefixed event name.\n * @private\n */\ngoog.events.getVendorPrefixedName_ = function(eventName) {\n  return goog.userAgent.WEBKIT ?\n      'webkit' + eventName :\n      (goog.userAgent.OPERA ? 'o' + eventName.toLowerCase() :\n                              eventName.toLowerCase());\n};\n\n\n/**\n * Constants for event names.\n * @enum {string}\n */\ngoog.events.EventType = {\n  // Mouse events\n  CLICK: 'click',\n  RIGHTCLICK: 'rightclick',\n  DBLCLICK: 'dblclick',\n  AUXCLICK: 'auxclick',\n  MOUSEDOWN: 'mousedown',\n  MOUSEUP: 'mouseup',\n  MOUSEOVER: 'mouseover',\n  MOUSEOUT: 'mouseout',\n  MOUSEMOVE: 'mousemove',\n  MOUSEENTER: 'mouseenter',\n  MOUSELEAVE: 'mouseleave',\n\n  // Non-existent event; will never fire. This exists as a mouse counterpart to\n  // POINTERCANCEL.\n  MOUSECANCEL: 'mousecancel',\n\n  // Selection events.\n  // https://www.w3.org/TR/selection-api/\n  SELECTIONCHANGE: 'selectionchange',\n  SELECTSTART: 'selectstart',  // IE, Safari, Chrome\n\n  // Wheel events\n  // http://www.w3.org/TR/DOM-Level-3-Events/#events-wheelevents\n  WHEEL: 'wheel',\n\n  // Key events\n  KEYPRESS: 'keypress',\n  KEYDOWN: 'keydown',\n  KEYUP: 'keyup',\n\n  // Focus\n  BLUR: 'blur',\n  FOCUS: 'focus',\n  DEACTIVATE: 'deactivate',  // IE only\n  FOCUSIN: 'focusin',\n  FOCUSOUT: 'focusout',\n\n  // Forms\n  CHANGE: 'change',\n  RESET: 'reset',\n  SELECT: 'select',\n  SUBMIT: 'submit',\n  INPUT: 'input',\n  PROPERTYCHANGE: 'propertychange',  // IE only\n\n  // Drag and drop\n  DRAGSTART: 'dragstart',\n  DRAG: 'drag',\n  DRAGENTER: 'dragenter',\n  DRAGOVER: 'dragover',\n  DRAGLEAVE: 'dragleave',\n  DROP: 'drop',\n  DRAGEND: 'dragend',\n\n  // Touch events\n  // Note that other touch events exist, but we should follow the W3C list here.\n  // http://www.w3.org/TR/touch-events/#list-of-touchevent-types\n  TOUCHSTART: 'touchstart',\n  TOUCHMOVE: 'touchmove',\n  TOUCHEND: 'touchend',\n  TOUCHCANCEL: 'touchcancel',\n\n  // Misc\n  BEFOREUNLOAD: 'beforeunload',\n  CONSOLEMESSAGE: 'consolemessage',\n  CONTEXTMENU: 'contextmenu',\n  DEVICECHANGE: 'devicechange',\n  DEVICEMOTION: 'devicemotion',\n  DEVICEORIENTATION: 'deviceorientation',\n  DOMCONTENTLOADED: 'DOMContentLoaded',\n  ERROR: 'error',\n  HELP: 'help',\n  LOAD: 'load',\n  LOSECAPTURE: 'losecapture',\n  ORIENTATIONCHANGE: 'orientationchange',\n  READYSTATECHANGE: 'readystatechange',\n  RESIZE: 'resize',\n  SCROLL: 'scroll',\n  UNLOAD: 'unload',\n\n  // Media events\n  CANPLAY: 'canplay',\n  CANPLAYTHROUGH: 'canplaythrough',\n  DURATIONCHANGE: 'durationchange',\n  EMPTIED: 'emptied',\n  ENDED: 'ended',\n  LOADEDDATA: 'loadeddata',\n  LOADEDMETADATA: 'loadedmetadata',\n  PAUSE: 'pause',\n  PLAY: 'play',\n  PLAYING: 'playing',\n  PROGRESS: 'progress',\n  RATECHANGE: 'ratechange',\n  SEEKED: 'seeked',\n  SEEKING: 'seeking',\n  STALLED: 'stalled',\n  SUSPEND: 'suspend',\n  TIMEUPDATE: 'timeupdate',\n  VOLUMECHANGE: 'volumechange',\n  WAITING: 'waiting',\n\n  // Media Source Extensions events\n  // https://www.w3.org/TR/media-source/#mediasource-events\n  SOURCEOPEN: 'sourceopen',\n  SOURCEENDED: 'sourceended',\n  SOURCECLOSED: 'sourceclosed',\n  // https://www.w3.org/TR/media-source/#sourcebuffer-events\n  ABORT: 'abort',\n  UPDATE: 'update',\n  UPDATESTART: 'updatestart',\n  UPDATEEND: 'updateend',\n\n  // HTML 5 History events\n  // See http://www.w3.org/TR/html5/browsers.html#event-definitions-0\n  HASHCHANGE: 'hashchange',\n  PAGEHIDE: 'pagehide',\n  PAGESHOW: 'pageshow',\n  POPSTATE: 'popstate',\n\n  // Copy and Paste\n  // Support is limited. Make sure it works on your favorite browser\n  // before using.\n  // http://www.quirksmode.org/dom/events/cutcopypaste.html\n  COPY: 'copy',\n  PASTE: 'paste',\n  CUT: 'cut',\n  BEFORECOPY: 'beforecopy',\n  BEFORECUT: 'beforecut',\n  BEFOREPASTE: 'beforepaste',\n\n  // HTML5 online/offline events.\n  // http://www.w3.org/TR/offline-webapps/#related\n  ONLINE: 'online',\n  OFFLINE: 'offline',\n\n  // HTML 5 worker events\n  MESSAGE: 'message',\n  CONNECT: 'connect',\n\n  // Service Worker Events - ServiceWorkerGlobalScope context\n  // See https://w3c.github.io/ServiceWorker/#execution-context-events\n  // Note: message event defined in worker events section\n  INSTALL: 'install',\n  ACTIVATE: 'activate',\n  FETCH: 'fetch',\n  FOREIGNFETCH: 'foreignfetch',\n  MESSAGEERROR: 'messageerror',\n\n  // Service Worker Events - Document context\n  // See https://w3c.github.io/ServiceWorker/#document-context-events\n  STATECHANGE: 'statechange',\n  UPDATEFOUND: 'updatefound',\n  CONTROLLERCHANGE: 'controllerchange',\n\n  // CSS animation events.\n  ANIMATIONSTART: goog.events.getVendorPrefixedName_('AnimationStart'),\n  ANIMATIONEND: goog.events.getVendorPrefixedName_('AnimationEnd'),\n  ANIMATIONITERATION: goog.events.getVendorPrefixedName_('AnimationIteration'),\n\n  // CSS transition events. Based on the browser support described at:\n  // https://developer.mozilla.org/en/css/css_transitions#Browser_compatibility\n  TRANSITIONEND: goog.events.getVendorPrefixedName_('TransitionEnd'),\n\n  // W3C Pointer Events\n  // http://www.w3.org/TR/pointerevents/\n  POINTERDOWN: 'pointerdown',\n  POINTERUP: 'pointerup',\n  POINTERCANCEL: 'pointercancel',\n  POINTERMOVE: 'pointermove',\n  POINTEROVER: 'pointerover',\n  POINTEROUT: 'pointerout',\n  POINTERENTER: 'pointerenter',\n  POINTERLEAVE: 'pointerleave',\n  GOTPOINTERCAPTURE: 'gotpointercapture',\n  LOSTPOINTERCAPTURE: 'lostpointercapture',\n\n  // IE specific events.\n  // See http://msdn.microsoft.com/en-us/library/ie/hh772103(v=vs.85).aspx\n  // Note: these events will be supplanted in IE11.\n  MSGESTURECHANGE: 'MSGestureChange',\n  MSGESTUREEND: 'MSGestureEnd',\n  MSGESTUREHOLD: 'MSGestureHold',\n  MSGESTURESTART: 'MSGestureStart',\n  MSGESTURETAP: 'MSGestureTap',\n  MSGOTPOINTERCAPTURE: 'MSGotPointerCapture',\n  MSINERTIASTART: 'MSInertiaStart',\n  MSLOSTPOINTERCAPTURE: 'MSLostPointerCapture',\n  MSPOINTERCANCEL: 'MSPointerCancel',\n  MSPOINTERDOWN: 'MSPointerDown',\n  MSPOINTERENTER: 'MSPointerEnter',\n  MSPOINTERHOVER: 'MSPointerHover',\n  MSPOINTERLEAVE: 'MSPointerLeave',\n  MSPOINTERMOVE: 'MSPointerMove',\n  MSPOINTEROUT: 'MSPointerOut',\n  MSPOINTEROVER: 'MSPointerOver',\n  MSPOINTERUP: 'MSPointerUp',\n\n  // Native IMEs/input tools events.\n  TEXT: 'text',\n  // The textInput event is supported in IE9+, but only in lower case. All other\n  // browsers use the camel-case event name.\n  TEXTINPUT: goog.userAgent.IE ? 'textinput' : 'textInput',\n  COMPOSITIONSTART: 'compositionstart',\n  COMPOSITIONUPDATE: 'compositionupdate',\n  COMPOSITIONEND: 'compositionend',\n\n  // The beforeinput event is initially only supported in Safari. See\n  // https://bugs.chromium.org/p/chromium/issues/detail?id=342670 for Chrome\n  // implementation tracking.\n  BEFOREINPUT: 'beforeinput',\n\n  // Webview tag events\n  // See https://developer.chrome.com/apps/tags/webview\n  EXIT: 'exit',\n  LOADABORT: 'loadabort',\n  LOADCOMMIT: 'loadcommit',\n  LOADREDIRECT: 'loadredirect',\n  LOADSTART: 'loadstart',\n  LOADSTOP: 'loadstop',\n  RESPONSIVE: 'responsive',\n  SIZECHANGED: 'sizechanged',\n  UNRESPONSIVE: 'unresponsive',\n\n  // HTML5 Page Visibility API.  See details at\n  // `goog.labs.dom.PageVisibilityMonitor`.\n  VISIBILITYCHANGE: 'visibilitychange',\n\n  // LocalStorage event.\n  STORAGE: 'storage',\n\n  // DOM Level 2 mutation events (deprecated).\n  DOMSUBTREEMODIFIED: 'DOMSubtreeModified',\n  DOMNODEINSERTED: 'DOMNodeInserted',\n  DOMNODEREMOVED: 'DOMNodeRemoved',\n  DOMNODEREMOVEDFROMDOCUMENT: 'DOMNodeRemovedFromDocument',\n  DOMNODEINSERTEDINTODOCUMENT: 'DOMNodeInsertedIntoDocument',\n  DOMATTRMODIFIED: 'DOMAttrModified',\n  DOMCHARACTERDATAMODIFIED: 'DOMCharacterDataModified',\n\n  // Print events.\n  BEFOREPRINT: 'beforeprint',\n  AFTERPRINT: 'afterprint',\n\n  // Web app manifest events.\n  BEFOREINSTALLPROMPT: 'beforeinstallprompt',\n  APPINSTALLED: 'appinstalled'\n};\n\n\n/**\n * Returns one of the given pointer fallback event names in order of preference:\n *   1. pointerEventName\n *   2. msPointerEventName\n *   3. fallbackEventName\n * @param {string} pointerEventName\n * @param {string} msPointerEventName\n * @param {string} fallbackEventName\n * @return {string} The supported pointer or fallback (mouse or touch) event\n *     name.\n * @private\n */\ngoog.events.getPointerFallbackEventName_ = function(\n    pointerEventName, msPointerEventName, fallbackEventName) {\n  if (goog.events.BrowserFeature.POINTER_EVENTS) {\n    return pointerEventName;\n  }\n  if (goog.events.BrowserFeature.MSPOINTER_EVENTS) {\n    return msPointerEventName;\n  }\n  return fallbackEventName;\n};\n\n\n/**\n * Constants for pointer event names that fall back to corresponding mouse event\n * names on unsupported platforms. These are intended to be drop-in replacements\n * for corresponding values in `goog.events.EventType`.\n * @enum {string}\n */\ngoog.events.PointerFallbackEventType = {\n  POINTERDOWN: goog.events.getPointerFallbackEventName_(\n      goog.events.EventType.POINTERDOWN, goog.events.EventType.MSPOINTERDOWN,\n      goog.events.EventType.MOUSEDOWN),\n  POINTERUP: goog.events.getPointerFallbackEventName_(\n      goog.events.EventType.POINTERUP, goog.events.EventType.MSPOINTERUP,\n      goog.events.EventType.MOUSEUP),\n  POINTERCANCEL: goog.events.getPointerFallbackEventName_(\n      goog.events.EventType.POINTERCANCEL,\n      goog.events.EventType.MSPOINTERCANCEL,\n      // When falling back to mouse events, there is no MOUSECANCEL equivalent\n      // of POINTERCANCEL. In this case POINTERUP already falls back to MOUSEUP\n      // which represents both UP and CANCEL. POINTERCANCEL does not fall back\n      // to MOUSEUP to prevent listening twice on the same event.\n      goog.events.EventType.MOUSECANCEL),\n  POINTERMOVE: goog.events.getPointerFallbackEventName_(\n      goog.events.EventType.POINTERMOVE, goog.events.EventType.MSPOINTERMOVE,\n      goog.events.EventType.MOUSEMOVE),\n  POINTEROVER: goog.events.getPointerFallbackEventName_(\n      goog.events.EventType.POINTEROVER, goog.events.EventType.MSPOINTEROVER,\n      goog.events.EventType.MOUSEOVER),\n  POINTEROUT: goog.events.getPointerFallbackEventName_(\n      goog.events.EventType.POINTEROUT, goog.events.EventType.MSPOINTEROUT,\n      goog.events.EventType.MOUSEOUT),\n  POINTERENTER: goog.events.getPointerFallbackEventName_(\n      goog.events.EventType.POINTERENTER, goog.events.EventType.MSPOINTERENTER,\n      goog.events.EventType.MOUSEENTER),\n  POINTERLEAVE: goog.events.getPointerFallbackEventName_(\n      goog.events.EventType.POINTERLEAVE, goog.events.EventType.MSPOINTERLEAVE,\n      goog.events.EventType.MOUSELEAVE)\n};\n\n\n/**\n * Constants for pointer event names that fall back to corresponding touch event\n * names on unsupported platforms. These are intended to be drop-in replacements\n * for corresponding values in `goog.events.EventType`.\n * @enum {string}\n */\ngoog.events.PointerTouchFallbackEventType = {\n  POINTERDOWN: goog.events.getPointerFallbackEventName_(\n      goog.events.EventType.POINTERDOWN, goog.events.EventType.MSPOINTERDOWN,\n      goog.events.EventType.TOUCHSTART),\n  POINTERUP: goog.events.getPointerFallbackEventName_(\n      goog.events.EventType.POINTERUP, goog.events.EventType.MSPOINTERUP,\n      goog.events.EventType.TOUCHEND),\n  POINTERCANCEL: goog.events.getPointerFallbackEventName_(\n      goog.events.EventType.POINTERCANCEL,\n      goog.events.EventType.MSPOINTERCANCEL, goog.events.EventType.TOUCHCANCEL),\n  POINTERMOVE: goog.events.getPointerFallbackEventName_(\n      goog.events.EventType.POINTERMOVE, goog.events.EventType.MSPOINTERMOVE,\n      goog.events.EventType.TOUCHMOVE)\n};\n\n\n/**\n * Mapping of mouse event names to underlying browser event names.\n * @typedef {{\n *     MOUSEDOWN: string,\n *     MOUSEUP: string,\n *     MOUSECANCEL:string,\n *     MOUSEMOVE:string,\n *     MOUSEOVER:string,\n *     MOUSEOUT:string,\n *     MOUSEENTER:string,\n *     MOUSELEAVE: string,\n * }}\n */\ngoog.events.MouseEvents;\n\n\n/**\n * An alias for `goog.events.EventType.MOUSE*` event types that is overridden by\n * corresponding `POINTER*` event types.\n * @const {!goog.events.MouseEvents}\n */\ngoog.events.PointerAsMouseEventType = {\n  MOUSEDOWN: goog.events.PointerFallbackEventType.POINTERDOWN,\n  MOUSEUP: goog.events.PointerFallbackEventType.POINTERUP,\n  MOUSECANCEL: goog.events.PointerFallbackEventType.POINTERCANCEL,\n  MOUSEMOVE: goog.events.PointerFallbackEventType.POINTERMOVE,\n  MOUSEOVER: goog.events.PointerFallbackEventType.POINTEROVER,\n  MOUSEOUT: goog.events.PointerFallbackEventType.POINTEROUT,\n  MOUSEENTER: goog.events.PointerFallbackEventType.POINTERENTER,\n  MOUSELEAVE: goog.events.PointerFallbackEventType.POINTERLEAVE\n};\n\n\n/**\n * An alias for `goog.events.EventType.MOUSE*` event types that continue to use\n * mouse events.\n * @const {!goog.events.MouseEvents}\n */\ngoog.events.MouseAsMouseEventType = {\n  MOUSEDOWN: goog.events.EventType.MOUSEDOWN,\n  MOUSEUP: goog.events.EventType.MOUSEUP,\n  MOUSECANCEL: goog.events.EventType.MOUSECANCEL,\n  MOUSEMOVE: goog.events.EventType.MOUSEMOVE,\n  MOUSEOVER: goog.events.EventType.MOUSEOVER,\n  MOUSEOUT: goog.events.EventType.MOUSEOUT,\n  MOUSEENTER: goog.events.EventType.MOUSEENTER,\n  MOUSELEAVE: goog.events.EventType.MOUSELEAVE\n};\n\n\n/**\n * An alias for `goog.events.EventType.TOUCH*` event types that is overridden by\n * corresponding `POINTER*` event types.\n * @enum {string}\n */\ngoog.events.PointerAsTouchEventType = {\n  TOUCHCANCEL: goog.events.PointerTouchFallbackEventType.POINTERCANCEL,\n  TOUCHEND: goog.events.PointerTouchFallbackEventType.POINTERUP,\n  TOUCHMOVE: goog.events.PointerTouchFallbackEventType.POINTERMOVE,\n  TOUCHSTART: goog.events.PointerTouchFallbackEventType.POINTERDOWN\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview An interface for a listenable JavaScript object.\n */\n\ngoog.provide('goog.events.Listenable');\ngoog.provide('goog.events.ListenableKey');\n/** @suppress {extraRequire} */\ngoog.require('goog.events.EventId');\n\ngoog.requireType('goog.events.EventLike');\n\n\n/**\n * A listenable interface. A listenable is an object with the ability\n * to dispatch/broadcast events to \"event listeners\" registered via\n * listen/listenOnce.\n *\n * The interface allows for an event propagation mechanism similar\n * to one offered by native browser event targets, such as\n * capture/bubble mechanism, stopping propagation, and preventing\n * default actions. Capture/bubble mechanism depends on the ancestor\n * tree constructed via `#getParentEventTarget`; this tree\n * must be directed acyclic graph. The meaning of default action(s)\n * in preventDefault is specific to a particular use case.\n *\n * Implementations that do not support capture/bubble or can not have\n * a parent listenable can simply not implement any ability to set the\n * parent listenable (and have `#getParentEventTarget` return\n * null).\n *\n * Implementation of this class can be used with or independently from\n * goog.events.\n *\n * Implementation must call `#addImplementation(implClass)`.\n *\n * @interface\n * @see goog.events\n * @see http://www.w3.org/TR/DOM-Level-2-Events/events.html\n */\ngoog.events.Listenable = function() {};\n\n\n/**\n * An expando property to indicate that an object implements\n * goog.events.Listenable.\n *\n * See addImplementation/isImplementedBy.\n *\n * @type {string}\n * @const\n */\ngoog.events.Listenable.IMPLEMENTED_BY_PROP =\n    'closure_listenable_' + ((Math.random() * 1e6) | 0);\n\n\n/**\n * Marks a given class (constructor) as an implementation of\n * Listenable, so that we can query that fact at runtime. The class\n * must have already implemented the interface.\n * @param {function(new:goog.events.Listenable,...)} cls The class constructor.\n *     The corresponding class must have already implemented the interface.\n */\ngoog.events.Listenable.addImplementation = function(cls) {\n  cls.prototype[goog.events.Listenable.IMPLEMENTED_BY_PROP] = true;\n};\n\n\n/**\n * @param {Object} obj The object to check.\n * @return {boolean} Whether a given instance implements Listenable. The\n *     class/superclass of the instance must call addImplementation.\n */\ngoog.events.Listenable.isImplementedBy = function(obj) {\n  return !!(obj && obj[goog.events.Listenable.IMPLEMENTED_BY_PROP]);\n};\n\n\n/**\n * Adds an event listener. A listener can only be added once to an\n * object and if it is added again the key for the listener is\n * returned. Note that if the existing listener is a one-off listener\n * (registered via listenOnce), it will no longer be a one-off\n * listener after a call to listen().\n *\n * @param {string|!goog.events.EventId<EVENTOBJ>} type The event type id.\n * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback\n *     method.\n * @param {boolean=} opt_useCapture Whether to fire in capture phase\n *     (defaults to false).\n * @param {SCOPE=} opt_listenerScope Object in whose scope to call the\n *     listener.\n * @return {!goog.events.ListenableKey} Unique key for the listener.\n * @template SCOPE,EVENTOBJ\n */\ngoog.events.Listenable.prototype.listen;\n\n\n/**\n * Adds an event listener that is removed automatically after the\n * listener fired once.\n *\n * If an existing listener already exists, listenOnce will do\n * nothing. In particular, if the listener was previously registered\n * via listen(), listenOnce() will not turn the listener into a\n * one-off listener. Similarly, if there is already an existing\n * one-off listener, listenOnce does not modify the listeners (it is\n * still a once listener).\n *\n * @param {string|!goog.events.EventId<EVENTOBJ>} type The event type id.\n * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback\n *     method.\n * @param {boolean=} opt_useCapture Whether to fire in capture phase\n *     (defaults to false).\n * @param {SCOPE=} opt_listenerScope Object in whose scope to call the\n *     listener.\n * @return {!goog.events.ListenableKey} Unique key for the listener.\n * @template SCOPE,EVENTOBJ\n */\ngoog.events.Listenable.prototype.listenOnce;\n\n\n/**\n * Removes an event listener which was added with listen() or listenOnce().\n *\n * @param {string|!goog.events.EventId<EVENTOBJ>} type The event type id.\n * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback\n *     method.\n * @param {boolean=} opt_useCapture Whether to fire in capture phase\n *     (defaults to false).\n * @param {SCOPE=} opt_listenerScope Object in whose scope to call\n *     the listener.\n * @return {boolean} Whether any listener was removed.\n * @template SCOPE,EVENTOBJ\n */\ngoog.events.Listenable.prototype.unlisten;\n\n\n/**\n * Removes an event listener which was added with listen() by the key\n * returned by listen().\n *\n * @param {!goog.events.ListenableKey} key The key returned by\n *     listen() or listenOnce().\n * @return {boolean} Whether any listener was removed.\n */\ngoog.events.Listenable.prototype.unlistenByKey;\n\n\n/**\n * Dispatches an event (or event like object) and calls all listeners\n * listening for events of this type. The type of the event is decided by the\n * type property on the event object.\n *\n * If any of the listeners returns false OR calls preventDefault then this\n * function will return false.  If one of the capture listeners calls\n * stopPropagation, then the bubble listeners won't fire.\n *\n * @param {goog.events.EventLike} e Event object.\n * @return {boolean} If anyone called preventDefault on the event object (or\n *     if any of the listeners returns false) this will also return false.\n */\ngoog.events.Listenable.prototype.dispatchEvent;\n\n\n/**\n * Removes all listeners from this listenable. If type is specified,\n * it will only remove listeners of the particular type. otherwise all\n * registered listeners will be removed.\n *\n * @param {string=} opt_type Type of event to remove, default is to\n *     remove all types.\n * @return {number} Number of listeners removed.\n */\ngoog.events.Listenable.prototype.removeAllListeners;\n\n\n/**\n * Returns the parent of this event target to use for capture/bubble\n * mechanism.\n *\n * NOTE(chrishenry): The name reflects the original implementation of\n * custom event target (`goog.events.EventTarget`). We decided\n * that changing the name is not worth it.\n *\n * @return {goog.events.Listenable} The parent EventTarget or null if\n *     there is no parent.\n */\ngoog.events.Listenable.prototype.getParentEventTarget;\n\n\n/**\n * Fires all registered listeners in this listenable for the given\n * type and capture mode, passing them the given eventObject. This\n * does not perform actual capture/bubble. Only implementors of the\n * interface should be using this.\n *\n * @param {string|!goog.events.EventId<EVENTOBJ>} type The type of the\n *     listeners to fire.\n * @param {boolean} capture The capture mode of the listeners to fire.\n * @param {EVENTOBJ} eventObject The event object to fire.\n * @return {boolean} Whether all listeners succeeded without\n *     attempting to prevent default behavior. If any listener returns\n *     false or called goog.events.Event#preventDefault, this returns\n *     false.\n * @template EVENTOBJ\n */\ngoog.events.Listenable.prototype.fireListeners;\n\n\n/**\n * Gets all listeners in this listenable for the given type and\n * capture mode.\n *\n * @param {string|!goog.events.EventId} type The type of the listeners to fire.\n * @param {boolean} capture The capture mode of the listeners to fire.\n * @return {!Array<!goog.events.ListenableKey>} An array of registered\n *     listeners.\n * @template EVENTOBJ\n */\ngoog.events.Listenable.prototype.getListeners;\n\n\n/**\n * Gets the goog.events.ListenableKey for the event or null if no such\n * listener is in use.\n *\n * @param {string|!goog.events.EventId<EVENTOBJ>} type The name of the event\n *     without the 'on' prefix.\n * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener The\n *     listener function to get.\n * @param {boolean} capture Whether the listener is a capturing listener.\n * @param {SCOPE=} opt_listenerScope Object in whose scope to call the\n *     listener.\n * @return {goog.events.ListenableKey} the found listener or null if not found.\n * @template SCOPE,EVENTOBJ\n */\ngoog.events.Listenable.prototype.getListener;\n\n\n/**\n * Whether there is any active listeners matching the specified\n * signature. If either the type or capture parameters are\n * unspecified, the function will match on the remaining criteria.\n *\n * @param {string|!goog.events.EventId<EVENTOBJ>=} opt_type Event type.\n * @param {boolean=} opt_capture Whether to check for capture or bubble\n *     listeners.\n * @return {boolean} Whether there is any active listeners matching\n *     the requested type and/or capture phase.\n * @template EVENTOBJ\n */\ngoog.events.Listenable.prototype.hasListener;\n\n\n\n/**\n * An interface that describes a single registered listener.\n * @interface\n */\ngoog.events.ListenableKey = function() {};\n\n\n/**\n * Counter used to create a unique key\n * @type {number}\n * @private\n */\ngoog.events.ListenableKey.counter_ = 0;\n\n\n/**\n * Reserves a key to be used for ListenableKey#key field.\n * @return {number} A number to be used to fill ListenableKey#key\n *     field.\n */\ngoog.events.ListenableKey.reserveKey = function() {\n  return ++goog.events.ListenableKey.counter_;\n};\n\n\n/**\n * The source event target.\n * @type {?Object|?goog.events.Listenable}\n */\ngoog.events.ListenableKey.prototype.src;\n\n\n/**\n * The event type the listener is listening to.\n * @type {string}\n */\ngoog.events.ListenableKey.prototype.type;\n\n\n/**\n * The listener function.\n * @type {function(?):?|{handleEvent:function(?):?}|null}\n */\ngoog.events.ListenableKey.prototype.listener;\n\n\n/**\n * Whether the listener works on capture phase.\n * @type {boolean}\n */\ngoog.events.ListenableKey.prototype.capture;\n\n\n/**\n * The 'this' object for the listener function's scope.\n * @type {Object|undefined}\n */\ngoog.events.ListenableKey.prototype.handler;\n\n\n/**\n * A globally unique number to identify the key.\n * @type {number}\n */\ngoog.events.ListenableKey.prototype.key;\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Listener object.\n * @see ../demos/events.html\n */\n\ngoog.provide('goog.events.Listener');\n\ngoog.require('goog.events.ListenableKey');\ngoog.requireType('goog.events.Listenable');\n\n\n\n/**\n * Simple class that stores information about a listener\n * @param {function(?):?} listener Callback function.\n * @param {Function} proxy Wrapper for the listener that patches the event.\n * @param {EventTarget|goog.events.Listenable} src Source object for\n *     the event.\n * @param {string} type Event type.\n * @param {boolean} capture Whether in capture or bubble phase.\n * @param {Object=} opt_handler Object in whose context to execute the callback.\n * @implements {goog.events.ListenableKey}\n * @constructor\n */\ngoog.events.Listener = function(\n    listener, proxy, src, type, capture, opt_handler) {\n  if (goog.events.Listener.ENABLE_MONITORING) {\n    this.creationStack = new Error().stack;\n  }\n\n  /** @override */\n  this.listener = listener;\n\n  /**\n   * A wrapper over the original listener. This is used solely to\n   * handle native browser events (it is used to simulate the capture\n   * phase and to patch the event object).\n   * @type {Function}\n   */\n  this.proxy = proxy;\n\n  /**\n   * Object or node that callback is listening to\n   * @type {EventTarget|goog.events.Listenable}\n   */\n  this.src = src;\n\n  /**\n   * The event type.\n   * @const {string}\n   */\n  this.type = type;\n\n  /**\n   * Whether the listener is being called in the capture or bubble phase\n   * @const {boolean}\n   */\n  this.capture = !!capture;\n\n  /**\n   * Optional object whose context to execute the listener in\n   * @type {Object|undefined}\n   */\n  this.handler = opt_handler;\n\n  /**\n   * The key of the listener.\n   * @const {number}\n   * @override\n   */\n  this.key = goog.events.ListenableKey.reserveKey();\n\n  /**\n   * Whether to remove the listener after it has been called.\n   * @type {boolean}\n   */\n  this.callOnce = false;\n\n  /**\n   * Whether the listener has been removed.\n   * @type {boolean}\n   */\n  this.removed = false;\n};\n\n\n/**\n * @define {boolean} Whether to enable the monitoring of the\n *     goog.events.Listener instances. Switching on the monitoring is only\n *     recommended for debugging because it has a significant impact on\n *     performance and memory usage. If switched off, the monitoring code\n *     compiles down to 0 bytes.\n */\ngoog.events.Listener.ENABLE_MONITORING =\n    goog.define('goog.events.Listener.ENABLE_MONITORING', false);\n\n\n/**\n * If monitoring the goog.events.Listener instances is enabled, stores the\n * creation stack trace of the Disposable instance.\n * @type {string}\n */\ngoog.events.Listener.prototype.creationStack;\n\n\n/**\n * Marks this listener as removed. This also remove references held by\n * this listener object (such as listener and event source).\n */\ngoog.events.Listener.prototype.markAsRemoved = function() {\n  this.removed = true;\n  this.listener = null;\n  this.proxy = null;\n  this.src = null;\n  this.handler = null;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview A map of listeners that provides utility functions to\n * deal with listeners on an event target. Used by\n * `goog.events.EventTarget`.\n *\n * WARNING: Do not use this class from outside goog.events package.\n *\n */\n\ngoog.provide('goog.events.ListenerMap');\n\ngoog.require('goog.array');\ngoog.require('goog.events.Listener');\ngoog.require('goog.object');\ngoog.requireType('goog.events.EventId');\ngoog.requireType('goog.events.Listenable');\ngoog.requireType('goog.events.ListenableKey');\n\n\n\n/**\n * Creates a new listener map.\n * @param {EventTarget|goog.events.Listenable} src The src object.\n * @constructor\n * @final\n */\ngoog.events.ListenerMap = function(src) {\n  /** @type {EventTarget|goog.events.Listenable} */\n  this.src = src;\n\n  /**\n   * Maps of event type to an array of listeners.\n   * @type {!Object<string, !Array<!goog.events.Listener>>}\n   */\n  this.listeners = {};\n\n  /**\n   * The count of types in this map that have registered listeners.\n   * @private {number}\n   */\n  this.typeCount_ = 0;\n};\n\n\n/**\n * @return {number} The count of event types in this map that actually\n *     have registered listeners.\n */\ngoog.events.ListenerMap.prototype.getTypeCount = function() {\n  return this.typeCount_;\n};\n\n\n/**\n * @return {number} Total number of registered listeners.\n */\ngoog.events.ListenerMap.prototype.getListenerCount = function() {\n  var count = 0;\n  for (var type in this.listeners) {\n    count += this.listeners[type].length;\n  }\n  return count;\n};\n\n\n/**\n * Adds an event listener. A listener can only be added once to an\n * object and if it is added again the key for the listener is\n * returned.\n *\n * Note that a one-off listener will not change an existing listener,\n * if any. On the other hand a normal listener will change existing\n * one-off listener to become a normal listener.\n *\n * @param {string|!goog.events.EventId} type The listener event type.\n * @param {!Function} listener This listener callback method.\n * @param {boolean} callOnce Whether the listener is a one-off\n *     listener.\n * @param {boolean=} opt_useCapture The capture mode of the listener.\n * @param {Object=} opt_listenerScope Object in whose scope to call the\n *     listener.\n * @return {!goog.events.ListenableKey} Unique key for the listener.\n */\ngoog.events.ListenerMap.prototype.add = function(\n    type, listener, callOnce, opt_useCapture, opt_listenerScope) {\n  var typeStr = type.toString();\n  var listenerArray = this.listeners[typeStr];\n  if (!listenerArray) {\n    listenerArray = this.listeners[typeStr] = [];\n    this.typeCount_++;\n  }\n\n  var listenerObj;\n  var index = goog.events.ListenerMap.findListenerIndex_(\n      listenerArray, listener, opt_useCapture, opt_listenerScope);\n  if (index > -1) {\n    listenerObj = listenerArray[index];\n    if (!callOnce) {\n      // Ensure that, if there is an existing callOnce listener, it is no\n      // longer a callOnce listener.\n      listenerObj.callOnce = false;\n    }\n  } else {\n    listenerObj = new goog.events.Listener(\n        listener, null, this.src, typeStr, !!opt_useCapture, opt_listenerScope);\n    listenerObj.callOnce = callOnce;\n    listenerArray.push(listenerObj);\n  }\n  return listenerObj;\n};\n\n\n/**\n * Removes a matching listener.\n * @param {string|!goog.events.EventId} type The listener event type.\n * @param {!Function} listener This listener callback method.\n * @param {boolean=} opt_useCapture The capture mode of the listener.\n * @param {Object=} opt_listenerScope Object in whose scope to call the\n *     listener.\n * @return {boolean} Whether any listener was removed.\n */\ngoog.events.ListenerMap.prototype.remove = function(\n    type, listener, opt_useCapture, opt_listenerScope) {\n  var typeStr = type.toString();\n  if (!(typeStr in this.listeners)) {\n    return false;\n  }\n\n  var listenerArray = this.listeners[typeStr];\n  var index = goog.events.ListenerMap.findListenerIndex_(\n      listenerArray, listener, opt_useCapture, opt_listenerScope);\n  if (index > -1) {\n    var listenerObj = listenerArray[index];\n    listenerObj.markAsRemoved();\n    goog.array.removeAt(listenerArray, index);\n    if (listenerArray.length == 0) {\n      delete this.listeners[typeStr];\n      this.typeCount_--;\n    }\n    return true;\n  }\n  return false;\n};\n\n\n/**\n * Removes the given listener object.\n * @param {!goog.events.ListenableKey} listener The listener to remove.\n * @return {boolean} Whether the listener is removed.\n */\ngoog.events.ListenerMap.prototype.removeByKey = function(listener) {\n  var type = listener.type;\n  if (!(type in this.listeners)) {\n    return false;\n  }\n\n  var removed = goog.array.remove(this.listeners[type], listener);\n  if (removed) {\n    /** @type {!goog.events.Listener} */ (listener).markAsRemoved();\n    if (this.listeners[type].length == 0) {\n      delete this.listeners[type];\n      this.typeCount_--;\n    }\n  }\n  return removed;\n};\n\n\n/**\n * Removes all listeners from this map. If opt_type is provided, only\n * listeners that match the given type are removed.\n * @param {string|!goog.events.EventId=} opt_type Type of event to remove.\n * @return {number} Number of listeners removed.\n */\ngoog.events.ListenerMap.prototype.removeAll = function(opt_type) {\n  var typeStr = opt_type && opt_type.toString();\n  var count = 0;\n  for (var type in this.listeners) {\n    if (!typeStr || type == typeStr) {\n      var listenerArray = this.listeners[type];\n      for (var i = 0; i < listenerArray.length; i++) {\n        ++count;\n        listenerArray[i].markAsRemoved();\n      }\n      delete this.listeners[type];\n      this.typeCount_--;\n    }\n  }\n  return count;\n};\n\n\n/**\n * Gets all listeners that match the given type and capture mode. The\n * returned array is a copy (but the listener objects are not).\n * @param {string|!goog.events.EventId} type The type of the listeners\n *     to retrieve.\n * @param {boolean} capture The capture mode of the listeners to retrieve.\n * @return {!Array<!goog.events.ListenableKey>} An array of matching\n *     listeners.\n */\ngoog.events.ListenerMap.prototype.getListeners = function(type, capture) {\n  var listenerArray = this.listeners[type.toString()];\n  var rv = [];\n  if (listenerArray) {\n    for (var i = 0; i < listenerArray.length; ++i) {\n      var listenerObj = listenerArray[i];\n      if (listenerObj.capture == capture) {\n        rv.push(listenerObj);\n      }\n    }\n  }\n  return rv;\n};\n\n\n/**\n * Gets the goog.events.ListenableKey for the event or null if no such\n * listener is in use.\n *\n * @param {string|!goog.events.EventId} type The type of the listener\n *     to retrieve.\n * @param {!Function} listener The listener function to get.\n * @param {boolean} capture Whether the listener is a capturing listener.\n * @param {Object=} opt_listenerScope Object in whose scope to call the\n *     listener.\n * @return {goog.events.ListenableKey} the found listener or null if not found.\n */\ngoog.events.ListenerMap.prototype.getListener = function(\n    type, listener, capture, opt_listenerScope) {\n  var listenerArray = this.listeners[type.toString()];\n  var i = -1;\n  if (listenerArray) {\n    i = goog.events.ListenerMap.findListenerIndex_(\n        listenerArray, listener, capture, opt_listenerScope);\n  }\n  return i > -1 ? listenerArray[i] : null;\n};\n\n\n/**\n * Whether there is a matching listener. If either the type or capture\n * parameters are unspecified, the function will match on the\n * remaining criteria.\n *\n * @param {string|!goog.events.EventId=} opt_type The type of the listener.\n * @param {boolean=} opt_capture The capture mode of the listener.\n * @return {boolean} Whether there is an active listener matching\n *     the requested type and/or capture phase.\n */\ngoog.events.ListenerMap.prototype.hasListener = function(\n    opt_type, opt_capture) {\n  var hasType = (opt_type !== undefined);\n  var typeStr = hasType ? opt_type.toString() : '';\n  var hasCapture = (opt_capture !== undefined);\n\n  return goog.object.some(this.listeners, function(listenerArray, type) {\n    for (var i = 0; i < listenerArray.length; ++i) {\n      if ((!hasType || listenerArray[i].type == typeStr) &&\n          (!hasCapture || listenerArray[i].capture == opt_capture)) {\n        return true;\n      }\n    }\n\n    return false;\n  });\n};\n\n\n/**\n * Finds the index of a matching goog.events.Listener in the given\n * listenerArray.\n * @param {!Array<!goog.events.Listener>} listenerArray Array of listener.\n * @param {!Function} listener The listener function.\n * @param {boolean=} opt_useCapture The capture flag for the listener.\n * @param {Object=} opt_listenerScope The listener scope.\n * @return {number} The index of the matching listener within the\n *     listenerArray.\n * @private\n */\ngoog.events.ListenerMap.findListenerIndex_ = function(\n    listenerArray, listener, opt_useCapture, opt_listenerScope) {\n  for (var i = 0; i < listenerArray.length; ++i) {\n    var listenerObj = listenerArray[i];\n    if (!listenerObj.removed && listenerObj.listener == listener &&\n        listenerObj.capture == !!opt_useCapture &&\n        listenerObj.handler == opt_listenerScope) {\n      return i;\n    }\n  }\n  return -1;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview An event manager for both native browser event\n * targets and custom JavaScript event targets\n * (`goog.events.Listenable`). This provides an abstraction\n * over browsers' event systems.\n *\n * It also provides a simulation of W3C event model's capture phase in\n * Internet Explorer (IE 8 and below). Caveat: the simulation does not\n * interact well with listeners registered directly on the elements\n * (bypassing goog.events) or even with listeners registered via\n * goog.events in a separate JS binary. In these cases, we provide\n * no ordering guarantees.\n *\n * The listeners will receive a \"patched\" event object. Such event object\n * contains normalized values for certain event properties that differs in\n * different browsers.\n *\n * Example usage:\n * <pre>\n * goog.events.listen(myNode, 'click', function(e) { alert('woo') });\n * goog.events.listen(myNode, 'mouseover', mouseHandler, true);\n * goog.events.unlisten(myNode, 'mouseover', mouseHandler, true);\n * goog.events.removeAll(myNode);\n * </pre>\n *\n *                                            in IE and event object patching]\n *\n * @see ../demos/events.html\n * @see ../demos/event-propagation.html\n * @see ../demos/stopevent.html\n */\n\n// IMPLEMENTATION NOTES:\n// goog.events stores an auxiliary data structure on each EventTarget\n// source being listened on. This allows us to take advantage of GC,\n// having the data structure GC'd when the EventTarget is GC'd. This\n// GC behavior is equivalent to using W3C DOM Events directly.\n\ngoog.provide('goog.events');\ngoog.provide('goog.events.CaptureSimulationMode');\ngoog.provide('goog.events.Key');\ngoog.provide('goog.events.ListenableType');\n\ngoog.require('goog.asserts');\ngoog.require('goog.debug.entryPointRegistry');\ngoog.require('goog.events.BrowserEvent');\ngoog.require('goog.events.BrowserFeature');\ngoog.require('goog.events.Listenable');\ngoog.require('goog.events.ListenerMap');\ngoog.requireType('goog.debug.ErrorHandler');\ngoog.requireType('goog.events.EventId');\ngoog.requireType('goog.events.EventLike');\ngoog.requireType('goog.events.EventWrapper');\ngoog.requireType('goog.events.ListenableKey');\ngoog.requireType('goog.events.Listener');\n\n\n/**\n * @typedef {number|goog.events.ListenableKey}\n */\ngoog.events.Key;\n\n\n/**\n * @typedef {EventTarget|goog.events.Listenable}\n */\ngoog.events.ListenableType;\n\n\n/**\n * Property name on a native event target for the listener map\n * associated with the event target.\n * @private @const {string}\n */\ngoog.events.LISTENER_MAP_PROP_ = 'closure_lm_' + ((Math.random() * 1e6) | 0);\n\n\n/**\n * String used to prepend to IE event types.\n * @const\n * @private\n */\ngoog.events.onString_ = 'on';\n\n\n/**\n * Map of computed \"on<eventname>\" strings for IE event types. Caching\n * this removes an extra object allocation in goog.events.listen which\n * improves IE6 performance.\n * @const\n * @dict\n * @private\n */\ngoog.events.onStringMap_ = {};\n\n\n/**\n * @enum {number} Different capture simulation mode for IE8-.\n */\ngoog.events.CaptureSimulationMode = {\n  /**\n   * Does not perform capture simulation. Will asserts in IE8- when you\n   * add capture listeners.\n   */\n  OFF_AND_FAIL: 0,\n\n  /**\n   * Does not perform capture simulation, silently ignore capture\n   * listeners.\n   */\n  OFF_AND_SILENT: 1,\n\n  /**\n   * Performs capture simulation.\n   */\n  ON: 2\n};\n\n\n/**\n * @define {number} The capture simulation mode for IE8-. By default,\n *     this is ON.\n */\ngoog.events.CAPTURE_SIMULATION_MODE =\n    goog.define('goog.events.CAPTURE_SIMULATION_MODE', 2);\n\n\n/**\n * Estimated count of total native listeners.\n * @private {number}\n */\ngoog.events.listenerCountEstimate_ = 0;\n\n\n/**\n * Adds an event listener for a specific event on a native event\n * target (such as a DOM element) or an object that has implemented\n * {@link goog.events.Listenable}. A listener can only be added once\n * to an object and if it is added again the key for the listener is\n * returned. Note that if the existing listener is a one-off listener\n * (registered via listenOnce), it will no longer be a one-off\n * listener after a call to listen().\n *\n * @param {EventTarget|goog.events.Listenable} src The node to listen\n *     to events on.\n * @param {string|Array<string>|\n *     !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}\n *     type Event type or array of event types.\n * @param {function(this:T, EVENTOBJ):?|{handleEvent:function(?):?}|null}\n *     listener Callback method, or an object with a handleEvent function.\n *     WARNING: passing an Object is now softly deprecated.\n * @param {(boolean|!AddEventListenerOptions)=} opt_options\n * @param {T=} opt_handler Element in whose scope to call the listener.\n * @return {goog.events.Key} Unique key for the listener.\n * @template T,EVENTOBJ\n */\ngoog.events.listen = function(src, type, listener, opt_options, opt_handler) {\n  if (opt_options && opt_options.once) {\n    return goog.events.listenOnce(\n        src, type, listener, opt_options, opt_handler);\n  }\n  if (Array.isArray(type)) {\n    for (var i = 0; i < type.length; i++) {\n      goog.events.listen(src, type[i], listener, opt_options, opt_handler);\n    }\n    return null;\n  }\n\n  listener = goog.events.wrapListener(listener);\n  if (goog.events.Listenable.isImplementedBy(src)) {\n    var capture =\n        goog.isObject(opt_options) ? !!opt_options.capture : !!opt_options;\n    return src.listen(\n        /** @type {string|!goog.events.EventId} */ (type), listener, capture,\n        opt_handler);\n  } else {\n    return goog.events.listen_(\n        /** @type {!EventTarget} */ (src), type, listener,\n        /* callOnce */ false, opt_options, opt_handler);\n  }\n};\n\n\n/**\n * Adds an event listener for a specific event on a native event\n * target. A listener can only be added once to an object and if it\n * is added again the key for the listener is returned.\n *\n * Note that a one-off listener will not change an existing listener,\n * if any. On the other hand a normal listener will change existing\n * one-off listener to become a normal listener.\n *\n * @param {EventTarget} src The node to listen to events on.\n * @param {string|?goog.events.EventId<EVENTOBJ>} type Event type.\n * @param {!Function} listener Callback function.\n * @param {boolean} callOnce Whether the listener is a one-off\n *     listener or otherwise.\n * @param {(boolean|!AddEventListenerOptions)=} opt_options\n * @param {Object=} opt_handler Element in whose scope to call the listener.\n * @return {goog.events.ListenableKey} Unique key for the listener.\n * @template EVENTOBJ\n * @private\n */\ngoog.events.listen_ = function(\n    src, type, listener, callOnce, opt_options, opt_handler) {\n  if (!type) {\n    throw new Error('Invalid event type');\n  }\n\n  var capture =\n      goog.isObject(opt_options) ? !!opt_options.capture : !!opt_options;\n  if (capture && !goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT) {\n    if (goog.events.CAPTURE_SIMULATION_MODE ==\n        goog.events.CaptureSimulationMode.OFF_AND_FAIL) {\n      goog.asserts.fail('Can not register capture listener in IE8-.');\n      return null;\n    } else if (\n        goog.events.CAPTURE_SIMULATION_MODE ==\n        goog.events.CaptureSimulationMode.OFF_AND_SILENT) {\n      return null;\n    }\n  }\n\n  var listenerMap = goog.events.getListenerMap_(src);\n  if (!listenerMap) {\n    src[goog.events.LISTENER_MAP_PROP_] = listenerMap =\n        new goog.events.ListenerMap(src);\n  }\n\n  var listenerObj = /** @type {goog.events.Listener} */ (\n      listenerMap.add(type, listener, callOnce, capture, opt_handler));\n\n  // If the listenerObj already has a proxy, it has been set up\n  // previously. We simply return.\n  if (listenerObj.proxy) {\n    return listenerObj;\n  }\n\n  var proxy = goog.events.getProxy();\n  listenerObj.proxy = proxy;\n\n  proxy.src = src;\n  proxy.listener = listenerObj;\n\n  // Attach the proxy through the browser's API\n  if (src.addEventListener) {\n    // Don't pass an object as `capture` if the browser doesn't support that.\n    if (!goog.events.BrowserFeature.PASSIVE_EVENTS) {\n      opt_options = capture;\n    }\n    // Don't break tests that expect a boolean.\n    if (opt_options === undefined) opt_options = false;\n    src.addEventListener(type.toString(), proxy, opt_options);\n  } else if (src.attachEvent) {\n    // The else if above used to be an unconditional else. It would call\n    // attachEvent come gws or high water. This would sometimes throw an\n    // exception on IE11, spoiling the day of some callers. The previous\n    // incarnation of this code, from 2007, indicates that it replaced an\n    // earlier still version that caused excess allocations on IE6.\n    src.attachEvent(goog.events.getOnString_(type.toString()), proxy);\n  } else if (src.addListener && src.removeListener) {\n    // In IE, MediaQueryList uses addListener() insteadd of addEventListener. In\n    // Safari, there is no global for the MediaQueryList constructor, so we just\n    // check whether the object \"looks like\" MediaQueryList.\n    goog.asserts.assert(\n        type === 'change', 'MediaQueryList only has a change event');\n    src.addListener(proxy);\n  } else {\n    throw new Error('addEventListener and attachEvent are unavailable.');\n  }\n\n  goog.events.listenerCountEstimate_++;\n  return listenerObj;\n};\n\n\n/**\n * Helper function for returning a proxy function.\n * @return {!Function} A new or reused function object.\n */\ngoog.events.getProxy = function() {\n  var proxyCallbackFunction = goog.events.handleBrowserEvent_;\n  // Use a local var f to prevent one allocation.\n  var f =\n      goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT ? function(eventObject) {\n        return proxyCallbackFunction.call(f.src, f.listener, eventObject);\n      } : function(eventObject) {\n        var v = proxyCallbackFunction.call(f.src, f.listener, eventObject);\n        // NOTE(chrishenry): In IE, we hack in a capture phase. However, if\n        // there is inline event handler which tries to prevent default (for\n        // example <a href=\"...\" onclick=\"return false\">...</a>) in a\n        // descendant element, the prevent default will be overridden\n        // by this listener if this listener were to return true. Hence, we\n        // return undefined.\n        if (!v) return v;\n      };\n  return f;\n};\n\n\n/**\n * Adds an event listener for a specific event on a native event\n * target (such as a DOM element) or an object that has implemented\n * {@link goog.events.Listenable}. After the event has fired the event\n * listener is removed from the target.\n *\n * If an existing listener already exists, listenOnce will do\n * nothing. In particular, if the listener was previously registered\n * via listen(), listenOnce() will not turn the listener into a\n * one-off listener. Similarly, if there is already an existing\n * one-off listener, listenOnce does not modify the listeners (it is\n * still a once listener).\n *\n * @param {EventTarget|goog.events.Listenable} src The node to listen\n *     to events on.\n * @param {string|Array<string>|\n *     !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}\n *     type Event type or array of event types.\n * @param {function(this:T, EVENTOBJ):?|{handleEvent:function(?):?}|null}\n *     listener Callback method.\n * @param {(boolean|!AddEventListenerOptions)=} opt_options\n * @param {T=} opt_handler Element in whose scope to call the listener.\n * @return {goog.events.Key} Unique key for the listener.\n * @template T,EVENTOBJ\n */\ngoog.events.listenOnce = function(\n    src, type, listener, opt_options, opt_handler) {\n  if (Array.isArray(type)) {\n    for (var i = 0; i < type.length; i++) {\n      goog.events.listenOnce(src, type[i], listener, opt_options, opt_handler);\n    }\n    return null;\n  }\n\n  listener = goog.events.wrapListener(listener);\n  if (goog.events.Listenable.isImplementedBy(src)) {\n    var capture =\n        goog.isObject(opt_options) ? !!opt_options.capture : !!opt_options;\n    return src.listenOnce(\n        /** @type {string|!goog.events.EventId} */ (type), listener, capture,\n        opt_handler);\n  } else {\n    return goog.events.listen_(\n        /** @type {!EventTarget} */ (src), type, listener,\n        /* callOnce */ true, opt_options, opt_handler);\n  }\n};\n\n\n/**\n * Adds an event listener with a specific event wrapper on a DOM Node or an\n * object that has implemented {@link goog.events.Listenable}. A listener can\n * only be added once to an object.\n *\n * @param {EventTarget|goog.events.Listenable} src The target to\n *     listen to events on.\n * @param {goog.events.EventWrapper} wrapper Event wrapper to use.\n * @param {function(this:T, ?):?|{handleEvent:function(?):?}|null} listener\n *     Callback method, or an object with a handleEvent function.\n * @param {boolean=} opt_capt Whether to fire in capture phase (defaults to\n *     false).\n * @param {T=} opt_handler Element in whose scope to call the listener.\n * @template T\n */\ngoog.events.listenWithWrapper = function(\n    src, wrapper, listener, opt_capt, opt_handler) {\n  wrapper.listen(src, listener, opt_capt, opt_handler);\n};\n\n\n/**\n * Removes an event listener which was added with listen().\n *\n * @param {EventTarget|goog.events.Listenable} src The target to stop\n *     listening to events on.\n * @param {string|Array<string>|\n *     !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}\n *     type Event type or array of event types to unlisten to.\n * @param {function(?):?|{handleEvent:function(?):?}|null} listener The\n *     listener function to remove.\n * @param {(boolean|!EventListenerOptions)=} opt_options\n *     whether the listener is fired during the capture or bubble phase of the\n *     event.\n * @param {Object=} opt_handler Element in whose scope to call the listener.\n * @return {?boolean} indicating whether the listener was there to remove.\n * @template EVENTOBJ\n */\ngoog.events.unlisten = function(src, type, listener, opt_options, opt_handler) {\n  if (Array.isArray(type)) {\n    for (var i = 0; i < type.length; i++) {\n      goog.events.unlisten(src, type[i], listener, opt_options, opt_handler);\n    }\n    return null;\n  }\n  var capture =\n      goog.isObject(opt_options) ? !!opt_options.capture : !!opt_options;\n\n  listener = goog.events.wrapListener(listener);\n  if (goog.events.Listenable.isImplementedBy(src)) {\n    return src.unlisten(\n        /** @type {string|!goog.events.EventId} */ (type), listener, capture,\n        opt_handler);\n  }\n\n  if (!src) {\n    // TODO(chrishenry): We should tighten the API to only accept\n    // non-null objects, or add an assertion here.\n    return false;\n  }\n\n  var listenerMap = goog.events.getListenerMap_(\n      /** @type {!EventTarget} */ (src));\n  if (listenerMap) {\n    var listenerObj = listenerMap.getListener(\n        /** @type {string|!goog.events.EventId} */ (type), listener, capture,\n        opt_handler);\n    if (listenerObj) {\n      return goog.events.unlistenByKey(listenerObj);\n    }\n  }\n\n  return false;\n};\n\n\n/**\n * Removes an event listener which was added with listen() by the key\n * returned by listen().\n *\n * @param {goog.events.Key} key The key returned by listen() for this\n *     event listener.\n * @return {boolean} indicating whether the listener was there to remove.\n */\ngoog.events.unlistenByKey = function(key) {\n  // TODO(chrishenry): Remove this check when tests that rely on this\n  // are fixed.\n  if (typeof key === 'number') {\n    return false;\n  }\n\n  var listener = key;\n  if (!listener || listener.removed) {\n    return false;\n  }\n\n  var src = listener.src;\n  if (goog.events.Listenable.isImplementedBy(src)) {\n    return /** @type {!goog.events.Listenable} */ (src).unlistenByKey(listener);\n  }\n\n  var type = listener.type;\n  var proxy = listener.proxy;\n  if (src.removeEventListener) {\n    src.removeEventListener(type, proxy, listener.capture);\n  } else if (src.detachEvent) {\n    src.detachEvent(goog.events.getOnString_(type), proxy);\n  } else if (src.addListener && src.removeListener) {\n    src.removeListener(proxy);\n  }\n  goog.events.listenerCountEstimate_--;\n\n  var listenerMap = goog.events.getListenerMap_(\n      /** @type {!EventTarget} */ (src));\n  // TODO(chrishenry): Try to remove this conditional and execute the\n  // first branch always. This should be safe.\n  if (listenerMap) {\n    listenerMap.removeByKey(listener);\n    if (listenerMap.getTypeCount() == 0) {\n      // Null the src, just because this is simple to do (and useful\n      // for IE <= 7).\n      listenerMap.src = null;\n      // We don't use delete here because IE does not allow delete\n      // on a window object.\n      src[goog.events.LISTENER_MAP_PROP_] = null;\n    }\n  } else {\n    /** @type {!goog.events.Listener} */ (listener).markAsRemoved();\n  }\n\n  return true;\n};\n\n\n/**\n * Removes an event listener which was added with listenWithWrapper().\n *\n * @param {EventTarget|goog.events.Listenable} src The target to stop\n *     listening to events on.\n * @param {goog.events.EventWrapper} wrapper Event wrapper to use.\n * @param {function(?):?|{handleEvent:function(?):?}|null} listener The\n *     listener function to remove.\n * @param {boolean=} opt_capt In DOM-compliant browsers, this determines\n *     whether the listener is fired during the capture or bubble phase of the\n *     event.\n * @param {Object=} opt_handler Element in whose scope to call the listener.\n */\ngoog.events.unlistenWithWrapper = function(\n    src, wrapper, listener, opt_capt, opt_handler) {\n  wrapper.unlisten(src, listener, opt_capt, opt_handler);\n};\n\n\n/**\n * Removes all listeners from an object. You can also optionally\n * remove listeners of a particular type.\n *\n * @param {Object|undefined} obj Object to remove listeners from. Must be an\n *     EventTarget or a goog.events.Listenable.\n * @param {string|!goog.events.EventId=} opt_type Type of event to remove.\n *     Default is all types.\n * @return {number} Number of listeners removed.\n */\ngoog.events.removeAll = function(obj, opt_type) {\n  // TODO(chrishenry): Change the type of obj to\n  // (!EventTarget|!goog.events.Listenable).\n\n  if (!obj) {\n    return 0;\n  }\n\n  if (goog.events.Listenable.isImplementedBy(obj)) {\n    return /** @type {?} */ (obj).removeAllListeners(opt_type);\n  }\n\n  var listenerMap = goog.events.getListenerMap_(\n      /** @type {!EventTarget} */ (obj));\n  if (!listenerMap) {\n    return 0;\n  }\n\n  var count = 0;\n  var typeStr = opt_type && opt_type.toString();\n  for (var type in listenerMap.listeners) {\n    if (!typeStr || type == typeStr) {\n      // Clone so that we don't need to worry about unlistenByKey\n      // changing the content of the ListenerMap.\n      var listeners = listenerMap.listeners[type].concat();\n      for (var i = 0; i < listeners.length; ++i) {\n        if (goog.events.unlistenByKey(listeners[i])) {\n          ++count;\n        }\n      }\n    }\n  }\n  return count;\n};\n\n\n/**\n * Gets the listeners for a given object, type and capture phase.\n *\n * @param {Object} obj Object to get listeners for.\n * @param {string|!goog.events.EventId} type Event type.\n * @param {boolean} capture Capture phase?.\n * @return {!Array<!goog.events.Listener>} Array of listener objects.\n */\ngoog.events.getListeners = function(obj, type, capture) {\n  if (goog.events.Listenable.isImplementedBy(obj)) {\n    return /** @type {!goog.events.Listenable} */ (obj).getListeners(\n        type, capture);\n  } else {\n    if (!obj) {\n      // TODO(chrishenry): We should tighten the API to accept\n      // !EventTarget|goog.events.Listenable, and add an assertion here.\n      return [];\n    }\n\n    var listenerMap = goog.events.getListenerMap_(\n        /** @type {!EventTarget} */ (obj));\n    return listenerMap ? listenerMap.getListeners(type, capture) : [];\n  }\n};\n\n\n/**\n * Gets the goog.events.Listener for the event or null if no such listener is\n * in use.\n *\n * @param {EventTarget|goog.events.Listenable} src The target from\n *     which to get listeners.\n * @param {?string|!goog.events.EventId<EVENTOBJ>} type The type of the event.\n * @param {function(EVENTOBJ):?|{handleEvent:function(?):?}|null} listener The\n *     listener function to get.\n * @param {boolean=} opt_capt In DOM-compliant browsers, this determines\n *                            whether the listener is fired during the\n *                            capture or bubble phase of the event.\n * @param {Object=} opt_handler Element in whose scope to call the listener.\n * @return {goog.events.ListenableKey} the found listener or null if not found.\n * @template EVENTOBJ\n */\ngoog.events.getListener = function(src, type, listener, opt_capt, opt_handler) {\n  // TODO(chrishenry): Change type from ?string to string, or add assertion.\n  type = /** @type {string} */ (type);\n  listener = goog.events.wrapListener(listener);\n  var capture = !!opt_capt;\n  if (goog.events.Listenable.isImplementedBy(src)) {\n    return src.getListener(type, listener, capture, opt_handler);\n  }\n\n  if (!src) {\n    // TODO(chrishenry): We should tighten the API to only accept\n    // non-null objects, or add an assertion here.\n    return null;\n  }\n\n  var listenerMap = goog.events.getListenerMap_(\n      /** @type {!EventTarget} */ (src));\n  if (listenerMap) {\n    return listenerMap.getListener(type, listener, capture, opt_handler);\n  }\n  return null;\n};\n\n\n/**\n * Returns whether an event target has any active listeners matching the\n * specified signature. If either the type or capture parameters are\n * unspecified, the function will match on the remaining criteria.\n *\n * @param {EventTarget|goog.events.Listenable} obj Target to get\n *     listeners for.\n * @param {string|!goog.events.EventId=} opt_type Event type.\n * @param {boolean=} opt_capture Whether to check for capture or bubble-phase\n *     listeners.\n * @return {boolean} Whether an event target has one or more listeners matching\n *     the requested type and/or capture phase.\n */\ngoog.events.hasListener = function(obj, opt_type, opt_capture) {\n  if (goog.events.Listenable.isImplementedBy(obj)) {\n    return obj.hasListener(opt_type, opt_capture);\n  }\n\n  var listenerMap = goog.events.getListenerMap_(\n      /** @type {!EventTarget} */ (obj));\n  return !!listenerMap && listenerMap.hasListener(opt_type, opt_capture);\n};\n\n\n/**\n * Provides a nice string showing the normalized event objects public members\n * @param {Object} e Event Object.\n * @return {string} String of the public members of the normalized event object.\n */\ngoog.events.expose = function(e) {\n  var str = [];\n  for (var key in e) {\n    if (e[key] && e[key].id) {\n      str.push(key + ' = ' + e[key] + ' (' + e[key].id + ')');\n    } else {\n      str.push(key + ' = ' + e[key]);\n    }\n  }\n  return str.join('\\n');\n};\n\n\n/**\n * Returns a string with on prepended to the specified type. This is used for IE\n * which expects \"on\" to be prepended. This function caches the string in order\n * to avoid extra allocations in steady state.\n * @param {string} type Event type.\n * @return {string} The type string with 'on' prepended.\n * @private\n */\ngoog.events.getOnString_ = function(type) {\n  if (type in goog.events.onStringMap_) {\n    return goog.events.onStringMap_[type];\n  }\n  return goog.events.onStringMap_[type] = goog.events.onString_ + type;\n};\n\n\n/**\n * Fires an object's listeners of a particular type and phase\n *\n * @param {Object} obj Object whose listeners to call.\n * @param {string|!goog.events.EventId} type Event type.\n * @param {boolean} capture Which event phase.\n * @param {Object} eventObject Event object to be passed to listener.\n * @return {boolean} True if all listeners returned true else false.\n */\ngoog.events.fireListeners = function(obj, type, capture, eventObject) {\n  if (goog.events.Listenable.isImplementedBy(obj)) {\n    return /** @type {!goog.events.Listenable} */ (obj).fireListeners(\n        type, capture, eventObject);\n  }\n\n  return goog.events.fireListeners_(obj, type, capture, eventObject);\n};\n\n\n/**\n * Fires an object's listeners of a particular type and phase.\n * @param {Object} obj Object whose listeners to call.\n * @param {string|!goog.events.EventId} type Event type.\n * @param {boolean} capture Which event phase.\n * @param {Object} eventObject Event object to be passed to listener.\n * @return {boolean} True if all listeners returned true else false.\n * @private\n */\ngoog.events.fireListeners_ = function(obj, type, capture, eventObject) {\n  /** @type {boolean} */\n  var retval = true;\n\n  var listenerMap = goog.events.getListenerMap_(\n      /** @type {EventTarget} */ (obj));\n  if (listenerMap) {\n    // TODO(chrishenry): Original code avoids array creation when there\n    // is no listener, so we do the same. If this optimization turns\n    // out to be not required, we can replace this with\n    // listenerMap.getListeners(type, capture) instead, which is simpler.\n    var listenerArray = listenerMap.listeners[type.toString()];\n    if (listenerArray) {\n      listenerArray = listenerArray.concat();\n      for (var i = 0; i < listenerArray.length; i++) {\n        var listener = listenerArray[i];\n        // We might not have a listener if the listener was removed.\n        if (listener && listener.capture == capture && !listener.removed) {\n          var result = goog.events.fireListener(listener, eventObject);\n          retval = retval && (result !== false);\n        }\n      }\n    }\n  }\n  return retval;\n};\n\n\n/**\n * Fires a listener with a set of arguments\n *\n * @param {goog.events.Listener} listener The listener object to call.\n * @param {Object} eventObject The event object to pass to the listener.\n * @return {*} Result of listener.\n */\ngoog.events.fireListener = function(listener, eventObject) {\n  var listenerFn = listener.listener;\n  var listenerHandler = listener.handler || listener.src;\n\n  if (listener.callOnce) {\n    goog.events.unlistenByKey(listener);\n  }\n  return listenerFn.call(listenerHandler, eventObject);\n};\n\n\n/**\n * Gets the total number of listeners currently in the system.\n * @return {number} Number of listeners.\n * @deprecated This returns estimated count, now that Closure no longer\n * stores a central listener registry. We still return an estimation\n * to keep existing listener-related tests passing. In the near future,\n * this function will be removed.\n */\ngoog.events.getTotalListenerCount = function() {\n  return goog.events.listenerCountEstimate_;\n};\n\n\n/**\n * Dispatches an event (or event like object) and calls all listeners\n * listening for events of this type. The type of the event is decided by the\n * type property on the event object.\n *\n * If any of the listeners returns false OR calls preventDefault then this\n * function will return false.  If one of the capture listeners calls\n * stopPropagation, then the bubble listeners won't fire.\n *\n * @param {goog.events.Listenable} src The event target.\n * @param {goog.events.EventLike} e Event object.\n * @return {boolean} If anyone called preventDefault on the event object (or\n *     if any of the handlers returns false) this will also return false.\n *     If there are no handlers, or if all handlers return true, this returns\n *     true.\n */\ngoog.events.dispatchEvent = function(src, e) {\n  goog.asserts.assert(\n      goog.events.Listenable.isImplementedBy(src),\n      'Can not use goog.events.dispatchEvent with ' +\n          'non-goog.events.Listenable instance.');\n  return src.dispatchEvent(e);\n};\n\n\n/**\n * Installs exception protection for the browser event entry point using the\n * given error handler.\n *\n * @param {goog.debug.ErrorHandler} errorHandler Error handler with which to\n *     protect the entry point.\n */\ngoog.events.protectBrowserEventEntryPoint = function(errorHandler) {\n  goog.events.handleBrowserEvent_ =\n      errorHandler.protectEntryPoint(goog.events.handleBrowserEvent_);\n};\n\n\n/**\n * Handles an event and dispatches it to the correct listeners. This\n * function is a proxy for the real listener the user specified.\n *\n * @param {goog.events.Listener} listener The listener object.\n * @param {Event=} opt_evt Optional event object that gets passed in via the\n *     native event handlers.\n * @return {*} Result of the event handler.\n * @this {EventTarget} The object or Element that fired the event.\n * @private\n */\ngoog.events.handleBrowserEvent_ = function(listener, opt_evt) {\n  if (listener.removed) {\n    return true;\n  }\n\n  // Synthesize event propagation if the browser does not support W3C\n  // event model.\n  if (!goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT) {\n    var ieEvent = opt_evt ||\n        /** @type {Event} */ (goog.getObjectByName('window.event'));\n    var evt = new goog.events.BrowserEvent(ieEvent, this);\n    /** @type {*} */\n    var retval = true;\n\n    if (goog.events.CAPTURE_SIMULATION_MODE ==\n        goog.events.CaptureSimulationMode.ON) {\n      // If we have not marked this event yet, we should perform capture\n      // simulation.\n      if (!goog.events.isMarkedIeEvent_(ieEvent)) {\n        goog.events.markIeEvent_(ieEvent);\n\n        var ancestors = [];\n        for (var parent = evt.currentTarget; parent;\n             parent = parent.parentNode) {\n          ancestors.push(parent);\n        }\n\n        // Fire capture listeners.\n        var type = listener.type;\n        for (var i = ancestors.length - 1;\n             !evt.hasPropagationStopped() && i >= 0; i--) {\n          evt.currentTarget = ancestors[i];\n          var result =\n              goog.events.fireListeners_(ancestors[i], type, true, evt);\n          retval = retval && result;\n        }\n\n        // Fire bubble listeners.\n        //\n        // We can technically rely on IE to perform bubble event\n        // propagation. However, it turns out that IE fires events in\n        // opposite order of attachEvent registration, which broke\n        // some code and tests that rely on the order. (While W3C DOM\n        // Level 2 Events TR leaves the event ordering unspecified,\n        // modern browsers and W3C DOM Level 3 Events Working Draft\n        // actually specify the order as the registration order.)\n        for (var i = 0; !evt.hasPropagationStopped() && i < ancestors.length;\n             i++) {\n          evt.currentTarget = ancestors[i];\n          var result =\n              goog.events.fireListeners_(ancestors[i], type, false, evt);\n          retval = retval && result;\n        }\n      }\n    } else {\n      retval = goog.events.fireListener(listener, evt);\n    }\n    return retval;\n  }\n\n  // Otherwise, simply fire the listener.\n  return goog.events.fireListener(\n      listener, new goog.events.BrowserEvent(opt_evt, this));\n};\n\n\n/**\n * This is used to mark the IE event object so we do not do the Closure pass\n * twice for a bubbling event.\n * @param {Event} e The IE browser event.\n * @private\n */\ngoog.events.markIeEvent_ = function(e) {\n  // Only the keyCode and the returnValue can be changed. We use keyCode for\n  // non keyboard events.\n  // event.returnValue is a bit more tricky. It is undefined by default. A\n  // boolean false prevents the default action. In a window.onbeforeunload and\n  // the returnValue is non undefined it will be alerted. However, we will only\n  // modify the returnValue for keyboard events. We can get a problem if non\n  // closure events sets the keyCode or the returnValue\n\n  var useReturnValue = false;\n\n  if (e.keyCode == 0) {\n    // We cannot change the keyCode in case that srcElement is input[type=file].\n    // We could test that that is the case but that would allocate 3 objects.\n    // If we use try/catch we will only allocate extra objects in the case of a\n    // failure.\n\n    try {\n      e.keyCode = -1;\n      return;\n    } catch (ex) {\n      useReturnValue = true;\n    }\n  }\n\n  if (useReturnValue ||\n      /** @type {boolean|undefined} */ (e.returnValue) == undefined) {\n    e.returnValue = true;\n  }\n};\n\n\n/**\n * This is used to check if an IE event has already been handled by the Closure\n * system so we do not do the Closure pass twice for a bubbling event.\n * @param {Event} e  The IE browser event.\n * @return {boolean} True if the event object has been marked.\n * @private\n */\ngoog.events.isMarkedIeEvent_ = function(e) {\n  return e.keyCode < 0 || e.returnValue != undefined;\n};\n\n\n/**\n * Counter to create unique event ids.\n * @private {number}\n */\ngoog.events.uniqueIdCounter_ = 0;\n\n\n/**\n * Creates a unique event id.\n *\n * @param {string} identifier The identifier.\n * @return {string} A unique identifier.\n * @idGenerator {unique}\n */\ngoog.events.getUniqueId = function(identifier) {\n  return identifier + '_' + goog.events.uniqueIdCounter_++;\n};\n\n\n/**\n * @param {EventTarget} src The source object.\n * @return {goog.events.ListenerMap} A listener map for the given\n *     source object, or null if none exists.\n * @private\n */\ngoog.events.getListenerMap_ = function(src) {\n  var listenerMap = src[goog.events.LISTENER_MAP_PROP_];\n  // IE serializes the property as well (e.g. when serializing outer\n  // HTML). So we must check that the value is of the correct type.\n  return listenerMap instanceof goog.events.ListenerMap ? listenerMap : null;\n};\n\n\n/**\n * Expando property for listener function wrapper for Object with\n * handleEvent.\n * @private @const {string}\n */\ngoog.events.LISTENER_WRAPPER_PROP_ =\n    '__closure_events_fn_' + ((Math.random() * 1e9) >>> 0);\n\n\n/**\n * @param {Object|Function} listener The listener function or an\n *     object that contains handleEvent method.\n * @return {!Function} Either the original function or a function that\n *     calls obj.handleEvent. If the same listener is passed to this\n *     function more than once, the same function is guaranteed to be\n *     returned.\n */\ngoog.events.wrapListener = function(listener) {\n  goog.asserts.assert(listener, 'Listener can not be null.');\n\n  if (goog.isFunction(listener)) {\n    return listener;\n  }\n\n  goog.asserts.assert(\n      listener.handleEvent, 'An object listener must have handleEvent method.');\n  if (!listener[goog.events.LISTENER_WRAPPER_PROP_]) {\n    listener[goog.events.LISTENER_WRAPPER_PROP_] = function(e) {\n      return /** @type {?} */ (listener).handleEvent(e);\n    };\n  }\n  return listener[goog.events.LISTENER_WRAPPER_PROP_];\n};\n\n\n// Register the browser event handler as an entry point, so that\n// it can be monitored for exception handling, etc.\ngoog.debug.entryPointRegistry.register(\n    /**\n     * @param {function(!Function): !Function} transformer The transforming\n     *     function.\n     */\n    function(transformer) {\n      goog.events.handleBrowserEvent_ =\n          transformer(goog.events.handleBrowserEvent_);\n    });\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview A disposable implementation of a custom\n * listenable/event target. See also: documentation for\n * `goog.events.Listenable`.\n *\n * @see ../demos/eventtarget.html\n * @see goog.events.Listenable\n */\n\ngoog.provide('goog.events.EventTarget');\n\ngoog.require('goog.Disposable');\ngoog.require('goog.asserts');\ngoog.require('goog.events');\ngoog.require('goog.events.Event');\ngoog.require('goog.events.Listenable');\ngoog.require('goog.events.ListenerMap');\ngoog.require('goog.object');\ngoog.requireType('goog.events.EventId');\n\n\n\n/**\n * An implementation of `goog.events.Listenable` with full W3C\n * EventTarget-like support (capture/bubble mechanism, stopping event\n * propagation, preventing default actions).\n *\n * You may subclass this class to turn your class into a Listenable.\n *\n * Unless propagation is stopped, an event dispatched by an\n * EventTarget will bubble to the parent returned by\n * `getParentEventTarget`. To set the parent, call\n * `setParentEventTarget`. Subclasses that don't support\n * changing the parent can override the setter to throw an error.\n *\n * Example usage:\n * <pre>\n *   var source = new goog.events.EventTarget();\n *   function handleEvent(e) {\n *     alert('Type: ' + e.type + '; Target: ' + e.target);\n *   }\n *   source.listen('foo', handleEvent);\n *   // Or: goog.events.listen(source, 'foo', handleEvent);\n *   ...\n *   source.dispatchEvent('foo');  // will call handleEvent\n *   ...\n *   source.unlisten('foo', handleEvent);\n *   // Or: goog.events.unlisten(source, 'foo', handleEvent);\n * </pre>\n *\n * @constructor\n * @extends {goog.Disposable}\n * @implements {goog.events.Listenable}\n */\ngoog.events.EventTarget = function() {\n  goog.Disposable.call(this);\n\n  /**\n   * Maps of event type to an array of listeners.\n   * @private {!goog.events.ListenerMap}\n   */\n  this.eventTargetListeners_ = new goog.events.ListenerMap(this);\n\n  /**\n   * The object to use for event.target. Useful when mixing in an\n   * EventTarget to another object.\n   * @private {!Object}\n   */\n  this.actualEventTarget_ = this;\n\n  /**\n   * Parent event target, used during event bubbling.\n   *\n   * TODO(chrishenry): Change this to goog.events.Listenable. This\n   * currently breaks people who expect getParentEventTarget to return\n   * goog.events.EventTarget.\n   *\n   * @private {?goog.events.EventTarget}\n   */\n  this.parentEventTarget_ = null;\n};\ngoog.inherits(goog.events.EventTarget, goog.Disposable);\ngoog.events.Listenable.addImplementation(goog.events.EventTarget);\n\n\n/**\n * An artificial cap on the number of ancestors you can have. This is mainly\n * for loop detection.\n * @const {number}\n * @private\n */\ngoog.events.EventTarget.MAX_ANCESTORS_ = 1000;\n\n\n/**\n * Returns the parent of this event target to use for bubbling.\n *\n * @return {goog.events.EventTarget} The parent EventTarget or null if\n *     there is no parent.\n * @override\n */\ngoog.events.EventTarget.prototype.getParentEventTarget = function() {\n  return this.parentEventTarget_;\n};\n\n\n/**\n * Sets the parent of this event target to use for capture/bubble\n * mechanism.\n * @param {goog.events.EventTarget} parent Parent listenable (null if none).\n */\ngoog.events.EventTarget.prototype.setParentEventTarget = function(parent) {\n  this.parentEventTarget_ = parent;\n};\n\n\n/**\n * Adds an event listener to the event target. The same handler can only be\n * added once per the type. Even if you add the same handler multiple times\n * using the same type then it will only be called once when the event is\n * dispatched.\n *\n * @param {string|!goog.events.EventId} type The type of the event to listen for\n * @param {function(?):?|{handleEvent:function(?):?}|null} handler The function\n *     to handle the event. The handler can also be an object that implements\n *     the handleEvent method which takes the event object as argument.\n * @param {boolean=} opt_capture In DOM-compliant browsers, this determines\n *     whether the listener is fired during the capture or bubble phase\n *     of the event.\n * @param {Object=} opt_handlerScope Object in whose scope to call\n *     the listener.\n * @deprecated Use `#listen` instead, when possible. Otherwise, use\n *     `goog.events.listen` if you are passing Object\n *     (instead of Function) as handler.\n */\ngoog.events.EventTarget.prototype.addEventListener = function(\n    type, handler, opt_capture, opt_handlerScope) {\n  goog.events.listen(this, type, handler, opt_capture, opt_handlerScope);\n};\n\n\n/**\n * Removes an event listener from the event target. The handler must be the\n * same object as the one added. If the handler has not been added then\n * nothing is done.\n *\n * @param {string} type The type of the event to listen for.\n * @param {function(?):?|{handleEvent:function(?):?}|null} handler The function\n *     to handle the event. The handler can also be an object that implements\n *     the handleEvent method which takes the event object as argument.\n * @param {boolean=} opt_capture In DOM-compliant browsers, this determines\n *     whether the listener is fired during the capture or bubble phase\n *     of the event.\n * @param {Object=} opt_handlerScope Object in whose scope to call\n *     the listener.\n * @deprecated Use `#unlisten` instead, when possible. Otherwise, use\n *     `goog.events.unlisten` if you are passing Object\n *     (instead of Function) as handler.\n */\ngoog.events.EventTarget.prototype.removeEventListener = function(\n    type, handler, opt_capture, opt_handlerScope) {\n  goog.events.unlisten(this, type, handler, opt_capture, opt_handlerScope);\n};\n\n\n/**\n * @param {?goog.events.EventLike} e Event object.\n * @return {boolean} If anyone called preventDefault on the event object (or\n *     if any of the listeners returns false) this will also return false.\n * @override\n */\ngoog.events.EventTarget.prototype.dispatchEvent = function(e) {\n  this.assertInitialized_();\n\n  var ancestorsTree, ancestor = this.getParentEventTarget();\n  if (ancestor) {\n    ancestorsTree = [];\n    var ancestorCount = 1;\n    for (; ancestor; ancestor = ancestor.getParentEventTarget()) {\n      ancestorsTree.push(ancestor);\n      goog.asserts.assert(\n          (++ancestorCount < goog.events.EventTarget.MAX_ANCESTORS_),\n          'infinite loop');\n    }\n  }\n\n  return goog.events.EventTarget.dispatchEventInternal_(\n      this.actualEventTarget_, e, ancestorsTree);\n};\n\n\n/**\n * Removes listeners from this object.  Classes that extend EventTarget may\n * need to override this method in order to remove references to DOM Elements\n * and additional listeners.\n * @override\n * @protected\n */\ngoog.events.EventTarget.prototype.disposeInternal = function() {\n  goog.events.EventTarget.superClass_.disposeInternal.call(this);\n\n  this.removeAllListeners();\n  this.parentEventTarget_ = null;\n};\n\n\n/**\n * @param {string|!goog.events.EventId<EVENTOBJ>} type The event type id.\n * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback\n *     method.\n * @param {boolean=} opt_useCapture Whether to fire in capture phase\n *     (defaults to false).\n * @param {SCOPE=} opt_listenerScope Object in whose scope to call the\n *     listener.\n * @return {!goog.events.ListenableKey} Unique key for the listener.\n * @template SCOPE,EVENTOBJ\n * @override\n */\ngoog.events.EventTarget.prototype.listen = function(\n    type, listener, opt_useCapture, opt_listenerScope) {\n  this.assertInitialized_();\n  return this.eventTargetListeners_.add(\n      String(type), listener, false /* callOnce */, opt_useCapture,\n      opt_listenerScope);\n};\n\n\n/**\n * @param {string|!goog.events.EventId<EVENTOBJ>} type The event type id.\n * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback\n *     method.\n * @param {boolean=} opt_useCapture Whether to fire in capture phase\n *     (defaults to false).\n * @param {SCOPE=} opt_listenerScope Object in whose scope to call the\n *     listener.\n * @return {!goog.events.ListenableKey} Unique key for the listener.\n * @template SCOPE,EVENTOBJ\n * @override\n */\ngoog.events.EventTarget.prototype.listenOnce = function(\n    type, listener, opt_useCapture, opt_listenerScope) {\n  return this.eventTargetListeners_.add(\n      String(type), listener, true /* callOnce */, opt_useCapture,\n      opt_listenerScope);\n};\n\n\n/**\n * @param {string|!goog.events.EventId<EVENTOBJ>} type The event type id.\n * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback\n *     method.\n * @param {boolean=} opt_useCapture Whether to fire in capture phase\n *     (defaults to false).\n * @param {SCOPE=} opt_listenerScope Object in whose scope to call\n *     the listener.\n * @return {boolean} Whether any listener was removed.\n * @template SCOPE,EVENTOBJ\n * @override\n */\ngoog.events.EventTarget.prototype.unlisten = function(\n    type, listener, opt_useCapture, opt_listenerScope) {\n  return this.eventTargetListeners_.remove(\n      String(type), listener, opt_useCapture, opt_listenerScope);\n};\n\n\n/**\n * @param {!goog.events.ListenableKey} key The key returned by\n *     listen() or listenOnce().\n * @return {boolean} Whether any listener was removed.\n * @override\n */\ngoog.events.EventTarget.prototype.unlistenByKey = function(key) {\n  return this.eventTargetListeners_.removeByKey(key);\n};\n\n\n/**\n * @param {string=} opt_type Type of event to remove, default is to\n *     remove all types.\n * @return {number} Number of listeners removed.\n * @override\n */\ngoog.events.EventTarget.prototype.removeAllListeners = function(opt_type) {\n  // TODO(chrishenry): Previously, removeAllListeners can be called on\n  // uninitialized EventTarget, so we preserve that behavior. We\n  // should remove this when usages that rely on that fact are purged.\n  if (!this.eventTargetListeners_) {\n    return 0;\n  }\n  return this.eventTargetListeners_.removeAll(opt_type);\n};\n\n\n/**\n * @param {string|!goog.events.EventId<EVENTOBJ>} type The type of the\n *     listeners to fire.\n * @param {boolean} capture The capture mode of the listeners to fire.\n * @param {EVENTOBJ} eventObject The event object to fire.\n * @return {boolean} Whether all listeners succeeded without\n *     attempting to prevent default behavior. If any listener returns\n *     false or called goog.events.Event#preventDefault, this returns\n *     false.\n * @template EVENTOBJ\n * @override\n */\ngoog.events.EventTarget.prototype.fireListeners = function(\n    type, capture, eventObject) {\n  // TODO(chrishenry): Original code avoids array creation when there\n  // is no listener, so we do the same. If this optimization turns\n  // out to be not required, we can replace this with\n  // getListeners(type, capture) instead, which is simpler.\n  var listenerArray = this.eventTargetListeners_.listeners[String(type)];\n  if (!listenerArray) {\n    return true;\n  }\n  listenerArray = listenerArray.concat();\n\n  var rv = true;\n  for (var i = 0; i < listenerArray.length; ++i) {\n    var listener = listenerArray[i];\n    // We might not have a listener if the listener was removed.\n    if (listener && !listener.removed && listener.capture == capture) {\n      var listenerFn = listener.listener;\n      var listenerHandler = listener.handler || listener.src;\n\n      if (listener.callOnce) {\n        this.unlistenByKey(listener);\n      }\n      rv = listenerFn.call(listenerHandler, eventObject) !== false && rv;\n    }\n  }\n\n  return rv && !eventObject.defaultPrevented;\n};\n\n\n/**\n * @param {string|!goog.events.EventId} type The type of the listeners to fire.\n * @param {boolean} capture The capture mode of the listeners to fire.\n * @return {!Array<!goog.events.ListenableKey>} An array of registered\n *     listeners.\n * @template EVENTOBJ\n * @override\n */\ngoog.events.EventTarget.prototype.getListeners = function(type, capture) {\n  return this.eventTargetListeners_.getListeners(String(type), capture);\n};\n\n\n/**\n * @param {string|!goog.events.EventId<EVENTOBJ>} type The name of the event\n *     without the 'on' prefix.\n * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener The\n *     listener function to get.\n * @param {boolean} capture Whether the listener is a capturing listener.\n * @param {SCOPE=} opt_listenerScope Object in whose scope to call the\n *     listener.\n * @return {?goog.events.ListenableKey} the found listener or null if not found.\n * @template SCOPE,EVENTOBJ\n * @override\n */\ngoog.events.EventTarget.prototype.getListener = function(\n    type, listener, capture, opt_listenerScope) {\n  return this.eventTargetListeners_.getListener(\n      String(type), listener, capture, opt_listenerScope);\n};\n\n\n/**\n * @param {string|!goog.events.EventId<EVENTOBJ>=} opt_type Event type.\n * @param {boolean=} opt_capture Whether to check for capture or bubble\n *     listeners.\n * @return {boolean} Whether there is any active listeners matching\n *     the requested type and/or capture phase.\n * @template EVENTOBJ\n * @override\n */\ngoog.events.EventTarget.prototype.hasListener = function(\n    opt_type, opt_capture) {\n  var id = (opt_type !== undefined) ? String(opt_type) : undefined;\n  return this.eventTargetListeners_.hasListener(id, opt_capture);\n};\n\n\n/**\n * Sets the target to be used for `event.target` when firing\n * event. Mainly used for testing. For example, see\n * `goog.testing.events.mixinListenable`.\n * @param {!Object} target The target.\n */\ngoog.events.EventTarget.prototype.setTargetForTesting = function(target) {\n  this.actualEventTarget_ = target;\n};\n\n\n/**\n * Asserts that the event target instance is initialized properly.\n * @private\n */\ngoog.events.EventTarget.prototype.assertInitialized_ = function() {\n  goog.asserts.assert(\n      this.eventTargetListeners_,\n      'Event target is not initialized. Did you call the superclass ' +\n          '(goog.events.EventTarget) constructor?');\n};\n\n\n/**\n * Dispatches the given event on the ancestorsTree.\n *\n * @param {!Object} target The target to dispatch on.\n * @param {goog.events.Event|Object|string} e The event object.\n * @param {Array<goog.events.Listenable>=} opt_ancestorsTree The ancestors\n *     tree of the target, in reverse order from the closest ancestor\n *     to the root event target. May be null if the target has no ancestor.\n * @return {boolean} If anyone called preventDefault on the event object (or\n *     if any of the listeners returns false) this will also return false.\n * @private\n */\ngoog.events.EventTarget.dispatchEventInternal_ = function(\n    target, e, opt_ancestorsTree) {\n  /** @suppress {missingProperties} */\n  var type = e.type || /** @type {string} */ (e);\n\n  // If accepting a string or object, create a custom event object so that\n  // preventDefault and stopPropagation work with the event.\n  if (typeof e === 'string') {\n    e = new goog.events.Event(e, target);\n  } else if (!(e instanceof goog.events.Event)) {\n    var oldEvent = e;\n    e = new goog.events.Event(type, target);\n    goog.object.extend(e, oldEvent);\n  } else {\n    e.target = e.target || target;\n  }\n\n  var rv = true, currentTarget;\n\n  // Executes all capture listeners on the ancestors, if any.\n  if (opt_ancestorsTree) {\n    for (var i = opt_ancestorsTree.length - 1;\n         !e.hasPropagationStopped() && i >= 0; i--) {\n      currentTarget = e.currentTarget = opt_ancestorsTree[i];\n      rv = currentTarget.fireListeners(type, true, e) && rv;\n    }\n  }\n\n  // Executes capture and bubble listeners on the target.\n  if (!e.hasPropagationStopped()) {\n    currentTarget = /** @type {?} */ (e.currentTarget = target);\n    rv = currentTarget.fireListeners(type, true, e) && rv;\n    if (!e.hasPropagationStopped()) {\n      rv = currentTarget.fireListeners(type, false, e) && rv;\n    }\n  }\n\n  // Executes all bubble listeners on the ancestors, if any.\n  if (opt_ancestorsTree) {\n    for (i = 0; !e.hasPropagationStopped() && i < opt_ancestorsTree.length;\n         i++) {\n      currentTarget = e.currentTarget = opt_ancestorsTree[i];\n      rv = currentTarget.fireListeners(type, false, e) && rv;\n    }\n  }\n\n  return rv;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview A timer class to which other classes and objects can listen on.\n * This is only an abstraction above `setInterval`.\n *\n * @see ../demos/timers.html\n */\n\ngoog.provide('goog.Timer');\n\ngoog.require('goog.Promise');\ngoog.require('goog.events.EventTarget');\ngoog.requireType('goog.Thenable');\n\n\n\n/**\n * Class for handling timing events.\n *\n * @param {number=} opt_interval Number of ms between ticks (default: 1ms).\n * @param {Object=} opt_timerObject  An object that has `setTimeout`,\n *     `setInterval`, `clearTimeout` and `clearInterval`\n *     (e.g., `window`).\n * @constructor\n * @extends {goog.events.EventTarget}\n */\ngoog.Timer = function(opt_interval, opt_timerObject) {\n  'use strict';\n  goog.events.EventTarget.call(this);\n\n  /**\n   * Number of ms between ticks\n   * @private {number}\n   */\n  this.interval_ = opt_interval || 1;\n\n  /**\n   * An object that implements `setTimeout`, `setInterval`,\n   * `clearTimeout` and `clearInterval`. We default to the window\n   * object. Changing this on {@link goog.Timer.prototype} changes the object\n   * for all timer instances which can be useful if your environment has some\n   * other implementation of timers than the `window` object.\n   * @private {{setTimeout:!Function, clearTimeout:!Function}}\n   */\n  this.timerObject_ = /** @type {{setTimeout, clearTimeout}} */ (\n      opt_timerObject || goog.Timer.defaultTimerObject);\n\n  /**\n   * Cached `tick_` bound to the object for later use in the timer.\n   * @private {Function}\n   * @const\n   */\n  this.boundTick_ = goog.bind(this.tick_, this);\n\n  /**\n   * Firefox browser often fires the timer event sooner (sometimes MUCH sooner)\n   * than the requested timeout. So we compare the time to when the event was\n   * last fired, and reschedule if appropriate. See also\n   * {@link goog.Timer.intervalScale}.\n   * @private {number}\n   */\n  this.last_ = goog.now();\n};\ngoog.inherits(goog.Timer, goog.events.EventTarget);\n\n\n/**\n * Maximum timeout value.\n *\n * Timeout values too big to fit into a signed 32-bit integer may cause overflow\n * in FF, Safari, and Chrome, resulting in the timeout being scheduled\n * immediately. It makes more sense simply not to schedule these timeouts, since\n * 24.8 days is beyond a reasonable expectation for the browser to stay open.\n *\n * @private {number}\n * @const\n */\ngoog.Timer.MAX_TIMEOUT_ = 2147483647;\n\n\n/**\n * A timer ID that cannot be returned by any known implementation of\n * `window.setTimeout`. Passing this value to `window.clearTimeout`\n * should therefore be a no-op.\n *\n * @private {number}\n * @const\n */\ngoog.Timer.INVALID_TIMEOUT_ID_ = -1;\n\n\n/**\n * Whether this timer is enabled\n * @type {boolean}\n */\ngoog.Timer.prototype.enabled = false;\n\n\n/**\n * An object that implements `setTimeout`, `setInterval`,\n * `clearTimeout` and `clearInterval`. We default to the global\n * object. Changing `goog.Timer.defaultTimerObject` changes the object for\n * all timer instances which can be useful if your environment has some other\n * implementation of timers you'd like to use.\n * @type {{setTimeout, clearTimeout}}\n */\ngoog.Timer.defaultTimerObject = goog.global;\n\n\n/**\n * Variable that controls the timer error correction. If the timer is called\n * before the requested interval times `intervalScale`, which often\n * happens on Mozilla, the timer is rescheduled.\n * @see {@link #last_}\n * @type {number}\n */\ngoog.Timer.intervalScale = 0.8;\n\n\n/**\n * Variable for storing the result of `setInterval`.\n * @private {?number}\n */\ngoog.Timer.prototype.timer_ = null;\n\n\n/**\n * Gets the interval of the timer.\n * @return {number} interval Number of ms between ticks.\n */\ngoog.Timer.prototype.getInterval = function() {\n  'use strict';\n  return this.interval_;\n};\n\n\n/**\n * Sets the interval of the timer.\n * @param {number} interval Number of ms between ticks.\n */\ngoog.Timer.prototype.setInterval = function(interval) {\n  'use strict';\n  this.interval_ = interval;\n  if (this.timer_ && this.enabled) {\n    // Stop and then start the timer to reset the interval.\n    this.stop();\n    this.start();\n  } else if (this.timer_) {\n    this.stop();\n  }\n};\n\n\n/**\n * Callback for the `setTimeout` used by the timer.\n * @private\n */\ngoog.Timer.prototype.tick_ = function() {\n  'use strict';\n  if (this.enabled) {\n    var elapsed = goog.now() - this.last_;\n    if (elapsed > 0 && elapsed < this.interval_ * goog.Timer.intervalScale) {\n      this.timer_ = this.timerObject_.setTimeout(\n          this.boundTick_, this.interval_ - elapsed);\n      return;\n    }\n\n    // Prevents setInterval from registering a duplicate timeout when called\n    // in the timer event handler.\n    if (this.timer_) {\n      this.timerObject_.clearTimeout(this.timer_);\n      this.timer_ = null;\n    }\n\n    this.dispatchTick();\n    // The timer could be stopped in the timer event handler.\n    if (this.enabled) {\n      // Stop and start to ensure there is always only one timeout even if\n      // start is called in the timer event handler.\n      this.stop();\n      this.start();\n    }\n  }\n};\n\n\n/**\n * Dispatches the TICK event. This is its own method so subclasses can override.\n */\ngoog.Timer.prototype.dispatchTick = function() {\n  'use strict';\n  this.dispatchEvent(goog.Timer.TICK);\n};\n\n\n/**\n * Starts the timer.\n */\ngoog.Timer.prototype.start = function() {\n  'use strict';\n  this.enabled = true;\n\n  // If there is no interval already registered, start it now\n  if (!this.timer_) {\n    // IMPORTANT!\n    // window.setInterval in FireFox has a bug - it fires based on\n    // absolute time, rather than on relative time. What this means\n    // is that if a computer is sleeping/hibernating for 24 hours\n    // and the timer interval was configured to fire every 1000ms,\n    // then after the PC wakes up the timer will fire, in rapid\n    // succession, 3600*24 times.\n    // This bug is described here and is already fixed, but it will\n    // take time to propagate, so for now I am switching this over\n    // to setTimeout logic.\n    //     https://bugzilla.mozilla.org/show_bug.cgi?id=376643\n    //\n    this.timer_ = this.timerObject_.setTimeout(this.boundTick_, this.interval_);\n    this.last_ = goog.now();\n  }\n};\n\n\n/**\n * Stops the timer.\n */\ngoog.Timer.prototype.stop = function() {\n  'use strict';\n  this.enabled = false;\n  if (this.timer_) {\n    this.timerObject_.clearTimeout(this.timer_);\n    this.timer_ = null;\n  }\n};\n\n\n/** @override */\ngoog.Timer.prototype.disposeInternal = function() {\n  'use strict';\n  goog.Timer.superClass_.disposeInternal.call(this);\n  this.stop();\n  delete this.timerObject_;\n};\n\n\n/**\n * Constant for the timer's event type.\n * @const\n */\ngoog.Timer.TICK = 'tick';\n\n\n/**\n * Calls the given function once, after the optional pause.\n * <p>\n * The function is always called asynchronously, even if the delay is 0. This\n * is a common trick to schedule a function to run after a batch of browser\n * event processing.\n *\n * @param {function(this:SCOPE)|{handleEvent:function()}|null} listener Function\n *     or object that has a handleEvent method.\n * @param {number=} opt_delay Milliseconds to wait; default is 0.\n * @param {SCOPE=} opt_handler Object in whose scope to call the listener.\n * @return {number} A handle to the timer ID.\n * @template SCOPE\n */\ngoog.Timer.callOnce = function(listener, opt_delay, opt_handler) {\n  'use strict';\n  if (goog.isFunction(listener)) {\n    if (opt_handler) {\n      listener = goog.bind(listener, opt_handler);\n    }\n  } else if (listener && typeof listener.handleEvent == 'function') {\n    // using typeof to prevent strict js warning\n    listener = goog.bind(listener.handleEvent, listener);\n  } else {\n    throw new Error('Invalid listener argument');\n  }\n\n  if (Number(opt_delay) > goog.Timer.MAX_TIMEOUT_) {\n    // Timeouts greater than MAX_INT return immediately due to integer\n    // overflow in many browsers.  Since MAX_INT is 24.8 days, just don't\n    // schedule anything at all.\n    return goog.Timer.INVALID_TIMEOUT_ID_;\n  } else {\n    return goog.Timer.defaultTimerObject.setTimeout(listener, opt_delay || 0);\n  }\n};\n\n\n/**\n * Clears a timeout initiated by {@link #callOnce}.\n * @param {?number} timerId A timer ID.\n */\ngoog.Timer.clear = function(timerId) {\n  'use strict';\n  goog.Timer.defaultTimerObject.clearTimeout(timerId);\n};\n\n\n/**\n * @param {number} delay Milliseconds to wait.\n * @param {(RESULT|goog.Thenable<RESULT>|Thenable)=} opt_result The value\n *     with which the promise will be resolved.\n * @return {!goog.Promise<RESULT>} A promise that will be resolved after\n *     the specified delay, unless it is canceled first.\n * @template RESULT\n */\ngoog.Timer.promise = function(delay, opt_result) {\n  'use strict';\n  var timerKey = null;\n  return new goog\n      .Promise(function(resolve, reject) {\n        'use strict';\n        timerKey = goog.Timer.callOnce(function() {\n          'use strict';\n          resolve(opt_result);\n        }, delay);\n        if (timerKey == goog.Timer.INVALID_TIMEOUT_ID_) {\n          reject(new Error('Failed to schedule timer.'));\n        }\n      })\n      .thenCatch(function(error) {\n        'use strict';\n        // Clear the timer. The most likely reason is \"cancel\" signal.\n        goog.Timer.clear(timerKey);\n        throw error;\n      });\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Generics method for collection-like classes and objects.\n *\n *\n * This file contains functions to work with collections. It supports using\n * Map, Set, Array and Object and other classes that implement collection-like\n * methods.\n * @suppress {strictMissingProperties}\n */\n\n\ngoog.provide('goog.structs');\n\ngoog.require('goog.array');\ngoog.require('goog.object');\n\n\n// We treat an object as a dictionary if it has getKeys or it is an object that\n// isn't arrayLike.\n\n\n/**\n * Returns the number of values in the collection-like object.\n * @param {Object} col The collection-like object.\n * @return {number} The number of values in the collection-like object.\n */\ngoog.structs.getCount = function(col) {\n  if (col.getCount && typeof col.getCount == 'function') {\n    return col.getCount();\n  }\n  if (goog.isArrayLike(col) || typeof col === 'string') {\n    return col.length;\n  }\n  return goog.object.getCount(col);\n};\n\n\n/**\n * Returns the values of the collection-like object.\n * @param {Object} col The collection-like object.\n * @return {!Array<?>} The values in the collection-like object.\n */\ngoog.structs.getValues = function(col) {\n  if (col.getValues && typeof col.getValues == 'function') {\n    return col.getValues();\n  }\n  if (typeof col === 'string') {\n    return col.split('');\n  }\n  if (goog.isArrayLike(col)) {\n    var rv = [];\n    var l = col.length;\n    for (var i = 0; i < l; i++) {\n      rv.push(col[i]);\n    }\n    return rv;\n  }\n  return goog.object.getValues(col);\n};\n\n\n/**\n * Returns the keys of the collection. Some collections have no notion of\n * keys/indexes and this function will return undefined in those cases.\n * @param {Object} col The collection-like object.\n * @return {!Array|undefined} The keys in the collection.\n */\ngoog.structs.getKeys = function(col) {\n  if (col.getKeys && typeof col.getKeys == 'function') {\n    return col.getKeys();\n  }\n  // if we have getValues but no getKeys we know this is a key-less collection\n  if (col.getValues && typeof col.getValues == 'function') {\n    return undefined;\n  }\n  if (goog.isArrayLike(col) || typeof col === 'string') {\n    var rv = [];\n    var l = col.length;\n    for (var i = 0; i < l; i++) {\n      rv.push(i);\n    }\n    return rv;\n  }\n\n  return goog.object.getKeys(col);\n};\n\n\n/**\n * Whether the collection contains the given value. This is O(n) and uses\n * equals (==) to test the existence.\n * @param {Object} col The collection-like object.\n * @param {*} val The value to check for.\n * @return {boolean} True if the map contains the value.\n */\ngoog.structs.contains = function(col, val) {\n  if (col.contains && typeof col.contains == 'function') {\n    return col.contains(val);\n  }\n  if (col.containsValue && typeof col.containsValue == 'function') {\n    return col.containsValue(val);\n  }\n  if (goog.isArrayLike(col) || typeof col === 'string') {\n    return goog.array.contains(/** @type {!Array<?>} */ (col), val);\n  }\n  return goog.object.containsValue(col, val);\n};\n\n\n/**\n * Whether the collection is empty.\n * @param {Object} col The collection-like object.\n * @return {boolean} True if empty.\n */\ngoog.structs.isEmpty = function(col) {\n  if (col.isEmpty && typeof col.isEmpty == 'function') {\n    return col.isEmpty();\n  }\n\n  // We do not use goog.string.isEmptyOrWhitespace because here we treat the\n  // string as\n  // collection and as such even whitespace matters\n\n  if (goog.isArrayLike(col) || typeof col === 'string') {\n    return goog.array.isEmpty(/** @type {!Array<?>} */ (col));\n  }\n  return goog.object.isEmpty(col);\n};\n\n\n/**\n * Removes all the elements from the collection.\n * @param {Object} col The collection-like object.\n */\ngoog.structs.clear = function(col) {\n  // NOTE(arv): This should not contain strings because strings are immutable\n  if (col.clear && typeof col.clear == 'function') {\n    col.clear();\n  } else if (goog.isArrayLike(col)) {\n    goog.array.clear(/** @type {IArrayLike<?>} */ (col));\n  } else {\n    goog.object.clear(col);\n  }\n};\n\n\n/**\n * Calls a function for each value in a collection. The function takes\n * three arguments; the value, the key and the collection.\n *\n * @param {S} col The collection-like object.\n * @param {function(this:T,?,?,S):?} f The function to call for every value.\n *     This function takes\n *     3 arguments (the value, the key or undefined if the collection has no\n *     notion of keys, and the collection) and the return value is irrelevant.\n * @param {T=} opt_obj The object to be used as the value of 'this'\n *     within `f`.\n * @template T,S\n * @deprecated Use a more specific method, e.g. goog.array.forEach,\n *     goog.object.forEach, or for-of.\n */\ngoog.structs.forEach = function(col, f, opt_obj) {\n  if (col.forEach && typeof col.forEach == 'function') {\n    col.forEach(f, opt_obj);\n  } else if (goog.isArrayLike(col) || typeof col === 'string') {\n    goog.array.forEach(/** @type {!Array<?>} */ (col), f, opt_obj);\n  } else {\n    var keys = goog.structs.getKeys(col);\n    var values = goog.structs.getValues(col);\n    var l = values.length;\n    for (var i = 0; i < l; i++) {\n      f.call(/** @type {?} */ (opt_obj), values[i], keys && keys[i], col);\n    }\n  }\n};\n\n\n/**\n * Calls a function for every value in the collection. When a call returns true,\n * adds the value to a new collection (Array is returned by default).\n *\n * @param {S} col The collection-like object.\n * @param {function(this:T,?,?,S):boolean} f The function to call for every\n *     value. This function takes\n *     3 arguments (the value, the key or undefined if the collection has no\n *     notion of keys, and the collection) and should return a Boolean. If the\n *     return value is true the value is added to the result collection. If it\n *     is false the value is not included.\n * @param {T=} opt_obj The object to be used as the value of 'this'\n *     within `f`.\n * @return {!Object|!Array<?>} A new collection where the passed values are\n *     present. If col is a key-less collection an array is returned.  If col\n *     has keys and values a plain old JS object is returned.\n * @template T,S\n */\ngoog.structs.filter = function(col, f, opt_obj) {\n  if (typeof col.filter == 'function') {\n    return col.filter(f, opt_obj);\n  }\n  if (goog.isArrayLike(col) || typeof col === 'string') {\n    return goog.array.filter(/** @type {!Array<?>} */ (col), f, opt_obj);\n  }\n\n  var rv;\n  var keys = goog.structs.getKeys(col);\n  var values = goog.structs.getValues(col);\n  var l = values.length;\n  if (keys) {\n    rv = {};\n    for (var i = 0; i < l; i++) {\n      if (f.call(/** @type {?} */ (opt_obj), values[i], keys[i], col)) {\n        rv[keys[i]] = values[i];\n      }\n    }\n  } else {\n    // We should not use goog.array.filter here since we want to make sure that\n    // the index is undefined as well as make sure that col is passed to the\n    // function.\n    rv = [];\n    for (var i = 0; i < l; i++) {\n      if (f.call(opt_obj, values[i], undefined, col)) {\n        rv.push(values[i]);\n      }\n    }\n  }\n  return rv;\n};\n\n\n/**\n * Calls a function for every value in the collection and adds the result into a\n * new collection (defaults to creating a new Array).\n *\n * @param {S} col The collection-like object.\n * @param {function(this:T,?,?,S):V} f The function to call for every value.\n *     This function takes 3 arguments (the value, the key or undefined if the\n *     collection has no notion of keys, and the collection) and should return\n *     something. The result will be used as the value in the new collection.\n * @param {T=} opt_obj  The object to be used as the value of 'this'\n *     within `f`.\n * @return {!Object<V>|!Array<V>} A new collection with the new values.  If\n *     col is a key-less collection an array is returned.  If col has keys and\n *     values a plain old JS object is returned.\n * @template T,S,V\n */\ngoog.structs.map = function(col, f, opt_obj) {\n  if (typeof col.map == 'function') {\n    return col.map(f, opt_obj);\n  }\n  if (goog.isArrayLike(col) || typeof col === 'string') {\n    return goog.array.map(/** @type {!Array<?>} */ (col), f, opt_obj);\n  }\n\n  var rv;\n  var keys = goog.structs.getKeys(col);\n  var values = goog.structs.getValues(col);\n  var l = values.length;\n  if (keys) {\n    rv = {};\n    for (var i = 0; i < l; i++) {\n      rv[keys[i]] = f.call(/** @type {?} */ (opt_obj), values[i], keys[i], col);\n    }\n  } else {\n    // We should not use goog.array.map here since we want to make sure that\n    // the index is undefined as well as make sure that col is passed to the\n    // function.\n    rv = [];\n    for (var i = 0; i < l; i++) {\n      rv[i] = f.call(/** @type {?} */ (opt_obj), values[i], undefined, col);\n    }\n  }\n  return rv;\n};\n\n\n/**\n * Calls f for each value in a collection. If any call returns true this returns\n * true (without checking the rest). If all returns false this returns false.\n *\n * @param {S} col The collection-like object.\n * @param {function(this:T,?,?,S):boolean} f The function to call for every\n *     value. This function takes 3 arguments (the value, the key or undefined\n *     if the collection has no notion of keys, and the collection) and should\n *     return a boolean.\n * @param {T=} opt_obj  The object to be used as the value of 'this'\n *     within `f`.\n * @return {boolean} True if any value passes the test.\n * @template T,S\n */\ngoog.structs.some = function(col, f, opt_obj) {\n  if (typeof col.some == 'function') {\n    return col.some(f, opt_obj);\n  }\n  if (goog.isArrayLike(col) || typeof col === 'string') {\n    return goog.array.some(/** @type {!Array<?>} */ (col), f, opt_obj);\n  }\n  var keys = goog.structs.getKeys(col);\n  var values = goog.structs.getValues(col);\n  var l = values.length;\n  for (var i = 0; i < l; i++) {\n    if (f.call(/** @type {?} */ (opt_obj), values[i], keys && keys[i], col)) {\n      return true;\n    }\n  }\n  return false;\n};\n\n\n/**\n * Calls f for each value in a collection. If all calls return true this return\n * true this returns true. If any returns false this returns false at this point\n *  and does not continue to check the remaining values.\n *\n * @param {S} col The collection-like object.\n * @param {function(this:T,?,?,S):boolean} f The function to call for every\n *     value. This function takes 3 arguments (the value, the key or\n *     undefined if the collection has no notion of keys, and the collection)\n *     and should return a boolean.\n * @param {T=} opt_obj  The object to be used as the value of 'this'\n *     within `f`.\n * @return {boolean} True if all key-value pairs pass the test.\n * @template T,S\n */\ngoog.structs.every = function(col, f, opt_obj) {\n  if (typeof col.every == 'function') {\n    return col.every(f, opt_obj);\n  }\n  if (goog.isArrayLike(col) || typeof col === 'string') {\n    return goog.array.every(/** @type {!Array<?>} */ (col), f, opt_obj);\n  }\n  var keys = goog.structs.getKeys(col);\n  var values = goog.structs.getValues(col);\n  var l = values.length;\n  for (var i = 0; i < l; i++) {\n    if (!f.call(/** @type {?} */ (opt_obj), values[i], keys && keys[i], col)) {\n      return false;\n    }\n  }\n  return true;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Datastructure: Hash Map.\n *\n *\n * This file contains an implementation of a Map structure. It implements a lot\n * of the methods used in goog.structs so those functions work on hashes. This\n * is best suited for complex key types. For simple keys such as numbers and\n * strings consider using the lighter-weight utilities in goog.object.\n */\n\n\ngoog.provide('goog.structs.Map');\n\ngoog.require('goog.iter.Iterator');\ngoog.require('goog.iter.StopIteration');\n\n\n\n/**\n * Class for Hash Map datastructure.\n * @param {*=} opt_map Map or Object to initialize the map with.\n * @param {...*} var_args If 2 or more arguments are present then they\n *     will be used as key-value pairs.\n * @constructor\n * @template K, V\n * @deprecated This type is misleading: use ES6 Map instead.\n */\ngoog.structs.Map = function(opt_map, var_args) {\n\n  /**\n   * Underlying JS object used to implement the map.\n   * @private {!Object}\n   */\n  this.map_ = {};\n\n  /**\n   * An array of keys. This is necessary for two reasons:\n   *   1. Iterating the keys using for (var key in this.map_) allocates an\n   *      object for every key in IE which is really bad for IE6 GC perf.\n   *   2. Without a side data structure, we would need to escape all the keys\n   *      as that would be the only way we could tell during iteration if the\n   *      key was an internal key or a property of the object.\n   *\n   * This array can contain deleted keys so it's necessary to check the map\n   * as well to see if the key is still in the map (this doesn't require a\n   * memory allocation in IE).\n   * @private {!Array<string>}\n   */\n  this.keys_ = [];\n\n  /**\n   * The number of key value pairs in the map.\n   * @private {number}\n   */\n  this.count_ = 0;\n\n  /**\n   * Version used to detect changes while iterating.\n   * @private {number}\n   */\n  this.version_ = 0;\n\n  var argLength = arguments.length;\n\n  if (argLength > 1) {\n    if (argLength % 2) {\n      throw new Error('Uneven number of arguments');\n    }\n    for (var i = 0; i < argLength; i += 2) {\n      this.set(arguments[i], arguments[i + 1]);\n    }\n  } else if (opt_map) {\n    this.addAll(/** @type {!Object} */ (opt_map));\n  }\n};\n\n\n/**\n * @return {number} The number of key-value pairs in the map.\n */\ngoog.structs.Map.prototype.getCount = function() {\n  return this.count_;\n};\n\n\n/**\n * Returns the values of the map.\n * @return {!Array<V>} The values in the map.\n */\ngoog.structs.Map.prototype.getValues = function() {\n  this.cleanupKeysArray_();\n\n  var rv = [];\n  for (var i = 0; i < this.keys_.length; i++) {\n    var key = this.keys_[i];\n    rv.push(this.map_[key]);\n  }\n  return rv;\n};\n\n\n/**\n * Returns the keys of the map.\n * @return {!Array<string>} Array of string values.\n */\ngoog.structs.Map.prototype.getKeys = function() {\n  this.cleanupKeysArray_();\n  return /** @type {!Array<string>} */ (this.keys_.concat());\n};\n\n\n/**\n * Whether the map contains the given key.\n * @param {*} key The key to check for.\n * @return {boolean} Whether the map contains the key.\n */\ngoog.structs.Map.prototype.containsKey = function(key) {\n  return goog.structs.Map.hasKey_(this.map_, key);\n};\n\n\n/**\n * Whether the map contains the given value. This is O(n).\n * @param {V} val The value to check for.\n * @return {boolean} Whether the map contains the value.\n */\ngoog.structs.Map.prototype.containsValue = function(val) {\n  for (var i = 0; i < this.keys_.length; i++) {\n    var key = this.keys_[i];\n    if (goog.structs.Map.hasKey_(this.map_, key) && this.map_[key] == val) {\n      return true;\n    }\n  }\n  return false;\n};\n\n\n/**\n * Whether this map is equal to the argument map.\n * @param {goog.structs.Map} otherMap The map against which to test equality.\n * @param {function(V, V): boolean=} opt_equalityFn Optional equality function\n *     to test equality of values. If not specified, this will test whether\n *     the values contained in each map are identical objects.\n * @return {boolean} Whether the maps are equal.\n */\ngoog.structs.Map.prototype.equals = function(otherMap, opt_equalityFn) {\n  if (this === otherMap) {\n    return true;\n  }\n\n  if (this.count_ != otherMap.getCount()) {\n    return false;\n  }\n\n  var equalityFn = opt_equalityFn || goog.structs.Map.defaultEquals;\n\n  this.cleanupKeysArray_();\n  for (var key, i = 0; key = this.keys_[i]; i++) {\n    if (!equalityFn(this.get(key), otherMap.get(key))) {\n      return false;\n    }\n  }\n\n  return true;\n};\n\n\n/**\n * Default equality test for values.\n * @param {*} a The first value.\n * @param {*} b The second value.\n * @return {boolean} Whether a and b reference the same object.\n */\ngoog.structs.Map.defaultEquals = function(a, b) {\n  return a === b;\n};\n\n\n/**\n * @return {boolean} Whether the map is empty.\n */\ngoog.structs.Map.prototype.isEmpty = function() {\n  return this.count_ == 0;\n};\n\n\n/**\n * Removes all key-value pairs from the map.\n */\ngoog.structs.Map.prototype.clear = function() {\n  this.map_ = {};\n  this.keys_.length = 0;\n  this.count_ = 0;\n  this.version_ = 0;\n};\n\n\n/**\n * Removes a key-value pair based on the key. This is O(logN) amortized due to\n * updating the keys array whenever the count becomes half the size of the keys\n * in the keys array.\n * @param {*} key  The key to remove.\n * @return {boolean} Whether object was removed.\n */\ngoog.structs.Map.prototype.remove = function(key) {\n  if (goog.structs.Map.hasKey_(this.map_, key)) {\n    delete this.map_[key];\n    this.count_--;\n    this.version_++;\n\n    // clean up the keys array if the threshold is hit\n    if (this.keys_.length > 2 * this.count_) {\n      this.cleanupKeysArray_();\n    }\n\n    return true;\n  }\n  return false;\n};\n\n\n/**\n * Cleans up the temp keys array by removing entries that are no longer in the\n * map.\n * @private\n */\ngoog.structs.Map.prototype.cleanupKeysArray_ = function() {\n  if (this.count_ != this.keys_.length) {\n    // First remove keys that are no longer in the map.\n    var srcIndex = 0;\n    var destIndex = 0;\n    while (srcIndex < this.keys_.length) {\n      var key = this.keys_[srcIndex];\n      if (goog.structs.Map.hasKey_(this.map_, key)) {\n        this.keys_[destIndex++] = key;\n      }\n      srcIndex++;\n    }\n    this.keys_.length = destIndex;\n  }\n\n  if (this.count_ != this.keys_.length) {\n    // If the count still isn't correct, that means we have duplicates. This can\n    // happen when the same key is added and removed multiple times. Now we have\n    // to allocate one extra Object to remove the duplicates. This could have\n    // been done in the first pass, but in the common case, we can avoid\n    // allocating an extra object by only doing this when necessary.\n    var seen = {};\n    var srcIndex = 0;\n    var destIndex = 0;\n    while (srcIndex < this.keys_.length) {\n      var key = this.keys_[srcIndex];\n      if (!(goog.structs.Map.hasKey_(seen, key))) {\n        this.keys_[destIndex++] = key;\n        seen[key] = 1;\n      }\n      srcIndex++;\n    }\n    this.keys_.length = destIndex;\n  }\n};\n\n\n/**\n * Returns the value for the given key.  If the key is not found and the default\n * value is not given this will return `undefined`.\n * @param {*} key The key to get the value for.\n * @param {DEFAULT=} opt_val The value to return if no item is found for the\n *     given key, defaults to undefined.\n * @return {V|DEFAULT} The value for the given key.\n * @template DEFAULT\n */\ngoog.structs.Map.prototype.get = function(key, opt_val) {\n  if (goog.structs.Map.hasKey_(this.map_, key)) {\n    return this.map_[key];\n  }\n  return opt_val;\n};\n\n\n/**\n * Adds a key-value pair to the map.\n * @param {*} key The key.\n * @param {V} value The value to add.\n * @return {*} Some subclasses return a value.\n */\ngoog.structs.Map.prototype.set = function(key, value) {\n  if (!(goog.structs.Map.hasKey_(this.map_, key))) {\n    this.count_++;\n    // TODO(johnlenz): This class lies, it claims to return an array of string\n    // keys, but instead returns the original object used.\n    this.keys_.push(/** @type {?} */ (key));\n    // Only change the version if we add a new key.\n    this.version_++;\n  }\n  this.map_[key] = value;\n};\n\n\n/**\n * Adds multiple key-value pairs from another goog.structs.Map or Object.\n * @param {?Object} map Object containing the data to add.\n */\ngoog.structs.Map.prototype.addAll = function(map) {\n  if (map instanceof goog.structs.Map) {\n    var keys = map.getKeys();\n    for (var i = 0; i < keys.length; i++) {\n      this.set(keys[i], map.get(keys[i]));\n    }\n  } else {\n    for (var key in map) {\n      this.set(key, map[key]);\n    }\n  }\n};\n\n\n/**\n * Calls the given function on each entry in the map.\n * @param {function(this:T, V, K, goog.structs.Map<K,V>)} f\n * @param {T=} opt_obj The value of \"this\" inside f.\n * @template T\n */\ngoog.structs.Map.prototype.forEach = function(f, opt_obj) {\n  var keys = this.getKeys();\n  for (var i = 0; i < keys.length; i++) {\n    var key = keys[i];\n    var value = this.get(key);\n    f.call(opt_obj, value, key, this);\n  }\n};\n\n\n/**\n * Clones a map and returns a new map.\n * @return {!goog.structs.Map} A new map with the same key-value pairs.\n */\ngoog.structs.Map.prototype.clone = function() {\n  return new goog.structs.Map(this);\n};\n\n\n/**\n * Returns a new map in which all the keys and values are interchanged\n * (keys become values and values become keys). If multiple keys map to the\n * same value, the chosen transposed value is implementation-dependent.\n *\n * It acts very similarly to {goog.object.transpose(Object)}.\n *\n * @return {!goog.structs.Map} The transposed map.\n */\ngoog.structs.Map.prototype.transpose = function() {\n  var transposed = new goog.structs.Map();\n  for (var i = 0; i < this.keys_.length; i++) {\n    var key = this.keys_[i];\n    var value = this.map_[key];\n    transposed.set(value, key);\n  }\n\n  return transposed;\n};\n\n\n/**\n * @return {!Object} Object representation of the map.\n */\ngoog.structs.Map.prototype.toObject = function() {\n  this.cleanupKeysArray_();\n  var obj = {};\n  for (var i = 0; i < this.keys_.length; i++) {\n    var key = this.keys_[i];\n    obj[key] = this.map_[key];\n  }\n  return obj;\n};\n\n\n/**\n * Returns an iterator that iterates over the keys in the map.  Removal of keys\n * while iterating might have undesired side effects.\n * @return {!goog.iter.Iterator} An iterator over the keys in the map.\n */\ngoog.structs.Map.prototype.getKeyIterator = function() {\n  return this.__iterator__(true);\n};\n\n\n/**\n * Returns an iterator that iterates over the values in the map.  Removal of\n * keys while iterating might have undesired side effects.\n * @return {!goog.iter.Iterator} An iterator over the values in the map.\n */\ngoog.structs.Map.prototype.getValueIterator = function() {\n  return this.__iterator__(false);\n};\n\n\n/**\n * Returns an iterator that iterates over the values or the keys in the map.\n * This throws an exception if the map was mutated since the iterator was\n * created.\n * @param {boolean=} opt_keys True to iterate over the keys. False to iterate\n *     over the values.  The default value is false.\n * @return {!goog.iter.Iterator} An iterator over the values or keys in the map.\n */\ngoog.structs.Map.prototype.__iterator__ = function(opt_keys) {\n  // Clean up keys to minimize the risk of iterating over dead keys.\n  this.cleanupKeysArray_();\n\n  var i = 0;\n  var version = this.version_;\n  var selfObj = this;\n\n  var newIter = new goog.iter.Iterator;\n  newIter.next = function() {\n    if (version != selfObj.version_) {\n      throw new Error('The map has changed since the iterator was created');\n    }\n    if (i >= selfObj.keys_.length) {\n      throw goog.iter.StopIteration;\n    }\n    var key = selfObj.keys_[i++];\n    return opt_keys ? key : selfObj.map_[key];\n  };\n  return newIter;\n};\n\n\n/**\n * Safe way to test for hasOwnProperty.  It even allows testing for\n * 'hasOwnProperty'.\n * @param {!Object} obj The object to test for presence of the given key.\n * @param {*} key The key to check for.\n * @return {boolean} Whether the object has the key.\n * @private\n */\ngoog.structs.Map.hasKey_ = function(obj, key) {\n  return Object.prototype.hasOwnProperty.call(obj, key);\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Simple utilities for dealing with URI strings.\n *\n * This is intended to be a lightweight alternative to constructing goog.Uri\n * objects.  Whereas goog.Uri adds several kilobytes to the binary regardless\n * of how much of its functionality you use, this is designed to be a set of\n * mostly-independent utilities so that the compiler includes only what is\n * necessary for the task.  Estimated savings of porting is 5k pre-gzip and\n * 1.5k post-gzip.  To ensure the savings remain, future developers should\n * avoid adding new functionality to existing functions, but instead create\n * new ones and factor out shared code.\n *\n * Many of these utilities have limited functionality, tailored to common\n * cases.  The query parameter utilities assume that the parameter keys are\n * already encoded, since most keys are compile-time alphanumeric strings.  The\n * query parameter mutation utilities also do not tolerate fragment identifiers.\n *\n * By design, these functions can be slower than goog.Uri equivalents.\n * Repeated calls to some of functions may be quadratic in behavior for IE,\n * although the effect is somewhat limited given the 2kb limit.\n *\n * One advantage of the limited functionality here is that this approach is\n * less sensitive to differences in URI encodings than goog.Uri, since these\n * functions operate on strings directly, rather than decoding them and\n * then re-encoding.\n *\n * Uses features of RFC 3986 for parsing/formatting URIs:\n *   http://www.ietf.org/rfc/rfc3986.txt\n */\n\ngoog.provide('goog.uri.utils');\ngoog.provide('goog.uri.utils.ComponentIndex');\ngoog.provide('goog.uri.utils.QueryArray');\ngoog.provide('goog.uri.utils.QueryValue');\ngoog.provide('goog.uri.utils.StandardQueryParam');\n\ngoog.require('goog.asserts');\ngoog.require('goog.string');\n\n\n/**\n * Character codes inlined to avoid object allocations due to charCode.\n * @enum {number}\n * @private\n */\ngoog.uri.utils.CharCode_ = {\n  AMPERSAND: 38,\n  EQUAL: 61,\n  HASH: 35,\n  QUESTION: 63\n};\n\n\n/**\n * Builds a URI string from already-encoded parts.\n *\n * No encoding is performed.  Any component may be omitted as either null or\n * undefined.\n *\n * @param {?string=} opt_scheme The scheme such as 'http'.\n * @param {?string=} opt_userInfo The user name before the '@'.\n * @param {?string=} opt_domain The domain such as 'www.google.com', already\n *     URI-encoded.\n * @param {(string|number|null)=} opt_port The port number.\n * @param {?string=} opt_path The path, already URI-encoded.  If it is not\n *     empty, it must begin with a slash.\n * @param {?string=} opt_queryData The URI-encoded query data.\n * @param {?string=} opt_fragment The URI-encoded fragment identifier.\n * @return {string} The fully combined URI.\n */\ngoog.uri.utils.buildFromEncodedParts = function(\n    opt_scheme, opt_userInfo, opt_domain, opt_port, opt_path, opt_queryData,\n    opt_fragment) {\n  var out = '';\n\n  if (opt_scheme) {\n    out += opt_scheme + ':';\n  }\n\n  if (opt_domain) {\n    out += '//';\n\n    if (opt_userInfo) {\n      out += opt_userInfo + '@';\n    }\n\n    out += opt_domain;\n\n    if (opt_port) {\n      out += ':' + opt_port;\n    }\n  }\n\n  if (opt_path) {\n    out += opt_path;\n  }\n\n  if (opt_queryData) {\n    out += '?' + opt_queryData;\n  }\n\n  if (opt_fragment) {\n    out += '#' + opt_fragment;\n  }\n\n  return out;\n};\n\n\n/**\n * A regular expression for breaking a URI into its component parts.\n *\n * {@link http://www.ietf.org/rfc/rfc3986.txt} says in Appendix B\n * As the \"first-match-wins\" algorithm is identical to the \"greedy\"\n * disambiguation method used by POSIX regular expressions, it is natural and\n * commonplace to use a regular expression for parsing the potential five\n * components of a URI reference.\n *\n * The following line is the regular expression for breaking-down a\n * well-formed URI reference into its components.\n *\n * <pre>\n * ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?\n *  12            3  4          5       6  7        8 9\n * </pre>\n *\n * The numbers in the second line above are only to assist readability; they\n * indicate the reference points for each subexpression (i.e., each paired\n * parenthesis). We refer to the value matched for subexpression <n> as $<n>.\n * For example, matching the above expression to\n * <pre>\n *     http://www.ics.uci.edu/pub/ietf/uri/#Related\n * </pre>\n * results in the following subexpression matches:\n * <pre>\n *    $1 = http:\n *    $2 = http\n *    $3 = //www.ics.uci.edu\n *    $4 = www.ics.uci.edu\n *    $5 = /pub/ietf/uri/\n *    $6 = <undefined>\n *    $7 = <undefined>\n *    $8 = #Related\n *    $9 = Related\n * </pre>\n * where <undefined> indicates that the component is not present, as is the\n * case for the query component in the above example. Therefore, we can\n * determine the value of the five components as\n * <pre>\n *    scheme    = $2\n *    authority = $4\n *    path      = $5\n *    query     = $7\n *    fragment  = $9\n * </pre>\n *\n * The regular expression has been modified slightly to expose the\n * userInfo, domain, and port separately from the authority.\n * The modified version yields\n * <pre>\n *    $1 = http              scheme\n *    $2 = <undefined>       userInfo -\\\n *    $3 = www.ics.uci.edu   domain     | authority\n *    $4 = <undefined>       port     -/\n *    $5 = /pub/ietf/uri/    path\n *    $6 = <undefined>       query without ?\n *    $7 = Related           fragment without #\n * </pre>\n *\n * TODO(user): separate out the authority terminating characters once this\n * file is moved to ES6.\n * @type {!RegExp}\n * @private\n */\ngoog.uri.utils.splitRe_ = new RegExp(\n    '^' +  // Anchor against the entire string.\n    '(?:' +\n    '([^:/?#.]+)' +  // scheme - ignore special characters\n                     // used by other URL parts such as :,\n                     // ?, /, #, and .\n    ':)?' +\n    '(?://' +\n    '(?:([^\\\\\\\\/?#]*)@)?' +  // userInfo\n    '([^\\\\\\\\/?#]*?)' +       // domain\n    '(?::([0-9]+))?' +       // port\n    '(?=[\\\\\\\\/?#]|$)' +      // authority-terminating character.\n    ')?' +\n    '([^?#]+)?' +          // path\n    '(?:\\\\?([^#]*))?' +    // query\n    '(?:#([\\\\s\\\\S]*))?' +  // fragment. Can't use '.*' with 's' flag as Firefox\n                           // doesn't support the flag, and can't use an\n                           // \"everything set\" ([^]) as IE10 doesn't match any\n                           // characters with it.\n    '$');\n\n\n/**\n * The index of each URI component in the return value of goog.uri.utils.split.\n * @enum {number}\n */\ngoog.uri.utils.ComponentIndex = {\n  SCHEME: 1,\n  USER_INFO: 2,\n  DOMAIN: 3,\n  PORT: 4,\n  PATH: 5,\n  QUERY_DATA: 6,\n  FRAGMENT: 7\n};\n\n/**\n * @type {?function(string)}\n * @private\n */\ngoog.uri.utils.urlPackageSupportLoggingHandler_ = null;\n\n/**\n * @param {?function(string)} handler The handler function to call when a URI\n *     with a protocol that is better supported by the Closure URL package is\n *     detected.\n */\ngoog.uri.utils.setUrlPackageSupportLoggingHandler = function(handler) {\n  goog.uri.utils.urlPackageSupportLoggingHandler_ = handler;\n};\n\n/**\n * Splits a URI into its component parts.\n *\n * Each component can be accessed via the component indices; for example:\n * <pre>\n * goog.uri.utils.split(someStr)[goog.uri.utils.ComponentIndex.QUERY_DATA];\n * </pre>\n *\n * @param {string} uri The URI string to examine.\n * @return {!Array<string|undefined>} Each component still URI-encoded.\n *     Each component that is present will contain the encoded value, whereas\n *     components that are not present will be undefined or empty, depending\n *     on the browser's regular expression implementation.  Never null, since\n *     arbitrary strings may still look like path names.\n */\ngoog.uri.utils.split = function(uri) {\n  // See @return comment -- never null.\n  var result = /** @type {!Array<string|undefined>} */ (\n      uri.match(goog.uri.utils.splitRe_));\n  if (goog.uri.utils.urlPackageSupportLoggingHandler_ &&\n      ['http', 'https', 'ws', 'wss',\n       'ftp'].indexOf(result[goog.uri.utils.ComponentIndex.SCHEME]) >= 0) {\n    goog.uri.utils.urlPackageSupportLoggingHandler_(uri);\n  }\n  return result;\n};\n\n\n/**\n * @param {?string} uri A possibly null string.\n * @param {boolean=} opt_preserveReserved If true, percent-encoding of RFC-3986\n *     reserved characters will not be removed.\n * @return {?string} The string URI-decoded, or null if uri is null.\n * @private\n */\ngoog.uri.utils.decodeIfPossible_ = function(uri, opt_preserveReserved) {\n  if (!uri) {\n    return uri;\n  }\n\n  return opt_preserveReserved ? decodeURI(uri) : decodeURIComponent(uri);\n};\n\n\n/**\n * Gets a URI component by index.\n *\n * It is preferred to use the getPathEncoded() variety of functions ahead,\n * since they are more readable.\n *\n * @param {goog.uri.utils.ComponentIndex} componentIndex The component index.\n * @param {string} uri The URI to examine.\n * @return {?string} The still-encoded component, or null if the component\n *     is not present.\n * @private\n */\ngoog.uri.utils.getComponentByIndex_ = function(componentIndex, uri) {\n  // Convert undefined, null, and empty string into null.\n  return goog.uri.utils.split(uri)[componentIndex] || null;\n};\n\n\n/**\n * @param {string} uri The URI to examine.\n * @return {?string} The protocol or scheme, or null if none.  Does not\n *     include trailing colons or slashes.\n */\ngoog.uri.utils.getScheme = function(uri) {\n  return goog.uri.utils.getComponentByIndex_(\n      goog.uri.utils.ComponentIndex.SCHEME, uri);\n};\n\n\n/**\n * Gets the effective scheme for the URL.  If the URL is relative then the\n * scheme is derived from the page's location.\n * @param {string} uri The URI to examine.\n * @return {string} The protocol or scheme, always lower case.\n */\ngoog.uri.utils.getEffectiveScheme = function(uri) {\n  var scheme = goog.uri.utils.getScheme(uri);\n  if (!scheme && goog.global.self && goog.global.self.location) {\n    var protocol = goog.global.self.location.protocol;\n    scheme = protocol.substr(0, protocol.length - 1);\n  }\n  // NOTE: When called from a web worker in Firefox 3.5, location may be null.\n  // All other browsers with web workers support self.location from the worker.\n  return scheme ? scheme.toLowerCase() : '';\n};\n\n\n/**\n * @param {string} uri The URI to examine.\n * @return {?string} The user name still encoded, or null if none.\n */\ngoog.uri.utils.getUserInfoEncoded = function(uri) {\n  return goog.uri.utils.getComponentByIndex_(\n      goog.uri.utils.ComponentIndex.USER_INFO, uri);\n};\n\n\n/**\n * @param {string} uri The URI to examine.\n * @return {?string} The decoded user info, or null if none.\n */\ngoog.uri.utils.getUserInfo = function(uri) {\n  return goog.uri.utils.decodeIfPossible_(\n      goog.uri.utils.getUserInfoEncoded(uri));\n};\n\n\n/**\n * @param {string} uri The URI to examine.\n * @return {?string} The domain name still encoded, or null if none.\n */\ngoog.uri.utils.getDomainEncoded = function(uri) {\n  return goog.uri.utils.getComponentByIndex_(\n      goog.uri.utils.ComponentIndex.DOMAIN, uri);\n};\n\n\n/**\n * @param {string} uri The URI to examine.\n * @return {?string} The decoded domain, or null if none.\n */\ngoog.uri.utils.getDomain = function(uri) {\n  return goog.uri.utils.decodeIfPossible_(\n      goog.uri.utils.getDomainEncoded(uri), true /* opt_preserveReserved */);\n};\n\n\n/**\n * @param {string} uri The URI to examine.\n * @return {?number} The port number, or null if none.\n */\ngoog.uri.utils.getPort = function(uri) {\n  // Coerce to a number.  If the result of getComponentByIndex_ is null or\n  // non-numeric, the number coersion yields NaN.  This will then return\n  // null for all non-numeric cases (though also zero, which isn't a relevant\n  // port number).\n  return Number(\n             goog.uri.utils.getComponentByIndex_(\n                 goog.uri.utils.ComponentIndex.PORT, uri)) ||\n      null;\n};\n\n\n/**\n * @param {string} uri The URI to examine.\n * @return {?string} The path still encoded, or null if none. Includes the\n *     leading slash, if any.\n */\ngoog.uri.utils.getPathEncoded = function(uri) {\n  return goog.uri.utils.getComponentByIndex_(\n      goog.uri.utils.ComponentIndex.PATH, uri);\n};\n\n\n/**\n * @param {string} uri The URI to examine.\n * @return {?string} The decoded path, or null if none.  Includes the leading\n *     slash, if any.\n */\ngoog.uri.utils.getPath = function(uri) {\n  return goog.uri.utils.decodeIfPossible_(\n      goog.uri.utils.getPathEncoded(uri), true /* opt_preserveReserved */);\n};\n\n\n/**\n * @param {string} uri The URI to examine.\n * @return {?string} The query data still encoded, or null if none.  Does not\n *     include the question mark itself.\n */\ngoog.uri.utils.getQueryData = function(uri) {\n  return goog.uri.utils.getComponentByIndex_(\n      goog.uri.utils.ComponentIndex.QUERY_DATA, uri);\n};\n\n\n/**\n * @param {string} uri The URI to examine.\n * @return {?string} The fragment identifier, or null if none.  Does not\n *     include the hash mark itself.\n */\ngoog.uri.utils.getFragmentEncoded = function(uri) {\n  // The hash mark may not appear in any other part of the URL.\n  var hashIndex = uri.indexOf('#');\n  return hashIndex < 0 ? null : uri.substr(hashIndex + 1);\n};\n\n\n/**\n * @param {string} uri The URI to examine.\n * @param {?string} fragment The encoded fragment identifier, or null if none.\n *     Does not include the hash mark itself.\n * @return {string} The URI with the fragment set.\n */\ngoog.uri.utils.setFragmentEncoded = function(uri, fragment) {\n  return goog.uri.utils.removeFragment(uri) + (fragment ? '#' + fragment : '');\n};\n\n\n/**\n * @param {string} uri The URI to examine.\n * @return {?string} The decoded fragment identifier, or null if none.  Does\n *     not include the hash mark.\n */\ngoog.uri.utils.getFragment = function(uri) {\n  return goog.uri.utils.decodeIfPossible_(\n      goog.uri.utils.getFragmentEncoded(uri));\n};\n\n\n/**\n * Extracts everything up to the port of the URI.\n * @param {string} uri The URI string.\n * @return {string} Everything up to and including the port.\n */\ngoog.uri.utils.getHost = function(uri) {\n  var pieces = goog.uri.utils.split(uri);\n  return goog.uri.utils.buildFromEncodedParts(\n      pieces[goog.uri.utils.ComponentIndex.SCHEME],\n      pieces[goog.uri.utils.ComponentIndex.USER_INFO],\n      pieces[goog.uri.utils.ComponentIndex.DOMAIN],\n      pieces[goog.uri.utils.ComponentIndex.PORT]);\n};\n\n\n/**\n * Returns the origin for a given URL.\n * @param {string} uri The URI string.\n * @return {string} Everything up to and including the port.\n */\ngoog.uri.utils.getOrigin = function(uri) {\n  var pieces = goog.uri.utils.split(uri);\n  return goog.uri.utils.buildFromEncodedParts(\n      pieces[goog.uri.utils.ComponentIndex.SCHEME], null /* opt_userInfo */,\n      pieces[goog.uri.utils.ComponentIndex.DOMAIN],\n      pieces[goog.uri.utils.ComponentIndex.PORT]);\n};\n\n\n/**\n * Extracts the path of the URL and everything after.\n * @param {string} uri The URI string.\n * @return {string} The URI, starting at the path and including the query\n *     parameters and fragment identifier.\n */\ngoog.uri.utils.getPathAndAfter = function(uri) {\n  var pieces = goog.uri.utils.split(uri);\n  return goog.uri.utils.buildFromEncodedParts(\n      null, null, null, null, pieces[goog.uri.utils.ComponentIndex.PATH],\n      pieces[goog.uri.utils.ComponentIndex.QUERY_DATA],\n      pieces[goog.uri.utils.ComponentIndex.FRAGMENT]);\n};\n\n\n/**\n * Gets the URI with the fragment identifier removed.\n * @param {string} uri The URI to examine.\n * @return {string} Everything preceding the hash mark.\n */\ngoog.uri.utils.removeFragment = function(uri) {\n  // The hash mark may not appear in any other part of the URL.\n  var hashIndex = uri.indexOf('#');\n  return hashIndex < 0 ? uri : uri.substr(0, hashIndex);\n};\n\n\n/**\n * Ensures that two URI's have the exact same domain, scheme, and port.\n *\n * Unlike the version in goog.Uri, this checks protocol, and therefore is\n * suitable for checking against the browser's same-origin policy.\n *\n * @param {string} uri1 The first URI.\n * @param {string} uri2 The second URI.\n * @return {boolean} Whether they have the same scheme, domain and port.\n */\ngoog.uri.utils.haveSameDomain = function(uri1, uri2) {\n  var pieces1 = goog.uri.utils.split(uri1);\n  var pieces2 = goog.uri.utils.split(uri2);\n  return pieces1[goog.uri.utils.ComponentIndex.DOMAIN] ==\n      pieces2[goog.uri.utils.ComponentIndex.DOMAIN] &&\n      pieces1[goog.uri.utils.ComponentIndex.SCHEME] ==\n      pieces2[goog.uri.utils.ComponentIndex.SCHEME] &&\n      pieces1[goog.uri.utils.ComponentIndex.PORT] ==\n      pieces2[goog.uri.utils.ComponentIndex.PORT];\n};\n\n\n/**\n * Asserts that there are no fragment or query identifiers, only in uncompiled\n * mode.\n * @param {string} uri The URI to examine.\n * @private\n */\ngoog.uri.utils.assertNoFragmentsOrQueries_ = function(uri) {\n  goog.asserts.assert(\n      uri.indexOf('#') < 0 && uri.indexOf('?') < 0,\n      'goog.uri.utils: Fragment or query identifiers are not supported: [%s]',\n      uri);\n};\n\n\n/**\n * Supported query parameter values by the parameter serializing utilities.\n *\n * If a value is null or undefined, the key-value pair is skipped, as an easy\n * way to omit parameters conditionally.  Non-array parameters are converted\n * to a string and URI encoded.  Array values are expanded into multiple\n * &key=value pairs, with each element stringized and URI-encoded.\n *\n * @typedef {*}\n */\ngoog.uri.utils.QueryValue;\n\n\n/**\n * An array representing a set of query parameters with alternating keys\n * and values.\n *\n * Keys are assumed to be URI encoded already and live at even indices.  See\n * goog.uri.utils.QueryValue for details on how parameter values are encoded.\n *\n * Example:\n * <pre>\n * var data = [\n *   // Simple param: ?name=BobBarker\n *   'name', 'BobBarker',\n *   // Conditional param -- may be omitted entirely.\n *   'specialDietaryNeeds', hasDietaryNeeds() ? getDietaryNeeds() : null,\n *   // Multi-valued param: &house=LosAngeles&house=NewYork&house=null\n *   'house', ['LosAngeles', 'NewYork', null]\n * ];\n * </pre>\n *\n * @typedef {!Array<string|goog.uri.utils.QueryValue>}\n */\ngoog.uri.utils.QueryArray;\n\n\n/**\n * Parses encoded query parameters and calls callback function for every\n * parameter found in the string.\n *\n * Missing value of parameter (e.g. “…&key&…”) is treated as if the value was an\n * empty string.  Keys may be empty strings (e.g. “…&=value&…”) which also means\n * that “…&=&…” and “…&&…” will result in an empty key and value.\n *\n * @param {string} encodedQuery Encoded query string excluding question mark at\n *     the beginning.\n * @param {function(string, string)} callback Function called for every\n *     parameter found in query string.  The first argument (name) will not be\n *     urldecoded (so the function is consistent with buildQueryData), but the\n *     second will.  If the parameter has no value (i.e. “=” was not present)\n *     the second argument (value) will be an empty string.\n */\ngoog.uri.utils.parseQueryData = function(encodedQuery, callback) {\n  if (!encodedQuery) {\n    return;\n  }\n  var pairs = encodedQuery.split('&');\n  for (var i = 0; i < pairs.length; i++) {\n    var indexOfEquals = pairs[i].indexOf('=');\n    var name = null;\n    var value = null;\n    if (indexOfEquals >= 0) {\n      name = pairs[i].substring(0, indexOfEquals);\n      value = pairs[i].substring(indexOfEquals + 1);\n    } else {\n      name = pairs[i];\n    }\n    callback(name, value ? goog.string.urlDecode(value) : '');\n  }\n};\n\n\n/**\n * Split the URI into 3 parts where the [1] is the queryData without a leading\n * '?'. For example, the URI http://foo.com/bar?a=b#abc returns\n * ['http://foo.com/bar','a=b','#abc'].\n * @param {string} uri The URI to parse.\n * @return {!Array<string>} An array representation of uri of length 3 where the\n *     middle value is the queryData without a leading '?'.\n * @private\n */\ngoog.uri.utils.splitQueryData_ = function(uri) {\n  // Find the query data and hash.\n  var hashIndex = uri.indexOf('#');\n  if (hashIndex < 0) {\n    hashIndex = uri.length;\n  }\n  var questionIndex = uri.indexOf('?');\n  var queryData;\n  if (questionIndex < 0 || questionIndex > hashIndex) {\n    questionIndex = hashIndex;\n    queryData = '';\n  } else {\n    queryData = uri.substring(questionIndex + 1, hashIndex);\n  }\n  return [uri.substr(0, questionIndex), queryData, uri.substr(hashIndex)];\n};\n\n\n/**\n * Join an array created by splitQueryData_ back into a URI.\n * @param {!Array<string>} parts A URI in the form generated by splitQueryData_.\n * @return {string} The joined URI.\n * @private\n */\ngoog.uri.utils.joinQueryData_ = function(parts) {\n  return parts[0] + (parts[1] ? '?' + parts[1] : '') + parts[2];\n};\n\n\n/**\n * @param {string} queryData\n * @param {string} newData\n * @return {string}\n * @private\n */\ngoog.uri.utils.appendQueryData_ = function(queryData, newData) {\n  if (!newData) {\n    return queryData;\n  }\n  return queryData ? queryData + '&' + newData : newData;\n};\n\n\n/**\n * @param {string} uri\n * @param {string} queryData\n * @return {string}\n * @private\n */\ngoog.uri.utils.appendQueryDataToUri_ = function(uri, queryData) {\n  if (!queryData) {\n    return uri;\n  }\n  var parts = goog.uri.utils.splitQueryData_(uri);\n  parts[1] = goog.uri.utils.appendQueryData_(parts[1], queryData);\n  return goog.uri.utils.joinQueryData_(parts);\n};\n\n\n/**\n * Appends key=value pairs to an array, supporting multi-valued objects.\n * @param {*} key The key prefix.\n * @param {goog.uri.utils.QueryValue} value The value to serialize.\n * @param {!Array<string>} pairs The array to which the 'key=value' strings\n *     should be appended.\n * @private\n */\ngoog.uri.utils.appendKeyValuePairs_ = function(key, value, pairs) {\n  goog.asserts.assertString(key);\n  if (Array.isArray(value)) {\n    // Convince the compiler it's an array.\n    goog.asserts.assertArray(value);\n    for (var j = 0; j < value.length; j++) {\n      // Convert to string explicitly, to short circuit the null and array\n      // logic in this function -- this ensures that null and undefined get\n      // written as literal 'null' and 'undefined', and arrays don't get\n      // expanded out but instead encoded in the default way.\n      goog.uri.utils.appendKeyValuePairs_(key, String(value[j]), pairs);\n    }\n  } else if (value != null) {\n    // Skip a top-level null or undefined entirely.\n    pairs.push(\n        key +\n        // Check for empty string. Zero gets encoded into the url as literal\n        // strings.  For empty string, skip the equal sign, to be consistent\n        // with UriBuilder.java.\n        (value === '' ? '' : '=' + goog.string.urlEncode(value)));\n  }\n};\n\n\n/**\n * Builds a query data string from a sequence of alternating keys and values.\n * Currently generates \"&key&\" for empty args.\n *\n * @param {!IArrayLike<string|goog.uri.utils.QueryValue>} keysAndValues\n *     Alternating keys and values. See the QueryArray typedef.\n * @param {number=} opt_startIndex A start offset into the arary, defaults to 0.\n * @return {string} The encoded query string, in the form 'a=1&b=2'.\n */\ngoog.uri.utils.buildQueryData = function(keysAndValues, opt_startIndex) {\n  goog.asserts.assert(\n      Math.max(keysAndValues.length - (opt_startIndex || 0), 0) % 2 == 0,\n      'goog.uri.utils: Key/value lists must be even in length.');\n\n  var params = [];\n  for (var i = opt_startIndex || 0; i < keysAndValues.length; i += 2) {\n    var key = /** @type {string} */ (keysAndValues[i]);\n    goog.uri.utils.appendKeyValuePairs_(key, keysAndValues[i + 1], params);\n  }\n  return params.join('&');\n};\n\n\n/**\n * Builds a query data string from a map.\n * Currently generates \"&key&\" for empty args.\n *\n * @param {!Object<string, goog.uri.utils.QueryValue>} map An object where keys\n *     are URI-encoded parameter keys, and the values are arbitrary types\n *     or arrays. Keys with a null value are dropped.\n * @return {string} The encoded query string, in the form 'a=1&b=2'.\n */\ngoog.uri.utils.buildQueryDataFromMap = function(map) {\n  var params = [];\n  for (var key in map) {\n    goog.uri.utils.appendKeyValuePairs_(key, map[key], params);\n  }\n  return params.join('&');\n};\n\n\n/**\n * Appends URI parameters to an existing URI.\n *\n * The variable arguments may contain alternating keys and values.  Keys are\n * assumed to be already URI encoded.  The values should not be URI-encoded,\n * and will instead be encoded by this function.\n * <pre>\n * appendParams('http://www.foo.com?existing=true',\n *     'key1', 'value1',\n *     'key2', 'value?willBeEncoded',\n *     'key3', ['valueA', 'valueB', 'valueC'],\n *     'key4', null);\n * result: 'http://www.foo.com?existing=true&' +\n *     'key1=value1&' +\n *     'key2=value%3FwillBeEncoded&' +\n *     'key3=valueA&key3=valueB&key3=valueC'\n * </pre>\n *\n * A single call to this function will not exhibit quadratic behavior in IE,\n * whereas multiple repeated calls may, although the effect is limited by\n * fact that URL's generally can't exceed 2kb.\n *\n * @param {string} uri The original URI, which may already have query data.\n * @param {...(goog.uri.utils.QueryArray|goog.uri.utils.QueryValue)}\n * var_args\n *     An array or argument list conforming to goog.uri.utils.QueryArray.\n * @return {string} The URI with all query parameters added.\n */\ngoog.uri.utils.appendParams = function(uri, var_args) {\n  var queryData = arguments.length == 2 ?\n      goog.uri.utils.buildQueryData(arguments[1], 0) :\n      goog.uri.utils.buildQueryData(arguments, 1);\n  return goog.uri.utils.appendQueryDataToUri_(uri, queryData);\n};\n\n\n/**\n * Appends query parameters from a map.\n *\n * @param {string} uri The original URI, which may already have query data.\n * @param {!Object<goog.uri.utils.QueryValue>} map An object where keys are\n *     URI-encoded parameter keys, and the values are arbitrary types or arrays.\n *     Keys with a null value are dropped.\n * @return {string} The new parameters.\n */\ngoog.uri.utils.appendParamsFromMap = function(uri, map) {\n  var queryData = goog.uri.utils.buildQueryDataFromMap(map);\n  return goog.uri.utils.appendQueryDataToUri_(uri, queryData);\n};\n\n\n/**\n * Appends a single URI parameter.\n *\n * Repeated calls to this can exhibit quadratic behavior in IE6 due to the\n * way string append works, though it should be limited given the 2kb limit.\n *\n * @param {string} uri The original URI, which may already have query data.\n * @param {string} key The key, which must already be URI encoded.\n * @param {*=} opt_value The value, which will be stringized and encoded\n *     (assumed not already to be encoded).  If omitted, undefined, or null, the\n *     key will be added as a valueless parameter.\n * @return {string} The URI with the query parameter added.\n */\ngoog.uri.utils.appendParam = function(uri, key, opt_value) {\n  var value = (opt_value != null) ? '=' + goog.string.urlEncode(opt_value) : '';\n  return goog.uri.utils.appendQueryDataToUri_(uri, key + value);\n};\n\n\n/**\n * Finds the next instance of a query parameter with the specified name.\n *\n * Does not instantiate any objects.\n *\n * @param {string} uri The URI to search.  May contain a fragment identifier\n *     if opt_hashIndex is specified.\n * @param {number} startIndex The index to begin searching for the key at.  A\n *     match may be found even if this is one character after the ampersand.\n * @param {string} keyEncoded The URI-encoded key.\n * @param {number} hashOrEndIndex Index to stop looking at.  If a hash\n *     mark is present, it should be its index, otherwise it should be the\n *     length of the string.\n * @return {number} The position of the first character in the key's name,\n *     immediately after either a question mark or a dot.\n * @private\n */\ngoog.uri.utils.findParam_ = function(\n    uri, startIndex, keyEncoded, hashOrEndIndex) {\n  var index = startIndex;\n  var keyLength = keyEncoded.length;\n\n  // Search for the key itself and post-filter for surronuding punctuation,\n  // rather than expensively building a regexp.\n  while ((index = uri.indexOf(keyEncoded, index)) >= 0 &&\n         index < hashOrEndIndex) {\n    var precedingChar = uri.charCodeAt(index - 1);\n    // Ensure that the preceding character is '&' or '?'.\n    if (precedingChar == goog.uri.utils.CharCode_.AMPERSAND ||\n        precedingChar == goog.uri.utils.CharCode_.QUESTION) {\n      // Ensure the following character is '&', '=', '#', or NaN\n      // (end of string).\n      var followingChar = uri.charCodeAt(index + keyLength);\n      if (!followingChar || followingChar == goog.uri.utils.CharCode_.EQUAL ||\n          followingChar == goog.uri.utils.CharCode_.AMPERSAND ||\n          followingChar == goog.uri.utils.CharCode_.HASH) {\n        return index;\n      }\n    }\n    index += keyLength + 1;\n  }\n\n  return -1;\n};\n\n\n/**\n * Regular expression for finding a hash mark or end of string.\n * @type {RegExp}\n * @private\n */\ngoog.uri.utils.hashOrEndRe_ = /#|$/;\n\n\n/**\n * Determines if the URI contains a specific key.\n *\n * Performs no object instantiations.\n *\n * @param {string} uri The URI to process.  May contain a fragment\n *     identifier.\n * @param {string} keyEncoded The URI-encoded key.  Case-sensitive.\n * @return {boolean} Whether the key is present.\n */\ngoog.uri.utils.hasParam = function(uri, keyEncoded) {\n  return goog.uri.utils.findParam_(\n             uri, 0, keyEncoded, uri.search(goog.uri.utils.hashOrEndRe_)) >= 0;\n};\n\n\n/**\n * Gets the first value of a query parameter.\n * @param {string} uri The URI to process.  May contain a fragment.\n * @param {string} keyEncoded The URI-encoded key.  Case-sensitive.\n * @return {?string} The first value of the parameter (URI-decoded), or null\n *     if the parameter is not found.\n */\ngoog.uri.utils.getParamValue = function(uri, keyEncoded) {\n  var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_);\n  var foundIndex =\n      goog.uri.utils.findParam_(uri, 0, keyEncoded, hashOrEndIndex);\n\n  if (foundIndex < 0) {\n    return null;\n  } else {\n    var endPosition = uri.indexOf('&', foundIndex);\n    if (endPosition < 0 || endPosition > hashOrEndIndex) {\n      endPosition = hashOrEndIndex;\n    }\n    // Progress forth to the end of the \"key=\" or \"key&\" substring.\n    foundIndex += keyEncoded.length + 1;\n    // Use substr, because it (unlike substring) will return empty string\n    // if foundIndex > endPosition.\n    return goog.string.urlDecode(\n        uri.substr(foundIndex, endPosition - foundIndex));\n  }\n};\n\n\n/**\n * Gets all values of a query parameter.\n * @param {string} uri The URI to process.  May contain a fragment.\n * @param {string} keyEncoded The URI-encoded key.  Case-sensitive.\n * @return {!Array<string>} All URI-decoded values with the given key.\n *     If the key is not found, this will have length 0, but never be null.\n */\ngoog.uri.utils.getParamValues = function(uri, keyEncoded) {\n  var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_);\n  var position = 0;\n  var foundIndex;\n  var result = [];\n\n  while ((foundIndex = goog.uri.utils.findParam_(\n              uri, position, keyEncoded, hashOrEndIndex)) >= 0) {\n    // Find where this parameter ends, either the '&' or the end of the\n    // query parameters.\n    position = uri.indexOf('&', foundIndex);\n    if (position < 0 || position > hashOrEndIndex) {\n      position = hashOrEndIndex;\n    }\n\n    // Progress forth to the end of the \"key=\" or \"key&\" substring.\n    foundIndex += keyEncoded.length + 1;\n    // Use substr, because it (unlike substring) will return empty string\n    // if foundIndex > position.\n    result.push(\n        goog.string.urlDecode(uri.substr(foundIndex, position - foundIndex)));\n  }\n\n  return result;\n};\n\n\n/**\n * Regexp to find trailing question marks and ampersands.\n * @type {RegExp}\n * @private\n */\ngoog.uri.utils.trailingQueryPunctuationRe_ = /[?&]($|#)/;\n\n\n/**\n * Removes all instances of a query parameter.\n * @param {string} uri The URI to process.  Must not contain a fragment.\n * @param {string} keyEncoded The URI-encoded key.\n * @return {string} The URI with all instances of the parameter removed.\n */\ngoog.uri.utils.removeParam = function(uri, keyEncoded) {\n  var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_);\n  var position = 0;\n  var foundIndex;\n  var buffer = [];\n\n  // Look for a query parameter.\n  while ((foundIndex = goog.uri.utils.findParam_(\n              uri, position, keyEncoded, hashOrEndIndex)) >= 0) {\n    // Get the portion of the query string up to, but not including, the ?\n    // or & starting the parameter.\n    buffer.push(uri.substring(position, foundIndex));\n    // Progress to immediately after the '&'.  If not found, go to the end.\n    // Avoid including the hash mark.\n    position = Math.min(\n        (uri.indexOf('&', foundIndex) + 1) || hashOrEndIndex, hashOrEndIndex);\n  }\n\n  // Append everything that is remaining.\n  buffer.push(uri.substr(position));\n\n  // Join the buffer, and remove trailing punctuation that remains.\n  return buffer.join('').replace(\n      goog.uri.utils.trailingQueryPunctuationRe_, '$1');\n};\n\n\n/**\n * Replaces all existing definitions of a parameter with a single definition.\n *\n * Repeated calls to this can exhibit quadratic behavior due to the need to\n * find existing instances and reconstruct the string, though it should be\n * limited given the 2kb limit.  Consider using appendParams or setParamsFromMap\n * to update multiple parameters in bulk.\n *\n * @param {string} uri The original URI, which may already have query data.\n * @param {string} keyEncoded The key, which must already be URI encoded.\n * @param {*} value The value, which will be stringized and encoded (assumed\n *     not already to be encoded).\n * @return {string} The URI with the query parameter added.\n */\ngoog.uri.utils.setParam = function(uri, keyEncoded, value) {\n  return goog.uri.utils.appendParam(\n      goog.uri.utils.removeParam(uri, keyEncoded), keyEncoded, value);\n};\n\n\n/**\n * Effeciently set or remove multiple query parameters in a URI. Order of\n * unchanged parameters will not be modified, all updated parameters will be\n * appended to the end of the query. Params with values of null or undefined are\n * removed.\n *\n * @param {string} uri The URI to process.\n * @param {!Object<string, goog.uri.utils.QueryValue>} params A list of\n *     parameters to update. If null or undefined, the param will be removed.\n * @return {string} An updated URI where the query data has been updated with\n *     the params.\n */\ngoog.uri.utils.setParamsFromMap = function(uri, params) {\n  var parts = goog.uri.utils.splitQueryData_(uri);\n  var queryData = parts[1];\n  var buffer = [];\n  if (queryData) {\n    queryData.split('&').forEach(function(pair) {\n      var indexOfEquals = pair.indexOf('=');\n      var name = indexOfEquals >= 0 ? pair.substr(0, indexOfEquals) : pair;\n      if (!params.hasOwnProperty(name)) {\n        buffer.push(pair);\n      }\n    });\n  }\n  parts[1] = goog.uri.utils.appendQueryData_(\n      buffer.join('&'), goog.uri.utils.buildQueryDataFromMap(params));\n  return goog.uri.utils.joinQueryData_(parts);\n};\n\n\n/**\n * Generates a URI path using a given URI and a path with checks to\n * prevent consecutive \"//\". The baseUri passed in must not contain\n * query or fragment identifiers. The path to append may not contain query or\n * fragment identifiers.\n *\n * @param {string} baseUri URI to use as the base.\n * @param {string} path Path to append.\n * @return {string} Updated URI.\n */\ngoog.uri.utils.appendPath = function(baseUri, path) {\n  goog.uri.utils.assertNoFragmentsOrQueries_(baseUri);\n\n  // Remove any trailing '/'\n  if (goog.string.endsWith(baseUri, '/')) {\n    baseUri = baseUri.substr(0, baseUri.length - 1);\n  }\n  // Remove any leading '/'\n  if (goog.string.startsWith(path, '/')) {\n    path = path.substr(1);\n  }\n  return '' + baseUri + '/' + path;\n};\n\n\n/**\n * Replaces the path.\n * @param {string} uri URI to use as the base.\n * @param {string} path New path.\n * @return {string} Updated URI.\n */\ngoog.uri.utils.setPath = function(uri, path) {\n  // Add any missing '/'.\n  if (!goog.string.startsWith(path, '/')) {\n    path = '/' + path;\n  }\n  var parts = goog.uri.utils.split(uri);\n  return goog.uri.utils.buildFromEncodedParts(\n      parts[goog.uri.utils.ComponentIndex.SCHEME],\n      parts[goog.uri.utils.ComponentIndex.USER_INFO],\n      parts[goog.uri.utils.ComponentIndex.DOMAIN],\n      parts[goog.uri.utils.ComponentIndex.PORT], path,\n      parts[goog.uri.utils.ComponentIndex.QUERY_DATA],\n      parts[goog.uri.utils.ComponentIndex.FRAGMENT]);\n};\n\n\n/**\n * Standard supported query parameters.\n * @enum {string}\n */\ngoog.uri.utils.StandardQueryParam = {\n\n  /** Unused parameter for unique-ifying. */\n  RANDOM: 'zx'\n};\n\n\n/**\n * Sets the zx parameter of a URI to a random value.\n * @param {string} uri Any URI.\n * @return {string} That URI with the \"zx\" parameter added or replaced to\n *     contain a random string.\n */\ngoog.uri.utils.makeUnique = function(uri) {\n  return goog.uri.utils.setParam(\n      uri, goog.uri.utils.StandardQueryParam.RANDOM,\n      goog.string.getRandomString());\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Class for parsing and formatting URIs.\n *\n * Use goog.Uri(string) to parse a URI string.  Use goog.Uri.create(...) to\n * create a new instance of the goog.Uri object from Uri parts.\n *\n * e.g: <code>var myUri = new goog.Uri(window.location);</code>\n *\n * Implements RFC 3986 for parsing/formatting URIs.\n * http://www.ietf.org/rfc/rfc3986.txt\n *\n * Some changes have been made to the interface (more like .NETs), though the\n * internal representation is now of un-encoded parts, this will change the\n * behavior slightly.\n */\n\ngoog.provide('goog.Uri');\ngoog.provide('goog.Uri.QueryData');\n\ngoog.require('goog.array');\ngoog.require('goog.asserts');\ngoog.require('goog.string');\ngoog.require('goog.structs');\ngoog.require('goog.structs.Map');\ngoog.require('goog.uri.utils');\ngoog.require('goog.uri.utils.ComponentIndex');\ngoog.require('goog.uri.utils.StandardQueryParam');\n\n\n\n/**\n * This class contains setters and getters for the parts of the URI.\n * The <code>getXyz</code>/<code>setXyz</code> methods return the decoded part\n * -- so<code>goog.Uri.parse('/foo%20bar').getPath()</code> will return the\n * decoded path, <code>/foo bar</code>.\n *\n * Reserved characters (see RFC 3986 section 2.2) can be present in\n * their percent-encoded form in scheme, domain, and path URI components and\n * will not be auto-decoded. For example:\n * <code>goog.Uri.parse('rel%61tive/path%2fto/resource').getPath()</code> will\n * return <code>relative/path%2fto/resource</code>.\n *\n * The constructor accepts an optional unparsed, raw URI string.  The parser\n * is relaxed, so special characters that aren't escaped but don't cause\n * ambiguities will not cause parse failures.\n *\n * All setters return <code>this</code> and so may be chained, a la\n * <code>goog.Uri.parse('/foo').setFragment('part').toString()</code>.\n *\n * @param {*=} opt_uri Optional string URI to parse\n *        (use goog.Uri.create() to create a URI from parts), or if\n *        a goog.Uri is passed, a clone is created.\n * @param {boolean=} opt_ignoreCase If true, #getParameterValue will ignore\n * the case of the parameter name.\n *\n * @throws URIError If opt_uri is provided and URI is malformed (that is,\n *     if decodeURIComponent fails on any of the URI components).\n * @constructor\n * @struct\n */\ngoog.Uri = function(opt_uri, opt_ignoreCase) {\n  /**\n   * Scheme such as \"http\".\n   * @private {string}\n   */\n  this.scheme_ = '';\n\n  /**\n   * User credentials in the form \"username:password\".\n   * @private {string}\n   */\n  this.userInfo_ = '';\n\n  /**\n   * Domain part, e.g. \"www.google.com\".\n   * @private {string}\n   */\n  this.domain_ = '';\n\n  /**\n   * Port, e.g. 8080.\n   * @private {?number}\n   */\n  this.port_ = null;\n\n  /**\n   * Path, e.g. \"/tests/img.png\".\n   * @private {string}\n   */\n  this.path_ = '';\n\n  /**\n   * The fragment without the #.\n   * @private {string}\n   */\n  this.fragment_ = '';\n\n  /**\n   * Whether or not this Uri should be treated as Read Only.\n   * @private {boolean}\n   */\n  this.isReadOnly_ = false;\n\n  /**\n   * Whether or not to ignore case when comparing query params.\n   * @private {boolean}\n   */\n  this.ignoreCase_ = false;\n\n  /**\n   * Object representing query data.\n   * @private {!goog.Uri.QueryData}\n   */\n  this.queryData_;\n\n  // Parse in the uri string\n  var m;\n  if (opt_uri instanceof goog.Uri) {\n    this.ignoreCase_ = (opt_ignoreCase !== undefined) ? opt_ignoreCase :\n                                                        opt_uri.getIgnoreCase();\n    this.setScheme(opt_uri.getScheme());\n    this.setUserInfo(opt_uri.getUserInfo());\n    this.setDomain(opt_uri.getDomain());\n    this.setPort(opt_uri.getPort());\n    this.setPath(opt_uri.getPath());\n    this.setQueryData(opt_uri.getQueryData().clone());\n    this.setFragment(opt_uri.getFragment());\n  } else if (opt_uri && (m = goog.uri.utils.split(String(opt_uri)))) {\n    this.ignoreCase_ = !!opt_ignoreCase;\n\n    // Set the parts -- decoding as we do so.\n    // COMPATIBILITY NOTE - In IE, unmatched fields may be empty strings,\n    // whereas in other browsers they will be undefined.\n    this.setScheme(m[goog.uri.utils.ComponentIndex.SCHEME] || '', true);\n    this.setUserInfo(m[goog.uri.utils.ComponentIndex.USER_INFO] || '', true);\n    this.setDomain(m[goog.uri.utils.ComponentIndex.DOMAIN] || '', true);\n    this.setPort(m[goog.uri.utils.ComponentIndex.PORT]);\n    this.setPath(m[goog.uri.utils.ComponentIndex.PATH] || '', true);\n    this.setQueryData(m[goog.uri.utils.ComponentIndex.QUERY_DATA] || '', true);\n    this.setFragment(m[goog.uri.utils.ComponentIndex.FRAGMENT] || '', true);\n\n  } else {\n    this.ignoreCase_ = !!opt_ignoreCase;\n    this.queryData_ = new goog.Uri.QueryData(null, this.ignoreCase_);\n  }\n};\n\n\n/**\n * Parameter name added to stop caching.\n * @type {string}\n */\ngoog.Uri.RANDOM_PARAM = goog.uri.utils.StandardQueryParam.RANDOM;\n\n\n/**\n * @return {string} The string form of the url.\n * @override\n */\ngoog.Uri.prototype.toString = function() {\n  var out = [];\n\n  var scheme = this.getScheme();\n  if (scheme) {\n    out.push(\n        goog.Uri.encodeSpecialChars_(\n            scheme, goog.Uri.reDisallowedInSchemeOrUserInfo_, true),\n        ':');\n  }\n\n  var domain = this.getDomain();\n  if (domain || scheme == 'file') {\n    out.push('//');\n\n    var userInfo = this.getUserInfo();\n    if (userInfo) {\n      out.push(\n          goog.Uri.encodeSpecialChars_(\n              userInfo, goog.Uri.reDisallowedInSchemeOrUserInfo_, true),\n          '@');\n    }\n\n    out.push(goog.Uri.removeDoubleEncoding_(goog.string.urlEncode(domain)));\n\n    var port = this.getPort();\n    if (port != null) {\n      out.push(':', String(port));\n    }\n  }\n\n  var path = this.getPath();\n  if (path) {\n    if (this.hasDomain() && path.charAt(0) != '/') {\n      out.push('/');\n    }\n    out.push(goog.Uri.encodeSpecialChars_(\n        path,\n        path.charAt(0) == '/' ? goog.Uri.reDisallowedInAbsolutePath_ :\n                                goog.Uri.reDisallowedInRelativePath_,\n        true));\n  }\n\n  var query = this.getEncodedQuery();\n  if (query) {\n    out.push('?', query);\n  }\n\n  var fragment = this.getFragment();\n  if (fragment) {\n    out.push(\n        '#',\n        goog.Uri.encodeSpecialChars_(\n            fragment, goog.Uri.reDisallowedInFragment_));\n  }\n  return out.join('');\n};\n\n\n/**\n * Resolves the given relative URI (a goog.Uri object), using the URI\n * represented by this instance as the base URI.\n *\n * There are several kinds of relative URIs:<br>\n * 1. foo - replaces the last part of the path, the whole query and fragment<br>\n * 2. /foo - replaces the path, the query and fragment<br>\n * 3. //foo - replaces everything from the domain on.  foo is a domain name<br>\n * 4. ?foo - replace the query and fragment<br>\n * 5. #foo - replace the fragment only\n *\n * Additionally, if relative URI has a non-empty path, all \"..\" and \".\"\n * segments will be resolved, as described in RFC 3986.\n *\n * @param {!goog.Uri} relativeUri The relative URI to resolve.\n * @return {!goog.Uri} The resolved URI.\n */\ngoog.Uri.prototype.resolve = function(relativeUri) {\n  var absoluteUri = this.clone();\n\n  // we satisfy these conditions by looking for the first part of relativeUri\n  // that is not blank and applying defaults to the rest\n\n  var overridden = relativeUri.hasScheme();\n\n  if (overridden) {\n    absoluteUri.setScheme(relativeUri.getScheme());\n  } else {\n    overridden = relativeUri.hasUserInfo();\n  }\n\n  if (overridden) {\n    absoluteUri.setUserInfo(relativeUri.getUserInfo());\n  } else {\n    overridden = relativeUri.hasDomain();\n  }\n\n  if (overridden) {\n    absoluteUri.setDomain(relativeUri.getDomain());\n  } else {\n    overridden = relativeUri.hasPort();\n  }\n\n  var path = relativeUri.getPath();\n  if (overridden) {\n    absoluteUri.setPort(relativeUri.getPort());\n  } else {\n    overridden = relativeUri.hasPath();\n    if (overridden) {\n      // resolve path properly\n      if (path.charAt(0) != '/') {\n        // path is relative\n        if (this.hasDomain() && !this.hasPath()) {\n          // RFC 3986, section 5.2.3, case 1\n          path = '/' + path;\n        } else {\n          // RFC 3986, section 5.2.3, case 2\n          var lastSlashIndex = absoluteUri.getPath().lastIndexOf('/');\n          if (lastSlashIndex != -1) {\n            path = absoluteUri.getPath().substr(0, lastSlashIndex + 1) + path;\n          }\n        }\n      }\n      path = goog.Uri.removeDotSegments(path);\n    }\n  }\n\n  if (overridden) {\n    absoluteUri.setPath(path);\n  } else {\n    overridden = relativeUri.hasQuery();\n  }\n\n  if (overridden) {\n    absoluteUri.setQueryData(relativeUri.getQueryData().clone());\n  } else {\n    overridden = relativeUri.hasFragment();\n  }\n\n  if (overridden) {\n    absoluteUri.setFragment(relativeUri.getFragment());\n  }\n\n  return absoluteUri;\n};\n\n\n/**\n * Clones the URI instance.\n * @return {!goog.Uri} New instance of the URI object.\n */\ngoog.Uri.prototype.clone = function() {\n  return new goog.Uri(this);\n};\n\n\n/**\n * @return {string} The encoded scheme/protocol for the URI.\n */\ngoog.Uri.prototype.getScheme = function() {\n  return this.scheme_;\n};\n\n\n/**\n * Sets the scheme/protocol.\n * @throws URIError If opt_decode is true and newScheme is malformed (that is,\n *     if decodeURIComponent fails).\n * @param {string} newScheme New scheme value.\n * @param {boolean=} opt_decode Optional param for whether to decode new value.\n * @return {!goog.Uri} Reference to this URI object.\n */\ngoog.Uri.prototype.setScheme = function(newScheme, opt_decode) {\n  this.enforceReadOnly();\n  this.scheme_ =\n      opt_decode ? goog.Uri.decodeOrEmpty_(newScheme, true) : newScheme;\n\n  // remove an : at the end of the scheme so somebody can pass in\n  // window.location.protocol\n  if (this.scheme_) {\n    this.scheme_ = this.scheme_.replace(/:$/, '');\n  }\n  return this;\n};\n\n\n/**\n * @return {boolean} Whether the scheme has been set.\n */\ngoog.Uri.prototype.hasScheme = function() {\n  return !!this.scheme_;\n};\n\n\n/**\n * @return {string} The decoded user info.\n */\ngoog.Uri.prototype.getUserInfo = function() {\n  return this.userInfo_;\n};\n\n\n/**\n * Sets the userInfo.\n * @throws URIError If opt_decode is true and newUserInfo is malformed (that is,\n *     if decodeURIComponent fails).\n * @param {string} newUserInfo New userInfo value.\n * @param {boolean=} opt_decode Optional param for whether to decode new value.\n * @return {!goog.Uri} Reference to this URI object.\n */\ngoog.Uri.prototype.setUserInfo = function(newUserInfo, opt_decode) {\n  this.enforceReadOnly();\n  this.userInfo_ =\n      opt_decode ? goog.Uri.decodeOrEmpty_(newUserInfo) : newUserInfo;\n  return this;\n};\n\n\n/**\n * @return {boolean} Whether the user info has been set.\n */\ngoog.Uri.prototype.hasUserInfo = function() {\n  return !!this.userInfo_;\n};\n\n\n/**\n * @return {string} The decoded domain.\n */\ngoog.Uri.prototype.getDomain = function() {\n  return this.domain_;\n};\n\n\n/**\n * Sets the domain.\n * @throws URIError If opt_decode is true and newDomain is malformed (that is,\n *     if decodeURIComponent fails).\n * @param {string} newDomain New domain value.\n * @param {boolean=} opt_decode Optional param for whether to decode new value.\n * @return {!goog.Uri} Reference to this URI object.\n */\ngoog.Uri.prototype.setDomain = function(newDomain, opt_decode) {\n  this.enforceReadOnly();\n  this.domain_ =\n      opt_decode ? goog.Uri.decodeOrEmpty_(newDomain, true) : newDomain;\n  return this;\n};\n\n\n/**\n * @return {boolean} Whether the domain has been set.\n */\ngoog.Uri.prototype.hasDomain = function() {\n  return !!this.domain_;\n};\n\n\n/**\n * @return {?number} The port number.\n */\ngoog.Uri.prototype.getPort = function() {\n  return this.port_;\n};\n\n\n/**\n * Sets the port number.\n * @param {*} newPort Port number. Will be explicitly casted to a number.\n * @return {!goog.Uri} Reference to this URI object.\n */\ngoog.Uri.prototype.setPort = function(newPort) {\n  this.enforceReadOnly();\n\n  if (newPort) {\n    newPort = Number(newPort);\n    if (isNaN(newPort) || newPort < 0) {\n      throw new Error('Bad port number ' + newPort);\n    }\n    this.port_ = newPort;\n  } else {\n    this.port_ = null;\n  }\n\n  return this;\n};\n\n\n/**\n * @return {boolean} Whether the port has been set.\n */\ngoog.Uri.prototype.hasPort = function() {\n  return this.port_ != null;\n};\n\n\n/**\n * @return {string} The decoded path.\n */\ngoog.Uri.prototype.getPath = function() {\n  return this.path_;\n};\n\n\n/**\n * Sets the path.\n * @throws URIError If opt_decode is true and newPath is malformed (that is,\n *     if decodeURIComponent fails).\n * @param {string} newPath New path value.\n * @param {boolean=} opt_decode Optional param for whether to decode new value.\n * @return {!goog.Uri} Reference to this URI object.\n */\ngoog.Uri.prototype.setPath = function(newPath, opt_decode) {\n  this.enforceReadOnly();\n  this.path_ = opt_decode ? goog.Uri.decodeOrEmpty_(newPath, true) : newPath;\n  return this;\n};\n\n\n/**\n * @return {boolean} Whether the path has been set.\n */\ngoog.Uri.prototype.hasPath = function() {\n  return !!this.path_;\n};\n\n\n/**\n * @return {boolean} Whether the query string has been set.\n */\ngoog.Uri.prototype.hasQuery = function() {\n  return this.queryData_.toString() !== '';\n};\n\n\n/**\n * Sets the query data.\n * @param {goog.Uri.QueryData|string|undefined} queryData QueryData object.\n * @param {boolean=} opt_decode Optional param for whether to decode new value.\n *     Applies only if queryData is a string.\n * @return {!goog.Uri} Reference to this URI object.\n */\ngoog.Uri.prototype.setQueryData = function(queryData, opt_decode) {\n  this.enforceReadOnly();\n\n  if (queryData instanceof goog.Uri.QueryData) {\n    this.queryData_ = queryData;\n    this.queryData_.setIgnoreCase(this.ignoreCase_);\n  } else {\n    if (!opt_decode) {\n      // QueryData accepts encoded query string, so encode it if\n      // opt_decode flag is not true.\n      queryData = goog.Uri.encodeSpecialChars_(\n          queryData, goog.Uri.reDisallowedInQuery_);\n    }\n    this.queryData_ = new goog.Uri.QueryData(queryData, this.ignoreCase_);\n  }\n\n  return this;\n};\n\n\n/**\n * Sets the URI query.\n * @param {string} newQuery New query value.\n * @param {boolean=} opt_decode Optional param for whether to decode new value.\n * @return {!goog.Uri} Reference to this URI object.\n */\ngoog.Uri.prototype.setQuery = function(newQuery, opt_decode) {\n  return this.setQueryData(newQuery, opt_decode);\n};\n\n\n/**\n * @return {string} The encoded URI query, not including the ?.\n */\ngoog.Uri.prototype.getEncodedQuery = function() {\n  return this.queryData_.toString();\n};\n\n\n/**\n * @return {string} The decoded URI query, not including the ?.\n */\ngoog.Uri.prototype.getDecodedQuery = function() {\n  return this.queryData_.toDecodedString();\n};\n\n\n/**\n * Returns the query data.\n * @return {!goog.Uri.QueryData} QueryData object.\n */\ngoog.Uri.prototype.getQueryData = function() {\n  return this.queryData_;\n};\n\n\n/**\n * @return {string} The encoded URI query, not including the ?.\n *\n * Warning: This method, unlike other getter methods, returns encoded\n * value, instead of decoded one.\n */\ngoog.Uri.prototype.getQuery = function() {\n  return this.getEncodedQuery();\n};\n\n\n/**\n * Sets the value of the named query parameters, clearing previous values for\n * that key.\n *\n * @param {string} key The parameter to set.\n * @param {*} value The new value. Value does not need to be encoded.\n * @return {!goog.Uri} Reference to this URI object.\n */\ngoog.Uri.prototype.setParameterValue = function(key, value) {\n  this.enforceReadOnly();\n  this.queryData_.set(key, value);\n  return this;\n};\n\n\n/**\n * Sets the values of the named query parameters, clearing previous values for\n * that key.  Not new values will currently be moved to the end of the query\n * string.\n *\n * So, <code>goog.Uri.parse('foo?a=b&c=d&e=f').setParameterValues('c', ['new'])\n * </code> yields <tt>foo?a=b&e=f&c=new</tt>.</p>\n *\n * @param {string} key The parameter to set.\n * @param {*} values The new values. If values is a single\n *     string then it will be treated as the sole value. Values do not need to\n *     be encoded.\n * @return {!goog.Uri} Reference to this URI object.\n */\ngoog.Uri.prototype.setParameterValues = function(key, values) {\n  this.enforceReadOnly();\n\n  if (!Array.isArray(values)) {\n    values = [String(values)];\n  }\n\n  this.queryData_.setValues(key, values);\n\n  return this;\n};\n\n\n/**\n * Returns the value<b>s</b> for a given cgi parameter as a list of decoded\n * query parameter values.\n * @param {string} name The parameter to get values for.\n * @return {!Array<?>} The values for a given cgi parameter as a list of\n *     decoded query parameter values.\n */\ngoog.Uri.prototype.getParameterValues = function(name) {\n  return this.queryData_.getValues(name);\n};\n\n\n/**\n * Returns the first value for a given cgi parameter or undefined if the given\n * parameter name does not appear in the query string.\n * @param {string} paramName Unescaped parameter name.\n * @return {string|undefined} The first value for a given cgi parameter or\n *     undefined if the given parameter name does not appear in the query\n *     string.\n */\ngoog.Uri.prototype.getParameterValue = function(paramName) {\n  return /** @type {string|undefined} */ (this.queryData_.get(paramName));\n};\n\n\n/**\n * @return {string} The URI fragment, not including the #.\n */\ngoog.Uri.prototype.getFragment = function() {\n  return this.fragment_;\n};\n\n\n/**\n * Sets the URI fragment.\n * @throws URIError If opt_decode is true and newFragment is malformed (that is,\n *     if decodeURIComponent fails).\n * @param {string} newFragment New fragment value.\n * @param {boolean=} opt_decode Optional param for whether to decode new value.\n * @return {!goog.Uri} Reference to this URI object.\n */\ngoog.Uri.prototype.setFragment = function(newFragment, opt_decode) {\n  this.enforceReadOnly();\n  this.fragment_ =\n      opt_decode ? goog.Uri.decodeOrEmpty_(newFragment) : newFragment;\n  return this;\n};\n\n\n/**\n * @return {boolean} Whether the URI has a fragment set.\n */\ngoog.Uri.prototype.hasFragment = function() {\n  return !!this.fragment_;\n};\n\n\n/**\n * Returns true if this has the same domain as that of uri2.\n * @param {!goog.Uri} uri2 The URI object to compare to.\n * @return {boolean} true if same domain; false otherwise.\n */\ngoog.Uri.prototype.hasSameDomainAs = function(uri2) {\n  return ((!this.hasDomain() && !uri2.hasDomain()) ||\n          this.getDomain() == uri2.getDomain()) &&\n      ((!this.hasPort() && !uri2.hasPort()) ||\n       this.getPort() == uri2.getPort());\n};\n\n\n/**\n * Adds a random parameter to the Uri.\n * @return {!goog.Uri} Reference to this Uri object.\n */\ngoog.Uri.prototype.makeUnique = function() {\n  this.enforceReadOnly();\n  this.setParameterValue(goog.Uri.RANDOM_PARAM, goog.string.getRandomString());\n\n  return this;\n};\n\n\n/**\n * Removes the named query parameter.\n *\n * @param {string} key The parameter to remove.\n * @return {!goog.Uri} Reference to this URI object.\n */\ngoog.Uri.prototype.removeParameter = function(key) {\n  this.enforceReadOnly();\n  this.queryData_.remove(key);\n  return this;\n};\n\n\n/**\n * Sets whether Uri is read only. If this goog.Uri is read-only,\n * enforceReadOnly_ will be called at the start of any function that may modify\n * this Uri.\n * @param {boolean} isReadOnly whether this goog.Uri should be read only.\n * @return {!goog.Uri} Reference to this Uri object.\n */\ngoog.Uri.prototype.setReadOnly = function(isReadOnly) {\n  this.isReadOnly_ = isReadOnly;\n  return this;\n};\n\n\n/**\n * @return {boolean} Whether the URI is read only.\n */\ngoog.Uri.prototype.isReadOnly = function() {\n  return this.isReadOnly_;\n};\n\n\n/**\n * Checks if this Uri has been marked as read only, and if so, throws an error.\n * This should be called whenever any modifying function is called.\n */\ngoog.Uri.prototype.enforceReadOnly = function() {\n  if (this.isReadOnly_) {\n    throw new Error('Tried to modify a read-only Uri');\n  }\n};\n\n\n/**\n * Sets whether to ignore case.\n * NOTE: If there are already key/value pairs in the QueryData, and\n * ignoreCase_ is set to false, the keys will all be lower-cased.\n * @param {boolean} ignoreCase whether this goog.Uri should ignore case.\n * @return {!goog.Uri} Reference to this Uri object.\n */\ngoog.Uri.prototype.setIgnoreCase = function(ignoreCase) {\n  this.ignoreCase_ = ignoreCase;\n  if (this.queryData_) {\n    this.queryData_.setIgnoreCase(ignoreCase);\n  }\n  return this;\n};\n\n\n/**\n * @return {boolean} Whether to ignore case.\n */\ngoog.Uri.prototype.getIgnoreCase = function() {\n  return this.ignoreCase_;\n};\n\n\n//==============================================================================\n// Static members\n//==============================================================================\n\n\n/**\n * Creates a uri from the string form.  Basically an alias of new goog.Uri().\n * If a Uri object is passed to parse then it will return a clone of the object.\n *\n * @throws URIError If parsing the URI is malformed. The passed URI components\n *     should all be parseable by decodeURIComponent.\n * @param {*} uri Raw URI string or instance of Uri\n *     object.\n * @param {boolean=} opt_ignoreCase Whether to ignore the case of parameter\n * names in #getParameterValue.\n * @return {!goog.Uri} The new URI object.\n */\ngoog.Uri.parse = function(uri, opt_ignoreCase) {\n  return uri instanceof goog.Uri ? uri.clone() :\n                                   new goog.Uri(uri, opt_ignoreCase);\n};\n\n\n/**\n * Creates a new goog.Uri object from unencoded parts.\n *\n * @param {?string=} opt_scheme Scheme/protocol or full URI to parse.\n * @param {?string=} opt_userInfo username:password.\n * @param {?string=} opt_domain www.google.com.\n * @param {?number=} opt_port 9830.\n * @param {?string=} opt_path /some/path/to/a/file.html.\n * @param {string|goog.Uri.QueryData=} opt_query a=1&b=2.\n * @param {?string=} opt_fragment The fragment without the #.\n * @param {boolean=} opt_ignoreCase Whether to ignore parameter name case in\n *     #getParameterValue.\n *\n * @return {!goog.Uri} The new URI object.\n */\ngoog.Uri.create = function(\n    opt_scheme, opt_userInfo, opt_domain, opt_port, opt_path, opt_query,\n    opt_fragment, opt_ignoreCase) {\n  var uri = new goog.Uri(null, opt_ignoreCase);\n\n  // Only set the parts if they are defined and not empty strings.\n  opt_scheme && uri.setScheme(opt_scheme);\n  opt_userInfo && uri.setUserInfo(opt_userInfo);\n  opt_domain && uri.setDomain(opt_domain);\n  opt_port && uri.setPort(opt_port);\n  opt_path && uri.setPath(opt_path);\n  opt_query && uri.setQueryData(opt_query);\n  opt_fragment && uri.setFragment(opt_fragment);\n\n  return uri;\n};\n\n\n/**\n * Resolves a relative Uri against a base Uri, accepting both strings and\n * Uri objects.\n *\n * @param {*} base Base Uri.\n * @param {*} rel Relative Uri.\n * @return {!goog.Uri} Resolved uri.\n */\ngoog.Uri.resolve = function(base, rel) {\n  if (!(base instanceof goog.Uri)) {\n    base = goog.Uri.parse(base);\n  }\n\n  if (!(rel instanceof goog.Uri)) {\n    rel = goog.Uri.parse(rel);\n  }\n\n  return base.resolve(rel);\n};\n\n\n/**\n * Removes dot segments in given path component, as described in\n * RFC 3986, section 5.2.4.\n *\n * @param {string} path A non-empty path component.\n * @return {string} Path component with removed dot segments.\n */\ngoog.Uri.removeDotSegments = function(path) {\n  if (path == '..' || path == '.') {\n    return '';\n\n  } else if (\n      !goog.string.contains(path, './') && !goog.string.contains(path, '/.')) {\n    // This optimization detects uris which do not contain dot-segments,\n    // and as a consequence do not require any processing.\n    return path;\n\n  } else {\n    var leadingSlash = goog.string.startsWith(path, '/');\n    var segments = path.split('/');\n    var out = [];\n\n    for (var pos = 0; pos < segments.length;) {\n      var segment = segments[pos++];\n\n      if (segment == '.') {\n        if (leadingSlash && pos == segments.length) {\n          out.push('');\n        }\n      } else if (segment == '..') {\n        if (out.length > 1 || out.length == 1 && out[0] != '') {\n          out.pop();\n        }\n        if (leadingSlash && pos == segments.length) {\n          out.push('');\n        }\n      } else {\n        out.push(segment);\n        leadingSlash = true;\n      }\n    }\n\n    return out.join('/');\n  }\n};\n\n\n/**\n * Decodes a value or returns the empty string if it isn't defined or empty.\n * @throws URIError If decodeURIComponent fails to decode val.\n * @param {string|undefined} val Value to decode.\n * @param {boolean=} opt_preserveReserved If true, restricted characters will\n *     not be decoded.\n * @return {string} Decoded value.\n * @private\n */\ngoog.Uri.decodeOrEmpty_ = function(val, opt_preserveReserved) {\n  // Don't use UrlDecode() here because val is not a query parameter.\n  if (!val) {\n    return '';\n  }\n\n  // decodeURI has the same output for '%2f' and '%252f'. We double encode %25\n  // so that we can distinguish between the 2 inputs. This is later undone by\n  // removeDoubleEncoding_.\n  return opt_preserveReserved ? decodeURI(val.replace(/%25/g, '%2525')) :\n                                decodeURIComponent(val);\n};\n\n\n/**\n * If unescapedPart is non null, then escapes any characters in it that aren't\n * valid characters in a url and also escapes any special characters that\n * appear in extra.\n *\n * @param {*} unescapedPart The string to encode.\n * @param {RegExp} extra A character set of characters in [\\01-\\177].\n * @param {boolean=} opt_removeDoubleEncoding If true, remove double percent\n *     encoding.\n * @return {?string} null iff unescapedPart == null.\n * @private\n */\ngoog.Uri.encodeSpecialChars_ = function(\n    unescapedPart, extra, opt_removeDoubleEncoding) {\n  if (typeof unescapedPart === 'string') {\n    var encoded = encodeURI(unescapedPart).replace(extra, goog.Uri.encodeChar_);\n    if (opt_removeDoubleEncoding) {\n      // encodeURI double-escapes %XX sequences used to represent restricted\n      // characters in some URI components, remove the double escaping here.\n      encoded = goog.Uri.removeDoubleEncoding_(encoded);\n    }\n    return encoded;\n  }\n  return null;\n};\n\n\n/**\n * Converts a character in [\\01-\\177] to its unicode character equivalent.\n * @param {string} ch One character string.\n * @return {string} Encoded string.\n * @private\n */\ngoog.Uri.encodeChar_ = function(ch) {\n  var n = ch.charCodeAt(0);\n  return '%' + ((n >> 4) & 0xf).toString(16) + (n & 0xf).toString(16);\n};\n\n\n/**\n * Removes double percent-encoding from a string.\n * @param  {string} doubleEncodedString String\n * @return {string} String with double encoding removed.\n * @private\n */\ngoog.Uri.removeDoubleEncoding_ = function(doubleEncodedString) {\n  return doubleEncodedString.replace(/%25([0-9a-fA-F]{2})/g, '%$1');\n};\n\n\n/**\n * Regular expression for characters that are disallowed in the scheme or\n * userInfo part of the URI.\n * @type {RegExp}\n * @private\n */\ngoog.Uri.reDisallowedInSchemeOrUserInfo_ = /[#\\/\\?@]/g;\n\n\n/**\n * Regular expression for characters that are disallowed in a relative path.\n * Colon is included due to RFC 3986 3.3.\n * @type {RegExp}\n * @private\n */\ngoog.Uri.reDisallowedInRelativePath_ = /[\\#\\?:]/g;\n\n\n/**\n * Regular expression for characters that are disallowed in an absolute path.\n * @type {RegExp}\n * @private\n */\ngoog.Uri.reDisallowedInAbsolutePath_ = /[\\#\\?]/g;\n\n\n/**\n * Regular expression for characters that are disallowed in the query.\n * @type {RegExp}\n * @private\n */\ngoog.Uri.reDisallowedInQuery_ = /[\\#\\?@]/g;\n\n\n/**\n * Regular expression for characters that are disallowed in the fragment.\n * @type {RegExp}\n * @private\n */\ngoog.Uri.reDisallowedInFragment_ = /#/g;\n\n\n/**\n * Checks whether two URIs have the same domain.\n * @param {string} uri1String First URI string.\n * @param {string} uri2String Second URI string.\n * @return {boolean} true if the two URIs have the same domain; false otherwise.\n */\ngoog.Uri.haveSameDomain = function(uri1String, uri2String) {\n  // Differs from goog.uri.utils.haveSameDomain, since this ignores scheme.\n  // TODO(gboyer): Have this just call goog.uri.util.haveSameDomain.\n  var pieces1 = goog.uri.utils.split(uri1String);\n  var pieces2 = goog.uri.utils.split(uri2String);\n  return pieces1[goog.uri.utils.ComponentIndex.DOMAIN] ==\n      pieces2[goog.uri.utils.ComponentIndex.DOMAIN] &&\n      pieces1[goog.uri.utils.ComponentIndex.PORT] ==\n      pieces2[goog.uri.utils.ComponentIndex.PORT];\n};\n\n\n\n/**\n * Class used to represent URI query parameters.  It is essentially a hash of\n * name-value pairs, though a name can be present more than once.\n *\n * Has the same interface as the collections in goog.structs.\n *\n * @param {?string=} opt_query Optional encoded query string to parse into\n *     the object.\n * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter\n *     name in #get.\n * @constructor\n * @struct\n * @final\n */\ngoog.Uri.QueryData = function(opt_query, opt_ignoreCase) {\n  /**\n   * The map containing name/value or name/array-of-values pairs.\n   * May be null if it requires parsing from the query string.\n   *\n   * We need to use a Map because we cannot guarantee that the key names will\n   * not be problematic for IE.\n   *\n   * @private {?goog.structs.Map<string, !Array<*>>}\n   */\n  this.keyMap_ = null;\n\n  /**\n   * The number of params, or null if it requires computing.\n   * @private {?number}\n   */\n  this.count_ = null;\n\n  /**\n   * Encoded query string, or null if it requires computing from the key map.\n   * @private {?string}\n   */\n  this.encodedQuery_ = opt_query || null;\n\n  /**\n   * If true, ignore the case of the parameter name in #get.\n   * @private {boolean}\n   */\n  this.ignoreCase_ = !!opt_ignoreCase;\n};\n\n\n/**\n * If the underlying key map is not yet initialized, it parses the\n * query string and fills the map with parsed data.\n * @private\n */\ngoog.Uri.QueryData.prototype.ensureKeyMapInitialized_ = function() {\n  if (!this.keyMap_) {\n    this.keyMap_ = new goog.structs.Map();\n    this.count_ = 0;\n    if (this.encodedQuery_) {\n      var self = this;\n      goog.uri.utils.parseQueryData(this.encodedQuery_, function(name, value) {\n        self.add(goog.string.urlDecode(name), value);\n      });\n    }\n  }\n};\n\n\n/**\n * Creates a new query data instance from a map of names and values.\n *\n * @param {!goog.structs.Map<string, ?>|!Object} map Map of string parameter\n *     names to parameter value. If parameter value is an array, it is\n *     treated as if the key maps to each individual value in the\n *     array.\n * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter\n *     name in #get.\n * @return {!goog.Uri.QueryData} The populated query data instance.\n */\ngoog.Uri.QueryData.createFromMap = function(map, opt_ignoreCase) {\n  var keys = goog.structs.getKeys(map);\n  if (typeof keys == 'undefined') {\n    throw new Error('Keys are undefined');\n  }\n\n  var queryData = new goog.Uri.QueryData(null, opt_ignoreCase);\n  var values = goog.structs.getValues(map);\n  for (var i = 0; i < keys.length; i++) {\n    var key = keys[i];\n    var value = values[i];\n    if (!Array.isArray(value)) {\n      queryData.add(key, value);\n    } else {\n      queryData.setValues(key, value);\n    }\n  }\n  return queryData;\n};\n\n\n/**\n * Creates a new query data instance from parallel arrays of parameter names\n * and values. Allows for duplicate parameter names. Throws an error if the\n * lengths of the arrays differ.\n *\n * @param {!Array<string>} keys Parameter names.\n * @param {!Array<?>} values Parameter values.\n * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter\n *     name in #get.\n * @return {!goog.Uri.QueryData} The populated query data instance.\n */\ngoog.Uri.QueryData.createFromKeysValues = function(\n    keys, values, opt_ignoreCase) {\n  if (keys.length != values.length) {\n    throw new Error('Mismatched lengths for keys/values');\n  }\n  var queryData = new goog.Uri.QueryData(null, opt_ignoreCase);\n  for (var i = 0; i < keys.length; i++) {\n    queryData.add(keys[i], values[i]);\n  }\n  return queryData;\n};\n\n\n/**\n * @return {?number} The number of parameters.\n */\ngoog.Uri.QueryData.prototype.getCount = function() {\n  this.ensureKeyMapInitialized_();\n  return this.count_;\n};\n\n\n/**\n * Adds a key value pair.\n * @param {string} key Name.\n * @param {*} value Value.\n * @return {!goog.Uri.QueryData} Instance of this object.\n */\ngoog.Uri.QueryData.prototype.add = function(key, value) {\n  this.ensureKeyMapInitialized_();\n  this.invalidateCache_();\n\n  key = this.getKeyName_(key);\n  var values = this.keyMap_.get(key);\n  if (!values) {\n    this.keyMap_.set(key, (values = []));\n  }\n  values.push(value);\n  this.count_ = goog.asserts.assertNumber(this.count_) + 1;\n  return this;\n};\n\n\n/**\n * Removes all the params with the given key.\n * @param {string} key Name.\n * @return {boolean} Whether any parameter was removed.\n */\ngoog.Uri.QueryData.prototype.remove = function(key) {\n  this.ensureKeyMapInitialized_();\n\n  key = this.getKeyName_(key);\n  if (this.keyMap_.containsKey(key)) {\n    this.invalidateCache_();\n\n    // Decrement parameter count.\n    this.count_ =\n        goog.asserts.assertNumber(this.count_) - this.keyMap_.get(key).length;\n    return this.keyMap_.remove(key);\n  }\n  return false;\n};\n\n\n/**\n * Clears the parameters.\n */\ngoog.Uri.QueryData.prototype.clear = function() {\n  this.invalidateCache_();\n  this.keyMap_ = null;\n  this.count_ = 0;\n};\n\n\n/**\n * @return {boolean} Whether we have any parameters.\n */\ngoog.Uri.QueryData.prototype.isEmpty = function() {\n  this.ensureKeyMapInitialized_();\n  return this.count_ == 0;\n};\n\n\n/**\n * Whether there is a parameter with the given name\n * @param {string} key The parameter name to check for.\n * @return {boolean} Whether there is a parameter with the given name.\n */\ngoog.Uri.QueryData.prototype.containsKey = function(key) {\n  this.ensureKeyMapInitialized_();\n  key = this.getKeyName_(key);\n  return this.keyMap_.containsKey(key);\n};\n\n\n/**\n * Whether there is a parameter with the given value.\n * @param {*} value The value to check for.\n * @return {boolean} Whether there is a parameter with the given value.\n */\ngoog.Uri.QueryData.prototype.containsValue = function(value) {\n  // NOTE(arv): This solution goes through all the params even if it was the\n  // first param. We can get around this by not reusing code or by switching to\n  // iterators.\n  var vals = this.getValues();\n  return goog.array.contains(vals, value);\n};\n\n\n/**\n * Runs a callback on every key-value pair in the map, including duplicate keys.\n * This won't maintain original order when duplicate keys are interspersed (like\n * getKeys() / getValues()).\n * @param {function(this:SCOPE, ?, string, !goog.Uri.QueryData)} f\n * @param {SCOPE=} opt_scope The value of \"this\" inside f.\n * @template SCOPE\n */\ngoog.Uri.QueryData.prototype.forEach = function(f, opt_scope) {\n  this.ensureKeyMapInitialized_();\n  this.keyMap_.forEach(function(values, key) {\n    goog.array.forEach(values, function(value) {\n      f.call(opt_scope, value, key, this);\n    }, this);\n  }, this);\n};\n\n\n/**\n * Returns all the keys of the parameters. If a key is used multiple times\n * it will be included multiple times in the returned array\n * @return {!Array<string>} All the keys of the parameters.\n */\ngoog.Uri.QueryData.prototype.getKeys = function() {\n  this.ensureKeyMapInitialized_();\n  // We need to get the values to know how many keys to add.\n  var vals = this.keyMap_.getValues();\n  var keys = this.keyMap_.getKeys();\n  var rv = [];\n  for (var i = 0; i < keys.length; i++) {\n    var val = vals[i];\n    for (var j = 0; j < val.length; j++) {\n      rv.push(keys[i]);\n    }\n  }\n  return rv;\n};\n\n\n/**\n * Returns all the values of the parameters with the given name. If the query\n * data has no such key this will return an empty array. If no key is given\n * all values wil be returned.\n * @param {string=} opt_key The name of the parameter to get the values for.\n * @return {!Array<?>} All the values of the parameters with the given name.\n */\ngoog.Uri.QueryData.prototype.getValues = function(opt_key) {\n  this.ensureKeyMapInitialized_();\n  var rv = [];\n  if (typeof opt_key === 'string') {\n    if (this.containsKey(opt_key)) {\n      rv = goog.array.concat(rv, this.keyMap_.get(this.getKeyName_(opt_key)));\n    }\n  } else {\n    // Return all values.\n    var values = this.keyMap_.getValues();\n    for (var i = 0; i < values.length; i++) {\n      rv = goog.array.concat(rv, values[i]);\n    }\n  }\n  return rv;\n};\n\n\n/**\n * Sets a key value pair and removes all other keys with the same value.\n *\n * @param {string} key Name.\n * @param {*} value Value.\n * @return {!goog.Uri.QueryData} Instance of this object.\n */\ngoog.Uri.QueryData.prototype.set = function(key, value) {\n  this.ensureKeyMapInitialized_();\n  this.invalidateCache_();\n\n  // TODO(chrishenry): This could be better written as\n  // this.remove(key), this.add(key, value), but that would reorder\n  // the key (since the key is first removed and then added at the\n  // end) and we would have to fix unit tests that depend on key\n  // ordering.\n  key = this.getKeyName_(key);\n  if (this.containsKey(key)) {\n    this.count_ =\n        goog.asserts.assertNumber(this.count_) - this.keyMap_.get(key).length;\n  }\n  this.keyMap_.set(key, [value]);\n  this.count_ = goog.asserts.assertNumber(this.count_) + 1;\n  return this;\n};\n\n\n/**\n * Returns the first value associated with the key. If the query data has no\n * such key this will return undefined or the optional default.\n * @param {string} key The name of the parameter to get the value for.\n * @param {*=} opt_default The default value to return if the query data\n *     has no such key.\n * @return {*} The first string value associated with the key, or opt_default\n *     if there's no value.\n */\ngoog.Uri.QueryData.prototype.get = function(key, opt_default) {\n  if (!key) {\n    return opt_default;\n  }\n  var values = this.getValues(key);\n  return values.length > 0 ? String(values[0]) : opt_default;\n};\n\n\n/**\n * Sets the values for a key. If the key already exists, this will\n * override all of the existing values that correspond to the key.\n * @param {string} key The key to set values for.\n * @param {!Array<?>} values The values to set.\n */\ngoog.Uri.QueryData.prototype.setValues = function(key, values) {\n  this.remove(key);\n\n  if (values.length > 0) {\n    this.invalidateCache_();\n    this.keyMap_.set(this.getKeyName_(key), goog.array.clone(values));\n    this.count_ = goog.asserts.assertNumber(this.count_) + values.length;\n  }\n};\n\n\n/**\n * @return {string} Encoded query string.\n * @override\n */\ngoog.Uri.QueryData.prototype.toString = function() {\n  if (this.encodedQuery_) {\n    return this.encodedQuery_;\n  }\n\n  if (!this.keyMap_) {\n    return '';\n  }\n\n  var sb = [];\n\n  // In the past, we use this.getKeys() and this.getVals(), but that\n  // generates a lot of allocations as compared to simply iterating\n  // over the keys.\n  var keys = this.keyMap_.getKeys();\n  for (var i = 0; i < keys.length; i++) {\n    var key = keys[i];\n    var encodedKey = goog.string.urlEncode(key);\n    var val = this.getValues(key);\n    for (var j = 0; j < val.length; j++) {\n      var param = encodedKey;\n      // Ensure that null and undefined are encoded into the url as\n      // literal strings.\n      if (val[j] !== '') {\n        param += '=' + goog.string.urlEncode(val[j]);\n      }\n      sb.push(param);\n    }\n  }\n\n  return this.encodedQuery_ = sb.join('&');\n};\n\n\n/**\n * @throws URIError If URI is malformed (that is, if decodeURIComponent fails on\n *     any of the URI components).\n * @return {string} Decoded query string.\n */\ngoog.Uri.QueryData.prototype.toDecodedString = function() {\n  return goog.Uri.decodeOrEmpty_(this.toString());\n};\n\n\n/**\n * Invalidate the cache.\n * @private\n */\ngoog.Uri.QueryData.prototype.invalidateCache_ = function() {\n  this.encodedQuery_ = null;\n};\n\n\n/**\n * Removes all keys that are not in the provided list. (Modifies this object.)\n * @param {Array<string>} keys The desired keys.\n * @return {!goog.Uri.QueryData} a reference to this object.\n */\ngoog.Uri.QueryData.prototype.filterKeys = function(keys) {\n  this.ensureKeyMapInitialized_();\n  this.keyMap_.forEach(function(value, key) {\n    if (!goog.array.contains(keys, key)) {\n      this.remove(key);\n    }\n  }, this);\n  return this;\n};\n\n\n/**\n * Clone the query data instance.\n * @return {!goog.Uri.QueryData} New instance of the QueryData object.\n */\ngoog.Uri.QueryData.prototype.clone = function() {\n  var rv = new goog.Uri.QueryData();\n  rv.encodedQuery_ = this.encodedQuery_;\n  if (this.keyMap_) {\n    rv.keyMap_ = this.keyMap_.clone();\n    rv.count_ = this.count_;\n  }\n  return rv;\n};\n\n\n/**\n * Helper function to get the key name from a JavaScript object. Converts\n * the object to a string, and to lower case if necessary.\n * @private\n * @param {*} arg The object to get a key name from.\n * @return {string} valid key name which can be looked up in #keyMap_.\n */\ngoog.Uri.QueryData.prototype.getKeyName_ = function(arg) {\n  var keyName = String(arg);\n  if (this.ignoreCase_) {\n    keyName = keyName.toLowerCase();\n  }\n  return keyName;\n};\n\n\n/**\n * Ignore case in parameter names.\n * NOTE: If there are already key/value pairs in the QueryData, and\n * ignoreCase_ is set to false, the keys will all be lower-cased.\n * @param {boolean} ignoreCase whether this goog.Uri should ignore case.\n */\ngoog.Uri.QueryData.prototype.setIgnoreCase = function(ignoreCase) {\n  var resetKeys = ignoreCase && !this.ignoreCase_;\n  if (resetKeys) {\n    this.ensureKeyMapInitialized_();\n    this.invalidateCache_();\n    this.keyMap_.forEach(function(value, key) {\n      var lowerCase = key.toLowerCase();\n      if (key != lowerCase) {\n        this.remove(key);\n        this.setValues(lowerCase, value);\n      }\n    }, this);\n  }\n  this.ignoreCase_ = ignoreCase;\n};\n\n\n/**\n * Extends a query data object with another query data or map like object. This\n * operates 'in-place', it does not create a new QueryData object.\n *\n * @param {...(?goog.Uri.QueryData|?goog.structs.Map<?, ?>|?Object)} var_args\n *     The object from which key value pairs will be copied. Note: does not\n *     accept null.\n * @suppress {deprecated} Use deprecated goog.structs.forEach to allow different\n * types of parameters.\n */\ngoog.Uri.QueryData.prototype.extend = function(var_args) {\n  for (var i = 0; i < arguments.length; i++) {\n    var data = arguments[i];\n    goog.structs.forEach(data, function(value, key) {\n      this.add(key, value);\n    }, this);\n  }\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview JSON utility functions.\n */\n\n\ngoog.provide('goog.json');\ngoog.provide('goog.json.Replacer');\ngoog.provide('goog.json.Reviver');\ngoog.provide('goog.json.Serializer');\n\n\n/**\n * @define {boolean} If true, use the native JSON parsing API.\n * NOTE: The default `goog.json.parse` implementation is able to handle\n * invalid JSON. JSPB used to produce invalid JSON which is not the case\n * anymore so this is safe to enable for parsing JSPB. Using native JSON is\n * faster and safer than the default implementation using `eval`.\n */\ngoog.json.USE_NATIVE_JSON = goog.define('goog.json.USE_NATIVE_JSON', false);\n\n/**\n * @define {boolean} If true, try the native JSON parsing API first. If it\n * fails, log an error and use `eval` instead. This is useful when\n * transitioning to `goog.json.USE_NATIVE_JSON`. The error logger needs to\n * be set by `goog.json.setErrorLogger`. If it is not set then the error\n * is ignored.\n */\ngoog.json.TRY_NATIVE_JSON = goog.define('goog.json.TRY_NATIVE_JSON', false);\n\n\n/**\n * Tests if a string is an invalid JSON string. This only ensures that we are\n * not using any invalid characters\n * @param {string} s The string to test.\n * @return {boolean} True if the input is a valid JSON string.\n */\ngoog.json.isValid = function(s) {\n  // All empty whitespace is not valid.\n  if (/^\\s*$/.test(s)) {\n    return false;\n  }\n\n  // This is taken from http://www.json.org/json2.js which is released to the\n  // public domain.\n  // Changes: We dissallow \\u2028 Line separator and \\u2029 Paragraph separator\n  // inside strings.  We also treat \\u2028 and \\u2029 as whitespace which they\n  // are in the RFC but IE and Safari does not match \\s to these so we need to\n  // include them in the reg exps in all places where whitespace is allowed.\n  // We allowed \\x7f inside strings because some tools don't escape it,\n  // e.g. http://www.json.org/java/org/json/JSONObject.java\n\n  // Parsing happens in three stages. In the first stage, we run the text\n  // against regular expressions that look for non-JSON patterns. We are\n  // especially concerned with '()' and 'new' because they can cause invocation,\n  // and '=' because it can cause mutation. But just to be safe, we want to\n  // reject all unexpected forms.\n\n  // We split the first stage into 4 regexp operations in order to work around\n  // crippling inefficiencies in IE's and Safari's regexp engines. First we\n  // replace all backslash pairs with '@' (a non-JSON character). Second, we\n  // replace all simple value tokens with ']' characters, but only when followed\n  // by a colon, comma, closing bracket or end of string. Third, we delete all\n  // open brackets that follow a colon or comma or that begin the text. Finally,\n  // we look to see that the remaining characters are only whitespace or ']' or\n  // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.\n\n  // Don't make these static since they have the global flag.\n  const backslashesRe = /\\\\[\"\\\\\\/bfnrtu]/g;\n  const simpleValuesRe =\n      /(?:\"[^\"\\\\\\n\\r\\u2028\\u2029\\x00-\\x08\\x0a-\\x1f]*\"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)[\\s\\u2028\\u2029]*(?=:|,|]|}|$)/g;\n  const openBracketsRe = /(?:^|:|,)(?:[\\s\\u2028\\u2029]*\\[)+/g;\n  const remainderRe = /^[\\],:{}\\s\\u2028\\u2029]*$/;\n\n  return remainderRe.test(\n      s.replace(backslashesRe, '@')\n          .replace(simpleValuesRe, ']')\n          .replace(openBracketsRe, ''));\n};\n\n/**\n * Logs a parsing error in `JSON.parse` solvable by using `eval`\n * if `goog.json.TRY_NATIVE_JSON` is enabled.\n * @private {function(string, !Error)} The first parameter is the error message,\n *     the second is the exception thrown by `JSON.parse`.\n */\ngoog.json.errorLogger_ = goog.nullFunction;\n\n\n/**\n * Sets an error logger to use if there's a recoverable parsing error and\n * `goog.json.TRY_NATIVE_JSON` is enabled.\n * @param {function(string, !Error)} errorLogger The first parameter is the\n *     error message, the second is the exception thrown by `JSON.parse`.\n */\ngoog.json.setErrorLogger = function(errorLogger) {\n  goog.json.errorLogger_ = errorLogger;\n};\n\n\n/**\n * Parses a JSON string and returns the result. This throws an exception if\n * the string is an invalid JSON string.\n *\n * Note that this is very slow on large strings. Use JSON.parse if possible.\n *\n * @param {*} s The JSON string to parse.\n * @throws Error if s is invalid JSON.\n * @return {Object} The object generated from the JSON string, or null.\n * @deprecated Use JSON.parse.\n */\ngoog.json.parse = goog.json.USE_NATIVE_JSON ?\n    /** @type {function(*):Object} */ (goog.global['JSON']['parse']) :\n    function(s) {\n      let error;\n      if (goog.json.TRY_NATIVE_JSON) {\n        try {\n          return goog.global['JSON']['parse'](s);\n        } catch (ex) {\n          error = ex;\n        }\n      }\n      const o = String(s);\n      if (goog.json.isValid(o)) {\n\n        try {\n          const result = /** @type {?Object} */ (eval('(' + o + ')'));\n          if (error) {\n            goog.json.errorLogger_('Invalid JSON: ' + o, error);\n          }\n          return result;\n        } catch (ex) {\n        }\n      }\n      throw new Error('Invalid JSON string: ' + o);\n    };\n\n\n/**\n * JSON replacer, as defined in Section 15.12.3 of the ES5 spec.\n * @see http://ecma-international.org/ecma-262/5.1/#sec-15.12.3\n *\n * TODO(nicksantos): Array should also be a valid replacer.\n *\n * @typedef {function(this:Object, string, *): *}\n */\ngoog.json.Replacer;\n\n\n/**\n * JSON reviver, as defined in Section 15.12.2 of the ES5 spec.\n * @see http://ecma-international.org/ecma-262/5.1/#sec-15.12.3\n *\n * @typedef {function(this:Object, string, *): *}\n */\ngoog.json.Reviver;\n\n\n/**\n * Serializes an object or a value to a JSON string.\n *\n * @param {*} object The object to serialize.\n * @param {?goog.json.Replacer=} opt_replacer A replacer function\n *     called for each (key, value) pair that determines how the value\n *     should be serialized. By defult, this just returns the value\n *     and allows default serialization to kick in.\n * @throws Error if there are loops in the object graph.\n * @return {string} A JSON string representation of the input.\n */\ngoog.json.serialize = goog.json.USE_NATIVE_JSON ?\n    /** @type {function(*, ?goog.json.Replacer=):string} */\n    (goog.global['JSON']['stringify']) :\n    function(object, opt_replacer) {\n      // NOTE(nicksantos): Currently, we never use JSON.stringify.\n      //\n      // The last time I evaluated this, JSON.stringify had subtle bugs and\n      // behavior differences on all browsers, and the performance win was not\n      // large enough to justify all the issues. This may change in the future\n      // as browser implementations get better.\n      //\n      // assertSerialize in json_test contains if branches for the cases\n      // that fail.\n      return new goog.json.Serializer(opt_replacer).serialize(object);\n    };\n\n\n\n/**\n * Class that is used to serialize JSON objects to a string.\n * @param {?goog.json.Replacer=} opt_replacer Replacer.\n * @constructor\n */\ngoog.json.Serializer = function(opt_replacer) {\n  /**\n   * @type {goog.json.Replacer|null|undefined}\n   * @private\n   */\n  this.replacer_ = opt_replacer;\n};\n\n\n/**\n * Serializes an object or a value to a JSON string.\n *\n * @param {*} object The object to serialize.\n * @throws Error if there are loops in the object graph.\n * @return {string} A JSON string representation of the input.\n */\ngoog.json.Serializer.prototype.serialize = function(object) {\n  const sb = [];\n  this.serializeInternal(object, sb);\n  return sb.join('');\n};\n\n\n/**\n * Serializes a generic value to a JSON string\n * @protected\n * @param {*} object The object to serialize.\n * @param {Array<string>} sb Array used as a string builder.\n * @throws Error if there are loops in the object graph.\n */\ngoog.json.Serializer.prototype.serializeInternal = function(object, sb) {\n  if (object == null) {\n    // undefined == null so this branch covers undefined as well as null\n    sb.push('null');\n    return;\n  }\n\n  if (typeof object == 'object') {\n    if (Array.isArray(object)) {\n      this.serializeArray(object, sb);\n      return;\n    } else if (\n        object instanceof String || object instanceof Number ||\n        object instanceof Boolean) {\n      object = object.valueOf();\n      // Fall through to switch below.\n    } else {\n      this.serializeObject_(/** @type {!Object} */ (object), sb);\n      return;\n    }\n  }\n\n  switch (typeof object) {\n    case 'string':\n      this.serializeString_(object, sb);\n      break;\n    case 'number':\n      this.serializeNumber_(object, sb);\n      break;\n    case 'boolean':\n      sb.push(String(object));\n      break;\n    case 'function':\n      sb.push('null');\n      break;\n    default:\n      throw new Error('Unknown type: ' + typeof object);\n  }\n};\n\n\n/**\n * Character mappings used internally for goog.string.quote\n * @private\n * @type {!Object}\n */\ngoog.json.Serializer.charToJsonCharCache_ = {\n  '\\\"': '\\\\\"',\n  '\\\\': '\\\\\\\\',\n  '/': '\\\\/',\n  '\\b': '\\\\b',\n  '\\f': '\\\\f',\n  '\\n': '\\\\n',\n  '\\r': '\\\\r',\n  '\\t': '\\\\t',\n\n  '\\x0B': '\\\\u000b'  // '\\v' is not supported in JScript\n};\n\n\n/**\n * Regular expression used to match characters that need to be replaced.\n * The S60 browser has a bug where unicode characters are not matched by\n * regular expressions. The condition below detects such behaviour and\n * adjusts the regular expression accordingly.\n * @private\n * @type {!RegExp}\n */\ngoog.json.Serializer.charsToReplace_ = /\\uffff/.test('\\uffff') ?\n    /[\\\\\\\"\\x00-\\x1f\\x7f-\\uffff]/g :\n    /[\\\\\\\"\\x00-\\x1f\\x7f-\\xff]/g;\n\n\n/**\n * Serializes a string to a JSON string\n * @private\n * @param {string} s The string to serialize.\n * @param {Array<string>} sb Array used as a string builder.\n */\ngoog.json.Serializer.prototype.serializeString_ = function(s, sb) {\n  // The official JSON implementation does not work with international\n  // characters.\n  sb.push('\"', s.replace(goog.json.Serializer.charsToReplace_, function(c) {\n    // caching the result improves performance by a factor 2-3\n    let rv = goog.json.Serializer.charToJsonCharCache_[c];\n    if (!rv) {\n      rv = '\\\\u' + (c.charCodeAt(0) | 0x10000).toString(16).substr(1);\n      goog.json.Serializer.charToJsonCharCache_[c] = rv;\n    }\n    return rv;\n  }), '\"');\n};\n\n\n/**\n * Serializes a number to a JSON string\n * @private\n * @param {number} n The number to serialize.\n * @param {Array<string>} sb Array used as a string builder.\n */\ngoog.json.Serializer.prototype.serializeNumber_ = function(n, sb) {\n  sb.push(isFinite(n) && !isNaN(n) ? String(n) : 'null');\n};\n\n\n/**\n * Serializes an array to a JSON string\n * @param {Array<string>} arr The array to serialize.\n * @param {Array<string>} sb Array used as a string builder.\n * @protected\n */\ngoog.json.Serializer.prototype.serializeArray = function(arr, sb) {\n  const l = arr.length;\n  sb.push('[');\n  let sep = '';\n  for (let i = 0; i < l; i++) {\n    sb.push(sep);\n\n    const value = arr[i];\n    this.serializeInternal(\n        this.replacer_ ? this.replacer_.call(arr, String(i), value) : value,\n        sb);\n\n    sep = ',';\n  }\n  sb.push(']');\n};\n\n\n/**\n * Serializes an object to a JSON string\n * @private\n * @param {!Object} obj The object to serialize.\n * @param {Array<string>} sb Array used as a string builder.\n */\ngoog.json.Serializer.prototype.serializeObject_ = function(obj, sb) {\n  sb.push('{');\n  let sep = '';\n  for (const key in obj) {\n    if (Object.prototype.hasOwnProperty.call(obj, key)) {\n      const value = obj[key];\n      // Skip functions.\n      if (typeof value != 'function') {\n        sb.push(sep);\n        this.serializeString_(key, sb);\n        sb.push(':');\n\n        this.serializeInternal(\n            this.replacer_ ? this.replacer_.call(obj, key, value) : value, sb);\n\n        sep = ',';\n      }\n    }\n  }\n  sb.push('}');\n};\n","/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines utility and helper functions.\n */\n\ngoog.provide('fireauth.util');\n\ngoog.require('goog.Promise');\ngoog.require('goog.Timer');\ngoog.require('goog.Uri');\ngoog.require('goog.dom');\ngoog.require('goog.events');\ngoog.require('goog.events.EventType');\ngoog.require('goog.html.SafeUrl');\ngoog.require('goog.json');\ngoog.require('goog.object');\ngoog.require('goog.string');\ngoog.require('goog.userAgent');\ngoog.require('goog.window');\n\n\n/** @suppress {duplicate} Suppress variable 'angular' first declared. */\nvar angular;\n\n/**\n * Checks whether the user agent is IE11.\n * @return {boolean} True if it is IE11.\n */\nfireauth.util.isIe11 = function() {\n  return goog.userAgent.IE &&\n      !!goog.userAgent.DOCUMENT_MODE &&\n      goog.userAgent.DOCUMENT_MODE == 11;\n};\n\n\n/**\n * Checks whether the user agent is IE10.\n * @return {boolean} True if it is IE10.\n */\nfireauth.util.isIe10 = function() {\n  return goog.userAgent.IE &&\n      !!goog.userAgent.DOCUMENT_MODE &&\n      goog.userAgent.DOCUMENT_MODE == 10;\n};\n\n\n/**\n * Checks whether the user agent is Edge.\n * @param {string} userAgent The browser user agent string.\n * @return {boolean} True if it is Edge.\n */\nfireauth.util.isEdge = function(userAgent) {\n  return /Edge\\/\\d+/.test(userAgent);\n};\n\n\n/**\n * @param {?string=} opt_userAgent The navigator user agent.\n * @return {boolean} Whether local storage is not synchronized between an iframe\n *     and a popup of the same domain.\n */\nfireauth.util.isLocalStorageNotSynchronized = function(opt_userAgent) {\n  var ua = opt_userAgent || fireauth.util.getUserAgentString();\n  return fireauth.util.isIe11() || fireauth.util.isEdge(ua);\n};\n\n\n/** @return {string} The current URL. */\nfireauth.util.getCurrentUrl = function() {\n  return (goog.global['window'] && goog.global['window']['location']['href']) ||\n      // Check for worker environments.\n      (self && self['location'] && self['location']['href']) || '';\n};\n\n\n/**\n * @param {string} requestUri The request URI to send in verifyAssertion\n *     request.\n * @return {string} The sanitized URI, in this case it undoes the hashbang\n *     angularJs routing changes to request URI.\n */\nfireauth.util.sanitizeRequestUri = function(requestUri) {\n  // If AngularJS is included.\n  if (typeof angular != 'undefined') {\n    // Remove hashbang modifications from URL.\n    requestUri = requestUri.replace('#/', '#').replace('#!/', '#');\n  }\n  return requestUri;\n};\n\n\n/**\n * @param {?string} url The target URL. When !url, redirects to a blank page.\n * @param {!Window=} opt_window The optional window to redirect to target URL.\n * @param {boolean=} opt_bypassCheck Whether to bypass check. Used for custom\n *     scheme redirects.\n */\nfireauth.util.goTo = function(url, opt_window, opt_bypassCheck) {\n  var win = opt_window || goog.global['window'];\n  // No URL, redirect to blank page.\n  var finalUrl = 'about:blank';\n  // Popping up a window and then assigning its URL seems to cause some weird\n  // error. Fixed by setting win.location.href for now in IE browsers.\n  // Bug was detected in Edge and IE9.\n  if (url && !opt_bypassCheck) {\n    // We cannot use goog.dom.safe.setLocationHref since it tries to read\n    // popup.location from a different origin, which is an error in IE.\n    // (In Chrome, popup.location is just an empty Location object)\n    finalUrl = goog.html.SafeUrl.unwrap(goog.html.SafeUrl.sanitize(url));\n  }\n  win.location.href = finalUrl;\n};\n\n\n/**\n * @param {string} url The target URL.\n * @param {!Window=} opt_window The optional window to replace with target URL.\n * @param {boolean=} opt_bypassCheck Whether to bypass check. Used for custom\n *     scheme redirects.\n */\nfireauth.util.replaceCurrentUrl = function(url, opt_window, opt_bypassCheck) {\n  var win = opt_window || goog.global['window'];\n  if (!opt_bypassCheck) {\n    win.location.replace(\n        goog.html.SafeUrl.unwrap(goog.html.SafeUrl.sanitize(url)));\n  } else {\n    win.location.replace(url);\n  }\n};\n\n\n/**\n * Deep comparison of two objects.\n * @param {!Object} a The first object.\n * @param {!Object} b The second object.\n * @return {!Array<string>} The list of keys that are different between both\n *     objects provided.\n */\nfireauth.util.getKeyDiff = function(a, b) {\n  var diff = [];\n  for (var k in a) {\n    if (!(k in b)) {\n      diff.push(k);\n    } else if (typeof a[k] != typeof b[k]) {\n      diff.push(k);\n    } else if (typeof a[k] == 'object' && a[k] != null && b[k] != null) {\n      if (fireauth.util.getKeyDiff(\n          a[k], b[k]).length > 0) {\n        diff.push(k);\n      }\n    } else if (a[k] !== b[k]) {\n      diff.push(k);\n    }\n  }\n  for (var k in b) {\n    if (!(k in a)) {\n      diff.push(k);\n    }\n  }\n  return diff;\n};\n\n\n/**\n * @param {?string=} opt_userAgent The navigator user agent.\n * @return {?number} The Chrome version, null if the user agent is not Chrome.\n */\nfireauth.util.getChromeVersion = function(opt_userAgent) {\n  var ua = opt_userAgent || fireauth.util.getUserAgentString();\n  var browserName = fireauth.util.getBrowserName(ua);\n  // Confirm current browser is Chrome.\n  if (browserName != fireauth.util.BrowserName.CHROME) {\n    return null;\n  }\n  var matches = ua.match(/\\sChrome\\/(\\d+)/i);\n  if (matches && matches.length == 2) {\n    return parseInt(matches[1], 10);\n  }\n  return null;\n};\n\n\n/**\n * Detects CORS support.\n * @param {?string=} opt_userAgent The navigator user agent.\n * @return {boolean} True if the browser supports CORS.\n */\nfireauth.util.supportsCors = function(opt_userAgent) {\n  // Chrome 7 has CORS issues, pick 30 as upper limit.\n  var chromeVersion = fireauth.util.getChromeVersion(opt_userAgent);\n  if (chromeVersion && chromeVersion < 30) {\n    return false;\n  }\n  // Among all other supported browsers, only IE8 and IE9 don't support CORS.\n  return !goog.userAgent.IE || // Not IE.\n      !goog.userAgent.DOCUMENT_MODE || // No document mode == IE Edge.\n      goog.userAgent.DOCUMENT_MODE > 9;\n};\n\n\n/**\n * Detects whether browser is running on a mobile device.\n * @param {?string=} opt_userAgent The navigator user agent.\n * @return {boolean} True if the browser is running on a mobile device.\n */\nfireauth.util.isMobileBrowser = function(opt_userAgent) {\n  var ua = opt_userAgent || fireauth.util.getUserAgentString();\n  var uaLower = ua.toLowerCase();\n  // TODO: implement getBrowserName equivalent for OS.\n  if (uaLower.match(/android/) ||\n      uaLower.match(/webos/) ||\n      uaLower.match(/iphone|ipad|ipod/) ||\n      uaLower.match(/blackberry/) ||\n      uaLower.match(/windows phone/) ||\n      uaLower.match(/iemobile/)) {\n    return true;\n  }\n  return false;\n};\n\n\n/**\n * Closes the provided window.\n * @param {?Window=} opt_window The optional window to close. The current window\n *     is used if none is provided.\n */\nfireauth.util.closeWindow = function(opt_window) {\n  var win = opt_window || goog.global['window'];\n  // In some browsers, in certain cases after the window closes, as seen in\n  // Samsung Galaxy S3 Android 4.4.2 stock browser, the win object here is an\n  // empty object {}. Try to catch the failure and ignore it.\n  try {\n    win.close();\n  } catch(e) {}\n};\n\n\n/**\n * Opens a popup window.\n * @param {?string=} opt_url initial URL of the popup window\n * @param {string=} opt_name title of the popup\n * @param {?number=} opt_width width of the popup\n * @param {?number=} opt_height height of the popup\n * @return {?Window} Returns the window object that was opened. This returns\n *                   null if a popup blocker prevented the window from being\n *                   opened.\n */\nfireauth.util.popup = function(opt_url, opt_name, opt_width, opt_height) {\n  var width = opt_width || 500;\n  var height = opt_height || 600;\n  var top = (window.screen.availHeight - height) / 2;\n  var left = (window.screen.availWidth - width) / 2;\n  var options = {\n    'width': width,\n    'height': height,\n    'top': top > 0 ? top : 0,\n    'left': left > 0 ? left : 0,\n    'location': true,\n    'resizable': true,\n    'statusbar': true,\n    'toolbar': false\n  };\n  // Chrome iOS 7 and 8 is returning an undefined popup win when target is\n  // specified, even though the popup is not necessarily blocked.\n  var ua = fireauth.util.getUserAgentString().toLowerCase();\n  if (opt_name) {\n    options['target'] = opt_name;\n    // This will force a new window on each call, achieving the same effect as\n    // passing a random name on each call.\n    if (goog.string.contains(ua, 'crios/')) {\n      options['target'] = '_blank';\n    }\n  }\n  var browserName = fireauth.util.getBrowserName(\n      fireauth.util.getUserAgentString());\n  if (browserName == fireauth.util.BrowserName.FIREFOX) {\n    // Firefox complains when invalid URLs are popped out. Hacky way to bypass.\n    opt_url = opt_url || 'http://localhost';\n    // Firefox disables by default scrolling on popup windows, which can create\n    // issues when the user has many Google accounts, for instance.\n    options['scrollbars'] = true;\n  }\n  // about:blank getting sanitized causing browsers like IE/Edge to display\n  // brief error message before redirecting to handler.\n  var newWin = goog.window.open(opt_url || '', options);\n  if (newWin) {\n    // Flaky on IE edge, encapsulate with a try and catch.\n    try {\n      newWin.focus();\n    } catch (e) {}\n  }\n  return newWin;\n};\n\n\n/**\n * The default value for the popup wait cycle in ms.\n * @const {number}\n * @private\n */\nfireauth.util.POPUP_WAIT_CYCLE_MS_ = 2000;\n\n\n/**\n * @param {?string=} opt_userAgent The optional user agent.\n * @return {boolean} Whether the popup requires a delay before closing itself.\n */\nfireauth.util.requiresPopupDelay = function(opt_userAgent) {\n  // TODO: remove this hack when CriOS behavior is fixed in iOS.\n  var ua = opt_userAgent || fireauth.util.getUserAgentString();\n  // Was observed in iOS 10.2 Chrome version 55.0.2883.79.\n  // Apply to Chrome 55+ iOS 10+ to ensure future Chrome versions or iOS 10\n  // minor updates do not suddenly resurface this bug. Revisit this check on\n  // next CriOS update.\n  var matches = ua.match(/OS (\\d+)_.*CriOS\\/(\\d+)\\./i);\n  if (matches && matches.length > 2) {\n    // iOS 10+ && chrome 55+.\n    return parseInt(matches[1], 10) >= 10 && parseInt(matches[2], 10) >= 55;\n  }\n  return false;\n};\n\n\n/**\n * @param {?Window} win The popup window to check.\n * @param {number=} opt_stepDuration The duration of each wait cycle before\n *     checking that window is closed.\n * @return {!goog.Promise<undefined>} The promise to resolve when window is\n *     closed.\n */\nfireauth.util.onPopupClose = function(win, opt_stepDuration) {\n  var stepDuration = opt_stepDuration || fireauth.util.POPUP_WAIT_CYCLE_MS_;\n  return new goog.Promise(function(resolve, reject) {\n    // Function to repeat each stepDuration.\n    var repeat = function() {\n      goog.Timer.promise(stepDuration).then(function() {\n        // After wait, check if window is closed.\n        if (!win || win.closed) {\n          // If so, resolve.\n          resolve();\n        } else {\n          // Call repeat again.\n          return repeat();\n        }\n      });\n    };\n    return repeat();\n  });\n};\n\n\n/**\n * @param {!Array<string>} authorizedDomains List of authorized domains.\n * @param {string} url The URL to check.\n * @return {boolean} Whether the passed domain is an authorized one.\n */\nfireauth.util.isAuthorizedDomain = function(authorizedDomains, url) {\n  var uri = goog.Uri.parse(url);\n  var scheme = uri.getScheme();\n  var domain = uri.getDomain();\n  for (var i = 0; i < authorizedDomains.length; i++) {\n    // Currently this corresponds to: domain.com = *://*.domain.com:* or\n    // exact domain match.\n    // In the case of Chrome extensions, the authorizedDomain will be formatted\n    // as 'chrome-extension://abcdefghijklmnopqrstuvwxyz123456'.\n    // The URL to check must have a chrome extension scheme and the domain\n    // must be an exact match domain == 'abcdefghijklmnopqrstuvwxyz123456'.\n    if (fireauth.util.matchDomain(authorizedDomains[i], domain, scheme)) {\n      return true;\n    }\n  }\n  return false;\n};\n\n\n/**\n * Represents the dimensions of an entity (width and height).\n * @typedef {{\n *   width: number,\n *   height: number\n * }}\n */\nfireauth.util.Dimensions;\n\n\n/**\n * @param {?Window=} opt_window The optional window whose dimensions are to be\n *     returned. The current window is used if not found.\n * @return {?fireauth.util.Dimensions} The requested window dimensions if\n *     available.\n */\nfireauth.util.getWindowDimensions = function(opt_window) {\n  var win = opt_window || goog.global['window'];\n  if (win && win['innerWidth'] && win['innerHeight']) {\n    return {\n      'width': parseFloat(win['innerWidth']),\n      'height': parseFloat(win['innerHeight'])\n    };\n  }\n  return null;\n};\n\n\n/**\n * RegExp to detect if the domain given is an IP address. This is only used\n * for validating http and https schemes.\n *\n * It does not strictly validate if the IP is a real IP address, but as the\n * matchDomain method tests against a set of valid domains (extracted from the\n * window's current URL), it is sufficient.\n *\n * @const {!RegExp}\n * @private\n */\nfireauth.util.IP_ADDRESS_REGEXP_ = /^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$/;\n\n\n/**\n * @param {string} domainPattern The domain pattern to match.\n * @param {string} domain The domain to check. It is assumed that it is a valid\n *     domain, not a user provided one.\n * @param {string} scheme The scheme of the domain to check.\n * @return {boolean} Whether the provided domain matches the domain pattern.\n */\nfireauth.util.matchDomain = function(domainPattern, domain, scheme) {\n  // Chrome extension matching.\n  if (domainPattern.indexOf('chrome-extension://') == 0) {\n    var chromeExtUri = goog.Uri.parse(domainPattern);\n    // Domain must match and the current scheme must be a Chrome extension.\n    return chromeExtUri.getDomain() == domain && scheme == 'chrome-extension';\n  } else if (scheme != 'http' && scheme != 'https') {\n    // Any other scheme that is not http or https cannot be whitelisted.\n    return false;\n  } else {\n    // domainPattern must not contain a scheme and the current scheme must be\n    // either http or https.\n    // Check if authorized domain pattern is an IP address.\n    if (fireauth.util.IP_ADDRESS_REGEXP_.test(domainPattern)) {\n      // The domain has to be exactly equal to the pattern, as an IP domain will\n      // only contain the IP, no extra character.\n      return domain == domainPattern;\n    }\n    // Dots in pattern should be escaped.\n    var escapedDomainPattern = domainPattern.split('.').join('\\\\.');\n    // Non ip address domains.\n    // domain.com = *.domain.com OR domain.com\n    var re = new RegExp(\n        '^(.+\\\\.' + escapedDomainPattern + '|' +\n        escapedDomainPattern + ')$', 'i');\n    return re.test(domain);\n  }\n};\n\n\n/**\n * RegExp to detect if the email address given is valid.\n * @const {!RegExp}\n * @private\n */\nfireauth.util.EMAIL_ADDRESS_REGEXP_ = /^[^@]+@[^@]+$/;\n\n\n/**\n * Determines if it is a valid email address.\n * @param {*} email The email address.\n * @return {boolean} Whether the email address is valid.\n */\nfireauth.util.isValidEmailAddress = function(email) {\n  return typeof email === 'string' &&\n      fireauth.util.EMAIL_ADDRESS_REGEXP_.test(email);\n};\n\n\n/**\n * @return {!goog.Promise<void>} A promise that resolves when DOM is ready.\n */\nfireauth.util.onDomReady = function() {\n  var resolver = null;\n  return new goog.Promise(function(resolve, reject) {\n    var doc = goog.global.document;\n    // If document already loaded, resolve immediately.\n    if (doc.readyState == 'complete') {\n      resolve();\n    } else {\n      // Document not ready, wait for load before resolving.\n      // Save resolver, so we can remove listener in case it was externally\n      // cancelled.\n      resolver = function() {\n        resolve();\n      };\n      goog.events.listenOnce(window, goog.events.EventType.LOAD, resolver);\n    }\n  }).thenCatch(function(error) {\n    // In case this promise was cancelled, make sure it unlistens to load.\n    goog.events.unlisten(window, goog.events.EventType.LOAD, resolver);\n    throw error;\n  });\n};\n\n\n/** @return {boolean} Whether environment supports DOM. */\nfireauth.util.isDOMSupported = function() {\n  return !!goog.global.document;\n};\n\n\n/**\n * The default ondeviceready Cordova timeout in ms.\n * @const {number}\n * @private\n */\nfireauth.util.CORDOVA_ONDEVICEREADY_TIMEOUT_MS_ = 1000;\n\n\n/**\n * @param {?string=} opt_userAgent The optional user agent.\n * @param {number=} opt_timeout The optional timeout in ms for deviceready\n *     event to resolve.\n * @return {!goog.Promise} A promise that resolves if the current environment is\n *     a Cordova environment.\n */\nfireauth.util.checkIfCordova = function(opt_userAgent, opt_timeout) {\n  // Errors generated are internal and should be converted if needed to\n  // developer facing Firebase errors.\n  // Only supported in Android/iOS environment.\n  if (fireauth.util.isAndroidOrIosCordovaScheme(opt_userAgent)) {\n    return fireauth.util.onDomReady().then(function() {\n      return new goog.Promise(function(resolve, reject) {\n        var doc = goog.global.document;\n        var timeoutId = setTimeout(function() {\n          reject(new Error('Cordova framework is not ready.'));\n        }, opt_timeout || fireauth.util.CORDOVA_ONDEVICEREADY_TIMEOUT_MS_);\n        // This should resolve immediately after DOM ready.\n        doc.addEventListener('deviceready', function() {\n          clearTimeout(timeoutId);\n          resolve();\n        }, false);\n      });\n    });\n  }\n  return goog.Promise.reject(\n      new Error('Cordova must run in an Android or iOS file scheme.'));\n};\n\n\n/**\n * @param {?string=} opt_userAgent The optional user agent.\n * @return {boolean} Whether the app is rendered in a mobile iOS or Android\n *     Cordova environment.\n */\nfireauth.util.isAndroidOrIosCordovaScheme = function(opt_userAgent) {\n  var ua = opt_userAgent || fireauth.util.getUserAgentString();\n  return !!((fireauth.util.getCurrentScheme() === 'file:' ||\n             fireauth.util.getCurrentScheme() === 'ionic:') &&\n            ua.toLowerCase().match(/iphone|ipad|ipod|android/));\n};\n\n\n/**\n * @param {?string=} opt_userAgent The optional user agent.\n * @return {boolean} Whether the app is rendered in a mobile iOS 7 or 8 browser.\n */\nfireauth.util.isIOS7Or8 = function(opt_userAgent) {\n  var ua = opt_userAgent || fireauth.util.getUserAgentString();\n  return !!(ua.match(/(iPad|iPhone|iPod).*OS 7_\\d/i) ||\n            ua.match(/(iPad|iPhone|iPod).*OS 8_\\d/i));\n};\n\n\n/**\n * @return {boolean} Whether browser is Safari or an iOS browser and page is\n *     embedded in an iframe. Local Storage does not synchronize with an iframe\n *     embedded on a page in a different domain but will still trigger storage\n *     event with storage changes.\n */\nfireauth.util.isSafariLocalStorageNotSynced = function() {\n  var ua = fireauth.util.getUserAgentString();\n  // Safari or iOS browser and embedded in an iframe.\n  if (!fireauth.util.iframeCanSyncWebStorage(ua) && fireauth.util.isIframe()) {\n    return true;\n  }\n  return false;\n};\n\n\n/**\n * @param {?Window=} opt_win Optional window to check whether it is an iframe.\n *     If not provided, the current window is checked.\n * @return {boolean} Whether web page is running in an iframe.\n */\nfireauth.util.isIframe = function(opt_win) {\n  var win = opt_win || goog.global['window'];\n  try {\n    // Check that the current window is not the top window.\n    // If so, return true.\n    return !!(win && win != win['top']);\n  } catch (e) {\n    return false;\n  }\n};\n\n\n/**\n * @param {?Window=} opt_win Optional window to check whether it has an opener\n *     that is an iframe.\n * @return {boolean} Whether the web page was opened from an iframe.\n */\nfireauth.util.isOpenerAnIframe = function(opt_win) {\n  var win = opt_win || goog.global['window'];\n  try {\n    // Get the opener if available.\n    var opener = win && win['opener'];\n    // Check if the opener is an iframe. If so, return true.\n    // Confirm opener is available, otherwise the current window is checked\n    // instead.\n    return !!(opener && fireauth.util.isIframe(opener));\n  } catch (e) {\n    return false;\n  }\n};\n\n\n/**\n * @param {?Object=} global The optional global scope.\n * @return {boolean} Whether current environment is a worker.\n */\nfireauth.util.isWorker = function(global) {\n  var scope = global || goog.global;\n  // WorkerGlobalScope only defined in worker environment.\n  return typeof scope['WorkerGlobalScope'] !== 'undefined' &&\n         typeof scope['importScripts'] === 'function';\n};\n\n\n/**\n * @param {?Object=} opt_global The optional global scope.\n * @return {boolean} Whether current environment supports fetch API and other\n *     APIs it depends on.\n */\nfireauth.util.isFetchSupported = function(opt_global) {\n  // Required by fetch API calls.\n  var scope = opt_global || goog.global;\n  return typeof scope['fetch'] !== 'undefined' &&\n         typeof scope['Headers'] !== 'undefined' &&\n         typeof scope['Request'] !== 'undefined';\n};\n\n\n/**\n * Enum for the runtime environment.\n * @enum {string}\n */\nfireauth.util.Env = {\n  BROWSER: 'Browser',\n  NODE: 'Node',\n  REACT_NATIVE: 'ReactNative',\n  WORKER: 'Worker'\n};\n\n\n/**\n * @return {!fireauth.util.Env} The current runtime environment.\n */\nfireauth.util.getEnvironment = function() {\n  if (firebase.INTERNAL.hasOwnProperty('reactNative')) {\n    return fireauth.util.Env.REACT_NATIVE;\n  } else if (firebase.INTERNAL.hasOwnProperty('node')) {\n    // browserify seems to keep the process property in some cases even though\n    // the library is browser only. Use this check instead to reliably detect\n    // a Node.js environment.\n    return fireauth.util.Env.NODE;\n  } else if (fireauth.util.isWorker()) {\n    // Worker environment.\n    return fireauth.util.Env.WORKER;\n  }\n  // The default is a browser environment.\n  return fireauth.util.Env.BROWSER;\n};\n\n\n/**\n * @return {boolean} Whether the environment is a native environment, where\n *     CORS checks do not apply.\n */\nfireauth.util.isNativeEnvironment = function() {\n  var environment = fireauth.util.getEnvironment();\n  return environment === fireauth.util.Env.REACT_NATIVE ||\n      environment === fireauth.util.Env.NODE;\n};\n\n\n/**\n * The separator for storage keys to concatenate App name and API key.\n * @const {string}\n * @private\n */\nfireauth.util.STORAGE_KEY_SEPARATOR_ = ':';\n\n\n/**\n * @param {string} apiKey The API Key of the app.\n * @param {string} appName The App name.\n * @return {string} The key used for identifying the app owner of the user.\n */\nfireauth.util.createStorageKey = function(apiKey, appName) {\n  return apiKey + fireauth.util.STORAGE_KEY_SEPARATOR_ + appName;\n};\n\n\n/** @return {string} a long random character string. */\nfireauth.util.generateRandomString = function() {\n  return Math.floor(Math.random() * 1000000000).toString();\n};\n\n\n/**\n * Generates a random alpha numeric string.\n * @param {number} numOfChars The number of random characters within the string.\n * @return {string} A string with a specific number of random characters.\n */\nfireauth.util.generateRandomAlphaNumericString = function(numOfChars) {\n  var chars = [];\n  var allowedChars =\n      '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';\n  while (numOfChars > 0) {\n    chars.push(\n        allowedChars.charAt(\n            Math.floor(Math.random() * allowedChars.length)));\n    numOfChars--;\n  }\n  return chars.join('');\n};\n\n\n/**\n * Enums for Browser name.\n * @enum {string}\n */\nfireauth.util.BrowserName = {\n  ANDROID: 'Android',\n  BLACKBERRY: 'Blackberry',\n  EDGE: 'Edge',\n  FIREFOX: 'Firefox',\n  IE: 'IE',\n  IEMOBILE: 'IEMobile',\n  OPERA: 'Opera',\n  OTHER: 'Other',\n  CHROME: 'Chrome',\n  SAFARI: 'Safari',\n  SILK: 'Silk',\n  WEBOS: 'Webos'\n};\n\n\n/**\n * @param {string} userAgent The navigator user agent string.\n * @return {string} The browser name, eg Safari, Firefox, etc.\n */\nfireauth.util.getBrowserName = function(userAgent) {\n  var ua = userAgent.toLowerCase();\n  if (goog.string.contains(ua, 'opera/') ||\n      goog.string.contains(ua, 'opr/') ||\n      goog.string.contains(ua, 'opios/')) {\n    return fireauth.util.BrowserName.OPERA;\n  } else if (goog.string.contains(ua, 'iemobile')) {\n    // Windows phone IEMobile browser.\n    return fireauth.util.BrowserName.IEMOBILE;\n  } else if (goog.string.contains(ua, 'msie') ||\n             goog.string.contains(ua, 'trident/')) {\n    return fireauth.util.BrowserName.IE;\n  } else if (goog.string.contains(ua, 'edge/')) {\n    return fireauth.util.BrowserName.EDGE;\n  } else if (goog.string.contains(ua, 'firefox/')) {\n    return fireauth.util.BrowserName.FIREFOX;\n  } else if (goog.string.contains(ua, 'silk/')) {\n    return fireauth.util.BrowserName.SILK;\n  } else if (goog.string.contains(ua, 'blackberry')) {\n    // Blackberry browser.\n    return fireauth.util.BrowserName.BLACKBERRY;\n  } else if (goog.string.contains(ua, 'webos')) {\n    // WebOS default browser.\n    return fireauth.util.BrowserName.WEBOS;\n  } else if (goog.string.contains(ua, 'safari/') &&\n             !goog.string.contains(ua, 'chrome/') &&\n             !goog.string.contains(ua, 'crios/') &&\n             !goog.string.contains(ua, 'android')) {\n    return fireauth.util.BrowserName.SAFARI;\n  } else if ((goog.string.contains(ua, 'chrome/') ||\n              goog.string.contains(ua, 'crios/')) &&\n             !goog.string.contains(ua, 'edge/')) {\n    return fireauth.util.BrowserName.CHROME;\n  } else if (goog.string.contains(ua, 'android')) {\n    // Android stock browser.\n    return fireauth.util.BrowserName.ANDROID;\n  } else {\n    // Most modern browsers have name/version at end of user agent string.\n    var re = new RegExp('([a-zA-Z\\\\d\\\\.]+)\\/[a-zA-Z\\\\d\\\\.]*$');\n    var matches = userAgent.match(re);\n    if (matches && matches.length == 2) {\n      return matches[1];\n    }\n  }\n  return fireauth.util.BrowserName.OTHER;\n};\n\n\n/**\n * Enums for client implementation name.\n * @enum {string}\n */\nfireauth.util.ClientImplementation = {\n  JSCORE: 'JsCore',\n  OAUTH_HANDLER: 'Handler',\n  OAUTH_IFRAME: 'Iframe'\n};\n\n\n/**\n * Enums for the framework ID to be logged in RPC header.\n * Future frameworks to possibly add: angularfire, polymerfire, reactfire, etc.\n * @enum {string}.\n */\nfireauth.util.Framework = {\n  // No other framework used.\n  DEFAULT: 'FirebaseCore-web',\n  // Firebase Auth used with FirebaseUI-web.\n  FIREBASEUI: 'FirebaseUI-web'\n};\n\n\n/**\n * @param {!Array<string>} providedFrameworks List of framework ID strings.\n * @return {!Array<!fireauth.util.Framework>} List of supported framework IDs\n *     with no duplicates.\n */\nfireauth.util.getFrameworkIds = function(providedFrameworks) {\n  var frameworkVersion = [];\n  var frameworkSet = {};\n  for (var key in fireauth.util.Framework) {\n    frameworkSet[fireauth.util.Framework[key]] = true;\n  }\n  for (var i = 0; i < providedFrameworks.length; i++) {\n    if (typeof frameworkSet[providedFrameworks[i]] !== 'undefined') {\n      // Delete it from set to prevent duplications.\n      delete frameworkSet[providedFrameworks[i]];\n      frameworkVersion.push(providedFrameworks[i]);\n    }\n  }\n  // Sort alphabetically so that \"FirebaseCore-web,FirebaseUI-web\" and\n  // \"FirebaseUI-web,FirebaseCore-web\" aren't viewed as different.\n  frameworkVersion.sort();\n  return frameworkVersion;\n};\n\n\n/**\n * @param {!fireauth.util.ClientImplementation} clientImplementation The client\n *     implementation.\n * @param {string} clientVersion The client version.\n * @param {?Array<string>=} opt_frameworkVersion The framework version.\n * @param {?string=} opt_userAgent The optional user agent.\n * @return {string} The full client SDK version.\n */\nfireauth.util.getClientVersion = function(clientImplementation, clientVersion,\n    opt_frameworkVersion, opt_userAgent) {\n  var frameworkVersion = fireauth.util.getFrameworkIds(\n      opt_frameworkVersion || []);\n  if (!frameworkVersion.length) {\n    frameworkVersion = [fireauth.util.Framework.DEFAULT];\n  }\n  var environment = fireauth.util.getEnvironment();\n  var reportedEnvironment = '';\n  if (environment === fireauth.util.Env.BROWSER) {\n    // In a browser environment, report the browser name.\n    var userAgent = opt_userAgent || fireauth.util.getUserAgentString();\n    reportedEnvironment = fireauth.util.getBrowserName(userAgent);\n  } else if (environment === fireauth.util.Env.WORKER) {\n    // Technically a worker runs from a browser but we need to differentiate a\n    // worker from a browser.\n    // For example: Chrome-Worker/JsCore/4.9.1/FirebaseCore-web.\n    var userAgent = opt_userAgent || fireauth.util.getUserAgentString();\n    reportedEnvironment = fireauth.util.getBrowserName(userAgent) + '-' +\n        environment;\n  } else {\n    // Otherwise, just report the environment name.\n    reportedEnvironment = environment;\n  }\n  // The format to be followed:\n  // ${browserName}/${clientImplementation}/${clientVersion}/${frameworkVersion}\n  // As multiple Firebase frameworks/libraries can be used, join their IDs with\n  // a comma.\n  return reportedEnvironment + '/' + clientImplementation +\n      '/' + clientVersion + '/' + frameworkVersion.join(',');\n};\n\n\n/**\n * @return {string} The user agent string reported by the environment, or the\n *     empty string if not available.\n */\nfireauth.util.getUserAgentString = function() {\n  return (goog.global['navigator'] && goog.global['navigator']['userAgent']) ||\n      '';\n};\n\n\n/**\n * @param {string} varStrName The variable string name.\n * @param {?Object=} opt_scope The optional scope where to look in. The default\n *     is window.\n * @return {*} The reference if found.\n */\nfireauth.util.getObjectRef = function(varStrName, opt_scope) {\n  var pieces = varStrName.split('.');\n  var last = opt_scope || goog.global;\n  for (var i = 0;\n       i < pieces.length && typeof last == 'object' && last != null;\n       i++) {\n    last = last[pieces[i]];\n  }\n  // Last hasn't reached the end yet, return undefined.\n  if (i != pieces.length) {\n    last = undefined;\n  }\n  return last;\n};\n\n\n/** @return {boolean} Whether web storage is supported. */\nfireauth.util.isWebStorageSupported = function() {\n  try {\n    var storage = goog.global['localStorage'];\n    var key = fireauth.util.generateEventId();\n    if (storage) {\n      // setItem will throw an exception if we cannot access WebStorage (e.g.,\n      // Safari in private mode).\n      storage['setItem'](key, '1');\n      storage['removeItem'](key);\n      // For browsers where iframe web storage does not synchronize with a popup\n      // of the same domain, indexedDB is used for persistent storage. These\n      // browsers include IE11 and Edge.\n      // Make sure it is supported (IE11 and Edge private mode does not support\n      // that).\n      if (fireauth.util.isLocalStorageNotSynchronized()) {\n        // In such browsers, if indexedDB is not supported, an iframe cannot be\n        // notified of the popup sign in result.\n        return !!goog.global['indexedDB'];\n      }\n      return true;\n    }\n  } catch (e) {\n    // localStorage is not available from a worker. Test availability of\n    // indexedDB.\n    return fireauth.util.isWorker() && !!goog.global['indexedDB'];\n  }\n  return false;\n};\n\n\n/**\n * This guards against leaking Cordova support before official launch.\n * This field will be removed or updated to return true when the new feature is\n * ready for launch.\n * @return {boolean} Whether Cordova OAuth support is enabled.\n */\nfireauth.util.isCordovaOAuthEnabled = function() {\n  return false;\n};\n\n\n/**\n * @return {boolean} Whether popup and redirect operations are supported in the\n *     current environment.\n */\nfireauth.util.isPopupRedirectSupported = function() {\n  // Popup and redirect are supported in an environment where the container\n  // origin can be securely whitelisted.\n  return (fireauth.util.isHttpOrHttps() ||\n          fireauth.util.isChromeExtension() ||\n          fireauth.util.isAndroidOrIosCordovaScheme()) &&\n         // React Native with remote debugging reports its location.protocol as\n         // http.\n         !fireauth.util.isNativeEnvironment() &&\n         // Local storage has to be supported for browser popup and redirect\n         // operations to work.\n         fireauth.util.isWebStorageSupported() &&\n         // DOM, popups and redirects are not supported within a worker.\n         !fireauth.util.isWorker();\n};\n\n\n/**\n * @return {boolean} Whether the current environment is http or https.\n */\nfireauth.util.isHttpOrHttps = function() {\n  return fireauth.util.getCurrentScheme() === 'http:' ||\n       fireauth.util.getCurrentScheme() === 'https:';\n};\n\n\n/** @return {?string} The current URL scheme. */\nfireauth.util.getCurrentScheme = function() {\n  return (goog.global['location'] && goog.global['location']['protocol']) ||\n      null;\n};\n\n\n/**\n * Checks whether the current page is a Chrome extension.\n * @return {boolean} Whether the current page is a Chrome extension.\n */\nfireauth.util.isChromeExtension = function() {\n  return fireauth.util.getCurrentScheme() === 'chrome-extension:';\n};\n\n\n/**\n * @param {?string=} opt_userAgent The optional user agent.\n * @return {boolean} Whether the current browser is running in an iOS\n *     environment.\n */\nfireauth.util.isIOS = function(opt_userAgent) {\n  var ua = opt_userAgent || fireauth.util.getUserAgentString();\n  return !!ua.toLowerCase().match(/iphone|ipad|ipod/);\n};\n\n\n/**\n * @param {?string=} opt_userAgent The optional user agent.\n * @return {boolean} Whether the current browser is running in an Android\n *     environment.\n */\nfireauth.util.isAndroid = function(opt_userAgent) {\n  var ua = opt_userAgent || fireauth.util.getUserAgentString();\n  return !!ua.toLowerCase().match(/android/);\n};\n\n\n/**\n * @param {?string=} opt_userAgent The optional user agent.\n * @return {boolean} Whether the opener of a popup cannot communicate with the\n *     popup while it is in the foreground.\n */\nfireauth.util.runsInBackground = function(opt_userAgent) {\n  // TODO: split this check into 2, one check that opener can access\n  // popup, another check that storage synchronizes between popup and opener.\n  // Popup events fail in iOS version 7 (lowest version we currently support)\n  // browsers. When the popup is triggered, the opener is unable to redirect\n  // the popup url, close the popup and in some cases will miss the storage\n  // event triggered when localStorage is changed.\n  // Extend this to all mobile devices. This behavior is more likely to work\n  // cross mobile platforms.\n  var ua = opt_userAgent || fireauth.util.getUserAgentString();\n  if (fireauth.util.isMobileBrowser(ua)) {\n    return false;\n  } else if (fireauth.util.getBrowserName(ua) ==\n             fireauth.util.BrowserName.FIREFOX) {\n    // Latest version of Firefox 47.0 does not allow you to access properties on\n    // the popup window from the opener.\n    return false;\n  }\n  return true;\n};\n\n\n/**\n * Stringifies an object, retuning null if the object is not defined.\n * @param {*} obj The raw object.\n * @return {?string} The JSON-serialized object.\n */\nfireauth.util.stringifyJSON = function(obj) {\n  if (typeof obj === 'undefined') {\n    return null;\n  }\n  return goog.json.serialize(obj);\n};\n\n\n/**\n * @param {!Object} obj The original object.\n * @return {!Object} A copy of the original object with all entries that are\n *     null or undefined removed.\n */\nfireauth.util.copyWithoutNullsOrUndefined = function(obj) {\n  // The processed copy to return.\n  var trimmedObj = {};\n  // Remove all empty fields from data, allow zero and false booleans.\n  for (var key in obj) {\n    if (obj.hasOwnProperty(key) &&\n        obj[key] !== null &&\n        obj[key] !== undefined) {\n      trimmedObj[key] = obj[key];\n    }\n  }\n  return trimmedObj;\n};\n\n\n/**\n * Removes all key/pairs with the specified keys from the given object.\n * @param {!Object} obj The object to process.\n * @param {!Array<string>} keys The list of keys to remove.\n * @return {!Object} The object with the keys removed.\n */\nfireauth.util.removeEntriesWithKeys = function(obj, keys) {\n  // Clone object.\n  var copy = goog.object.clone(obj);\n  // Traverse keys.\n  for (var i = 0; i < keys.length; i++) {\n    var key = keys[i];\n    // If key found in object, remove it.\n    if (key in copy) {\n      delete copy[key];\n    }\n  }\n  // Returned filtered copy.\n  return copy;\n};\n\n\n/**\n * Parses a JSON string, returning undefined if null is passed.\n * @param {?string} json The JSON-serialized object.\n * @return {*} The raw object.\n */\nfireauth.util.parseJSON = function(json) {\n  if (json === null) {\n    return undefined;\n  }\n\n  // Do not use goog.json.parse since it uses eval underneath to support old\n  // browsers that do not provide JSON.parse. The recommended Content Security\n  // Policy does not allow unsafe-eval in some environments like Chrome\n  // extensions. Usage of eval is not recommend in Chrome in general.\n  // Use native parsing instead via JSON.parse. This is provided in our list\n  // of supported browsers.\n  return JSON.parse(json);\n};\n\n\n/**\n * @param {?string=} opt_prefix An optional prefix string to prepend to ID.\n * @return {string} The generated event ID used to identify a generic event.\n */\nfireauth.util.generateEventId = function(opt_prefix) {\n  return opt_prefix ? opt_prefix : '' +\n      Math.floor(Math.random() * 1000000000).toString();\n};\n\n\n/**\n * @param {?string=} opt_userAgent The optional user agent.\n * @return {boolean} Whether an embedded iframe can sync to web storage changes.\n *     Web storage sync fails in Safari desktop browsers and iOS mobile\n *     browsers.\n */\nfireauth.util.iframeCanSyncWebStorage = function(opt_userAgent) {\n  var ua = opt_userAgent || fireauth.util.getUserAgentString();\n  if (fireauth.util.getBrowserName(ua) == fireauth.util.BrowserName.SAFARI ||\n      ua.toLowerCase().match(/iphone|ipad|ipod/)) {\n    return false;\n  }\n  return true;\n};\n\n\n/**\n * Reset unlaoded GApi modules. If gapi.load fails due to a network error,\n * it will stop working after a retrial. This is a hack to fix this issue.\n */\nfireauth.util.resetUnloadedGapiModules = function() {\n  // Clear last failed gapi.load state to force next gapi.load to first\n  // load the failed gapi.iframes module.\n  // Get gapix.beacon context.\n  var beacon = goog.global['___jsl'];\n  // Get current hint.\n  if (beacon && beacon['H']) {\n    // Get gapi hint.\n    for (var hint in beacon['H']) {\n      // Requested modules.\n      beacon['H'][hint]['r'] = beacon['H'][hint]['r'] || [];\n      // Loaded modules.\n      beacon['H'][hint]['L'] = beacon['H'][hint]['L'] || [];\n      // Set requested modules to a copy of the loaded modules.\n      beacon['H'][hint]['r'] = beacon['H'][hint]['L'].concat();\n      // Clear pending callbacks.\n      if (beacon['CP']) {\n        for (var i = 0; i < beacon['CP'].length; i++) {\n          // Remove all failed pending callbacks.\n          beacon['CP'][i] = null;\n        }\n      }\n    }\n  }\n};\n\n\n/**\n * Returns whether the current device is a mobile device. Mobile browsers and\n * React-Native environments are considered mobile devices.\n * @param {?string=} opt_userAgent The optional navigator user agent.\n * @param {?fireauth.util.Env=} opt_env The optional environment.\n * @return {boolean} Whether the current device is a mobile device or not.\n */\nfireauth.util.isMobileDevice = function(opt_userAgent, opt_env) {\n  // Get user agent.\n  var ua = opt_userAgent || fireauth.util.getUserAgentString();\n  // Get environment.\n  var environment = opt_env || fireauth.util.getEnvironment();\n  return fireauth.util.isMobileBrowser(ua) ||\n      environment === fireauth.util.Env.REACT_NATIVE;\n};\n\n\n/**\n * @param {?Object=} opt_navigator The optional navigator object typically used\n *     for testing.\n * @return {boolean} Whether the app is currently online. If offline, false is\n *     returned. If this cannot be determined, true is returned.\n */\nfireauth.util.isOnline = function(opt_navigator) {\n  var navigator = opt_navigator || goog.global['navigator'];\n  if (navigator &&\n      typeof navigator['onLine'] === 'boolean' &&\n      // Apply only for traditional web apps and Chrome extensions.\n      // This is especially true for Cordova apps which have unreliable\n      // navigator.onLine behavior unless cordova-plugin-network-information is\n      // installed which overwrites the native navigator.onLine value and\n      // defines navigator.connection.\n      (fireauth.util.isHttpOrHttps() ||\n       fireauth.util.isChromeExtension() ||\n       typeof navigator['connection'] !== 'undefined')) {\n    return navigator['onLine'];\n  }\n  // If we can't determine the state, assume it is online.\n  return true;\n};\n\n\n/**\n * @param {?Object=} opt_navigator The object with navigator data, defaulting\n *     to window.navigator if unspecified.\n * @return {?string} The user's preferred language. Returns null if\n */\nfireauth.util.getUserLanguage = function(opt_navigator) {\n  var navigator = opt_navigator || goog.global['navigator'];\n  if (!navigator) {\n    return null;\n  }\n  return (\n      // Most reliable, but only supported in Chrome/Firefox.\n      navigator['languages'] && navigator['languages'][0] ||\n      // Supported in most browsers, but returns the language of the browser\n      // UI, not the language set in browser settings.\n      navigator['language'] ||\n      // IE <= 10.\n      navigator['userLanguage'] ||\n      // Couldn't determine language.\n      null\n  );\n};\n\n\n/**\n * A structure to help pick between a range of long and short delay durations\n * depending on the current environment. In general, the long delay is used for\n * mobile environments whereas short delays are used for desktop environments.\n * @param {number} shortDelay The short delay duration.\n * @param {number} longDelay The long delay duration.\n * @param {?string=} opt_userAgent The optional navigator user agent.\n * @param {?fireauth.util.Env=} opt_env The optional environment.\n * @constructor\n */\nfireauth.util.Delay = function(shortDelay, longDelay, opt_userAgent, opt_env) {\n  // Internal error when improperly initialized.\n  if (shortDelay > longDelay) {\n    throw new Error('Short delay should be less than long delay!');\n  }\n  /**\n   * @private @const {number} The short duration delay used for desktop\n   *     environments.\n   */\n  this.shortDelay_ = shortDelay;\n  /**\n   * @private @const {number} The long duration delay used for mobile\n   *     environments.\n   */\n  this.longDelay_ = longDelay;\n  /** @private @const {boolean} Whether the environment is a mobile one. */\n  this.isMobile_ = fireauth.util.isMobileDevice(opt_userAgent, opt_env);\n};\n\n\n/**\n * The default value for the offline delay timeout in ms.\n * @const {number}\n * @private\n */\nfireauth.util.Delay.OFFLINE_DELAY_MS_ = 5000;\n\n\n/**\n * @return {number} The delay that matches with the current environment.\n */\nfireauth.util.Delay.prototype.get = function() {\n  // navigator.onLine is unreliable in some cases.\n  // Failing hard in those cases may make it impossible to recover for end user.\n  // Waiting for the regular full duration when there is no network can result\n  // in a bad experience.\n  // Instead return a short timeout duration. If there is no network connection,\n  // the user would wait 5 seconds to detect that. If there is a connection\n  // (false alert case), the user still has the ability to try to send the\n  // request. If it fails (timeout too short), they can still retry.\n  if (!fireauth.util.isOnline()) {\n    // Pick the shorter timeout.\n    return Math.min(fireauth.util.Delay.OFFLINE_DELAY_MS_, this.shortDelay_);\n  }\n  // If running in a mobile environment, return the long delay, otherwise\n  // return the short delay.\n  // This could be improved in the future to dynamically change based on other\n  // variables instead of just reading the current environment.\n  return this.isMobile_ ? this.longDelay_ : this.shortDelay_;\n};\n\n\n/**\n * @return {boolean} Whether the app is visible in the foreground. This uses\n *     document.visibilityState. For browsers that do not support it, this is\n *     always true.\n */\nfireauth.util.isAppVisible = function() {\n  // https://developer.mozilla.org/en-US/docs/Web/API/Document/visibilityState\n  var doc = goog.global.document;\n  // Check if supported.\n  if (doc && typeof doc['visibilityState'] !== 'undefined') {\n    // Check if visible.\n    return doc['visibilityState'] == 'visible';\n  }\n  // API not supported in current browser, default to true.\n  return true;\n};\n\n\n/**\n * @return {!goog.Promise} A promise that resolves when the app is visible in\n *     the foreground.\n */\nfireauth.util.onAppVisible = function() {\n  var doc = goog.global.document;\n  // Visibility change listener reference.\n  var onVisibilityChange = null;\n  if (fireauth.util.isAppVisible() || !doc) {\n    // Visible or non browser environment.\n    return goog.Promise.resolve();\n  } else {\n    // Invisible and in browser environment.\n    return new goog.Promise(function(resolve, reject) {\n      // On visibility change listener.\n      onVisibilityChange = function(event) {\n        // App is visible.\n        if (fireauth.util.isAppVisible()) {\n          // Unregister event listener.\n          doc.removeEventListener(\n              'visibilitychange', onVisibilityChange, false);\n          // Resolve promise.\n          resolve();\n        }\n      };\n      // Listen to visibility change.\n      doc.addEventListener('visibilitychange', onVisibilityChange, false);\n    }).thenCatch(function(error) {\n      // In case this promise was cancelled, make sure it unlistens to\n      // visibilitychange event.\n      doc.removeEventListener('visibilitychange', onVisibilityChange, false);\n      // Rethrow the same error.\n      throw error;\n    });\n  }\n};\n\n\n/**\n * Logs a warning message to the console, if the console is available.\n * @param {string} message\n */\nfireauth.util.consoleWarn = function(message) {\n  if (typeof console !== 'undefined' && typeof console.warn === 'function') {\n    console.warn(message);\n  }\n};\n\n\n/**\n * Logs an info message to the console, if the console is available.\n * @param {string} message\n */\nfireauth.util.consoleInfo = function(message) {\n  if (typeof console !== 'undefined' && typeof console.info === 'function') {\n    console.info(message);\n  }\n};\n\n\n/**\n * Parses a UTC time stamp string or number and returns the corresponding UTC\n * date string if valid. Otherwise, returns null.\n * @param {?string|number} utcTimestamp The UTC timestamp number or string.\n * @return {?string} The corresponding UTC date string. Null if invalid.\n */\nfireauth.util.utcTimestampToDateString = function(utcTimestamp) {\n  try {\n    // Convert to date object.\n    var date = new Date(parseInt(utcTimestamp, 10));\n    // Test date is valid.\n    if (!isNaN(date.getTime()) &&\n        // Confirm that utcTimestamp is numeric.\n        goog.string.isNumeric(utcTimestamp)) {\n      // Convert to UTC date string.\n      return date.toUTCString();\n    }\n  } catch (e) {\n    // Do nothing. null will be returned.\n  }\n  return null;\n};\n\n\n/** @return {boolean} Whether indexedDB is available. */\nfireauth.util.isIndexedDBAvailable = function() {\n  return !!goog.global['indexedDB'];\n};\n\n\n/** @return {boolean} Whether current mode is Auth handler or iframe. */\nfireauth.util.isAuthHandlerOrIframe = function() {\n  return !!(fireauth.util.getObjectRef('fireauth.oauthhelper', goog.global) ||\n            fireauth.util.getObjectRef('fireauth.iframe', goog.global));\n};\n\n\n/** @return {boolean} Whether indexedDB is used to persist storage. */\nfireauth.util.persistsStorageWithIndexedDB = function() {\n  // This will cover:\n  // IE11, Edge when indexedDB is available (this is unavailable in InPrivate\n  // mode). (SDK, OAuth handler and iframe)\n  // Any environment where indexedDB is available (SDK only).\n\n  // In a browser environment, when an iframe and a popup web storage are not\n  // synchronized, use the indexedDB fireauth.storage.Storage implementation.\n  return (fireauth.util.isLocalStorageNotSynchronized() ||\n          !fireauth.util.isAuthHandlerOrIframe()) &&\n         fireauth.util.isIndexedDBAvailable();\n};\n\n\n/** Sets the no-referrer meta tag in the document head if applicable. */\nfireauth.util.setNoReferrer = function() {\n  var doc = goog.global.document;\n  if (doc) {\n    try {\n      var meta = goog.dom.createDom(goog.dom.TagName.META, {\n        'name': 'referrer',\n        'content': 'no-referrer'\n      });\n      var headCollection = goog.dom.getElementsByTagName(goog.dom.TagName.HEAD);\n      // Append meta tag to head.\n      if (headCollection.length) {\n        headCollection[0].appendChild(meta);\n      }\n    } catch (e) {\n      // Best effort approach.\n    }\n  }\n};\n\n\n/** @return {?ServiceWorker} The servicerWorker controller if available. */\nfireauth.util.getServiceWorkerController = function() {\n  var navigator = goog.global['navigator'];\n  return (navigator &&\n          navigator.serviceWorker &&\n          navigator.serviceWorker.controller) || null;\n};\n\n\n/** @return {?WorkerGlobalScope} The worker global scope if available. */\nfireauth.util.getWorkerGlobalScope = function() {\n  return fireauth.util.isWorker() ? /** @type {!WorkerGlobalScope} */ (self) :\n      null;\n};\n\n/**\n * @return {!goog.Promise<?ServiceWorker>} A promise that resolves with the\n *     service worker. This will resolve only when a service worker becomes\n *     available. If no service worker is supported, it will resolve with null.\n */\nfireauth.util.getActiveServiceWorker = function() {\n  var navigator = goog.global['navigator'];\n  if (navigator && navigator.serviceWorker) {\n    return goog.Promise.resolve()\n        .then(function() {\n          return navigator.serviceWorker.ready;\n        })\n        .then(function(registration) {\n          return /** @type {?ServiceWorker} */ (registration.active || null);\n        })\n        .thenCatch(function(error) {\n          return null;\n        });\n  }\n  return goog.Promise.resolve(/** @type {?ServiceWorker} */ (null));\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Utilities for window manipulation.\n */\n\n\ngoog.provide('goog.window');\n\ngoog.require('goog.dom');\ngoog.require('goog.dom.TagName');\ngoog.require('goog.dom.safe');\ngoog.require('goog.html.SafeUrl');\ngoog.require('goog.html.uncheckedconversions');\ngoog.require('goog.labs.userAgent.platform');\ngoog.require('goog.string');\ngoog.require('goog.string.Const');\ngoog.require('goog.userAgent');\ngoog.requireType('goog.string.TypedString');\n\n\n/**\n * Default height for popup windows\n * @type {number}\n */\ngoog.window.DEFAULT_POPUP_HEIGHT = 500;\n\n\n/**\n * Default width for popup windows\n * @type {number}\n */\ngoog.window.DEFAULT_POPUP_WIDTH = 690;\n\n\n/**\n * Default target for popup windows\n * @type {string}\n */\ngoog.window.DEFAULT_POPUP_TARGET = 'google_popup';\n\n\n/**\n * @return {!Window}\n * @suppress {checkTypes}\n * @private\n */\ngoog.window.createFakeWindow_ = function() {\n  'use strict';\n  return /** @type {!Window} */ ({});\n};\n\n/**\n * Opens a new window.\n *\n * @param {!goog.html.SafeUrl|string|!Object|null} linkRef If an Object with an\n *     'href' attribute (such as HTMLAnchorElement) is passed then the value of\n *     'href' is used, otherwise its toString method is called. Note that if a\n *     string|Object is used, it will be sanitized with SafeUrl.sanitize().\n *\n * @param {?Object=} opt_options supports the following options:\n *  'target': (string) target (window name). If null, linkRef.target will\n *      be used.\n *  'width': (number) window width.\n *  'height': (number) window height.\n *  'top': (number) distance from top of screen\n *  'left': (number) distance from left of screen\n *  'toolbar': (boolean) show toolbar\n *  'scrollbars': (boolean) show scrollbars\n *  'location': (boolean) show location\n *  'statusbar': (boolean) show statusbar\n *  'menubar': (boolean) show menubar\n *  'resizable': (boolean) resizable\n *  'noreferrer': (boolean) whether to attempt to remove the referrer header\n *      from the request headers. Does this by opening a blank window that\n *      then redirects to the target url, so users may see some flickering.\n *  'noopener': (boolean) whether to remove the `opener` property from the\n *      window object of the newly created window. The property contains a\n *      reference to the original window, and can be used to launch a\n *      reverse tabnabbing attack.\n *\n * @param {?Window=} opt_parentWin Parent window that should be used to open the\n *                 new window.\n *\n * @return {?Window} Returns the window object that was opened. This returns\n *                  null if a popup blocker prevented the window from being\n *                  opened. In case when a new window is opened in a different\n *                  browser sandbox (such as iOS standalone mode), the returned\n *                  object is a emulated Window object that functions as if\n *                  a cross-origin window has been opened.\n */\ngoog.window.open = function(linkRef, opt_options, opt_parentWin) {\n  'use strict';\n  if (!opt_options) {\n    opt_options = {};\n  }\n  var parentWin = opt_parentWin || window;\n\n  /** @type {!goog.html.SafeUrl} */\n  var safeLinkRef;\n\n  if (linkRef instanceof goog.html.SafeUrl) {\n    safeLinkRef = linkRef;\n  } else {\n    // HTMLAnchorElement has a toString() method with the same behavior as\n    // goog.Uri in all browsers except for Safari, which returns\n    // '[object HTMLAnchorElement]'.  We check for the href first, then\n    // assume that it's a goog.Uri or String otherwise.\n    /**\n     * @type {string|!goog.string.TypedString}\n     * @suppress {missingProperties}\n     */\n    var url =\n        typeof linkRef.href != 'undefined' ? linkRef.href : String(linkRef);\n    safeLinkRef = goog.html.SafeUrl.sanitize(url);\n  }\n\n  /** @suppress {missingProperties} loose references to 'target' */\n  /** @suppress {strictMissingProperties} */\n  var target = opt_options.target || linkRef.target;\n\n  var sb = [];\n  for (var option in opt_options) {\n    switch (option) {\n      case 'width':\n      case 'height':\n      case 'top':\n      case 'left':\n        sb.push(option + '=' + opt_options[option]);\n        break;\n      case 'target':\n      case 'noopener':\n      case 'noreferrer':\n        break;\n      default:\n        sb.push(option + '=' + (opt_options[option] ? 1 : 0));\n    }\n  }\n  var optionString = sb.join(',');\n\n  var newWin;\n  if (goog.labs.userAgent.platform.isIos() && parentWin.navigator &&\n      parentWin.navigator['standalone'] && target && target != '_self') {\n    // iOS in standalone mode disregards \"target\" in window.open and always\n    // opens new URL in the same window. The workaround is to create an \"A\"\n    // element and send a click event to it.\n    // Notice that the \"A\" tag does NOT have to be added to the DOM.\n\n    var a = goog.dom.createElement(goog.dom.TagName.A);\n    goog.dom.safe.setAnchorHref(a, safeLinkRef);\n\n    a.setAttribute('target', target);\n    if (opt_options['noreferrer']) {\n      a.setAttribute('rel', 'noreferrer');\n    }\n\n    var click = /** @type {!MouseEvent} */ (document.createEvent('MouseEvent'));\n    click.initMouseEvent(\n        'click',\n        true,  // canBubble\n        true,  // cancelable\n        parentWin,\n        1);  // detail = mousebutton\n    a.dispatchEvent(click);\n    // New window is not available in this case. Instead, a fake Window object\n    // is returned. In particular, it will have window.document undefined. In\n    // general, it will appear to most of clients as a Window for a different\n    // origin. Since iOS standalone web apps are run in their own sandbox, this\n    // is the most appropriate return value.\n    newWin = goog.window.createFakeWindow_();\n  } else if (opt_options['noreferrer']) {\n    // This code used to use meta-refresh to stop the referrer from being\n    // included in the request headers. This was the only cross-browser way\n    // to remove the referrer circa 2009. However, this never worked in Chrome,\n    // and, instead newWin.opener had to be set to null on this browser. This\n    // behavior is slated to be removed in Chrome and should not be relied\n    // upon. Referrer Policy is the only spec'd and supported way of stripping\n    // referrers and works across all current browsers. This is used in\n    // addition to the aforementioned tricks.\n    //\n    // We also set the opener to be set to null in the new window, thus\n    // disallowing the opened window from navigating its opener.\n    //\n    // Detecting user agent and then using a different strategy per browser\n    // would allow the referrer to leak in case of an incorrect/missing user\n    // agent.\n    newWin = goog.dom.safe.openInWindow('', parentWin, target, optionString);\n\n    var sanitizedLinkRef = goog.html.SafeUrl.unwrap(safeLinkRef);\n    if (newWin) {\n      if (goog.userAgent.EDGE_OR_IE) {\n        // IE/EDGE can't parse the content attribute if the url contains\n        // a semicolon. We can fix this by adding quotes around the url, but\n        // then we can't parse quotes in the URL correctly. We take a\n        // best-effort approach.\n        //\n        // If the URL has semicolons, wrap it in single quotes to protect\n        // the semicolons.\n        // If the URL has semicolons and single quotes, url-encode the single\n        // quotes as well.\n        //\n        // This is imperfect. Notice that both ' and ; are reserved characters\n        // in URIs, so this could do the wrong thing, but at least it will\n        // do the wrong thing in only rare cases.\n        // ugh.\n        if (goog.string.contains(sanitizedLinkRef, ';')) {\n          sanitizedLinkRef = \"'\" + sanitizedLinkRef.replace(/'/g, '%27') + \"'\";\n        }\n      }\n      newWin.opener = null;\n\n      // TODO(rjamet): Building proper SafeHtml with SafeHtml.createMetaRefresh\n      // pulls in a lot of compiled code, which is composed of various unneeded\n      // goog.html parts such as SafeStyle.create among others. So, for now,\n      // keep the unchecked conversion until we figure out how to make the\n      // dependencies of createSafeHtmlTagSecurityPrivateDoNotAccessOrElse less\n      // heavy.\n      var safeHtml =\n          goog.html.uncheckedconversions\n              .safeHtmlFromStringKnownToSatisfyTypeContract(\n                  goog.string.Const.from(\n                      'b/12014412, meta tag with sanitized URL'),\n                  '<meta name=\"referrer\" content=\"no-referrer\">' +\n                      '<meta http-equiv=\"refresh\" content=\"0; url=' +\n                      goog.string.htmlEscape(sanitizedLinkRef) + '\">');\n\n      // During window loading `newWin.document` may be unset in some browsers.\n      // Storing and checking a reference to the document prevents NPEs.\n      var newDoc = newWin.document;\n      if (newDoc) {\n        goog.dom.safe.documentWrite(newDoc, safeHtml);\n        newDoc.close();\n      }\n    }\n  } else {\n    newWin = goog.dom.safe.openInWindow(\n        safeLinkRef, parentWin, target, optionString);\n    // Passing in 'noopener' into the 'windowFeatures' param of window.open(...)\n    // will yield a feature-deprived browser. This is an known issue, tracked\n    // here: https://github.com/whatwg/html/issues/1902\n    if (newWin && opt_options['noopener']) {\n      newWin.opener = null;\n    }\n  }\n  // newWin is null if a popup blocker prevented the window open.\n  return newWin;\n};\n\n\n/**\n * Opens a new window without any real content in it.\n *\n * This can be used to get around popup blockers if you need to open a window\n * in response to a user event, but need to do asynchronous work to determine\n * the URL to open, and then set the URL later.\n *\n * Example usage:\n *\n * var newWin = goog.window.openBlank('Loading...');\n * setTimeout(\n *     function() {\n *       newWin.location.href = 'http://www.google.com';\n *     }, 100);\n *\n * @param {string=} opt_message String to show in the new window. This string\n *     will be HTML-escaped to avoid XSS issues.\n * @param {?Object=} opt_options Options to open window with.\n *     {@see goog.window.open for exact option semantics}.\n * @param {?Window=} opt_parentWin Parent window that should be used to open the\n *                 new window.\n * @return {?Window} Returns the window object that was opened. This returns\n *                  null if a popup blocker prevented the window from being\n *                  opened.\n */\ngoog.window.openBlank = function(opt_message, opt_options, opt_parentWin) {\n  'use strict';\n  // Open up a window with the loading message and nothing else.\n  // This will be interpreted as HTML content type with a missing doctype\n  // and html/body tags, but is otherwise acceptable.\n  //\n  // IMPORTANT: The order of escaping is crucial here in order to avoid XSS.\n  // First, HTML-escaping is needed because the result of the JS expression\n  // is evaluated as HTML. Second, JS-string escaping is needed; this avoids\n  // \\u escaping from inserting HTML tags and \\ from escaping the final \".\n  // Finally, URL percent-encoding is done with encodeURI(); this\n  // avoids percent-encoding from bypassing HTML and JS escaping.\n  //\n  // Note: There are other ways the same result could be achieved but the\n  // current behavior was preserved when this code was refactored to use\n  // SafeUrl, in order to avoid breakage.\n  var loadingMessage;\n  if (!opt_message) {\n    loadingMessage = '';\n  } else {\n    loadingMessage =\n        goog.string.escapeString(goog.string.htmlEscape(opt_message));\n  }\n  var url = goog.html.uncheckedconversions\n                .safeUrlFromStringKnownToSatisfyTypeContract(\n                    goog.string.Const.from(\n                        'b/12014412, encoded string in javascript: URL'),\n                    'javascript:\"' + encodeURI(loadingMessage) + '\"');\n  return /** @type {?Window} */ (\n      goog.window.open(url, opt_options, opt_parentWin));\n};\n\n\n/**\n * Raise a help popup window, defaulting to \"Google standard\" size and name.\n *\n * (If your project is using GXPs, consider using {@link PopUpLink.gxp}.)\n *\n* @param {?goog.html.SafeUrl|string|?Object} linkRef If an Object with an 'href'\n *     attribute (such as HTMLAnchorElement) is passed then the value of 'href'\n *     is used, otherwise  otherwise its toString method is called. Note that\n *     if a string|Object is used, it will be sanitized with SafeUrl.sanitize().\n *\n * @param {?Object=} opt_options Options to open window with.\n *     {@see goog.window.open for exact option semantics}\n *     Additional wrinkles to the options:\n *     - if 'target' field is null, linkRef.target will be used. If *that's*\n *     null, the default is \"google_popup\".\n *     - if 'width' field is not specified, the default is 690.\n *     - if 'height' field is not specified, the default is 500.\n *\n * @return {boolean} true if the window was not popped up, false if it was.\n */\ngoog.window.popup = function(linkRef, opt_options) {\n  'use strict';\n  if (!opt_options) {\n    opt_options = {};\n  }\n\n  // set default properties\n  opt_options['target'] = opt_options['target'] || linkRef['target'] ||\n      goog.window.DEFAULT_POPUP_TARGET;\n  opt_options['width'] =\n      opt_options['width'] || goog.window.DEFAULT_POPUP_WIDTH;\n  opt_options['height'] =\n      opt_options['height'] || goog.window.DEFAULT_POPUP_HEIGHT;\n\n  var newWin = goog.window.open(linkRef, opt_options);\n  if (!newWin) {\n    return true;\n  }\n  newWin.focus();\n\n  return false;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Closure user agent platform detection.\n * @see <a href=\"http://www.useragentstring.com/\">User agent strings</a>\n * For more information on browser brand, rendering engine, or device see the\n * other sub-namespaces in goog.labs.userAgent (browser, engine, and device\n * respectively).\n */\n\ngoog.provide('goog.labs.userAgent.platform');\n\ngoog.require('goog.labs.userAgent.util');\ngoog.require('goog.string');\n\n\n/**\n * @return {boolean} Whether the platform is Android.\n */\ngoog.labs.userAgent.platform.isAndroid = function() {\n  'use strict';\n  return goog.labs.userAgent.util.matchUserAgent('Android');\n};\n\n\n/**\n * @return {boolean} Whether the platform is iPod.\n */\ngoog.labs.userAgent.platform.isIpod = function() {\n  'use strict';\n  return goog.labs.userAgent.util.matchUserAgent('iPod');\n};\n\n\n/**\n * @return {boolean} Whether the platform is iPhone.\n */\ngoog.labs.userAgent.platform.isIphone = function() {\n  'use strict';\n  return goog.labs.userAgent.util.matchUserAgent('iPhone') &&\n      !goog.labs.userAgent.util.matchUserAgent('iPod') &&\n      !goog.labs.userAgent.util.matchUserAgent('iPad');\n};\n\n\n/**\n * @return {boolean} Whether the platform is iPad.\n */\ngoog.labs.userAgent.platform.isIpad = function() {\n  'use strict';\n  return goog.labs.userAgent.util.matchUserAgent('iPad');\n};\n\n\n/**\n * @return {boolean} Whether the platform is iOS.\n */\ngoog.labs.userAgent.platform.isIos = function() {\n  'use strict';\n  return goog.labs.userAgent.platform.isIphone() ||\n      goog.labs.userAgent.platform.isIpad() ||\n      goog.labs.userAgent.platform.isIpod();\n};\n\n\n/**\n * @return {boolean} Whether the platform is Mac.\n */\ngoog.labs.userAgent.platform.isMacintosh = function() {\n  'use strict';\n  return goog.labs.userAgent.util.matchUserAgent('Macintosh');\n};\n\n\n/**\n * Note: ChromeOS is not considered to be Linux as it does not report itself\n * as Linux in the user agent string.\n * @return {boolean} Whether the platform is Linux.\n */\ngoog.labs.userAgent.platform.isLinux = function() {\n  'use strict';\n  return goog.labs.userAgent.util.matchUserAgent('Linux');\n};\n\n\n/**\n * @return {boolean} Whether the platform is Windows.\n */\ngoog.labs.userAgent.platform.isWindows = function() {\n  'use strict';\n  return goog.labs.userAgent.util.matchUserAgent('Windows');\n};\n\n\n/**\n * @return {boolean} Whether the platform is ChromeOS.\n */\ngoog.labs.userAgent.platform.isChromeOS = function() {\n  'use strict';\n  return goog.labs.userAgent.util.matchUserAgent('CrOS');\n};\n\n/**\n * @return {boolean} Whether the platform is Chromecast.\n */\ngoog.labs.userAgent.platform.isChromecast = function() {\n  'use strict';\n  return goog.labs.userAgent.util.matchUserAgent('CrKey');\n};\n\n/**\n * @return {boolean} Whether the platform is KaiOS.\n */\ngoog.labs.userAgent.platform.isKaiOS = function() {\n  'use strict';\n  return goog.labs.userAgent.util.matchUserAgentIgnoreCase('KaiOS');\n};\n\n/**\n * The version of the platform. We only determine the version for Windows,\n * Mac, and Chrome OS. It doesn't make much sense on Linux. For Windows, we only\n * look at the NT version. Non-NT-based versions (e.g. 95, 98, etc.) are given\n * version 0.0.\n *\n * @return {string} The platform version or empty string if version cannot be\n *     determined.\n */\ngoog.labs.userAgent.platform.getVersion = function() {\n  'use strict';\n  var userAgentString = goog.labs.userAgent.util.getUserAgent();\n  var version = '', re;\n  if (goog.labs.userAgent.platform.isWindows()) {\n    re = /Windows (?:NT|Phone) ([0-9.]+)/;\n    var match = re.exec(userAgentString);\n    if (match) {\n      version = match[1];\n    } else {\n      version = '0.0';\n    }\n  } else if (goog.labs.userAgent.platform.isIos()) {\n    re = /(?:iPhone|iPod|iPad|CPU)\\s+OS\\s+(\\S+)/;\n    var match = re.exec(userAgentString);\n    // Report the version as x.y.z and not x_y_z\n    version = match && match[1].replace(/_/g, '.');\n  } else if (goog.labs.userAgent.platform.isMacintosh()) {\n    re = /Mac OS X ([0-9_.]+)/;\n    var match = re.exec(userAgentString);\n    // Note: some old versions of Camino do not report an OSX version.\n    // Default to 10.\n    version = match ? match[1].replace(/_/g, '.') : '10';\n  } else if (goog.labs.userAgent.platform.isKaiOS()) {\n    re = /(?:KaiOS)\\/(\\S+)/i;\n    var match = re.exec(userAgentString);\n    version = match && match[1];\n  } else if (goog.labs.userAgent.platform.isAndroid()) {\n    re = /Android\\s+([^\\);]+)(\\)|;)/;\n    var match = re.exec(userAgentString);\n    version = match && match[1];\n  } else if (goog.labs.userAgent.platform.isChromeOS()) {\n    re = /(?:CrOS\\s+(?:i686|x86_64)\\s+([0-9.]+))/;\n    var match = re.exec(userAgentString);\n    version = match && match[1];\n  }\n  return version || '';\n};\n\n\n/**\n * @param {string|number} version The version to check.\n * @return {boolean} Whether the browser version is higher or the same as the\n *     given version.\n */\ngoog.labs.userAgent.platform.isVersionOrHigher = function(version) {\n  'use strict';\n  return goog.string.compareVersions(\n             goog.labs.userAgent.platform.getVersion(), version) >= 0;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Unchecked conversions to create values of goog.html types from\n * plain strings.  Use of these functions could potentially result in instances\n * of goog.html types that violate their type contracts, and hence result in\n * security vulnerabilties.\n *\n * Therefore, all uses of the methods herein must be carefully security\n * reviewed.  Avoid use of the methods in this file whenever possible; instead\n * prefer to create instances of goog.html types using inherently safe builders\n * or template systems.\n *\n *\n */\n\n\ngoog.provide('goog.html.uncheckedconversions');\n\ngoog.require('goog.asserts');\ngoog.require('goog.html.SafeHtml');\ngoog.require('goog.html.SafeScript');\ngoog.require('goog.html.SafeStyle');\ngoog.require('goog.html.SafeStyleSheet');\ngoog.require('goog.html.SafeUrl');\ngoog.require('goog.html.TrustedResourceUrl');\ngoog.require('goog.string.Const');\ngoog.require('goog.string.internal');\ngoog.requireType('goog.i18n.bidi.Dir');\n\n\n/**\n * Performs an \"unchecked conversion\" to SafeHtml from a plain string that is\n * known to satisfy the SafeHtml type contract.\n *\n * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure\n * that the value of `html` satisfies the SafeHtml type contract in all\n * possible program states.\n *\n *\n * @param {!goog.string.Const} justification A constant string explaining why\n *     this use of this method is safe. May include a security review ticket\n *     number.\n * @param {string} html A string that is claimed to adhere to the SafeHtml\n *     contract.\n * @param {?goog.i18n.bidi.Dir=} opt_dir The optional directionality of the\n *     SafeHtml to be constructed. A null or undefined value signifies an\n *     unknown directionality.\n * @return {!goog.html.SafeHtml} The value of html, wrapped in a SafeHtml\n *     object.\n */\ngoog.html.uncheckedconversions.safeHtmlFromStringKnownToSatisfyTypeContract =\n    function(justification, html, opt_dir) {\n  // unwrap() called inside an assert so that justification can be optimized\n  // away in production code.\n  goog.asserts.assertString(\n      goog.string.Const.unwrap(justification), 'must provide justification');\n  goog.asserts.assert(\n      !goog.string.internal.isEmptyOrWhitespace(\n          goog.string.Const.unwrap(justification)),\n      'must provide non-empty justification');\n  return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(\n      html, opt_dir || null);\n};\n\n\n/**\n * Performs an \"unchecked conversion\" to SafeScript from a plain string that is\n * known to satisfy the SafeScript type contract.\n *\n * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure\n * that the value of `script` satisfies the SafeScript type contract in\n * all possible program states.\n *\n *\n * @param {!goog.string.Const} justification A constant string explaining why\n *     this use of this method is safe. May include a security review ticket\n *     number.\n * @param {string} script The string to wrap as a SafeScript.\n * @return {!goog.html.SafeScript} The value of `script`, wrapped in a\n *     SafeScript object.\n */\ngoog.html.uncheckedconversions.safeScriptFromStringKnownToSatisfyTypeContract =\n    function(justification, script) {\n  // unwrap() called inside an assert so that justification can be optimized\n  // away in production code.\n  goog.asserts.assertString(\n      goog.string.Const.unwrap(justification), 'must provide justification');\n  goog.asserts.assert(\n      !goog.string.internal.isEmptyOrWhitespace(\n          goog.string.Const.unwrap(justification)),\n      'must provide non-empty justification');\n  return goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse(\n      script);\n};\n\n\n/**\n * Performs an \"unchecked conversion\" to SafeStyle from a plain string that is\n * known to satisfy the SafeStyle type contract.\n *\n * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure\n * that the value of `style` satisfies the SafeStyle type contract in all\n * possible program states.\n *\n *\n * @param {!goog.string.Const} justification A constant string explaining why\n *     this use of this method is safe. May include a security review ticket\n *     number.\n * @param {string} style The string to wrap as a SafeStyle.\n * @return {!goog.html.SafeStyle} The value of `style`, wrapped in a\n *     SafeStyle object.\n */\ngoog.html.uncheckedconversions.safeStyleFromStringKnownToSatisfyTypeContract =\n    function(justification, style) {\n  // unwrap() called inside an assert so that justification can be optimized\n  // away in production code.\n  goog.asserts.assertString(\n      goog.string.Const.unwrap(justification), 'must provide justification');\n  goog.asserts.assert(\n      !goog.string.internal.isEmptyOrWhitespace(\n          goog.string.Const.unwrap(justification)),\n      'must provide non-empty justification');\n  return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(\n      style);\n};\n\n\n/**\n * Performs an \"unchecked conversion\" to SafeStyleSheet from a plain string\n * that is known to satisfy the SafeStyleSheet type contract.\n *\n * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure\n * that the value of `styleSheet` satisfies the SafeStyleSheet type\n * contract in all possible program states.\n *\n *\n * @param {!goog.string.Const} justification A constant string explaining why\n *     this use of this method is safe. May include a security review ticket\n *     number.\n * @param {string} styleSheet The string to wrap as a SafeStyleSheet.\n * @return {!goog.html.SafeStyleSheet} The value of `styleSheet`, wrapped\n *     in a SafeStyleSheet object.\n */\ngoog.html.uncheckedconversions\n    .safeStyleSheetFromStringKnownToSatisfyTypeContract = function(\n    justification, styleSheet) {\n  // unwrap() called inside an assert so that justification can be optimized\n  // away in production code.\n  goog.asserts.assertString(\n      goog.string.Const.unwrap(justification), 'must provide justification');\n  goog.asserts.assert(\n      !goog.string.internal.isEmptyOrWhitespace(\n          goog.string.Const.unwrap(justification)),\n      'must provide non-empty justification');\n  return goog.html.SafeStyleSheet\n      .createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(styleSheet);\n};\n\n\n/**\n * Performs an \"unchecked conversion\" to SafeUrl from a plain string that is\n * known to satisfy the SafeUrl type contract.\n *\n * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure\n * that the value of `url` satisfies the SafeUrl type contract in all\n * possible program states.\n *\n *\n * @param {!goog.string.Const} justification A constant string explaining why\n *     this use of this method is safe. May include a security review ticket\n *     number.\n * @param {string} url The string to wrap as a SafeUrl.\n * @return {!goog.html.SafeUrl} The value of `url`, wrapped in a SafeUrl\n *     object.\n */\ngoog.html.uncheckedconversions.safeUrlFromStringKnownToSatisfyTypeContract =\n    function(justification, url) {\n  // unwrap() called inside an assert so that justification can be optimized\n  // away in production code.\n  goog.asserts.assertString(\n      goog.string.Const.unwrap(justification), 'must provide justification');\n  goog.asserts.assert(\n      !goog.string.internal.isEmptyOrWhitespace(\n          goog.string.Const.unwrap(justification)),\n      'must provide non-empty justification');\n  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);\n};\n\n\n/**\n * Performs an \"unchecked conversion\" to TrustedResourceUrl from a plain string\n * that is known to satisfy the TrustedResourceUrl type contract.\n *\n * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure\n * that the value of `url` satisfies the TrustedResourceUrl type contract\n * in all possible program states.\n *\n *\n * @param {!goog.string.Const} justification A constant string explaining why\n *     this use of this method is safe. May include a security review ticket\n *     number.\n * @param {string} url The string to wrap as a TrustedResourceUrl.\n * @return {!goog.html.TrustedResourceUrl} The value of `url`, wrapped in\n *     a TrustedResourceUrl object.\n */\ngoog.html.uncheckedconversions\n    .trustedResourceUrlFromStringKnownToSatisfyTypeContract = function(\n    justification, url) {\n  // unwrap() called inside an assert so that justification can be optimized\n  // away in production code.\n  goog.asserts.assertString(\n      goog.string.Const.unwrap(justification), 'must provide justification');\n  goog.asserts.assert(\n      !goog.string.internal.isEmptyOrWhitespace(\n          goog.string.Const.unwrap(justification)),\n      'must provide non-empty justification');\n  return goog.html.TrustedResourceUrl\n      .createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(url);\n};\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Provides utilities for displaying deprecation notices.\n */\ngoog.provide('fireauth.deprecation');\ngoog.provide('fireauth.deprecation.Deprecations');\ngoog.require('fireauth.util');\n\n\n/**\n * An enum of valid notices to display. All deprecation notices must be in this\n * enum. Deprecation messages should be unique and provide the full context\n * of what is deprecated (e.g. the fully qualified path to a method).\n * @enum {string}\n */\nfireauth.deprecation.Deprecations = {\n  LINK_WITH_CREDENTIAL: 'firebase.User.prototype.linkAndRetrieveDataWithCrede' +\n      'ntial is deprecated. Please use firebase.User.prototype.linkWithCreden' +\n      'tial instead.',\n  REAUTH_WITH_CREDENTIAL: 'firebase.User.prototype.reauthenticateAndRetrieveD' +\n      'ataWithCredential is deprecated. Please use firebase.User.prototype.re' +\n      'authenticateWithCredential instead.',\n  SIGN_IN_WITH_CREDENTIAL: 'firebase.auth.Auth.prototype.signInAndRetrieveDat' +\n      'aWithCredential is deprecated. Please use firebase.auth.Auth.prototype' +\n      '.signInWithCredential instead.'\n};\n\n\n/**\n * Keeps track of notices that were already displayed.\n * @type {!Object<fireauth.deprecation.Deprecations, boolean>}\n * @private\n */\nfireauth.deprecation.shownMessages_ = {};\n\n\n/**\n * Logs a deprecation notice to the developer.\n * @param {!fireauth.deprecation.Deprecations} message\n */\nfireauth.deprecation.log = function(message) {\n  if (fireauth.deprecation.shownMessages_[message]) {\n    return;\n  }\n  fireauth.deprecation.shownMessages_[message] = true;\n  fireauth.util.consoleWarn(message);\n};\n\n\n/**\n * Resets the displayed deprecation notices.\n */\nfireauth.deprecation.resetForTesting = function() {\n  fireauth.deprecation.shownMessages_ =\n      /** @type {!Object<fireauth.deprecation.Deprecations, boolean>} */ ({});\n};\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Provides methods for manipulating objects.\n */\n\ngoog.provide('fireauth.object');\n\ngoog.require('fireauth.deprecation');\ngoog.require('fireauth.deprecation.Deprecations');\n\n\n/**\n * Checks whether the defineProperty method allows to change the value of\n * the property.\n * @return {boolean} Whether the defineProperty method allows to change the\n *    value of the property.\n * @private\n */\nfireauth.object.isReadonlyConfigurable_ = function() {\n  // Android 2.3 stock browser doesn't allow to change the value of\n  // a read-only property once defined.\n  try {\n    var obj = {};\n    Object.defineProperty(obj, 'abcd', {\n      configurable: true,\n      enumerable: true,\n      value: 1\n    });\n    Object.defineProperty(obj, 'abcd', {\n      configurable: true,\n      enumerable: true,\n      value: 2\n    });\n    return obj['abcd'] == 2;\n  } catch (e) {\n    return false;\n  }\n};\n\n\n/**\n * @private {boolean} Whether the defineProperty method allows to change the\n *     value of the property.\n */\nfireauth.object.readonlyConfigurable_ =\n    fireauth.object.isReadonlyConfigurable_();\n\n\n/**\n * Defines a property on an object that is not writable by clients. However, the\n * property can be overwritten within the Firebase library through subsequent\n * calls to setReadonlyProperty.\n *\n * In browsers that do not support read-only properties (notably IE8 and below),\n * fall back to writable properties.\n *\n * @param {!Object} obj The object to which we add the property.\n * @param {string} key The name of the property.\n * @param {*} value The desired value.\n */\nfireauth.object.setReadonlyProperty = function(obj, key, value) {\n  if (fireauth.object.readonlyConfigurable_) {\n    Object.defineProperty(obj, key, {\n      configurable: true,\n      enumerable: true,\n      value: value\n    });\n  } else {\n    obj[key] = value;\n  }\n};\n\n\n/**\n * Defines a deprecated property, which emits a warning if the developer tries\n * to use it.\n *\n * In browsers that do not support getters, we fall back to a normal property\n * with no message.\n *\n * @param {!Object} obj The object to which we add the property.\n * @param {string} key The name of the deprecated property.\n * @param {*} value The desired value.\n * @param {!fireauth.deprecation.Deprecations} deprecationMessage The\n *     deprecation warning to display.\n */\nfireauth.object.setDeprecatedReadonlyProperty = function(obj, key, value,\n    deprecationMessage) {\n  if (fireauth.object.readonlyConfigurable_) {\n    Object.defineProperty(obj, key, {\n      configurable: true,\n      enumerable: true,\n      get: function() {\n        fireauth.deprecation.log(deprecationMessage);\n        return value;\n      }\n    });\n  } else {\n    obj[key] = value;\n  }\n};\n\n\n/**\n * Defines properties on an object that are not writable by clients, equivalent\n * to many calls to setReadonlyProperty.\n * @param {!Object} obj The object to which we add the properties.\n * @param {?Object<string, *>} props An object that maps the keys and values\n *     that we wish to add.\n */\nfireauth.object.setReadonlyProperties = function(obj, props) {\n  if (!props) {\n    return;\n  }\n\n  for (var key in props) {\n    if (props.hasOwnProperty(key)) {\n      fireauth.object.setReadonlyProperty(obj, key, props[key]);\n    }\n  }\n};\n\n\n/**\n * Makes a shallow read-only copy of an object. The writability of any child\n * objects will not be affected.\n * @param {?Object} obj The object that we wish to copy.\n * @return {!Object}\n */\nfireauth.object.makeReadonlyCopy = function(obj) {\n  var output = {};\n  fireauth.object.setReadonlyProperties(output, obj);\n  return output;\n};\n\n\n/**\n * Makes a shallow writable copy of a read-only object. The writability of any\n * child objects will not be affected.\n * @param {?Object} obj The object that we wish to copy.\n * @return {!Object}\n */\nfireauth.object.makeWritableCopy = function(obj) {\n  var output = {};\n  for (var key in obj) {\n    if (obj.hasOwnProperty(key)) {\n      output[key] = obj[key];\n    }\n  }\n  return output;\n};\n\n\n/**\n * Returns true if the all the specified fields are present in obj and are not\n * null, undefined, or the empty string. If the field list is empty, returns\n * true regardless of the value of obj.\n * @param {?Object=} opt_obj The object.\n * @param {?Array<string>=} opt_fields The desired fields of the object.\n * @return {boolean} True if obj has all the specified fields.\n */\nfireauth.object.hasNonEmptyFields = function(opt_obj, opt_fields) {\n  if (!opt_fields || !opt_fields.length) {\n    return true;\n  }\n  if (!opt_obj) {\n    return false;\n  }\n  for (var i = 0; i < opt_fields.length; i++) {\n    var field = opt_obj[opt_fields[i]];\n    if (field === undefined || field === null || field === '') {\n      return false;\n    }\n  }\n  return true;\n};\n\n\n/**\n * Traverses the specified object and creates a read-only deep copy of it.\n * This will fail when circular references are contained within the object.\n * @param {*} obj The object to make a read-only copy from.\n * @return {*} A Read-only copy of the obj specified.\n */\nfireauth.object.unsafeCreateReadOnlyCopy = function(obj) {\n  var copy = obj;\n  if (typeof obj == 'object' && obj != null) {\n    // Make the right type of copy.\n    copy = 'length' in obj ? [] : {};\n    // Make a deep copy.\n    for (var key in obj) {\n      fireauth.object.setReadonlyProperty(\n          copy, key, fireauth.object.unsafeCreateReadOnlyCopy(obj[key]));\n    }\n  }\n  // Return the copy.\n  return copy;\n};\n\n","/**\n * @license\n * Copyright 2019 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines the multi-factor enrollment information.\n */\n\ngoog.provide('fireauth.MultiFactorInfo');\ngoog.provide('fireauth.PhoneMultiFactorInfo');\n\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.authenum.Error');\ngoog.require('fireauth.constants');\ngoog.require('fireauth.object');\n\n\n/**\n * Abstract class representing a `firebase.auth.MultiFactorInfo` interface.\n * This is typically parsed from a server response.\n * @param {?Object} resp The server response.\n * @abstract\n * @constructor\n */\nfireauth.MultiFactorInfo = function(resp) {\n  var factorId = resp && this.getFactorId(resp);\n  if (factorId && resp &&\n      resp[fireauth.MultiFactorInfo.MfaEnrollmentField.MFA_ENROLLMENT_ID]) {\n    fireauth.object.setReadonlyProperty(\n        this,\n        'uid',\n        resp[fireauth.MultiFactorInfo.MfaEnrollmentField.MFA_ENROLLMENT_ID]);\n    fireauth.object.setReadonlyProperty(\n        this,\n        'displayName',\n        resp[fireauth.MultiFactorInfo.MfaEnrollmentField.DISPLAY_NAME] || null);\n    var enrollmentTime = null;\n    // Encoded using [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format.\n    // For example, \"2017-01-15T01:30:15.01Z\".\n    // This can be parsed directly in modern browsers via Date constructor.\n    // This can be computed using Data.prototype.toISOString.\n    if (resp[fireauth.MultiFactorInfo.MfaEnrollmentField.ENROLLED_AT]) {\n      enrollmentTime = new Date(\n            resp[fireauth.MultiFactorInfo.MfaEnrollmentField.ENROLLED_AT])\n                .toUTCString();\n    }\n    fireauth.object.setReadonlyProperty(\n        this,\n        'enrollmentTime',\n        enrollmentTime);\n    fireauth.object.setReadonlyProperty(\n        this,\n        'factorId',\n        factorId);\n  } else {\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.INTERNAL_ERROR,\n        'Internal assert: invalid MultiFactorInfo object');\n  }\n};\n\n\n/** @return {!Object} The plain object representation. */\nfireauth.MultiFactorInfo.prototype.toPlainObject = function() {\n  return {\n    'uid': this['uid'],\n    'displayName': this['displayName'],\n    'factorId': this['factorId'],\n    'enrollmentTime': this['enrollmentTime']\n  };\n};\n\n\n/**\n * Returns the factor ID based on the server response. This function needs to be\n * implemented by the subclass.\n * @param {!Object} resp The server response.\n * @return {?fireauth.constants.SecondFactorType} The factor ID based on the\n *     response type.\n * @protected\n */\nfireauth.MultiFactorInfo.prototype.getFactorId = goog.abstractMethod;\n\n\n/**\n * Returns the corresponding `firebase.auth.MultiFactor` instance if the\n * server response maps to one. Otherwise, null is returned.\n * @param {?Object} resp The server response.\n * @return {?fireauth.MultiFactorInfo} The corresponding\n *     `firebase.auth.MultiFactorInfo` instance, null otherwise.\n */\nfireauth.MultiFactorInfo.fromServerResponse = function(resp) {\n  var multiFactorInfo;\n  // Only PhoneMultiFactorInfo currently available.\n  try {\n    multiFactorInfo = new fireauth.PhoneMultiFactorInfo(resp);\n  } catch (e) {\n    multiFactorInfo = null;\n  }\n  return multiFactorInfo;\n};\n\n\n/**\n * Returns the corresponding `firebase.auth.MultiFactor` instance if the\n * plain object maps to one. Otherwise, null is returned.\n * @param {?Object} obj The plain object representation.\n * @return {?fireauth.MultiFactorInfo} The corresponding\n *     `firebase.auth.MultiFactorInfo` instance, null otherwise.\n */\nfireauth.MultiFactorInfo.fromPlainObject = function(obj) {\n  var multiFactorInfo = null;\n  var resp = {};\n  if (!obj) {\n    return null;\n  }\n  if (obj['uid']) {\n    resp[fireauth.MultiFactorInfo.MfaEnrollmentField.MFA_ENROLLMENT_ID] =\n        obj['uid'];\n  }\n  if (obj['displayName']) {\n    resp[fireauth.MultiFactorInfo.MfaEnrollmentField.DISPLAY_NAME] =\n        obj['displayName'];\n  }\n  if (obj['enrollmentTime']) {\n    resp[fireauth.MultiFactorInfo.MfaEnrollmentField.ENROLLED_AT] =\n        new Date(obj['enrollmentTime']).toISOString();\n  }\n  if (obj['phoneNumber']) {\n    resp[fireauth.MultiFactorInfo.MfaEnrollmentField.PHONE_INFO] =\n        obj['phoneNumber'];\n  }\n\n  // Only PhoneMultiFactorInfo currently available.\n  try {\n    multiFactorInfo = new fireauth.PhoneMultiFactorInfo(resp);\n  } catch (e) {\n    // Ignore error.\n  }\n  return multiFactorInfo;\n};\n\n\n/**\n * MfaEnrollment server side response fields.\n * @enum {string}\n */\nfireauth.MultiFactorInfo.MfaEnrollmentField = {\n  DISPLAY_NAME: 'displayName',\n  ENROLLED_AT: 'enrolledAt',\n  MFA_ENROLLMENT_ID: 'mfaEnrollmentId',\n  PHONE_INFO: 'phoneInfo'\n};\n\n\n/**\n * Initializes a `firebase.auth.PhoneMultiFactorInfo` instance from the provided\n * server response.\n * @param {?Object} resp The server response.\n * @constructor\n * @extends {fireauth.MultiFactorInfo}\n */\nfireauth.PhoneMultiFactorInfo = function(resp) {\n  fireauth.PhoneMultiFactorInfo.base(this, 'constructor', resp);\n  fireauth.object.setReadonlyProperty(\n      this,\n      'phoneNumber',\n      // PhoneInfo may be masked for security reasons for sign-in flows after\n      // the user signs in with the first factor but hasn't yet proven ownership\n      // of the second factor yet.\n      // For enrollment flows or for a user already signed in with a second\n      // factor, this field should not be masked.\n      resp[fireauth.MultiFactorInfo.MfaEnrollmentField.PHONE_INFO]);\n};\ngoog.inherits(\n    fireauth.PhoneMultiFactorInfo, fireauth.MultiFactorInfo);\n\n\n/**\n * Implements the factor ID getter based on the response. If the response is an\n * invalid PhoneMultiFactorInfo, null is returned.\n * @param {!Object} resp The server response.\n * @return {?fireauth.constants.SecondFactorType} The phone factor ID.\n * @protected\n * @override\n */\nfireauth.PhoneMultiFactorInfo.prototype.getFactorId = function(resp) {\n  return !!resp[fireauth.MultiFactorInfo.MfaEnrollmentField.PHONE_INFO] ?\n      fireauth.constants.SecondFactorType.PHONE : null;\n};\n\n\n/**\n * @return {!Object} The plain object representation.\n * @override\n */\nfireauth.PhoneMultiFactorInfo.prototype.toPlainObject = function() {\n  var obj = fireauth.PhoneMultiFactorInfo.base(this, 'toPlainObject');\n  obj['phoneNumber'] = this['phoneNumber'];\n  return obj;\n};\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines the firebase.auth.ActionCodeInfo class that is returned\n * when calling checkActionCode API and is populated from the server response\n * directly.\n */\n\ngoog.provide('fireauth.ActionCodeInfo');\n\ngoog.require('fireauth.MultiFactorInfo');\ngoog.require('fireauth.object');\n\n\n/**\n * Constructs the action code info object which provides metadata corresponding\n * to action codes. This includes the type of operation (RESET_PASSWORD,\n * VERIFY_EMAIL and RECOVER_EMAIL), the email corresponding to the operation\n * and in case of the recover email flow, the old and new email.\n * @param {!Object} response The server response for checkActionCode.\n * @constructor\n */\nfireauth.ActionCodeInfo = function(response) {\n  var data = {};\n  // Original email for email change revocation.\n  var email = response[fireauth.ActionCodeInfo.ServerFieldName.EMAIL];\n  // The new email.\n  var newEmail = response[fireauth.ActionCodeInfo.ServerFieldName.NEW_EMAIL];\n  var operation =\n      response[fireauth.ActionCodeInfo.ServerFieldName.REQUEST_TYPE];\n  // The multi-factor info for revert second factor addition.\n  var mfaInfo =\n      fireauth.MultiFactorInfo.fromServerResponse(\n          response[fireauth.ActionCodeInfo.ServerFieldName.MFA_INFO]);\n  // Email could be empty only if the request type is EMAIL_SIGNIN or\n  // VERIFY_AND_CHANGE_EMAIL.\n  // New email should not be empty if the request type is\n  // VERIFY_AND_CHANGE_EMAIL.\n  // Multi-factor info could not be empty if the request type is\n  // REVERT_SECOND_FACTOR_ADDITION.\n  if (!operation ||\n      (operation != fireauth.ActionCodeInfo.Operation.EMAIL_SIGNIN &&\n       operation != fireauth.ActionCodeInfo.Operation.VERIFY_AND_CHANGE_EMAIL &&\n       !email) ||\n      (operation == fireauth.ActionCodeInfo.Operation.VERIFY_AND_CHANGE_EMAIL &&\n       !newEmail) ||\n      (operation ==\n       fireauth.ActionCodeInfo.Operation.REVERT_SECOND_FACTOR_ADDITION &&\n       !mfaInfo)) {\n    // This is internal only.\n    throw new Error('Invalid checkActionCode response!');\n  }\n  if (operation == fireauth.ActionCodeInfo.Operation.VERIFY_AND_CHANGE_EMAIL) {\n    data[fireauth.ActionCodeInfo.DataField.FROM_EMAIL] = email || null;\n    data[fireauth.ActionCodeInfo.DataField.PREVIOUS_EMAIL] = email || null;\n    data[fireauth.ActionCodeInfo.DataField.EMAIL] = newEmail;\n  } else {\n    data[fireauth.ActionCodeInfo.DataField.FROM_EMAIL] = newEmail || null;\n    data[fireauth.ActionCodeInfo.DataField.PREVIOUS_EMAIL] = newEmail || null;\n    data[fireauth.ActionCodeInfo.DataField.EMAIL] = email || null;\n  }\n  data[fireauth.ActionCodeInfo.DataField.MULTI_FACTOR_INFO] = mfaInfo || null;\n  fireauth.object.setReadonlyProperty(\n      this,\n      fireauth.ActionCodeInfo.PropertyName.OPERATION,\n      operation);\n  fireauth.object.setReadonlyProperty(\n      this,\n      fireauth.ActionCodeInfo.PropertyName.DATA,\n      fireauth.object.unsafeCreateReadOnlyCopy(data));\n};\n\n\n/**\n * Firebase Auth Action Code Info operation possible values.\n * @enum {string}\n */\nfireauth.ActionCodeInfo.Operation = {\n  PASSWORD_RESET: 'PASSWORD_RESET',\n  RECOVER_EMAIL: 'RECOVER_EMAIL',\n  REVERT_SECOND_FACTOR_ADDITION: 'REVERT_SECOND_FACTOR_ADDITION',\n  EMAIL_SIGNIN: 'EMAIL_SIGNIN',\n  VERIFY_AND_CHANGE_EMAIL: 'VERIFY_AND_CHANGE_EMAIL',\n  VERIFY_EMAIL: 'VERIFY_EMAIL'\n};\n\n\n/**\n * The checkActionCode endpoint server response field names.\n * @enum {string}\n */\nfireauth.ActionCodeInfo.ServerFieldName = {\n  // This is the current email of the account and in email recovery, the email\n  // to revert to.\n  EMAIL: 'email',\n  // The multi-factor info to unenroll for revert second factor addition action.\n  MFA_INFO: 'mfaInfo',\n  // For email recovery, this is the new email.\n  NEW_EMAIL: 'newEmail',\n  // The action code request type.\n  REQUEST_TYPE: 'requestType'\n};\n\n\n/**\n * The ActionCodeInfo data object field names.\n * @enum {string}\n */\nfireauth.ActionCodeInfo.DataField = {\n  EMAIL: 'email',\n  // This field will be deprecated in favor of PREVIOUS_EMAIL.\n  FROM_EMAIL: 'fromEmail',\n  MULTI_FACTOR_INFO: 'multiFactorInfo',\n  PREVIOUS_EMAIL: 'previousEmail'\n};\n\n\n/**\n * The ActionCodeInfo main property names\n * @enum {string}\n */\nfireauth.ActionCodeInfo.PropertyName = {\n  DATA: 'data',\n  OPERATION: 'operation'\n};\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines firebase.auth.ActionCodeURL class which is the utility\n * to parse action code URLs.\n */\n\ngoog.provide('fireauth.ActionCodeURL');\n\ngoog.require('fireauth.ActionCodeInfo');\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.authenum.Error');\ngoog.require('fireauth.object');\ngoog.require('goog.Uri');\n\n\n/**\n * The utility class to help parse action code URLs used for out of band email\n * flows such as password reset, email verification, email link sign in, etc.\n * @param {string} actionLink The action link string.\n * @constructor\n */\nfireauth.ActionCodeURL = function(actionLink) {\n  var uri = goog.Uri.parse(actionLink);\n  var apiKey = uri.getParameterValue(\n      fireauth.ActionCodeURL.QueryField.API_KEY) || null;\n  var code = uri.getParameterValue(\n      fireauth.ActionCodeURL.QueryField.CODE) || null;\n  var mode = uri.getParameterValue(\n      fireauth.ActionCodeURL.QueryField.MODE) || null;\n  var operation = fireauth.ActionCodeURL.getOperation(mode);\n  // Validate API key, code and mode.\n  if (!apiKey || !code || !operation) {\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.ARGUMENT_ERROR,\n        fireauth.ActionCodeURL.QueryField.API_KEY + ', ' +\n        fireauth.ActionCodeURL.QueryField.CODE + 'and ' +\n        fireauth.ActionCodeURL.QueryField.MODE +\n        ' are required in a valid action code URL.');\n  }\n  fireauth.object.setReadonlyProperties(this, {\n    'apiKey': apiKey,\n    'operation': operation,\n    'code': code,\n    'continueUrl': uri.getParameterValue(\n        fireauth.ActionCodeURL.QueryField.CONTINUE_URL) || null,\n    'languageCode': uri.getParameterValue(\n        fireauth.ActionCodeURL.QueryField.LANGUAGE_CODE) || null,\n    'tenantId': uri.getParameterValue(\n        fireauth.ActionCodeURL.QueryField.TENANT_ID) || null\n  });\n};\n\n/**\n * Enums for fields in URL query string.\n * @enum {string}\n */\nfireauth.ActionCodeURL.QueryField = {\n  API_KEY: 'apiKey',\n  CODE: 'oobCode',\n  CONTINUE_URL: 'continueUrl',\n  LANGUAGE_CODE: 'languageCode',\n  MODE: 'mode',\n  TENANT_ID: 'tenantId'\n};\n\n\n/**\n * Map of mode string to Action Code Info operation.\n * @const @private {!Object<string, !fireauth.ActionCodeInfo.Operation>}\n */\nfireauth.ActionCodeURL.ModeToOperationMap_ = {\n  'recoverEmail': fireauth.ActionCodeInfo.Operation.RECOVER_EMAIL,\n  'resetPassword': fireauth.ActionCodeInfo.Operation.PASSWORD_RESET,\n  'revertSecondFactorAddition':\n      fireauth.ActionCodeInfo.Operation.REVERT_SECOND_FACTOR_ADDITION,\n  'signIn': fireauth.ActionCodeInfo.Operation.EMAIL_SIGNIN,\n  'verifyAndChangeEmail':\n      fireauth.ActionCodeInfo.Operation.VERIFY_AND_CHANGE_EMAIL,\n  'verifyEmail': fireauth.ActionCodeInfo.Operation.VERIFY_EMAIL\n};\n\n\n/**\n * Maps the mode string in action code URL to Action Code Info operation.\n * @param {?string} mode The mode string in the URL.\n * @return {?fireauth.ActionCodeInfo.Operation}\n */\nfireauth.ActionCodeURL.getOperation = function(mode) {\n  if (!mode) {\n    return null;\n  }\n  return fireauth.ActionCodeURL.ModeToOperationMap_[mode] || null;\n\n};\n\n\n/**\n * Returns an ActionCodeURL instance if the link is valid, otherwise null.\n * @param {string} actionLink The action code link string.\n * @return {?fireauth.ActionCodeURL}\n */\nfireauth.ActionCodeURL.parseLink = function(actionLink) {\n  try {\n    return new fireauth.ActionCodeURL(actionLink);\n  } catch(e) {\n    return null;\n  }\n};\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Utility for firebase.auth.ActionCodeSettings and its helper\n * functions.\n */\n\ngoog.provide('fireauth.ActionCodeSettings');\n\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.authenum.Error');\n\n\n/**\n * Defines the action code settings structure used to specify how email action\n * links are handled.\n * @param {!Object} settingsObj The action code settings object used to\n *     construct the action code link.\n * @constructor @struct @final\n */\nfireauth.ActionCodeSettings = function(settingsObj) {\n  // Validate the settings object passed.\n  this.initialize_(settingsObj);\n};\n\n\n/**\n * Validate the action code settings object.\n * @param {!Object} settingsObj The action code settings object to validate.\n * @private\n */\nfireauth.ActionCodeSettings.prototype.initialize_ = function(settingsObj) {\n  // URL should be required.\n  var continueUrl = settingsObj[fireauth.ActionCodeSettings.RawField.URL];\n  if (typeof continueUrl === 'undefined') {\n    throw new fireauth.AuthError(fireauth.authenum.Error.MISSING_CONTINUE_URI);\n  } else if (typeof continueUrl !== 'string' ||\n             (typeof continueUrl === 'string' && !continueUrl.length)) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INVALID_CONTINUE_URI);\n  }\n  /** @const @private {string} The continue URL. */\n  this.continueUrl_ = /** @type {string} */ (continueUrl);\n\n  // Validate Android parameters.\n  /** @private {?string} The Android package name. */\n  this.apn_ = null;\n  /** @private {?string} The Android minimum version. */\n  this.amv_ = null;\n  /** @private {boolean} Whether to install the Android app. */\n  this.installApp_ = false;\n  var androidSettings =\n      settingsObj[fireauth.ActionCodeSettings.RawField.ANDROID];\n  if (androidSettings && typeof androidSettings === 'object') {\n    var apn = androidSettings[\n      fireauth.ActionCodeSettings.AndroidRawField.PACKAGE_NAME];\n    var installApp = androidSettings[\n      fireauth.ActionCodeSettings.AndroidRawField.INSTALL_APP];\n    var amv = androidSettings[\n      fireauth.ActionCodeSettings.AndroidRawField.MINIMUM_VERSION];\n    if (typeof apn === 'string' && apn.length) {\n      this.apn_ = /** @type {string} */ (apn);\n      if (typeof installApp !== 'undefined' &&\n          typeof installApp !== 'boolean') {\n        throw new fireauth.AuthError(\n            fireauth.authenum.Error.ARGUMENT_ERROR,\n            fireauth.ActionCodeSettings.AndroidRawField.INSTALL_APP +\n            ' property must be a boolean when specified.');\n      }\n      this.installApp_ = !!installApp;\n      if (typeof amv !== 'undefined' &&\n          (typeof amv !== 'string' ||\n           (typeof amv === 'string' && !amv.length))) {\n        throw new fireauth.AuthError(\n            fireauth.authenum.Error.ARGUMENT_ERROR,\n            fireauth.ActionCodeSettings.AndroidRawField.MINIMUM_VERSION +\n            ' property must be a non empty string when specified.');\n      }\n      this.amv_ = /** @type {?string}*/ (amv || null);\n    } else if (typeof apn !== 'undefined') {\n      throw new fireauth.AuthError(\n          fireauth.authenum.Error.ARGUMENT_ERROR,\n          fireauth.ActionCodeSettings.AndroidRawField.PACKAGE_NAME +\n          ' property must be a non empty string when specified.');\n    } else if (typeof installApp !== 'undefined' ||\n               typeof amv !== 'undefined') {\n      // If installApp or amv specified with no valid APN, fail quickly.\n      throw new fireauth.AuthError(\n          fireauth.authenum.Error.MISSING_ANDROID_PACKAGE_NAME);\n    }\n  } else if (typeof androidSettings !== 'undefined') {\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.ARGUMENT_ERROR,\n        fireauth.ActionCodeSettings.RawField.ANDROID +\n        ' property must be a non null object when specified.');\n  }\n\n  // Validate iOS parameters.\n  /** @private {?string} The iOS bundle ID. */\n  this.ibi_ = null;\n  var iosSettings = settingsObj[fireauth.ActionCodeSettings.RawField.IOS];\n  if (iosSettings && typeof iosSettings === 'object') {\n    var ibi = iosSettings[\n      fireauth.ActionCodeSettings.IosRawField.BUNDLE_ID];\n    if (typeof ibi === 'string' && ibi.length) {\n      this.ibi_ = /** @type {string}*/ (ibi);\n    } else if (typeof ibi !== 'undefined') {\n      throw new fireauth.AuthError(\n          fireauth.authenum.Error.ARGUMENT_ERROR,\n          fireauth.ActionCodeSettings.IosRawField.BUNDLE_ID +\n          ' property must be a non empty string when specified.');\n    }\n  } else if (typeof iosSettings !== 'undefined') {\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.ARGUMENT_ERROR,\n        fireauth.ActionCodeSettings.RawField.IOS +\n        ' property must be a non null object when specified.');\n  }\n\n  // Validate canHandleCodeInApp.\n  var canHandleCodeInApp =\n      settingsObj[fireauth.ActionCodeSettings.RawField.HANDLE_CODE_IN_APP];\n  if (typeof canHandleCodeInApp !== 'undefined' &&\n      typeof canHandleCodeInApp !== 'boolean') {\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.ARGUMENT_ERROR,\n        fireauth.ActionCodeSettings.RawField.HANDLE_CODE_IN_APP +\n        ' property must be a boolean when specified.');\n  }\n  /** @const @private {boolean} Whether the code can be handled in app. */\n  this.canHandleCodeInApp_ = !!canHandleCodeInApp;\n\n  // Validate dynamicLinkDomain.\n  var dynamicLinkDomain = settingsObj[\n      fireauth.ActionCodeSettings.RawField.DYNAMIC_LINK_DOMAIN];\n  if (typeof dynamicLinkDomain !== 'undefined' &&\n      (typeof dynamicLinkDomain !== 'string' ||\n       (typeof dynamicLinkDomain === 'string' &&\n        !dynamicLinkDomain.length))) {\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.ARGUMENT_ERROR,\n        fireauth.ActionCodeSettings.RawField.DYNAMIC_LINK_DOMAIN +\n        ' property must be a non empty string when specified.');\n  }\n  /** @const @private {?string} The FDL domain. */\n  this.dynamicLinkDomain_ = dynamicLinkDomain || null;\n};\n\n\n/**\n * Action code settings backend request field names.\n * @enum {string}\n */\nfireauth.ActionCodeSettings.RequestField = {\n  ANDROID_INSTALL_APP: 'androidInstallApp',\n  ANDROID_MINIMUM_VERSION: 'androidMinimumVersion',\n  ANDROID_PACKAGE_NAME: 'androidPackageName',\n  CAN_HANDLE_CODE_IN_APP: 'canHandleCodeInApp',\n  CONTINUE_URL: 'continueUrl',\n  DYNAMIC_LINK_DOMAIN: 'dynamicLinkDomain',\n  IOS_BUNDLE_ID: 'iOSBundleId'\n};\n\n\n/**\n * Action code settings raw field names.\n * @enum {string}\n */\nfireauth.ActionCodeSettings.RawField = {\n  ANDROID: 'android',\n  DYNAMIC_LINK_DOMAIN: 'dynamicLinkDomain',\n  HANDLE_CODE_IN_APP: 'handleCodeInApp',\n  IOS: 'iOS',\n  URL: 'url'\n};\n\n\n/**\n * Action code settings raw Android raw field names.\n * @enum {string}\n */\nfireauth.ActionCodeSettings.AndroidRawField = {\n  INSTALL_APP: 'installApp',\n  MINIMUM_VERSION: 'minimumVersion',\n  PACKAGE_NAME: 'packageName'\n};\n\n\n/**\n * Action code settings raw iOS raw field names.\n * @enum {string}\n */\nfireauth.ActionCodeSettings.IosRawField = {\n  BUNDLE_ID: 'bundleId'\n};\n\n\n/**\n * Builds and returns the backend request for the passed action code settings.\n * @return {!Object} The constructed backend request populated with the action\n *     code settings parameters.\n */\nfireauth.ActionCodeSettings.prototype.buildRequest = function() {\n  // Construct backend request.\n  var request = {};\n  request[fireauth.ActionCodeSettings.RequestField.CONTINUE_URL] =\n      this.continueUrl_;\n  request[fireauth.ActionCodeSettings.RequestField.CAN_HANDLE_CODE_IN_APP] =\n      this.canHandleCodeInApp_;\n  request[fireauth.ActionCodeSettings.RequestField.ANDROID_PACKAGE_NAME] =\n      this.apn_;\n  if (this.apn_) {\n    request[fireauth.ActionCodeSettings.RequestField.ANDROID_MINIMUM_VERSION] =\n        this.amv_;\n    request[fireauth.ActionCodeSettings.RequestField.ANDROID_INSTALL_APP] =\n        this.installApp_;\n  }\n  request[fireauth.ActionCodeSettings.RequestField.IOS_BUNDLE_ID] = this.ibi_;\n  request[fireauth.ActionCodeSettings.RequestField.DYNAMIC_LINK_DOMAIN] =\n      this.dynamicLinkDomain_;\n  // Remove null fields.\n  for (var key in request) {\n    if (request[key] === null) {\n      delete request[key];\n    }\n  }\n  return request;\n};\n\n\n/**\n * Returns the canHandleCodeInApp setting of ActionCodeSettings.\n * @return {boolean} Whether the code can be handled in app.\n */\nfireauth.ActionCodeSettings.prototype.canHandleCodeInApp = function() {\n  return this.canHandleCodeInApp_;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Base64 en/decoding. Not much to say here except that we\n * work with decoded values in arrays of bytes. By \"byte\" I mean a number\n * in [0, 255].\n */\n\ngoog.provide('goog.crypt.base64');\n\ngoog.require('goog.asserts');\ngoog.require('goog.crypt');\ngoog.require('goog.string');\ngoog.require('goog.userAgent');\ngoog.require('goog.userAgent.product');\n\n/**\n * Default alphabet, shared between alphabets. Only 62 characters.\n * @private {string}\n */\ngoog.crypt.base64.DEFAULT_ALPHABET_COMMON_ = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +\n    'abcdefghijklmnopqrstuvwxyz' +\n    '0123456789';\n\n\n/**\n * Alphabet characters for Alphabet.DEFAULT encoding.\n * For characters without padding, please consider using\n * `goog.crypt.baseN.BASE_64` instead.\n *\n * @type {string}\n */\ngoog.crypt.base64.ENCODED_VALS =\n    goog.crypt.base64.DEFAULT_ALPHABET_COMMON_ + '+/=';\n\n\n/**\n * Alphabet characters for Alphabet.WEBSAFE_DOT_PADDING encoding.\n * The dot padding is no Internet Standard, according to RFC 4686.\n * https://tools.ietf.org/html/rfc4648\n * For characters without padding, please consider using\n * `goog.crypt.baseN.BASE_64_URL_SAFE` instead.\n *\n * @type {string}\n */\ngoog.crypt.base64.ENCODED_VALS_WEBSAFE =\n    goog.crypt.base64.DEFAULT_ALPHABET_COMMON_ + '-_.';\n\n\n/**\n * Alphabets for Base64 encoding\n * Alphabets with no padding character are for encoding without padding.\n * About the alphabets, please refer to RFC 4686.\n * https://tools.ietf.org/html/rfc4648\n * @enum {number}\n */\ngoog.crypt.base64.Alphabet = {\n  DEFAULT: 0,\n  NO_PADDING: 1,\n  WEBSAFE: 2,\n  WEBSAFE_DOT_PADDING: 3,\n  WEBSAFE_NO_PADDING: 4,\n};\n\n\n/**\n * Padding chars for Base64 encoding\n * @const {string}\n * @private\n */\ngoog.crypt.base64.paddingChars_ = '=.';\n\n\n/**\n * Check if a character is a padding character\n *\n * @param {string} char\n * @return {boolean}\n * @private\n */\ngoog.crypt.base64.isPadding_ = function(char) {\n  return goog.string.contains(goog.crypt.base64.paddingChars_, char);\n};\n\n\n// Static lookup maps, lazily populated by init_()\n\n/**\n * For each `Alphabet`, maps from bytes to characters.\n *\n * @see https://jsperf.com/char-lookups\n * @type {!Object<!goog.crypt.base64.Alphabet, !Array<string>>}\n * @private\n */\ngoog.crypt.base64.byteToCharMaps_ = {};\n\n/**\n * Maps characters to bytes.\n *\n * This map is used for all alphabets since, across alphabets, common chars\n * always map to the same byte.\n *\n * `null` indicates `init` has not yet been called.\n *\n * @type {?Object<string, number>}\n * @private\n */\ngoog.crypt.base64.charToByteMap_ = null;\n\n\n/**\n * White list of implementations with known-good native atob and btoa functions.\n * Listing these explicitly (via the ASSUME_* wrappers) benefits dead-code\n * removal in per-browser compilations.\n * @private {boolean}\n */\ngoog.crypt.base64.ASSUME_NATIVE_SUPPORT_ = goog.userAgent.GECKO ||\n    (goog.userAgent.WEBKIT && !goog.userAgent.product.SAFARI) ||\n    goog.userAgent.OPERA;\n\n\n/**\n * Does this browser have a working btoa function?\n * @private {boolean}\n */\ngoog.crypt.base64.HAS_NATIVE_ENCODE_ =\n    goog.crypt.base64.ASSUME_NATIVE_SUPPORT_ ||\n    typeof(goog.global.btoa) == 'function';\n\n\n/**\n * Does this browser have a working atob function?\n * We blacklist known-bad implementations:\n *  - IE (10+) added atob() but it does not tolerate whitespace on the input.\n * @private {boolean}\n */\ngoog.crypt.base64.HAS_NATIVE_DECODE_ =\n    goog.crypt.base64.ASSUME_NATIVE_SUPPORT_ ||\n    (!goog.userAgent.product.SAFARI && !goog.userAgent.IE &&\n     typeof(goog.global.atob) == 'function');\n\n\n/**\n * Base64-encode an array of bytes.\n *\n * @param {Array<number>|Uint8Array} input An array of bytes (numbers with\n *     value in [0, 255]) to encode.\n * @param {!goog.crypt.base64.Alphabet=} alphabet Base 64 alphabet to\n *     use in encoding. Alphabet.DEFAULT is used by default.\n * @return {string} The base64 encoded string.\n */\ngoog.crypt.base64.encodeByteArray = function(input, alphabet) {\n  // Assert avoids runtime dependency on goog.isArrayLike, which helps reduce\n  // size of jscompiler output, and which yields slight performance increase.\n  goog.asserts.assert(\n      goog.isArrayLike(input), 'encodeByteArray takes an array as a parameter');\n\n  if (alphabet === undefined) {\n    alphabet = goog.crypt.base64.Alphabet.DEFAULT;\n  }\n\n  goog.crypt.base64.init_();\n\n  var byteToCharMap = goog.crypt.base64.byteToCharMaps_[alphabet];\n\n  var output = [];\n\n  for (var i = 0; i < input.length; i += 3) {\n    var byte1 = input[i];\n    var haveByte2 = i + 1 < input.length;\n    var byte2 = haveByte2 ? input[i + 1] : 0;\n    var haveByte3 = i + 2 < input.length;\n    var byte3 = haveByte3 ? input[i + 2] : 0;\n\n    var outByte1 = byte1 >> 2;\n    var outByte2 = ((byte1 & 0x03) << 4) | (byte2 >> 4);\n    var outByte3 = ((byte2 & 0x0F) << 2) | (byte3 >> 6);\n    var outByte4 = byte3 & 0x3F;\n\n    if (!haveByte3) {\n      outByte4 = 64;\n\n      if (!haveByte2) {\n        outByte3 = 64;\n      }\n    }\n\n    output.push(\n        byteToCharMap[outByte1], byteToCharMap[outByte2],\n        byteToCharMap[outByte3] || '', byteToCharMap[outByte4] || '');\n  }\n\n  return output.join('');\n};\n\n\n/**\n * Base64-encode a string.\n *\n * @param {string} input A string to encode.\n * @param {!goog.crypt.base64.Alphabet=} alphabet Base 64 alphabet to\n *     use in encoding. Alphabet.DEFAULT is used by default.\n * @return {string} The base64 encoded string.\n */\ngoog.crypt.base64.encodeString = function(input, alphabet) {\n  // Shortcut for browsers that implement\n  // a native base64 encoder in the form of \"btoa/atob\"\n  if (goog.crypt.base64.HAS_NATIVE_ENCODE_ && !alphabet) {\n    return goog.global.btoa(input);\n  }\n  return goog.crypt.base64.encodeByteArray(\n      goog.crypt.stringToByteArray(input), alphabet);\n};\n\n\n/**\n * Base64-decode a string.\n *\n * @param {string} input Input to decode. Any whitespace is ignored, and the\n *     input maybe encoded with either supported alphabet (or a mix thereof).\n * @param {boolean=} useCustomDecoder True indicates the custom decoder is used,\n *     which supports alternative alphabets. Note that passing false may still\n *     use the custom decoder on browsers without native support.\n * @return {string} string representing the decoded value.\n */\ngoog.crypt.base64.decodeString = function(input, useCustomDecoder) {\n  // Shortcut for browsers that implement\n  // a native base64 encoder in the form of \"btoa/atob\"\n  if (goog.crypt.base64.HAS_NATIVE_DECODE_ && !useCustomDecoder) {\n    return goog.global.atob(input);\n  }\n  var output = '';\n  function pushByte(b) {\n    output += String.fromCharCode(b);\n  }\n\n  goog.crypt.base64.decodeStringInternal_(input, pushByte);\n\n  return output;\n};\n\n\n/**\n * Base64-decode a string to an Array of numbers.\n *\n * In base-64 decoding, groups of four characters are converted into three\n * bytes.  If the encoder did not apply padding, the input length may not\n * be a multiple of 4.\n *\n * In this case, the last group will have fewer than 4 characters, and\n * padding will be inferred.  If the group has one or two characters, it decodes\n * to one byte.  If the group has three characters, it decodes to two bytes.\n *\n * @param {string} input Input to decode. Any whitespace is ignored, and the\n *     input maybe encoded with either supported alphabet (or a mix thereof).\n * @param {boolean=} opt_ignored Unused parameter, retained for compatibility.\n * @return {!Array<number>} bytes representing the decoded value.\n */\ngoog.crypt.base64.decodeStringToByteArray = function(input, opt_ignored) {\n  var output = [];\n  function pushByte(b) { output.push(b); }\n\n  goog.crypt.base64.decodeStringInternal_(input, pushByte);\n\n  return output;\n};\n\n\n/**\n * Base64-decode a string to a Uint8Array.\n *\n * Note that Uint8Array is not supported on older browsers, e.g. IE < 10.\n * @see http://caniuse.com/uint8array\n *\n * In base-64 decoding, groups of four characters are converted into three\n * bytes.  If the encoder did not apply padding, the input length may not\n * be a multiple of 4.\n *\n * In this case, the last group will have fewer than 4 characters, and\n * padding will be inferred.  If the group has one or two characters, it decodes\n * to one byte.  If the group has three characters, it decodes to two bytes.\n *\n * @param {string} input Input to decode. Any whitespace is ignored, and the\n *     input maybe encoded with either supported alphabet (or a mix thereof).\n * @return {!Uint8Array} bytes representing the decoded value.\n */\ngoog.crypt.base64.decodeStringToUint8Array = function(input) {\n  goog.asserts.assert(\n      !goog.userAgent.IE || goog.userAgent.isVersionOrHigher('10'),\n      'Browser does not support typed arrays');\n  var len = input.length;\n  // Approximate the length of the array needed for output.\n  // Our method varies according to the format of the input, which we can\n  // consider in three categories:\n  //   A) well-formed with proper padding\n  //   B) well-formed without any padding\n  //   C) not-well-formed, either with extra whitespace in the middle or with\n  //      extra padding characters.\n  //\n  //  In the case of (A), (length * 3 / 4) will result in an integer number of\n  //  bytes evenly divisible by 3, and we need only subtract bytes according to\n  //  the padding observed.\n  //\n  //  In the case of (B), (length * 3 / 4) will result in a non-integer number\n  //  of bytes, or not evenly divisible by 3. (If the result is evenly divisible\n  //  by 3, it's well-formed with the proper amount of padding [0 padding]).\n  //  This approximation can become exact by rounding down.\n  //\n  //  In the case of (C), the only way to get the length is to walk the full\n  //  length of the string to consider each character. This is handled by\n  //  tracking the number of bytes added to the array and using subarray to\n  //  trim the array back down to size.\n  var approxByteLength = len * 3 / 4;\n  if (approxByteLength % 3) {\n    // The string isn't complete, either because it didn't include padding, or\n    // because it has extra white space.\n    // In either case, we won't generate more bytes than are completely encoded,\n    // so rounding down is appropriate to have a buffer at least as large as\n    // output.\n    approxByteLength = Math.floor(approxByteLength);\n  } else if (goog.crypt.base64.isPadding_(input[len - 1])) {\n    // The string has a round length, and has some padding.\n    // Reduce the byte length according to the quantity of padding.\n    if (goog.crypt.base64.isPadding_(input[len - 2])) {\n      approxByteLength -= 2;\n    } else {\n      approxByteLength -= 1;\n    }\n  }\n  var output = new Uint8Array(approxByteLength);\n  var outLen = 0;\n  function pushByte(b) {\n    output[outLen++] = b;\n  }\n\n  goog.crypt.base64.decodeStringInternal_(input, pushByte);\n\n  // Return a subarray to handle the case that input included extra whitespace\n  // or extra padding and approxByteLength was incorrect.\n  return output.subarray(0, outLen);\n};\n\n\n/**\n * @param {string} input Input to decode.\n * @param {function(number):void} pushByte result accumulator.\n * @private\n */\ngoog.crypt.base64.decodeStringInternal_ = function(input, pushByte) {\n  goog.crypt.base64.init_();\n\n  var nextCharIndex = 0;\n  /**\n   * @param {number} default_val Used for end-of-input.\n   * @return {number} The next 6-bit value, or the default for end-of-input.\n   */\n  function getByte(default_val) {\n    while (nextCharIndex < input.length) {\n      var ch = input.charAt(nextCharIndex++);\n      var b = goog.crypt.base64.charToByteMap_[ch];\n      if (b != null) {\n        return b;  // Common case: decoded the char.\n      }\n      if (!goog.string.isEmptyOrWhitespace(ch)) {\n        throw new Error('Unknown base64 encoding at char: ' + ch);\n      }\n      // We encountered whitespace: loop around to the next input char.\n    }\n    return default_val;  // No more input remaining.\n  }\n\n  while (true) {\n    var byte1 = getByte(-1);\n    var byte2 = getByte(0);\n    var byte3 = getByte(64);\n    var byte4 = getByte(64);\n\n    // The common case is that all four bytes are present, so if we have byte4\n    // we can skip over the truncated input special case handling.\n    if (byte4 === 64) {\n      if (byte1 === -1) {\n        return;  // Terminal case: no input left to decode.\n      }\n      // Here we know an intermediate number of bytes are missing.\n      // The defaults for byte2, byte3 and byte4 apply the inferred padding\n      // rules per the public API documentation. i.e: 1 byte\n      // missing should yield 2 bytes of output, but 2 or 3 missing bytes yield\n      // a single byte of output. (Recall that 64 corresponds the padding char).\n    }\n\n    var outByte1 = (byte1 << 2) | (byte2 >> 4);\n    pushByte(outByte1);\n\n    if (byte3 != 64) {\n      var outByte2 = ((byte2 << 4) & 0xF0) | (byte3 >> 2);\n      pushByte(outByte2);\n\n      if (byte4 != 64) {\n        var outByte3 = ((byte3 << 6) & 0xC0) | byte4;\n        pushByte(outByte3);\n      }\n    }\n  }\n};\n\n\n/**\n * Lazy static initialization function. Called before\n * accessing any of the static map variables.\n * @private\n */\ngoog.crypt.base64.init_ = function() {\n  if (goog.crypt.base64.charToByteMap_) {\n    return;\n  }\n  goog.crypt.base64.charToByteMap_ = {};\n\n  // We want quick mappings back and forth, so we precompute encoding maps.\n\n  /** @type {!Array<string>} */\n  var commonChars = goog.crypt.base64.DEFAULT_ALPHABET_COMMON_.split('');\n  var specialChars = [\n    '+/=',  // DEFAULT\n    '+/',   // NO_PADDING\n    '-_=',  // WEBSAFE\n    '-_.',  // WEBSAFE_DOT_PADDING\n    '-_',   // WEBSAFE_NO_PADDING\n  ];\n\n  for (var i = 0; i < 5; i++) {\n    // `i` is each value of the `goog.crypt.base64.Alphabet` enum\n    var chars = commonChars.concat(specialChars[i].split(''));\n\n    // Sets byte-to-char map\n    goog.crypt.base64\n        .byteToCharMaps_[/** @type {!goog.crypt.base64.Alphabet} */ (i)] =\n        chars;\n\n    // Sets char-to-byte map\n    for (var j = 0; j < chars.length; j++) {\n      var char = chars[j];\n\n      var existingByte = goog.crypt.base64.charToByteMap_[char];\n      if (existingByte === undefined) {\n        goog.crypt.base64.charToByteMap_[char] = j;\n      } else {\n        goog.asserts.assert(existingByte === j);\n      }\n    }\n  }\n};\n","/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Utility functions to handle Firebase Auth ID tokens.\n */\n\ngoog.provide('fireauth.IdToken');\n\ngoog.require('goog.crypt');\ngoog.require('goog.crypt.base64');\n\n\n/**\n * Parses the token string into a {@code Token} object.\n * @param {?string} tokenString The JWT token string.\n * @constructor\n */\nfireauth.IdToken = function(tokenString) {\n  const token = fireauth.IdToken.parseIdTokenClaims(tokenString);\n  if (!(token && token['sub'] && token['iss'] &&\n        token['aud'] && token['exp'])) {\n    throw new Error('Invalid JWT');\n  }\n  /** @const @private {string} The plain JWT string. */\n  this.jwt_ = /** @type {string} */ (tokenString);\n  /** @const @private {string} The issuer of the token. */\n  this.iss_ = token['iss'];\n  /** @const @private {string} The audience of the token. */\n  this.aud_ = token['aud'];\n  /** @const @private {number} The expire time in seconds of the token. */\n  this.exp_ = token['exp'];\n  /** @const @private {string} The local user ID of the token. */\n  this.localId_ = token['sub'];\n  const now = Date.now() / 1000;\n  /** @const @private {number} The issue time in seconds of the token. */\n  this.iat_ = token['iat'] || (now > this.exp_ ? this.exp_ : now);\n  /** @const @private {?string} The email address of the token. */\n  this.email_ = token['email'] || null;\n  /** @const @private {boolean} Whether the user is verified. */\n  this.verified_ = !!token['verified'];\n  /** @const @private {?string} The provider ID of the token. */\n  this.providerId_ = token['provider_id'] ||\n      (token['firebase'] && token['firebase']['sign_in_provider']) ||\n      null;\n  /** @const @private {?string} The tenant ID of the token. */\n  this.tenantId_ = (token['firebase'] && token['firebase']['tenant']) || null;\n  /** @const @private {boolean} Whether the user is anonymous. */\n  this.anonymous_ = !!token['is_anonymous'] || this.providerId_ == 'anonymous';\n  /** @const @private {?string} The federated ID of the token. */\n  this.federatedId_ = token['federated_id'] || null;\n  /** @const @private {?string} The display name of the token. */\n  this.displayName_ = token['display_name'] || null;\n  /** @const @private {?string} The photo URL of the token. */\n  this.photoURL_ = token['photo_url'] || null;\n  /**\n   * @const @private {?string} The phone number of the user identified by the\n   *     token.\n   */\n  this.phoneNumber_ = token['phone_number'] || null;\n};\n\n\n/**\n * @typedef {{\n *   identities: (?Object|undefined),\n *   sign_in_provider: (?string|undefined),\n *   tenant: (string|undefined)\n * }}\n */\nfireauth.IdToken.Firebase;\n\n\n/**\n * @typedef {{\n *   iss: string,\n *   aud: string,\n *   exp: number,\n *   sub: string,\n *   iat: (?number|undefined),\n *   email: (?string|undefined),\n *   verified: (?boolean|undefined),\n *   provider_id: (?string|undefined),\n *   is_anonymous: (?boolean|undefined),\n *   federated_id: (?string|undefined),\n *   display_name: (?string|undefined),\n *   photo_url: (?string|undefined),\n *   phone_number: (?string|undefined),\n *   firebase: (?fireauth.IdToken.Firebase|undefined)\n * }}\n */\nfireauth.IdToken.JsonToken;\n\n\n/** @return {?string} The email address of the account. */\nfireauth.IdToken.prototype.getEmail = function() {\n  return this.email_;\n};\n\n\n/**\n * @deprecated Use client side clock to calculate when the token expires.\n * @return {number} The expire time in seconds.\n */\nfireauth.IdToken.prototype.getExp = function() {\n  return this.exp_;\n};\n\n\n/**\n * @return {number} The difference in seconds between when the token was\n *     issued and when it expires.\n */\nfireauth.IdToken.prototype.getExpiresIn = function() {\n  return this.exp_ - this.iat_;\n};\n\n\n/** @return {?string} The ID of the identity provider. */\nfireauth.IdToken.prototype.getProviderId = function() {\n  return this.providerId_;\n};\n\n\n/** @return {?string} The tenant ID. */\nfireauth.IdToken.prototype.getTenantId = function() {\n  return this.tenantId_;\n};\n\n\n/** @return {?string} The display name of the account. */\nfireauth.IdToken.prototype.getDisplayName = function() {\n  return this.displayName_;\n};\n\n\n/** @return {?string} The photo URL of the account. */\nfireauth.IdToken.prototype.getPhotoUrl = function() {\n  return this.photoURL_;\n};\n\n\n/** @return {string} The user ID of the account. */\nfireauth.IdToken.prototype.getLocalId = function() {\n  return this.localId_;\n};\n\n\n/** @return {?string} The federated ID of the account. */\nfireauth.IdToken.prototype.getFederatedId = function() {\n  return this.federatedId_;\n};\n\n\n/** @return {boolean} Whether the user is anonymous. */\nfireauth.IdToken.prototype.isAnonymous = function() {\n  return this.anonymous_;\n};\n\n\n/** @return {boolean} Whether the user email is verified. */\nfireauth.IdToken.prototype.isVerified = function() {\n  return this.verified_;\n};\n\n\n/**\n * @deprecated Use client side clock to calculate when the token expires.\n * @return {boolean} Whether token is expired.\n */\nfireauth.IdToken.prototype.isExpired = function() {\n  const now = Math.floor(Date.now() / 1000);\n  // It is expired if token expiration time is less than current time.\n  return this.getExp() <= now;\n};\n\n\n/** @return {string} The issuer of the token. */\nfireauth.IdToken.prototype.getIssuer = function() {\n  return this.iss_;\n};\n\n\n/** @return {?string} The phone number of the account. */\nfireauth.IdToken.prototype.getPhoneNumber = function() {\n  return this.phoneNumber_;\n};\n\n\n/**\n * @return {string} The JWT string.\n * @override\n */\nfireauth.IdToken.prototype.toString = function() {\n  return this.jwt_;\n};\n\n\n/**\n * Parses the JWT token and extracts the information part without verifying the\n * token signature.\n * @param {string} tokenString The JWT token.\n * @return {?fireauth.IdToken} The decoded token.\n */\nfireauth.IdToken.parse = function(tokenString) {\n  try {\n    return new fireauth.IdToken(tokenString);\n  } catch (e) {\n    return null;\n  }\n};\n\n/**\n * Converts the information part of JWT token to plain object format.\n * @param {?string} tokenString The JWT token.\n * @return {?Object}\n */\nfireauth.IdToken.parseIdTokenClaims = function(tokenString) {\n  if (!tokenString) {\n    return null;\n  }\n  // Token format is <algorithm>.<info>.<sig>\n  const fields = tokenString.split('.');\n  if (fields.length != 3) {\n    return null;\n  }\n  let jsonInfo = fields[1];\n  // Google base64 library does not handle padding.\n  const padLen = (4 - jsonInfo.length % 4) % 4;\n  for (let i = 0; i < padLen; i++) {\n    jsonInfo += '.';\n  }\n  try {\n    const decodedClaims = goog.crypt.utf8ByteArrayToString(\n        goog.crypt.base64.decodeStringToByteArray(jsonInfo));\n    const token = JSON.parse(decodedClaims);\n    return /** @type {?Object} */ (token);\n  } catch (e) {}\n  return null;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Namespace with crypto related helper functions.\n */\n\ngoog.provide('goog.crypt');\n\ngoog.require('goog.array');\ngoog.require('goog.asserts');\n\n\n/**\n * Turns a string into an array of bytes; a \"byte\" being a JS number in the\n * range 0-255. Multi-byte characters are written as little-endian.\n * @param {string} str String value to arrify.\n * @return {!Array<number>} Array of numbers corresponding to the\n *     UCS character codes of each character in str.\n */\ngoog.crypt.stringToByteArray = function(str) {\n  var output = [], p = 0;\n  for (var i = 0; i < str.length; i++) {\n    var c = str.charCodeAt(i);\n    // NOTE: c <= 0xffff since JavaScript strings are UTF-16.\n    if (c > 0xff) {\n      output[p++] = c & 0xff;\n      c >>= 8;\n    }\n    output[p++] = c;\n  }\n  return output;\n};\n\n\n/**\n * Turns an array of numbers into the string given by the concatenation of the\n * characters to which the numbers correspond.\n * @param {!Uint8Array|!Array<number>} bytes Array of numbers representing\n *     characters.\n * @return {string} Stringification of the array.\n */\ngoog.crypt.byteArrayToString = function(bytes) {\n  var CHUNK_SIZE = 8192;\n\n  // Special-case the simple case for speed's sake.\n  if (bytes.length <= CHUNK_SIZE) {\n    return String.fromCharCode.apply(null, bytes);\n  }\n\n  // The remaining logic splits conversion by chunks since\n  // Function#apply() has a maximum parameter count.\n  // See discussion: http://goo.gl/LrWmZ9\n\n  var str = '';\n  for (var i = 0; i < bytes.length; i += CHUNK_SIZE) {\n    var chunk = goog.array.slice(bytes, i, i + CHUNK_SIZE);\n    str += String.fromCharCode.apply(null, chunk);\n  }\n  return str;\n};\n\n\n/**\n * Turns an array of numbers into the hex string given by the concatenation of\n * the hex values to which the numbers correspond.\n * @param {Uint8Array|Array<number>} array Array of numbers representing\n *     characters.\n * @param {string=} opt_separator Optional separator between values\n * @return {string} Hex string.\n */\ngoog.crypt.byteArrayToHex = function(array, opt_separator) {\n  return goog.array\n      .map(\n          array,\n          function(numByte) {\n            var hexByte = numByte.toString(16);\n            return hexByte.length > 1 ? hexByte : '0' + hexByte;\n          })\n      .join(opt_separator || '');\n};\n\n\n/**\n * Converts a hex string into an integer array.\n * @param {string} hexString Hex string of 16-bit integers (two characters\n *     per integer).\n * @return {!Array<number>} Array of {0,255} integers for the given string.\n */\ngoog.crypt.hexToByteArray = function(hexString) {\n  goog.asserts.assert(\n      hexString.length % 2 == 0, 'Key string length must be multiple of 2');\n  var arr = [];\n  for (var i = 0; i < hexString.length; i += 2) {\n    arr.push(parseInt(hexString.substring(i, i + 2), 16));\n  }\n  return arr;\n};\n\n\n/**\n * Converts a JS string to a UTF-8 \"byte\" array.\n * @param {string} str 16-bit unicode string.\n * @return {!Array<number>} UTF-8 byte array.\n */\ngoog.crypt.stringToUtf8ByteArray = function(str) {\n  // TODO(user): Use native implementations if/when available\n  var out = [], p = 0;\n  for (var i = 0; i < str.length; i++) {\n    var c = str.charCodeAt(i);\n    if (c < 128) {\n      out[p++] = c;\n    } else if (c < 2048) {\n      out[p++] = (c >> 6) | 192;\n      out[p++] = (c & 63) | 128;\n    } else if (\n        ((c & 0xFC00) == 0xD800) && (i + 1) < str.length &&\n        ((str.charCodeAt(i + 1) & 0xFC00) == 0xDC00)) {\n      // Surrogate Pair\n      c = 0x10000 + ((c & 0x03FF) << 10) + (str.charCodeAt(++i) & 0x03FF);\n      out[p++] = (c >> 18) | 240;\n      out[p++] = ((c >> 12) & 63) | 128;\n      out[p++] = ((c >> 6) & 63) | 128;\n      out[p++] = (c & 63) | 128;\n    } else {\n      out[p++] = (c >> 12) | 224;\n      out[p++] = ((c >> 6) & 63) | 128;\n      out[p++] = (c & 63) | 128;\n    }\n  }\n  return out;\n};\n\n\n/**\n * Converts a UTF-8 byte array to JavaScript's 16-bit Unicode.\n * @param {Uint8Array|Array<number>} bytes UTF-8 byte array.\n * @return {string} 16-bit Unicode string.\n */\ngoog.crypt.utf8ByteArrayToString = function(bytes) {\n  // TODO(user): Use native implementations if/when available\n  var out = [], pos = 0, c = 0;\n  while (pos < bytes.length) {\n    var c1 = bytes[pos++];\n    if (c1 < 128) {\n      out[c++] = String.fromCharCode(c1);\n    } else if (c1 > 191 && c1 < 224) {\n      var c2 = bytes[pos++];\n      out[c++] = String.fromCharCode((c1 & 31) << 6 | c2 & 63);\n    } else if (c1 > 239 && c1 < 365) {\n      // Surrogate Pair\n      var c2 = bytes[pos++];\n      var c3 = bytes[pos++];\n      var c4 = bytes[pos++];\n      var u = ((c1 & 7) << 18 | (c2 & 63) << 12 | (c3 & 63) << 6 | c4 & 63) -\n          0x10000;\n      out[c++] = String.fromCharCode(0xD800 + (u >> 10));\n      out[c++] = String.fromCharCode(0xDC00 + (u & 1023));\n    } else {\n      var c2 = bytes[pos++];\n      var c3 = bytes[pos++];\n      out[c++] =\n          String.fromCharCode((c1 & 15) << 12 | (c2 & 63) << 6 | c3 & 63);\n    }\n  }\n  return out.join('');\n};\n\n\n/**\n * XOR two byte arrays.\n * @param {!Uint8Array|!Int8Array|!Array<number>} bytes1 Byte array 1.\n * @param {!Uint8Array|!Int8Array|!Array<number>} bytes2 Byte array 2.\n * @return {!Array<number>} Resulting XOR of the two byte arrays.\n */\ngoog.crypt.xorByteArray = function(bytes1, bytes2) {\n  goog.asserts.assert(\n      bytes1.length == bytes2.length, 'XOR array lengths must match');\n\n  var result = [];\n  for (var i = 0; i < bytes1.length; i++) {\n    result.push(bytes1[i] ^ bytes2[i]);\n  }\n  return result;\n};\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines the IdP provider IDs and related settings.\n */\n\ngoog.provide('fireauth.idp');\ngoog.provide('fireauth.idp.IdpSettings');\ngoog.provide('fireauth.idp.ProviderId');\ngoog.provide('fireauth.idp.Settings');\ngoog.provide('fireauth.idp.SignInMethod');\n\ngoog.require('fireauth.constants');\n\n\n/**\n * Enums for supported provider IDs. These provider IDs correspond to the\n * sign_in_provider in the Firebase ID token and do not correspond to the\n * supported client exposed firebase.auth.AuthProviders.\n * @enum {string}\n */\nfireauth.idp.ProviderId = {\n  ANONYMOUS: 'anonymous',\n  CUSTOM: 'custom',\n  FACEBOOK: 'facebook.com',\n  FIREBASE: 'firebase',\n  GITHUB: 'github.com',\n  GOOGLE: 'google.com',\n  PASSWORD: 'password',\n  PHONE: 'phone',\n  TWITTER: 'twitter.com'\n};\n\n\n/**\n * Enums for supported sign in methods.\n * @enum {string}\n */\nfireauth.idp.SignInMethod = {\n  EMAIL_LINK: 'emailLink',\n  EMAIL_PASSWORD: 'password',\n  FACEBOOK: 'facebook.com',\n  GITHUB: 'github.com',\n  GOOGLE: 'google.com',\n  PHONE: 'phone',\n  TWITTER: 'twitter.com'\n};\n\n\n/**\n * The settings of an identity provider. The fields are:\n * <ul>\n * <li>languageParam: defines the custom OAuth language parameter.\n * <li>popupWidth: defines the popup recommended width.\n * <li>popupHeight: defines the popup recommended height.\n * <li>providerId: defines the provider ID.\n * <li>reservedOAuthParameters: defines the list of reserved OAuth parameters.\n * </ul>\n * @typedef {{\n *   languageParam: (?string|undefined),\n *   popupWidth: (?number|undefined),\n *   popupHeight: (?number|undefined),\n *   providerId: !fireauth.idp.ProviderId,\n *   reservedOAuthParameters: !Array<string>\n * }}\n */\nfireauth.idp.IdpSettings;\n\n\n/**\n * The list of reserved OAuth 1.0 parameters.\n * @const {!Array<string>}\n */\nfireauth.idp.RESERVED_OAUTH1_PARAMS =\n    ['oauth_consumer_key', 'oauth_nonce', 'oauth_signature',\n     'oauth_signature_method', 'oauth_timestamp', 'oauth_token',\n     'oauth_version'];\n\n\n/**\n * The list of reserved OAuth 2.0 parameters.\n * @const {!Array<string>}\n */\nfireauth.idp.RESERVED_OAUTH2_PARAMS =\n    ['client_id', 'response_type', 'scope', 'redirect_uri', 'state'];\n\n\n/**\n * The recommendations for the different IdP display settings.\n * @enum {!fireauth.idp.IdpSettings}\n */\nfireauth.idp.Settings = {\n  FACEBOOK: {\n    languageParam: 'locale',\n    popupWidth: 700,\n    popupHeight: 600,\n    providerId: fireauth.idp.ProviderId.FACEBOOK,\n    reservedOAuthParameters: fireauth.idp.RESERVED_OAUTH2_PARAMS,\n  },\n  GITHUB: {\n    languageParam: null,\n    popupWidth: 500,\n    popupHeight: 750,\n    providerId: fireauth.idp.ProviderId.GITHUB,\n    reservedOAuthParameters: fireauth.idp.RESERVED_OAUTH2_PARAMS,\n  },\n  GOOGLE: {\n    languageParam: 'hl',\n    popupWidth: 515,\n    popupHeight: 680,\n    providerId: fireauth.idp.ProviderId.GOOGLE,\n    reservedOAuthParameters: fireauth.idp.RESERVED_OAUTH2_PARAMS,\n  },\n  TWITTER: {\n    languageParam: 'lang',\n    popupWidth: 485,\n    popupHeight: 705,\n    providerId: fireauth.idp.ProviderId.TWITTER,\n    reservedOAuthParameters: fireauth.idp.RESERVED_OAUTH1_PARAMS,\n  },\n  APPLE: {\n    languageParam: 'locale',\n    popupWidth: 640,\n    popupHeight: 600,\n    providerId: 'apple.com',\n    reservedOAuthParameters: [],\n  },\n};\n\n\n/**\n * @param {!fireauth.idp.ProviderId} providerId The requested provider ID.\n * @return {?fireauth.idp.Settings} The settings for the requested provider ID.\n */\nfireauth.idp.getIdpSettings = function(providerId) {\n  for (var key in fireauth.idp.Settings) {\n    if (fireauth.idp.Settings[key].providerId == providerId) {\n      return fireauth.idp.Settings[key];\n    }\n  }\n  return null;\n};\n\n\n/**\n * @param {!fireauth.idp.ProviderId} providerId The requested provider ID.\n * @return {!Array<string>} The list of reserved OAuth parameters.\n */\nfireauth.idp.getReservedOAuthParams = function(providerId) {\n  var settings = fireauth.idp.getIdpSettings(providerId);\n  return (settings && settings.reservedOAuthParameters) || [];\n};\n\n\n/**\n * @param {?string|undefined} identifier The provider identifier.\n * @return {boolean} Whether the identifier provided is a SAML provider ID.\n */\nfireauth.idp.isSaml = function(identifier) {\n   return typeof identifier === 'string' &&\n     identifier.indexOf(fireauth.constants.SAML_PREFIX) == 0;\n};\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines all the fireauth additional user info interfaces,\n * implementations and subclasses.\n */\n\ngoog.provide('fireauth.AdditionalUserInfo');\ngoog.provide('fireauth.FacebookAdditionalUserInfo');\ngoog.provide('fireauth.FederatedAdditionalUserInfo');\ngoog.provide('fireauth.GenericAdditionalUserInfo');\ngoog.provide('fireauth.GithubAdditionalUserInfo');\ngoog.provide('fireauth.GoogleAdditionalUserInfo');\ngoog.provide('fireauth.TwitterAdditionalUserInfo');\n\ngoog.require('fireauth.IdToken');\ngoog.require('fireauth.idp');\ngoog.require('fireauth.object');\ngoog.require('fireauth.util');\n\n\n/**\n * The interface that represents additional user info.\n * @interface\n */\nfireauth.AdditionalUserInfo = function() {};\n\n\n/**\n * Constructs the corresponding additional user info for the backend\n * verifyAssertion response.\n * @param {?Object|undefined} resp The backend verifyAssertion,\n *     verifyPhoneNumber or verifyPassword/setAccountInfo response.\n * @return {?fireauth.AdditionalUserInfo} The fireauth.AdditionalUserInfo\n *     instance.\n */\nfireauth.AdditionalUserInfo.fromPlainObject = function(resp) {\n  var factory = {};\n  factory[fireauth.idp.ProviderId.FACEBOOK] =\n      fireauth.FacebookAdditionalUserInfo;\n  factory[fireauth.idp.ProviderId.GOOGLE] =\n      fireauth.GoogleAdditionalUserInfo;\n  factory[fireauth.idp.ProviderId.GITHUB] =\n      fireauth.GithubAdditionalUserInfo;\n  factory[fireauth.idp.ProviderId.TWITTER] =\n      fireauth.TwitterAdditionalUserInfo;\n  // Provider ID and UID are required.\n  var providerId =\n      resp &&\n      resp[fireauth.AdditionalUserInfo.VerifyAssertionField.PROVIDER_ID];\n  try {\n    // Provider ID already present.\n    if (providerId) {\n      if (factory[providerId]) {\n        // 1st class supported federated providers.\n        return new factory[providerId](resp);\n      } else {\n        // Generic federated providers.\n        return new fireauth.FederatedAdditionalUserInfo(\n            /** @type {!Object} */ (resp));\n      }\n    } else if (typeof resp[fireauth.AdditionalUserInfo.VerifyAssertionField\n                           .ID_TOKEN] !== 'undefined') {\n      // For all other ID token responses with no providerId, get the required\n      // providerId from the ID token itself.\n      return new fireauth.GenericAdditionalUserInfo(\n          /** @type {!Object} */ (resp));\n    }\n  } catch (e) {\n    // Do nothing, null will be returned.\n  }\n  return null;\n};\n\n\n\n/**\n * verifyAssertion response additional user info fields.\n * @enum {string}\n */\nfireauth.AdditionalUserInfo.VerifyAssertionField = {\n  ID_TOKEN: 'idToken',\n  IS_NEW_USER: 'isNewUser',\n  KIND: 'kind',\n  PROVIDER_ID: 'providerId',\n  RAW_USER_INFO: 'rawUserInfo',\n  SCREEN_NAME: 'screenName'\n};\n\n\n/**\n * Constructs a generic additional user info object from the backend\n * verifyPhoneNumber and verifyPassword provider response.\n * @param {!Object} info The verifyPhoneNumber/verifyPassword/setAccountInfo\n *     response data object.\n * @constructor\n * @implements {fireauth.AdditionalUserInfo}\n */\nfireauth.GenericAdditionalUserInfo = function(info) {\n  // Federated provider profile data.\n  var providerId =\n      info[fireauth.AdditionalUserInfo.VerifyAssertionField.PROVIDER_ID];\n  // Try to get providerId from the ID token if available.\n  if (!providerId &&\n      info[fireauth.AdditionalUserInfo.VerifyAssertionField.ID_TOKEN]) {\n    // verifyPassword/setAccountInfo and verifyPhoneNumber return an ID token\n    // but no providerId. Get providerId from the token itself.\n    // isNewUser will be returned for verifyPhoneNumber.\n    var idToken = fireauth.IdToken.parse(\n        info[fireauth.AdditionalUserInfo.VerifyAssertionField.ID_TOKEN]);\n    if (idToken && idToken.getProviderId()) {\n      providerId = idToken.getProviderId();\n    }\n  }\n  if (!providerId) {\n    // This is internal only.\n    throw new Error('Invalid additional user info!');\n  }\n  // For custom token and anonymous token, set provider ID to null.\n  if (providerId == fireauth.idp.ProviderId.ANONYMOUS ||\n      providerId == fireauth.idp.ProviderId.CUSTOM) {\n      providerId = null;\n  }\n  // Check whether user is new. Temporary Solution since backend does not return\n  // isNewUser field for SignupNewUserResponse.\n  var isNewUser = false;\n  if (typeof info[fireauth.AdditionalUserInfo.VerifyAssertionField.IS_NEW_USER]\n      !== 'undefined') {\n    isNewUser =\n        !!info[fireauth.AdditionalUserInfo.VerifyAssertionField.IS_NEW_USER];\n  } else if (info[fireauth.AdditionalUserInfo.VerifyAssertionField.KIND]\n             === 'identitytoolkit#SignupNewUserResponse') {\n    //For SignupNewUserResponse, always set isNewUser to true.\n    isNewUser = true;\n  }\n  // Set required providerId.\n  fireauth.object.setReadonlyProperty(this, 'providerId', providerId);\n  // Set read-only isNewUser property.\n  fireauth.object.setReadonlyProperty(this, 'isNewUser', isNewUser);\n};\n\n\n/**\n * Constructs a federated additional user info object from the backend\n * verifyAssertion federated provider response.\n * @param {!Object} info The verifyAssertion response data object.\n * @constructor\n * @extends {fireauth.GenericAdditionalUserInfo}\n */\nfireauth.FederatedAdditionalUserInfo = function(info) {\n  fireauth.FederatedAdditionalUserInfo.base(this, 'constructor', info);\n  // Federated provider profile data.\n  // This structure will also be used for generic IdPs.\n  var profile = fireauth.util.parseJSON(\n      info[fireauth.AdditionalUserInfo.VerifyAssertionField.RAW_USER_INFO] ||\n      '{}');\n  // Set read-only profile property.\n  fireauth.object.setReadonlyProperty(\n      this,\n      'profile',\n      fireauth.object.unsafeCreateReadOnlyCopy(profile || {}));\n};\ngoog.inherits(\n    fireauth.FederatedAdditionalUserInfo, fireauth.GenericAdditionalUserInfo);\n\n\n/**\n * Constructs a Facebook additional user info object from the backend\n * verifyAssertion Facebook provider response.\n * @param {!Object} info The verifyAssertion response data object.\n * @constructor\n * @extends {fireauth.FederatedAdditionalUserInfo}\n */\nfireauth.FacebookAdditionalUserInfo = function(info) {\n  fireauth.FacebookAdditionalUserInfo.base(this, 'constructor', info);\n  // This should not happen as this object is initialized via fromPlainObject.\n  if (this['providerId'] != fireauth.idp.ProviderId.FACEBOOK) {\n    throw new Error('Invalid provider ID!');\n  }\n};\ngoog.inherits(\n    fireauth.FacebookAdditionalUserInfo, fireauth.FederatedAdditionalUserInfo);\n\n\n\n/**\n * Constructs a GitHub additional user info object from the backend\n * verifyAssertion GitHub provider response.\n * @param {!Object} info The verifyAssertion response data object.\n * @constructor\n * @extends {fireauth.FederatedAdditionalUserInfo}\n */\nfireauth.GithubAdditionalUserInfo = function(info) {\n  fireauth.GithubAdditionalUserInfo.base(this, 'constructor', info);\n  // This should not happen as this object is initialized via fromPlainObject.\n  if (this['providerId'] != fireauth.idp.ProviderId.GITHUB) {\n    throw new Error('Invalid provider ID!');\n  }\n  // GitHub username.\n  fireauth.object.setReadonlyProperty(\n      this,\n      'username',\n      (this['profile'] && this['profile']['login']) || null);\n};\ngoog.inherits(\n    fireauth.GithubAdditionalUserInfo, fireauth.FederatedAdditionalUserInfo);\n\n\n\n/**\n * Constructs a Google additional user info object from the backend\n * verifyAssertion Google provider response.\n * @param {!Object} info The verifyAssertion response data object.\n * @constructor\n * @extends {fireauth.FederatedAdditionalUserInfo}\n */\nfireauth.GoogleAdditionalUserInfo = function(info) {\n  fireauth.GoogleAdditionalUserInfo.base(this, 'constructor', info);\n  // This should not happen as this object is initialized via fromPlainObject.\n  if (this['providerId'] != fireauth.idp.ProviderId.GOOGLE) {\n    throw new Error('Invalid provider ID!');\n  }\n};\ngoog.inherits(\n    fireauth.GoogleAdditionalUserInfo, fireauth.FederatedAdditionalUserInfo);\n\n\n\n/**\n * Constructs a Twitter additional user info object from the backend\n * verifyAssertion Twitter provider response.\n * @param {!Object} info The verifyAssertion response data object.\n * @constructor\n * @extends {fireauth.FederatedAdditionalUserInfo}\n */\nfireauth.TwitterAdditionalUserInfo = function(info) {\n  fireauth.TwitterAdditionalUserInfo.base(this, 'constructor', info);\n  // This should not happen as this object is initialized via fromPlainObject.\n  if (this['providerId'] != fireauth.idp.ProviderId.TWITTER) {\n    throw new Error('Invalid provider ID!');\n  }\n  // Twitter user name.\n  fireauth.object.setReadonlyProperty(\n      this,\n      'username',\n      info[fireauth.AdditionalUserInfo.VerifyAssertionField.SCREEN_NAME] ||\n      null);\n};\ngoog.inherits(\n    fireauth.TwitterAdditionalUserInfo, fireauth.FederatedAdditionalUserInfo);\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines the Firebase dynamic link constructor.\n */\n\ngoog.provide('fireauth.DynamicLink');\n\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.authenum.Error');\ngoog.require('fireauth.object');\ngoog.require('fireauth.util');\ngoog.require('goog.Uri');\n\n\n/**\n * Dynamic link builder used to help build the FDL link to redirect to an app\n * while passing some payload or error.\n * @param {?string} fdlDomain The FDL domain. If none is available, custom\n *     scheme redirects are used.\n * @param {!fireauth.DynamicLink.Platform} platform The FDL supported\n *     platform (Android or iOS).\n * @param {string} appIdentifier The app identifier (iOS bundle ID or Android\n *     package name).\n * @param {string} authDomain The Firebase application authDomain.\n * @param {string} payload The FDL deep link content.\n * @param {?string=} opt_clientId The optional OAuth client ID.\n * @constructor\n */\nfireauth.DynamicLink = function(fdlDomain, platform, appIdentifier, authDomain,\n    payload, opt_clientId) {\n  // The fallback error when the app is not installed on the device.\n  var defaultError =\n      new fireauth.AuthError(fireauth.authenum.Error.APP_NOT_INSTALLED);\n  /** @private {string} The fallback URL when the app is not installed. */\n  this.fallbackUrl_ = 'https://' + authDomain + '/__/auth/handler?' +\n      'firebaseError=' + encodeURIComponent(/** @type {string} */ (\n          fireauth.util.stringifyJSON(defaultError.toPlainObject())));\n  fireauth.object.setReadonlyProperty(this, 'fallbackUrl', this.fallbackUrl_);\n  /** @private {?string} The FDL domain if available. */\n  this.fdlDomain_ = fdlDomain;\n  fireauth.object.setReadonlyProperty(this, 'fdlDomain', fdlDomain);\n  /** @private {!fireauth.DynamicLink.Platform} The FDL link platform. */\n  this.platform_ = platform;\n  fireauth.object.setReadonlyProperty(this, 'platform', platform);\n  /** @private {string} The app identifier. */\n  this.appIdentifier_ = appIdentifier;\n  fireauth.object.setReadonlyProperty(this, 'appIdentifier', appIdentifier);\n  /** @private {string} The Firebase application authDomain. */\n  this.authDomain_ = authDomain;\n  fireauth.object.setReadonlyProperty(this, 'authDomain', authDomain);\n  /** @private {string} The FDL deep link content. */\n  this.link_ = payload;\n  fireauth.object.setReadonlyProperty(this, 'payload', payload);\n  /** @private {?string} The application display name. */\n  this.appName_ = null;\n  fireauth.object.setReadonlyProperty(this, 'appName', null);\n  /** @private {?string} The client ID if available. */\n  this.clientId_ = opt_clientId || null;\n  fireauth.object.setReadonlyProperty(this, 'clientId', this.clientId_);\n};\n\n\n/**\n * Sets the app name for the current dynamic link.\n * @param {?string|undefined} appName The app name typically displayed in an FDL\n *     button.\n */\nfireauth.DynamicLink.prototype.setAppName = function(appName) {\n  this.appName_ = appName || null;\n  fireauth.object.setReadonlyProperty(this, 'appName', appName);\n};\n\n\n/**\n * Sets the dynamic link fallback URL overriding the default one.\n * @param {string} fallbackUrl The dynamic link fallback URL.\n */\nfireauth.DynamicLink.prototype.setFallbackUrl = function(fallbackUrl) {\n  this.fallbackUrl_ = fallbackUrl;\n  fireauth.object.setReadonlyProperty(this, 'fallbackUrl', fallbackUrl);\n};\n\n\n/**\n * Parses a dynamic link object from an automatic FDL redirect link.\n * @param {string} url The URL string to parse and convert to a dynamic link.\n * @return {?fireauth.DynamicLink} The corresponding dynamic link if applicable.\n */\nfireauth.DynamicLink.fromURL = function(url) {\n  // This constructs the Dynamic link from the URL provided.\n  var uri = goog.Uri.parse(url);\n  var fdlDomain = uri.getParameterValue('fdlDomain');\n  var platform = uri.getParameterValue('platform');\n  var appIdentifier = uri.getParameterValue('appIdentifier');\n  var authDomain = uri.getParameterValue('authDomain');\n  var payload = uri.getParameterValue('link');\n  var appName = uri.getParameterValue('appName');\n  if (fdlDomain && platform && appIdentifier && authDomain && payload &&\n      appName) {\n    var dl = new fireauth.DynamicLink(\n        /** @type {string} */ (fdlDomain),\n        /** @type {!fireauth.DynamicLink.Platform} */ (platform),\n        /** @type {string} */ (appIdentifier),\n        /** @type {string} */ (authDomain),\n        /** @type {string} */ (payload));\n    dl.setAppName(appName);\n    return dl;\n  }\n  return null;\n};\n\n\n/**\n * @param {string} url The dynamic link URL.\n * @return {string} The deep link embedded within the dynamic link.\n */\nfireauth.DynamicLink.parseDeepLink = function(url) {\n  var uri = goog.Uri.parse(url);\n  var link = uri.getParameterValue('link');\n  // Double link case (automatic redirect).\n  var doubleDeepLink = goog.Uri.parse(link).getParameterValue('link');\n  // iOS custom scheme links.\n  var iOSdeepLink = uri.getParameterValue('deep_link_id');\n  var iOSDoubledeepLink = goog.Uri.parse(iOSdeepLink).getParameterValue('link');\n  var callbackUrl =\n      iOSDoubledeepLink || iOSdeepLink || doubleDeepLink || link || url;\n  return callbackUrl;\n};\n\n\n/**\n * The supported FDL platforms.\n * @enum {string}\n */\nfireauth.DynamicLink.Platform = {\n  ANDROID: 'android',\n  IOS: 'ios'\n};\n\n\n/**\n * Constructs the common FDL link base used for building the button link or the\n * automatic redirect link.\n * @param {string} fallbackUrl The fallback URL to use.\n * @return {!goog.Uri} The partial URI of the FDL link used to build the final\n *     button link or the automatic redirect link.\n * @private\n */\nfireauth.DynamicLink.prototype.constructFdlBase_ = function(fallbackUrl) {\n  var uri = goog.Uri.create(\n      'https',\n      null,\n      this.fdlDomain_,\n      null,\n      '/');\n  if (this.platform_ == fireauth.DynamicLink.Platform.ANDROID) {\n    uri.setParameterValue('apn', this.appIdentifier_);\n    uri.setParameterValue('afl', fallbackUrl);\n  } else if (this.platform_ == fireauth.DynamicLink.Platform.IOS) {\n    uri.setParameterValue('ibi', this.appIdentifier_);\n    uri.setParameterValue('ifl', fallbackUrl);\n  }\n  return uri;\n};\n\n\n/**\n * Constructs the custom scheme URL. This is used when no FDL domain is\n * available.\n * @return {!goog.Uri} The uri of the dynamic link used to build the final\n *      button link or the automatic redirect link.\n * @private\n */\nfireauth.DynamicLink.prototype.constructCustomSchemeUrl_ = function() {\n  // This mimics the FDL custom scheme URL format.\n  var uri = goog.Uri.create(\n      this.clientId_ ? this.clientId_.split('.').reverse().join('.') :\n          this.appIdentifier_,\n      null,\n      // 'firebaseauth' is used in the app verification flow.\n      // 'google' is used for the Cordova iOS flow.\n      this.clientId_ ? 'firebaseauth' : 'google',\n      null,\n      '/link');\n  uri.setParameterValue('deep_link_id', this.link_);\n  return uri;\n};\n\n\n/**\n * @param {boolean=} opt_isAutoRedirect Whether the link is an auto redirect\n *     link.\n * @return {string} The generated dynamic link string.\n * @override\n */\nfireauth.DynamicLink.prototype.toString = function(opt_isAutoRedirect) {\n  // When FDL domain is not available, always returns the custom scheme URL.\n  if (!this.fdlDomain_) {\n    return this.constructCustomSchemeUrl_().toString();\n  }\n  if (!!opt_isAutoRedirect) {\n    return this.generateAutomaticRedirectLink_();\n  }\n  return this.generateButtonLink_();\n};\n\n\n/**\n * @return {string} The final FDL button link.\n * @private\n */\nfireauth.DynamicLink.prototype.generateButtonLink_ = function() {\n  var fdlLink = this.constructFdlBase_(this.fallbackUrl_);\n  fdlLink.setParameterValue('link', this.link_);\n  return fdlLink.toString();\n};\n\n\n/**\n * @return {string} The final FDL automatic redirect link.\n * @private\n */\nfireauth.DynamicLink.prototype.generateAutomaticRedirectLink_ =\n    function() {\n  var doubleDeeplink = goog.Uri.create(\n      'https',\n      null,\n      this.authDomain_,\n      null,\n      '/__/auth/callback');\n  doubleDeeplink.setParameterValue('fdlDomain', this.fdlDomain_);\n  doubleDeeplink.setParameterValue('platform', this.platform_);\n  doubleDeeplink.setParameterValue('appIdentifier', this.appIdentifier_);\n  doubleDeeplink.setParameterValue('authDomain', this.authDomain_);\n  doubleDeeplink.setParameterValue('link', this.link_);\n  doubleDeeplink.setParameterValue('appName', this.appName_ || '');\n  // The fallback URL is the deep link itself.\n  // This is in case the link fails to be intercepted by the app, FDL will\n  // redirect to the fallback URL.\n  var fdlLink = this.constructFdlBase_(doubleDeeplink.toString());\n  fdlLink.setParameterValue('link', doubleDeeplink.toString());\n  return fdlLink.toString();\n};\n","/**\n * @license\n * Copyright 2019 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines the multi-factor session object used for enrolling a\n * second factor on a user or helping sign in an enrolled user with a second\n * factor.\n */\n\ngoog.provide('fireauth.MultiFactorSession');\n\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.authenum.Error');\ngoog.require('goog.Promise');\n\n\n/**\n * Initializes an instance of a multi-factor session object used for enrolling\n * a second factor on a user or helping sign in an enrolled user with a second\n * factor.\n * This will be constructed either after calling `user.getIdToken()` or from the\n * error containing the pending MFA credential after sign-in.\n * @param {?string} idToken The ID token for an enroll flow. This has to be\n *     retrieved after recent authentication.\n * @param {?string=} mfaPendingCredential The pending credential after an\n *     enrolled second factor user signs in successfully with the first factor.\n * @constructor\n */\nfireauth.MultiFactorSession = function(idToken, mfaPendingCredential) {\n  if (!idToken && !mfaPendingCredential) {\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.INTERNAL_ERROR,\n        'Internal assert: no raw session string available');\n  }\n  // Both fields should never be passed at the same time.\n  if (idToken && mfaPendingCredential) {\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.INTERNAL_ERROR,\n        'Internal assert: unable to determine the session type');\n  }\n  /** @const @private {?string} The ID token for an enroll flow. */\n  this.idToken_ = idToken || null;\n  /** @const @private {?string} The pending credential for a sign-in flow. */\n  this.mfaPendingCredential_ = mfaPendingCredential || null;\n  /** @const @public {!fireauth.MultiFactorSession.Type} The session type.*/\n  this.type = this.idToken_ ?\n      fireauth.MultiFactorSession.Type.ENROLL :\n      fireauth.MultiFactorSession.Type.SIGN_IN;\n};\n\n\n/**\n * Enum representing the type of multi-factor session.\n * @enum {string}\n */\nfireauth.MultiFactorSession.Type = {\n  ENROLL: 'enroll',\n  SIGN_IN: 'signin'\n};\n\n\n/**\n * Returns a promise that resolves with the raw session string.\n * @return {!goog.Promise<string>} A promise that resolves with the raw session\n *     string.\n */\nfireauth.MultiFactorSession.prototype.getRawSession = function() {\n  return this.idToken_ ?\n      goog.Promise.resolve(this.idToken_) :\n      goog.Promise.resolve(this.mfaPendingCredential_);\n};\n\n\n/**\n * Returns the plain object representation of the session object.\n * @return {!Object} The plain object representation of the session object.\n */\nfireauth.MultiFactorSession.prototype.toPlainObject = function() {\n  if (this.type == fireauth.MultiFactorSession.Type.ENROLL) {\n    return {\n      'multiFactorSession': {\n        'idToken': this.idToken_\n      }\n    };\n  } else {\n    return {\n      'multiFactorSession': {\n        'pendingCredential': this.mfaPendingCredential_\n      }\n    };\n  }\n};\n\n\n/**\n * Converts a plain object to a `fireauth.MultiFactorSession` if applicable.\n * @param {?Object} obj The plain object to convert to a\n *     `fireauth.MultiFactorSession`.\n * @return {?fireauth.MultiFactorSession} The corresponding\n *     `fireauth.MultiFactorSession` representation, null otherwise.\n */\nfireauth.MultiFactorSession.fromPlainObject = function(obj) {\n  if (obj && obj['multiFactorSession']) {\n    if (obj['multiFactorSession']['pendingCredential']) {\n      return new fireauth.MultiFactorSession(\n          null, obj['multiFactorSession']['pendingCredential']);\n    } else if (obj['multiFactorSession']['idToken']) {\n      return new fireauth.MultiFactorSession(\n          obj['multiFactorSession']['idToken'], null);\n    }\n  }\n  return null;\n};\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines Auth credentials used for signInWithCredential.\n */\n\ngoog.provide('fireauth.AuthCredential');\ngoog.provide('fireauth.AuthProvider');\ngoog.provide('fireauth.EmailAuthCredential');\ngoog.provide('fireauth.EmailAuthProvider');\ngoog.provide('fireauth.FacebookAuthProvider');\ngoog.provide('fireauth.FederatedProvider');\ngoog.provide('fireauth.GithubAuthProvider');\ngoog.provide('fireauth.GoogleAuthProvider');\ngoog.provide('fireauth.OAuthCredential');\ngoog.provide('fireauth.OAuthProvider');\ngoog.provide('fireauth.OAuthResponse');\ngoog.provide('fireauth.PhoneAuthCredential');\ngoog.provide('fireauth.PhoneAuthProvider');\ngoog.provide('fireauth.SAMLAuthCredential');\ngoog.provide('fireauth.SAMLAuthProvider');\ngoog.provide('fireauth.TwitterAuthProvider');\n\ngoog.requireType('fireauth.RpcHandler');\ngoog.require('fireauth.ActionCodeInfo');\ngoog.require('fireauth.ActionCodeURL');\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.DynamicLink');\ngoog.require('fireauth.IdToken');\ngoog.require('fireauth.MultiFactorAuthCredential');\ngoog.require('fireauth.MultiFactorEnrollmentRequestIdentifier');\ngoog.require('fireauth.MultiFactorSession');\ngoog.require('fireauth.MultiFactorSignInRequestIdentifier');\ngoog.require('fireauth.authenum.Error');\ngoog.require('fireauth.constants');\ngoog.require('fireauth.idp');\ngoog.require('fireauth.object');\ngoog.require('fireauth.util');\ngoog.require('goog.Promise');\ngoog.require('goog.Uri');\ngoog.require('goog.array');\ngoog.require('goog.object');\n\n\n\n/**\n * The interface that represents Auth credential. It provides the underlying\n * implementation for retrieving the ID token depending on the type of\n * credential.\n * @interface\n */\nfireauth.AuthCredential = function() {};\n\n\n/**\n * Returns a promise to retrieve ID token using the underlying RPC handler API\n * for the current credential.\n * @param {!fireauth.RpcHandler} rpcHandler The RPC handler.\n * @return {!goog.Promise<!Object, !fireauth.AuthError>}\n *     idTokenPromise The RPC handler method that returns a promise which\n *     resolves with an ID token.\n */\nfireauth.AuthCredential.prototype.getIdTokenProvider = function(rpcHandler) {};\n\n\n/**\n * Links the credential to an existing account, identified by an ID token.\n * @param {!fireauth.RpcHandler} rpcHandler The RPC handler.\n * @param {string} idToken The ID token of the existing account.\n * @return {!goog.Promise<!Object>} A Promise that resolves when the accounts\n *     are linked.\n */\nfireauth.AuthCredential.prototype.linkToIdToken =\n    function(rpcHandler, idToken) {};\n\n\n/**\n * Tries to match the credential's idToken with the provided UID.\n * @param {!fireauth.RpcHandler} rpcHandler The RPC handler.\n * @param {string} uid The UID of the user to reauthenticate.\n * @return {!goog.Promise<!Object>} A Promise that resolves when\n *     idToken UID match succeeds and returns the server response.\n */\nfireauth.AuthCredential.prototype.matchIdTokenWithUid =\n    function(rpcHandler, uid) {};\n\n\n/**\n * @return {!Object} The plain object representation of an Auth credential. This\n *     will be exposed as toJSON() externally.\n */\nfireauth.AuthCredential.prototype.toPlainObject = function() {};\n\n\n/**\n * @param {!goog.Promise<!Object>} idTokenResolver A promise that resolves with\n *     the ID token response.\n * @param {string} uid The UID to match in the token response.\n * @return {!goog.Promise<!Object>} A promise that resolves with the same\n *     response if the UID matches.\n */\nfireauth.AuthCredential.verifyTokenResponseUid =\n    function(idTokenResolver, uid) {\n  return idTokenResolver.then(function(response) {\n    // This should not happen as rpcHandler verifyAssertion and verifyPassword\n    // always guarantee an ID token is available.\n    if (response[fireauth.RpcHandler.AuthServerField.ID_TOKEN]) {\n      // Parse the token object.\n      var parsedIdToken = fireauth.IdToken.parse(\n          response[fireauth.RpcHandler.AuthServerField.ID_TOKEN]);\n      // Confirm token localId matches the provided UID. If not, throw the user\n      // mismatch error.\n      if (!parsedIdToken || uid != parsedIdToken.getLocalId()) {\n        throw new fireauth.AuthError(fireauth.authenum.Error.USER_MISMATCH);\n      }\n      return response;\n    }\n    throw new fireauth.AuthError(fireauth.authenum.Error.USER_MISMATCH);\n  })\n  .thenCatch(function(error) {\n    // Translate auth/user-not-found error directly to auth/user-mismatch.\n    throw fireauth.AuthError.translateError(\n        error,\n        fireauth.authenum.Error.USER_DELETED,\n        fireauth.authenum.Error.USER_MISMATCH);\n  });\n};\n\n\n\n/**\n * The interface that represents the Auth provider.\n * @interface\n */\nfireauth.AuthProvider = function() {};\n\n\n/**\n * @param {...*} var_args The credential data.\n * @return {!fireauth.AuthCredential} The Auth provider credential.\n */\nfireauth.AuthProvider.credential;\n\n\n/**\n * @typedef {{\n *   accessToken: (?string|undefined),\n *   idToken: (?string|undefined),\n *   nonce: (?string|undefined),\n *   oauthToken: (?string|undefined),\n *   oauthTokenSecret: (?string|undefined),\n *   pendingToken: (?string|undefined)\n * }}\n */\nfireauth.OAuthResponse;\n\n\n/**\n * The SAML Auth credential class. The Constructor is not publicly visible.\n * This is constructed by the SDK on successful or failure after SAML sign-in\n * and returned to developer.\n * @param {!fireauth.idp.ProviderId} providerId The provider ID.\n * @param {string} pendingToken The SAML response pending token.\n * @constructor\n * @implements {fireauth.AuthCredential}\n */\nfireauth.SAMLAuthCredential = function(providerId, pendingToken) {\n  if (pendingToken) {\n    /** @private {string} The pending token where SAML response is encrypted. */\n    this.pendingToken_ = pendingToken;\n  } else {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR,\n        'failed to construct a credential');\n  }\n\n  fireauth.object.setReadonlyProperty(this, 'providerId', providerId);\n  fireauth.object.setReadonlyProperty(this, 'signInMethod', providerId);\n};\n\n\n/**\n * Returns a promise to retrieve ID token using the underlying RPC handler API\n * for the current credential.\n * @param {!fireauth.RpcHandler} rpcHandler The RPC handler.\n * @return {!goog.Promise<!Object, !fireauth.AuthError>}\n *     idTokenPromise The RPC handler method that returns a promise which\n *     resolves with an ID token.\n * @override\n */\nfireauth.SAMLAuthCredential.prototype.getIdTokenProvider =\n    function(rpcHandler) {\n  return rpcHandler.verifyAssertion(\n      /** @type {!fireauth.RpcHandler.VerifyAssertionData} */ (\n      this.makeVerifyAssertionRequest_()));\n};\n\n\n/**\n * Links the credential to an existing account, identified by an ID token.\n * @param {!fireauth.RpcHandler} rpcHandler The rpc handler.\n * @param {string} idToken The ID token of the existing account.\n * @return {!goog.Promise<!Object>} A Promise that resolves when the accounts\n *     are linked, returning the backend response.\n * @override\n */\nfireauth.SAMLAuthCredential.prototype.linkToIdToken =\n    function(rpcHandler, idToken) {\n  var request = this.makeVerifyAssertionRequest_();\n  request['idToken'] = idToken;\n  return rpcHandler.verifyAssertionForLinking(\n      /** @type {!fireauth.RpcHandler.VerifyAssertionData} */ (request));\n};\n\n\n/**\n * Tries to match the credential's idToken with the provided UID.\n * @param {!fireauth.RpcHandler} rpcHandler The RPC handler.\n * @param {string} uid The UID of the user to reauthenticate.\n * @return {!goog.Promise<!Object>} A Promise that resolves when\n *     idToken UID match succeeds and returns the server response.\n * @override\n */\nfireauth.SAMLAuthCredential.prototype.matchIdTokenWithUid =\n    function(rpcHandler, uid) {\n  var request = this.makeVerifyAssertionRequest_();\n  // Do not create a new account if the user doesn't exist.\n  return fireauth.AuthCredential.verifyTokenResponseUid(\n      rpcHandler.verifyAssertionForExisting(\n          /** @type {!fireauth.RpcHandler.VerifyAssertionData} */ (request)),\n      uid);\n};\n\n\n/**\n * @return {!Object} A request to the VerifyAssertion endpoint, populated with\n *     the assertion data from this credential.\n * @private\n */\nfireauth.SAMLAuthCredential.prototype.makeVerifyAssertionRequest_ =\n    function() {\n  return {\n    'pendingToken': this.pendingToken_,\n    // Always use http://localhost.\n    'requestUri': 'http://localhost'\n  };\n};\n\n\n/**\n * @return {!Object} The plain object representation of an Auth credential.\n * @override\n */\nfireauth.SAMLAuthCredential.prototype.toPlainObject = function() {\n  return {\n    'providerId': this['providerId'],\n    'signInMethod': this['signInMethod'],\n    'pendingToken': this.pendingToken_\n  };\n};\n\n\n/**\n * @param {?Object|undefined} json The plain object representation of a\n *     SAMLAuthCredential.\n * @return {?fireauth.SAMLAuthCredential} The SAML credential if the object\n *     is a JSON representation of a SAMLAuthCredential, null otherwise.\n */\nfireauth.SAMLAuthCredential.fromJSON = function(json) {\n  if (json &&\n      json['providerId'] &&\n      json['signInMethod'] &&\n      json['providerId'].indexOf(fireauth.constants.SAML_PREFIX) == 0 &&\n      json['pendingToken']) {\n    try {\n      return new fireauth.SAMLAuthCredential(\n          json['providerId'], json['pendingToken']);\n    } catch (e) {\n      return null;\n    }\n  }\n  return null;\n};\n\n\n/**\n * The OAuth credential class.\n * @param {!fireauth.idp.ProviderId} providerId The provider ID.\n * @param {!fireauth.OAuthResponse} oauthResponse The OAuth\n *     response object containing token information.\n * @param {!fireauth.idp.SignInMethod} signInMethod The sign in method.\n * @constructor\n * @implements {fireauth.AuthCredential}\n */\nfireauth.OAuthCredential = function(providerId, oauthResponse, signInMethod) {\n  /**\n   * @private {?string} The pending token where the IdP response is encrypted.\n   */\n  this.pendingToken_ = null;\n  if (oauthResponse['idToken'] || oauthResponse['accessToken']) {\n    // OAuth 2 and either ID token or access token.\n    if (oauthResponse['idToken']) {\n      fireauth.object.setReadonlyProperty(\n          this, 'idToken', oauthResponse['idToken']);\n    }\n    if (oauthResponse['accessToken']) {\n      fireauth.object.setReadonlyProperty(\n          this, 'accessToken', oauthResponse['accessToken']);\n    }\n    // Add nonce if available and no pendingToken is present.\n    if (oauthResponse['nonce'] && !oauthResponse['pendingToken']) {\n      fireauth.object.setReadonlyProperty(\n          this, 'nonce', oauthResponse['nonce']);\n    }\n    if (oauthResponse['pendingToken']) {\n      this.pendingToken_ = oauthResponse['pendingToken'];\n    }\n  } else if (oauthResponse['oauthToken'] &&\n             oauthResponse['oauthTokenSecret'])  {\n    // OAuth 1 and OAuth token with OAuth token secret.\n    fireauth.object.setReadonlyProperty(\n        this, 'accessToken', oauthResponse['oauthToken']);\n    fireauth.object.setReadonlyProperty(\n        this, 'secret', oauthResponse['oauthTokenSecret']);\n  } else {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR,\n        'failed to construct a credential');\n  }\n\n  fireauth.object.setReadonlyProperty(this, 'providerId', providerId);\n  fireauth.object.setReadonlyProperty(this, 'signInMethod', signInMethod);\n};\n\n\n/**\n * Returns a promise to retrieve ID token using the underlying RPC handler API\n * for the current credential.\n * @param {!fireauth.RpcHandler} rpcHandler The RPC handler.\n * @return {!goog.Promise<!Object, !fireauth.AuthError>}\n *     idTokenPromise The RPC handler method that returns a promise which\n *     resolves with an ID token.\n * @override\n */\nfireauth.OAuthCredential.prototype.getIdTokenProvider = function(rpcHandler) {\n  return rpcHandler.verifyAssertion(\n      /** @type {!fireauth.RpcHandler.VerifyAssertionData} */ (\n      this.makeVerifyAssertionRequest_()));\n};\n\n\n/**\n * Links the credential to an existing account, identified by an ID token.\n * @param {!fireauth.RpcHandler} rpcHandler The rpc handler.\n * @param {string} idToken The ID token of the existing account.\n * @return {!goog.Promise<!Object>} A Promise that resolves when the accounts\n *     are linked, returning the backend response.\n * @override\n */\nfireauth.OAuthCredential.prototype.linkToIdToken =\n    function(rpcHandler, idToken) {\n  var request = this.makeVerifyAssertionRequest_();\n  request['idToken'] = idToken;\n  return rpcHandler.verifyAssertionForLinking(\n      /** @type {!fireauth.RpcHandler.VerifyAssertionData} */ (request));\n};\n\n\n/**\n * Tries to match the credential's idToken with the provided UID.\n * @param {!fireauth.RpcHandler} rpcHandler The RPC handler.\n * @param {string} uid The UID of the user to reauthenticate.\n * @return {!goog.Promise<!Object>} A Promise that resolves when\n *     idToken UID match succeeds and returns the server response.\n * @override\n */\nfireauth.OAuthCredential.prototype.matchIdTokenWithUid =\n    function(rpcHandler, uid) {\n  var request = this.makeVerifyAssertionRequest_();\n  // Do not create a new account if the user doesn't exist.\n  return fireauth.AuthCredential.verifyTokenResponseUid(\n      rpcHandler.verifyAssertionForExisting(\n          /** @type {!fireauth.RpcHandler.VerifyAssertionData} */ (request)),\n      uid);\n};\n\n\n/**\n * @return {!Object} A request to the VerifyAssertion endpoint, populated with\n *     the OAuth data from this credential.\n * @private\n */\nfireauth.OAuthCredential.prototype.makeVerifyAssertionRequest_ = function() {\n  var postBody = {};\n  if (this['idToken']) {\n    postBody['id_token'] = this['idToken'];\n  }\n  if (this['accessToken']) {\n    postBody['access_token'] = this['accessToken'];\n  }\n  if (this['secret']) {\n    postBody['oauth_token_secret'] = this['secret'];\n  }\n  postBody['providerId'] = this['providerId'];\n  // Pass nonce in postBody if available.\n  if (this['nonce'] && !this.pendingToken_) {\n    postBody['nonce'] = this['nonce'];\n  }\n  var request = {\n    'postBody': goog.Uri.QueryData.createFromMap(postBody).toString(),\n    // Always use http://localhost.\n    'requestUri': 'http://localhost'\n  };\n  if (this.pendingToken_) {\n    // For pendingToken, just pass it through and drop postBody.\n    delete request['postBody'];\n    request['pendingToken'] = this.pendingToken_;\n  }\n  return request;\n};\n\n\n/**\n * @return {!Object} The plain object representation of an Auth credential.\n * @override\n */\nfireauth.OAuthCredential.prototype.toPlainObject = function() {\n  var obj = {\n    'providerId': this['providerId'],\n    'signInMethod': this['signInMethod']\n  };\n  if (this['idToken']) {\n    obj['oauthIdToken'] = this['idToken'];\n  }\n  if (this['accessToken']) {\n    obj['oauthAccessToken'] = this['accessToken'];\n  }\n  if (this['secret']) {\n    obj['oauthTokenSecret'] = this['secret'];\n  }\n  if (this['nonce']) {\n    obj['nonce'] = this['nonce'];\n  }\n  if (this.pendingToken_) {\n    obj['pendingToken'] = this.pendingToken_;\n  }\n  return obj;\n};\n\n\n/**\n * @param {?Object|undefined} json The plain object representation of an\n *     OAuthCredential.\n * @return {?fireauth.OAuthCredential} The OAuth/OIDC credential if the object\n *     is a JSON representation of an OAuthCredential, null otherwise.\n */\nfireauth.OAuthCredential.fromJSON = function(json) {\n  if (json &&\n      json['providerId'] &&\n      json['signInMethod']) {\n    // Convert to OAuthResponse format.\n    var oauthResponse = {\n      // OIDC && google.com.\n      'idToken': json['oauthIdToken'],\n      // OAuth 2.0 providers.\n      'accessToken': json['oauthTokenSecret'] ? null : json['oauthAccessToken'],\n      // OAuth 1.0 provider, eg. Twitter.\n      'oauthTokenSecret': json['oauthTokenSecret'],\n      'oauthToken': json['oauthTokenSecret'] && json['oauthAccessToken'],\n      'nonce': json['nonce'],\n      'pendingToken': json['pendingToken']\n    };\n    try {\n      // Constructor will validate the OAuthResponse.\n      return new fireauth.OAuthCredential(\n          json['providerId'], oauthResponse, json['signInMethod']);\n    } catch (e) {\n      return null;\n    }\n  }\n  return null;\n};\n\n\n/**\n * A generic OAuth provider (OAuth1 or OAuth2).\n * @param {string} providerId The IdP provider ID (e.g. google.com,\n *     facebook.com) registered with the backend.\n * @param {?Array<string>=} opt_reservedParams The backlist of parameters that\n *     cannot be set through setCustomParameters.\n * @constructor\n */\nfireauth.FederatedProvider = function(providerId, opt_reservedParams) {\n  /** @private {!Array<string>} */\n  this.reservedParams_ = opt_reservedParams || [];\n\n  // Set read only instance providerId property.\n  // Set read only instance isOAuthProvider property.\n  fireauth.object.setReadonlyProperties(this, {\n    'providerId': providerId,\n    'isOAuthProvider': true\n  });\n\n  /** @private {!Object} The OAuth custom parameters for current provider. */\n  this.customParameters_ = {};\n  /** @protected {?string} The custom OAuth language parameter. */\n  this.languageParameter =\n      (fireauth.idp.getIdpSettings(/** @type {!fireauth.idp.ProviderId} */ (\n          providerId)) || {}).languageParam || null;\n  /** @protected {?string} The default language. */\n  this.defaultLanguageCode = null;\n};\n\n/**\n * @param {!Object} customParameters The custom OAuth parameters to pass\n *     in OAuth request.\n * @return {!fireauth.FederatedProvider} The FederatedProvider instance, for\n *     chaining method calls.\n */\nfireauth.FederatedProvider.prototype.setCustomParameters =\n    function(customParameters) {\n  this.customParameters_ = goog.object.clone(customParameters);\n  return this;\n};\n\n\n/**\n * Set the default language code on the provider instance.\n * @param {?string} languageCode The default language code to set if not already\n *     provided in the custom parameters.\n */\nfireauth.FederatedProvider.prototype.setDefaultLanguage =\n    function(languageCode) {\n  this.defaultLanguageCode = languageCode;\n};\n\n\n/**\n * @return {!Object} The custom OAuth parameters to pass in OAuth request.\n */\nfireauth.FederatedProvider.prototype.getCustomParameters = function() {\n  // The backend already checks for these values and makes sure no reserved\n  // fields like client ID, redirect URI, state are overwritten by these\n  // fields.\n  var params =\n      fireauth.util.copyWithoutNullsOrUndefined(this.customParameters_);\n  // Convert to strings.\n  for (var key in params) {\n    params[key] = params[key].toString();\n  }\n  // Remove blacklisted OAuth custom parameters.\n  var customParams =\n      fireauth.util.removeEntriesWithKeys(params, this.reservedParams_);\n  // If language param supported and not already provided, use default language.\n  if (this.languageParameter &&\n      this.defaultLanguageCode &&\n      !customParams[this.languageParameter]) {\n    customParams[this.languageParameter] = this.defaultLanguageCode;\n  }\n  return customParams;\n};\n\n\n/**\n * Generic SAML auth provider.\n * @param {string} providerId The SAML IdP provider ID (e.g. saml.saml2rp)\n *     registered with the backend.\n * @constructor\n * @extends {fireauth.FederatedProvider}\n * @implements {fireauth.AuthProvider}\n */\nfireauth.SAMLAuthProvider = function(providerId) {\n  // SAML provider IDs must be prefixed with the SAML_PREFIX.\n  if (!fireauth.idp.isSaml(providerId)) {\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.ARGUMENT_ERROR,\n        'SAML provider IDs must be prefixed with \"' +\n        fireauth.constants.SAML_PREFIX + '\"');\n  }\n  // isOAuthProvider is true even though this is not an OAuth provider.\n  // This can be confusing as this is a SAML provider. However, this property\n  // is needed to allow signInWithPopup/Redirect. We should rename it to\n  // something more accurate: isFederatedProvider.\n  fireauth.SAMLAuthProvider.base(this, 'constructor', providerId, []);\n};\ngoog.inherits(fireauth.SAMLAuthProvider, fireauth.FederatedProvider);\n\n\n/**\n * Generic OAuth2 Auth provider.\n * @param {string} providerId The IdP provider ID (e.g. google.com,\n *     facebook.com) registered with the backend.\n * @constructor\n * @extends {fireauth.FederatedProvider}\n * @implements {fireauth.AuthProvider}\n */\nfireauth.OAuthProvider = function(providerId) {\n  fireauth.OAuthProvider.base(this, 'constructor', providerId,\n      fireauth.idp.RESERVED_OAUTH2_PARAMS);\n\n  /** @private {!Array<string>} The list of OAuth2 scopes to request. */\n  this.scopes_ = [];\n};\ngoog.inherits(fireauth.OAuthProvider, fireauth.FederatedProvider);\n\n\n/**\n * @param {string} scope The OAuth scope to request.\n * @return {!fireauth.OAuthProvider} The OAuthProvider instance, for chaining\n *     method calls.\n */\nfireauth.OAuthProvider.prototype.addScope = function(scope) {\n  // If not already added, add scope to list.\n  if (!goog.array.contains(this.scopes_, scope)) {\n    this.scopes_.push(scope);\n  }\n  return this;\n};\n\n\n/** @return {!Array<string>} The Auth provider's list of scopes. */\nfireauth.OAuthProvider.prototype.getScopes = function() {\n  return goog.array.clone(this.scopes_);\n};\n\n\n/**\n * Initializes an OAuth AuthCredential. At least one of ID token or access token\n * must be defined. When providing an OIDC ID token with a nonce encoded, the\n * raw nonce must also be provided.\n * @param {?Object|string} optionsOrIdToken Either the options object containing\n *     the ID token, access token and raw nonce or the ID token string.\n * @param {?string=} opt_accessToken The optional OAuth access token.\n * @return {!fireauth.AuthCredential} The Auth credential object.\n */\nfireauth.OAuthProvider.prototype.credential =\n    function(optionsOrIdToken, opt_accessToken) {\n  var oauthResponse;\n  if (goog.isObject(optionsOrIdToken)) {\n    oauthResponse = {\n      'idToken': optionsOrIdToken['idToken'] || null,\n      'accessToken': optionsOrIdToken['accessToken'] || null,\n      'nonce': optionsOrIdToken['rawNonce'] || null\n    };\n  } else {\n    oauthResponse = {\n      'idToken': optionsOrIdToken || null,\n      'accessToken': opt_accessToken || null\n    };\n  }\n  if (!oauthResponse['idToken'] && !oauthResponse['accessToken']) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.ARGUMENT_ERROR,\n        'credential failed: must provide the ID token and/or the access ' +\n        'token.');\n  }\n  // For OAuthCredential, sign in method is same as providerId.\n  return new fireauth.OAuthCredential(this['providerId'],\n                                      oauthResponse,\n                                      this['providerId']);\n};\n\n\n/**\n * Facebook Auth provider.\n * @constructor\n * @extends {fireauth.OAuthProvider}\n * @implements {fireauth.AuthProvider}\n */\nfireauth.FacebookAuthProvider = function() {\n  fireauth.FacebookAuthProvider.base(this, 'constructor',\n      fireauth.idp.ProviderId.FACEBOOK);\n};\ngoog.inherits(fireauth.FacebookAuthProvider, fireauth.OAuthProvider);\n\nfireauth.object.setReadonlyProperty(fireauth.FacebookAuthProvider,\n    'PROVIDER_ID', fireauth.idp.ProviderId.FACEBOOK);\n\nfireauth.object.setReadonlyProperty(fireauth.FacebookAuthProvider,\n    'FACEBOOK_SIGN_IN_METHOD', fireauth.idp.SignInMethod.FACEBOOK);\n\n\n/**\n * Initializes a Facebook AuthCredential.\n * @param {string} accessTokenOrObject The Facebook access token, or object\n *     containing the token for FirebaseUI backwards compatibility.\n * @return {!fireauth.AuthCredential} The Auth credential object.\n */\nfireauth.FacebookAuthProvider.credential = function(accessTokenOrObject) {\n  if (!accessTokenOrObject) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.ARGUMENT_ERROR,\n        'credential failed: expected 1 argument (the OAuth access token).');\n  }\n  var accessToken = accessTokenOrObject;\n  if (goog.isObject(accessTokenOrObject)) {\n    accessToken = accessTokenOrObject['accessToken'];\n  }\n  return new fireauth.FacebookAuthProvider().credential({\n    'accessToken': /** @type {string} */ (accessToken)\n  });\n};\n\n\n/**\n * GitHub Auth provider.\n * @constructor\n * @extends {fireauth.OAuthProvider}\n * @implements {fireauth.AuthProvider}\n */\nfireauth.GithubAuthProvider = function() {\n  fireauth.GithubAuthProvider.base(this, 'constructor',\n      fireauth.idp.ProviderId.GITHUB);\n};\ngoog.inherits(fireauth.GithubAuthProvider, fireauth.OAuthProvider);\n\nfireauth.object.setReadonlyProperty(fireauth.GithubAuthProvider,\n    'PROVIDER_ID', fireauth.idp.ProviderId.GITHUB);\n\nfireauth.object.setReadonlyProperty(fireauth.GithubAuthProvider,\n    'GITHUB_SIGN_IN_METHOD', fireauth.idp.SignInMethod.GITHUB);\n\n\n/**\n * Initializes a GitHub AuthCredential.\n * @param {string} accessTokenOrObject The GitHub access token, or object\n *     containing the token for FirebaseUI backwards compatibility.\n * @return {!fireauth.AuthCredential} The Auth credential object.\n */\nfireauth.GithubAuthProvider.credential = function(accessTokenOrObject) {\n  if (!accessTokenOrObject) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.ARGUMENT_ERROR,\n        'credential failed: expected 1 argument (the OAuth access token).');\n  }\n  var accessToken = accessTokenOrObject;\n  if (goog.isObject(accessTokenOrObject)) {\n    accessToken = accessTokenOrObject['accessToken'];\n  }\n  return new fireauth.GithubAuthProvider().credential({\n    'accessToken': /** @type {string} */ (accessToken)\n  });\n};\n\n\n/**\n * Google Auth provider.\n * @constructor\n * @extends {fireauth.OAuthProvider}\n * @implements {fireauth.AuthProvider}\n */\nfireauth.GoogleAuthProvider = function() {\n  fireauth.GoogleAuthProvider.base(this, 'constructor',\n      fireauth.idp.ProviderId.GOOGLE);\n\n  // Add profile scope to Google Auth provider as default scope.\n  // This is to ensure profile info is populated in current user.\n  this.addScope('profile');\n};\ngoog.inherits(fireauth.GoogleAuthProvider, fireauth.OAuthProvider);\n\nfireauth.object.setReadonlyProperty(fireauth.GoogleAuthProvider,\n    'PROVIDER_ID', fireauth.idp.ProviderId.GOOGLE);\n\nfireauth.object.setReadonlyProperty(fireauth.GoogleAuthProvider,\n    'GOOGLE_SIGN_IN_METHOD', fireauth.idp.SignInMethod.GOOGLE);\n\n\n/**\n * Initializes a Google AuthCredential.\n * @param {?string=} idTokenOrObject The Google ID token. If null or undefined,\n *     we expect the access token to be passed. It can also be an object\n *     containing the tokens for FirebaseUI backwards compatibility.\n * @param {?string=} accessToken The Google access token. If null or\n *     undefined, we expect the ID token to have been passed.\n * @return {!fireauth.AuthCredential} The Auth credential object.\n */\nfireauth.GoogleAuthProvider.credential =\n    function(idTokenOrObject, accessToken) {\n  var idToken = idTokenOrObject;\n  if (goog.isObject(idTokenOrObject)) {\n    idToken = idTokenOrObject['idToken'];\n    accessToken = idTokenOrObject['accessToken'];\n  }\n  return new fireauth.GoogleAuthProvider().credential({\n    'idToken':  /** @type {string} */ (idToken),\n    'accessToken': /** @type {string} */ (accessToken)\n  });\n};\n\n\n/**\n * Twitter Auth provider.\n * @constructor\n * @extends {fireauth.FederatedProvider}\n * @implements {fireauth.AuthProvider}\n */\nfireauth.TwitterAuthProvider = function() {\n  fireauth.TwitterAuthProvider.base(this, 'constructor',\n      fireauth.idp.ProviderId.TWITTER,\n      fireauth.idp.RESERVED_OAUTH1_PARAMS);\n};\ngoog.inherits(fireauth.TwitterAuthProvider, fireauth.FederatedProvider);\n\nfireauth.object.setReadonlyProperty(fireauth.TwitterAuthProvider,\n    'PROVIDER_ID', fireauth.idp.ProviderId.TWITTER);\n\nfireauth.object.setReadonlyProperty(fireauth.TwitterAuthProvider,\n    'TWITTER_SIGN_IN_METHOD', fireauth.idp.SignInMethod.TWITTER);\n\n\n/**\n * Initializes a Twitter AuthCredential.\n * @param {string} tokenOrObject The Twitter access token, or object\n *     containing the token for FirebaseUI backwards compatibility.\n * @param {string} secret The Twitter secret.\n * @return {!fireauth.AuthCredential} The Auth credential object.\n */\nfireauth.TwitterAuthProvider.credential = function(tokenOrObject, secret) {\n  var tokenObject = tokenOrObject;\n  if (!goog.isObject(tokenObject)) {\n    tokenObject = {\n      'oauthToken': tokenOrObject,\n      'oauthTokenSecret': secret\n    };\n  }\n\n  if (!tokenObject['oauthToken'] || !tokenObject['oauthTokenSecret']) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.ARGUMENT_ERROR,\n        'credential failed: expected 2 arguments (the OAuth access token ' +\n        'and secret).');\n  }\n\n  return new fireauth.OAuthCredential(fireauth.idp.ProviderId.TWITTER,\n      /** @type {!fireauth.OAuthResponse} */ (tokenObject),\n      fireauth.idp.SignInMethod.TWITTER);\n};\n\n\n/**\n * The email and password credential class.\n * @param {string} email The credential email.\n * @param {string} password The credential password.\n * @param {string=} opt_signInMethod The credential sign in method can be either\n *     'password' or 'emailLink'\n * @constructor\n * @implements {fireauth.AuthCredential}\n */\nfireauth.EmailAuthCredential = function(email, password, opt_signInMethod) {\n  this.email_ = email;\n  this.password_ = password;\n  fireauth.object.setReadonlyProperty(this, 'providerId',\n      fireauth.idp.ProviderId.PASSWORD);\n  var signInMethod = opt_signInMethod ===\n      fireauth.EmailAuthProvider['EMAIL_LINK_SIGN_IN_METHOD'] ?\n      fireauth.EmailAuthProvider['EMAIL_LINK_SIGN_IN_METHOD'] :\n      fireauth.EmailAuthProvider['EMAIL_PASSWORD_SIGN_IN_METHOD'];\n  fireauth.object.setReadonlyProperty(this, 'signInMethod', signInMethod);\n};\n\n\n/**\n * Returns a promise to retrieve ID token using the underlying RPC handler API\n * for the current credential.\n * @param {!fireauth.RpcHandler} rpcHandler The RPC handler.\n * @return {!goog.Promise<!Object, !fireauth.AuthError>}\n *     idTokenPromise The RPC handler method that returns a promise which\n *     resolves with an ID token.\n * @override\n */\nfireauth.EmailAuthCredential.prototype.getIdTokenProvider =\n    function(rpcHandler) {\n  if (this['signInMethod'] ==\n      fireauth.EmailAuthProvider['EMAIL_LINK_SIGN_IN_METHOD']) {\n    return rpcHandler.emailLinkSignIn(this.email_, this.password_);\n  }\n  return rpcHandler.verifyPassword(this.email_, this.password_);\n};\n\n\n/**\n * Adds an email and password account to an existing account, identified by an\n * ID token.\n * @param {!fireauth.RpcHandler} rpcHandler The RPC handler.\n * @param {string} idToken The ID token of the existing account.\n * @return {!goog.Promise<!Object>} A Promise that resolves when the accounts\n *     are linked, returning the backend response.\n * @override\n */\nfireauth.EmailAuthCredential.prototype.linkToIdToken =\n    function(rpcHandler, idToken) {\n  if (this['signInMethod'] ==\n      fireauth.EmailAuthProvider['EMAIL_LINK_SIGN_IN_METHOD']) {\n    return rpcHandler.emailLinkSignInForLinking(\n        idToken, this.email_, this.password_);\n  }\n  return rpcHandler.updateEmailAndPassword(\n      idToken, this.email_, this.password_);\n};\n\n\n/**\n * Tries to match the credential's idToken with the provided UID.\n * @param {!fireauth.RpcHandler} rpcHandler The rpc handler.\n * @param {string} uid The UID of the user to reauthenticate.\n * @return {!goog.Promise<!Object>} A Promise that resolves when\n *     reauthentication succeeds.\n * @override\n */\nfireauth.EmailAuthCredential.prototype.matchIdTokenWithUid =\n    function(rpcHandler, uid) {\n  // Do not create a new account if the user doesn't exist.\n  return fireauth.AuthCredential.verifyTokenResponseUid(\n      // This shouldn't create a new email/password account.\n      this.getIdTokenProvider(rpcHandler),\n      uid);\n};\n\n\n/**\n * @return {!Object} The plain object representation of an Auth credential.\n * @override\n */\nfireauth.EmailAuthCredential.prototype.toPlainObject = function() {\n  return {\n    'email': this.email_,\n    'password': this.password_,\n    'signInMethod': this['signInMethod']\n  };\n};\n\n\n/**\n * @param {?Object|undefined} json The plain object representation of a\n *     EmailAuthCredential.\n * @return {?fireauth.EmailAuthCredential} The email credential if the object\n *     is a JSON representation of an EmailAuthCredential, null otherwise.\n */\nfireauth.EmailAuthCredential.fromJSON = function(json) {\n  if (json && json['email'] && json['password']) {\n    return new fireauth.EmailAuthCredential(\n        json['email'],\n        json['password'],\n        json['signInMethod']);\n  }\n  return null;\n};\n\n\n/**\n * Email password Auth provider implementation.\n * @constructor\n * @implements {fireauth.AuthProvider}\n */\nfireauth.EmailAuthProvider = function() {\n  // Set read-only instance providerId and isOAuthProvider property.\n  fireauth.object.setReadonlyProperties(this, {\n    'providerId': fireauth.idp.ProviderId.PASSWORD,\n    'isOAuthProvider': false\n  });\n};\n\n\n/**\n * Initializes an instance of an email/password Auth credential.\n * @param {string} email The credential email.\n * @param {string} password The credential password.\n * @return {!fireauth.EmailAuthCredential} The Auth credential object.\n */\nfireauth.EmailAuthProvider.credential = function(email, password) {\n  return new fireauth.EmailAuthCredential(email, password);\n};\n\n\n/**\n * @param {string} email The credential email.\n * @param {string} emailLink The credential email link.\n * @return {!fireauth.EmailAuthCredential} The Auth credential object.\n */\nfireauth.EmailAuthProvider.credentialWithLink = function(email, emailLink) {\n  var actionCodeUrl = fireauth.EmailAuthProvider\n      .getActionCodeUrlFromSignInEmailLink(emailLink);\n  if (!actionCodeUrl) {\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.ARGUMENT_ERROR, 'Invalid email link!');\n  }\n  return new fireauth.EmailAuthCredential(email, actionCodeUrl['code'],\n      fireauth.EmailAuthProvider['EMAIL_LINK_SIGN_IN_METHOD']);\n};\n\n\n/**\n * @param {string} emailLink The sign in email link to be validated.\n * @return {?fireauth.ActionCodeURL} The sign in email link action code URL.\n *     Returns null if the email link is invalid.\n */\nfireauth.EmailAuthProvider.getActionCodeUrlFromSignInEmailLink =\n    function(emailLink) {\n  emailLink = fireauth.DynamicLink.parseDeepLink(emailLink);\n  var actionCodeUrl = fireauth.ActionCodeURL.parseLink(emailLink);\n  if (actionCodeUrl && (actionCodeUrl['operation'] ===\n      fireauth.ActionCodeInfo.Operation.EMAIL_SIGNIN)) {\n    return actionCodeUrl;\n  }\n  return null;\n};\n\n\n// Set read only PROVIDER_ID property.\nfireauth.object.setReadonlyProperties(fireauth.EmailAuthProvider, {\n  'PROVIDER_ID': fireauth.idp.ProviderId.PASSWORD\n});\n\n// Set read only EMAIL_LINK_SIGN_IN_METHOD property.\nfireauth.object.setReadonlyProperties(fireauth.EmailAuthProvider, {\n  'EMAIL_LINK_SIGN_IN_METHOD': fireauth.idp.SignInMethod.EMAIL_LINK\n});\n\n// Set read only EMAIL_PASSWORD_SIGN_IN_METHOD property.\nfireauth.object.setReadonlyProperties(fireauth.EmailAuthProvider, {\n  'EMAIL_PASSWORD_SIGN_IN_METHOD': fireauth.idp.SignInMethod.EMAIL_PASSWORD\n});\n\n\n/**\n * A credential for phone number sign-in. Phone credentials can also be used as\n * second factor assertions.\n * A `PhoneAuthCredential` is also a `MultiFactorAuthCredential`. A\n * `PhoneMultiFactorAssertion` requires a `PhoneAuthCredential`.\n * @param {!fireauth.PhoneAuthCredential.Parameters_} params The credential\n *     parameters that prove the user owns the claimed phone number.\n * @constructor\n * @implements {fireauth.MultiFactorAuthCredential}\n */\nfireauth.PhoneAuthCredential = function(params) {\n  // Either verification ID and code, or phone number temporary proof must be\n  // provided.\n  if (!(params.verificationId && params.verificationCode) &&\n      !(params.temporaryProof && params.phoneNumber)) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR);\n  }\n\n  /**\n   * The phone Auth parameters that prove ownership of a phone number, either\n   * through completion of a phone verification flow, or by referencing a\n   * previously completed verification flow (\"temporaryProof\").\n   * @private {!fireauth.PhoneAuthCredential.Parameters_}\n   */\n  this.params_ = params;\n\n  fireauth.object.setReadonlyProperty(this, 'providerId',\n      fireauth.idp.ProviderId.PHONE);\n  /**\n   * @public {string} The provider ID required by the\n   *     `fireauth.MultiFactorAuthCredential` interface.\n   */\n  this.providerId = fireauth.idp.ProviderId.PHONE;\n\n  fireauth.object.setReadonlyProperty(\n      this, 'signInMethod', fireauth.idp.SignInMethod.PHONE);\n};\n\n\n/**\n * Parameters that prove ownership of a phone number via a ID \"verificationId\"\n * of a request to send a code to the phone number, with the code\n * \"verificationCode\" that the user received on their phone.\n * @private\n * @typedef {{\n *   verificationId: string,\n *   verificationCode: string\n * }}\n */\nfireauth.PhoneAuthCredential.VerificationParameters_;\n\n\n/**\n * Parameters that prove ownership of a phone number by referencing a previously\n * completed phone Auth flow.\n * @private\n * @typedef {{\n *   temporaryProof: string,\n *   phoneNumber: string\n * }}\n */\nfireauth.PhoneAuthCredential.TemporaryProofParameters_;\n\n\n/**\n * @private\n * @typedef {\n *   !fireauth.PhoneAuthCredential.VerificationParameters_|\n *   !fireauth.PhoneAuthCredential.TemporaryProofParameters_\n * }\n */\nfireauth.PhoneAuthCredential.Parameters_;\n\n\n/**\n * Retrieves an ID token from the backend given the current credential.\n * @param {!fireauth.RpcHandler} rpcHandler The RPC handler.\n * @return {!goog.Promise<!Object>} A Promise that resolves with the\n *     backend response.\n * @override\n */\nfireauth.PhoneAuthCredential.prototype.getIdTokenProvider =\n    function(rpcHandler) {\n  return rpcHandler.verifyPhoneNumber(this.makeVerifyPhoneNumberRequest_());\n};\n\n\n/**\n * Adds a phone credential to an existing account identified by an ID token.\n * @param {!fireauth.RpcHandler} rpcHandler The RPC handler.\n * @param {string} idToken The ID token of the existing account.\n * @return {!goog.Promise<!Object>} A Promise that resolves when the accounts\n *     are linked, returning the backend response.\n * @override\n */\nfireauth.PhoneAuthCredential.prototype.linkToIdToken =\n    function(rpcHandler, idToken) {\n  var request = this.makeVerifyPhoneNumberRequest_();\n  request['idToken'] = idToken;\n  return rpcHandler.verifyPhoneNumberForLinking(request);\n};\n\n\n/**\n * Tries to match the credential's idToken with the provided UID.\n * @param {!fireauth.RpcHandler} rpcHandler The RPC handler.\n * @param {string} uid The UID of the user to reauthenticate.\n * @return {!goog.Promise<!Object>} A Promise that resolves when\n *     reauthentication succeeds.\n * @override\n */\nfireauth.PhoneAuthCredential.prototype.matchIdTokenWithUid =\n    function(rpcHandler, uid) {\n  var request = this.makeVerifyPhoneNumberRequest_();\n  return fireauth.AuthCredential.verifyTokenResponseUid(\n      rpcHandler.verifyPhoneNumberForExisting(request),\n      uid);\n};\n\n\n/**\n * Converts a PhoneAuthCredential to a plain object.\n * @return {!Object}\n * @override\n */\nfireauth.PhoneAuthCredential.prototype.toPlainObject = function() {\n  var obj =  {\n    'providerId': fireauth.idp.ProviderId.PHONE\n  };\n  if (this.params_.verificationId) {\n    obj['verificationId'] = this.params_.verificationId;\n  }\n  if (this.params_.verificationCode) {\n    obj['verificationCode'] = this.params_.verificationCode;\n  }\n  if (this.params_.temporaryProof) {\n    obj['temporaryProof'] = this.params_.temporaryProof;\n  }\n  if (this.params_.phoneNumber) {\n    obj['phoneNumber'] = this.params_.phoneNumber;\n  }\n  return obj;\n};\n\n\n/**\n * @param {?Object|undefined} json The plain object representation of a\n *     PhoneAuthCredential.\n * @return {?fireauth.PhoneAuthCredential} The phone credential if the object\n *     is a JSON representation of an PhoneAuthCredential, null otherwise.\n */\nfireauth.PhoneAuthCredential.fromJSON = function(json) {\n  if (json &&\n      json['providerId'] === fireauth.idp.ProviderId.PHONE &&\n      ((json['verificationId'] && json['verificationCode']) ||\n       (json['temporaryProof'] && json['phoneNumber']))) {\n    var params = {};\n    var allowedKeys = [\n      'verificationId', 'verificationCode', 'temporaryProof', 'phoneNumber'\n    ];\n    goog.array.forEach(allowedKeys, function(key) {\n      if (json[key]) {\n        params[key] = json[key];\n      }\n    });\n    return new fireauth.PhoneAuthCredential(\n        /** @type {!fireauth.PhoneAuthCredential.Parameters_} */ (params));\n  }\n  return null;\n};\n\n\n/**\n * @return {!Object} A request to the verifyPhoneNumber endpoint based on the\n *     current state of the object.\n * @private\n */\nfireauth.PhoneAuthCredential.prototype.makeVerifyPhoneNumberRequest_ =\n    function() {\n  if (this.params_.temporaryProof && this.params_.phoneNumber) {\n    return {\n      'temporaryProof': this.params_.temporaryProof,\n      'phoneNumber': this.params_.phoneNumber\n    };\n  }\n\n  return {\n    'sessionInfo': this.params_.verificationId,\n    'code': this.params_.verificationCode\n  };\n};\n\n\n/**\n * Finalizes the 2nd factor enrollment flow with the current AuthCredential\n * using the enrollment request identifier.\n * @param {!fireauth.RpcHandler} rpcHandler The RPC handler instance.\n * @param {!fireauth.MultiFactorEnrollmentRequestIdentifier} enrollmentRequest\n *     The enrollment request identifying the user.\n * @return {!goog.Promise<{idToken: string, refreshToken: string}>} A promise\n *     that resolves with the updated ID and refresh tokens.\n * @override\n */\nfireauth.PhoneAuthCredential.prototype.finalizeMfaEnrollment =\n    function(rpcHandler, enrollmentRequest) {\n  goog.object.extend(\n      enrollmentRequest,\n      {\n        'phoneVerificationInfo': this.makeVerifyPhoneNumberRequest_()\n      });\n  return /** @type {!goog.Promise<{idToken: string, refreshToken: string}>} */ (\n      rpcHandler.finalizePhoneMfaEnrollment(enrollmentRequest));\n};\n\n\n/**\n * Finalizes the 2nd factor sign-in flow with the current AuthCredential\n * using the sign-in request identifier.\n * @param {!fireauth.RpcHandler} rpcHandler The RPC handler instance.\n * @param {!fireauth.MultiFactorSignInRequestIdentifier} signInRequest\n *     The sign-in request identifying the user.\n * @return {!goog.Promise<{idToken: string, refreshToken: string}>} A promise\n *     that resolves with the signed in user's ID and refresh tokens.\n * @override\n */\nfireauth.PhoneAuthCredential.prototype.finalizeMfaSignIn =\n    function(rpcHandler, signInRequest) {\n  goog.object.extend(\n      signInRequest,\n      {\n        'phoneVerificationInfo': this.makeVerifyPhoneNumberRequest_()\n      });\n  return /** @type {!goog.Promise<{idToken: string, refreshToken: string}>} */ (\n      rpcHandler.finalizePhoneMfaSignIn(signInRequest));\n};\n\n\n/**\n * Phone Auth provider implementation.\n * @param {?fireauth.Auth=} opt_auth The Firebase Auth instance.\n * @constructor\n * @implements {fireauth.AuthProvider}\n */\nfireauth.PhoneAuthProvider = function(opt_auth) {\n  try {\n    /** @private {!fireauth.Auth} */\n    this.auth_ = opt_auth || firebase['auth']();\n  } catch (e) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.ARGUMENT_ERROR,\n        'Either an instance of firebase.auth.Auth must be passed as an ' +\n        'argument to the firebase.auth.PhoneAuthProvider constructor, or the ' +\n        'default firebase App instance must be initialized via ' +\n        'firebase.initializeApp().');\n  }\n  fireauth.object.setReadonlyProperties(this, {\n    'providerId': fireauth.idp.ProviderId.PHONE,\n    'isOAuthProvider': false\n  });\n};\n\n\n/**\n * The phone info options for single-factor sign-in. Only phone number is\n * required.\n * @private\n * @typedef {{\n *   phoneNumber: string\n * }}\n */\nfireauth.PhoneAuthProvider.PhoneSingleFactorInfoOptions_;\n\n/**\n * The phone info options for multi-factor enrollment. Phone number and\n * multi-factor session are required.\n * @private\n * @typedef {{\n *   phoneNumber: string,\n *   session: !fireauth.MultiFactorSession\n * }}\n */\nfireauth.PhoneAuthProvider.PhoneMultiFactorEnrollInfoOptions_;\n\n\n/**\n * The phone info options for multi-factor sign-in. Either multi-factor hint or\n * multi-factor UID and multi-factor session are required.\n * @private\n * @typedef {{\n *   multiFactorHint: !fireauth.MultiFactorInfo,\n *   session: !fireauth.MultiFactorSession\n * }|{\n *   multiFactorUid: string,\n *   session: !fireauth.MultiFactorSession\n * }}\n */\nfireauth.PhoneAuthProvider.PhoneMultiFactorSignInInfoOptions_;\n\n\n/**\n * The options for verifying the ownership of the phone number. It could be\n * used for single-factor sign-in, multi-factor enrollment or multi-factor\n * sign-in.\n * @typedef {\n *   !fireauth.PhoneAuthProvider.PhoneSingleFactorInfoOptions_|\n *   !fireauth.PhoneAuthProvider.PhoneMultiFactorEnrollInfoOptions_|\n *   !fireauth.PhoneAuthProvider.PhoneMultiFactorSignInInfoOptions_\n * }\n */\nfireauth.PhoneAuthProvider.PhoneInfoOptions;\n\n\n/**\n * Initiates a phone number confirmation flow. If session is provided, it is\n * used to verify ownership of the second factor phone number.\n *\n * @param {string|!fireauth.PhoneAuthProvider.PhoneInfoOptions} phoneInfoOptions\n *     The user's phone options for verifying the ownship of the phone number.\n * @param {!firebase.auth.ApplicationVerifier} applicationVerifier The\n *     application verifier for anti-abuse purposes.\n * @return {!goog.Promise<string>} A Promise that resolves with the\n *     verificationId of the phone number confirmation flow.\n */\nfireauth.PhoneAuthProvider.prototype.verifyPhoneNumber =\n    function(phoneInfoOptions, applicationVerifier) {\n  var rpcHandler = this.auth_.getRpcHandler();\n\n  // Convert the promise into a goog.Promise. If the applicationVerifier throws\n  // an error, just propagate it to the client. Reset the reCAPTCHA widget every\n  // time after sending the token to the server.\n  return goog.Promise.resolve(applicationVerifier['verify']())\n      .then(function(assertion) {\n        if (typeof assertion !== 'string') {\n          throw new fireauth.AuthError(fireauth.authenum.Error.ARGUMENT_ERROR,\n              'An implementation of firebase.auth.ApplicationVerifier' +\n              '.prototype.verify() must return a firebase.Promise ' +\n              'that resolves with a string.');\n        }\n\n        switch (applicationVerifier['type']) {\n          case 'recaptcha':\n            var session = goog.isObject(phoneInfoOptions) ?\n                phoneInfoOptions['session'] : null;\n            // PhoneInfoOptions can be a phone number string for backward\n            // compatibility.\n            var phoneNumber = goog.isObject(phoneInfoOptions) ?\n                phoneInfoOptions['phoneNumber'] : phoneInfoOptions;\n            var verifyPromise;\n            if (session &&\n                session.type == fireauth.MultiFactorSession.Type.ENROLL) {\n              verifyPromise = session.getRawSession()\n                  .then(function(rawSession) {\n                    return rpcHandler.startPhoneMfaEnrollment({\n                      'idToken': rawSession,\n                      'phoneEnrollmentInfo': {\n                        'phoneNumber': phoneNumber,\n                        'recaptchaToken': assertion\n                      }\n                    });\n                  });\n            } else if (session &&\n                       session.type ==\n                           fireauth.MultiFactorSession.Type.SIGN_IN) {\n              verifyPromise = session.getRawSession()\n                  .then(function(rawSession) {\n                    var mfaEnrollmentId =\n                        (phoneInfoOptions['multiFactorHint'] &&\n                         phoneInfoOptions['multiFactorHint']['uid']) ||\n                        phoneInfoOptions['multiFactorUid'];\n                    return rpcHandler.startPhoneMfaSignIn({\n                      'mfaPendingCredential': rawSession,\n                      'mfaEnrollmentId': mfaEnrollmentId,\n                      'phoneSignInInfo': {\n                        'recaptchaToken': assertion\n                      }\n                    });\n                  });\n            } else {\n              verifyPromise = rpcHandler.sendVerificationCode({\n                'phoneNumber': phoneNumber,\n                'recaptchaToken': assertion\n              });\n            }\n            // Reset the applicationVerifier after code is sent.\n            return verifyPromise.then(function(verificationId) {\n              if (typeof applicationVerifier.reset === 'function') {\n                applicationVerifier.reset();\n              }\n              return verificationId;\n            }, function(error) {\n              if (typeof applicationVerifier.reset === 'function') {\n                applicationVerifier.reset();\n              }\n              throw error;\n            });\n          default:\n            throw new fireauth.AuthError(fireauth.authenum.Error.ARGUMENT_ERROR,\n                'Only firebase.auth.ApplicationVerifiers with ' +\n                'type=\"recaptcha\" are currently supported.');\n        }\n      });\n};\n\n\n/**\n * Creates a PhoneAuthCredential.\n * @param {string} verificationId The ID of the phone number flow, to correlate\n *     this request with a previous call to\n *     PhoneAuthProvider.prototype.verifyPhoneNumber.\n * @param {string} verificationCode The verification code that was sent to the\n *     user's phone.\n * @return {!fireauth.PhoneAuthCredential}\n */\nfireauth.PhoneAuthProvider.credential =\n    function(verificationId, verificationCode) {\n  if (!verificationId) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.MISSING_SESSION_INFO);\n  }\n  if (!verificationCode) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.MISSING_CODE);\n  }\n  return new fireauth.PhoneAuthCredential({\n    verificationId: verificationId,\n    verificationCode: verificationCode\n  });\n};\n\n\n// Set read only PROVIDER_ID property.\nfireauth.object.setReadonlyProperties(fireauth.PhoneAuthProvider, {\n  'PROVIDER_ID': fireauth.idp.ProviderId.PHONE\n});\n\n\n// Set read only PHONE_SIGN_IN_METHOD property.\nfireauth.object.setReadonlyProperties(fireauth.PhoneAuthProvider, {\n  'PHONE_SIGN_IN_METHOD': fireauth.idp.SignInMethod.PHONE\n});\n\n\n/**\n * Constructs an Auth credential from a backend response.\n * Note, unlike fromJSON which constructs the AuthCredential from a toJSON()\n * response, this helper constructs the credential from the server response.\n * @param {?Object} response The backend response to build a credential from.\n * @return {?fireauth.AuthCredential} The corresponding AuthCredential.\n */\nfireauth.AuthProvider.getCredentialFromResponse = function(response) {\n  // Handle phone Auth credential responses, as they have a different format\n  // from other backend responses (i.e. no providerId).\n  if (response['temporaryProof'] && response['phoneNumber']) {\n    return new fireauth.PhoneAuthCredential({\n      temporaryProof: response['temporaryProof'],\n      phoneNumber: response['phoneNumber']\n    });\n  }\n\n  // Get all OAuth response parameters from response.\n  var providerId = response && response['providerId'];\n\n  // Email and password is not supported as there is no situation where the\n  // server would return the password to the client.\n  if (!providerId || providerId === fireauth.idp.ProviderId.PASSWORD) {\n    return null;\n  }\n\n  var accessToken = response && response['oauthAccessToken'];\n  var accessTokenSecret = response && response['oauthTokenSecret'];\n  // Note this is not actually returned by the backend. It is introduced in\n  // rpcHandler.\n  var rawNonce = response && response['nonce'];\n  // Google Id Token returned when no additional scopes provided.\n  var idToken = response && response['oauthIdToken'];\n  // Pending token for SAML and OAuth/OIDC providers.\n  var pendingToken = response && response['pendingToken'];\n  try {\n    switch (providerId) {\n      case fireauth.idp.ProviderId.GOOGLE:\n        return fireauth.GoogleAuthProvider.credential(\n            idToken, accessToken);\n\n      case fireauth.idp.ProviderId.FACEBOOK:\n        return fireauth.FacebookAuthProvider.credential(\n            accessToken);\n\n      case fireauth.idp.ProviderId.GITHUB:\n        return fireauth.GithubAuthProvider.credential(\n            accessToken);\n\n      case fireauth.idp.ProviderId.TWITTER:\n        return fireauth.TwitterAuthProvider.credential(\n            accessToken, accessTokenSecret);\n\n      default:\n        if (!accessToken && !accessTokenSecret && !idToken && !pendingToken) {\n          return null;\n        }\n        if (pendingToken) {\n          if (providerId.indexOf(fireauth.constants.SAML_PREFIX) == 0) {\n            return new fireauth.SAMLAuthCredential(providerId, pendingToken);\n          } else {\n            // OIDC and non-default providers excluding Twitter.\n            return new fireauth.OAuthCredential(\n                providerId,\n                {\n                  'pendingToken': pendingToken,\n                  'idToken': response['oauthIdToken'],\n                  'accessToken': response['oauthAccessToken']\n                },\n                providerId);\n          }\n        }\n        return new fireauth.OAuthProvider(providerId).credential({\n          'idToken': idToken,\n          'accessToken': accessToken,\n          'rawNonce': rawNonce\n        });\n    }\n  } catch (e) {\n    return null;\n  }\n};\n\n\n/**\n * Constructs an Auth credential from a JSON representation.\n * Note, unlike getCredentialFromResponse which constructs the AuthCredential\n * from a server response, this helper constructs credential from the toJSON()\n * result.\n * @param {!Object|string} json The JSON representation to construct credential\n *     from.\n * @return {?fireauth.AuthCredential} The corresponding AuthCredential.\n */\nfireauth.AuthProvider.getCredentialFromJSON = function(json) {\n  var obj = typeof json === 'string' ? JSON.parse(json) : json;\n  var credential;\n  var fromJSON = [\n    fireauth.OAuthCredential.fromJSON,\n    fireauth.EmailAuthCredential.fromJSON,\n    fireauth.PhoneAuthCredential.fromJSON,\n    fireauth.SAMLAuthCredential.fromJSON\n  ];\n  for (var i = 0; i < fromJSON.length; i++) {\n    credential = fromJSON[i](obj);\n    if (credential) {\n      return credential;\n    }\n  }\n  return null;\n};\n\n\n/**\n * Constructs an Auth credential from a JSON representation.\n * @param {!Object|string} json The JSON representation to construct credential from.\n * @return {?fireauth.AuthCredential} The corresponding AuthCredential.\n */\nfireauth.AuthCredential.fromPlainObject =\n    fireauth.AuthProvider.getCredentialFromJSON;\n\n\n/**\n * Checks if OAuth is supported by provider, if not throws an error.\n * @param {!fireauth.AuthProvider} provider The provider to check.\n */\nfireauth.AuthProvider.checkIfOAuthSupported =\n    function(provider) {\n  if (!provider['isOAuthProvider']) {\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.INVALID_OAUTH_PROVIDER);\n  }\n};\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines the Auth event object.\n */\n\ngoog.provide('fireauth.AuthEvent');\n\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.authenum.Error');\n\n\n/**\n * Defines the authentication event.\n * @param {!fireauth.AuthEvent.Type} type The Auth event type.\n * @param {?string=} opt_eventId The event identifier.\n * @param {?string=} opt_urlResponse The URL with IdP response.\n * @param {?string=} opt_sessionId The session ID used to prevent session\n *     fixation attacks.\n * @param {?fireauth.AuthError=} opt_error The optional error encountered.\n * @param {?string=} opt_postBody The optional POST body.\n * @param {?string=} opt_tenantId The optional tenant ID.\n * @constructor\n */\nfireauth.AuthEvent = function(\n    type, opt_eventId, opt_urlResponse, opt_sessionId, opt_error,\n    opt_postBody, opt_tenantId) {\n  /** @const @private {!fireauth.AuthEvent.Type} The Auth event type. */\n  this.type_ = type;\n  /** @const @private {?string} The Auth event ID. */\n  this.eventId_ = opt_eventId || null;\n  /** @const @private {?string} The callback URL with the sign in response. */\n  this.urlResponse_ = opt_urlResponse || null;\n  /** @const @private {?string} The sign in operation session ID. */\n  this.sessionId_ = opt_sessionId || null;\n  /** @const @private {?string} The POST body string if available. */\n  this.postBody_ = opt_postBody || null;\n  /** @const @private {?string} The tenant ID if available. */\n  this.tenantId_ = opt_tenantId || null;\n  /**\n   * @const @private {?fireauth.AuthError} The Auth event error if available.\n   */\n  this.error_ = opt_error || null;\n  if (!this.urlResponse_ && !this.error_) {\n    // Either URL or error is required. They can't be both null.\n    throw new fireauth.AuthError(fireauth.authenum.Error.INVALID_AUTH_EVENT);\n  } else if (this.urlResponse_ && this.error_) {\n    // An error must not be provided when a URL is available.\n    throw new fireauth.AuthError(fireauth.authenum.Error.INVALID_AUTH_EVENT);\n  } else if (this.urlResponse_ && !this.sessionId_) {\n    // A session ID must accompany a URL response.\n    throw new fireauth.AuthError(fireauth.authenum.Error.INVALID_AUTH_EVENT);\n  }\n};\n\n\n\n/**\n * Auth event operation types.\n * All Auth event types that are used for popup operations should be suffixed\n * with `Popup`, whereas those used for redirect operations should be suffixed\n * with `Redirect`.\n * TODO: consider changing the type from a string to an object with ID\n * and some metadata for determining mode: redirect, popup or none.\n * @enum {string}\n */\nfireauth.AuthEvent.Type = {\n  LINK_VIA_POPUP: 'linkViaPopup',\n  LINK_VIA_REDIRECT: 'linkViaRedirect',\n  REAUTH_VIA_POPUP: 'reauthViaPopup',\n  REAUTH_VIA_REDIRECT: 'reauthViaRedirect',\n  SIGN_IN_VIA_POPUP: 'signInViaPopup',\n  SIGN_IN_VIA_REDIRECT: 'signInViaRedirect',\n  UNKNOWN: 'unknown',\n  VERIFY_APP: 'verifyApp'\n};\n\n\n/**\n * @param {!fireauth.AuthEvent} event The Auth event.\n * @return {boolean} Whether the event is a redirect type.\n */\nfireauth.AuthEvent.isRedirect = function(event) {\n  return !!event.getType().match(/Redirect$/);\n};\n\n\n/**\n * @param {!fireauth.AuthEvent} event The Auth event.\n * @return {boolean} Whether the event is a popup type.\n */\nfireauth.AuthEvent.isPopup = function(event) {\n  return !!event.getType().match(/Popup$/);\n};\n\n\n/** @return {!fireauth.AuthEvent.Type} The type of Auth event. */\nfireauth.AuthEvent.prototype.getType = function() {\n  return this.type_;\n};\n\n\n/** @return {?string} The Auth event identifier. */\nfireauth.AuthEvent.prototype.getEventId = function() {\n  return this.eventId_;\n};\n\n\n/** @return {string} The event unique identifier. */\nfireauth.AuthEvent.prototype.getUid = function() {\n  var components = [];\n  components.push(this.type_);\n  if (this.eventId_) {\n    components.push(this.eventId_);\n  }\n  if (this.sessionId_) {\n    components.push(this.sessionId_);\n  }\n  if (this.tenantId_) {\n    components.push(this.tenantId_);\n  }\n  return components.join('-');\n};\n\n\n/** @return {?string} The url response of Auth event. */\nfireauth.AuthEvent.prototype.getUrlResponse = function() {\n  return this.urlResponse_;\n};\n\n\n/** @return {?string} The session ID Auth event. */\nfireauth.AuthEvent.prototype.getSessionId = function() {\n  return this.sessionId_;\n};\n\n\n/** @return {?string} The POST body of the Auth event, if available. */\nfireauth.AuthEvent.prototype.getPostBody = function() {\n  return this.postBody_;\n};\n\n\n/** @return {?string} The tenant ID of the Auth event, if available. */\nfireauth.AuthEvent.prototype.getTenantId = function() {\n  return this.tenantId_;\n};\n\n\n/** @return {?fireauth.AuthError} The error of Auth event. */\nfireauth.AuthEvent.prototype.getError = function() {\n  return this.error_;\n};\n\n\n/** @return {boolean} Whether Auth event has an error. */\nfireauth.AuthEvent.prototype.hasError = function() {\n  return !!this.error_;\n};\n\n\n/** @return {!Object} The plain object representation of event. */\nfireauth.AuthEvent.prototype.toPlainObject = function() {\n  return {\n    'type': this.type_,\n    'eventId': this.eventId_,\n    'urlResponse': this.urlResponse_,\n    'sessionId': this.sessionId_,\n    'postBody': this.postBody_,\n    'tenantId': this.tenantId_,\n    'error': this.error_ && this.error_.toPlainObject()\n  };\n};\n\n\n/**\n * @param {?Object} rawResponse The plain object representation of Auth event.\n * @return {?fireauth.AuthEvent} The Auth event representation of plain object.\n */\nfireauth.AuthEvent.fromPlainObject = function(rawResponse) {\n  var response = rawResponse || {};\n  if (response['type']) {\n    return new fireauth.AuthEvent(\n        response['type'],\n        response['eventId'],\n        response['urlResponse'],\n        response['sessionId'],\n        response['error'] &&\n            fireauth.AuthError.fromPlainObject(response['error']),\n        response['postBody'],\n        response['tenantId']\n        );\n  }\n  return null;\n};\n","/**\n * @license\n * Copyright 2018 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Provides the universal link subscriber utility to allow\n * multiple subscriptions for incoming universal link detection.\n */\ngoog.provide('fireauth.UniversalLinkSubscriber');\n\ngoog.require('fireauth.util');\ngoog.require('goog.array');\n\n/**\n * Defines the universal link subscriber class used to allow multiple universal\n * link subscriptions since the underlying plugin only works with one.\n * This utility is needed since the universal link cordova plugin can only allow\n * one subscriber and multiple app instances can subscribe to this.\n * @constructor @final @struct\n */\nfireauth.UniversalLinkSubscriber = function() {\n  /**\n   * @private {?function(?Object)} The master callback that subscribes directly\n   *     to universalLinks.\n   */\n  this.masterCb_ = null;\n  /**\n   * @private {!Array<function(?Object)>} The list of external subscribers that\n   *     are triggered every time the master callback is triggered.\n   */\n  this.cb_ = [];\n};\n\n\n/**\n * @return {!fireauth.UniversalLinkSubscriber} The default universal link\n *     subscriber instance.\n */\nfireauth.UniversalLinkSubscriber.getInstance = function() {\n  if (!fireauth.UniversalLinkSubscriber.instance_) {\n    fireauth.UniversalLinkSubscriber.instance_ =\n        new fireauth.UniversalLinkSubscriber();\n  }\n  return fireauth.UniversalLinkSubscriber.instance_;\n};\n\n\n/** Clears singleton instance. Useful for testing. */\nfireauth.UniversalLinkSubscriber.clear = function() {\n  fireauth.UniversalLinkSubscriber.instance_ = null;\n};\n\n\n/**\n * @private {?fireauth.UniversalLinkSubscriber} The singleton universal\n *     link subscriber instance.\n */\nfireauth.UniversalLinkSubscriber.instance_ = null;\n\n\n/**\n * Subscribes a callback to the universal link plugin listener.\n * @param {function(?Object)} cb The callback to subscribe to the universal\n *     link plugin.\n */\nfireauth.UniversalLinkSubscriber.prototype.subscribe  = function(cb) {\n  var self = this;\n  this.cb_.push(cb);\n  if (!this.masterCb_) {\n    this.masterCb_ = function(event) {\n      for (var i = 0; i < self.cb_.length; i++) {\n        self.cb_[i](event);\n      }\n    };\n    var subscribe = fireauth.util.getObjectRef(\n        'universalLinks.subscribe', goog.global);\n    // For iOS environments, this plugin is not used, therefore this is a no-op\n    // and no error needs to be thrown.\n    if (typeof subscribe === 'function') {\n      subscribe(null, this.masterCb_);\n    }\n  }\n};\n\n\n/**\n * Unsubscribes a callback from the universal link plugin listener.\n * @param {function(?Object)} cb The callback to unsubscribe from the universal\n *     link plugin.\n */\nfireauth.UniversalLinkSubscriber.prototype.unsubscribe = function(cb) {\n  goog.array.removeAllIf(this.cb_, function(ele) {\n    return ele == cb;\n  });\n};\n\n","/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Utility for handling RPC requests to server.\n */\ngoog.provide('fireauth.RpcHandler');\ngoog.provide('fireauth.RpcHandler.ApiMethodHandler');\ngoog.provide('fireauth.RpcHandler.VerifyAssertionData');\ngoog.provide('fireauth.XmlHttpFactory');\n\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.AuthErrorWithCredential');\ngoog.require('fireauth.authenum.Error');\ngoog.require('fireauth.constants');\ngoog.require('fireauth.idp');\ngoog.require('fireauth.idp.ProviderId');\ngoog.require('fireauth.object');\ngoog.require('fireauth.util');\ngoog.require('goog.Promise');\ngoog.require('goog.Uri');\ngoog.require('goog.html.TrustedResourceUrl');\ngoog.require('goog.json');\ngoog.require('goog.net.CorsXmlHttpFactory');\ngoog.require('goog.net.EventType');\ngoog.require('goog.net.FetchXmlHttpFactory');\ngoog.require('goog.net.XhrIo');\ngoog.require('goog.net.XmlHttpFactory');\ngoog.require('goog.net.jsloader');\ngoog.require('goog.object');\ngoog.require('goog.string.Const');\n\n\n\n/**\n * Firebase Auth XmlHttpRequest factory. This is useful for environments like\n * Node.js where XMLHttpRequest does not exist. XmlHttpFactory would be\n * initialized using the polyfill XMLHttpRequest module.\n * @param {function(new:XMLHttpRequest)} xmlHttpRequest The xmlHttpRequest\n *     constructor.\n * @constructor\n * @extends {goog.net.XmlHttpFactory}\n * @final\n */\nfireauth.XmlHttpFactory = function(xmlHttpRequest) {\n  /**\n   * @private {function(new:XMLHttpRequest)} The underlying XHR reference.\n   */\n  this.xmlHttpRequest_ = xmlHttpRequest;\n  fireauth.XmlHttpFactory.base(this, 'constructor');\n};\ngoog.inherits(fireauth.XmlHttpFactory, goog.net.XmlHttpFactory);\n\n\n/**\n * @return {!goog.net.XhrLike|!XMLHttpRequest} A new XhrLike instance.\n * @override\n */\nfireauth.XmlHttpFactory.prototype.createInstance = function() {\n  return new this.xmlHttpRequest_();\n};\n\n\n/**\n * @return {!Object} Options describing how XHR objects obtained from this\n *     factory should be used.\n * @override\n */\nfireauth.XmlHttpFactory.prototype.internalGetOptions = function() {\n  return {};\n};\n\n\n\n/**\n * Creates an RPC request handler for the project specified by the API key.\n *\n * @param {string} apiKey The API key.\n * @param {?Object=} opt_config The RPC request processor configuration.\n * @param {?string=} opt_firebaseClientVersion The optional Firebase client\n *     version to log with requests to Firebase Auth server.\n * @constructor\n */\nfireauth.RpcHandler = function(apiKey, opt_config, opt_firebaseClientVersion) {\n  /** @private {string} The project API key. */\n  this.apiKey_ = apiKey;\n  var config = opt_config || {};\n  this.secureTokenEndpoint_ = config['secureTokenEndpoint'] ||\n      fireauth.RpcHandler.SECURE_TOKEN_ENDPOINT_;\n  /**\n   * @private @const {!fireauth.util.Delay} The delay for secure token endpoint\n   *     network timeout.\n   */\n  this.secureTokenTimeout_ = config['secureTokenTimeout'] ||\n      fireauth.RpcHandler.DEFAULT_SECURE_TOKEN_TIMEOUT_;\n  /** @private @const {!Object} The secure token server headers. */\n  this.secureTokenHeaders_ = goog.object.clone(\n      config['secureTokenHeaders'] ||\n      fireauth.RpcHandler.DEFAULT_SECURE_TOKEN_HEADERS_);\n  /** @private {string} The Firebase Auth endpoint. */\n  this.firebaseEndpoint_ = config['firebaseEndpoint'] ||\n      fireauth.RpcHandler.FIREBASE_ENDPOINT_;\n  /** @private {string} The identity platform endpoint. */\n  this.identityPlatformEndpoint_ = config['identityPlatformEndpoint'] ||\n      fireauth.RpcHandler.IDENTITY_PLATFORM_ENDPOINT_;\n  /**\n   * @private @const {!fireauth.util.Delay} The delay for Firebase Auth endpoint\n   *     network timeout.\n   */\n  this.firebaseTimeout_ = config['firebaseTimeout'] ||\n      fireauth.RpcHandler.DEFAULT_FIREBASE_TIMEOUT_;\n  this.firebaseHeaders_ = goog.object.clone(\n      config['firebaseHeaders'] ||\n      fireauth.RpcHandler.DEFAULT_FIREBASE_HEADERS_);\n  // If Firebase client version needs to be logged too.\n  if (opt_firebaseClientVersion) {\n    // Log client version for Firebase Auth server.\n    this.firebaseHeaders_['X-Client-Version'] = opt_firebaseClientVersion;\n    // Log client version for securetoken server.\n    this.secureTokenHeaders_['X-Client-Version'] = opt_firebaseClientVersion;\n  }\n\n  // Get XMLHttpRequest reference.\n  var XMLHttpRequest = fireauth.RpcHandler.getXMLHttpRequest();\n  if (!XMLHttpRequest && !fireauth.util.isWorker()) {\n    // In a Node.js environment, xmlhttprequest module needs to be required.\n    throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR,\n        'The XMLHttpRequest compatibility library was not found.');\n  }\n  /** @private {!goog.net.XmlHttpFactory|undefined} The XHR factory. */\n  this.rpcHandlerXhrFactory_ = undefined;\n  // Initialize XHR factory. CORS does not apply in native environments or\n  // workers so don't use CorsXmlHttpFactory in those cases.\n  if (fireauth.util.isWorker()) {\n    // For worker environment use FetchXmlHttpFactory.\n    this.rpcHandlerXhrFactory_ = new goog.net.FetchXmlHttpFactory(\n        /** @type {!WorkerGlobalScope} */ (self));\n  } else if (fireauth.util.isNativeEnvironment()) {\n    // For Node.js, this is the polyfill library. For other environments,\n    // this is the native global XMLHttpRequest.\n    this.rpcHandlerXhrFactory_ = new fireauth.XmlHttpFactory(\n        /** @type {function(new:XMLHttpRequest)} */ (XMLHttpRequest));\n  } else {\n    // CORS Browser environment.\n    this.rpcHandlerXhrFactory_ = new goog.net.CorsXmlHttpFactory();\n  }\n  /** @private {?string} The tenant ID. */\n  this.tenantId_ = null;\n};\n\n\n/**\n * @return {?function(new:XMLHttpRequest)|undefined} The current environment\n *     XMLHttpRequest. This is undefined for worker environment.\n */\nfireauth.RpcHandler.getXMLHttpRequest = function() {\n  // In Node.js XMLHttpRequest is polyfilled.\n  var isNode = fireauth.util.getEnvironment() == fireauth.util.Env.NODE;\n  var XMLHttpRequest = goog.global['XMLHttpRequest'] ||\n      (isNode &&\n       firebase.INTERNAL['node'] &&\n       firebase.INTERNAL['node']['XMLHttpRequest']);\n  return XMLHttpRequest;\n};\n\n\n/**\n * Enums for HTTP request methods.\n * @enum {string}\n */\nfireauth.RpcHandler.HttpMethod = {\n  POST: 'POST',\n  GET: 'GET'\n};\n\n\n/**\n * Firebase Auth server error codes.\n * @enum {string}\n */\nfireauth.RpcHandler.ServerError = {\n  ADMIN_ONLY_OPERATION: 'ADMIN_ONLY_OPERATION',\n  CAPTCHA_CHECK_FAILED: 'CAPTCHA_CHECK_FAILED',\n  CORS_UNSUPPORTED: 'CORS_UNSUPPORTED',\n  CREDENTIAL_MISMATCH: 'CREDENTIAL_MISMATCH',\n  CREDENTIAL_TOO_OLD_LOGIN_AGAIN: 'CREDENTIAL_TOO_OLD_LOGIN_AGAIN',\n  DYNAMIC_LINK_NOT_ACTIVATED: 'DYNAMIC_LINK_NOT_ACTIVATED',\n  EMAIL_CHANGE_NEEDS_VERIFICATION: 'EMAIL_CHANGE_NEEDS_VERIFICATION',\n  EMAIL_EXISTS: 'EMAIL_EXISTS',\n  EMAIL_NOT_FOUND: 'EMAIL_NOT_FOUND',\n  EXPIRED_OOB_CODE: 'EXPIRED_OOB_CODE',\n  FEDERATED_USER_ID_ALREADY_LINKED: 'FEDERATED_USER_ID_ALREADY_LINKED',\n  INVALID_APP_CREDENTIAL: 'INVALID_APP_CREDENTIAL',\n  INVALID_APP_ID: 'INVALID_APP_ID',\n  INVALID_CERT_HASH: 'INVALID_CERT_HASH',\n  INVALID_CODE: 'INVALID_CODE',\n  INVALID_CONTINUE_URI: 'INVALID_CONTINUE_URI',\n  INVALID_CUSTOM_TOKEN: 'INVALID_CUSTOM_TOKEN',\n  INVALID_DYNAMIC_LINK_DOMAIN: 'INVALID_DYNAMIC_LINK_DOMAIN',\n  INVALID_EMAIL: 'INVALID_EMAIL',\n  INVALID_ID_TOKEN: 'INVALID_ID_TOKEN',\n  INVALID_IDP_RESPONSE: 'INVALID_IDP_RESPONSE',\n  INVALID_IDENTIFIER: 'INVALID_IDENTIFIER',\n  INVALID_MESSAGE_PAYLOAD: 'INVALID_MESSAGE_PAYLOAD',\n  INVALID_MFA_PENDING_CREDENTIAL: 'INVALID_MFA_PENDING_CREDENTIAL',\n  INVALID_OAUTH_CLIENT_ID: 'INVALID_OAUTH_CLIENT_ID',\n  INVALID_OOB_CODE: 'INVALID_OOB_CODE',\n  INVALID_PASSWORD: 'INVALID_PASSWORD',\n  INVALID_PENDING_TOKEN: 'INVALID_PENDING_TOKEN',\n  INVALID_PHONE_NUMBER: 'INVALID_PHONE_NUMBER',\n  INVALID_PROVIDER_ID: 'INVALID_PROVIDER_ID',\n  INVALID_RECIPIENT_EMAIL: 'INVALID_RECIPIENT_EMAIL',\n  INVALID_SENDER: 'INVALID_SENDER',\n  INVALID_SESSION_INFO: 'INVALID_SESSION_INFO',\n  INVALID_TEMPORARY_PROOF: 'INVALID_TEMPORARY_PROOF',\n  INVALID_TENANT_ID: 'INVALID_TENANT_ID',\n  MFA_ENROLLMENT_NOT_FOUND: 'MFA_ENROLLMENT_NOT_FOUND',\n  MISSING_ANDROID_PACKAGE_NAME: 'MISSING_ANDROID_PACKAGE_NAME',\n  MISSING_APP_CREDENTIAL: 'MISSING_APP_CREDENTIAL',\n  MISSING_CODE: 'MISSING_CODE',\n  MISSING_CONTINUE_URI: 'MISSING_CONTINUE_URI',\n  MISSING_CUSTOM_TOKEN: 'MISSING_CUSTOM_TOKEN',\n  MISSING_IOS_BUNDLE_ID: 'MISSING_IOS_BUNDLE_ID',\n  MISSING_MFA_ENROLLMENT_ID: 'MISSING_MFA_ENROLLMENT_ID',\n  MISSING_MFA_PENDING_CREDENTIAL: 'MISSING_MFA_PENDING_CREDENTIAL',\n  MISSING_OOB_CODE: 'MISSING_OOB_CODE',\n  MISSING_OR_INVALID_NONCE: 'MISSING_OR_INVALID_NONCE',\n  MISSING_PASSWORD: 'MISSING_PASSWORD',\n  MISSING_PHONE_NUMBER: 'MISSING_PHONE_NUMBER',\n  MISSING_SESSION_INFO: 'MISSING_SESSION_INFO',\n  OPERATION_NOT_ALLOWED: 'OPERATION_NOT_ALLOWED',\n  PASSWORD_LOGIN_DISABLED: 'PASSWORD_LOGIN_DISABLED',\n  QUOTA_EXCEEDED: 'QUOTA_EXCEEDED',\n  RESET_PASSWORD_EXCEED_LIMIT: 'RESET_PASSWORD_EXCEED_LIMIT',\n  REJECTED_CREDENTIAL: 'REJECTED_CREDENTIAL',\n  SECOND_FACTOR_EXISTS: 'SECOND_FACTOR_EXISTS',\n  SECOND_FACTOR_LIMIT_EXCEEDED: 'SECOND_FACTOR_LIMIT_EXCEEDED',\n  SESSION_EXPIRED: 'SESSION_EXPIRED',\n  TENANT_ID_MISMATCH: 'TENANT_ID_MISMATCH',\n  TOKEN_EXPIRED: 'TOKEN_EXPIRED',\n  TOO_MANY_ATTEMPTS_TRY_LATER: 'TOO_MANY_ATTEMPTS_TRY_LATER',\n  UNSUPPORTED_FIRST_FACTOR: 'UNSUPPORTED_FIRST_FACTOR',\n  UNSUPPORTED_TENANT_OPERATION: 'UNSUPPORTED_TENANT_OPERATION',\n  UNVERIFIED_EMAIL: 'UNVERIFIED_EMAIL',\n  UNAUTHORIZED_DOMAIN: 'UNAUTHORIZED_DOMAIN',\n  USER_CANCELLED: 'USER_CANCELLED',\n  USER_DISABLED: 'USER_DISABLED',\n  USER_NOT_FOUND: 'USER_NOT_FOUND',\n  WEAK_PASSWORD: 'WEAK_PASSWORD'\n};\n\n\n/**\n * A map of server error codes to client errors.\n * @typedef {!Object<\n *     !fireauth.RpcHandler.ServerError, !fireauth.authenum.Error>}\n */\nfireauth.RpcHandler.ServerErrorMap;\n\n\n/**\n * Firebase Auth response field names.\n * @enum {string}\n */\nfireauth.RpcHandler.AuthServerField = {\n  ALL_PROVIDERS: 'allProviders',\n  AUTH_URI: 'authUri',\n  AUTHORIZED_DOMAINS: 'authorizedDomains',\n  DYNAMIC_LINKS_DOMAIN: 'dynamicLinksDomain',\n  EMAIL: 'email',\n  ERROR_MESSAGE: 'errorMessage',\n  EXPIRES_IN: 'expiresIn',\n  ID_TOKEN: 'idToken',\n  MFA_PENDING_CREDENTIAL: 'mfaPendingCredential',\n  NEED_CONFIRMATION: 'needConfirmation',\n  OAUTH_ID_TOKEN: 'oauthIdToken',\n  PENDING_TOKEN: 'pendingToken',\n  PHONE_RESPONSE_INFO: 'phoneResponseInfo',\n  PHONE_SESSION_INFO: 'phoneSessionInfo',\n  POST_BODY: 'postBody',\n  PROVIDER_ID: 'providerId',\n  RECAPTCHA_SITE_KEY: 'recaptchaSiteKey',\n  REQUEST_URI: 'requestUri',\n  REFRESH_TOKEN: 'refreshToken',\n  SESSION_ID: 'sessionId',\n  SESSION_INFO: 'sessionInfo',\n  SIGNIN_METHODS: 'signinMethods',\n  TEMPORARY_PROOF: 'temporaryProof'\n};\n\n\n/**\n * Firebase Auth response injected fields.\n * @enum {string}\n */\nfireauth.RpcHandler.InjectedResponseField = {\n  NONCE: 'nonce'\n};\n\n\n/**\n * Firebase Auth getOobConfirmationCode requestType possible values.\n * @enum {string}\n */\nfireauth.RpcHandler.GetOobCodeRequestType = {\n  EMAIL_SIGNIN: 'EMAIL_SIGNIN',\n  NEW_EMAIL_ACCEPT: 'NEW_EMAIL_ACCEPT',\n  PASSWORD_RESET: 'PASSWORD_RESET',\n  VERIFY_AND_CHANGE_EMAIL: 'VERIFY_AND_CHANGE_EMAIL',\n  VERIFY_EMAIL: 'VERIFY_EMAIL'\n};\n\n\n/**\n * Firebase Auth response field names.\n * @enum {string}\n */\nfireauth.RpcHandler.StsServerField = {\n  ACCESS_TOKEN: 'access_token',\n  EXPIRES_IN: 'expires_in',\n  REFRESH_TOKEN: 'refresh_token'\n};\n\n\n/**\n * @return {string} The API key.\n */\nfireauth.RpcHandler.prototype.getApiKey = function() {\n  return this.apiKey_;\n};\n\n\n/**\n * The Firebase custom locale header.\n * @const {string}\n * @private\n */\nfireauth.RpcHandler.FIREBASE_LOCALE_KEY_ = 'X-Firebase-Locale';\n\n\n/**\n * The secure token endpoint.\n * @const {string}\n * @private\n */\nfireauth.RpcHandler.SECURE_TOKEN_ENDPOINT_ =\n    'https://securetoken.googleapis.com/v1/token';\n\n\n/**\n * The default timeout delay (units in milliseconds) for requests sending to\n *     STS token endpoint.\n * @const {!fireauth.util.Delay}\n * @private\n */\nfireauth.RpcHandler.DEFAULT_SECURE_TOKEN_TIMEOUT_ =\n    new fireauth.util.Delay(30000, 60000);\n\n\n/**\n * The STS token RPC content headers.\n * @const {!Object}\n * @private\n */\nfireauth.RpcHandler.DEFAULT_SECURE_TOKEN_HEADERS_ = {\n  'Content-Type': 'application/x-www-form-urlencoded'\n};\n\n\n/**\n * The Firebase endpoint.\n * @const {string}\n * @private\n */\nfireauth.RpcHandler.FIREBASE_ENDPOINT_ =\n    'https://www.googleapis.com/identitytoolkit/v3/relyingparty/';\n\n\n/**\n * The Identity Platform endpoint.\n * @const {string}\n * @private\n */\nfireauth.RpcHandler.IDENTITY_PLATFORM_ENDPOINT_ =\n    'https://identitytoolkit.googleapis.com/v2/';\n\n\n/**\n * The default timeout delay (units in milliseconds) for requests sending to\n *     Firebase endpoint.\n * @const {!fireauth.util.Delay}\n * @private\n */\nfireauth.RpcHandler.DEFAULT_FIREBASE_TIMEOUT_ =\n    new fireauth.util.Delay(30000, 60000);\n\n\n/**\n * The Firebase RPC content headers.\n * @const {!Object}\n * @private\n */\nfireauth.RpcHandler.DEFAULT_FIREBASE_HEADERS_ = {\n  'Content-Type': 'application/json'\n};\n\n\n/**\n * Updates the custom locale header.\n * @param {?string} languageCode The new languageCode.\n */\nfireauth.RpcHandler.prototype.updateCustomLocaleHeader =\n    function(languageCode) {\n  if (languageCode) {\n    // If a language code is provided, add it to the header.\n    this.firebaseHeaders_[fireauth.RpcHandler.FIREBASE_LOCALE_KEY_] =\n        languageCode;\n  } else {\n    // Otherwise remove the custom locale header.\n    delete this.firebaseHeaders_[fireauth.RpcHandler.FIREBASE_LOCALE_KEY_];\n  }\n};\n\n\n/**\n * Updates the emulator configuration.\n * @param {?fireauth.constants.EmulatorSettings} emulatorConfig The new\n *     emulator config.\n */\nfireauth.RpcHandler.prototype.updateEmulatorConfig = function(emulatorConfig) {\n  if (!emulatorConfig) {\n    return;\n  }\n  // If an emulator config is provided, update the endpoints.\n  this.secureTokenEndpoint_ =\n    fireauth.RpcHandler.generateEmululatorEndpointUrl_(\n      fireauth.RpcHandler.SECURE_TOKEN_ENDPOINT_, emulatorConfig);\n  this.firebaseEndpoint_ =\n    fireauth.RpcHandler.generateEmululatorEndpointUrl_(\n      fireauth.RpcHandler.FIREBASE_ENDPOINT_, emulatorConfig);\n  this.identityPlatformEndpoint_ =\n    fireauth.RpcHandler.generateEmululatorEndpointUrl_(\n      fireauth.RpcHandler.IDENTITY_PLATFORM_ENDPOINT_, emulatorConfig);\n}\n\n  /**\n   * Creates an endpoint URL intended for use by the emulator.\n   * @param {string} endpoint the production endpoint URL.\n   * @param {?fireauth.constants.EmulatorSettings} emulatorConfig The emulator\n   *     config.\n   * @return {string} The emulator endpoint URL.\n   * @private\n   */\nfireauth.RpcHandler.generateEmululatorEndpointUrl_ = function(endpoint, emulatorConfig) {\n  const uri = goog.Uri.parse(endpoint);\n  const emulatorUri = goog.Uri.parse(emulatorConfig.url);\n  uri.setPath(uri.getDomain() + uri.getPath());\n  uri.setScheme(emulatorUri.getScheme());\n  uri.setDomain(emulatorUri.getDomain());\n  uri.setPort(emulatorUri.getPort());\n  return uri.toString();\n}\n\n\n/**\n * Updates the X-Client-Version in the header.\n * @param {?string} clientVersion The new client version.\n */\nfireauth.RpcHandler.prototype.updateClientVersion = function(clientVersion) {\n  if (clientVersion) {\n    // Update client version for Firebase Auth server.\n    this.firebaseHeaders_['X-Client-Version'] = clientVersion;\n    // Update client version for securetoken server.\n    this.secureTokenHeaders_['X-Client-Version'] = clientVersion;\n  } else {\n    // Remove client version from header.\n    delete this.firebaseHeaders_['X-Client-Version'];\n    delete this.secureTokenHeaders_['X-Client-Version'];\n  }\n};\n\n\n/**\n * Updates the tenant ID in the request.\n * @param {?string} tenantId The new tenant ID.\n */\nfireauth.RpcHandler.prototype.updateTenantId = function(tenantId) {\n  this.tenantId_ = tenantId;\n};\n\n\n/**\n * Returns the tenant ID.\n * @return {?string} The tenant ID.\n */\nfireauth.RpcHandler.prototype.getTenantId = function() {\n  return this.tenantId_;\n};\n\n\n/**\n * Sends XhrIo request using goog.net.XhrIo.\n * @param {string} url The URL to make a request to.\n * @param {function(?Object)=} opt_callback The callback to run on completion.\n * @param {fireauth.RpcHandler.HttpMethod=} opt_httpMethod The HTTP send method.\n * @param {?ArrayBuffer|?ArrayBufferView|?Blob|?Document|?FormData|string=}\n *     opt_data The request content.\n * @param {?Object=} opt_headers The request content headers.\n * @param {number=} opt_timeout The request timeout.\n * @private\n */\nfireauth.RpcHandler.prototype.sendXhr_ = function(\n    url,\n    opt_callback,\n    opt_httpMethod,\n    opt_data,\n    opt_headers,\n    opt_timeout) {\n  var sendXhr;\n  if (fireauth.util.supportsCors() || fireauth.util.isWorker()) {\n    // If supports CORS use goog.net.XhrIo.\n    sendXhr = goog.bind(this.sendXhrUsingXhrIo_, this);\n  } else {\n    // Load gapi.client.request and gapi.auth dependency dynamically.\n    if (!fireauth.RpcHandler.loadGApi_) {\n      fireauth.RpcHandler.loadGApi_ =\n          new goog.Promise(function(resolve, reject) {\n            // On load, resolve.\n            fireauth.RpcHandler.loadGApiJs_(resolve, reject);\n          });\n    }\n    // If does not support CORS, use gapi.client.request.\n    sendXhr = goog.bind(this.sendXhrUsingGApiClient_, this);\n  }\n  sendXhr(\n      url, opt_callback, opt_httpMethod, opt_data, opt_headers, opt_timeout);\n};\n\n\n/**\n * Sends XhrIo request using goog.net.XhrIo.\n * @param {string} url The URL to make a request to.\n * @param {function(?Object)=} opt_callback The callback to run on completion.\n * @param {fireauth.RpcHandler.HttpMethod=} opt_httpMethod The HTTP send method.\n * @param {?ArrayBuffer|?ArrayBufferView|?Blob|?Document|?FormData|string=}\n *     opt_data The request content.\n * @param {?Object=} opt_headers The request content headers.\n * @param {number=} opt_timeout The request timeout.\n * @private\n */\nfireauth.RpcHandler.prototype.sendXhrUsingXhrIo_ = function(\n    url,\n    opt_callback,\n    opt_httpMethod,\n    opt_data,\n    opt_headers,\n    opt_timeout) {\n  if (fireauth.util.isWorker() && !fireauth.util.isFetchSupported()) {\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.OPERATION_NOT_SUPPORTED,\n        'fetch, Headers and Request native APIs or equivalent Polyfills ' +\n        'must be available to support HTTP requests from a Worker ' +\n        'environment.');\n  }\n  var xhrIo = new goog.net.XhrIo(this.rpcHandlerXhrFactory_);\n\n  // xhrIo.setTimeoutInterval not working in IE10 and IE11, handle manually.\n  var requestTimeout;\n  if (opt_timeout) {\n    xhrIo.setTimeoutInterval(opt_timeout);\n    requestTimeout = setTimeout(function() {\n      xhrIo.dispatchEvent(goog.net.EventType.TIMEOUT);\n    }, opt_timeout);\n  }\n  // Run callback function on completion.\n  xhrIo.listen(\n      goog.net.EventType.COMPLETE,\n      /** @this {goog.net.XhrIo} */\n      function() {\n        // Clear timeout timer.\n        if (requestTimeout) {\n          clearTimeout(requestTimeout);\n        }\n        // Response assumed to be in json format. If not, catch, log error and\n        // pass null to callback.\n        var response = null;\n        try {\n          // Do not use this.responseJson() as it uses goog.json.parse\n          // underneath. Internal goog.json.parse parsing uses eval and since\n          // recommended Content Security Policy does not allow unsafe-eval,\n          // this is failing and throwing an error in chrome extensions and\n          // warnings else where. Use native parsing instead via JSON.parse.\n          response = JSON.parse(this.getResponseText()) || null;\n        } catch (e) {\n          response = null;\n        }\n        if (opt_callback) {\n          opt_callback(/** @type {?Object} */ (response));\n        }\n      });\n  // Dispose xhrIo on ready.\n  xhrIo.listenOnce(\n      goog.net.EventType.READY,\n      /** @this {goog.net.XhrIo} */\n      function() {\n        // Clear timeout timer.\n        if (requestTimeout) {\n          clearTimeout(requestTimeout);\n        }\n        // Dispose xhrIo.\n        this.dispose();\n      });\n  // Listen to timeout error.\n  // This should work when request is aborted too.\n  xhrIo.listenOnce(\n      goog.net.EventType.TIMEOUT,\n      /** @this {goog.net.XhrIo} */\n      function() {\n        // Clear timeout timer.\n        if (requestTimeout) {\n          clearTimeout(requestTimeout);\n        }\n        // Dispose xhrIo.\n        this.dispose();\n        // The request timed out.\n        if (opt_callback) {\n          opt_callback(null);\n        }\n      });\n  xhrIo.send(url, opt_httpMethod, opt_data, opt_headers);\n};\n\n\n/**\n * @const {!goog.string.Const} The GApi client library URL.\n * @private\n */\nfireauth.RpcHandler.GAPI_SRC_ = goog.string.Const.from(\n    'https://apis.google.com/js/client.js?onload=%{onload}');\n\n\n/**\n * @const {string}\n * @private\n */\nfireauth.RpcHandler.GAPI_CALLBACK_NAME_ =\n    '__fcb' + Math.floor(Math.random() * 1000000).toString();\n\n\n/**\n * Loads the GApi client library if it is not loaded.\n * @param {function()} callback The callback to invoke once it's loaded.\n * @param {function(?Object)} errback The error callback.\n * @private\n */\nfireauth.RpcHandler.loadGApiJs_ = function(callback, errback) {\n  // If gapi.client.request not available, load it dynamically.\n  if (!((window['gapi'] || {})['client'] || {})['request']) {\n    goog.global[fireauth.RpcHandler.GAPI_CALLBACK_NAME_] = function() {\n      // Callback will be called by GApi, test properly loaded here instead of\n      // after jsloader resolves.\n      if (!((window['gapi'] || {})['client'] || {})['request']) {\n        errback(new Error(fireauth.RpcHandler.ServerError.CORS_UNSUPPORTED));\n      } else {\n        callback();\n      }\n    };\n    var url = goog.html.TrustedResourceUrl.format(\n        fireauth.RpcHandler.GAPI_SRC_,\n        {'onload': fireauth.RpcHandler.GAPI_CALLBACK_NAME_});\n    // TODO: replace goog.net.jsloader with our own script includer.\n    var result = goog.net.jsloader.safeLoad(url);\n    result.addErrback(function() {\n      // In case file fails to load.\n      errback(new Error(fireauth.RpcHandler.ServerError.CORS_UNSUPPORTED));\n    });\n  } else {\n    callback();\n  }\n};\n\n\n/**\n * Sends XhrIo request using gapi.client.\n * @param {string} url The URL to make a request to.\n * @param {function(?Object)=} opt_callback The callback to run on completion.\n * @param {fireauth.RpcHandler.HttpMethod=} opt_httpMethod The HTTP send method.\n * @param {?ArrayBuffer|?ArrayBufferView|?Blob|?Document|?FormData|string=}\n *     opt_data The request content.\n * @param {?Object=} opt_headers The request content headers.\n * @param {number=} opt_timeout The request timeout.\n * @private\n */\nfireauth.RpcHandler.prototype.sendXhrUsingGApiClient_ = function(\n    url,\n    opt_callback,\n    opt_httpMethod,\n    opt_data,\n    opt_headers,\n    opt_timeout) {\n  var self = this;\n  // Wait for GApi dependency to load.\n  fireauth.RpcHandler.loadGApi_.then(function() {\n    window['gapi']['client']['setApiKey'](self.getApiKey());\n    // GApi maintains the Auth result and automatically append the Auth token to\n    // all outgoing requests. Firebase Auth requests will be rejected if there\n    // are others scopes (e.g. google plus) for the Auth token. Need to empty\n    // the token before call gitkit api. Restored in callback.\n    var oauth2Token = window['gapi']['auth']['getToken']();\n    window['gapi']['auth']['setToken'](null);\n    window['gapi']['client']['request']({\n      'path': url,\n      'method': opt_httpMethod,\n      'body': opt_data,\n      'headers': opt_headers,\n      // This needs to be set to none, otherwise the access token will be passed\n      // in the header field causing apiary to complain.\n      'authType': 'none',\n      'callback': function(response) {\n        window['gapi']['auth']['setToken'](oauth2Token);\n        if (opt_callback) {\n          opt_callback(response);\n        }\n      }\n    });\n  }).thenCatch(function(error) {\n    // Catches failure to support CORS and propagates it.\n    if (opt_callback) {\n      // Simulate backend server error to be caught by upper layer.\n      opt_callback({\n        'error': {\n          'message': (error && error['message']) ||\n              fireauth.RpcHandler.ServerError.CORS_UNSUPPORTED\n        }\n      });\n    }\n  });\n};\n\n\n/**\n * Validates the request for the STS access token.\n *\n * @param {?Object} data The STS token request body.\n * @return {boolean} Whether the request is valid.\n * @private\n */\nfireauth.RpcHandler.prototype.validateStsTokenRequest_ = function(data) {\n  if (data['grant_type'] == 'refresh_token' && data['refresh_token']) {\n    // Exchange refresh token.\n    return true;\n  } else if (data['grant_type'] == 'authorization_code' && data['code']) {\n    // Exchange ID token.\n    return true;\n  } else {\n    // Invalid.\n    return false;\n  }\n};\n\n\n/**\n * Handles the request for the STS access token.\n *\n * @param {!Object} data The STS token request body.\n * @return {!goog.Promise<!Object>}\n */\nfireauth.RpcHandler.prototype.requestStsToken = function(data) {\n  var self = this;\n  return new goog.Promise(function(resolve, reject) {\n    if (self.validateStsTokenRequest_(data)) {\n      self.sendXhr_(\n          self.secureTokenEndpoint_ + '?key=' +\n          encodeURIComponent(self.getApiKey()),\n          function(response) {\n            if (!response) {\n              // An unparseable response from the XHR most likely indicates some\n              // problem with the network.\n              reject(new fireauth.AuthError(\n                  fireauth.authenum.Error.NETWORK_REQUEST_FAILED));\n            } else if (fireauth.RpcHandler.hasError_(response)) {\n              reject(fireauth.RpcHandler.getDeveloperError_(response));\n            } else if (\n                !response[fireauth.RpcHandler.StsServerField.ACCESS_TOKEN] ||\n                !response[fireauth.RpcHandler.StsServerField.REFRESH_TOKEN]) {\n              reject(new fireauth.AuthError(\n                  fireauth.authenum.Error.INTERNAL_ERROR));\n            } else {\n              resolve(response);\n            }\n          },\n          fireauth.RpcHandler.HttpMethod.POST,\n          goog.Uri.QueryData.createFromMap(data).toString(),\n          self.secureTokenHeaders_,\n          self.secureTokenTimeout_.get());\n    } else {\n      reject(new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR));\n    }\n  });\n};\n\n\n/**\n * @param {!Object} data The object to serialize.\n * @return {string} The serialized object with null, undefined and empty string\n *     values removed.\n * @private\n */\nfireauth.RpcHandler.serialize_ = function(data) {\n  // goog.json.serialize converts undefined values to null.\n  // This helper removes all empty strings, nulls and undefined from serialized\n  // object.\n  // Serialize trimmed data.\n  return goog.json.serialize(fireauth.util.copyWithoutNullsOrUndefined(data));\n};\n\n\n/**\n * Creates and executes a request for the given API method using the legacy\n * Firebase Auth endpoint.\n * @param {string} method The API method.\n * @param {!fireauth.RpcHandler.HttpMethod} httpMethod The http request method.\n * @param {!Object} data The data for the API request. In the case of a GET\n *     request, the contents of this object will be form encoded and appended\n *     to the query string of the URL. No post body is sent in that case. If an\n *     object value is specified, it will be converted to a string:\n *     encodeURIComponent(String(value)).\n * @param {?fireauth.RpcHandler.ServerErrorMap=} opt_customErrorMap A map\n *     of server error codes to client errors to override default error\n *     handling.\n * @param {boolean=} opt_cachebuster Whether to append a unique string to\n *     request to force backend to return an uncached response to request.\n * @return {!goog.Promise<!Object>}\n */\nfireauth.RpcHandler.prototype.requestFirebaseEndpoint = function(\n    method, httpMethod, data, opt_customErrorMap, opt_cachebuster) {\n  return this.requestAuthEndpoint_(\n      this.firebaseEndpoint_, method, httpMethod, data, opt_customErrorMap,\n      opt_cachebuster);\n};\n\n\n/**\n * Creates and executes a request for the given API method using the identity\n * platform endpoint.\n * @param {string} method The API method.\n * @param {!fireauth.RpcHandler.HttpMethod} httpMethod The http request method.\n * @param {!Object} data The data for the API request. In the case of a GET\n *     request, the contents of this object will be form encoded and appended\n *     to the query string of the URL. No post body is sent in that case. If an\n *     object value is specified, it will be converted to a string:\n *     encodeURIComponent(String(value)).\n * @param {?fireauth.RpcHandler.ServerErrorMap=} opt_customErrorMap A map\n *     of server error codes to client errors to override default error\n *     handling.\n * @param {boolean=} opt_cachebuster Whether to append a unique string to\n *     request to force backend to return an uncached response to request.\n * @return {!goog.Promise<!Object>}\n */\nfireauth.RpcHandler.prototype.requestIdentityPlatformEndpoint = function(\n    method, httpMethod, data, opt_customErrorMap, opt_cachebuster) {\n  return this.requestAuthEndpoint_(\n      this.identityPlatformEndpoint_, method, httpMethod, data,\n      opt_customErrorMap, opt_cachebuster);\n};\n\n\n/**\n * Creates and executes a request for the given API method and Auth endpoint.\n * @param {string} endpoint The Auth endpoint to use.\n * @param {string} method The API method.\n * @param {!fireauth.RpcHandler.HttpMethod} httpMethod The http request method.\n * @param {!Object} data The data for the API request. In the case of a GET\n *     request, the contents of this object will be form encoded and appended\n *     to the query string of the URL. No post body is sent in that case. If an\n *     object value is specified, it will be converted to a string:\n *     encodeURIComponent(String(value)).\n * @param {?fireauth.RpcHandler.ServerErrorMap=} opt_customErrorMap A map\n *     of server error codes to client errors to override default error\n *     handling.\n * @param {boolean=} opt_cachebuster Whether to append a unique string to\n *     request to force backend to return an uncached response to request.\n * @return {!goog.Promise<!Object>}\n * @private\n */\nfireauth.RpcHandler.prototype.requestAuthEndpoint_ = function(\n    endpoint, method, httpMethod, data, opt_customErrorMap, opt_cachebuster) {\n  var self = this;\n  // Construct endpoint URL.\n  var uri = goog.Uri.parse(endpoint + method);\n  uri.setParameterValue('key', this.getApiKey());\n  // Check whether to append cachebuster to request.\n  if (opt_cachebuster) {\n    uri.setParameterValue('cb', Date.now().toString());\n  }\n  // Firebase allows GET endpoints.\n  var isGet = httpMethod == fireauth.RpcHandler.HttpMethod.GET;\n  if (isGet) {\n    // For GET HTTP method, append data to query string.\n    for (var key in data) {\n      if (data.hasOwnProperty(key)) {\n        uri.setParameterValue(key, data[key]);\n      }\n    }\n  }\n  return new goog.Promise(function(resolve, reject) {\n    self.sendXhr_(\n        uri.toString(),\n        function(response) {\n          if (!response) {\n            // An unparseable response from the XHR most likely indicates some\n            // problem with the network.\n            reject(new fireauth.AuthError(\n                fireauth.authenum.Error.NETWORK_REQUEST_FAILED));\n          } else if (fireauth.RpcHandler.hasError_(response)) {\n            reject(fireauth.RpcHandler.getDeveloperError_(response,\n                opt_customErrorMap || {}));\n          } else {\n            resolve(response);\n          }\n        },\n        httpMethod,\n        // No post body data in GET requests.\n        isGet ? undefined : fireauth.RpcHandler.serialize_(data),\n        self.firebaseHeaders_,\n        self.firebaseTimeout_.get());\n  });\n};\n\n\n/**\n * Verifies that the request has a valid email set.\n * @param {!Object} request\n * @private\n */\nfireauth.RpcHandler.validateRequestHasEmail_ = function(request) {\n  if (!fireauth.util.isValidEmailAddress(request['email'])) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INVALID_EMAIL);\n  }\n};\n\n\n/**\n * Verifies that the response has a valid email set.\n * @param {!Object} response\n * @private\n */\nfireauth.RpcHandler.validateResponseHasEmail_ = function(response) {\n  if (!fireauth.util.isValidEmailAddress(response['email'])) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR);\n  }\n};\n\n\n/**\n * Verifies that the an email is valid, if it is there.\n * @param {!Object} request\n * @private\n */\nfireauth.RpcHandler.validateEmailIfPresent_ = function(request) {\n  if ('email' in request) {\n    fireauth.RpcHandler.validateRequestHasEmail_(request);\n  }\n};\n\n\n/**\n * @param {string} providerId The provider ID.\n * @param {?Array<string>=} opt_additionalScopes The list of scope strings.\n * @return {?string} The IDP and its comma separated scope strings serialized.\n * @private\n */\nfireauth.RpcHandler.getAdditionalScopes_ =\n    function(providerId, opt_additionalScopes) {\n  var scopes = {};\n  if (opt_additionalScopes && opt_additionalScopes.length) {\n    scopes[providerId] = opt_additionalScopes.join(',');\n    // Return stringified scopes.\n    return goog.json.serialize(scopes);\n  }\n  return null;\n};\n\n\n/**\n * Validates a response from getAuthUri.\n * @param {?Object} response The getAuthUri response data.\n * @private\n */\nfireauth.RpcHandler.validateGetAuthResponse_ = function(response) {\n  if (!response[fireauth.RpcHandler.AuthServerField.AUTH_URI]) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR,\n        'Unable to determine the authorization endpoint for the specified '+\n        'provider. This may be an issue in the provider configuration.');\n  } else if ( !response[fireauth.RpcHandler.AuthServerField.SESSION_ID]) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR);\n  }\n};\n\n\n/**\n * Requests createAuthUri endpoint to retrieve the authUri and session ID for\n * the start of an OAuth handshake.\n * @param {string} providerId The provider ID.\n * @param {string} continueUri The IdP callback URL.\n * @param {?Object=} opt_customParameters The optional OAuth custom parameters\n *     plain object.\n * @param {?Array<string>=} opt_additionalScopes The list of scope strings.\n * @param {?string=} opt_email The optional email.\n * @param {?string=} opt_sessionId The optional session ID.\n * @return {!goog.Promise<!Object>}\n */\nfireauth.RpcHandler.prototype.getAuthUri = function(\n    providerId,\n    continueUri,\n    opt_customParameters,\n    opt_additionalScopes,\n    opt_email,\n    opt_sessionId) {\n  // SAML provider request is constructed differently than OAuth requests.\n  var isSaml = fireauth.idp.isSaml(providerId);\n  var request = {\n    'identifier': opt_email,\n    'providerId': providerId,\n    'continueUri': continueUri,\n    'customParameter': opt_customParameters || {},\n    'oauthScope': fireauth.RpcHandler.getAdditionalScopes_(\n        providerId, opt_additionalScopes),\n    'sessionId': opt_sessionId\n  };\n  // Custom parameters and OAuth scopes should be ignored.\n  if (isSaml) {\n    delete request['customParameter'];\n    delete request['oauthScope'];\n  }\n  // When sessionId is provided, mobile flow (Cordova) is being used, force\n  // code flow and not implicit flow. All other providers use code flow by\n  // default.\n  if (opt_sessionId && providerId == fireauth.idp.ProviderId.GOOGLE) {\n    request['authFlowType'] = 'CODE_FLOW';\n  }\n  return this.invokeRpc(fireauth.RpcHandler.ApiMethod.GET_AUTH_URI,\n      request);\n};\n\n\n/**\n * Gets the list of IDPs that can be used to log in for the given identifier.\n * @param {string} identifier The identifier, such as an email address.\n * @return {!goog.Promise<!Array<string>>}\n */\nfireauth.RpcHandler.prototype.fetchProvidersForIdentifier =\n    function(identifier) {\n  // createAuthUri returns an error if continue URI is not http or https.\n  // For environments like Cordova, Chrome extensions, native frameworks, file\n  // systems, etc, use http://localhost as continue URL.\n  var continueUri = fireauth.util.isHttpOrHttps() ?\n      fireauth.util.getCurrentUrl() : 'http://localhost';\n  var request = {\n    'identifier': identifier,\n    'continueUri': continueUri\n  };\n  return this.invokeRpc(fireauth.RpcHandler.ApiMethod.CREATE_AUTH_URI, request)\n      .then(function(response) {\n        return response[fireauth.RpcHandler.AuthServerField.ALL_PROVIDERS] ||\n            [];\n      });\n};\n\n\n/**\n * Returns the list of sign in methods for the given identifier.\n * @param {string} identifier The identifier, such as an email address.\n * @return {!goog.Promise<!Array<string>>}\n */\nfireauth.RpcHandler.prototype.fetchSignInMethodsForIdentifier = function(\n    identifier) {\n  // createAuthUri returns an error if continue URI is not http or https.\n  // For environments like Cordova, Chrome extensions, native frameworks, file\n  // systems, etc, use http://localhost as continue URL.\n  var continueUri = fireauth.util.isHttpOrHttps() ?\n      fireauth.util.getCurrentUrl() :\n      'http://localhost';\n  var request = {\n    'identifier': identifier,\n    'continueUri': continueUri\n  };\n  return this.invokeRpc(fireauth.RpcHandler.ApiMethod.CREATE_AUTH_URI, request)\n      .then(function(response) {\n        return response[fireauth.RpcHandler.AuthServerField.SIGNIN_METHODS] ||\n            [];\n      });\n};\n\n\n/**\n * Gets the list of authorized domains for the specified project.\n * @return {!goog.Promise<!Array<string>>}\n */\nfireauth.RpcHandler.prototype.getAuthorizedDomains = function() {\n  return this.invokeRpc(fireauth.RpcHandler.ApiMethod.GET_PROJECT_CONFIG, {})\n      .then(function(response) {\n        return response[\n           fireauth.RpcHandler.AuthServerField.AUTHORIZED_DOMAINS] || [];\n      });\n};\n\n\n/**\n * Gets the reCAPTCHA parameters needed to render the project's provisioned\n * reCAPTCHA.\n * @return {!goog.Promise<!Object>}\n */\nfireauth.RpcHandler.prototype.getRecaptchaParam = function() {\n  return this.invokeRpc(fireauth.RpcHandler.ApiMethod.GET_RECAPTCHA_PARAM, {});\n};\n\n\n/**\n * Gets the list of authorized domains for the specified project.\n * @return {!goog.Promise<string>}\n */\nfireauth.RpcHandler.prototype.getDynamicLinkDomain = function() {\n  var request = {\n    'returnDynamicLink': true\n  };\n  return this.invokeRpc(\n      fireauth.RpcHandler.ApiMethod.RETURN_DYNAMIC_LINK, request);\n};\n\n\n/**\n * Checks if the provided iOS bundle ID belongs to the project as specified by\n * the API key.\n * @param {string} iosBundleId  The iOS bundle ID to check.\n * @return {!goog.Promise<void>}\n */\nfireauth.RpcHandler.prototype.isIosBundleIdValid = function(iosBundleId) {\n  var request = {\n    'iosBundleId': iosBundleId\n  };\n  // This will either resolve if the identifier is valid or throw INVALID_APP_ID\n  // if not.\n  return this.invokeRpc(\n      fireauth.RpcHandler.ApiMethod.GET_PROJECT_CONFIG, request)\n      .then(function(result) {\n        // Do not return anything.\n      });\n};\n\n\n/**\n * Checks if the provided Android package name belongs to the project as\n * specified by the API key.\n * @param {string} androidPackageName  The iOS bundle ID to check.\n * @param {?string=} opt_sha1Cert The optional SHA-1 Android cert to check.\n * @return {!goog.Promise<void>}\n */\nfireauth.RpcHandler.prototype.isAndroidPackageNameValid =\n    function(androidPackageName, opt_sha1Cert) {\n  var request = {\n    'androidPackageName': androidPackageName\n  };\n  // This is relevant for the native Android SDK flow.\n  // This will redirect to an FDL domain owned by GMScore instead of\n  // the developer's FDL domain as is done for Cordova apps.\n  if (!!opt_sha1Cert) {\n    request['sha1Cert'] = opt_sha1Cert;\n  }\n  // When no sha1Cert is passed, this will either resolve if the identifier is\n  // valid or throw INVALID_APP_ID if not.\n  // When sha1Cert is also passed, this will either resolve or fail with an\n  // INVALID_CERT_HASH error.\n  return this.invokeRpc(\n      fireauth.RpcHandler.ApiMethod.GET_PROJECT_CONFIG, request)\n      .then(function(result) {\n        // Do not return anything.\n      });\n};\n\n\n/**\n * Checks if the provided OAuth client ID belongs to the project as specified by\n * the API key.\n * @param {string} clientId The OAuth client ID to check.\n * @return {!goog.Promise<void>}\n */\nfireauth.RpcHandler.prototype.isOAuthClientIdValid = function(clientId) {\n  var request = {\n    'clientId': clientId\n  };\n  // This will either resolve if the client ID is valid or throw\n  // INVALID_OAUTH_CLIENT_ID if not.\n  return this.invokeRpc(\n      fireauth.RpcHandler.ApiMethod.GET_PROJECT_CONFIG, request)\n      .then(function(result) {\n        // Do not return anything.\n      });\n};\n\n\n/**\n * Requests getAccountInfo endpoint using an ID token.\n * @param {string} idToken The ID token.\n * @return {!goog.Promise<!Object>}\n */\nfireauth.RpcHandler.prototype.getAccountInfoByIdToken = function(idToken) {\n  var request = {'idToken': idToken};\n  return this.invokeRpc(fireauth.RpcHandler.ApiMethod.GET_ACCOUNT_INFO,\n      request);\n};\n\n\n/**\n * Validates a request to sign in with email and password.\n * @param {!Object} request\n * @private\n */\nfireauth.RpcHandler.validateVerifyCustomTokenRequest_ = function(request) {\n  if (!request['token']) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INVALID_CUSTOM_TOKEN);\n  }\n};\n\n\n/**\n * Verifies a custom token and returns a Promise that resolves with the ID\n * token.\n * @param {string} token The custom token.\n * @return {!goog.Promise<!Object>}\n */\nfireauth.RpcHandler.prototype.verifyCustomToken = function(token) {\n  var request = {'token': token};\n  return this.invokeRpc(fireauth.RpcHandler.ApiMethod.VERIFY_CUSTOM_TOKEN,\n      request);\n};\n\n\n/**\n * Validates a request to sign in with email and password.\n * @param {!Object} request\n * @private\n */\nfireauth.RpcHandler.validateVerifyPasswordRequest_ = function(request) {\n  fireauth.RpcHandler.validateRequestHasEmail_(request);\n  if (!request['password']) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INVALID_PASSWORD);\n  }\n};\n\n\n/**\n * Verifies a password and returns a Promise that resolves with the ID\n * token.\n * @param {string} email The email address.\n * @param {string} password The entered password.\n * @return {!goog.Promise<!Object>}\n */\nfireauth.RpcHandler.prototype.verifyPassword = function(email, password) {\n  var request = {\n    'email': email,\n    'password': password\n  };\n  return this.invokeRpc(fireauth.RpcHandler.ApiMethod.VERIFY_PASSWORD,\n      request);\n};\n\n\n/**\n * Verifies an email link OTP for sign-in and returns a Promise that resolves\n * with the ID token.\n * @param {string} email The email address.\n * @param {string} oobCode The email action OTP.\n * @return {!goog.Promise<!Object>}\n */\nfireauth.RpcHandler.prototype.emailLinkSignIn = function(email, oobCode) {\n  var request = {\n    'email': email,\n    'oobCode': oobCode\n  };\n  return this.invokeRpc(\n      fireauth.RpcHandler.ApiMethod.EMAIL_LINK_SIGNIN, request);\n};\n\n\n/**\n * Verifies an email link OTP for linking and returns a Promise that resolves\n * with the ID token.\n * @param {string} idToken The ID token.\n * @param {string} email The email address.\n * @param {string} oobCode The email action OTP.\n * @return {!goog.Promise<!Object>}\n */\nfireauth.RpcHandler.prototype.emailLinkSignInForLinking =\n    function(idToken, email, oobCode) {\n  var request = {\n    'idToken': idToken,\n    'email': email,\n    'oobCode': oobCode\n  };\n  return this.invokeRpc(\n      fireauth.RpcHandler.ApiMethod.EMAIL_LINK_SIGNIN_FOR_LINKING,\n      request);\n};\n\n\n/**\n * Validates a response that should contain an ID token.\n * If no ID token is available, it checks if a multi-factor pending credential\n * is available instead. In that case, it throws the MFA_REQUIRED error code.\n * @param {?Object} response The server response data.\n * @private\n */\nfireauth.RpcHandler.validateIdTokenResponse_ = function(response) {\n  if (!response[fireauth.RpcHandler.AuthServerField.ID_TOKEN]) {\n    // User could be a second factor user.\n    // When second factor is required, a pending credential is returned.\n    if (response[fireauth.RpcHandler.AuthServerField.MFA_PENDING_CREDENTIAL]) {\n      throw new fireauth.AuthError(\n          fireauth.authenum.Error.MFA_REQUIRED,\n          null,\n          goog.object.clone(response));\n    }\n    throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR);\n  }\n};\n\n\n/**\n * Validates a getRecaptchaParam response.\n * @param {?Object} response The server response data.\n * @private\n */\nfireauth.RpcHandler.validateGetRecaptchaParamResponse_ = function(response) {\n  // Both are required. This could change though.\n  if (!response[fireauth.RpcHandler.AuthServerField.RECAPTCHA_SITE_KEY]) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR);\n  }\n};\n\n\n/**\n * Validates a request that sends the verification ID and code for a sign in/up\n * phone Auth flow.\n * @param {!Object} request The server request object.\n * @private\n */\nfireauth.RpcHandler.validateVerifyPhoneNumberRequest_ = function(request) {\n  // There are 2 cases here:\n  // case 1: sessionInfo and code\n  // case 2: phoneNumber and temporaryProof\n  if (request['phoneNumber'] || request['temporaryProof']) {\n    // Case 2. Both phoneNumber and temporaryProof should be set.\n    if (!request['phoneNumber'] || !request['temporaryProof']) {\n      throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR);\n    }\n  } else {\n    // Otherwise it's case 1, so we expect sessionInfo and code.\n    if (!request['sessionInfo']) {\n      throw new fireauth.AuthError(\n          fireauth.authenum.Error.MISSING_SESSION_INFO);\n    }\n    if (!request['code']) {\n      throw new fireauth.AuthError(fireauth.authenum.Error.MISSING_CODE);\n    }\n  }\n};\n\n\n/**\n * Validates a request that sends the verification ID and code for a link/update\n * phone Auth flow.\n * @param {!Object} request The server request object.\n * @private\n */\nfireauth.RpcHandler.validateVerifyPhoneNumberLinkRequest_ = function(request) {\n  // idToken should be required here.\n  if (!request['idToken']) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR);\n  }\n  // The other request parameters match the sign in flow.\n  fireauth.RpcHandler.validateVerifyPhoneNumberRequest_(request);\n};\n\n\n/**\n * Validates a request to create an email and password account.\n * @param {!Object} request\n * @private\n */\nfireauth.RpcHandler.validateCreateAccountRequest_ = function(request) {\n  fireauth.RpcHandler.validateRequestHasEmail_(request);\n  if (!request['password']) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.WEAK_PASSWORD);\n  }\n};\n\n\n/**\n * Validates a request to createAuthUri.\n * @param {!Object} request\n * @private\n */\nfireauth.RpcHandler.validateGetAuthUriRequest_ = function(request) {\n  if (!request['continueUri']) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.MISSING_CONTINUE_URI);\n  }\n  // Either a SAML or non SAML providerId must be provided.\n  if (!request['providerId']) {\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.INTERNAL_ERROR,\n        'A provider ID must be provided in the request.');\n  }\n};\n\n\n/**\n * Creates an email/password account. Returns a Promise that resolves with the\n * ID token.\n * @param {string} email The email address of the account.\n * @param {string} password The password.\n * @return {!goog.Promise<!Object>}\n */\nfireauth.RpcHandler.prototype.createAccount = function(email, password) {\n  var request = {\n    'email': email,\n    'password': password\n  };\n  return this.invokeRpc(fireauth.RpcHandler.ApiMethod.CREATE_ACCOUNT,\n      request);\n};\n\n\n/**\n * Signs in a user as anonymous. Returns a Promise that resolves with the\n * ID token.\n * @return {!goog.Promise<!Object>}\n */\nfireauth.RpcHandler.prototype.signInAnonymously = function() {\n  return this.invokeRpc(fireauth.RpcHandler.ApiMethod.SIGN_IN_ANONYMOUSLY, {});\n};\n\n\n/**\n * Deletes the user's account corresponding to the idToken given.\n * @param {string} idToken The idToken of the user.\n * @return {!goog.Promise<undefined>}\n */\nfireauth.RpcHandler.prototype.deleteAccount = function(idToken) {\n  var request = {\n    'idToken': idToken\n  };\n  return this.invokeRpc(fireauth.RpcHandler.ApiMethod.DELETE_ACCOUNT,\n      request);\n};\n\n\n/**\n * Requests setAccountInfo endpoint for updateEmail operation.\n * @param {string} idToken The ID token.\n * @param {string} newEmail The new email.\n * @return {!goog.Promise<!Object>}\n */\nfireauth.RpcHandler.prototype.updateEmail = function(idToken, newEmail) {\n  var request = {\n    'idToken': idToken,\n    'email': newEmail\n  };\n  return this.invokeRpc(fireauth.RpcHandler.ApiMethod.SET_ACCOUNT_INFO,\n      request);\n};\n\n\n/**\n * Validates a setAccountInfo request that updates the password.\n * @param {!Object} request\n * @private\n */\nfireauth.RpcHandler.validateSetAccountInfoSensitive_ = function(request) {\n  fireauth.RpcHandler.validateEmailIfPresent_(request);\n  if (!request['password']) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.WEAK_PASSWORD);\n  }\n};\n\n\n/**\n * Requests setAccountInfo endpoint for updatePassword operation.\n * @param {string} idToken The ID token.\n * @param {string} newPassword The new password.\n * @return {!goog.Promise<!Object>}\n */\nfireauth.RpcHandler.prototype.updatePassword = function(idToken, newPassword) {\n  var request = {\n    'idToken': idToken,\n    'password': newPassword\n  };\n  return this.invokeRpc(\n      fireauth.RpcHandler.ApiMethod.SET_ACCOUNT_INFO_SENSITIVE, request);\n};\n\n\n/**\n * Requests setAccountInfo endpoint to set the email and password. This can be\n * used to link an existing account to a new email and password account.\n * @param {string} idToken The ID token.\n * @param {string} newEmail The new email.\n * @param {string} newPassword The new password.\n * @return {!goog.Promise<!Object>}\n */\nfireauth.RpcHandler.prototype.updateEmailAndPassword = function(idToken,\n    newEmail, newPassword) {\n  var request = {\n    'idToken': idToken,\n    'email': newEmail,\n    'password': newPassword\n  };\n  return this.invokeRpc(\n      fireauth.RpcHandler.ApiMethod.SET_ACCOUNT_INFO_SENSITIVE, request);\n};\n\n\n/**\n * Maps the name of a field in the account info object to the backend enum\n * value, for deletion of profile fields.\n * @private {!Object<string, string>}\n */\nfireauth.RpcHandler.PROFILE_FIELD_TO_ENUM_NAME_ = {\n  'displayName': 'DISPLAY_NAME',\n  'photoUrl': 'PHOTO_URL'\n};\n\n\n/**\n * Updates the profile of the user. When resolved, promise returns a response\n * similar to that of getAccountInfo.\n * @param {string} idToken The ID token of the user whose profile is changing.\n * @param {!Object} profileData The new profile data.\n * @return {!goog.Promise<!Object>}\n */\nfireauth.RpcHandler.prototype.updateProfile = function(idToken, profileData) {\n  var data = {\n    'idToken': idToken\n  };\n  var fieldsToDelete = [];\n\n  // Copy over the relevant fields from profileData, or explicitly flag a field\n  // for deletion if null is passed as the value. Note that this currently only\n  // checks profileData to the first level.\n  goog.object.forEach(fireauth.RpcHandler.PROFILE_FIELD_TO_ENUM_NAME_,\n      function(enumName, fieldName) {\n        var fieldValue = profileData[fieldName];\n        if (fieldValue === null) {\n          // If null is explicitly provided, delete the field.\n          fieldsToDelete.push(enumName);\n        } else if (fieldName in profileData) {\n          // If the field is explicitly set, send it to the backend.\n          data[fieldName] = fieldValue;\n        }\n      });\n  if (fieldsToDelete.length) {\n    data['deleteAttribute'] = fieldsToDelete;\n  }\n  return this.invokeRpc(fireauth.RpcHandler.ApiMethod.SET_ACCOUNT_INFO, data);\n};\n\n\n/**\n * Validates a request for an email action code for password reset.\n * @param {!Object} request The getOobCode request data for password reset.\n * @private\n */\nfireauth.RpcHandler.validateOobCodeRequest_ = function(request) {\n  if (request['requestType'] !=\n          fireauth.RpcHandler.GetOobCodeRequestType.PASSWORD_RESET) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR);\n  }\n  fireauth.RpcHandler.validateRequestHasEmail_(request);\n};\n\n\n/**\n * Validates a request for an email action for passwordless email sign-in.\n * @param {!Object} request The getOobCode request data for email sign-in.\n * @private\n */\nfireauth.RpcHandler.validateEmailSignInCodeRequest_ = function(request) {\n  if (request['requestType'] !=\n      fireauth.RpcHandler.GetOobCodeRequestType.EMAIL_SIGNIN) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR);\n  }\n  fireauth.RpcHandler.validateRequestHasEmail_(request);\n};\n\n\n/**\n * Validates a request for an email action for email verification.\n * @param {!Object} request The getOobCode request data for email verification.\n * @private\n */\nfireauth.RpcHandler.validateEmailVerificationCodeRequest_ = function(request) {\n  if (request['requestType'] !=\n          fireauth.RpcHandler.GetOobCodeRequestType.VERIFY_EMAIL) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR);\n  }\n};\n\n\n/**\n * Validates a request for an email action for email verification before update.\n * @param {!Object} request The getOobCode request data for email verification\n *     before update.\n * @private\n */\nfireauth.RpcHandler.validateEmailVerificationCodeBeforeUpdateRequest_ =\n    function(request) {\n  if (request['requestType'] !=\n          fireauth.RpcHandler.GetOobCodeRequestType.VERIFY_AND_CHANGE_EMAIL) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR);\n  }\n};\n\n\n/**\n * Requests getOobCode endpoint for password reset, returns promise that\n * resolves with user's email.\n * @param {string} email The email account with the password to be reset.\n * @param {!Object} additionalRequestData Additional data to add to the request.\n * @return {!goog.Promise<string>}\n */\nfireauth.RpcHandler.prototype.sendPasswordResetEmail =\n    function(email, additionalRequestData) {\n  var request = {\n    'requestType': fireauth.RpcHandler.GetOobCodeRequestType.PASSWORD_RESET,\n    'email': email\n  };\n  // Extend the original request with the additional data.\n  goog.object.extend(request, additionalRequestData);\n  return this.invokeRpc(fireauth.RpcHandler.ApiMethod.GET_OOB_CODE, request);\n};\n\n\n/**\n * Requests getOobCode endpoint for passwordless email sign-in, returns promise\n * that resolves with user's email.\n * @param {string} email The email account to sign in with.\n * @param {!Object} additionalRequestData Additional data to add to the request.\n * @return {!goog.Promise<string>}\n */\nfireauth.RpcHandler.prototype.sendSignInLinkToEmail = function(\n    email, additionalRequestData) {\n  var request = {\n    'requestType': fireauth.RpcHandler.GetOobCodeRequestType.EMAIL_SIGNIN,\n    'email': email\n  };\n  // Extend the original request with the additional data.\n  goog.object.extend(request, additionalRequestData);\n  return this.invokeRpc(\n      fireauth.RpcHandler.ApiMethod.GET_EMAIL_SIGNIN_CODE, request);\n};\n\n\n/**\n * Requests getOobCode endpoint for email verification, returns promise that\n * resolves with user's email.\n * @param {string} idToken The idToken of the user confirming his email.\n * @param {!Object} additionalRequestData Additional data to add to the request.\n * @return {!goog.Promise<string>}\n */\nfireauth.RpcHandler.prototype.sendEmailVerification =\n    function(idToken, additionalRequestData) {\n  var request = {\n    'requestType': fireauth.RpcHandler.GetOobCodeRequestType.VERIFY_EMAIL,\n    'idToken': idToken\n  };\n  // Extend the original request with the additional data.\n  goog.object.extend(request, additionalRequestData);\n  return this.invokeRpc(\n      fireauth.RpcHandler.ApiMethod.GET_EMAIL_VERIFICATION_CODE, request);\n};\n\n\n/**\n * Requests getOobCode endpoint for email verification before updating the\n * email.\n * @param {string} idToken The idToken of the user updating his email.\n * @param {string} newEmail The new email.\n * @param {!Object} additionalRequestData Additional data to add to the request.\n * @return {!goog.Promise<void>}\n */\nfireauth.RpcHandler.prototype.verifyBeforeUpdateEmail =\n    function(idToken, newEmail, additionalRequestData) {\n  var request = {\n    'requestType':\n        fireauth.RpcHandler.GetOobCodeRequestType.VERIFY_AND_CHANGE_EMAIL,\n    'idToken': idToken,\n    'newEmail': newEmail\n  };\n  // Extend the original request with the additional data.\n  goog.object.extend(request, additionalRequestData);\n  return this.invokeRpc(\n      fireauth.RpcHandler.ApiMethod.GET_EMAIL_VERIFICATION_CODE_BEFORE_UPDATE,\n      request);\n};\n\n\n/**\n * Requests sendVerificationCode endpoint for verifying the user's ownership of\n * a phone number. It resolves with a sessionInfo (verificationId).\n * @param {!Object} request The verification request which contains a phone\n *     number and an assertion.\n * @return {!goog.Promise<string>}\n */\nfireauth.RpcHandler.prototype.sendVerificationCode = function(request) {\n  // In the future, we could support other types of assertions so for now,\n  // we are keeping the request an object.\n  return this.invokeRpc(\n      fireauth.RpcHandler.ApiMethod.SEND_VERIFICATION_CODE, request);\n};\n\n\n/**\n * Requests verifyPhoneNumber endpoint for sign in/sign up phone number\n * authentication flow and resolves with the STS token response.\n * @param {!Object} request The phone number ID and code to exchange for a\n *     Firebase Auth session.\n * @return {!goog.Promise<!Object>}\n */\nfireauth.RpcHandler.prototype.verifyPhoneNumber = function(request) {\n  return this.invokeRpc(\n      fireauth.RpcHandler.ApiMethod.VERIFY_PHONE_NUMBER, request);\n};\n\n\n/**\n * Requests verifyPhoneNumber endpoint for link/update phone number\n * authentication flow and resolves with the STS token response.\n * @param {!Object} request The phone number ID and code to exchange for a\n *     Firebase Auth session.\n * @return {!goog.Promise<!Object>}\n */\nfireauth.RpcHandler.prototype.verifyPhoneNumberForLinking = function(request) {\n  return this.invokeRpc(\n      fireauth.RpcHandler.ApiMethod.VERIFY_PHONE_NUMBER_FOR_LINKING, request);\n};\n\n\n/**\n * Validates a response to a phone number linking request.\n * @param {?Object} response The server response data.\n * @private\n */\nfireauth.RpcHandler.validateVerifyPhoneNumberForLinkingResponse_ =\n    function(response) {\n  if (response[fireauth.RpcHandler.AuthServerField.TEMPORARY_PROOF]) {\n    response['code'] = fireauth.authenum.Error.CREDENTIAL_ALREADY_IN_USE;\n    throw fireauth.AuthErrorWithCredential.fromPlainObject(response);\n  }\n\n  // If there's no temporary proof, then we expect the request to have\n  // succeeded and returned an ID token.\n  fireauth.RpcHandler.validateIdTokenResponse_(response);\n};\n\n\n/**\n * Requests verifyPhoneNumber endpoint for reauthenticating with a phone number\n * and resolves with the STS token response.\n * @param {!Object} request The phone number ID, code, and current ID token to\n *     exchange for a refreshed Firebase Auth session.\n * @return {!goog.Promise<!Object>}\n */\nfireauth.RpcHandler.prototype.verifyPhoneNumberForExisting = function(request) {\n  request['operation'] = 'REAUTH';\n  return this.invokeRpc(\n      fireauth.RpcHandler.ApiMethod.VERIFY_PHONE_NUMBER_FOR_EXISTING,\n      request);\n};\n\n\n/**\n * Validates a request for starting phone MFA enrollment.\n * @param {!Object} request The startPhoneMfaEnrollment request data.\n * @private\n */\nfireauth.RpcHandler.validateStartPhoneMfaEnrollmentRequest_ =\n    function(request) {\n  if (!request['phoneEnrollmentInfo']) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR);\n  }\n  if (!request['phoneEnrollmentInfo']['phoneNumber']) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.MISSING_PHONE_NUMBER);\n  }\n  if (!request['phoneEnrollmentInfo']['recaptchaToken']) {\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.MISSING_APP_CREDENTIAL);\n  }\n};\n\n\n/**\n * Validates a response to a start phone MFA enrollment request.\n * @param {!Object} response The server response data.\n * @private\n */\nfireauth.RpcHandler.validateStartPhoneMfaEnrollmentResponse_ =\n    function(response) {\n  if (!response[fireauth.RpcHandler.AuthServerField.PHONE_SESSION_INFO] ||\n      !response[fireauth.RpcHandler.AuthServerField.PHONE_SESSION_INFO]\n          [fireauth.RpcHandler.AuthServerField.SESSION_INFO]) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR);\n  }\n};\n\n\n/**\n * Requests startMfaEnrollment endpoint for verifying the user's ownership of\n * a phone number before enrolling to MFA. It resolves with a sessionInfo\n * (verificationId) string.\n * @param {!Object} request The enroll MFA request for phone.\n * @return {!goog.Promise<string>}\n */\nfireauth.RpcHandler.prototype.startPhoneMfaEnrollment = function(request) {\n  return this.invokeRpc(\n      fireauth.RpcHandler.ApiMethod.START_PHONE_MFA_ENROLLMENT, request)\n      .then(function(response) {\n        // Extract the sessionInfo(verificationId) from response.\n        return response[fireauth.RpcHandler.AuthServerField.PHONE_SESSION_INFO]\n            [fireauth.RpcHandler.AuthServerField.SESSION_INFO];\n      });\n};\n\n\n/**\n * Validates a request for finalizing phone MFA enrollment or sign-in.\n * @param {!Object} request The finalizePhoneMfaEnrollment or\n *     finalizePhoneMfaSignIn request data.\n * @private\n */\nfireauth.RpcHandler.validateFinalizePhoneMfaRequest_ = function(request) {\n  if (!request['phoneVerificationInfo']) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR);\n  }\n  if (!request['phoneVerificationInfo']['sessionInfo']) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.MISSING_SESSION_INFO);\n  }\n  if (!request['phoneVerificationInfo']['code']) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.MISSING_CODE);\n  }\n};\n\n\n/**\n * Requests finalizeMfaEnrollment endpoint to finish the enrollment flow for\n * phone MFA. It resolves with the MFA token response.\n * @param {!Object} request The enroll MFA request for phone.\n * @return {!goog.Promise<!Object>}\n */\nfireauth.RpcHandler.prototype.finalizePhoneMfaEnrollment = function(request) {\n  return this.invokeRpc(\n      fireauth.RpcHandler.ApiMethod.FINALIZE_PHONE_MFA_ENROLLMENT, request);\n};\n\n\n/**\n * Validates a request for starting phone MFA sign-in.\n * @param {!Object} request The startPhoneMfaSignIn request data.\n * @private\n */\nfireauth.RpcHandler.validateStartPhoneMfaSignInRequest_ = function(request) {\n  if (!request['phoneSignInInfo'] ||\n      !request['phoneSignInInfo']['recaptchaToken']) {\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.MISSING_APP_CREDENTIAL);\n  }\n};\n\n\n/**\n * Validates a response to a start phone MFA sign-in request.\n * @param {!Object} response The server response data.\n * @private\n */\nfireauth.RpcHandler.validateStartPhoneMfaSignInResponse_ = function(response) {\n  if (!response[fireauth.RpcHandler.AuthServerField.PHONE_RESPONSE_INFO] ||\n      !response[fireauth.RpcHandler.AuthServerField.PHONE_RESPONSE_INFO]\n          [fireauth.RpcHandler.AuthServerField.SESSION_INFO]) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR);\n  }\n};\n\n\n/**\n * Requests startMfaSignIn endpoint for verifying the user's ownership of\n * a phone number before signing in to MFA. It resolves with a sessionInfo\n * (verificationId) string.\n * @param {!Object} request The sign in MFA request for phone.\n * @return {!goog.Promise<string>}\n */\nfireauth.RpcHandler.prototype.startPhoneMfaSignIn = function(request) {\n  return this.invokeRpc(\n      fireauth.RpcHandler.ApiMethod.START_PHONE_MFA_SIGN_IN, request)\n      .then(function(response) {\n        // Extract the sessionInfo(verificationId) from response.\n        return response[fireauth.RpcHandler.AuthServerField.PHONE_RESPONSE_INFO]\n            [fireauth.RpcHandler.AuthServerField.SESSION_INFO];\n      });\n};\n\n\n/**\n * Requests finalizeMfaSignIn endpoint to finish the sign-in flow for\n * phone MFA. It resolves with the MFA token response.\n * @param {!Object} request The sign in MFA request for phone.\n * @return {!goog.Promise<!Object>}\n */\nfireauth.RpcHandler.prototype.finalizePhoneMfaSignIn = function(request) {\n  return this.invokeRpc(\n      fireauth.RpcHandler.ApiMethod.FINALIZE_PHONE_MFA_SIGN_IN, request);\n};\n\n\n/**\n * Validates the response for unenrolling a second factor. If the user is still\n * considered signed in, the endpoint returns new tokens. However, if the\n * user session is revoked no tokens will be returned.\n * @param {!Object} response The server response data.\n * @private\n */\nfireauth.RpcHandler.validateWithdrawMfaResponse_ = function(response) {\n  var hasIdToken = !!response[fireauth.RpcHandler.AuthServerField.ID_TOKEN];\n  var hasRefreshToken =\n      !!response[fireauth.RpcHandler.AuthServerField.REFRESH_TOKEN];\n\n  if (hasIdToken ^ hasRefreshToken) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR);\n  }\n};\n\n\n/**\n * Requests the withdraw endpoint to unenroll a second factor. Returns new\n * tokens if the user is still considered signed in or no tokens if the user\n * session is revoked and the user is being signed out.\n * @param {string} idToken The ID token.\n * @param {string} mfaEnrollmentId The MFA enrollment ID.\n * @return {!goog.Promise<!Object>}\n */\nfireauth.RpcHandler.prototype.withdrawMfa = function(idToken, mfaEnrollmentId) {\n  var request = {\n    'idToken': idToken,\n    'mfaEnrollmentId': mfaEnrollmentId\n  };\n  return this.invokeRpc(fireauth.RpcHandler.ApiMethod.WITHDRAW_MFA, request);\n};\n\n\n/**\n * The custom error map for reauth with verifyPhoneNumber.\n * @private {!fireauth.RpcHandler.ServerErrorMap}\n */\nfireauth.RpcHandler.verifyPhoneNumberForExistingErrorMap_ = {};\n\n// For most RPCs, the backend error USER_NOT_FOUND means that the sent STS\n// token is invalid. However, for this specific case, USER_NOT_FOUND actually\n// means that the sent credential is invalid.\nfireauth.RpcHandler.verifyPhoneNumberForExistingErrorMap_[\n  fireauth.RpcHandler.ServerError.USER_NOT_FOUND] =\n    fireauth.authenum.Error.USER_DELETED;\n\n\n/**\n * Validates a request to deleteLinkedAccounts.\n * @param {?Object} request\n * @private\n */\nfireauth.RpcHandler.validateDeleteLinkedAccountsRequest_ = function(request) {\n  if (!Array.isArray(request['deleteProvider'])) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR);\n  }\n};\n\n\n/**\n * Updates the providers for the account associated with the idToken.\n * @param {string} idToken The ID token.\n * @param {!Array<string>} providersToDelete The array of providers to delete.\n * @return {!goog.Promise<!Object>}\n */\nfireauth.RpcHandler.prototype.deleteLinkedAccounts =\n    function(idToken, providersToDelete) {\n  var request = {\n    'idToken': idToken,\n    'deleteProvider': providersToDelete\n  };\n  return this.invokeRpc(fireauth.RpcHandler.ApiMethod.DELETE_LINKED_ACCOUNTS,\n      request);\n};\n\n\n/**\n * Validates a verifyAssertion request.\n * @param {?Object} request The verifyAssertion request data.\n * @private\n */\nfireauth.RpcHandler.validateVerifyAssertionRequest_ = function(request) {\n  // Either (requestUri and sessionId), (requestUri and postBody) or\n  // (requestUri and pendingToken) are required.\n  if (!request[fireauth.RpcHandler.AuthServerField.REQUEST_URI] ||\n      (!request[fireauth.RpcHandler.AuthServerField.SESSION_ID] &&\n       !request[fireauth.RpcHandler.AuthServerField.POST_BODY] &&\n       !request[fireauth.RpcHandler.AuthServerField.PENDING_TOKEN])) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR);\n  }\n};\n\n\n/**\n * Processes the verifyAssertion response and injects the same raw nonce\n * if available in request.\n * @param {!Object} request The verifyAssertion request data.\n * @param {!Object} response The original verifyAssertion response data.\n * @return {!Object} The modified verifyAssertion response.\n * @private\n */\nfireauth.RpcHandler.processVerifyAssertionResponse_ =\n    function(request, response) {\n  // This makes it possible for OIDC providers to:\n  // 1. Initialize an OIDC Auth credential on successful response.\n  // 2. Initialize an OIDC Auth credential within the recovery error.\n\n  // When request has sessionId and response has OIDC ID token and no pending\n  // token, a credential with raw nonce and OIDC ID token needs to be returned.\n  if (response[fireauth.RpcHandler.AuthServerField.OAUTH_ID_TOKEN] &&\n      response[fireauth.RpcHandler.AuthServerField.PROVIDER_ID] &&\n      response[fireauth.RpcHandler.AuthServerField.PROVIDER_ID]\n          .indexOf(fireauth.constants.OIDC_PREFIX) == 0 &&\n      // Use pendingToken instead of idToken and rawNonce when available.\n      !response[fireauth.RpcHandler.AuthServerField.PENDING_TOKEN]) {\n    if (request[fireauth.RpcHandler.AuthServerField.SESSION_ID]) {\n      // For full OAuth flow, the nonce is in the session ID.\n      response[fireauth.RpcHandler.InjectedResponseField.NONCE] =\n          request[fireauth.RpcHandler.AuthServerField.SESSION_ID];\n    } else if (request[fireauth.RpcHandler.AuthServerField.POST_BODY]) {\n      // For credential flow, the nonce is in the postBody nonce field.\n      var queryData = new goog.Uri.QueryData(\n          request[fireauth.RpcHandler.AuthServerField.POST_BODY]);\n      if (queryData.containsKey(\n              fireauth.RpcHandler.InjectedResponseField.NONCE)) {\n        response[fireauth.RpcHandler.InjectedResponseField.NONCE] =\n            queryData.get(fireauth.RpcHandler.InjectedResponseField.NONCE);\n      }\n    }\n  }\n  return response;\n};\n\n\n/**\n * Validates a response from verifyAssertionForExisting.\n * @param {?Object} response The verifyAssertionForExisting response data.\n * @private\n */\nfireauth.RpcHandler.validateVerifyAssertionForExistingResponse_ =\n    function(response) {\n  // When returnIdpCredential is set to true and the account is new, no error\n  // is thrown but an errorMessage is added to the response. No idToken is\n  // passed.\n  if (response[fireauth.RpcHandler.AuthServerField.ERROR_MESSAGE] &&\n      response[fireauth.RpcHandler.AuthServerField.ERROR_MESSAGE] ==\n          fireauth.RpcHandler.ServerError.USER_NOT_FOUND) {\n    // This corresponds to user-not-found.\n    throw new fireauth.AuthError(fireauth.authenum.Error.USER_DELETED);\n  } else if (response[fireauth.RpcHandler.AuthServerField.ERROR_MESSAGE]) {\n    // Construct developer facing error message from server code in errorMessage\n    // field.\n    throw fireauth.RpcHandler.getDeveloperErrorFromCode_(\n        response[fireauth.RpcHandler.AuthServerField.ERROR_MESSAGE]);\n  }\n  // Need confirmation should not be returned when do not create new user flag\n  // is set.\n  // Validate if ID token or multi-factor pending credential is available.\n  fireauth.RpcHandler.validateIdTokenResponse_(response);\n};\n\n\n/**\n * Validates a response from verifyAssertion.\n * @param {?Object} response The verifyAssertion response data.\n * @private\n */\nfireauth.RpcHandler.validateVerifyAssertionResponse_ = function(response) {\n  var error = null;\n  if (response[fireauth.RpcHandler.AuthServerField.NEED_CONFIRMATION]) {\n    // Account linking required, previously logged in to another account\n    // with same email. User must authenticate they are owners of the\n    // first account.\n    // If enough info for Auth linking error, throw an instance of Auth linking\n    // error. This will be used by developer after reauthenticating with email\n    // provided by error to link using the credentials in Auth linking error.\n    // If missing information, return regular Auth error.\n    response['code'] = fireauth.authenum.Error.NEED_CONFIRMATION;\n    error = fireauth.AuthErrorWithCredential.fromPlainObject(response);\n  } else if (response[fireauth.RpcHandler.AuthServerField.ERROR_MESSAGE] ==\n             fireauth.RpcHandler.ServerError.FEDERATED_USER_ID_ALREADY_LINKED) {\n    // When FEDERATED_USER_ID_ALREADY_LINKED returned in error message, auth\n    // credential and email will also be returned, throw relevant error in that\n    // case.\n    // In this case the developer needs to signInWithCredential to the returned\n    // credentials.\n    response['code'] = fireauth.authenum.Error.CREDENTIAL_ALREADY_IN_USE;\n    error = fireauth.AuthErrorWithCredential.fromPlainObject(response);\n  } else if (response[fireauth.RpcHandler.AuthServerField.ERROR_MESSAGE] ==\n             fireauth.RpcHandler.ServerError.EMAIL_EXISTS) {\n    // When EMAIL_EXISTS returned in error message, Auth credential and email\n    // will also be returned, throw relevant error in that case.\n    // In this case, the developers needs to sign in the user to the original\n    // owner of the account and then link to the returned credential here.\n    response['code'] = fireauth.authenum.Error.EMAIL_EXISTS;\n    error = fireauth.AuthErrorWithCredential.fromPlainObject(response);\n  } else if (response[fireauth.RpcHandler.AuthServerField.ERROR_MESSAGE]) {\n    // Construct developer facing error message from server code in errorMessage\n    // field.\n    error = fireauth.RpcHandler.getDeveloperErrorFromCode_(\n        response[fireauth.RpcHandler.AuthServerField.ERROR_MESSAGE]);\n  }\n  // If error found, throw it.\n  if (error) {\n    throw error;\n  }\n  // Validate if ID token or multi-factor pending credential is available.\n  fireauth.RpcHandler.validateIdTokenResponse_(response);\n};\n\n\n/**\n * Validates a verifyAssertion with linking request.\n * @param {?Object} request The verifyAssertion request data.\n * @private\n */\nfireauth.RpcHandler.validateVerifyAssertionLinkRequest_ = function(request) {\n  // idToken with either (requestUri and sessionId) or (requestUri and postBody)\n  // are required.\n  fireauth.RpcHandler.validateVerifyAssertionRequest_(request);\n  if (!request['idToken']) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR);\n  }\n};\n\n\n/**\n * @typedef {{\n *   autoCreate: (boolean|undefined),\n *   requestUri: string,\n *   postBody: (?string|undefined),\n *   pendingIdToken: (?string|undefined),\n *   sessionId: (?string|undefined),\n *   idToken: (?string|undefined),\n *   returnIdpCredential: (boolean|undefined),\n *   tenantId: (?string|undefined)\n * }}\n */\nfireauth.RpcHandler.VerifyAssertionData;\n\n\n/**\n * Requests verifyAssertion endpoint. When resolved, promise returns the whole\n * response.\n * @param {!fireauth.RpcHandler.VerifyAssertionData} request\n * @return {!goog.Promise<!Object>}\n */\nfireauth.RpcHandler.prototype.verifyAssertion = function(request) {\n  // Force Auth credential to be returned on the following errors:\n  // FEDERATED_USER_ID_ALREADY_LINKED\n  // EMAIL_EXISTS\n  request['returnIdpCredential'] = true;\n  return this.invokeRpc(\n      fireauth.RpcHandler.ApiMethod.VERIFY_ASSERTION,\n      request);\n};\n\n\n/**\n * Requests verifyAssertion endpoint for federated account linking. When\n * resolved, promise returns the whole response.\n * @param {!fireauth.RpcHandler.VerifyAssertionData} request\n * @return {!goog.Promise<!Object>}\n */\nfireauth.RpcHandler.prototype.verifyAssertionForLinking = function(request) {\n  // Force Auth credential to be returned on the following errors:\n  // FEDERATED_USER_ID_ALREADY_LINKED\n  // EMAIL_EXISTS\n  request['returnIdpCredential'] = true;\n  return this.invokeRpc(\n      fireauth.RpcHandler.ApiMethod.VERIFY_ASSERTION_FOR_LINKING,\n      request);\n};\n\n\n/**\n * Requests verifyAssertion endpoint for an existing federated account. When\n * resolved, promise returns the whole response. If not existing, a\n * user-not-found error is thrown.\n * @param {!fireauth.RpcHandler.VerifyAssertionData} request\n * @return {!goog.Promise<!Object>}\n */\nfireauth.RpcHandler.prototype.verifyAssertionForExisting = function(request) {\n  // Since we are setting returnIdpCredential to true, a response will be\n  // returned even though the account doesn't exist but an error message is\n  // appended with value set to USER_NOT_FOUND. If this flag is not passed, only\n  // the USER_NOT_FOUND error is thrown without any response.\n  request['returnIdpCredential'] = true;\n  // Do not create a new account if the user doesn't exist.\n  request['autoCreate'] = false;\n  return this.invokeRpc(\n      fireauth.RpcHandler.ApiMethod.VERIFY_ASSERTION_FOR_EXISTING,\n      request);\n};\n\n\n/**\n * Validates a request that should contain an action code.\n * @param {!Object} request\n * @private\n */\nfireauth.RpcHandler.validateApplyActionCodeRequest_ = function(request) {\n  if (!request['oobCode']) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INVALID_OOB_CODE);\n  }\n};\n\n\n/**\n * Validates that a checkActionCode response contains the email and requestType\n * fields.\n * @param {!Object} response The raw response returned by the server.\n * @private\n */\nfireauth.RpcHandler.validateCheckActionCodeResponse_ = function(response) {\n  // If the code is invalid, usually a clear error would be returned.\n  // In this case, something unexpected happened.\n  // Email could be empty only if the request type is EMAIL_SIGNIN.\n  var operation = response['requestType'];\n  if (!operation ||\n      (!response['email'] && operation != 'EMAIL_SIGNIN' &&\n       operation != 'VERIFY_AND_CHANGE_EMAIL')) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR);\n  }\n};\n\n\n/**\n * Requests resetPassword endpoint for password reset, returns promise that\n * resolves with user's email.\n * @param {string} code The email action code to confirm for password reset.\n * @param {string} newPassword The new password.\n * @return {!goog.Promise<string>}\n */\nfireauth.RpcHandler.prototype.confirmPasswordReset =\n    function(code, newPassword) {\n  var request = {\n    'oobCode': code,\n    'newPassword': newPassword\n  };\n  return this.invokeRpc(fireauth.RpcHandler.ApiMethod.RESET_PASSWORD, request);\n};\n\n\n/**\n * Checks the validity of an email action code and returns the response\n * received.\n * @param {string} code The email action code to check.\n * @return {!goog.Promise<!Object>}\n */\nfireauth.RpcHandler.prototype.checkActionCode = function(code) {\n  var request = {\n    'oobCode': code\n  };\n  return this.invokeRpc(\n      fireauth.RpcHandler.ApiMethod.CHECK_ACTION_CODE, request);\n};\n\n\n/**\n * Applies an out-of-band email action code, such as an email verification code.\n * @param {string} code The email action code.\n * @return {!goog.Promise<string>} A promise that resolves with the user's\n *     email.\n */\nfireauth.RpcHandler.prototype.applyActionCode = function(code) {\n  var request = {\n    'oobCode': code\n  };\n  return this.invokeRpc(\n      fireauth.RpcHandler.ApiMethod.APPLY_OOB_CODE, request);\n};\n\n\n/**\n * The specification of an RPC call. The fields are:\n * <ul>\n * <li>cachebuster: defines whether to send a unique string with request to\n *     force the backend to return an uncached response to request.\n * <li>customErrorMap: A map of backend error codes to client-side errors.\n *     Any entries set here override the default handling of the backend error\n *     code.\n * <li>endpoint: defines the backend endpoint to call.\n * <li>httpMethod: defines the HTTP method to use, defaulting to POST if not\n *     specified.\n * <li>requestRequiredFields: an array of the fields that are required in the\n *     request. The RPC call will fail with an INTERNAL_ERROR error if a\n *     required field is not present or if it is null, undefined, or the empty\n *     string.\n * <li>requestValidator: a function that takes in the request object and throws\n *     an error if the request is invalid.\n * <li>responsePreprocessor: a function to modify the response before running\n *     validation. The function takes in the request and response object.\n * <li>responseValidator: a function that takes in the response object and\n *     throws an error if the response is invalid.\n * <li>responseField: the field of the response object that will be returned\n *     from the RPC call. If no field is specified, the entire response object\n *     will be returned.\n * <li>returnSecureToken: Set to true to explicitly request STS tokens instead\n *     of legacy Google Identity Toolkit tokens from the backend.\n * <li>requireTenantId: Set to true to send tenant ID to backend in the request.\n * <li>useIdentityPlatformEndpoint: Whether to use new identity platform\n *     endpoints. The default is false.\n * </ul>\n * @typedef {{\n *   cachebuster: (boolean|undefined),\n *   customErrorMap: (!fireauth.RpcHandler.ServerErrorMap|undefined),\n *   endpoint: string,\n *   httpMethod: (!fireauth.RpcHandler.HttpMethod|undefined),\n *   requestRequiredFields: (!Array<string>|undefined),\n *   requestValidator: (function(!Object):void|undefined),\n *   responsePreprocessor: ((function(!Object, !Object):!Object)|undefined),\n *   responseValidator: (function(!Object):void|undefined),\n *   responseField: (string|undefined),\n *   returnSecureToken: (boolean|undefined),\n *   requireTenantId: (boolean|undefined),\n *   useIdentityPlatformEndpoint: (boolean|undefined)\n * }}\n */\nfireauth.RpcHandler.ApiMethodHandler;\n\n\n/**\n * The specifications for the backend API methods.\n * @enum {!fireauth.RpcHandler.ApiMethodHandler}\n */\nfireauth.RpcHandler.ApiMethod = {\n  APPLY_OOB_CODE: {\n    endpoint: 'setAccountInfo',\n    requestValidator: fireauth.RpcHandler.validateApplyActionCodeRequest_,\n    responseField: fireauth.RpcHandler.AuthServerField.EMAIL,\n    requireTenantId: true\n  },\n  CHECK_ACTION_CODE: {\n    endpoint: 'resetPassword',\n    requestValidator: fireauth.RpcHandler.validateApplyActionCodeRequest_,\n    responseValidator: fireauth.RpcHandler.validateCheckActionCodeResponse_,\n    requireTenantId: true\n  },\n  CREATE_ACCOUNT: {\n    endpoint: 'signupNewUser',\n    requestValidator: fireauth.RpcHandler.validateCreateAccountRequest_,\n    responseValidator: fireauth.RpcHandler.validateIdTokenResponse_,\n    returnSecureToken: true,\n    requireTenantId: true\n  },\n  CREATE_AUTH_URI: {\n    endpoint: 'createAuthUri',\n    requireTenantId: true\n  },\n  DELETE_ACCOUNT: {\n    endpoint: 'deleteAccount',\n    requestRequiredFields: ['idToken']\n  },\n  DELETE_LINKED_ACCOUNTS: {\n    endpoint: 'setAccountInfo',\n    requestRequiredFields: ['idToken', 'deleteProvider'],\n    requestValidator: fireauth.RpcHandler.validateDeleteLinkedAccountsRequest_\n  },\n  EMAIL_LINK_SIGNIN: {\n    endpoint: 'emailLinkSignin',\n    requestRequiredFields: ['email', 'oobCode'],\n    requestValidator: fireauth.RpcHandler.validateRequestHasEmail_,\n    responseValidator: fireauth.RpcHandler.validateIdTokenResponse_,\n    returnSecureToken: true,\n    requireTenantId: true\n  },\n  EMAIL_LINK_SIGNIN_FOR_LINKING: {\n    endpoint: 'emailLinkSignin',\n    requestRequiredFields: ['idToken', 'email', 'oobCode'],\n    requestValidator: fireauth.RpcHandler.validateRequestHasEmail_,\n    responseValidator: fireauth.RpcHandler.validateIdTokenResponse_,\n    returnSecureToken: true\n  },\n  FINALIZE_PHONE_MFA_ENROLLMENT: {\n    endpoint: 'accounts/mfaEnrollment:finalize',\n    requestRequiredFields: ['idToken', 'phoneVerificationInfo'],\n    requestValidator:\n        fireauth.RpcHandler.validateFinalizePhoneMfaRequest_,\n    responseValidator: fireauth.RpcHandler.validateIdTokenResponse_,\n    requireTenantId: true,\n    useIdentityPlatformEndpoint: true\n  },\n  FINALIZE_PHONE_MFA_SIGN_IN: {\n    endpoint: 'accounts/mfaSignIn:finalize',\n    requestRequiredFields: ['mfaPendingCredential', 'phoneVerificationInfo'],\n    requestValidator:\n        fireauth.RpcHandler.validateFinalizePhoneMfaRequest_,\n    responseValidator: fireauth.RpcHandler.validateIdTokenResponse_,\n    requireTenantId: true,\n    useIdentityPlatformEndpoint: true\n  },\n  GET_ACCOUNT_INFO: {\n    endpoint: 'getAccountInfo'\n  },\n  GET_AUTH_URI: {\n    endpoint: 'createAuthUri',\n    requestValidator: fireauth.RpcHandler.validateGetAuthUriRequest_,\n    responseValidator: fireauth.RpcHandler.validateGetAuthResponse_,\n    requireTenantId: true\n  },\n  GET_EMAIL_SIGNIN_CODE: {\n    endpoint: 'getOobConfirmationCode',\n    requestRequiredFields: ['requestType'],\n    requestValidator: fireauth.RpcHandler.validateEmailSignInCodeRequest_,\n    responseField: fireauth.RpcHandler.AuthServerField.EMAIL,\n    requireTenantId: true\n  },\n  GET_EMAIL_VERIFICATION_CODE: {\n    endpoint: 'getOobConfirmationCode',\n    requestRequiredFields: ['idToken', 'requestType'],\n    requestValidator: fireauth.RpcHandler.validateEmailVerificationCodeRequest_,\n    responseField: fireauth.RpcHandler.AuthServerField.EMAIL,\n    requireTenantId: true\n  },\n  GET_EMAIL_VERIFICATION_CODE_BEFORE_UPDATE: {\n    endpoint: 'getOobConfirmationCode',\n    requestRequiredFields: ['idToken', 'newEmail', 'requestType'],\n    requestValidator:\n        fireauth.RpcHandler.validateEmailVerificationCodeBeforeUpdateRequest_,\n    responseField: fireauth.RpcHandler.AuthServerField.EMAIL,\n    requireTenantId: true\n  },\n  GET_OOB_CODE: {\n    endpoint: 'getOobConfirmationCode',\n    requestRequiredFields: ['requestType'],\n    requestValidator: fireauth.RpcHandler.validateOobCodeRequest_,\n    responseField: fireauth.RpcHandler.AuthServerField.EMAIL,\n    requireTenantId: true\n  },\n  GET_PROJECT_CONFIG: {\n    // Microsoft edge caching bug. There are two getProjectConfig API calls,\n    // first from top level window and then from iframe. The second call has a\n    // response of 304 which means it's a cached response. We suspect the call\n    // from iframe is reusing the response from the first call and checks the\n    // allowed origin in the cached response, which only contains the domain for\n    // the top level window.\n    cachebuster: true,\n    endpoint: 'getProjectConfig',\n    httpMethod: fireauth.RpcHandler.HttpMethod.GET\n  },\n  GET_RECAPTCHA_PARAM: {\n    cachebuster: true,\n    endpoint: 'getRecaptchaParam',\n    httpMethod: fireauth.RpcHandler.HttpMethod.GET,\n    responseValidator: fireauth.RpcHandler.validateGetRecaptchaParamResponse_\n  },\n  RESET_PASSWORD: {\n    endpoint: 'resetPassword',\n    requestValidator: fireauth.RpcHandler.validateApplyActionCodeRequest_,\n    responseField: fireauth.RpcHandler.AuthServerField.EMAIL,\n    requireTenantId: true\n  },\n  RETURN_DYNAMIC_LINK: {\n    cachebuster: true,\n    endpoint: 'getProjectConfig',\n    httpMethod: fireauth.RpcHandler.HttpMethod.GET,\n    responseField: fireauth.RpcHandler.AuthServerField.DYNAMIC_LINKS_DOMAIN\n  },\n  SEND_VERIFICATION_CODE: {\n    endpoint: 'sendVerificationCode',\n    // Currently only reCAPTCHA tokens supported.\n    requestRequiredFields: ['phoneNumber', 'recaptchaToken'],\n    responseField: fireauth.RpcHandler.AuthServerField.SESSION_INFO,\n    requireTenantId: true\n  },\n  SET_ACCOUNT_INFO: {\n    endpoint: 'setAccountInfo',\n    requestRequiredFields: ['idToken'],\n    requestValidator: fireauth.RpcHandler.validateEmailIfPresent_,\n    returnSecureToken: true // Maybe updating email will invalidate token in the\n                            // future, this will prevent breaking the client.\n  },\n  SET_ACCOUNT_INFO_SENSITIVE: {\n    endpoint: 'setAccountInfo',\n    requestRequiredFields: ['idToken'],\n    requestValidator: fireauth.RpcHandler.validateSetAccountInfoSensitive_,\n    responseValidator: fireauth.RpcHandler.validateIdTokenResponse_,\n    returnSecureToken: true // Updating password will send back new sts tokens.\n  },\n  SIGN_IN_ANONYMOUSLY: {\n    endpoint: 'signupNewUser',\n    responseValidator: fireauth.RpcHandler.validateIdTokenResponse_,\n    returnSecureToken: true,\n    requireTenantId: true\n  },\n  START_PHONE_MFA_ENROLLMENT: {\n    endpoint: 'accounts/mfaEnrollment:start',\n    requestRequiredFields: ['idToken', 'phoneEnrollmentInfo'],\n    requestValidator:\n        fireauth.RpcHandler.validateStartPhoneMfaEnrollmentRequest_,\n    responseValidator:\n        fireauth.RpcHandler.validateStartPhoneMfaEnrollmentResponse_,\n    requireTenantId: true,\n    useIdentityPlatformEndpoint: true\n  },\n  START_PHONE_MFA_SIGN_IN: {\n    endpoint: 'accounts/mfaSignIn:start',\n    requestRequiredFields: ['mfaPendingCredential', 'mfaEnrollmentId',\n                            'phoneSignInInfo'],\n    requestValidator:\n        fireauth.RpcHandler.validateStartPhoneMfaSignInRequest_,\n    responseValidator:\n        fireauth.RpcHandler.validateStartPhoneMfaSignInResponse_,\n    requireTenantId: true,\n    useIdentityPlatformEndpoint: true\n  },\n  VERIFY_ASSERTION: {\n    endpoint: 'verifyAssertion',\n    requestValidator: fireauth.RpcHandler.validateVerifyAssertionRequest_,\n    responsePreprocessor: fireauth.RpcHandler.processVerifyAssertionResponse_,\n    responseValidator: fireauth.RpcHandler.validateVerifyAssertionResponse_,\n    returnSecureToken: true,\n    // Tenant ID is required for this endpoint. But for\n    // signInWithRedirect/Popup APIs, to make createAuthUri call and\n    // verifyAssertion call atomic, the tenant ID on RPC handler will be\n    // overridden by the tenant ID passed directly from the\n    // request, which is retrieved from Auth event. For signInWithCredential\n    // API, the tenant ID will still be retrieved from the RPC handler.\n    requireTenantId: true\n  },\n  VERIFY_ASSERTION_FOR_EXISTING: {\n    endpoint: 'verifyAssertion',\n    requestValidator: fireauth.RpcHandler.validateVerifyAssertionRequest_,\n    responsePreprocessor: fireauth.RpcHandler.processVerifyAssertionResponse_,\n    responseValidator:\n        fireauth.RpcHandler.validateVerifyAssertionForExistingResponse_,\n    returnSecureToken: true,\n    // Tenant ID is required for this endpoint. But for\n    // reauthenticateWithRedirect/Popup APIs, to make createAuthUri call and\n    // verifyAssertion call atomic, the tenant ID on RPC handler will be\n    // overridden by the tenant ID passed directly from the\n    // request, which is retrieved from Auth event. For\n    // reauthenticateWithCredential API, the tenant ID will still be retrieved\n    // from the RPC handler.\n    requireTenantId: true\n  },\n  VERIFY_ASSERTION_FOR_LINKING: {\n    endpoint: 'verifyAssertion',\n    requestValidator: fireauth.RpcHandler.validateVerifyAssertionLinkRequest_,\n    responsePreprocessor: fireauth.RpcHandler.processVerifyAssertionResponse_,\n    responseValidator: fireauth.RpcHandler.validateVerifyAssertionResponse_,\n    returnSecureToken: true\n  },\n  VERIFY_CUSTOM_TOKEN: {\n    endpoint: 'verifyCustomToken',\n    requestValidator: fireauth.RpcHandler.validateVerifyCustomTokenRequest_,\n    responseValidator: fireauth.RpcHandler.validateIdTokenResponse_,\n    returnSecureToken: true,\n    requireTenantId: true\n  },\n  VERIFY_PASSWORD: {\n    endpoint: 'verifyPassword',\n    requestValidator: fireauth.RpcHandler.validateVerifyPasswordRequest_,\n    responseValidator: fireauth.RpcHandler.validateIdTokenResponse_,\n    returnSecureToken: true,\n    requireTenantId: true\n  },\n  VERIFY_PHONE_NUMBER: {\n    endpoint: 'verifyPhoneNumber',\n    requestValidator: fireauth.RpcHandler.validateVerifyPhoneNumberRequest_,\n    responseValidator: fireauth.RpcHandler.validateIdTokenResponse_,\n    requireTenantId: true\n  },\n  VERIFY_PHONE_NUMBER_FOR_LINKING: {\n    endpoint: 'verifyPhoneNumber',\n    requestValidator: fireauth.RpcHandler.validateVerifyPhoneNumberLinkRequest_,\n    responseValidator:\n        fireauth.RpcHandler.validateVerifyPhoneNumberForLinkingResponse_\n  },\n  VERIFY_PHONE_NUMBER_FOR_EXISTING: {\n    customErrorMap: fireauth.RpcHandler.verifyPhoneNumberForExistingErrorMap_,\n    endpoint: 'verifyPhoneNumber',\n    requestValidator: fireauth.RpcHandler.validateVerifyPhoneNumberRequest_,\n    responseValidator: fireauth.RpcHandler.validateIdTokenResponse_,\n    requireTenantId: true\n  },\n  WITHDRAW_MFA: {\n    endpoint: 'accounts/mfaEnrollment:withdraw',\n    requestRequiredFields: ['idToken', 'mfaEnrollmentId'],\n    responseValidator: fireauth.RpcHandler.validateWithdrawMfaResponse_,\n    requireTenantId: true,\n    useIdentityPlatformEndpoint: true\n  }\n};\n\n\n/**\n * @const {string} The parameter to send to the backend to specify that the\n *     client accepts STS tokens directly from Firebear backends.\n * @private\n */\nfireauth.RpcHandler.USE_STS_TOKEN_PARAM_ = 'returnSecureToken';\n\n\n/**\n * @const {string} The parameter to send to the backend to specify the tenant\n *     ID.\n * @private\n */\nfireauth.RpcHandler.TENANT_ID_PARAM_ = 'tenantId';\n\n\n/**\n * Invokes an RPC method according to the specification defined by\n * {@code fireauth.RpcHandler.ApiMethod}.\n * @param {!fireauth.RpcHandler.ApiMethod} method The method to invoke.\n * @param {!Object} request The input data to the method.\n * @return {!goog.Promise} A promise that resolves with the results of the RPC.\n *     The format of the results can be modified in\n *     {@code fireauth.RpcHandler.ApiMethod}.\n */\nfireauth.RpcHandler.prototype.invokeRpc = function(method, request) {\n  if (!fireauth.object.hasNonEmptyFields(\n      request, method.requestRequiredFields)) {\n    return goog.Promise.reject(new fireauth.AuthError(\n        fireauth.authenum.Error.INTERNAL_ERROR));\n  }\n\n  var useIdentityPlatformEndpoint = !!method.useIdentityPlatformEndpoint;\n  var httpMethod = method.httpMethod || fireauth.RpcHandler.HttpMethod.POST;\n  var self = this;\n  var response;\n  return goog.Promise.resolve(request)\n      .then(method.requestValidator)\n      .then(function() {\n        if (method.returnSecureToken) {\n          // Signal that the client accepts STS tokens, for the legacy Google\n          // Identity Toolkit token to STS token migration.\n          request[fireauth.RpcHandler.USE_STS_TOKEN_PARAM_] = true;\n        }\n        // If tenant ID is explicitly passed in the request, it will override\n        // the tenant ID on RPC handler.\n        if (method.requireTenantId && self.tenantId_ &&\n            (typeof request[fireauth.RpcHandler.TENANT_ID_PARAM_] ===\n             'undefined')) {\n          request[fireauth.RpcHandler.TENANT_ID_PARAM_] = self.tenantId_;\n        }\n        return useIdentityPlatformEndpoint ?\n            self.requestIdentityPlatformEndpoint(method.endpoint, httpMethod,\n                request, method.customErrorMap, method.cachebuster || false) :\n            self.requestFirebaseEndpoint(method.endpoint, httpMethod,\n                request, method.customErrorMap, method.cachebuster || false);\n      })\n      .then(function(tempResponse) {\n        response = tempResponse;\n        // If response processor is available, pass request and response through\n        // it. Modifications would be made using response reference.\n        if (method.responsePreprocessor) {\n          return method.responsePreprocessor(request, response);\n        }\n        return response;\n      })\n      .then(method.responseValidator)\n      .then(function() {\n        if (!method.responseField) {\n          return response;\n        }\n        if (!(method.responseField in response)) {\n          throw new fireauth.AuthError(\n              fireauth.authenum.Error.INTERNAL_ERROR);\n        }\n        return response[method.responseField];\n      });\n};\n\n\n/**\n * Checks if the server response contains errors.\n * @param {!Object} resp The API response.\n * @return {boolean} {@code true} if the response contains errors.\n * @private\n */\nfireauth.RpcHandler.hasError_ = function(resp) {\n  return !!resp['error'];\n};\n\n\n/**\n * Returns the developer facing error corresponding to the server code provided.\n * @param {string} serverErrorCode The server error message.\n * @return {!fireauth.AuthError} The corresponding error object.\n * @private\n */\nfireauth.RpcHandler.getDeveloperErrorFromCode_ = function(serverErrorCode) {\n  // Encapsulate the server error code in a typical server error response with\n  // the code populated within. This will convert the response to a developer\n  // facing one.\n  return fireauth.RpcHandler.getDeveloperError_({\n    'error': {\n      'errors': [\n        {\n          'message': serverErrorCode\n        }\n      ],\n      'code': 400,\n      'message': serverErrorCode\n    }\n  });\n};\n\n\n/**\n * Converts a server response with errors to a developer-facing AuthError.\n * @param {!Object} response The server response.\n * @param {?fireauth.RpcHandler.ServerErrorMap=} opt_customErrorMap A map of\n *     backend error codes to client-side errors. Any entries set here\n *     override the default handling of the backend error code.\n * @return {!fireauth.AuthError} The corresponding error object.\n * @private\n */\nfireauth.RpcHandler.getDeveloperError_ =\n    function(response, opt_customErrorMap) {\n  var errorMessage;\n  var apiaryError = fireauth.RpcHandler.getApiaryError_(response);\n  if (apiaryError) {\n    return apiaryError;\n  }\n\n  var serverErrorCode = fireauth.RpcHandler.getErrorCode_(response);\n\n  var errorMap = {};\n\n  // Custom token errors.\n  errorMap[fireauth.RpcHandler.ServerError.INVALID_CUSTOM_TOKEN] =\n      fireauth.authenum.Error.INVALID_CUSTOM_TOKEN;\n  errorMap[fireauth.RpcHandler.ServerError.CREDENTIAL_MISMATCH] =\n      fireauth.authenum.Error.CREDENTIAL_MISMATCH;\n  // This can only happen if the SDK sends a bad request.\n  errorMap[fireauth.RpcHandler.ServerError.MISSING_CUSTOM_TOKEN] =\n      fireauth.authenum.Error.INTERNAL_ERROR;\n\n  // Create Auth URI errors.\n  errorMap[fireauth.RpcHandler.ServerError.INVALID_IDENTIFIER] =\n      fireauth.authenum.Error.INVALID_EMAIL;\n  // This can only happen if the SDK sends a bad request.\n  errorMap[fireauth.RpcHandler.ServerError.MISSING_CONTINUE_URI] =\n      fireauth.authenum.Error.INTERNAL_ERROR;\n\n  // Sign in with email and password errors (some apply to sign up too).\n  errorMap[fireauth.RpcHandler.ServerError.INVALID_EMAIL] =\n      fireauth.authenum.Error.INVALID_EMAIL;\n  errorMap[fireauth.RpcHandler.ServerError.INVALID_PASSWORD] =\n      fireauth.authenum.Error.INVALID_PASSWORD;\n  errorMap[fireauth.RpcHandler.ServerError.USER_DISABLED] =\n      fireauth.authenum.Error.USER_DISABLED;\n  // This can only happen if the SDK sends a bad request.\n  errorMap[fireauth.RpcHandler.ServerError.MISSING_PASSWORD] =\n      fireauth.authenum.Error.INTERNAL_ERROR;\n\n  // Sign up with email and password errors.\n  errorMap[fireauth.RpcHandler.ServerError.EMAIL_EXISTS] =\n      fireauth.authenum.Error.EMAIL_EXISTS;\n  errorMap[fireauth.RpcHandler.ServerError.PASSWORD_LOGIN_DISABLED] =\n      fireauth.authenum.Error.OPERATION_NOT_ALLOWED;\n\n  // Verify assertion for sign in with credential errors:\n  errorMap[fireauth.RpcHandler.ServerError.INVALID_IDP_RESPONSE] =\n      fireauth.authenum.Error.INVALID_IDP_RESPONSE;\n  errorMap[fireauth.RpcHandler.ServerError.INVALID_PENDING_TOKEN] =\n      fireauth.authenum.Error.INVALID_IDP_RESPONSE;\n  errorMap[fireauth.RpcHandler.ServerError.FEDERATED_USER_ID_ALREADY_LINKED] =\n      fireauth.authenum.Error.CREDENTIAL_ALREADY_IN_USE;\n  errorMap[fireauth.RpcHandler.ServerError.MISSING_OR_INVALID_NONCE] =\n      fireauth.authenum.Error.MISSING_OR_INVALID_NONCE;\n\n  // Email template errors while sending emails:\n  errorMap[fireauth.RpcHandler.ServerError.INVALID_MESSAGE_PAYLOAD] =\n      fireauth.authenum.Error.INVALID_MESSAGE_PAYLOAD;\n  errorMap[fireauth.RpcHandler.ServerError.INVALID_RECIPIENT_EMAIL] =\n      fireauth.authenum.Error.INVALID_RECIPIENT_EMAIL;\n  errorMap[fireauth.RpcHandler.ServerError.INVALID_SENDER] =\n      fireauth.authenum.Error.INVALID_SENDER;\n\n  // Send Password reset email errors:\n  errorMap[fireauth.RpcHandler.ServerError.EMAIL_NOT_FOUND] =\n      fireauth.authenum.Error.USER_DELETED;\n  errorMap[fireauth.RpcHandler.ServerError.RESET_PASSWORD_EXCEED_LIMIT] =\n      fireauth.authenum.Error.TOO_MANY_ATTEMPTS_TRY_LATER;\n\n  // Reset password errors:\n  errorMap[fireauth.RpcHandler.ServerError.EXPIRED_OOB_CODE] =\n      fireauth.authenum.Error.EXPIRED_OOB_CODE;\n  errorMap[fireauth.RpcHandler.ServerError.INVALID_OOB_CODE] =\n      fireauth.authenum.Error.INVALID_OOB_CODE;\n  // This can only happen if the SDK sends a bad request.\n  errorMap[fireauth.RpcHandler.ServerError.MISSING_OOB_CODE] =\n      fireauth.authenum.Error.INTERNAL_ERROR;\n\n  // Get Auth URI errors:\n  errorMap[fireauth.RpcHandler.ServerError.INVALID_PROVIDER_ID] =\n      fireauth.authenum.Error.INVALID_PROVIDER_ID;\n\n  // Operations that require ID token in request:\n  errorMap[fireauth.RpcHandler.ServerError.CREDENTIAL_TOO_OLD_LOGIN_AGAIN] =\n      fireauth.authenum.Error.CREDENTIAL_TOO_OLD_LOGIN_AGAIN;\n  errorMap[fireauth.RpcHandler.ServerError.INVALID_ID_TOKEN] =\n      fireauth.authenum.Error.INVALID_AUTH;\n  errorMap[fireauth.RpcHandler.ServerError.TOKEN_EXPIRED] =\n      fireauth.authenum.Error.TOKEN_EXPIRED;\n  errorMap[fireauth.RpcHandler.ServerError.USER_NOT_FOUND] =\n      fireauth.authenum.Error.TOKEN_EXPIRED;\n\n  // CORS issues.\n  errorMap[fireauth.RpcHandler.ServerError.CORS_UNSUPPORTED] =\n      fireauth.authenum.Error.CORS_UNSUPPORTED;\n\n  // Dynamic link not activated.\n  errorMap[fireauth.RpcHandler.ServerError.DYNAMIC_LINK_NOT_ACTIVATED] =\n      fireauth.authenum.Error.DYNAMIC_LINK_NOT_ACTIVATED;\n\n  // iosBundleId or androidPackageName not valid error.\n  errorMap[fireauth.RpcHandler.ServerError.INVALID_APP_ID] =\n      fireauth.authenum.Error.INVALID_APP_ID;\n\n  // Other errors.\n  errorMap[fireauth.RpcHandler.ServerError.TOO_MANY_ATTEMPTS_TRY_LATER] =\n      fireauth.authenum.Error.TOO_MANY_ATTEMPTS_TRY_LATER;\n  errorMap[fireauth.RpcHandler.ServerError.WEAK_PASSWORD] =\n      fireauth.authenum.Error.WEAK_PASSWORD;\n  errorMap[fireauth.RpcHandler.ServerError.OPERATION_NOT_ALLOWED] =\n      fireauth.authenum.Error.OPERATION_NOT_ALLOWED;\n  errorMap[fireauth.RpcHandler.ServerError.USER_CANCELLED] =\n      fireauth.authenum.Error.USER_CANCELLED;\n\n  // Phone Auth related errors.\n  errorMap[fireauth.RpcHandler.ServerError.CAPTCHA_CHECK_FAILED] =\n      fireauth.authenum.Error.CAPTCHA_CHECK_FAILED;\n  errorMap[fireauth.RpcHandler.ServerError.INVALID_APP_CREDENTIAL] =\n      fireauth.authenum.Error.INVALID_APP_CREDENTIAL;\n  errorMap[fireauth.RpcHandler.ServerError.INVALID_CODE] =\n      fireauth.authenum.Error.INVALID_CODE;\n  errorMap[fireauth.RpcHandler.ServerError.INVALID_PHONE_NUMBER] =\n      fireauth.authenum.Error.INVALID_PHONE_NUMBER;\n  errorMap[fireauth.RpcHandler.ServerError.INVALID_SESSION_INFO] =\n      fireauth.authenum.Error.INVALID_SESSION_INFO;\n  errorMap[fireauth.RpcHandler.ServerError.INVALID_TEMPORARY_PROOF] =\n      fireauth.authenum.Error.INVALID_IDP_RESPONSE;\n  errorMap[fireauth.RpcHandler.ServerError.MISSING_APP_CREDENTIAL] =\n      fireauth.authenum.Error.MISSING_APP_CREDENTIAL;\n  errorMap[fireauth.RpcHandler.ServerError.MISSING_CODE] =\n      fireauth.authenum.Error.MISSING_CODE;\n  errorMap[fireauth.RpcHandler.ServerError.MISSING_PHONE_NUMBER] =\n      fireauth.authenum.Error.MISSING_PHONE_NUMBER;\n  errorMap[fireauth.RpcHandler.ServerError.MISSING_SESSION_INFO] =\n      fireauth.authenum.Error.MISSING_SESSION_INFO;\n  errorMap[fireauth.RpcHandler.ServerError.QUOTA_EXCEEDED] =\n      fireauth.authenum.Error.QUOTA_EXCEEDED;\n  errorMap[fireauth.RpcHandler.ServerError.SESSION_EXPIRED] =\n      fireauth.authenum.Error.CODE_EXPIRED;\n  errorMap[fireauth.RpcHandler.ServerError.REJECTED_CREDENTIAL] =\n      fireauth.authenum.Error.REJECTED_CREDENTIAL;\n\n  // Other action code errors when additional settings passed.\n  errorMap[fireauth.RpcHandler.ServerError.INVALID_CONTINUE_URI] =\n      fireauth.authenum.Error.INVALID_CONTINUE_URI;\n  // MISSING_CONTINUE_URI is getting mapped to INTERNAL_ERROR above.\n  // This is OK as this error will be caught by client side validation.\n  errorMap[fireauth.RpcHandler.ServerError.MISSING_ANDROID_PACKAGE_NAME] =\n      fireauth.authenum.Error.MISSING_ANDROID_PACKAGE_NAME;\n  errorMap[fireauth.RpcHandler.ServerError.MISSING_IOS_BUNDLE_ID] =\n      fireauth.authenum.Error.MISSING_IOS_BUNDLE_ID;\n  errorMap[fireauth.RpcHandler.ServerError.UNAUTHORIZED_DOMAIN] =\n      fireauth.authenum.Error.UNAUTHORIZED_DOMAIN;\n  errorMap[fireauth.RpcHandler.ServerError.INVALID_DYNAMIC_LINK_DOMAIN] =\n      fireauth.authenum.Error.INVALID_DYNAMIC_LINK_DOMAIN;\n\n  // getProjectConfig errors when clientId is passed.\n  errorMap[fireauth.RpcHandler.ServerError.INVALID_OAUTH_CLIENT_ID] =\n      fireauth.authenum.Error.INVALID_OAUTH_CLIENT_ID;\n  // getProjectConfig errors when sha1Cert is passed.\n  errorMap[fireauth.RpcHandler.ServerError.INVALID_CERT_HASH] =\n      fireauth.authenum.Error.INVALID_CERT_HASH;\n\n  // Multi-tenant related errors.\n  errorMap[fireauth.RpcHandler.ServerError.UNSUPPORTED_TENANT_OPERATION] =\n      fireauth.authenum.Error.UNSUPPORTED_TENANT_OPERATION;\n  errorMap[fireauth.RpcHandler.ServerError.INVALID_TENANT_ID] =\n      fireauth.authenum.Error.INVALID_TENANT_ID;\n  errorMap[fireauth.RpcHandler.ServerError.TENANT_ID_MISMATCH] =\n      fireauth.authenum.Error.TENANT_ID_MISMATCH;\n\n  // User actions (sign-up or deletion) disabled errors.\n  errorMap[fireauth.RpcHandler.ServerError.ADMIN_ONLY_OPERATION] =\n      fireauth.authenum.Error.ADMIN_ONLY_OPERATION;\n\n  // Multi-factor related errors.\n  errorMap[fireauth.RpcHandler.ServerError.INVALID_MFA_PENDING_CREDENTIAL] =\n      fireauth.authenum.Error.INVALID_MFA_PENDING_CREDENTIAL;\n  errorMap[fireauth.RpcHandler.ServerError.MFA_ENROLLMENT_NOT_FOUND] =\n      fireauth.authenum.Error.MFA_ENROLLMENT_NOT_FOUND;\n  errorMap[fireauth.RpcHandler.ServerError.MISSING_MFA_PENDING_CREDENTIAL] =\n      fireauth.authenum.Error.MISSING_MFA_PENDING_CREDENTIAL;\n  errorMap[fireauth.RpcHandler.ServerError.MISSING_MFA_ENROLLMENT_ID] =\n      fireauth.authenum.Error.MISSING_MFA_ENROLLMENT_ID;\n  errorMap[fireauth.RpcHandler.ServerError.EMAIL_CHANGE_NEEDS_VERIFICATION] =\n      fireauth.authenum.Error.EMAIL_CHANGE_NEEDS_VERIFICATION;\n  errorMap[fireauth.RpcHandler.ServerError.SECOND_FACTOR_EXISTS] =\n      fireauth.authenum.Error.SECOND_FACTOR_EXISTS;\n  errorMap[fireauth.RpcHandler.ServerError.SECOND_FACTOR_LIMIT_EXCEEDED] =\n      fireauth.authenum.Error.SECOND_FACTOR_LIMIT_EXCEEDED;\n  errorMap[fireauth.RpcHandler.ServerError.UNSUPPORTED_FIRST_FACTOR] =\n      fireauth.authenum.Error.UNSUPPORTED_FIRST_FACTOR;\n  errorMap[fireauth.RpcHandler.ServerError.UNVERIFIED_EMAIL] =\n      fireauth.authenum.Error.UNVERIFIED_EMAIL;\n\n  // Override errors set in the custom map.\n  var customErrorMap = opt_customErrorMap || {};\n  goog.object.extend(errorMap, customErrorMap);\n\n  // Get detailed message if available.\n  errorMessage = fireauth.RpcHandler.getErrorCodeDetails(serverErrorCode);\n\n  // Handle backend errors where the error code can be a prefix of the message\n  // (e.g. \"WEAK_PASSWORD : Password should be at least 6 characters\").\n  // Use the details after the colon as the error message. If none available,\n  // pass undefined, which will default to the client hard coded error messages.\n  for (var prefixCode in errorMap) {\n    if (serverErrorCode.indexOf(prefixCode) === 0) {\n      return new fireauth.AuthError(errorMap[prefixCode], errorMessage);\n    }\n  }\n\n  // No error message found, return the serialized response as the message.\n  // This is likely to be an Apiary error for unexpected cases like keyExpired,\n  // etc.\n  if (!errorMessage && response) {\n     errorMessage = fireauth.util.stringifyJSON(response);\n  }\n  // The backend returned some error we don't recognize; this is an error on\n  // our side.\n  return new fireauth.AuthError(\n      fireauth.authenum.Error.INTERNAL_ERROR, errorMessage);\n};\n\n\n/**\n * @param {string} serverMessage The server error code.\n * @return {string|undefined} The detailed error code message.\n */\nfireauth.RpcHandler.getErrorCodeDetails = function(serverMessage) {\n  // Use the error details part as the autherror message.\n  // For a message INVALID_CUSTOM_TOKEN : [error detail here],\n  // The Auth error message should be [error detail here].\n  // No space should be contained in the error code, otherwise no detailed error\n  // message returned.\n  var matches = serverMessage.match(/^[^\\s]+\\s*:\\s*([\\s\\S]*)$/);\n  if (matches && matches.length > 1) {\n    return matches[1];\n  }\n  return undefined;\n};\n\n\n/**\n * Gets the Apiary error from a backend response, if applicable.\n * @param {!Object} response The API response.\n * @return {?fireauth.AuthError} The error, if applicable.\n * @private\n */\nfireauth.RpcHandler.getApiaryError_ = function(response) {\n  var error = response['error'] && response['error']['errors'] &&\n      response['error']['errors'][0] || {};\n  var reason = error['reason'] || '';\n\n  var errorReasonMap = {\n    'keyInvalid': fireauth.authenum.Error.INVALID_API_KEY,\n    'ipRefererBlocked': fireauth.authenum.Error.APP_NOT_AUTHORIZED\n  };\n\n  if (errorReasonMap[reason]) {\n    return new fireauth.AuthError(errorReasonMap[reason]);\n  }\n\n  return null;\n};\n\n\n/**\n * Gets the server error code from the response.\n * @param {!Object} resp The API response.\n * @return {string} The error code if present.\n * @private\n */\nfireauth.RpcHandler.getErrorCode_ = function(resp) {\n  return (resp['error'] && resp['error']['message']) || '';\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Low level handling of XMLHttpRequest.\n */\n\ngoog.provide('goog.net.DefaultXmlHttpFactory');\ngoog.provide('goog.net.XmlHttp');\ngoog.provide('goog.net.XmlHttp.OptionType');\ngoog.provide('goog.net.XmlHttp.ReadyState');\ngoog.provide('goog.net.XmlHttpDefines');\n\ngoog.require('goog.asserts');\ngoog.require('goog.net.WrapperXmlHttpFactory');\ngoog.require('goog.net.XmlHttpFactory');\ngoog.requireType('goog.net.XhrLike');\n\n\n/**\n * Static class for creating XMLHttpRequest objects.\n * @return {!goog.net.XhrLike.OrNative} A new XMLHttpRequest object.\n */\ngoog.net.XmlHttp = function() {\n  return goog.net.XmlHttp.factory_.createInstance();\n};\n\n\n/**\n * @define {boolean} Whether to assume XMLHttpRequest exists. Setting this to\n *     true bypasses the ActiveX probing code.\n * NOTE(ruilopes): Due to the way JSCompiler works, this define *will not* strip\n * out the ActiveX probing code from binaries.  To achieve this, use\n * `goog.net.XmlHttpDefines.ASSUME_NATIVE_XHR` instead.\n * TODO(ruilopes): Collapse both defines.\n */\ngoog.net.XmlHttp.ASSUME_NATIVE_XHR =\n    goog.define('goog.net.XmlHttp.ASSUME_NATIVE_XHR', false);\n\n\n/** @const */\ngoog.net.XmlHttpDefines = {};\n\n\n/**\n * @define {boolean} Whether to assume XMLHttpRequest exists. Setting this to\n *     true eliminates the ActiveX probing code.\n */\ngoog.net.XmlHttpDefines.ASSUME_NATIVE_XHR =\n    goog.define('goog.net.XmlHttpDefines.ASSUME_NATIVE_XHR', false);\n\n\n/**\n * Gets the options to use with the XMLHttpRequest objects obtained using\n * the static methods.\n * @return {Object} The options.\n */\ngoog.net.XmlHttp.getOptions = function() {\n  return goog.net.XmlHttp.factory_.getOptions();\n};\n\n\n/**\n * Type of options that an XmlHttp object can have.\n * @enum {number}\n */\ngoog.net.XmlHttp.OptionType = {\n  /**\n   * Whether a goog.nullFunction should be used to clear the onreadystatechange\n   * handler instead of null.\n   */\n  USE_NULL_FUNCTION: 0,\n\n  /**\n   * NOTE(user): In IE if send() errors on a *local* request the readystate\n   * is still changed to COMPLETE.  We need to ignore it and allow the\n   * try/catch around send() to pick up the error.\n   */\n  LOCAL_REQUEST_ERROR: 1,\n};\n\n\n/**\n * Status constants for XMLHTTP, matches:\n * https://msdn.microsoft.com/en-us/library/ms534361(v=vs.85).aspx\n * @enum {number}\n */\ngoog.net.XmlHttp.ReadyState = {\n  /**\n   * Constant for when xmlhttprequest.readyState is uninitialized\n   */\n  UNINITIALIZED: 0,\n\n  /**\n   * Constant for when xmlhttprequest.readyState is loading.\n   */\n  LOADING: 1,\n\n  /**\n   * Constant for when xmlhttprequest.readyState is loaded.\n   */\n  LOADED: 2,\n\n  /**\n   * Constant for when xmlhttprequest.readyState is in an interactive state.\n   */\n  INTERACTIVE: 3,\n\n  /**\n   * Constant for when xmlhttprequest.readyState is completed\n   */\n  COMPLETE: 4,\n};\n\n\n/**\n * The global factory instance for creating XMLHttpRequest objects.\n * @type {goog.net.XmlHttpFactory}\n * @private\n */\ngoog.net.XmlHttp.factory_;\n\n\n/**\n * Sets the factories for creating XMLHttpRequest objects and their options.\n * @param {Function} factory The factory for XMLHttpRequest objects.\n * @param {Function} optionsFactory The factory for options.\n * @deprecated Use setGlobalFactory instead.\n */\ngoog.net.XmlHttp.setFactory = function(factory, optionsFactory) {\n  goog.net.XmlHttp.setGlobalFactory(\n      new goog.net.WrapperXmlHttpFactory(\n          goog.asserts.assert(factory), goog.asserts.assert(optionsFactory)));\n};\n\n\n/**\n * Sets the global factory object.\n * @param {!goog.net.XmlHttpFactory} factory New global factory object.\n */\ngoog.net.XmlHttp.setGlobalFactory = function(factory) {\n  goog.net.XmlHttp.factory_ = factory;\n};\n\n\n\n/**\n * Default factory to use when creating xhr objects.  You probably shouldn't be\n * instantiating this directly, but rather using it via goog.net.XmlHttp.\n * @extends {goog.net.XmlHttpFactory}\n * @constructor\n */\ngoog.net.DefaultXmlHttpFactory = function() {\n  goog.net.XmlHttpFactory.call(this);\n};\ngoog.inherits(goog.net.DefaultXmlHttpFactory, goog.net.XmlHttpFactory);\n\n\n/** @override */\ngoog.net.DefaultXmlHttpFactory.prototype.createInstance = function() {\n  var progId = this.getProgId_();\n  if (progId) {\n    return new ActiveXObject(progId);\n  } else {\n    return new XMLHttpRequest();\n  }\n};\n\n\n/** @override */\ngoog.net.DefaultXmlHttpFactory.prototype.internalGetOptions = function() {\n  var progId = this.getProgId_();\n  var options = {};\n  if (progId) {\n    options[goog.net.XmlHttp.OptionType.USE_NULL_FUNCTION] = true;\n    options[goog.net.XmlHttp.OptionType.LOCAL_REQUEST_ERROR] = true;\n  }\n  return options;\n};\n\n\n/**\n * The ActiveX PROG ID string to use to create xhr's in IE. Lazily initialized.\n * @type {string|undefined}\n * @private\n */\ngoog.net.DefaultXmlHttpFactory.prototype.ieProgId_;\n\n\n/**\n * Initialize the private state used by other functions.\n * @return {string} The ActiveX PROG ID string to use to create xhr's in IE.\n * @private\n */\ngoog.net.DefaultXmlHttpFactory.prototype.getProgId_ = function() {\n  if (goog.net.XmlHttp.ASSUME_NATIVE_XHR ||\n      goog.net.XmlHttpDefines.ASSUME_NATIVE_XHR) {\n    return '';\n  }\n\n  // The following blog post describes what PROG IDs to use to create the\n  // XMLHTTP object in Internet Explorer:\n  // http://blogs.msdn.com/xmlteam/archive/2006/10/23/using-the-right-version-of-msxml-in-internet-explorer.aspx\n  // However we do not (yet) fully trust that this will be OK for old versions\n  // of IE on Win9x so we therefore keep the last 2.\n  if (!this.ieProgId_ && typeof XMLHttpRequest == 'undefined' &&\n      typeof ActiveXObject != 'undefined') {\n    // Candidate Active X types.\n    var ACTIVE_X_IDENTS = [\n      'MSXML2.XMLHTTP.6.0',\n      'MSXML2.XMLHTTP.3.0',\n      'MSXML2.XMLHTTP',\n      'Microsoft.XMLHTTP',\n    ];\n    for (var i = 0; i < ACTIVE_X_IDENTS.length; i++) {\n      var candidate = ACTIVE_X_IDENTS[i];\n\n      try {\n        new ActiveXObject(candidate);\n        // NOTE(user): cannot assign progid and return candidate in one line\n        // because JSCompiler complaings: BUG 658126\n        this.ieProgId_ = candidate;\n        return candidate;\n      } catch (e) {\n        // do nothing; try next choice\n      }\n    }\n\n    // couldn't find any matches\n    throw new Error(\n        'Could not create ActiveXObject. ActiveX might be disabled,' +\n        ' or MSXML might not be installed');\n  }\n\n  return /** @type {string} */ (this.ieProgId_);\n};\n\n\n// Set the global factory to an instance of the default factory.\ngoog.net.XmlHttp.setGlobalFactory(new goog.net.DefaultXmlHttpFactory());\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines the invalid origin error, a subclass of\n * fireauth.AuthError.\n */\n\n\ngoog.provide('fireauth.InvalidOriginError');\n\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.authenum.Error');\ngoog.require('goog.Uri');\ngoog.require('goog.string');\n\n\n\n/**\n * Invalid origin error that can be returned to the developer.\n * @param {string} origin The invalid domain name.\n * @constructor\n * @extends {fireauth.AuthError}\n */\nfireauth.InvalidOriginError = function(origin) {\n  var code = fireauth.authenum.Error.INVALID_ORIGIN;\n  var message = undefined;\n  var uri = goog.Uri.parse(origin);\n  // Get domain.\n  var domain = uri.getDomain();\n  // Get scheme.\n  var scheme = uri.getScheme();\n  // Only http, https and chrome-extension currently supported.\n  if (scheme == 'chrome-extension') {\n    // Chrome extension whitelisting.\n    // Replace chrome-extension://CHROME_EXT_ID in error message template.\n    message = goog.string.subs(\n        fireauth.InvalidOriginError.CHROME_EXTENSION_MESSAGE_TEMPLATE_,\n        domain);\n  } else if (scheme == 'http' || scheme == 'https') {\n    // Replace domain in error message template.\n    message = goog.string.subs(\n        fireauth.InvalidOriginError.HTTP_MESSAGE_TEMPLATE_,\n        domain);\n  } else {\n    // Throw operation not supported when non http, https or Chrome extension\n    // protocol.\n    code = fireauth.authenum.Error.OPERATION_NOT_SUPPORTED;\n  }\n  fireauth.InvalidOriginError.base(this, 'constructor', code, message);\n};\ngoog.inherits(fireauth.InvalidOriginError, fireauth.AuthError);\n\n\n/** @private @const {string} The http invalid origin message template. */\nfireauth.InvalidOriginError.HTTP_MESSAGE_TEMPLATE_ = 'This domain (%s) is no' +\n    't authorized to run this operation. Add it to the OAuth redirect domain' +\n    's list in the Firebase console -> Auth section -> Sign in method tab.';\n\n\n/**\n * @private @const {string} The Chrome extension invalid origin message\n *     template.\n */\nfireauth.InvalidOriginError.CHROME_EXTENSION_MESSAGE_TEMPLATE_ = 'This chrom' +\n    'e extension ID (chrome-extension://%s) is not authorized to run this op' +\n    'eration. Add it to the OAuth redirect domains list in the Firebase cons' +\n    'ole -> Auth section -> Sign in method tab.';\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines the Auth errors that include emails and an Auth\n * credential, a subclass of fireauth.AuthError.\n */\n\n\ngoog.provide('fireauth.AuthErrorWithCredential');\n\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.AuthProvider');\ngoog.require('fireauth.object');\ngoog.require('goog.object');\n\n\n/**\n * Error with email and credential that can be returned to the developer.\n * @param {fireauth.authenum.Error} code The error code.\n * @param {?fireauth.AuthErrorWithCredential.CredentialInfo=} opt_credentialInfo\n *     Additional credential information to associate with the error.\n * @param {string=} opt_message The human-readable message.\n * @constructor\n * @extends {fireauth.AuthError}\n */\nfireauth.AuthErrorWithCredential =\n    function(code, opt_credentialInfo, opt_message) {\n  fireauth.AuthErrorWithCredential.base(\n      this, 'constructor', code, opt_message);\n  var credentialInfo = opt_credentialInfo || {};\n\n  // These properties are public.\n  if (credentialInfo.email) {\n    fireauth.object.setReadonlyProperty(this, 'email', credentialInfo.email);\n  }\n  if (credentialInfo.phoneNumber) {\n    fireauth.object.setReadonlyProperty(this, 'phoneNumber',\n        credentialInfo.phoneNumber);\n  }\n  if (credentialInfo.credential) {\n    fireauth.object.setReadonlyProperty(this, 'credential',\n        credentialInfo.credential);\n  }\n  if (credentialInfo.tenantId) {\n    fireauth.object.setReadonlyProperty(this, 'tenantId',\n        credentialInfo.tenantId);\n  }\n};\ngoog.inherits(fireauth.AuthErrorWithCredential, fireauth.AuthError);\n\n\n/**\n * Additional credential information to associate with an error, so that the\n * user does not have to execute the Auth flow again on linking errors.\n * @typedef {{\n *   email: (?string|undefined),\n *   phoneNumber: (?string|undefined),\n *   credential: (?fireauth.AuthCredential|undefined),\n *   tenantId: (?string|undefined),\n * }}\n */\nfireauth.AuthErrorWithCredential.CredentialInfo;\n\n\n/**\n * @return {!Object} The plain object form of the error.\n * @override\n */\nfireauth.AuthErrorWithCredential.prototype.toPlainObject = function() {\n  var obj = {\n    'code': this['code'],\n    'message': this.message\n  };\n  if (this['email']) {\n    obj['email'] = this['email'];\n  }\n  if (this['phoneNumber']) {\n    obj['phoneNumber'] = this['phoneNumber'];\n  }\n  if (this['tenantId']) {\n    obj['tenantId'] = this['tenantId'];\n  }\n\n  var credential = this['credential'] && this['credential'].toPlainObject();\n  if (credential){\n    goog.object.extend(obj, credential);\n  }\n  return obj;\n};\n\n\n/**\n * @return {!Object} The plain object form of the error. This is used by\n *     JSON.toStringify() to return the stringified representation of the error;\n * @override\n */\nfireauth.AuthErrorWithCredential.prototype.toJSON = function() {\n  // Return the plain object representation in case JSON.stringify is called on\n  // an Auth error instance.\n  return this.toPlainObject();\n};\n\n\n/**\n * @param {?Object|undefined} response The object response to convert to a\n *     fireauth.AuthErrorWithCredential.\n * @return {?fireauth.AuthError} The error representation of the response.\n */\nfireauth.AuthErrorWithCredential.fromPlainObject = function(response) {\n  // Code included.\n  if (response['code']) {\n    var code = response['code'] || '';\n    // Remove prefix from name if available.\n    if (code.indexOf(fireauth.AuthError.ERROR_CODE_PREFIX) == 0) {\n      code = code.substring(fireauth.AuthError.ERROR_CODE_PREFIX.length);\n    }\n\n    // Credentials and tenant ID in response.\n    var credentialInfo = {\n      credential: fireauth.AuthProvider.getCredentialFromResponse(response),\n      tenantId: response['tenantId']\n    };\n    if (response['email']) {\n      credentialInfo.email = response['email'];\n    } else if (response['phoneNumber']) {\n      credentialInfo.phoneNumber = response['phoneNumber'];\n    } else if (!credentialInfo.credential) {\n      // Neither email, phone number or credentials are set; return a generic\n      // error.\n      return new fireauth.AuthError(code, response['message'] || undefined);\n    }\n\n    return new fireauth.AuthErrorWithCredential(code, credentialInfo,\n        response['message']);\n  }\n  // No error or invalid response.\n  return null;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Interface for a factory for creating XMLHttpRequest objects\n * and metadata about them.\n */\n\ngoog.provide('goog.net.XmlHttpFactory');\n\n/** @suppress {extraRequire} Typedef. */\ngoog.require('goog.net.XhrLike');\n\n\n\n/**\n * Abstract base class for an XmlHttpRequest factory.\n * @constructor\n */\ngoog.net.XmlHttpFactory = function() {};\n\n\n/**\n * Cache of options - we only actually call internalGetOptions once.\n * @type {?Object}\n * @private\n */\ngoog.net.XmlHttpFactory.prototype.cachedOptions_ = null;\n\n\n/**\n * @return {!goog.net.XhrLike.OrNative} A new XhrLike instance.\n */\ngoog.net.XmlHttpFactory.prototype.createInstance = goog.abstractMethod;\n\n\n/**\n * @return {Object} Options describing how xhr objects obtained from this\n *     factory should be used.\n */\ngoog.net.XmlHttpFactory.prototype.getOptions = function() {\n  return this.cachedOptions_ ||\n      (this.cachedOptions_ = this.internalGetOptions());\n};\n\n\n/**\n * Override this method in subclasses to preserve the caching offered by\n * getOptions().\n * @return {Object} Options describing how xhr objects obtained from this\n *     factory should be used.\n * @protected\n */\ngoog.net.XmlHttpFactory.prototype.internalGetOptions = goog.abstractMethod;\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview This file contain classes that add support for cross-domain XHR\n * requests (see http://www.w3.org/TR/cors/). Most modern browsers are able to\n * use a regular XMLHttpRequest for that, but IE 8 use XDomainRequest object\n * instead. This file provides an adapter from this object to a goog.net.XhrLike\n * and a factory to allow using this with a goog.net.XhrIo instance.\n *\n * IE 7 and older versions are not supported (given that they do not support\n * CORS requests).\n */\ngoog.provide('goog.net.CorsXmlHttpFactory');\ngoog.provide('goog.net.IeCorsXhrAdapter');\n\ngoog.require('goog.net.HttpStatus');\ngoog.require('goog.net.XhrLike');\ngoog.require('goog.net.XmlHttp');\ngoog.require('goog.net.XmlHttpFactory');\n\n\n\n/**\n * A factory of XML http request objects that supports cross domain requests.\n * This class should be instantiated and passed as the parameter of a\n * goog.net.XhrIo constructor to allow cross-domain requests in every browser.\n *\n * @extends {goog.net.XmlHttpFactory}\n * @constructor\n * @final\n */\ngoog.net.CorsXmlHttpFactory = function() {\n  goog.net.XmlHttpFactory.call(this);\n};\ngoog.inherits(goog.net.CorsXmlHttpFactory, goog.net.XmlHttpFactory);\n\n\n/** @override */\ngoog.net.CorsXmlHttpFactory.prototype.createInstance = function() {\n  var xhr = new XMLHttpRequest();\n  if (('withCredentials' in xhr)) {\n    return xhr;\n  } else if (typeof XDomainRequest != 'undefined') {\n    return new goog.net.IeCorsXhrAdapter();\n  } else {\n    throw new Error('Unsupported browser');\n  }\n};\n\n\n/** @override */\ngoog.net.CorsXmlHttpFactory.prototype.internalGetOptions = function() {\n  return {};\n};\n\n\n\n/**\n * An adapter around Internet Explorer's XDomainRequest object that makes it\n * look like a standard XMLHttpRequest. This can be used instead of\n * XMLHttpRequest to support CORS.\n *\n * @implements {goog.net.XhrLike}\n * @constructor\n * @struct\n * @final\n */\ngoog.net.IeCorsXhrAdapter = function() {\n  /**\n   * The underlying XDomainRequest used to make the HTTP request.\n   * @type {!XDomainRequest}\n   * @private\n   */\n  this.xdr_ = new XDomainRequest();\n\n  /**\n   * The simulated ready state.\n   * @type {number}\n   */\n  this.readyState = goog.net.XmlHttp.ReadyState.UNINITIALIZED;\n\n  /**\n   * The simulated ready state change callback function.\n   * @type {?function()|undefined}\n   */\n  this.onreadystatechange = null;\n\n  /** @override */\n  this.response = '';\n\n  /**\n   * The simulated response text parameter.\n   * @type {string}\n   */\n  this.responseText = '';\n\n  /**\n   * This implementation only supports text response.\n   * @type {string}\n   * @override\n   */\n  this.responseType = '';\n\n  /**\n   * The simulated status code\n   * @type {number}\n   */\n  this.status = -1;\n\n  /** @override */\n  this.responseXML = null;\n\n  /** @override */\n  this.statusText = '';\n\n  this.xdr_.onload = goog.bind(this.handleLoad_, this);\n  this.xdr_.onerror = goog.bind(this.handleError_, this);\n  this.xdr_.onprogress = goog.bind(this.handleProgress_, this);\n  this.xdr_.ontimeout = goog.bind(this.handleTimeout_, this);\n};\n\n\n/**\n * Opens a connection to the provided URL.\n * @param {string} method The HTTP method to use. Valid methods include GET and\n *     POST.\n * @param {string} url The URL to contact. The authority of this URL must match\n *     the authority of the current page's URL (e.g. http or https).\n * @param {?boolean=} opt_async Whether the request is asynchronous, defaulting\n *     to true. XDomainRequest does not support syncronous requests, so setting\n *     it to false will actually raise an exception.\n * @override\n */\ngoog.net.IeCorsXhrAdapter.prototype.open = function(method, url, opt_async) {\n  if (opt_async != null && (!opt_async)) {\n    throw new Error('Only async requests are supported.');\n  }\n  this.xdr_.open(method, url);\n};\n\n\n/**\n * Sends the request to the remote server. Before calling this function, always\n * call {@link open}.\n * @param {(ArrayBuffer|ArrayBufferView|Blob|Document|FormData|null|string)=}\n *     opt_content The content to send as POSTDATA, if any. Only string data is\n *     supported by this implementation.\n * @override\n */\ngoog.net.IeCorsXhrAdapter.prototype.send = function(opt_content) {\n  if (opt_content) {\n    if (typeof opt_content == 'string') {\n      this.xdr_.send(opt_content);\n    } else {\n      throw new Error('Only string data is supported');\n    }\n  } else {\n    this.xdr_.send();\n  }\n};\n\n\n/**\n * @override\n */\ngoog.net.IeCorsXhrAdapter.prototype.abort = function() {\n  this.xdr_.abort();\n};\n\n\n/**\n * Sets a request header to send to the remote server. Because this\n * implementation does not support request headers, this function does nothing.\n * @param {string} key The name of the HTTP header to set. Ignored.\n * @param {string} value The value to set for the HTTP header. Ignored.\n * @override\n */\ngoog.net.IeCorsXhrAdapter.prototype.setRequestHeader = function(key, value) {\n  // Unsupported; ignore the header.\n};\n\n\n/**\n * Returns the value of the response header identified by key. This\n * implementation only supports the 'content-type' header.\n * @param {string} key The request header to fetch. If this parameter is set to\n *     'content-type' (case-insensitive), this function returns the value of\n *     the 'content-type' request header. If this parameter is set to any other\n *     value, this function always returns an empty string.\n * @return {string} The value of the response header, or an empty string if key\n *     is not 'content-type' (case-insensitive).\n * @override\n */\ngoog.net.IeCorsXhrAdapter.prototype.getResponseHeader = function(key) {\n  if (key.toLowerCase() == 'content-type') {\n    return this.xdr_.contentType;\n  }\n  return '';\n};\n\n\n/**\n * Handles a request that has fully loaded successfully.\n * @private\n */\ngoog.net.IeCorsXhrAdapter.prototype.handleLoad_ = function() {\n  // IE only calls onload if the status is 200, so the status code must be OK.\n  this.status = goog.net.HttpStatus.OK;\n  this.response = this.responseText = this.xdr_.responseText;\n  this.setReadyState_(goog.net.XmlHttp.ReadyState.COMPLETE);\n};\n\n\n/**\n * Handles a request that has failed to load.\n * @private\n */\ngoog.net.IeCorsXhrAdapter.prototype.handleError_ = function() {\n  // IE doesn't tell us what the status code actually is (other than the fact\n  // that it is not 200), so simulate an INTERNAL_SERVER_ERROR.\n  this.status = goog.net.HttpStatus.INTERNAL_SERVER_ERROR;\n  this.response = this.responseText = '';\n  this.setReadyState_(goog.net.XmlHttp.ReadyState.COMPLETE);\n};\n\n\n/**\n * Handles a request that timed out.\n * @private\n */\ngoog.net.IeCorsXhrAdapter.prototype.handleTimeout_ = function() {\n  this.handleError_();\n};\n\n\n/**\n * Handles a request that is in the process of loading.\n * @private\n */\ngoog.net.IeCorsXhrAdapter.prototype.handleProgress_ = function() {\n  // IE only calls onprogress if the status is 200, so the status code must be\n  // OK.\n  this.status = goog.net.HttpStatus.OK;\n  this.setReadyState_(goog.net.XmlHttp.ReadyState.LOADING);\n};\n\n\n/**\n * Sets this XHR's ready state and fires the onreadystatechange listener (if one\n * is set).\n * @param {number} readyState The new ready state.\n * @private\n */\ngoog.net.IeCorsXhrAdapter.prototype.setReadyState_ = function(readyState) {\n  this.readyState = readyState;\n  if (this.onreadystatechange) {\n    this.onreadystatechange();\n  }\n};\n\n\n/**\n * Returns the response headers from the server. This implemntation only returns\n * the 'content-type' header.\n * @return {string} The headers returned from the server.\n * @override\n */\ngoog.net.IeCorsXhrAdapter.prototype.getAllResponseHeaders = function() {\n  return 'content-type: ' + this.xdr_.contentType;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Definition of the LogRecord class. Please minimize\n * dependencies this file has on other closure classes as any dependency it\n * takes won't be able to use the logging infrastructure.\n */\n\ngoog.provide('goog.debug.LogRecord');\n\n\n\n/**\n * LogRecord objects are used to pass logging requests between\n * the logging framework and individual log Handlers.\n * @constructor\n * @param {goog.debug.Logger.Level} level One of the level identifiers.\n * @param {string} msg The string message.\n * @param {string} loggerName The name of the source logger.\n * @param {number=} opt_time Time this log record was created if other than now.\n *     If 0, we use #goog.now.\n * @param {number=} opt_sequenceNumber Sequence number of this log record. This\n *     should only be passed in when restoring a log record from persistence.\n */\ngoog.debug.LogRecord = function(\n    level, msg, loggerName, opt_time, opt_sequenceNumber) {\n  this.reset(level, msg, loggerName, opt_time, opt_sequenceNumber);\n};\n\n\n/**\n * Time the LogRecord was created.\n * @type {number}\n * @private\n */\ngoog.debug.LogRecord.prototype.time_;\n\n\n/**\n * Level of the LogRecord\n * @type {goog.debug.Logger.Level}\n * @private\n */\ngoog.debug.LogRecord.prototype.level_;\n\n\n/**\n * Message associated with the record\n * @type {string}\n * @private\n */\ngoog.debug.LogRecord.prototype.msg_;\n\n\n/**\n * Name of the logger that created the record.\n * @type {string}\n * @private\n */\ngoog.debug.LogRecord.prototype.loggerName_;\n\n\n/**\n * Sequence number for the LogRecord. Each record has a unique sequence number\n * that is greater than all log records created before it.\n * @type {number}\n * @private\n */\ngoog.debug.LogRecord.prototype.sequenceNumber_ = 0;\n\n\n/**\n * Exception associated with the record\n * @type {?Object}\n * @private\n */\ngoog.debug.LogRecord.prototype.exception_ = null;\n\n\n/**\n * @define {boolean} Whether to enable log sequence numbers.\n */\ngoog.debug.LogRecord.ENABLE_SEQUENCE_NUMBERS =\n    goog.define('goog.debug.LogRecord.ENABLE_SEQUENCE_NUMBERS', true);\n\n\n/**\n * A sequence counter for assigning increasing sequence numbers to LogRecord\n * objects.\n * @type {number}\n * @private\n */\ngoog.debug.LogRecord.nextSequenceNumber_ = 0;\n\n\n/**\n * Sets all fields of the log record.\n * @param {goog.debug.Logger.Level} level One of the level identifiers.\n * @param {string} msg The string message.\n * @param {string} loggerName The name of the source logger.\n * @param {number=} opt_time Time this log record was created if other than now.\n *     If 0, we use #goog.now.\n * @param {number=} opt_sequenceNumber Sequence number of this log record. This\n *     should only be passed in when restoring a log record from persistence.\n */\ngoog.debug.LogRecord.prototype.reset = function(\n    level, msg, loggerName, opt_time, opt_sequenceNumber) {\n  if (goog.debug.LogRecord.ENABLE_SEQUENCE_NUMBERS) {\n    this.sequenceNumber_ = typeof opt_sequenceNumber == 'number' ?\n        opt_sequenceNumber :\n        goog.debug.LogRecord.nextSequenceNumber_++;\n  }\n\n  this.time_ = opt_time || goog.now();\n  this.level_ = level;\n  this.msg_ = msg;\n  this.loggerName_ = loggerName;\n  delete this.exception_;\n};\n\n\n/**\n * Get the source Logger's name.\n *\n * @return {string} source logger name (may be null).\n */\ngoog.debug.LogRecord.prototype.getLoggerName = function() {\n  return this.loggerName_;\n};\n\n\n/**\n * Get the exception that is part of the log record.\n *\n * @return {Object} the exception.\n */\ngoog.debug.LogRecord.prototype.getException = function() {\n  return this.exception_;\n};\n\n\n/**\n * Set the exception that is part of the log record.\n *\n * @param {Object} exception the exception.\n */\ngoog.debug.LogRecord.prototype.setException = function(exception) {\n  this.exception_ = exception;\n};\n\n\n/**\n * Get the source Logger's name.\n *\n * @param {string} loggerName source logger name (may be null).\n */\ngoog.debug.LogRecord.prototype.setLoggerName = function(loggerName) {\n  this.loggerName_ = loggerName;\n};\n\n\n/**\n * Get the logging message level, for example Level.SEVERE.\n * @return {goog.debug.Logger.Level} the logging message level.\n */\ngoog.debug.LogRecord.prototype.getLevel = function() {\n  return this.level_;\n};\n\n\n/**\n * Set the logging message level, for example Level.SEVERE.\n * @param {goog.debug.Logger.Level} level the logging message level.\n */\ngoog.debug.LogRecord.prototype.setLevel = function(level) {\n  this.level_ = level;\n};\n\n\n/**\n * Get the \"raw\" log message, before localization or formatting.\n *\n * @return {string} the raw message string.\n */\ngoog.debug.LogRecord.prototype.getMessage = function() {\n  return this.msg_;\n};\n\n\n/**\n * Set the \"raw\" log message, before localization or formatting.\n *\n * @param {string} msg the raw message string.\n */\ngoog.debug.LogRecord.prototype.setMessage = function(msg) {\n  this.msg_ = msg;\n};\n\n\n/**\n * Get event time in milliseconds since 1970.\n *\n * @return {number} event time in millis since 1970.\n */\ngoog.debug.LogRecord.prototype.getMillis = function() {\n  return this.time_;\n};\n\n\n/**\n * Set event time in milliseconds since 1970.\n *\n * @param {number} time event time in millis since 1970.\n */\ngoog.debug.LogRecord.prototype.setMillis = function(time) {\n  this.time_ = time;\n};\n\n\n/**\n * Get the sequence number.\n * <p>\n * Sequence numbers are normally assigned in the LogRecord\n * constructor, which assigns unique sequence numbers to\n * each new LogRecord in increasing order.\n * @return {number} the sequence number.\n */\ngoog.debug.LogRecord.prototype.getSequenceNumber = function() {\n  return this.sequenceNumber_;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Definition of the Logger class. Please minimize dependencies\n * this file has on other closure classes as any dependency it takes won't be\n * able to use the logging infrastructure.\n *\n * @see ../demos/debug.html\n */\n\ngoog.provide('goog.debug.LogManager');\ngoog.provide('goog.debug.Loggable');\ngoog.provide('goog.debug.Logger');\ngoog.provide('goog.debug.Logger.Level');\n\ngoog.require('goog.array');\ngoog.require('goog.asserts');\ngoog.require('goog.debug');\ngoog.require('goog.debug.LogBuffer');\ngoog.require('goog.debug.LogRecord');\n\n\n/**\n * A message value that can be handled by a Logger.\n *\n * Functions are treated like callbacks, but are only called when the event's\n * log level is enabled. This is useful for logging messages that are expensive\n * to construct.\n *\n * @typedef {string|function(): string}\n */\ngoog.debug.Loggable;\n\n\n\n/**\n * The Logger is an object used for logging debug messages. Loggers are\n * normally named, using a hierarchical dot-separated namespace. Logger names\n * can be arbitrary strings, but they should normally be based on the package\n * name or class name of the logged component, such as goog.net.BrowserChannel.\n *\n * The Logger object is loosely based on the java class\n * java.util.logging.Logger. It supports different levels of filtering for\n * different loggers.\n *\n * The logger object should never be instantiated by application code. It\n * should always use the goog.debug.Logger.getLogger function.\n *\n * @constructor\n * @param {string} name The name of the Logger.\n * @final\n */\ngoog.debug.Logger = function(name) {\n  /**\n   * Name of the Logger. Generally a dot-separated namespace\n   * @private {string}\n   */\n  this.name_ = name;\n\n  /**\n   * Parent Logger.\n   * @private {?goog.debug.Logger}\n   */\n  this.parent_ = null;\n\n  /**\n   * Level that this logger only filters above. Null indicates it should\n   * inherit from the parent.\n   * @private {?goog.debug.Logger.Level}\n   */\n  this.level_ = null;\n\n  /**\n   * Map of children loggers. The keys are the leaf names of the children and\n   * the values are the child loggers.\n   * @private {?Object}\n   */\n  this.children_ = null;\n\n  /**\n   * Handlers that are listening to this logger.\n   * @private {?Array<?Function>}\n   */\n  this.handlers_ = null;\n};\n\n\n/** @const */\ngoog.debug.Logger.ROOT_LOGGER_NAME = '';\n\n\n/**\n * @define {boolean} Toggles whether loggers other than the root logger can have\n *     log handlers attached to them and whether they can have their log level\n *     set. Logging is a bit faster when this is set to false.\n */\ngoog.debug.Logger.ENABLE_HIERARCHY =\n    goog.define('goog.debug.Logger.ENABLE_HIERARCHY', true);\n\n\n/**\n * @define {boolean} Toggles whether active log statements are also recorded\n *     to the profiler.\n */\ngoog.debug.Logger.ENABLE_PROFILER_LOGGING =\n    goog.define('goog.debug.Logger.ENABLE_PROFILER_LOGGING', false);\n\n\nif (!goog.debug.Logger.ENABLE_HIERARCHY) {\n  /**\n   * @type {!Array<Function>}\n   * @private\n   */\n  goog.debug.Logger.rootHandlers_ = [];\n\n\n  /**\n   * @type {goog.debug.Logger.Level}\n   * @private\n   */\n  goog.debug.Logger.rootLevel_;\n}\n\n\n\n/**\n * The Level class defines a set of standard logging levels that\n * can be used to control logging output.  The logging Level objects\n * are ordered and are specified by ordered integers.  Enabling logging\n * at a given level also enables logging at all higher levels.\n * <p>\n * Clients should normally use the predefined Level constants such\n * as Level.SEVERE.\n * <p>\n * The levels in descending order are:\n * <ul>\n * <li>SEVERE (highest value)\n * <li>WARNING\n * <li>INFO\n * <li>CONFIG\n * <li>FINE\n * <li>FINER\n * <li>FINEST  (lowest value)\n * </ul>\n * In addition there is a level OFF that can be used to turn\n * off logging, and a level ALL that can be used to enable\n * logging of all messages.\n *\n * @param {string} name The name of the level.\n * @param {number} value The numeric value of the level.\n * @constructor\n * @final\n */\ngoog.debug.Logger.Level = function(name, value) {\n  /**\n   * The name of the level\n   * @type {string}\n   */\n  this.name = name;\n\n  /**\n   * The numeric value of the level\n   * @type {number}\n   */\n  this.value = value;\n};\n\n\n/**\n * @return {string} String representation of the logger level.\n * @override\n */\ngoog.debug.Logger.Level.prototype.toString = function() {\n  return this.name;\n};\n\n\n/**\n * OFF is a special level that can be used to turn off logging.\n * This level is initialized to <CODE>Infinity</CODE>.\n * @type {!goog.debug.Logger.Level}\n */\ngoog.debug.Logger.Level.OFF = new goog.debug.Logger.Level('OFF', Infinity);\n\n\n/**\n * SHOUT is a message level for extra debugging loudness.\n * This level is initialized to <CODE>1200</CODE>.\n * @type {!goog.debug.Logger.Level}\n */\ngoog.debug.Logger.Level.SHOUT = new goog.debug.Logger.Level('SHOUT', 1200);\n\n\n/**\n * SEVERE is a message level indicating a serious failure.\n * This level is initialized to <CODE>1000</CODE>.\n * @type {!goog.debug.Logger.Level}\n */\ngoog.debug.Logger.Level.SEVERE = new goog.debug.Logger.Level('SEVERE', 1000);\n\n\n/**\n * WARNING is a message level indicating a potential problem.\n * This level is initialized to <CODE>900</CODE>.\n * @type {!goog.debug.Logger.Level}\n */\ngoog.debug.Logger.Level.WARNING = new goog.debug.Logger.Level('WARNING', 900);\n\n\n/**\n * INFO is a message level for informational messages.\n * This level is initialized to <CODE>800</CODE>.\n * @type {!goog.debug.Logger.Level}\n */\ngoog.debug.Logger.Level.INFO = new goog.debug.Logger.Level('INFO', 800);\n\n\n/**\n * CONFIG is a message level for static configuration messages.\n * This level is initialized to <CODE>700</CODE>.\n * @type {!goog.debug.Logger.Level}\n */\ngoog.debug.Logger.Level.CONFIG = new goog.debug.Logger.Level('CONFIG', 700);\n\n\n/**\n * FINE is a message level providing tracing information.\n * This level is initialized to <CODE>500</CODE>.\n * @type {!goog.debug.Logger.Level}\n */\ngoog.debug.Logger.Level.FINE = new goog.debug.Logger.Level('FINE', 500);\n\n\n/**\n * FINER indicates a fairly detailed tracing message.\n * This level is initialized to <CODE>400</CODE>.\n * @type {!goog.debug.Logger.Level}\n */\ngoog.debug.Logger.Level.FINER = new goog.debug.Logger.Level('FINER', 400);\n\n/**\n * FINEST indicates a highly detailed tracing message.\n * This level is initialized to <CODE>300</CODE>.\n * @type {!goog.debug.Logger.Level}\n */\n\ngoog.debug.Logger.Level.FINEST = new goog.debug.Logger.Level('FINEST', 300);\n\n\n/**\n * ALL indicates that all messages should be logged.\n * This level is initialized to <CODE>0</CODE>.\n * @type {!goog.debug.Logger.Level}\n */\ngoog.debug.Logger.Level.ALL = new goog.debug.Logger.Level('ALL', 0);\n\n\n/**\n * The predefined levels.\n * @type {!Array<!goog.debug.Logger.Level>}\n * @final\n */\ngoog.debug.Logger.Level.PREDEFINED_LEVELS = [\n  goog.debug.Logger.Level.OFF, goog.debug.Logger.Level.SHOUT,\n  goog.debug.Logger.Level.SEVERE, goog.debug.Logger.Level.WARNING,\n  goog.debug.Logger.Level.INFO, goog.debug.Logger.Level.CONFIG,\n  goog.debug.Logger.Level.FINE, goog.debug.Logger.Level.FINER,\n  goog.debug.Logger.Level.FINEST, goog.debug.Logger.Level.ALL\n];\n\n\n/**\n * A lookup map used to find the level object based on the name or value of\n * the level object.\n * @type {?Object}\n * @private\n */\ngoog.debug.Logger.Level.predefinedLevelsCache_ = null;\n\n\n/**\n * Creates the predefined levels cache and populates it.\n * @private\n */\ngoog.debug.Logger.Level.createPredefinedLevelsCache_ = function() {\n  goog.debug.Logger.Level.predefinedLevelsCache_ = {};\n  for (var i = 0, level; level = goog.debug.Logger.Level.PREDEFINED_LEVELS[i];\n       i++) {\n    goog.debug.Logger.Level.predefinedLevelsCache_[level.value] = level;\n    goog.debug.Logger.Level.predefinedLevelsCache_[level.name] = level;\n  }\n};\n\n\n/**\n * Gets the predefined level with the given name.\n * @param {string} name The name of the level.\n * @return {goog.debug.Logger.Level} The level, or null if none found.\n */\ngoog.debug.Logger.Level.getPredefinedLevel = function(name) {\n  if (!goog.debug.Logger.Level.predefinedLevelsCache_) {\n    goog.debug.Logger.Level.createPredefinedLevelsCache_();\n  }\n\n  return goog.debug.Logger.Level.predefinedLevelsCache_[name] || null;\n};\n\n\n/**\n * Gets the highest predefined level <= #value.\n * @param {number} value Level value.\n * @return {goog.debug.Logger.Level} The level, or null if none found.\n */\ngoog.debug.Logger.Level.getPredefinedLevelByValue = function(value) {\n  if (!goog.debug.Logger.Level.predefinedLevelsCache_) {\n    goog.debug.Logger.Level.createPredefinedLevelsCache_();\n  }\n\n  if (value in /** @type {!Object} */ (\n          goog.debug.Logger.Level.predefinedLevelsCache_)) {\n    return goog.debug.Logger.Level.predefinedLevelsCache_[value];\n  }\n\n  for (var i = 0; i < goog.debug.Logger.Level.PREDEFINED_LEVELS.length; ++i) {\n    var level = goog.debug.Logger.Level.PREDEFINED_LEVELS[i];\n    if (level.value <= value) {\n      return level;\n    }\n  }\n  return null;\n};\n\n\n/**\n * Finds or creates a logger for a named subsystem. If a logger has already been\n * created with the given name it is returned. Otherwise a new logger is\n * created. If a new logger is created its log level will be configured based\n * on the LogManager configuration and it will configured to also send logging\n * output to its parent's handlers. It will be registered in the LogManager\n * global namespace.\n *\n * @param {string} name A name for the logger. This should be a dot-separated\n * name and should normally be based on the package name or class name of the\n * subsystem, such as goog.net.BrowserChannel.\n * @return {!goog.debug.Logger} The named logger.\n * @deprecated use {@link goog.log} instead.\n */\ngoog.debug.Logger.getLogger = function(name) {\n  return goog.debug.LogManager.getLogger(name);\n};\n\n\n/**\n * Logs a message to profiling tools, if available.\n * {@see https://developers.google.com/web-toolkit/speedtracer/logging-api}\n * {@see http://msdn.microsoft.com/en-us/library/dd433074(VS.85).aspx}\n * @param {string} msg The message to log.\n */\ngoog.debug.Logger.logToProfilers = function(msg) {\n  // Some browsers also log timeStamp calls to the console, only log\n  // if actually asked.\n  if (goog.debug.Logger.ENABLE_PROFILER_LOGGING) {\n    var msWriteProfilerMark = goog.global['msWriteProfilerMark'];\n    if (msWriteProfilerMark) {\n      // Logs a message to the Microsoft profiler\n      // On IE, console['timeStamp'] may output to console\n      msWriteProfilerMark(msg);\n      return;\n    }\n\n    // Using goog.global, as loggers might be used in window-less contexts.\n    var console = goog.global['console'];\n    if (console && console['timeStamp']) {\n      // Logs a message to Firebug, Web Inspector, SpeedTracer, etc.\n      console['timeStamp'](msg);\n    }\n  }\n};\n\n\n/**\n * Gets the name of this logger.\n * @return {string} The name of this logger.\n */\ngoog.debug.Logger.prototype.getName = function() {\n  return this.name_;\n};\n\n\n/**\n * Adds a handler to the logger. This doesn't use the event system because\n * we want to be able to add logging to the event system.\n * @param {Function} handler Handler function to add.\n */\ngoog.debug.Logger.prototype.addHandler = function(handler) {\n  if (goog.debug.LOGGING_ENABLED) {\n    if (goog.debug.Logger.ENABLE_HIERARCHY) {\n      if (!this.handlers_) {\n        this.handlers_ = [];\n      }\n      this.handlers_.push(handler);\n    } else {\n      goog.asserts.assert(\n          !this.name_, 'Cannot call addHandler on a non-root logger when ' +\n              'goog.debug.Logger.ENABLE_HIERARCHY is false.');\n      goog.debug.Logger.rootHandlers_.push(handler);\n    }\n  }\n};\n\n\n/**\n * Removes a handler from the logger. This doesn't use the event system because\n * we want to be able to add logging to the event system.\n * @param {Function} handler Handler function to remove.\n * @return {boolean} Whether the handler was removed.\n */\ngoog.debug.Logger.prototype.removeHandler = function(handler) {\n  if (goog.debug.LOGGING_ENABLED) {\n    var handlers = goog.debug.Logger.ENABLE_HIERARCHY ?\n        this.handlers_ :\n        goog.debug.Logger.rootHandlers_;\n    return !!handlers && goog.array.remove(handlers, handler);\n  } else {\n    return false;\n  }\n};\n\n\n/**\n * Returns the parent of this logger.\n * @return {goog.debug.Logger} The parent logger or null if this is the root.\n */\ngoog.debug.Logger.prototype.getParent = function() {\n  return this.parent_;\n};\n\n\n/**\n * Returns the children of this logger as a map of the child name to the logger.\n * @return {!Object} The map where the keys are the child leaf names and the\n *     values are the Logger objects.\n */\ngoog.debug.Logger.prototype.getChildren = function() {\n  if (!this.children_) {\n    this.children_ = {};\n  }\n  return this.children_;\n};\n\n\n/**\n * Set the log level specifying which message levels will be logged by this\n * logger. Message levels lower than this value will be discarded.\n * The level value Level.OFF can be used to turn off logging. If the new level\n * is null, it means that this node should inherit its level from its nearest\n * ancestor with a specific (non-null) level value.\n *\n * @param {goog.debug.Logger.Level} level The new level.\n */\ngoog.debug.Logger.prototype.setLevel = function(level) {\n  if (goog.debug.LOGGING_ENABLED) {\n    if (goog.debug.Logger.ENABLE_HIERARCHY) {\n      this.level_ = level;\n    } else {\n      goog.asserts.assert(\n          !this.name_, 'Cannot call setLevel() on a non-root logger when ' +\n              'goog.debug.Logger.ENABLE_HIERARCHY is false.');\n      goog.debug.Logger.rootLevel_ = level;\n    }\n  }\n};\n\n\n/**\n * Gets the log level specifying which message levels will be logged by this\n * logger. Message levels lower than this value will be discarded.\n * The level value Level.OFF can be used to turn off logging. If the level\n * is null, it means that this node should inherit its level from its nearest\n * ancestor with a specific (non-null) level value.\n *\n * @return {goog.debug.Logger.Level} The level.\n */\ngoog.debug.Logger.prototype.getLevel = function() {\n  return goog.debug.LOGGING_ENABLED ? this.level_ : goog.debug.Logger.Level.OFF;\n};\n\n\n/**\n * Returns the effective level of the logger based on its ancestors' levels.\n * @return {goog.debug.Logger.Level} The level.\n */\ngoog.debug.Logger.prototype.getEffectiveLevel = function() {\n  if (!goog.debug.LOGGING_ENABLED) {\n    return goog.debug.Logger.Level.OFF;\n  }\n\n  if (!goog.debug.Logger.ENABLE_HIERARCHY) {\n    return goog.debug.Logger.rootLevel_;\n  }\n  if (this.level_) {\n    return this.level_;\n  }\n  if (this.parent_) {\n    return this.parent_.getEffectiveLevel();\n  }\n  goog.asserts.fail('Root logger has no level set.');\n  return null;\n};\n\n\n/**\n * Checks if a message of the given level would actually be logged by this\n * logger. This check is based on the Loggers effective level, which may be\n * inherited from its parent.\n * @param {goog.debug.Logger.Level} level The level to check.\n * @return {boolean} Whether the message would be logged.\n */\ngoog.debug.Logger.prototype.isLoggable = function(level) {\n  return goog.debug.LOGGING_ENABLED &&\n      level.value >= this.getEffectiveLevel().value;\n};\n\n\n/**\n * Logs a message. If the logger is currently enabled for the\n * given message level then the given message is forwarded to all the\n * registered output Handler objects.\n * @param {goog.debug.Logger.Level} level One of the level identifiers.\n * @param {goog.debug.Loggable} msg The message to log.\n * @param {Error|Object=} opt_exception An exception associated with the\n *     message.\n */\ngoog.debug.Logger.prototype.log = function(level, msg, opt_exception) {\n  // java caches the effective level, not sure it's necessary here\n  if (goog.debug.LOGGING_ENABLED && this.isLoggable(level)) {\n    // Message callbacks can be useful when a log message is expensive to build.\n    if (goog.isFunction(msg)) {\n      msg = msg();\n    }\n\n    this.doLogRecord_(this.getLogRecord(level, msg, opt_exception));\n  }\n};\n\n\n/**\n * Creates a new log record and adds the exception (if present) to it.\n * @param {goog.debug.Logger.Level} level One of the level identifiers.\n * @param {string} msg The string message.\n * @param {Error|Object=} opt_exception An exception associated with the\n *     message.\n * @return {!goog.debug.LogRecord} A log record.\n * @suppress {es5Strict}\n */\ngoog.debug.Logger.prototype.getLogRecord = function(level, msg, opt_exception) {\n  if (goog.debug.LogBuffer.isBufferingEnabled()) {\n    var logRecord =\n        goog.debug.LogBuffer.getInstance().addRecord(level, msg, this.name_);\n  } else {\n    logRecord = new goog.debug.LogRecord(level, String(msg), this.name_);\n  }\n  if (opt_exception) {\n    logRecord.setException(opt_exception);\n  }\n  return logRecord;\n};\n\n\n/**\n * Logs a message at the Logger.Level.SHOUT level.\n * If the logger is currently enabled for the given message level then the\n * given message is forwarded to all the registered output Handler objects.\n * @param {goog.debug.Loggable} msg The message to log.\n * @param {Error=} opt_exception An exception associated with the message.\n */\ngoog.debug.Logger.prototype.shout = function(msg, opt_exception) {\n  if (goog.debug.LOGGING_ENABLED) {\n    this.log(goog.debug.Logger.Level.SHOUT, msg, opt_exception);\n  }\n};\n\n\n/**\n * Logs a message at the Logger.Level.SEVERE level.\n * If the logger is currently enabled for the given message level then the\n * given message is forwarded to all the registered output Handler objects.\n * @param {goog.debug.Loggable} msg The message to log.\n * @param {Error=} opt_exception An exception associated with the message.\n */\ngoog.debug.Logger.prototype.severe = function(msg, opt_exception) {\n  if (goog.debug.LOGGING_ENABLED) {\n    this.log(goog.debug.Logger.Level.SEVERE, msg, opt_exception);\n  }\n};\n\n\n/**\n * Logs a message at the Logger.Level.WARNING level.\n * If the logger is currently enabled for the given message level then the\n * given message is forwarded to all the registered output Handler objects.\n * @param {goog.debug.Loggable} msg The message to log.\n * @param {Error=} opt_exception An exception associated with the message.\n */\ngoog.debug.Logger.prototype.warning = function(msg, opt_exception) {\n  if (goog.debug.LOGGING_ENABLED) {\n    this.log(goog.debug.Logger.Level.WARNING, msg, opt_exception);\n  }\n};\n\n\n/**\n * Logs a message at the Logger.Level.INFO level.\n * If the logger is currently enabled for the given message level then the\n * given message is forwarded to all the registered output Handler objects.\n * @param {goog.debug.Loggable} msg The message to log.\n * @param {Error=} opt_exception An exception associated with the message.\n */\ngoog.debug.Logger.prototype.info = function(msg, opt_exception) {\n  if (goog.debug.LOGGING_ENABLED) {\n    this.log(goog.debug.Logger.Level.INFO, msg, opt_exception);\n  }\n};\n\n\n/**\n * Logs a message at the Logger.Level.CONFIG level.\n * If the logger is currently enabled for the given message level then the\n * given message is forwarded to all the registered output Handler objects.\n * @param {goog.debug.Loggable} msg The message to log.\n * @param {Error=} opt_exception An exception associated with the message.\n */\ngoog.debug.Logger.prototype.config = function(msg, opt_exception) {\n  if (goog.debug.LOGGING_ENABLED) {\n    this.log(goog.debug.Logger.Level.CONFIG, msg, opt_exception);\n  }\n};\n\n\n/**\n * Logs a message at the Logger.Level.FINE level.\n * If the logger is currently enabled for the given message level then the\n * given message is forwarded to all the registered output Handler objects.\n * @param {goog.debug.Loggable} msg The message to log.\n * @param {Error=} opt_exception An exception associated with the message.\n */\ngoog.debug.Logger.prototype.fine = function(msg, opt_exception) {\n  if (goog.debug.LOGGING_ENABLED) {\n    this.log(goog.debug.Logger.Level.FINE, msg, opt_exception);\n  }\n};\n\n\n/**\n * Logs a message at the Logger.Level.FINER level.\n * If the logger is currently enabled for the given message level then the\n * given message is forwarded to all the registered output Handler objects.\n * @param {goog.debug.Loggable} msg The message to log.\n * @param {Error=} opt_exception An exception associated with the message.\n */\ngoog.debug.Logger.prototype.finer = function(msg, opt_exception) {\n  if (goog.debug.LOGGING_ENABLED) {\n    this.log(goog.debug.Logger.Level.FINER, msg, opt_exception);\n  }\n};\n\n\n/**\n * Logs a message at the Logger.Level.FINEST level.\n * If the logger is currently enabled for the given message level then the\n * given message is forwarded to all the registered output Handler objects.\n * @param {goog.debug.Loggable} msg The message to log.\n * @param {Error=} opt_exception An exception associated with the message.\n */\ngoog.debug.Logger.prototype.finest = function(msg, opt_exception) {\n  if (goog.debug.LOGGING_ENABLED) {\n    this.log(goog.debug.Logger.Level.FINEST, msg, opt_exception);\n  }\n};\n\n\n/**\n * Logs a LogRecord. If the logger is currently enabled for the\n * given message level then the given message is forwarded to all the\n * registered output Handler objects.\n * @param {goog.debug.LogRecord} logRecord A log record to log.\n */\ngoog.debug.Logger.prototype.logRecord = function(logRecord) {\n  if (goog.debug.LOGGING_ENABLED && this.isLoggable(logRecord.getLevel())) {\n    this.doLogRecord_(logRecord);\n  }\n};\n\n\n/**\n * Logs a LogRecord.\n * @param {goog.debug.LogRecord} logRecord A log record to log.\n * @private\n */\ngoog.debug.Logger.prototype.doLogRecord_ = function(logRecord) {\n  if (goog.debug.Logger.ENABLE_PROFILER_LOGGING) {\n    goog.debug.Logger.logToProfilers('log:' + logRecord.getMessage());\n  }\n  if (goog.debug.Logger.ENABLE_HIERARCHY) {\n    var target = this;\n    while (target) {\n      target.callPublish_(logRecord);\n      target = target.getParent();\n    }\n  } else {\n    for (var i = 0, handler; handler = goog.debug.Logger.rootHandlers_[i++];) {\n      handler(logRecord);\n    }\n  }\n};\n\n\n/**\n * Calls the handlers for publish.\n * @param {goog.debug.LogRecord} logRecord The log record to publish.\n * @private\n */\ngoog.debug.Logger.prototype.callPublish_ = function(logRecord) {\n  if (this.handlers_) {\n    for (var i = 0, handler; handler = this.handlers_[i]; i++) {\n      handler(logRecord);\n    }\n  }\n};\n\n\n/**\n * Sets the parent of this logger. This is used for setting up the logger tree.\n * @param {goog.debug.Logger} parent The parent logger.\n * @private\n */\ngoog.debug.Logger.prototype.setParent_ = function(parent) {\n  this.parent_ = parent;\n};\n\n\n/**\n * Adds a child to this logger. This is used for setting up the logger tree.\n * @param {string} name The leaf name of the child.\n * @param {goog.debug.Logger} logger The child logger.\n * @private\n */\ngoog.debug.Logger.prototype.addChild_ = function(name, logger) {\n  this.getChildren()[name] = logger;\n};\n\n\n/**\n * There is a single global LogManager object that is used to maintain a set of\n * shared state about Loggers and log services. This is loosely based on the\n * java class java.util.logging.LogManager.\n * @const\n */\ngoog.debug.LogManager = {};\n\n\n/**\n * Map of logger names to logger objects.\n *\n * @type {!Object<string, !goog.debug.Logger>}\n * @private\n */\ngoog.debug.LogManager.loggers_ = {};\n\n\n/**\n * The root logger which is the root of the logger tree.\n * @type {?goog.debug.Logger}\n * @private\n */\ngoog.debug.LogManager.rootLogger_ = null;\n\n\n/**\n * Initializes the LogManager if not already initialized.\n */\ngoog.debug.LogManager.initialize = function() {\n  if (!goog.debug.LogManager.rootLogger_) {\n    goog.debug.LogManager.rootLogger_ =\n        new goog.debug.Logger(goog.debug.Logger.ROOT_LOGGER_NAME);\n    goog.debug.LogManager.loggers_[goog.debug.Logger.ROOT_LOGGER_NAME] =\n        goog.debug.LogManager.rootLogger_;\n    goog.debug.LogManager.rootLogger_.setLevel(goog.debug.Logger.Level.CONFIG);\n  }\n};\n\n\n/**\n * Returns all the loggers.\n * @return {!Object<string, !goog.debug.Logger>} Map of logger names to logger\n *     objects.\n */\ngoog.debug.LogManager.getLoggers = function() {\n  return goog.debug.LogManager.loggers_;\n};\n\n\n/**\n * Returns the root of the logger tree namespace, the logger with the empty\n * string as its name.\n *\n * @return {!goog.debug.Logger} The root logger.\n */\ngoog.debug.LogManager.getRoot = function() {\n  goog.debug.LogManager.initialize();\n  return /** @type {!goog.debug.Logger} */ (goog.debug.LogManager.rootLogger_);\n};\n\n\n/**\n * Finds a named logger.\n *\n * @param {string} name A name for the logger. This should be a dot-separated\n * name and should normally be based on the package name or class name of the\n * subsystem, such as goog.net.BrowserChannel.\n * @return {!goog.debug.Logger} The named logger.\n */\ngoog.debug.LogManager.getLogger = function(name) {\n  goog.debug.LogManager.initialize();\n  var ret = goog.debug.LogManager.loggers_[name];\n  return ret || goog.debug.LogManager.createLogger_(name);\n};\n\n\n/**\n * Creates a function that can be passed to goog.debug.catchErrors. The function\n * will log all reported errors using the given logger.\n * @param {goog.debug.Logger=} opt_logger The logger to log the errors to.\n *     Defaults to the root logger.\n * @return {function(Object)} The created function.\n */\ngoog.debug.LogManager.createFunctionForCatchErrors = function(opt_logger) {\n  return function(info) {\n    var logger = opt_logger || goog.debug.LogManager.getRoot();\n    logger.severe(\n        'Error: ' + info.message + ' (' + info.fileName + ' @ Line: ' +\n        info.line + ')');\n  };\n};\n\n\n/**\n * Creates the named logger. Will also create the parents of the named logger\n * if they don't yet exist.\n * @param {string} name The name of the logger.\n * @return {!goog.debug.Logger} The named logger.\n * @private\n */\ngoog.debug.LogManager.createLogger_ = function(name) {\n  // find parent logger\n  var logger = new goog.debug.Logger(name);\n  if (goog.debug.Logger.ENABLE_HIERARCHY) {\n    var lastDotIndex = name.lastIndexOf('.');\n    var parentName = name.substr(0, lastDotIndex);\n    var leafName = name.substr(lastDotIndex + 1);\n    var parentLogger = goog.debug.LogManager.getLogger(parentName);\n\n    // tell the parent about the child and the child about the parent\n    parentLogger.addChild_(leafName, logger);\n    logger.setParent_(parentLogger);\n  }\n\n  goog.debug.LogManager.loggers_[name] = logger;\n  return logger;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Constants for HTTP status codes.\n */\n\ngoog.provide('goog.net.HttpStatus');\n\n\n/**\n * HTTP Status Codes defined in RFC 2616, RFC 6585, RFC 4918 and RFC 7538.\n * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html\n * @see http://tools.ietf.org/html/rfc6585\n * @see https://tools.ietf.org/html/rfc4918\n * @see https://tools.ietf.org/html/rfc7538\n * @enum {number}\n */\ngoog.net.HttpStatus = {\n  // Informational 1xx\n  CONTINUE: 100,\n  SWITCHING_PROTOCOLS: 101,\n\n  // Successful 2xx\n  OK: 200,\n  CREATED: 201,\n  ACCEPTED: 202,\n  NON_AUTHORITATIVE_INFORMATION: 203,\n  NO_CONTENT: 204,\n  RESET_CONTENT: 205,\n  PARTIAL_CONTENT: 206,\n  MULTI_STATUS: 207,\n\n  // Redirection 3xx\n  MULTIPLE_CHOICES: 300,\n  MOVED_PERMANENTLY: 301,\n  FOUND: 302,\n  SEE_OTHER: 303,\n  NOT_MODIFIED: 304,\n  USE_PROXY: 305,\n  TEMPORARY_REDIRECT: 307,\n  PERMANENT_REDIRECT: 308,\n\n  // Client Error 4xx\n  BAD_REQUEST: 400,\n  UNAUTHORIZED: 401,\n  PAYMENT_REQUIRED: 402,\n  FORBIDDEN: 403,\n  NOT_FOUND: 404,\n  METHOD_NOT_ALLOWED: 405,\n  NOT_ACCEPTABLE: 406,\n  PROXY_AUTHENTICATION_REQUIRED: 407,\n  REQUEST_TIMEOUT: 408,\n  CONFLICT: 409,\n  GONE: 410,\n  LENGTH_REQUIRED: 411,\n  PRECONDITION_FAILED: 412,\n  REQUEST_ENTITY_TOO_LARGE: 413,\n  REQUEST_URI_TOO_LONG: 414,\n  UNSUPPORTED_MEDIA_TYPE: 415,\n  REQUEST_RANGE_NOT_SATISFIABLE: 416,\n  EXPECTATION_FAILED: 417,\n  UNPROCESSABLE_ENTITY: 422,\n  LOCKED: 423,\n  FAILED_DEPENDENCY: 424,\n  PRECONDITION_REQUIRED: 428,\n  TOO_MANY_REQUESTS: 429,\n  REQUEST_HEADER_FIELDS_TOO_LARGE: 431,\n\n  // Server Error 5xx\n  INTERNAL_SERVER_ERROR: 500,\n  NOT_IMPLEMENTED: 501,\n  BAD_GATEWAY: 502,\n  SERVICE_UNAVAILABLE: 503,\n  GATEWAY_TIMEOUT: 504,\n  HTTP_VERSION_NOT_SUPPORTED: 505,\n  INSUFFICIENT_STORAGE: 507,\n  NETWORK_AUTHENTICATION_REQUIRED: 511,\n\n  /*\n   * IE returns this code for 204 due to its use of URLMon, which returns this\n   * code for 'Operation Aborted'. The status text is 'Unknown', the response\n   * headers are ''. Known to occur on IE 6 on XP through IE9 on Win7.\n   */\n  QUIRK_IE_NO_CONTENT: 1223,\n};\n\n\n/**\n * Returns whether the given status should be considered successful.\n *\n * Successful codes are OK (200), CREATED (201), ACCEPTED (202),\n * NO CONTENT (204), PARTIAL CONTENT (206), NOT MODIFIED (304),\n * and IE's no content code (1223).\n *\n * @param {number} status The status code to test.\n * @return {boolean} Whether the status code should be considered successful.\n */\ngoog.net.HttpStatus.isSuccess = function(status) {\n  switch (status) {\n    case goog.net.HttpStatus.OK:\n    case goog.net.HttpStatus.CREATED:\n    case goog.net.HttpStatus.ACCEPTED:\n    case goog.net.HttpStatus.NO_CONTENT:\n    case goog.net.HttpStatus.PARTIAL_CONTENT:\n    case goog.net.HttpStatus.NOT_MODIFIED:\n    case goog.net.HttpStatus.QUIRK_IE_NO_CONTENT:\n      return true;\n\n    default:\n      return false;\n  }\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Utilities for creating functions. Loosely inspired by these\n * java classes from the Guava library:\n * com.google.common.base.Functions\n * https://google.github.io/guava/releases/snapshot-jre/api/docs/index.html?com/google/common/base/Functions.html\n *\n * com.google.common.base.Predicates\n * https://google.github.io/guava/releases/snapshot-jre/api/docs/index.html?com/google/common/base/Predicates.html\n *\n * More about these can be found at\n * https://github.com/google/guava/wiki/FunctionalExplained\n */\n\n\ngoog.provide('goog.functions');\n\n\n/**\n * Creates a function that always returns the same value.\n * @param {T} retValue The value to return.\n * @return {function():T} The new function.\n * @template T\n */\ngoog.functions.constant = function(retValue) {\n  return function() { return retValue; };\n};\n\n\n/**\n * Always returns false.\n * @type {function(...): boolean}\n */\ngoog.functions.FALSE = function() {\n  return false;\n};\n\n\n/**\n * Always returns true.\n * @type {function(...): boolean}\n */\ngoog.functions.TRUE = function() {\n  return true;\n};\n\n\n/**\n * Always returns NULL.\n * @type {function(...): null}\n */\ngoog.functions.NULL = function() {\n  return null;\n};\n\n\n/**\n * A simple function that returns the first argument of whatever is passed\n * into it.\n * @param {T=} opt_returnValue The single value that will be returned.\n * @param {...*} var_args Optional trailing arguments. These are ignored.\n * @return {T} The first argument passed in, or undefined if nothing was passed.\n * @template T\n */\ngoog.functions.identity = function(opt_returnValue, var_args) {\n  return opt_returnValue;\n};\n\n\n/**\n * Creates a function that always throws an error with the given message.\n * @param {string} message The error message.\n * @return {!Function} The error-throwing function.\n */\ngoog.functions.error = function(message) {\n  return function() {\n    throw new Error(message);\n  };\n};\n\n\n/**\n * Creates a function that throws the given object.\n * @param {*} err An object to be thrown.\n * @return {!Function} The error-throwing function.\n */\ngoog.functions.fail = function(err) {\n  return function() { throw err; };\n};\n\n\n/**\n * Given a function, create a function that keeps opt_numArgs arguments and\n * silently discards all additional arguments.\n * @param {Function} f The original function.\n * @param {number=} opt_numArgs The number of arguments to keep. Defaults to 0.\n * @return {!Function} A version of f that only keeps the first opt_numArgs\n *     arguments.\n */\ngoog.functions.lock = function(f, opt_numArgs) {\n  opt_numArgs = opt_numArgs || 0;\n  return function() {\n    const self = /** @type {*} */ (this);\n    return f.apply(self, Array.prototype.slice.call(arguments, 0, opt_numArgs));\n  };\n};\n\n\n/**\n * Creates a function that returns its nth argument.\n * @param {number} n The position of the return argument.\n * @return {!Function} A new function.\n */\ngoog.functions.nth = function(n) {\n  return function() { return arguments[n]; };\n};\n\n\n/**\n * Like goog.partial(), except that arguments are added after arguments to the\n * returned function.\n *\n * Usage:\n * function f(arg1, arg2, arg3, arg4) { ... }\n * var g = goog.functions.partialRight(f, arg3, arg4);\n * g(arg1, arg2);\n *\n * @param {!Function} fn A function to partially apply.\n * @param {...*} var_args Additional arguments that are partially applied to fn\n *     at the end.\n * @return {!Function} A partially-applied form of the function goog.partial()\n *     was invoked as a method of.\n */\ngoog.functions.partialRight = function(fn, var_args) {\n  const rightArgs = Array.prototype.slice.call(arguments, 1);\n  return function() {\n    const self = /** @type {*} */ (this);\n    const newArgs = Array.prototype.slice.call(arguments);\n    newArgs.push.apply(newArgs, rightArgs);\n    return fn.apply(self, newArgs);\n  };\n};\n\n\n/**\n * Given a function, create a new function that swallows its return value\n * and replaces it with a new one.\n * @param {Function} f A function.\n * @param {T} retValue A new return value.\n * @return {function(...?):T} A new function.\n * @template T\n */\ngoog.functions.withReturnValue = function(f, retValue) {\n  return goog.functions.sequence(f, goog.functions.constant(retValue));\n};\n\n\n/**\n * Creates a function that returns whether its argument equals the given value.\n *\n * Example:\n * var key = goog.object.findKey(obj, goog.functions.equalTo('needle'));\n *\n * @param {*} value The value to compare to.\n * @param {boolean=} opt_useLooseComparison Whether to use a loose (==)\n *     comparison rather than a strict (===) one. Defaults to false.\n * @return {function(*):boolean} The new function.\n */\ngoog.functions.equalTo = function(value, opt_useLooseComparison) {\n  return function(other) {\n    return opt_useLooseComparison ? (value == other) : (value === other);\n  };\n};\n\n\n/**\n * Creates the composition of the functions passed in.\n * For example, (goog.functions.compose(f, g))(a) is equivalent to f(g(a)).\n * @param {function(...?):T} fn The final function.\n * @param {...Function} var_args A list of functions.\n * @return {function(...?):T} The composition of all inputs.\n * @template T\n */\ngoog.functions.compose = function(fn, var_args) {\n  const functions = arguments;\n  const length = functions.length;\n  return function() {\n    const self = /** @type {*} */ (this);\n    let result;\n    if (length) {\n      result = functions[length - 1].apply(self, arguments);\n    }\n\n    for (let i = length - 2; i >= 0; i--) {\n      result = functions[i].call(self, result);\n    }\n    return result;\n  };\n};\n\n\n/**\n * Creates a function that calls the functions passed in in sequence, and\n * returns the value of the last function. For example,\n * (goog.functions.sequence(f, g))(x) is equivalent to f(x),g(x).\n * @param {...Function} var_args A list of functions.\n * @return {!Function} A function that calls all inputs in sequence.\n */\ngoog.functions.sequence = function(var_args) {\n  const functions = arguments;\n  const length = functions.length;\n  return function() {\n    const self = /** @type {*} */ (this);\n    let result;\n    for (let i = 0; i < length; i++) {\n      result = functions[i].apply(self, arguments);\n    }\n    return result;\n  };\n};\n\n\n/**\n * Creates a function that returns true if each of its components evaluates\n * to true. The components are evaluated in order, and the evaluation will be\n * short-circuited as soon as a function returns false.\n * For example, (goog.functions.and(f, g))(x) is equivalent to f(x) && g(x).\n * @param {...Function} var_args A list of functions.\n * @return {function(...?):boolean} A function that ANDs its component\n *      functions.\n */\ngoog.functions.and = function(var_args) {\n  const functions = arguments;\n  const length = functions.length;\n  return function() {\n    const self = /** @type {*} */ (this);\n    for (let i = 0; i < length; i++) {\n      if (!functions[i].apply(self, arguments)) {\n        return false;\n      }\n    }\n    return true;\n  };\n};\n\n\n/**\n * Creates a function that returns true if any of its components evaluates\n * to true. The components are evaluated in order, and the evaluation will be\n * short-circuited as soon as a function returns true.\n * For example, (goog.functions.or(f, g))(x) is equivalent to f(x) || g(x).\n * @param {...Function} var_args A list of functions.\n * @return {function(...?):boolean} A function that ORs its component\n *    functions.\n */\ngoog.functions.or = function(var_args) {\n  const functions = arguments;\n  const length = functions.length;\n  return function() {\n    const self = /** @type {*} */ (this);\n    for (let i = 0; i < length; i++) {\n      if (functions[i].apply(self, arguments)) {\n        return true;\n      }\n    }\n    return false;\n  };\n};\n\n\n/**\n * Creates a function that returns the Boolean opposite of a provided function.\n * For example, (goog.functions.not(f))(x) is equivalent to !f(x).\n * @param {!Function} f The original function.\n * @return {function(...?):boolean} A function that delegates to f and returns\n * opposite.\n */\ngoog.functions.not = function(f) {\n  return function() {\n    const self = /** @type {*} */ (this);\n    return !f.apply(self, arguments);\n  };\n};\n\n\n/**\n * Generic factory function to construct an object given the constructor\n * and the arguments. Intended to be bound to create object factories.\n *\n * Example:\n *\n * var factory = goog.partial(goog.functions.create, Class);\n *\n * @param {function(new:T, ...)} constructor The constructor for the Object.\n * @param {...*} var_args The arguments to be passed to the constructor.\n * @return {T} A new instance of the class given in `constructor`.\n * @template T\n * @deprecated This function does not work with ES6 class constructors. Use\n *     arrow functions + spread args instead.\n */\ngoog.functions.create = function(constructor, var_args) {\n  /**\n   * @constructor\n   * @final\n   */\n  const temp = function() {};\n  temp.prototype = constructor.prototype;\n\n  // obj will have constructor's prototype in its chain and\n  // 'obj instanceof constructor' will be true.\n  const obj = new temp();\n\n  // obj is initialized by constructor.\n  // arguments is only array-like so lacks shift(), but can be used with\n  // the Array prototype function.\n  constructor.apply(obj, Array.prototype.slice.call(arguments, 1));\n  return obj;\n};\n\n\n/**\n * @define {boolean} Whether the return value cache should be used.\n *    This should only be used to disable caches when testing.\n */\ngoog.functions.CACHE_RETURN_VALUE =\n    goog.define('goog.functions.CACHE_RETURN_VALUE', true);\n\n\n/**\n * Gives a wrapper function that caches the return value of a parameterless\n * function when first called.\n *\n * When called for the first time, the given function is called and its\n * return value is cached (thus this is only appropriate for idempotent\n * functions).  Subsequent calls will return the cached return value. This\n * allows the evaluation of expensive functions to be delayed until first used.\n *\n * To cache the return values of functions with parameters, see goog.memoize.\n *\n * @param {function():T} fn A function to lazily evaluate.\n * @return {function():T} A wrapped version the function.\n * @template T\n */\ngoog.functions.cacheReturnValue = function(fn) {\n  let called = false;\n  let value;\n\n  return function() {\n    if (!goog.functions.CACHE_RETURN_VALUE) {\n      return fn();\n    }\n\n    if (!called) {\n      value = fn();\n      called = true;\n    }\n\n    return value;\n  };\n};\n\n\n/**\n * Wraps a function to allow it to be called, at most, once. All\n * additional calls are no-ops.\n *\n * This is particularly useful for initialization functions\n * that should be called, at most, once.\n *\n * @param {function():*} f Function to call.\n * @return {function():undefined} Wrapped function.\n */\ngoog.functions.once = function(f) {\n  // Keep a reference to the function that we null out when we're done with\n  // it -- that way, the function can be GC'd when we're done with it.\n  let inner = f;\n  return function() {\n    if (inner) {\n      const tmp = inner;\n      inner = null;\n      tmp();\n    }\n  };\n};\n\n\n/**\n * Wraps a function to allow it to be called, at most, once per interval\n * (specified in milliseconds). If the wrapper function is called N times within\n * that interval, only the Nth call will go through.\n *\n * This is particularly useful for batching up repeated actions where the\n * last action should win. This can be used, for example, for refreshing an\n * autocomplete pop-up every so often rather than updating with every keystroke,\n * since the final text typed by the user is the one that should produce the\n * final autocomplete results. For more stateful debouncing with support for\n * pausing, resuming, and canceling debounced actions, use\n * `goog.async.Debouncer`.\n *\n * @param {function(this:SCOPE, ...?)} f Function to call.\n * @param {number} interval Interval over which to debounce. The function will\n *     only be called after the full interval has elapsed since the last call.\n * @param {SCOPE=} opt_scope Object in whose scope to call the function.\n * @return {function(...?): undefined} Wrapped function.\n * @template SCOPE\n */\ngoog.functions.debounce = function(f, interval, opt_scope) {\n  let timeout = 0;\n  return /** @type {function(...?)} */ (function(var_args) {\n    goog.global.clearTimeout(timeout);\n    const args = arguments;\n    timeout = goog.global.setTimeout(function() {\n      f.apply(opt_scope, args);\n    }, interval);\n  });\n};\n\n\n/**\n * Wraps a function to allow it to be called, at most, once per interval\n * (specified in milliseconds). If the wrapper function is called N times in\n * that interval, both the 1st and the Nth calls will go through.\n *\n * This is particularly useful for limiting repeated user requests where the\n * the last action should win, but you also don't want to wait until the end of\n * the interval before sending a request out, as it leads to a perception of\n * slowness for the user.\n *\n * @param {function(this:SCOPE, ...?)} f Function to call.\n * @param {number} interval Interval over which to throttle. The function can\n *     only be called once per interval.\n * @param {SCOPE=} opt_scope Object in whose scope to call the function.\n * @return {function(...?): undefined} Wrapped function.\n * @template SCOPE\n */\ngoog.functions.throttle = function(f, interval, opt_scope) {\n  let timeout = 0;\n  let shouldFire = false;\n  let args = [];\n\n  const handleTimeout = function() {\n    timeout = 0;\n    if (shouldFire) {\n      shouldFire = false;\n      fire();\n    }\n  };\n\n  const fire = function() {\n    timeout = goog.global.setTimeout(handleTimeout, interval);\n    f.apply(opt_scope, args);\n  };\n\n  return /** @type {function(...?)} */ (function(var_args) {\n    args = arguments;\n    if (!timeout) {\n      fire();\n    } else {\n      shouldFire = true;\n    }\n  });\n};\n\n\n/**\n * Wraps a function to allow it to be called, at most, once per interval\n * (specified in milliseconds). If the wrapper function is called N times within\n * that interval, only the 1st call will go through.\n *\n * This is particularly useful for limiting repeated user requests where the\n * first request is guaranteed to have all the data required to perform the\n * final action, so there's no need to wait until the end of the interval before\n * sending the request out.\n *\n * @param {function(this:SCOPE, ...?)} f Function to call.\n * @param {number} interval Interval over which to rate-limit. The function will\n *     only be called once per interval, and ignored for the remainer of the\n *     interval.\n * @param {SCOPE=} opt_scope Object in whose scope to call the function.\n * @return {function(...?): undefined} Wrapped function.\n * @template SCOPE\n */\ngoog.functions.rateLimit = function(f, interval, opt_scope) {\n  let timeout = 0;\n\n  const handleTimeout = function() {\n    timeout = 0;\n  };\n\n  return /** @type {function(...?)} */ (function(var_args) {\n    if (!timeout) {\n      timeout = goog.global.setTimeout(handleTimeout, interval);\n      f.apply(opt_scope, arguments);\n    }\n  });\n};\n\n/**\n * Returns true if the specified value is a function.\n * @param {*} val Variable to test.\n * @return {boolean} Whether variable is a function.\n */\ngoog.functions.isFunction = (val) => {\n  return typeof val === 'function';\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Basic strippable logging definitions.\n * @see http://go/closurelogging\n */\n\ngoog.provide('goog.log');\ngoog.provide('goog.log.Level');\ngoog.provide('goog.log.LogRecord');\ngoog.provide('goog.log.Logger');\n\ngoog.require('goog.debug');\ngoog.require('goog.debug.LogBuffer');\ngoog.require('goog.debug.LogManager');\ngoog.require('goog.debug.LogRecord');\ngoog.require('goog.debug.Logger');\ngoog.requireType('goog.debug.Loggable');\n\n\n/** @define {boolean} Whether logging is enabled. */\ngoog.log.ENABLED = goog.define('goog.log.ENABLED', goog.debug.LOGGING_ENABLED);\n\n\n/** @const {string} */\ngoog.log.ROOT_LOGGER_NAME = goog.debug.Logger.ROOT_LOGGER_NAME;\n\n\n\n/**\n * @constructor\n * @final\n */\ngoog.log.Logger = goog.debug.Logger;\n\n\n\n/**\n * @constructor\n * @final\n */\ngoog.log.Level = goog.debug.Logger.Level;\n\n\n\n/**\n * @constructor\n * @final\n */\ngoog.log.LogRecord = goog.debug.LogRecord;\n\n\n/**\n * For access of goog.log.LogBuffer.CAPACITY only.\n * @constructor\n * @final\n */\ngoog.log.LogBuffer = goog.debug.LogBuffer;\n\n\n/**\n * Finds or creates a logger for a named subsystem. If a logger has already been\n * created with the given name it is returned. Otherwise a new logger is\n * created. If a new logger is created its log level will be configured based\n * on the goog.debug.LogManager configuration and it will configured to also\n * send logging output to its parent's handlers.\n * @see goog.debug.LogManager\n *\n * @param {string} name A name for the logger. This should be a dot-separated\n *     name and should normally be based on the package name or class name of\n *     the subsystem, such as goog.net.BrowserChannel.\n * @param {goog.log.Level=} opt_level If provided, override the\n *     default logging level with the provided level.\n * @return {goog.log.Logger} The named logger or null if logging is disabled.\n */\ngoog.log.getLogger = function(name, opt_level) {\n  if (goog.log.ENABLED) {\n    var logger = goog.debug.LogManager.getLogger(name);\n    if (opt_level && logger) {\n      logger.setLevel(opt_level);\n    }\n    return logger;\n  } else {\n    return null;\n  }\n};\n\n\n/**\n * Returns the root logger.\n *\n * @return {?goog.log.Logger} The root logger, or null if logging is disabled.\n */\ngoog.log.getRootLogger = function() {\n  return goog.log.getLogger(goog.log.ROOT_LOGGER_NAME);\n};\n\n\n// TODO(johnlenz): try to tighten the types to these functions.\n/**\n * Adds a handler to the logger. This doesn't use the event system because\n * we want to be able to add logging to the event system.\n * @param {goog.log.Logger} logger\n * @param {Function} handler Handler function to add.\n */\ngoog.log.addHandler = function(logger, handler) {\n  if (goog.log.ENABLED && logger) {\n    logger.addHandler(handler);\n  }\n};\n\n\n/**\n * Removes a handler from the logger. This doesn't use the event system because\n * we want to be able to add logging to the event system.\n * @param {goog.log.Logger} logger\n * @param {Function} handler Handler function to remove.\n * @return {boolean} Whether the handler was removed.\n */\ngoog.log.removeHandler = function(logger, handler) {\n  if (goog.log.ENABLED && logger) {\n    return logger.removeHandler(handler);\n  } else {\n    return false;\n  }\n};\n\n\n/**\n * Set the log level specifying which message levels will be logged by the\n * given logger.\n * @param {?goog.log.Logger} logger\n * @param {?goog.debug.Logger.Level} level The level to set.\n */\ngoog.log.setLevel = function(logger, level) {\n  if (goog.log.ENABLED && logger) {\n    logger.setLevel(level);\n  }\n};\n\n\n/**\n * Gets the log level specifying which message levels will be logged by this\n * logger. Message levels lower than this value will be discarded.\n * The level value Level.OFF can be used to turn off logging. If the level\n * is null, it means that this node should inherit its level from its\n * nearest ancestor with a specific (non-null) level value.\n * @param {?goog.log.Logger} logger\n * @return {?goog.debug.Logger.Level} The level.\n */\ngoog.log.getLevel = function(logger) {\n  if (goog.log.ENABLED && logger) {\n    return logger.getLevel();\n  }\n  return null;\n};\n\n\n/**\n * Returns the effective level of the logger based on its ancestors'\n * levels.\n * @param {?goog.log.Logger} logger\n * @return {?goog.debug.Logger.Level} The level.\n */\ngoog.log.getEffectiveLevel = function(logger) {\n  if (goog.log.ENABLED && logger) {\n    return logger.getEffectiveLevel();\n  }\n  return null;\n};\n\n\n/**\n * Checks if a message of the given level would actually be logged\n * by this logger. This check is based on the Loggers effective\n * level, which may be inherited from its parent.\n * @param {?goog.log.Logger} logger\n * @param {?goog.debug.Logger.Level} level The level to check.\n * @return {boolean} Whether the message would be logged.\n */\ngoog.log.isLoggable = function(logger, level) {\n  if (goog.log.ENABLED && logger) {\n    return logger.isLoggable(level);\n  }\n  return false;\n};\n\n\n/**\n * Creates a new log record and adds the exception (if present) to it.\n * @param {?goog.log.Logger} logger\n * @param {?goog.debug.Logger.Level} level One of the level identifiers.\n * @param {string} msg The string message.\n * @param {?Error|?Object=} opt_exception An exception associated with the\n *     message.\n * @return {?goog.debug.LogRecord} A log record.\n */\ngoog.log.getLogRecord = function(logger, level, msg, opt_exception) {\n  if (goog.log.ENABLED && logger) {\n    return logger.getLogRecord(level, msg, opt_exception);\n  }\n  return null;\n};\n\n\n/**\n * Logs a LogRecord. If the logger is currently enabled for the\n * given message level then the given message is forwarded to all the\n * registered output Handler objects.\n * @param {?goog.log.Logger} logger\n * @param {?goog.debug.LogRecord} logRecord A log record to log.\n */\ngoog.log.publishLogRecord = function(logger, logRecord) {\n  if (goog.log.ENABLED && logger) {\n    logger.logRecord(logRecord);\n  }\n};\n\n\n/**\n * Logs a message. If the logger is currently enabled for the\n * given message level then the given message is forwarded to all the\n * registered output Handler objects.\n * @param {goog.log.Logger} logger\n * @param {goog.log.Level} level One of the level identifiers.\n * @param {goog.debug.Loggable} msg The message to log.\n * @param {Error|Object=} opt_exception An exception associated with the\n *     message.\n */\ngoog.log.log = function(logger, level, msg, opt_exception) {\n  if (goog.log.ENABLED && logger) {\n    logger.log(level, msg, opt_exception);\n  }\n};\n\n\n/**\n * Logs a message at the Level.SEVERE level.\n * If the logger is currently enabled for the given message level then the\n * given message is forwarded to all the registered output Handler objects.\n * @param {goog.log.Logger} logger\n * @param {goog.debug.Loggable} msg The message to log.\n * @param {Error=} opt_exception An exception associated with the message.\n */\ngoog.log.error = function(logger, msg, opt_exception) {\n  if (goog.log.ENABLED && logger) {\n    logger.severe(msg, opt_exception);\n  }\n};\n\n\n/**\n * Logs a message at the Level.WARNING level.\n * If the logger is currently enabled for the given message level then the\n * given message is forwarded to all the registered output Handler objects.\n * @param {goog.log.Logger} logger\n * @param {goog.debug.Loggable} msg The message to log.\n * @param {Error=} opt_exception An exception associated with the message.\n */\ngoog.log.warning = function(logger, msg, opt_exception) {\n  if (goog.log.ENABLED && logger) {\n    logger.warning(msg, opt_exception);\n  }\n};\n\n\n/**\n * Logs a message at the Level.INFO level.\n * If the logger is currently enabled for the given message level then the\n * given message is forwarded to all the registered output Handler objects.\n * @param {goog.log.Logger} logger\n * @param {goog.debug.Loggable} msg The message to log.\n * @param {Error=} opt_exception An exception associated with the message.\n */\ngoog.log.info = function(logger, msg, opt_exception) {\n  if (goog.log.ENABLED && logger) {\n    logger.info(msg, opt_exception);\n  }\n};\n\n\n/**\n * Logs a message at the Level.Fine level.\n * If the logger is currently enabled for the given message level then the\n * given message is forwarded to all the registered output Handler objects.\n * @param {goog.log.Logger} logger\n * @param {goog.debug.Loggable} msg The message to log.\n * @param {Error=} opt_exception An exception associated with the message.\n */\ngoog.log.fine = function(logger, msg, opt_exception) {\n  if (goog.log.ENABLED && logger) {\n    logger.fine(msg, opt_exception);\n  }\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\ngoog.provide('goog.net.FetchXmlHttp');\ngoog.provide('goog.net.FetchXmlHttpFactory');\n\ngoog.require('goog.asserts');\ngoog.require('goog.events.EventTarget');\ngoog.require('goog.functions');\ngoog.require('goog.log');\ngoog.require('goog.net.XhrLike');\ngoog.require('goog.net.XmlHttpFactory');\n\n\n\n/**\n * Factory for creating Xhr objects that uses the native fetch() method.\n * https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\n * Note that this factory is intended for use in Service Worker only.\n * @param {!WorkerGlobalScope} worker The Service Worker global scope.\n * @extends {goog.net.XmlHttpFactory}\n * @struct\n * @constructor\n */\ngoog.net.FetchXmlHttpFactory = function(worker) {\n  goog.net.FetchXmlHttpFactory.base(this, 'constructor');\n\n  /** @private @final {!WorkerGlobalScope} */\n  this.worker_ = worker;\n\n  /** @private {!RequestCredentials|undefined} */\n  this.credentialsMode_ = undefined;\n\n  /** @private {!RequestCache|undefined} */\n  this.cacheMode_ = undefined;\n};\ngoog.inherits(goog.net.FetchXmlHttpFactory, goog.net.XmlHttpFactory);\n\n\n/** @override */\ngoog.net.FetchXmlHttpFactory.prototype.createInstance = function() {\n  var instance = new goog.net.FetchXmlHttp(this.worker_);\n  if (this.credentialsMode_) {\n    instance.setCredentialsMode(this.credentialsMode_);\n  }\n  if (this.cacheMode_) {\n    instance.setCacheMode(this.cacheMode_);\n  }\n  return instance;\n};\n\n\n/** @override */\ngoog.net.FetchXmlHttpFactory.prototype.internalGetOptions =\n    goog.functions.constant({});\n\n\n/**\n * @param {!RequestCredentials} credentialsMode The credentials mode of the\n *     Service Worker fetch.\n */\ngoog.net.FetchXmlHttpFactory.prototype.setCredentialsMode = function(\n    credentialsMode) {\n  this.credentialsMode_ = credentialsMode;\n};\n\n\n/**\n * @param {!RequestCache} cacheMode The cache mode of the Service Worker fetch.\n */\ngoog.net.FetchXmlHttpFactory.prototype.setCacheMode = function(cacheMode) {\n  this.cacheMode_ = cacheMode;\n};\n\n\n\n/**\n * FetchXmlHttp object constructor.\n * @param {!WorkerGlobalScope} worker\n * @extends {goog.events.EventTarget}\n * @implements {goog.net.XhrLike}\n * @constructor\n * @struct\n */\ngoog.net.FetchXmlHttp = function(worker) {\n  goog.net.FetchXmlHttp.base(this, 'constructor');\n\n  /** @private @final {!WorkerGlobalScope} */\n  this.worker_ = worker;\n\n  /** @private {RequestCredentials|undefined} */\n  this.credentialsMode_ = undefined;\n\n  /** @private {RequestCache|undefined} */\n  this.cacheMode_ = undefined;\n\n  /**\n   * Request state.\n   * @type {goog.net.FetchXmlHttp.RequestState}\n   */\n  this.readyState = goog.net.FetchXmlHttp.RequestState.UNSENT;\n\n  /**\n   * HTTP status.\n   * @type {number}\n   */\n  this.status = 0;\n\n  /**\n   * HTTP status string.\n   * @type {string}\n   */\n  this.statusText = '';\n\n  /**\n   * Content of the response.\n   * @type {string|!ArrayBuffer}\n   */\n  this.response = '';\n\n  /**\n   * Content of the response.\n   * @type {string}\n   */\n  this.responseText = '';\n\n  /**\n   * The type of the response.  If this is set to 'arraybuffer' the request will\n   * be discrete, streaming is only supported for text encoded requests.\n   * @type {string}\n   */\n  this.responseType = '';\n\n  /**\n   * Document response entity body.\n   * NOTE: This is always null and not supported by this class.\n   * @final {null}\n   */\n  this.responseXML = null;\n\n  /**\n   * Method to call when the state changes.\n   * @type {?function()}\n   */\n  this.onreadystatechange = null;\n\n  /** @private {!Headers} */\n  this.requestHeaders_ = new Headers();\n\n  /** @private {?Headers} */\n  this.responseHeaders_ = null;\n\n  /**\n   * Request method (GET or POST).\n   * @private {string}\n   */\n  this.method_ = 'GET';\n\n  /**\n   * Request URL.\n   * @private {string}\n   */\n  this.url_ = '';\n\n  /**\n   * Whether the request is in progress.\n   * @private {boolean}\n   */\n  this.inProgress_ = false;\n\n  /** @private @final {?goog.log.Logger} */\n  this.logger_ = goog.log.getLogger('goog.net.FetchXmlHttp');\n\n  /** @private {?Response} */\n  this.fetchResponse_ = null;\n\n  /** @private {!ReadableStreamDefaultReader|null} */\n  this.currentReader_ = null;\n\n  /** @private {?TextDecoder} */\n  this.textDecoder_ = null;\n};\ngoog.inherits(goog.net.FetchXmlHttp, goog.events.EventTarget);\n\n\n/**\n * State of the requests.\n * @enum {number}\n */\ngoog.net.FetchXmlHttp.RequestState = {\n  UNSENT: 0,\n  OPENED: 1,\n  HEADER_RECEIVED: 2,\n  LOADING: 3,\n  DONE: 4,\n};\n\n\n/** @override */\ngoog.net.FetchXmlHttp.prototype.open = function(method, url, opt_async) {\n  goog.asserts.assert(!!opt_async, 'Only async requests are supported.');\n  if (this.readyState != goog.net.FetchXmlHttp.RequestState.UNSENT) {\n    this.abort();\n    throw new Error('Error reopening a connection');\n  }\n\n  this.method_ = method;\n  this.url_ = url;\n\n  this.readyState = goog.net.FetchXmlHttp.RequestState.OPENED;\n  this.dispatchCallback_();\n};\n\n\n/** @override */\ngoog.net.FetchXmlHttp.prototype.send = function(opt_data) {\n  if (this.readyState != goog.net.FetchXmlHttp.RequestState.OPENED) {\n    this.abort();\n    throw new Error('need to call open() first. ');\n  }\n\n  this.inProgress_ = true;\n  var requestInit = {\n    headers: this.requestHeaders_,\n    method: this.method_,\n    credentials: this.credentialsMode_,\n    cache: this.cacheMode_,\n  };\n  if (opt_data) {\n    requestInit['body'] = opt_data;\n  }\n  this.worker_\n      .fetch(new Request(this.url_, /** @type {!RequestInit} */ (requestInit)))\n      .then(\n          this.handleResponse_.bind(this), this.handleSendFailure_.bind(this));\n};\n\n\n/** @override */\ngoog.net.FetchXmlHttp.prototype.abort = function() {\n  this.response = this.responseText = '';\n  this.requestHeaders_ = new Headers();\n  this.status = 0;\n\n  if (!!this.currentReader_) {\n    this.currentReader_.cancel('Request was aborted.');\n  }\n\n  if (((this.readyState >= goog.net.FetchXmlHttp.RequestState.OPENED) &&\n       this.inProgress_) &&\n      (this.readyState != goog.net.FetchXmlHttp.RequestState.DONE)) {\n    this.inProgress_ = false;\n    this.requestDone_();\n  }\n\n  this.readyState = goog.net.FetchXmlHttp.RequestState.UNSENT;\n};\n\n\n/**\n * Handles the fetch response.\n * @param {!Response} response\n * @private\n */\ngoog.net.FetchXmlHttp.prototype.handleResponse_ = function(response) {\n  if (!this.inProgress_) {\n    // The request was aborted, ignore.\n    return;\n  }\n\n  this.fetchResponse_ = response;\n\n  if (!this.responseHeaders_) {\n    this.status = this.fetchResponse_.status;\n    this.statusText = this.fetchResponse_.statusText;\n    this.responseHeaders_ = response.headers;\n    this.readyState = goog.net.FetchXmlHttp.RequestState.HEADER_RECEIVED;\n    this.dispatchCallback_();\n  }\n  // A callback may abort the request.\n  if (!this.inProgress_) {\n    // The request was aborted, ignore.\n    return;\n  }\n\n  this.readyState = goog.net.FetchXmlHttp.RequestState.LOADING;\n  this.dispatchCallback_();\n  // A callback may abort the request.\n  if (!this.inProgress_) {\n    // The request was aborted, ignore.\n    return;\n  }\n\n  if (this.responseType === 'arraybuffer') {\n    response.arrayBuffer().then(\n        this.handleResponseArrayBuffer_.bind(this),\n        this.handleSendFailure_.bind(this));\n  } else if (\n      typeof (goog.global.ReadableStream) !== 'undefined' &&\n      'body' in response) {\n    this.response = this.responseText = '';\n    this.currentReader_ =\n        /** @type {!ReadableStreamDefaultReader} */ (response.body.getReader());\n    this.textDecoder_ = new TextDecoder();\n    this.readInputFromFetch_();\n  } else {\n    response.text().then(\n        this.handleResponseText_.bind(this),\n        this.handleSendFailure_.bind(this));\n  }\n};\n\n\n/**\n * Reads the next chunk of data from the fetch response.\n * @private\n */\ngoog.net.FetchXmlHttp.prototype.readInputFromFetch_ = function() {\n  this.currentReader_.read()\n      .then(this.handleDataFromStream_.bind(this))\n      .catch(this.handleSendFailure_.bind(this));\n};\n\n\n/**\n * Handles a chunk of data from the fetch response stream reader.\n * @param {!IIterableResult} result\n * @private\n */\ngoog.net.FetchXmlHttp.prototype.handleDataFromStream_ = function(result) {\n  if (!this.inProgress_) {\n    // The request was aborted, ignore.\n    return;\n  }\n\n  var dataPacket = result.value ? /** @type {!Uint8Array} */ (result.value) :\n                                  new Uint8Array(0);\n  var newText = this.textDecoder_.decode(dataPacket, {stream: !result.done});\n  if (newText) {\n    this.responseText += newText;\n    this.response = this.responseText;\n  }\n\n  if (result.done) {\n    this.requestDone_();\n  } else {\n    this.dispatchCallback_();\n  }\n\n  if (this.readyState == goog.net.FetchXmlHttp.RequestState.LOADING) {\n    this.readInputFromFetch_();\n  }\n};\n\n\n/**\n * Handles the response text.\n * @param {string} responseText\n * @private\n */\ngoog.net.FetchXmlHttp.prototype.handleResponseText_ = function(responseText) {\n  if (!this.inProgress_) {\n    // The request was aborted, ignore.\n    return;\n  }\n  this.response = this.responseText = responseText;\n  this.requestDone_();\n};\n\n\n/**\n * Handles the response text.\n * @param {!ArrayBuffer} responseArrayBuffer\n * @private\n */\ngoog.net.FetchXmlHttp.prototype.handleResponseArrayBuffer_ = function(\n    responseArrayBuffer) {\n  if (!this.inProgress_) {\n    // The request was aborted, ignore.\n    return;\n  }\n  this.response = responseArrayBuffer;\n  this.requestDone_();\n};\n\n\n/**\n * Handles the send failure.\n * @param {*} error\n * @private\n */\ngoog.net.FetchXmlHttp.prototype.handleSendFailure_ = function(error) {\n  var e = error instanceof Error ? error : Error(error);\n  goog.log.warning(this.logger_, 'Failed to fetch url ' + this.url_, e);\n  if (!this.inProgress_) {\n    // The request was aborted, ignore.\n    return;\n  }\n  this.requestDone_();\n};\n\n\n/**\n * Sets the request state to DONE and performs cleanup.\n * @private\n */\ngoog.net.FetchXmlHttp.prototype.requestDone_ = function() {\n  this.readyState = goog.net.FetchXmlHttp.RequestState.DONE;\n\n  this.fetchResponse_ = null;\n  this.currentReader_ = null;\n  this.textDecoder_ = null;\n\n  this.dispatchCallback_();\n};\n\n\n/** @override */\ngoog.net.FetchXmlHttp.prototype.setRequestHeader = function(header, value) {\n  this.requestHeaders_.append(header, value);\n};\n\n\n/** @override */\ngoog.net.FetchXmlHttp.prototype.getResponseHeader = function(header) {\n  // TODO(user): This method should return null when the headers are not\n  // present or the specified header is missing. The externs need to be fixed.\n  if (!this.responseHeaders_) {\n    goog.log.warning(\n        this.logger_,\n        'Attempting to get response header but no headers have been received ' +\n            'for url: ' + this.url_);\n    return '';\n  }\n  return this.responseHeaders_.get(header.toLowerCase()) || '';\n};\n\n\n/** @override */\ngoog.net.FetchXmlHttp.prototype.getAllResponseHeaders = function() {\n  if (!this.responseHeaders_) {\n    goog.log.warning(\n        this.logger_,\n        'Attempting to get all response headers but no headers have been ' +\n            'received for url: ' + this.url_);\n    return '';\n  }\n  var lines = [];\n  var iter = this.responseHeaders_.entries();\n  var entry = iter.next();\n  while (!entry.done) {\n    var pair = entry.value;\n    lines.push(pair[0] + ': ' + pair[1]);\n    entry = iter.next();\n  }\n  return lines.join('\\r\\n');\n};\n\n\n/**\n * @param {!RequestCredentials} credentialsMode The credentials mode of the\n *     Service Worker fetch.\n */\ngoog.net.FetchXmlHttp.prototype.setCredentialsMode = function(credentialsMode) {\n  this.credentialsMode_ = credentialsMode;\n};\n\n/**\n * @return {!RequestCredentials|undefined} The credentials mode of the\n *     Service Worker fetch.\n */\ngoog.net.FetchXmlHttp.prototype.getCredentialsMode = function() {\n  return this.credentialsMode_;\n};\n\n/**\n * @param {!RequestCache} cacheMode The cache mode of the Service Worker fetch.\n */\ngoog.net.FetchXmlHttp.prototype.setCacheMode = function(cacheMode) {\n  this.cacheMode_ = cacheMode;\n};\n\n\n/**\n * Dispatches the callback, if the callback attribute is defined.\n * @private\n */\ngoog.net.FetchXmlHttp.prototype.dispatchCallback_ = function() {\n  if (this.onreadystatechange) {\n    this.onreadystatechange.call(this);\n  }\n};\n\n// Polyfill XmlHttpRequest's withCredentials property for specifying whether to\n// include credentials on cross domain requests.\nObject.defineProperty(goog.net.FetchXmlHttp.prototype, 'withCredentials', {\n  get:\n      /**\n       * @this {goog.net.FetchXmlHttp}\n       * @return {boolean} Whether to include credentials in cross domain\n       *     requests.\n       */\n      function() {\n        return this.getCredentialsMode() === 'include';\n      },\n\n  set:\n      /**\n       * @param {boolean} value Whether to include credentials in cross domain\n       *     requests.\n       * @this {goog.net.FetchXmlHttp}\n       **/\n      function(value) {\n        this.setCredentialsMode(value ? 'include' : 'same-origin');\n      }\n});\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Wrapper class for handling XmlHttpRequests.\n *\n * One off requests can be sent through goog.net.XhrIo.send() or an\n * instance can be created to send multiple requests.  Each request uses its\n * own XmlHttpRequest object and handles clearing of the event callback to\n * ensure no leaks.\n *\n * XhrIo is event based, it dispatches events on success, failure, finishing,\n * ready-state change, or progress (download and upload).\n *\n * The ready-state or timeout event fires first, followed by\n * a generic completed event. Then the abort, error, or success event\n * is fired as appropriate. Progress events are fired as they are\n * received. Lastly, the ready event will fire to indicate that the\n * object may be used to make another request.\n *\n * The error event may also be called before completed and\n * ready-state-change if the XmlHttpRequest.open() or .send() methods throw.\n *\n * This class does not support multiple requests, queuing, or prioritization.\n *\n * When progress events are supported by the browser, and progress is\n * enabled via .setProgressEventsEnabled(true), the\n * goog.net.EventType.PROGRESS event will be the re-dispatched browser\n * progress event. Additionally, a DOWNLOAD_PROGRESS or UPLOAD_PROGRESS event\n * will be fired for download and upload progress respectively.\n */\n\n\ngoog.provide('goog.net.XhrIo');\ngoog.provide('goog.net.XhrIo.ResponseType');\n\ngoog.require('goog.Timer');\ngoog.require('goog.array');\ngoog.require('goog.asserts');\ngoog.require('goog.debug.entryPointRegistry');\ngoog.require('goog.events.EventTarget');\ngoog.require('goog.json.hybrid');\ngoog.require('goog.log');\ngoog.require('goog.net.ErrorCode');\ngoog.require('goog.net.EventType');\ngoog.require('goog.net.HttpStatus');\ngoog.require('goog.net.XmlHttp');\ngoog.require('goog.object');\ngoog.require('goog.string');\ngoog.require('goog.structs');\ngoog.require('goog.structs.Map');\ngoog.require('goog.uri.utils');\ngoog.require('goog.userAgent');\ngoog.requireType('goog.Uri');\ngoog.requireType('goog.debug.ErrorHandler');\ngoog.requireType('goog.net.XhrLike');\ngoog.requireType('goog.net.XmlHttpFactory');\n\ngoog.scope(function() {\n\n/**\n * Basic class for handling XMLHttpRequests.\n * @param {goog.net.XmlHttpFactory=} opt_xmlHttpFactory Factory to use when\n *     creating XMLHttpRequest objects.\n * @constructor\n * @extends {goog.events.EventTarget}\n */\ngoog.net.XhrIo = function(opt_xmlHttpFactory) {\n  XhrIo.base(this, 'constructor');\n\n  /**\n   * Map of default headers to add to every request, use:\n   * XhrIo.headers.set(name, value)\n   * @type {!goog.structs.Map}\n   */\n  this.headers = new goog.structs.Map();\n\n  /**\n   * Optional XmlHttpFactory\n   * @private {goog.net.XmlHttpFactory}\n   */\n  this.xmlHttpFactory_ = opt_xmlHttpFactory || null;\n\n  /**\n   * Whether XMLHttpRequest is active.  A request is active from the time send()\n   * is called until onReadyStateChange() is complete, or error() or abort()\n   * is called.\n   * @private {boolean}\n   */\n  this.active_ = false;\n\n  /**\n   * The XMLHttpRequest object that is being used for the transfer.\n   * @private {?goog.net.XhrLike.OrNative}\n   */\n  this.xhr_ = null;\n\n  /**\n   * The options to use with the current XMLHttpRequest object.\n   * @private {?Object}\n   */\n  this.xhrOptions_ = null;\n\n  /**\n   * Last URL that was requested.\n   * @private {string|goog.Uri}\n   */\n  this.lastUri_ = '';\n\n  /**\n   * Method for the last request.\n   * @private {string}\n   */\n  this.lastMethod_ = '';\n\n  /**\n   * Last error code.\n   * @private {!goog.net.ErrorCode}\n   */\n  this.lastErrorCode_ = goog.net.ErrorCode.NO_ERROR;\n\n  /**\n   * Last error message.\n   * @private {Error|string}\n   */\n  this.lastError_ = '';\n\n  /**\n   * Used to ensure that we don't dispatch an multiple ERROR events. This can\n   * happen in IE when it does a synchronous load and one error is handled in\n   * the ready state change and one is handled due to send() throwing an\n   * exception.\n   * @private {boolean}\n   */\n  this.errorDispatched_ = false;\n\n  /**\n   * Used to make sure we don't fire the complete event from inside a send call.\n   * @private {boolean}\n   */\n  this.inSend_ = false;\n\n  /**\n   * Used in determining if a call to {@link #onReadyStateChange_} is from\n   * within a call to this.xhr_.open.\n   * @private {boolean}\n   */\n  this.inOpen_ = false;\n\n  /**\n   * Used in determining if a call to {@link #onReadyStateChange_} is from\n   * within a call to this.xhr_.abort.\n   * @private {boolean}\n   */\n  this.inAbort_ = false;\n\n  /**\n   * Number of milliseconds after which an incomplete request will be aborted\n   * and a {@link goog.net.EventType.TIMEOUT} event raised; 0 means no timeout\n   * is set.\n   * @private {number}\n   */\n  this.timeoutInterval_ = 0;\n\n  /**\n   * Timer to track request timeout.\n   * @private {?number}\n   */\n  this.timeoutId_ = null;\n\n  /**\n   * The requested type for the response. The empty string means use the default\n   * XHR behavior.\n   * @private {goog.net.XhrIo.ResponseType}\n   */\n  this.responseType_ = ResponseType.DEFAULT;\n\n  /**\n   * Whether a \"credentialed\" request is to be sent (one that is aware of\n   * cookies and authentication). This is applicable only for cross-domain\n   * requests and more recent browsers that support this part of the HTTP Access\n   * Control standard.\n   *\n   * @see http://www.w3.org/TR/XMLHttpRequest/#the-withcredentials-attribute\n   *\n   * @private {boolean}\n   */\n  this.withCredentials_ = false;\n\n  /**\n   * Whether progress events are enabled for this request. This is\n   * disabled by default because setting a progress event handler\n   * causes pre-flight OPTIONS requests to be sent for CORS requests,\n   * even in cases where a pre-flight request would not otherwise be\n   * sent.\n   *\n   * @see http://xhr.spec.whatwg.org/#security-considerations\n   *\n   * Note that this can cause problems for Firefox 22 and below, as an\n   * older \"LSProgressEvent\" will be dispatched by the browser. That\n   * progress event is no longer supported, and can lead to failures,\n   * including throwing exceptions.\n   *\n   * @see http://bugzilla.mozilla.org/show_bug.cgi?id=845631\n   * @see b/23469793\n   *\n   * @private {boolean}\n   */\n  this.progressEventsEnabled_ = false;\n\n  /**\n   * True if we can use XMLHttpRequest's timeout directly.\n   * @private {boolean}\n   */\n  this.useXhr2Timeout_ = false;\n};\ngoog.inherits(goog.net.XhrIo, goog.events.EventTarget);\n\nvar XhrIo = goog.net.XhrIo;\n\n/**\n * Response types that may be requested for XMLHttpRequests.\n * @enum {string}\n * @see http://www.w3.org/TR/XMLHttpRequest/#the-responsetype-attribute\n */\ngoog.net.XhrIo.ResponseType = {\n  DEFAULT: '',\n  TEXT: 'text',\n  DOCUMENT: 'document',\n  // Not supported as of Chrome 10.0.612.1 dev\n  BLOB: 'blob',\n  ARRAY_BUFFER: 'arraybuffer',\n};\n\nvar ResponseType = goog.net.XhrIo.ResponseType;\n\n\n/**\n * A reference to the XhrIo logger\n * @private {?goog.log.Logger}\n * @const\n */\ngoog.net.XhrIo.prototype.logger_ = goog.log.getLogger('goog.net.XhrIo');\n\n\n/**\n * The Content-Type HTTP header name\n * @type {string}\n */\ngoog.net.XhrIo.CONTENT_TYPE_HEADER = 'Content-Type';\n\n\n/**\n * The Content-Transfer-Encoding HTTP header name\n * @type {string}\n */\ngoog.net.XhrIo.CONTENT_TRANSFER_ENCODING = 'Content-Transfer-Encoding';\n\n\n/**\n * The pattern matching the 'http' and 'https' URI schemes\n * @type {!RegExp}\n */\ngoog.net.XhrIo.HTTP_SCHEME_PATTERN = /^https?$/i;\n\n\n/**\n * The methods that typically come along with form data.  We set different\n * headers depending on whether the HTTP action is one of these.\n * @type {!Array<string>}\n */\ngoog.net.XhrIo.METHODS_WITH_FORM_DATA = ['POST', 'PUT'];\n\n\n/**\n * The Content-Type HTTP header value for a url-encoded form\n * @type {string}\n */\ngoog.net.XhrIo.FORM_CONTENT_TYPE =\n    'application/x-www-form-urlencoded;charset=utf-8';\n\n\n/**\n * The XMLHttpRequest Level two timeout delay ms property name.\n *\n * @see http://www.w3.org/TR/XMLHttpRequest/#the-timeout-attribute\n *\n * @private {string}\n * @const\n */\ngoog.net.XhrIo.XHR2_TIMEOUT_ = 'timeout';\n\n\n/**\n * The XMLHttpRequest Level two ontimeout handler property name.\n *\n * @see http://www.w3.org/TR/XMLHttpRequest/#the-timeout-attribute\n *\n * @private {string}\n * @const\n */\ngoog.net.XhrIo.XHR2_ON_TIMEOUT_ = 'ontimeout';\n\n\n/**\n * All non-disposed instances of goog.net.XhrIo created\n * by {@link goog.net.XhrIo.send} are in this Array.\n * @see goog.net.XhrIo.cleanup\n * @private {!Array<!goog.net.XhrIo>}\n */\ngoog.net.XhrIo.sendInstances_ = [];\n\n\n/**\n * Static send that creates a short lived instance of XhrIo to send the\n * request.\n * @see goog.net.XhrIo.cleanup\n * @param {string|goog.Uri} url Uri to make request to.\n * @param {?function(this:goog.net.XhrIo, ?)=} opt_callback Callback function\n *     for when request is complete.\n * @param {string=} opt_method Send method, default: GET.\n * @param {ArrayBuffer|ArrayBufferView|Blob|Document|FormData|string=}\n *     opt_content Body data.\n * @param {Object|goog.structs.Map=} opt_headers Map of headers to add to the\n *     request.\n * @param {number=} opt_timeoutInterval Number of milliseconds after which an\n *     incomplete request will be aborted; 0 means no timeout is set.\n * @param {boolean=} opt_withCredentials Whether to send credentials with the\n *     request. Default to false. See {@link goog.net.XhrIo#setWithCredentials}.\n * @return {!goog.net.XhrIo} The sent XhrIo.\n */\ngoog.net.XhrIo.send = function(\n    url, opt_callback, opt_method, opt_content, opt_headers,\n    opt_timeoutInterval, opt_withCredentials) {\n  var x = new goog.net.XhrIo();\n  goog.net.XhrIo.sendInstances_.push(x);\n  if (opt_callback) {\n    x.listen(goog.net.EventType.COMPLETE, opt_callback);\n  }\n  x.listenOnce(goog.net.EventType.READY, x.cleanupSend_);\n  if (opt_timeoutInterval) {\n    x.setTimeoutInterval(opt_timeoutInterval);\n  }\n  if (opt_withCredentials) {\n    x.setWithCredentials(opt_withCredentials);\n  }\n  x.send(url, opt_method, opt_content, opt_headers);\n  return x;\n};\n\n\n/**\n * Disposes all non-disposed instances of goog.net.XhrIo created by\n * {@link goog.net.XhrIo.send}.\n * {@link goog.net.XhrIo.send} cleans up the goog.net.XhrIo instance\n * it creates when the request completes or fails.  However, if\n * the request never completes, then the goog.net.XhrIo is not disposed.\n * This can occur if the window is unloaded before the request completes.\n * We could have {@link goog.net.XhrIo.send} return the goog.net.XhrIo\n * it creates and make the client of {@link goog.net.XhrIo.send} be\n * responsible for disposing it in this case.  However, this makes things\n * significantly more complicated for the client, and the whole point\n * of {@link goog.net.XhrIo.send} is that it's simple and easy to use.\n * Clients of {@link goog.net.XhrIo.send} should call\n * {@link goog.net.XhrIo.cleanup} when doing final\n * cleanup on window unload.\n */\ngoog.net.XhrIo.cleanup = function() {\n  var instances = goog.net.XhrIo.sendInstances_;\n  while (instances.length) {\n    instances.pop().dispose();\n  }\n};\n\n\n/**\n * Installs exception protection for all entry point introduced by\n * goog.net.XhrIo instances which are not protected by\n * {@link goog.debug.ErrorHandler#protectWindowSetTimeout},\n * {@link goog.debug.ErrorHandler#protectWindowSetInterval}, or\n * {@link goog.events.protectBrowserEventEntryPoint}.\n *\n * @param {goog.debug.ErrorHandler} errorHandler Error handler with which to\n *     protect the entry point(s).\n */\ngoog.net.XhrIo.protectEntryPoints = function(errorHandler) {\n  goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_ =\n      errorHandler.protectEntryPoint(\n          goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_);\n};\n\n\n/**\n * Disposes of the specified goog.net.XhrIo created by\n * {@link goog.net.XhrIo.send} and removes it from\n * {@link goog.net.XhrIo.pendingStaticSendInstances_}.\n * @private\n */\ngoog.net.XhrIo.prototype.cleanupSend_ = function() {\n  this.dispose();\n  goog.array.remove(goog.net.XhrIo.sendInstances_, this);\n};\n\n\n/**\n * Returns the number of milliseconds after which an incomplete request will be\n * aborted, or 0 if no timeout is set.\n * @return {number} Timeout interval in milliseconds.\n */\ngoog.net.XhrIo.prototype.getTimeoutInterval = function() {\n  return this.timeoutInterval_;\n};\n\n\n/**\n * Sets the number of milliseconds after which an incomplete request will be\n * aborted and a {@link goog.net.EventType.TIMEOUT} event raised; 0 means no\n * timeout is set.\n * @param {number} ms Timeout interval in milliseconds; 0 means none.\n */\ngoog.net.XhrIo.prototype.setTimeoutInterval = function(ms) {\n  this.timeoutInterval_ = Math.max(0, ms);\n};\n\n\n/**\n * Sets the desired type for the response. At time of writing, this is only\n * supported in very recent versions of WebKit (10.0.612.1 dev and later).\n *\n * If this is used, the response may only be accessed via {@link #getResponse}.\n *\n * @param {goog.net.XhrIo.ResponseType} type The desired type for the response.\n */\ngoog.net.XhrIo.prototype.setResponseType = function(type) {\n  this.responseType_ = type;\n};\n\n\n/**\n * Gets the desired type for the response.\n * @return {goog.net.XhrIo.ResponseType} The desired type for the response.\n */\ngoog.net.XhrIo.prototype.getResponseType = function() {\n  return this.responseType_;\n};\n\n\n/**\n * Sets whether a \"credentialed\" request that is aware of cookie and\n * authentication information should be made. This option is only supported by\n * browsers that support HTTP Access Control. As of this writing, this option\n * is not supported in IE.\n *\n * @param {boolean} withCredentials Whether this should be a \"credentialed\"\n *     request.\n */\ngoog.net.XhrIo.prototype.setWithCredentials = function(withCredentials) {\n  this.withCredentials_ = withCredentials;\n};\n\n\n/**\n * Gets whether a \"credentialed\" request is to be sent.\n * @return {boolean} The desired type for the response.\n */\ngoog.net.XhrIo.prototype.getWithCredentials = function() {\n  return this.withCredentials_;\n};\n\n\n/**\n * Sets whether progress events are enabled for this request. Note\n * that progress events require pre-flight OPTIONS request handling\n * for CORS requests, and may cause trouble with older browsers. See\n * progressEventsEnabled_ for details.\n * @param {boolean} enabled Whether progress events should be enabled.\n */\ngoog.net.XhrIo.prototype.setProgressEventsEnabled = function(enabled) {\n  this.progressEventsEnabled_ = enabled;\n};\n\n\n/**\n * Gets whether progress events are enabled.\n * @return {boolean} Whether progress events are enabled for this request.\n */\ngoog.net.XhrIo.prototype.getProgressEventsEnabled = function() {\n  return this.progressEventsEnabled_;\n};\n\n\n/**\n * Instance send that actually uses XMLHttpRequest to make a server call.\n * @param {string|goog.Uri} url Uri to make request to.\n * @param {string=} opt_method Send method, default: GET.\n * @param {ArrayBuffer|ArrayBufferView|Blob|Document|FormData|string=}\n *     opt_content Body data.\n * @param {Object|goog.structs.Map=} opt_headers Map of headers to add to the\n *     request.\n * @suppress {deprecated} Use deprecated goog.structs.forEach to allow different\n * types of parameters for opt_headers.\n */\ngoog.net.XhrIo.prototype.send = function(\n    url, opt_method, opt_content, opt_headers) {\n  if (this.xhr_) {\n    throw new Error(\n        '[goog.net.XhrIo] Object is active with another request=' +\n        this.lastUri_ + '; newUri=' + url);\n  }\n\n  var method = opt_method ? opt_method.toUpperCase() : 'GET';\n\n  this.lastUri_ = url;\n  this.lastError_ = '';\n  this.lastErrorCode_ = goog.net.ErrorCode.NO_ERROR;\n  this.lastMethod_ = method;\n  this.errorDispatched_ = false;\n  this.active_ = true;\n\n  // Use the factory to create the XHR object and options\n  this.xhr_ = this.createXhr();\n  this.xhrOptions_ = this.xmlHttpFactory_ ? this.xmlHttpFactory_.getOptions() :\n                                            goog.net.XmlHttp.getOptions();\n\n  // Set up the onreadystatechange callback\n  this.xhr_.onreadystatechange = goog.bind(this.onReadyStateChange_, this);\n\n  // Set up upload/download progress events, if progress events are supported.\n  if (this.getProgressEventsEnabled() && 'onprogress' in this.xhr_) {\n    this.xhr_.onprogress =\n        goog.bind(function(e) { this.onProgressHandler_(e, true); }, this);\n    if (this.xhr_.upload) {\n      this.xhr_.upload.onprogress = goog.bind(this.onProgressHandler_, this);\n    }\n  }\n\n  /**\n   * Try to open the XMLHttpRequest (always async), if an error occurs here it\n   * is generally permission denied\n   */\n  try {\n    goog.log.fine(this.logger_, this.formatMsg_('Opening Xhr'));\n    this.inOpen_ = true;\n    this.xhr_.open(method, String(url), true);  // Always async!\n    this.inOpen_ = false;\n  } catch (err) {\n    goog.log.fine(\n        this.logger_, this.formatMsg_('Error opening Xhr: ' + err.message));\n    this.error_(goog.net.ErrorCode.EXCEPTION, err);\n    return;\n  }\n\n  // We can't use null since this won't allow requests with form data to have a\n  // content length specified which will cause some proxies to return a 411\n  // error.\n  var content = opt_content || '';\n\n  var headers = this.headers.clone();\n\n  // Add headers specific to this request\n  if (opt_headers) {\n    goog.structs.forEach(\n        opt_headers, function(value, key) { headers.set(key, value); });\n  }\n\n  // Find whether a content type header is set, ignoring case.\n  // HTTP header names are case-insensitive.  See:\n  // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2\n  var contentTypeKey =\n      goog.array.find(headers.getKeys(), goog.net.XhrIo.isContentTypeHeader_);\n\n  var contentIsFormData =\n      (goog.global['FormData'] && (content instanceof goog.global['FormData']));\n  if (goog.array.contains(goog.net.XhrIo.METHODS_WITH_FORM_DATA, method) &&\n      !contentTypeKey && !contentIsFormData) {\n    // For requests typically with form data, default to the url-encoded form\n    // content type unless this is a FormData request.  For FormData,\n    // the browser will automatically add a multipart/form-data content type\n    // with an appropriate multipart boundary.\n    headers.set(\n        goog.net.XhrIo.CONTENT_TYPE_HEADER, goog.net.XhrIo.FORM_CONTENT_TYPE);\n  }\n\n  // Add the headers to the Xhr object\n  headers.forEach(function(value, key) {\n    this.xhr_.setRequestHeader(key, value);\n  }, this);\n\n  if (this.responseType_) {\n    this.xhr_.responseType = this.responseType_;\n  }\n  // Set xhr_.withCredentials only when the value is different, or else in\n  // synchronous XMLHtppRequest.open Firefox will throw an exception.\n  // https://bugzilla.mozilla.org/show_bug.cgi?id=736340\n  if ('withCredentials' in this.xhr_ &&\n      this.xhr_.withCredentials !== this.withCredentials_) {\n    this.xhr_.withCredentials = this.withCredentials_;\n  }\n\n  /**\n   * Try to send the request, or other wise report an error (404 not found).\n   */\n  try {\n    this.cleanUpTimeoutTimer_();  // Paranoid, should never be running.\n    if (this.timeoutInterval_ > 0) {\n      this.useXhr2Timeout_ = goog.net.XhrIo.shouldUseXhr2Timeout_(this.xhr_);\n      goog.log.fine(\n          this.logger_, this.formatMsg_(\n                            'Will abort after ' + this.timeoutInterval_ +\n                            'ms if incomplete, xhr2 ' + this.useXhr2Timeout_));\n      if (this.useXhr2Timeout_) {\n        this.xhr_[goog.net.XhrIo.XHR2_TIMEOUT_] = this.timeoutInterval_;\n        this.xhr_[goog.net.XhrIo.XHR2_ON_TIMEOUT_] =\n            goog.bind(this.timeout_, this);\n      } else {\n        this.timeoutId_ =\n            goog.Timer.callOnce(this.timeout_, this.timeoutInterval_, this);\n      }\n    }\n    goog.log.fine(this.logger_, this.formatMsg_('Sending request'));\n    this.inSend_ = true;\n    this.xhr_.send(content);\n    this.inSend_ = false;\n\n  } catch (err) {\n    goog.log.fine(this.logger_, this.formatMsg_('Send error: ' + err.message));\n    this.error_(goog.net.ErrorCode.EXCEPTION, err);\n  }\n};\n\n\n/**\n * Determines if the argument is an XMLHttpRequest that supports the level 2\n * timeout value and event.\n *\n * Currently, FF 21.0 OS X has the fields but won't actually call the timeout\n * handler.  Perhaps the confusion in the bug referenced below hasn't\n * entirely been resolved.\n *\n * @see http://www.w3.org/TR/XMLHttpRequest/#the-timeout-attribute\n * @see https://bugzilla.mozilla.org/show_bug.cgi?id=525816\n *\n * @param {!goog.net.XhrLike.OrNative} xhr The request.\n * @return {boolean} True if the request supports level 2 timeout.\n * @private\n */\ngoog.net.XhrIo.shouldUseXhr2Timeout_ = function(xhr) {\n  return goog.userAgent.IE && goog.userAgent.isVersionOrHigher(9) &&\n      typeof xhr[goog.net.XhrIo.XHR2_TIMEOUT_] === 'number' &&\n      xhr[goog.net.XhrIo.XHR2_ON_TIMEOUT_] !== undefined;\n};\n\n\n/**\n * @param {string} header An HTTP header key.\n * @return {boolean} Whether the key is a content type header (ignoring\n *     case.\n * @private\n */\ngoog.net.XhrIo.isContentTypeHeader_ = function(header) {\n  return goog.string.caseInsensitiveEquals(\n      goog.net.XhrIo.CONTENT_TYPE_HEADER, header);\n};\n\n\n/**\n * Creates a new XHR object.\n * @return {!goog.net.XhrLike.OrNative} The newly created XHR object.\n * @protected\n */\ngoog.net.XhrIo.prototype.createXhr = function() {\n  return this.xmlHttpFactory_ ? this.xmlHttpFactory_.createInstance() :\n                                goog.net.XmlHttp();\n};\n\n\n/**\n * The request didn't complete after {@link goog.net.XhrIo#timeoutInterval_}\n * milliseconds; raises a {@link goog.net.EventType.TIMEOUT} event and aborts\n * the request.\n * @private\n */\ngoog.net.XhrIo.prototype.timeout_ = function() {\n  if (typeof goog == 'undefined') {\n    // If goog is undefined then the callback has occurred as the application\n    // is unloading and will error.  Thus we let it silently fail.\n  } else if (this.xhr_) {\n    this.lastError_ =\n        'Timed out after ' + this.timeoutInterval_ + 'ms, aborting';\n    this.lastErrorCode_ = goog.net.ErrorCode.TIMEOUT;\n    goog.log.fine(this.logger_, this.formatMsg_(this.lastError_));\n    this.dispatchEvent(goog.net.EventType.TIMEOUT);\n    this.abort(goog.net.ErrorCode.TIMEOUT);\n  }\n};\n\n\n/**\n * Something errorred, so inactivate, fire error callback and clean up\n * @param {goog.net.ErrorCode} errorCode The error code.\n * @param {Error} err The error object.\n * @private\n */\ngoog.net.XhrIo.prototype.error_ = function(errorCode, err) {\n  this.active_ = false;\n  if (this.xhr_) {\n    this.inAbort_ = true;\n    this.xhr_.abort();  // Ensures XHR isn't hung (FF)\n    this.inAbort_ = false;\n  }\n  this.lastError_ = err;\n  this.lastErrorCode_ = errorCode;\n  this.dispatchErrors_();\n  this.cleanUpXhr_();\n};\n\n\n/**\n * Dispatches COMPLETE and ERROR in case of an error. This ensures that we do\n * not dispatch multiple error events.\n * @private\n */\ngoog.net.XhrIo.prototype.dispatchErrors_ = function() {\n  if (!this.errorDispatched_) {\n    this.errorDispatched_ = true;\n    this.dispatchEvent(goog.net.EventType.COMPLETE);\n    this.dispatchEvent(goog.net.EventType.ERROR);\n  }\n};\n\n\n/**\n * Abort the current XMLHttpRequest\n * @param {goog.net.ErrorCode=} opt_failureCode Optional error code to use -\n *     defaults to ABORT.\n */\ngoog.net.XhrIo.prototype.abort = function(opt_failureCode) {\n  if (this.xhr_ && this.active_) {\n    goog.log.fine(this.logger_, this.formatMsg_('Aborting'));\n    this.active_ = false;\n    this.inAbort_ = true;\n    this.xhr_.abort();\n    this.inAbort_ = false;\n    this.lastErrorCode_ = opt_failureCode || goog.net.ErrorCode.ABORT;\n    this.dispatchEvent(goog.net.EventType.COMPLETE);\n    this.dispatchEvent(goog.net.EventType.ABORT);\n    this.cleanUpXhr_();\n  }\n};\n\n\n/**\n * Nullifies all callbacks to reduce risks of leaks.\n * @override\n * @protected\n */\ngoog.net.XhrIo.prototype.disposeInternal = function() {\n  if (this.xhr_) {\n    // We explicitly do not call xhr_.abort() unless active_ is still true.\n    // This is to avoid unnecessarily aborting a successful request when\n    // dispose() is called in a callback triggered by a complete response, but\n    // in which browser cleanup has not yet finished.\n    // (See http://b/issue?id=1684217.)\n    if (this.active_) {\n      this.active_ = false;\n      this.inAbort_ = true;\n      this.xhr_.abort();\n      this.inAbort_ = false;\n    }\n    this.cleanUpXhr_(true);\n  }\n\n  XhrIo.base(this, 'disposeInternal');\n};\n\n\n/**\n * Internal handler for the XHR object's readystatechange event.  This method\n * checks the status and the readystate and fires the correct callbacks.\n * If the request has ended, the handlers are cleaned up and the XHR object is\n * nullified.\n * @private\n */\ngoog.net.XhrIo.prototype.onReadyStateChange_ = function() {\n  if (this.isDisposed()) {\n    // This method is the target of an untracked goog.Timer.callOnce().\n    return;\n  }\n  if (!this.inOpen_ && !this.inSend_ && !this.inAbort_) {\n    // Were not being called from within a call to this.xhr_.send\n    // this.xhr_.abort, or this.xhr_.open, so this is an entry point\n    this.onReadyStateChangeEntryPoint_();\n  } else {\n    this.onReadyStateChangeHelper_();\n  }\n};\n\n\n/**\n * Used to protect the onreadystatechange handler entry point.  Necessary\n * as {#onReadyStateChange_} maybe called from within send or abort, this\n * method is only called when {#onReadyStateChange_} is called as an\n * entry point.\n * {@see #protectEntryPoints}\n * @private\n */\ngoog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_ = function() {\n  this.onReadyStateChangeHelper_();\n};\n\n\n/**\n * Helper for {@link #onReadyStateChange_}.  This is used so that\n * entry point calls to {@link #onReadyStateChange_} can be routed through\n * {@link #onReadyStateChangeEntryPoint_}.\n * @private\n */\ngoog.net.XhrIo.prototype.onReadyStateChangeHelper_ = function() {\n  if (!this.active_) {\n    // can get called inside abort call\n    return;\n  }\n\n  if (typeof goog == 'undefined') {\n    // NOTE(user): If goog is undefined then the callback has occurred as the\n    // application is unloading and will error.  Thus we let it silently fail.\n\n  } else if (\n      this.xhrOptions_[goog.net.XmlHttp.OptionType.LOCAL_REQUEST_ERROR] &&\n      this.getReadyState() == goog.net.XmlHttp.ReadyState.COMPLETE &&\n      this.getStatus() == 2) {\n    // NOTE(user): In IE if send() errors on a *local* request the readystate\n    // is still changed to COMPLETE.  We need to ignore it and allow the\n    // try/catch around send() to pick up the error.\n    goog.log.fine(\n        this.logger_,\n        this.formatMsg_('Local request error detected and ignored'));\n\n  } else {\n    // In IE when the response has been cached we sometimes get the callback\n    // from inside the send call and this usually breaks code that assumes that\n    // XhrIo is asynchronous.  If that is the case we delay the callback\n    // using a timer.\n    if (this.inSend_ &&\n        this.getReadyState() == goog.net.XmlHttp.ReadyState.COMPLETE) {\n      goog.Timer.callOnce(this.onReadyStateChange_, 0, this);\n      return;\n    }\n\n    this.dispatchEvent(goog.net.EventType.READY_STATE_CHANGE);\n\n    // readyState indicates the transfer has finished\n    if (this.isComplete()) {\n      goog.log.fine(this.logger_, this.formatMsg_('Request complete'));\n\n      this.active_ = false;\n\n      try {\n        // Call the specific callbacks for success or failure. Only call the\n        // success if the status is 200 (HTTP_OK) or 304 (HTTP_CACHED)\n        if (this.isSuccess()) {\n          this.dispatchEvent(goog.net.EventType.COMPLETE);\n          this.dispatchEvent(goog.net.EventType.SUCCESS);\n        } else {\n          this.lastErrorCode_ = goog.net.ErrorCode.HTTP_ERROR;\n          this.lastError_ =\n              this.getStatusText() + ' [' + this.getStatus() + ']';\n          this.dispatchErrors_();\n        }\n      } finally {\n        this.cleanUpXhr_();\n      }\n    }\n  }\n};\n\n\n/**\n * Internal handler for the XHR object's onprogress event. Fires both a generic\n * PROGRESS event and either a DOWNLOAD_PROGRESS or UPLOAD_PROGRESS event to\n * allow specific binding for each XHR progress event.\n * @param {!ProgressEvent} e XHR progress event.\n * @param {boolean=} opt_isDownload Whether the current progress event is from a\n *     download. Used to determine whether DOWNLOAD_PROGRESS or UPLOAD_PROGRESS\n *     event should be dispatched.\n * @private\n */\ngoog.net.XhrIo.prototype.onProgressHandler_ = function(e, opt_isDownload) {\n  goog.asserts.assert(\n      e.type === goog.net.EventType.PROGRESS,\n      'goog.net.EventType.PROGRESS is of the same type as raw XHR progress.');\n  this.dispatchEvent(\n      goog.net.XhrIo.buildProgressEvent_(e, goog.net.EventType.PROGRESS));\n  this.dispatchEvent(\n      goog.net.XhrIo.buildProgressEvent_(\n          e, opt_isDownload ? goog.net.EventType.DOWNLOAD_PROGRESS :\n                              goog.net.EventType.UPLOAD_PROGRESS));\n};\n\n\n/**\n * Creates a representation of the native ProgressEvent. IE doesn't support\n * constructing ProgressEvent via \"new\", and the alternatives (e.g.,\n * ProgressEvent.initProgressEvent) are non-standard or deprecated.\n * @param {!ProgressEvent} e XHR progress event.\n * @param {!goog.net.EventType} eventType The type of the event.\n * @return {!ProgressEvent} The progress event.\n * @private\n */\ngoog.net.XhrIo.buildProgressEvent_ = function(e, eventType) {\n  return /** @type {!ProgressEvent} */ ({\n    type: eventType,\n    lengthComputable: e.lengthComputable,\n    loaded: e.loaded,\n    total: e.total,\n  });\n};\n\n\n/**\n * Remove the listener to protect against leaks, and nullify the XMLHttpRequest\n * object.\n * @param {boolean=} opt_fromDispose If this is from the dispose (don't want to\n *     fire any events).\n * @private\n */\ngoog.net.XhrIo.prototype.cleanUpXhr_ = function(opt_fromDispose) {\n  if (this.xhr_) {\n    // Cancel any pending timeout event handler.\n    this.cleanUpTimeoutTimer_();\n\n    // Save reference so we can mark it as closed after the READY event.  The\n    // READY event may trigger another request, thus we must nullify this.xhr_\n    var xhr = this.xhr_;\n    var clearedOnReadyStateChange =\n        this.xhrOptions_[goog.net.XmlHttp.OptionType.USE_NULL_FUNCTION] ?\n        goog.nullFunction :\n        null;\n    this.xhr_ = null;\n    this.xhrOptions_ = null;\n\n    if (!opt_fromDispose) {\n      this.dispatchEvent(goog.net.EventType.READY);\n    }\n\n    try {\n      // NOTE(user): Not nullifying in FireFox can still leak if the callbacks\n      // are defined in the same scope as the instance of XhrIo. But, IE doesn't\n      // allow you to set the onreadystatechange to NULL so nullFunction is\n      // used.\n      xhr.onreadystatechange = clearedOnReadyStateChange;\n    } catch (e) {\n      // This seems to occur with a Gears HTTP request. Delayed the setting of\n      // this onreadystatechange until after READY is sent out and catching the\n      // error to see if we can track down the problem.\n      goog.log.error(\n          this.logger_,\n          'Problem encountered resetting onreadystatechange: ' + e.message);\n    }\n  }\n};\n\n\n/**\n * Make sure the timeout timer isn't running.\n * @private\n */\ngoog.net.XhrIo.prototype.cleanUpTimeoutTimer_ = function() {\n  if (this.xhr_ && this.useXhr2Timeout_) {\n    this.xhr_[goog.net.XhrIo.XHR2_ON_TIMEOUT_] = null;\n  }\n  if (this.timeoutId_) {\n    goog.Timer.clear(this.timeoutId_);\n    this.timeoutId_ = null;\n  }\n};\n\n\n/**\n * @return {boolean} Whether there is an active request.\n */\ngoog.net.XhrIo.prototype.isActive = function() {\n  return !!this.xhr_;\n};\n\n\n/**\n * @return {boolean} Whether the request has completed.\n */\ngoog.net.XhrIo.prototype.isComplete = function() {\n  return this.getReadyState() == goog.net.XmlHttp.ReadyState.COMPLETE;\n};\n\n\n/**\n * @return {boolean} Whether the request completed with a success.\n */\ngoog.net.XhrIo.prototype.isSuccess = function() {\n  var status = this.getStatus();\n  // A zero status code is considered successful for local files.\n  return goog.net.HttpStatus.isSuccess(status) ||\n      status === 0 && !this.isLastUriEffectiveSchemeHttp_();\n};\n\n\n/**\n * @return {boolean} whether the effective scheme of the last URI that was\n *     fetched was 'http' or 'https'.\n * @private\n */\ngoog.net.XhrIo.prototype.isLastUriEffectiveSchemeHttp_ = function() {\n  var scheme = goog.uri.utils.getEffectiveScheme(String(this.lastUri_));\n  return goog.net.XhrIo.HTTP_SCHEME_PATTERN.test(scheme);\n};\n\n\n/**\n * Get the readystate from the Xhr object\n * Will only return correct result when called from the context of a callback\n * @return {goog.net.XmlHttp.ReadyState} goog.net.XmlHttp.ReadyState.*.\n */\ngoog.net.XhrIo.prototype.getReadyState = function() {\n  return this.xhr_ ?\n      /** @type {goog.net.XmlHttp.ReadyState} */ (this.xhr_.readyState) :\n                                                 goog.net.XmlHttp.ReadyState\n                                                     .UNINITIALIZED;\n};\n\n\n/**\n * Get the status from the Xhr object\n * Will only return correct result when called from the context of a callback\n * @return {number} Http status.\n */\ngoog.net.XhrIo.prototype.getStatus = function() {\n  /**\n   * IE doesn't like you checking status until the readystate is greater than 2\n   * (i.e. it is receiving or complete).  The try/catch is used for when the\n   * page is unloading and an ERROR_NOT_AVAILABLE may occur when accessing xhr_.\n   */\n  try {\n    return this.getReadyState() > goog.net.XmlHttp.ReadyState.LOADED ?\n        this.xhr_.status :\n        -1;\n  } catch (e) {\n    return -1;\n  }\n};\n\n\n/**\n * Get the status text from the Xhr object\n * Will only return correct result when called from the context of a callback\n * @return {string} Status text.\n */\ngoog.net.XhrIo.prototype.getStatusText = function() {\n  /**\n   * IE doesn't like you checking status until the readystate is greater than 2\n   * (i.e. it is receiving or complete).  The try/catch is used for when the\n   * page is unloading and an ERROR_NOT_AVAILABLE may occur when accessing xhr_.\n   */\n  try {\n    return this.getReadyState() > goog.net.XmlHttp.ReadyState.LOADED ?\n        this.xhr_.statusText :\n        '';\n  } catch (e) {\n    goog.log.fine(this.logger_, 'Can not get status: ' + e.message);\n    return '';\n  }\n};\n\n\n/**\n * Get the last Uri that was requested\n * @return {string} Last Uri.\n */\ngoog.net.XhrIo.prototype.getLastUri = function() {\n  return String(this.lastUri_);\n};\n\n\n/**\n * Get the response text from the Xhr object\n * Will only return correct result when called from the context of a callback.\n * @return {string} Result from the server, or '' if no result available.\n */\ngoog.net.XhrIo.prototype.getResponseText = function() {\n  try {\n    return this.xhr_ ? this.xhr_.responseText : '';\n  } catch (e) {\n    // http://www.w3.org/TR/XMLHttpRequest/#the-responsetext-attribute\n    // states that responseText should return '' (and responseXML null)\n    // when the state is not LOADING or DONE. Instead, IE can\n    // throw unexpected exceptions, for example when a request is aborted\n    // or no data is available yet.\n    goog.log.fine(this.logger_, 'Can not get responseText: ' + e.message);\n    return '';\n  }\n};\n\n\n/**\n * Get the response body from the Xhr object. This property is only available\n * in IE since version 7 according to MSDN:\n * http://msdn.microsoft.com/en-us/library/ie/ms534368(v=vs.85).aspx\n * Will only return correct result when called from the context of a callback.\n *\n * One option is to construct a VBArray from the returned object and convert\n * it to a JavaScript array using the toArray method:\n * `(new window['VBArray'](xhrIo.getResponseBody())).toArray()`\n * This will result in an array of numbers in the range of [0..255]\n *\n * Another option is to use the VBScript CStr method to convert it into a\n * string as outlined in http://stackoverflow.com/questions/1919972\n *\n * @return {Object} Binary result from the server or null if not available.\n */\ngoog.net.XhrIo.prototype.getResponseBody = function() {\n  try {\n    if (this.xhr_ && 'responseBody' in this.xhr_) {\n      return this.xhr_['responseBody'];\n    }\n  } catch (e) {\n    // IE can throw unexpected exceptions, for example when a request is aborted\n    // or no data is yet available.\n    goog.log.fine(this.logger_, 'Can not get responseBody: ' + e.message);\n  }\n  return null;\n};\n\n\n/**\n * Get the response XML from the Xhr object\n * Will only return correct result when called from the context of a callback.\n * @return {Document} The DOM Document representing the XML file, or null\n * if no result available.\n */\ngoog.net.XhrIo.prototype.getResponseXml = function() {\n  try {\n    return this.xhr_ ? this.xhr_.responseXML : null;\n  } catch (e) {\n    goog.log.fine(this.logger_, 'Can not get responseXML: ' + e.message);\n    return null;\n  }\n};\n\n\n/**\n * Get the response and evaluates it as JSON from the Xhr object\n * Will only return correct result when called from the context of a callback\n * @param {string=} opt_xssiPrefix Optional XSSI prefix string to use for\n *     stripping of the response before parsing. This needs to be set only if\n *     your backend server prepends the same prefix string to the JSON response.\n * @throws Error if the response text is invalid JSON.\n * @return {Object|undefined} JavaScript object.\n */\ngoog.net.XhrIo.prototype.getResponseJson = function(opt_xssiPrefix) {\n  if (!this.xhr_) {\n    return undefined;\n  }\n\n  var responseText = this.xhr_.responseText;\n  if (opt_xssiPrefix && responseText.indexOf(opt_xssiPrefix) == 0) {\n    responseText = responseText.substring(opt_xssiPrefix.length);\n  }\n\n  return goog.json.hybrid.parse(responseText);\n};\n\n\n/**\n * Get the response as the type specificed by {@link #setResponseType}. At time\n * of writing, this is only directly supported in very recent versions of WebKit\n * (10.0.612.1 dev and later). If the field is not supported directly, we will\n * try to emulate it.\n *\n * Emulating the response means following the rules laid out at\n * http://www.w3.org/TR/XMLHttpRequest/#the-response-attribute\n *\n * On browsers with no support for this (Chrome < 10, Firefox < 4, etc), only\n * response types of DEFAULT or TEXT may be used, and the response returned will\n * be the text response.\n *\n * On browsers with Mozilla's draft support for array buffers (Firefox 4, 5),\n * only response types of DEFAULT, TEXT, and ARRAY_BUFFER may be used, and the\n * response returned will be either the text response or the Mozilla\n * implementation of the array buffer response.\n *\n * On browsers will full support, any valid response type supported by the\n * browser may be used, and the response provided by the browser will be\n * returned.\n *\n * @return {*} The response.\n */\ngoog.net.XhrIo.prototype.getResponse = function() {\n  try {\n    if (!this.xhr_) {\n      return null;\n    }\n    if ('response' in this.xhr_) {\n      return this.xhr_.response;\n    }\n    switch (this.responseType_) {\n      case ResponseType.DEFAULT:\n      case ResponseType.TEXT:\n        return this.xhr_.responseText;\n      // DOCUMENT and BLOB don't need to be handled here because they are\n      // introduced in the same spec that adds the .response field, and would\n      // have been caught above.\n      // ARRAY_BUFFER needs an implementation for Firefox 4, where it was\n      // implemented using a draft spec rather than the final spec.\n      case ResponseType.ARRAY_BUFFER:\n        if ('mozResponseArrayBuffer' in this.xhr_) {\n          return this.xhr_.mozResponseArrayBuffer;\n        }\n    }\n    // Fell through to a response type that is not supported on this browser.\n    goog.log.error(\n        this.logger_, 'Response type ' + this.responseType_ + ' is not ' +\n            'supported on this browser');\n    return null;\n  } catch (e) {\n    goog.log.fine(this.logger_, 'Can not get response: ' + e.message);\n    return null;\n  }\n};\n\n\n/**\n * Get the value of the response-header with the given name from the Xhr object\n * Will only return correct result when called from the context of a callback\n * and the request has completed\n * @param {string} key The name of the response-header to retrieve.\n * @return {string|undefined} The value of the response-header named key.\n */\ngoog.net.XhrIo.prototype.getResponseHeader = function(key) {\n  if (!this.xhr_ || !this.isComplete()) {\n    return undefined;\n  }\n\n  var value = this.xhr_.getResponseHeader(key);\n  return value === null ? undefined : value;\n};\n\n\n/**\n * Gets the text of all the headers in the response.\n * Will only return correct result when called from the context of a callback\n * and the request has completed.\n * @return {string} The value of the response headers or empty string.\n */\ngoog.net.XhrIo.prototype.getAllResponseHeaders = function() {\n  // getAllResponseHeaders can return null if no response has been received,\n  // ensure we always return an empty string.\n  return this.xhr_ && this.isComplete() ?\n      (this.xhr_.getAllResponseHeaders() || '') :\n      '';\n};\n\n\n/**\n * Returns all response headers as a key-value map.\n * Multiple values for the same header key can be combined into one,\n * separated by a comma and a space.\n * Note that the native getResponseHeader method for retrieving a single header\n * does a case insensitive match on the header name. This method does not\n * include any case normalization logic, it will just return a key-value\n * representation of the headers.\n * See: http://www.w3.org/TR/XMLHttpRequest/#the-getresponseheader()-method\n * @return {!Object<string, string>} An object with the header keys as keys\n *     and header values as values.\n */\ngoog.net.XhrIo.prototype.getResponseHeaders = function() {\n  // TODO(user): Make this function parse headers as per the spec\n  // (https://tools.ietf.org/html/rfc2616#section-4.2).\n\n  var headersObject = {};\n  var headersArray = this.getAllResponseHeaders().split('\\r\\n');\n  for (var i = 0; i < headersArray.length; i++) {\n    if (goog.string.isEmptyOrWhitespace(headersArray[i])) {\n      continue;\n    }\n    var keyValue =\n        goog.string.splitLimit(headersArray[i], ':', /* maxSplitCount= */ 1);\n    var key = keyValue[0];\n    var value = keyValue[1];\n\n    if (typeof value !== 'string') {\n      // There must be a value but it can be the empty string.\n      continue;\n    }\n\n    // Whitespace at the start and end of the value is meaningless.\n    value = value.trim();\n    // The key should not contain whitespace but we currently ignore that.\n\n    var values = headersObject[key] || [];\n    headersObject[key] = values;\n    values.push(value);\n  }\n\n  return goog.object.map(headersObject, function(values) {\n    return values.join(', ');\n  });\n};\n\n\n/**\n * Get the value of the response-header with the given name from the Xhr object.\n * As opposed to {@link #getResponseHeader}, this method does not require that\n * the request has completed.\n * @param {string} key The name of the response-header to retrieve.\n * @return {?string} The value of the response-header, or null if it is\n *     unavailable.\n */\ngoog.net.XhrIo.prototype.getStreamingResponseHeader = function(key) {\n  return this.xhr_ ? this.xhr_.getResponseHeader(key) : null;\n};\n\n\n/**\n * Gets the text of all the headers in the response. As opposed to\n * {@link #getAllResponseHeaders}, this method does not require that the request\n * has completed.\n * @return {string} The value of the response headers or empty string.\n */\ngoog.net.XhrIo.prototype.getAllStreamingResponseHeaders = function() {\n  return this.xhr_ ? this.xhr_.getAllResponseHeaders() : '';\n};\n\n\n/**\n * Get the last error message\n * @return {!goog.net.ErrorCode} Last error code.\n */\ngoog.net.XhrIo.prototype.getLastErrorCode = function() {\n  return this.lastErrorCode_;\n};\n\n\n/**\n * Get the last error message\n * @return {string} Last error message.\n */\ngoog.net.XhrIo.prototype.getLastError = function() {\n  return typeof this.lastError_ === 'string' ? this.lastError_ :\n                                               String(this.lastError_);\n};\n\n\n/**\n * Adds the last method, status and URI to the message.  This is used to add\n * this information to the logging calls.\n * @param {string} msg The message text that we want to add the extra text to.\n * @return {string} The message with the extra text appended.\n * @private\n */\ngoog.net.XhrIo.prototype.formatMsg_ = function(msg) {\n  return msg + ' [' + this.lastMethod_ + ' ' + this.lastUri_ + ' ' +\n      this.getStatus() + ']';\n};\n\n\n// Register the xhr handler as an entry point, so that\n// it can be monitored for exception handling, etc.\ngoog.debug.entryPointRegistry.register(\n    /**\n     * @param {function(!Function): !Function} transformer The transforming\n     *     function.\n     */\n    function(transformer) {\n      goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_ =\n          transformer(goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_);\n    });\n});  // goog.scope\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Common events for the network classes.\n */\n\n\ngoog.provide('goog.net.EventType');\n\n\n/**\n * Event names for network events\n * @enum {string}\n */\ngoog.net.EventType = {\n  COMPLETE: 'complete',\n  SUCCESS: 'success',\n  ERROR: 'error',\n  ABORT: 'abort',\n  READY: 'ready',\n  READY_STATE_CHANGE: 'readystatechange',\n  TIMEOUT: 'timeout',\n  INCREMENTAL_DATA: 'incrementaldata',\n  PROGRESS: 'progress',\n  // DOWNLOAD_PROGRESS and UPLOAD_PROGRESS are special events dispatched by\n  // goog.net.XhrIo to allow binding listeners specific to each type of\n  // progress.\n  DOWNLOAD_PROGRESS: 'downloadprogress',\n  UPLOAD_PROGRESS: 'uploadprogress',\n};\n","// Copyright 2007 Bob Ippolito. All Rights Reserved.\n// Modifications Copyright 2009 The Closure Library Authors. All Rights\n// Reserved.\n\n/**\n * @license Portions of this code are from MochiKit, received by\n * The Closure Authors under the MIT license. All other code is Copyright\n * 2005-2009 The Closure Authors. All Rights Reserved.\n */\n\n/**\n * @fileoverview Classes for tracking asynchronous operations and handling the\n * results. The Deferred object here is patterned after the Deferred object in\n * the Twisted python networking framework.\n *\n * See: http://twistedmatrix.com/projects/core/documentation/howto/defer.html\n *\n * Based on the Dojo code which in turn is based on the MochiKit code.\n *\n */\n\ngoog.provide('goog.async.Deferred');\ngoog.provide('goog.async.Deferred.AlreadyCalledError');\ngoog.provide('goog.async.Deferred.CanceledError');\n\ngoog.require('goog.Promise');\ngoog.require('goog.Thenable');\ngoog.require('goog.array');\ngoog.require('goog.asserts');\ngoog.require('goog.debug.Error');\n\n\n\n/**\n * A Deferred represents the result of an asynchronous operation. A Deferred\n * instance has no result when it is created, and is \"fired\" (given an initial\n * result) by calling `callback` or `errback`.\n *\n * Once fired, the result is passed through a sequence of callback functions\n * registered with `addCallback` or `addErrback`. The functions may\n * mutate the result before it is passed to the next function in the sequence.\n *\n * Callbacks and errbacks may be added at any time, including after the Deferred\n * has been \"fired\". If there are no pending actions in the execution sequence\n * of a fired Deferred, any new callback functions will be called with the last\n * computed result. Adding a callback function is the only way to access the\n * result of the Deferred.\n *\n * If a Deferred operation is canceled, an optional user-provided cancellation\n * function is invoked which may perform any special cleanup, followed by firing\n * the Deferred's errback sequence with a `CanceledError`. If the\n * Deferred has already fired, cancellation is ignored.\n *\n * Deferreds may be templated to a specific type they produce using generics\n * with syntax such as:\n *\n *    /** @type {goog.async.Deferred<string>} *\\\n *    var d = new goog.async.Deferred();\n *    // Compiler can infer that foo is a string.\n *    d.addCallback(function(foo) {...});\n *    d.callback('string');  // Checked to be passed a string\n *\n * Since deferreds are often used to produce different values across a chain,\n * the type information is not propagated across chains, but rather only\n * associated with specifically cast objects.\n *\n * @param {Function=} opt_onCancelFunction A function that will be called if the\n *     Deferred is canceled. If provided, this function runs before the\n *     Deferred is fired with a `CanceledError`.\n * @param {Object=} opt_defaultScope The default object context to call\n *     callbacks and errbacks in.\n * @constructor\n * @implements {goog.Thenable<VALUE>}\n * @template VALUE\n */\ngoog.async.Deferred = function(opt_onCancelFunction, opt_defaultScope) {\n  'use strict';\n  /**\n   * Entries in the sequence are arrays containing a callback, an errback, and\n   * an optional scope. The callback or errback in an entry may be null.\n   * @type {!Array<!Array>}\n   * @private\n   */\n  this.sequence_ = [];\n\n  /**\n   * Optional function that will be called if the Deferred is canceled.\n   * @type {Function|undefined}\n   * @private\n   */\n  this.onCancelFunction_ = opt_onCancelFunction;\n\n  /**\n   * The default scope to execute callbacks and errbacks in.\n   * @type {Object}\n   * @private\n   */\n  this.defaultScope_ = opt_defaultScope || null;\n\n  /**\n   * Whether the Deferred has been fired.\n   * @type {boolean}\n   * @private\n   */\n  this.fired_ = false;\n\n  /**\n   * Whether the last result in the execution sequence was an error.\n   * @type {boolean}\n   * @private\n   */\n  this.hadError_ = false;\n\n  /**\n   * The current Deferred result, updated as callbacks and errbacks are\n   * executed.\n   * @type {*}\n   * @private\n   */\n  this.result_ = undefined;\n\n  /**\n   * Whether the Deferred is blocked waiting on another Deferred to fire. If a\n   * callback or errback returns a Deferred as a result, the execution sequence\n   * is blocked until that Deferred result becomes available.\n   * @type {boolean}\n   * @private\n   */\n  this.blocked_ = false;\n\n  /**\n   * Whether this Deferred is blocking execution of another Deferred. If this\n   * instance was returned as a result in another Deferred's execution\n   * sequence,that other Deferred becomes blocked until this instance's\n   * execution sequence completes. No additional callbacks may be added to a\n   * Deferred once it is blocking another instance.\n   * @type {boolean}\n   * @private\n   */\n  this.blocking_ = false;\n\n  /**\n   * Whether the Deferred has been canceled without having a custom cancel\n   * function.\n   * @type {boolean}\n   * @private\n   */\n  this.silentlyCanceled_ = false;\n\n  /**\n   * If an error is thrown during Deferred execution with no errback to catch\n   * it, the error is rethrown after a timeout. Reporting the error after a\n   * timeout allows execution to continue in the calling context (empty when\n   * no error is scheduled).\n   * @type {number}\n   * @private\n   */\n  this.unhandledErrorId_ = 0;\n\n  /**\n   * If this Deferred was created by branch(), this will be the \"parent\"\n   * Deferred.\n   * @type {?goog.async.Deferred}\n   * @private\n   */\n  this.parent_ = null;\n\n  /**\n   * The number of Deferred objects that have been branched off this one. This\n   * will be decremented whenever a branch is fired or canceled.\n   * @type {number}\n   * @private\n   */\n  this.branches_ = 0;\n\n  if (goog.async.Deferred.LONG_STACK_TRACES) {\n    /**\n     * Holds the stack trace at time of deferred creation if the JS engine\n     * provides the Error.captureStackTrace API.\n     * @private {?string}\n     */\n    this.constructorStack_ = null;\n    if (Error.captureStackTrace) {\n      var target = { stack: '' };\n      Error.captureStackTrace(target, goog.async.Deferred);\n      // Check if Error.captureStackTrace worked. It fails in gjstest.\n      if (typeof target.stack == 'string') {\n        // Remove first line and force stringify to prevent memory leak due to\n        // holding on to actual stack frames.\n        this.constructorStack_ = target.stack.replace(/^[^\\n]*\\n/, '');\n      }\n    }\n  }\n};\n\n\n/**\n * @define {boolean} Whether unhandled errors should always get rethrown to the\n * global scope. Defaults to false.\n */\ngoog.async.Deferred.STRICT_ERRORS =\n    goog.define('goog.async.Deferred.STRICT_ERRORS', false);\n\n\n/**\n * @define {boolean} Whether to attempt to make stack traces long.  Defaults to\n * false.\n */\ngoog.async.Deferred.LONG_STACK_TRACES =\n    goog.define('goog.async.Deferred.LONG_STACK_TRACES', false);\n\n\n/**\n * Cancels a Deferred that has not yet been fired, or is blocked on another\n * deferred operation. If this Deferred is waiting for a blocking Deferred to\n * fire, the blocking Deferred will also be canceled.\n *\n * If this Deferred was created by calling branch() on a parent Deferred with\n * opt_propagateCancel set to true, the parent may also be canceled. If\n * opt_deepCancel is set, cancel() will be called on the parent (as well as any\n * other ancestors if the parent is also a branch). If one or more branches were\n * created with opt_propagateCancel set to true, the parent will be canceled if\n * cancel() is called on all of those branches.\n *\n * @param {boolean=} opt_deepCancel If true, cancels this Deferred's parent even\n *     if cancel() hasn't been called on some of the parent's branches. Has no\n *     effect on a branch without opt_propagateCancel set to true.\n */\ngoog.async.Deferred.prototype.cancel = function(opt_deepCancel) {\n  'use strict';\n  if (!this.hasFired()) {\n    if (this.parent_) {\n      // Get rid of the parent reference before potentially running the parent's\n      // canceler function to ensure that this cancellation isn't\n      // double-counted.\n      var parent = this.parent_;\n      delete this.parent_;\n      if (opt_deepCancel) {\n        parent.cancel(opt_deepCancel);\n      } else {\n        parent.branchCancel_();\n      }\n    }\n\n    if (this.onCancelFunction_) {\n      // Call in user-specified scope.\n      this.onCancelFunction_.call(this.defaultScope_, this);\n    } else {\n      this.silentlyCanceled_ = true;\n    }\n    if (!this.hasFired()) {\n      this.errback(new goog.async.Deferred.CanceledError(this));\n    }\n  } else if (this.result_ instanceof goog.async.Deferred) {\n    this.result_.cancel();\n  }\n};\n\n\n/**\n * Handle a single branch being canceled. Once all branches are canceled, this\n * Deferred will be canceled as well.\n *\n * @private\n */\ngoog.async.Deferred.prototype.branchCancel_ = function() {\n  'use strict';\n  this.branches_--;\n  if (this.branches_ <= 0) {\n    this.cancel();\n  }\n};\n\n\n/**\n * Called after a blocking Deferred fires. Unblocks this Deferred and resumes\n * its execution sequence.\n *\n * @param {boolean} isSuccess Whether the result is a success or an error.\n * @param {*} res The result of the blocking Deferred.\n * @private\n */\ngoog.async.Deferred.prototype.continue_ = function(isSuccess, res) {\n  'use strict';\n  this.blocked_ = false;\n  this.updateResult_(isSuccess, res);\n};\n\n\n/**\n * Updates the current result based on the success or failure of the last action\n * in the execution sequence.\n *\n * @param {boolean} isSuccess Whether the new result is a success or an error.\n * @param {*} res The result.\n * @private\n */\ngoog.async.Deferred.prototype.updateResult_ = function(isSuccess, res) {\n  'use strict';\n  this.fired_ = true;\n  this.result_ = res;\n  this.hadError_ = !isSuccess;\n  this.fire_();\n};\n\n\n/**\n * Verifies that the Deferred has not yet been fired.\n *\n * @private\n * @throws {Error} If this has already been fired.\n */\ngoog.async.Deferred.prototype.check_ = function() {\n  'use strict';\n  if (this.hasFired()) {\n    if (!this.silentlyCanceled_) {\n      throw new goog.async.Deferred.AlreadyCalledError(this);\n    }\n    this.silentlyCanceled_ = false;\n  }\n};\n\n\n/**\n * Fire the execution sequence for this Deferred by passing the starting result\n * to the first registered callback.\n * @param {VALUE=} opt_result The starting result.\n */\ngoog.async.Deferred.prototype.callback = function(opt_result) {\n  'use strict';\n  this.check_();\n  this.assertNotDeferred_(opt_result);\n  this.updateResult_(true /* isSuccess */, opt_result);\n};\n\n\n/**\n * Fire the execution sequence for this Deferred by passing the starting error\n * result to the first registered errback.\n * @param {*=} opt_result The starting error.\n */\ngoog.async.Deferred.prototype.errback = function(opt_result) {\n  'use strict';\n  this.check_();\n  this.assertNotDeferred_(opt_result);\n  this.makeStackTraceLong_(opt_result);\n  this.updateResult_(false /* isSuccess */, opt_result);\n};\n\n\n/**\n * A method that is invoked with the reason of Deferred's failure for unhandled\n * errors.\n * @type {function(*)}\n * @private\n */\ngoog.async.Deferred.unhandledErrorHandler_ = (e) => {\n  throw e;\n};\n\n\n/**\n * @param {function(*)} handler A function that will be called with the reason\n *     of Deferred's failure on unhandled errors. If none is specified, errors\n *     will fail with `throw`.\n */\ngoog.async.Deferred.setUnhandledErrorHandler = function(handler) {\n  'use strict';\n  goog.async.Deferred.unhandledErrorHandler_ = handler;\n};\n\n\n/**\n * Attempt to make the error's stack trace be long in that it contains the\n * stack trace from the point where the deferred was created on top of the\n * current stack trace to give additional context.\n * @param {*} error\n * @private\n * @suppress {missingProperties} error.stack\n */\ngoog.async.Deferred.prototype.makeStackTraceLong_ = function(error) {\n  'use strict';\n  if (!goog.async.Deferred.LONG_STACK_TRACES) {\n    return;\n  }\n  if (this.constructorStack_ && goog.isObject(error) && error.stack &&\n      // Stack looks like it was system generated. See\n      // https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi\n      (/^[^\\n]+(\\n   [^\\n]+)+/).test(error.stack)) {\n    error.stack = error.stack + '\\nDEFERRED OPERATION:\\n' +\n        this.constructorStack_;\n  }\n};\n\n\n/**\n * Asserts that an object is not a Deferred.\n * @param {*} obj The object to test.\n * @throws {Error} Throws an exception if the object is a Deferred.\n * @private\n */\ngoog.async.Deferred.prototype.assertNotDeferred_ = function(obj) {\n  'use strict';\n  goog.asserts.assert(\n      !(obj instanceof goog.async.Deferred),\n      'An execution sequence may not be initiated with a blocking Deferred.');\n};\n\n\n/**\n * Register a callback function to be called with a successful result. If no\n * value is returned by the callback function, the result value is unchanged. If\n * a new value is returned, it becomes the Deferred result and will be passed to\n * the next callback in the execution sequence.\n *\n * If the function throws an error, the error becomes the new result and will be\n * passed to the next errback in the execution chain.\n *\n * If the function returns a Deferred, the execution sequence will be blocked\n * until that Deferred fires. Its result will be passed to the next callback (or\n * errback if it is an error result) in this Deferred's execution sequence.\n *\n * @param {function(this:T,VALUE):?} cb The function to be called with a\n *     successful result.\n * @param {T=} opt_scope An optional scope to call the callback in.\n * @return {!goog.async.Deferred} This Deferred.\n * @template T\n */\ngoog.async.Deferred.prototype.addCallback = function(cb, opt_scope) {\n  'use strict';\n  return this.addCallbacks(cb, null, opt_scope);\n};\n\n\n/**\n * Register a callback function to be called with an error result. If no value\n * is returned by the function, the error result is unchanged. If a new error\n * value is returned or thrown, that error becomes the Deferred result and will\n * be passed to the next errback in the execution sequence.\n *\n * If the errback function handles the error by returning a non-error value,\n * that result will be passed to the next normal callback in the sequence.\n *\n * If the function returns a Deferred, the execution sequence will be blocked\n * until that Deferred fires. Its result will be passed to the next callback (or\n * errback if it is an error result) in this Deferred's execution sequence.\n *\n * @param {function(this:T,?):?} eb The function to be called on an\n *     unsuccessful result.\n * @param {T=} opt_scope An optional scope to call the errback in.\n * @return {!goog.async.Deferred<VALUE>} This Deferred.\n * @template T\n */\ngoog.async.Deferred.prototype.addErrback = function(eb, opt_scope) {\n  'use strict';\n  return this.addCallbacks(null, eb, opt_scope);\n};\n\n\n/**\n * Registers one function as both a callback and errback.\n *\n * @param {function(this:T,?):?} f The function to be called on any result.\n * @param {T=} opt_scope An optional scope to call the function in.\n * @return {!goog.async.Deferred} This Deferred.\n * @template T\n */\ngoog.async.Deferred.prototype.addBoth = function(f, opt_scope) {\n  'use strict';\n  return this.addCallbacks(f, f, opt_scope);\n};\n\n\n/**\n * Like addBoth, but propagates uncaught exceptions in the errback.\n *\n * @param {function(this:T,?):?} f The function to be called on any result.\n * @param {T=} opt_scope An optional scope to call the function in.\n * @return {!goog.async.Deferred<VALUE>} This Deferred.\n * @template T\n */\ngoog.async.Deferred.prototype.addFinally = function(f, opt_scope) {\n  'use strict';\n  return this.addCallbacks(f, function(err) {\n    'use strict';\n    var result = f.call(/** @type {?} */ (this), err);\n    if (result === undefined) {\n      throw err;\n    }\n    return result;\n  }, opt_scope);\n};\n\n\n/**\n * Registers a callback function and an errback function at the same position\n * in the execution sequence. Only one of these functions will execute,\n * depending on the error state during the execution sequence.\n *\n * NOTE: This is not equivalent to {@code def.addCallback().addErrback()}! If\n * the callback is invoked, the errback will be skipped, and vice versa.\n *\n * @param {?(function(this:T,VALUE):?)} cb The function to be called on a\n *     successful result.\n * @param {?(function(this:T,?):?)} eb The function to be called on an\n *     unsuccessful result.\n * @param {T=} opt_scope An optional scope to call the functions in.\n * @return {!goog.async.Deferred} This Deferred.\n * @template T\n */\ngoog.async.Deferred.prototype.addCallbacks = function(cb, eb, opt_scope) {\n  'use strict';\n  goog.asserts.assert(!this.blocking_, 'Blocking Deferreds can not be re-used');\n  this.sequence_.push([cb, eb, opt_scope]);\n  if (this.hasFired()) {\n    this.fire_();\n  }\n  return this;\n};\n\n\n/**\n * Implements {@see goog.Thenable} for seamless integration with\n * {@see goog.Promise}.\n * Deferred results are mutable and may represent multiple values over\n * their lifetime. Calling `then` on a Deferred returns a Promise\n * with the result of the Deferred at that point in its callback chain.\n * Note that if the Deferred result is never mutated, and only\n * `then` calls are made, the Deferred will behave like a Promise.\n *\n * @override\n */\ngoog.async.Deferred.prototype.then = function(\n    opt_onFulfilled, opt_onRejected, opt_context) {\n  'use strict';\n  var resolve, reject;\n  var promise = new goog.Promise(function(res, rej) {\n    'use strict';\n    // Copying resolvers to outer scope, so that they are available when the\n    // deferred callback fires (which may be synchronous).\n    resolve = res;\n    reject = rej;\n  });\n  this.addCallbacks(resolve, function(reason) {\n    'use strict';\n    if (reason instanceof goog.async.Deferred.CanceledError) {\n      promise.cancel();\n    } else {\n      reject(reason);\n    }\n  });\n  return promise.then(opt_onFulfilled, opt_onRejected, opt_context);\n};\ngoog.Thenable.addImplementation(goog.async.Deferred);\n\n\n/**\n * Links another Deferred to the end of this Deferred's execution sequence. The\n * result of this execution sequence will be passed as the starting result for\n * the chained Deferred, invoking either its first callback or errback.\n *\n * @param {!goog.async.Deferred} otherDeferred The Deferred to chain.\n * @return {!goog.async.Deferred} This Deferred.\n */\ngoog.async.Deferred.prototype.chainDeferred = function(otherDeferred) {\n  'use strict';\n  this.addCallbacks(\n      otherDeferred.callback, otherDeferred.errback, otherDeferred);\n  return this;\n};\n\n\n/**\n * Makes this Deferred wait for another Deferred's execution sequence to\n * complete before continuing.\n *\n * This is equivalent to adding a callback that returns `otherDeferred`,\n * but doesn't prevent additional callbacks from being added to\n * `otherDeferred`.\n *\n * @param {!goog.async.Deferred|!goog.Thenable} otherDeferred The Deferred\n *     to wait for.\n * @return {!goog.async.Deferred} This Deferred.\n */\ngoog.async.Deferred.prototype.awaitDeferred = function(otherDeferred) {\n  'use strict';\n  if (!(otherDeferred instanceof goog.async.Deferred)) {\n    // The Thenable case.\n    return this.addCallback(function() {\n      'use strict';\n      return otherDeferred;\n    });\n  }\n  return this.addCallback(goog.bind(otherDeferred.branch, otherDeferred));\n};\n\n\n/**\n * Creates a branch off this Deferred's execution sequence, and returns it as a\n * new Deferred. The branched Deferred's starting result will be shared with the\n * parent at the point of the branch, even if further callbacks are added to the\n * parent.\n *\n * All branches at the same stage in the execution sequence will receive the\n * same starting value.\n *\n * @param {boolean=} opt_propagateCancel If cancel() is called on every child\n *     branch created with opt_propagateCancel, the parent will be canceled as\n *     well.\n * @return {!goog.async.Deferred<VALUE>} A Deferred that will be started with\n *     the computed result from this stage in the execution sequence.\n */\ngoog.async.Deferred.prototype.branch = function(opt_propagateCancel) {\n  'use strict';\n  var d = new goog.async.Deferred();\n  this.chainDeferred(d);\n  if (opt_propagateCancel) {\n    d.parent_ = this;\n    this.branches_++;\n  }\n  return d;\n};\n\n\n/**\n * @return {boolean} Whether the execution sequence has been started on this\n *     Deferred by invoking `callback` or `errback`.\n */\ngoog.async.Deferred.prototype.hasFired = function() {\n  'use strict';\n  return this.fired_;\n};\n\n\n/**\n * @param {*} res The latest result in the execution sequence.\n * @return {boolean} Whether the current result is an error that should cause\n *     the next errback to fire. May be overridden by subclasses to handle\n *     special error types.\n * @protected\n */\ngoog.async.Deferred.prototype.isError = function(res) {\n  'use strict';\n  return res instanceof Error;\n};\n\n\n/**\n * @return {boolean} Whether an errback exists in the remaining sequence.\n * @private\n */\ngoog.async.Deferred.prototype.hasErrback_ = function() {\n  'use strict';\n  return goog.array.some(this.sequence_, function(sequenceRow) {\n    'use strict';\n    // The errback is the second element in the array.\n    return goog.isFunction(sequenceRow[1]);\n  });\n};\n\n\n/**\n * Return the most recent value fired.\n *\n * @return {VALUE|undefined}\n * @deprecated This method is only for facilitating migrations from other async\n *     primitives.\n */\ngoog.async.Deferred.prototype.getLastValueForMigration = function() {\n  'use strict';\n  return (this.hasFired() && !this.hadError_) ? this.result_ : undefined;\n};\n\n\n/**\n * Exhausts the execution sequence while a result is available. The result may\n * be modified by callbacks or errbacks, and execution will block if the\n * returned result is an incomplete Deferred.\n *\n * @private\n */\ngoog.async.Deferred.prototype.fire_ = function() {\n  'use strict';\n  if (this.unhandledErrorId_ && this.hasFired() && this.hasErrback_()) {\n    // It is possible to add errbacks after the Deferred has fired. If a new\n    // errback is added immediately after the Deferred encountered an unhandled\n    // error, but before that error is rethrown, the error is unscheduled.\n    goog.async.Deferred.unscheduleError_(this.unhandledErrorId_);\n    this.unhandledErrorId_ = 0;\n  }\n\n  if (this.parent_) {\n    this.parent_.branches_--;\n    delete this.parent_;\n  }\n\n  var res = this.result_;\n  var unhandledException = false;\n  var isNewlyBlocked = false;\n\n  while (this.sequence_.length && !this.blocked_) {\n    var sequenceEntry = this.sequence_.shift();\n\n    var callback = sequenceEntry[0];\n    var errback = sequenceEntry[1];\n    var scope = sequenceEntry[2];\n\n    var f = this.hadError_ ? errback : callback;\n    if (f) {\n\n      try {\n        var ret = f.call(scope || this.defaultScope_, res);\n\n        // If no result, then use previous result.\n        if (ret !== undefined) {\n          // Bubble up the error as long as the return value hasn't changed.\n          this.hadError_ = this.hadError_ && (ret == res || this.isError(ret));\n          this.result_ = res = ret;\n        }\n\n        if (goog.Thenable.isImplementedBy(res) ||\n            (typeof goog.global['Promise'] === 'function' &&\n            res instanceof goog.global['Promise'])) {\n          isNewlyBlocked = true;\n          this.blocked_ = true;\n        }\n\n      } catch (ex) {\n        res = ex;\n        this.hadError_ = true;\n        this.makeStackTraceLong_(res);\n\n        if (!this.hasErrback_()) {\n          // If an error is thrown with no additional errbacks in the queue,\n          // prepare to rethrow the error.\n          unhandledException = true;\n        }\n      }\n    }\n  }\n\n  this.result_ = res;\n\n  if (isNewlyBlocked) {\n    var onCallback = goog.bind(this.continue_, this, true /* isSuccess */);\n    var onErrback = goog.bind(this.continue_, this, false /* isSuccess */);\n\n    if (res instanceof goog.async.Deferred) {\n      res.addCallbacks(onCallback, onErrback);\n      res.blocking_ = true;\n    } else {\n      /** @type {!IThenable} */ (res).then(onCallback, onErrback);\n    }\n  } else if (goog.async.Deferred.STRICT_ERRORS && this.isError(res) &&\n      !(res instanceof goog.async.Deferred.CanceledError)) {\n    this.hadError_ = true;\n    unhandledException = true;\n  }\n\n  if (unhandledException) {\n    // Rethrow the unhandled error after a timeout. Execution will continue, but\n    // the error will be seen by global handlers and the user. The throw will\n    // be canceled if another errback is appended before the timeout executes.\n    // The error's original stack trace is preserved where available.\n    this.unhandledErrorId_ = goog.async.Deferred.scheduleError_(res);\n  }\n};\n\n\n/**\n * Creates a Deferred that has an initial result.\n *\n * @param {*=} opt_result The result.\n * @return {!goog.async.Deferred} The new Deferred.\n */\ngoog.async.Deferred.succeed = function(opt_result) {\n  'use strict';\n  var d = new goog.async.Deferred();\n  d.callback(opt_result);\n  return d;\n};\n\n\n/**\n * Creates a Deferred that fires when the given promise resolves.\n * Use only during migration to Promises.\n *\n * Note: If the promise resolves to a thenable value (which is not allowed by\n * conforming promise implementations), then the deferred may behave\n * unexpectedly as it tries to wait on it. This should not be a risk when using\n * goog.Promise, goog.async.Deferred, or native Promise objects.\n *\n * @param {!IThenable<T>} promise\n * @return {!goog.async.Deferred<T>} The new Deferred.\n * @template T\n */\ngoog.async.Deferred.fromPromise = function(promise) {\n  'use strict';\n  var d = new goog.async.Deferred();\n  promise.then(\n      function(value) {\n        'use strict';\n        d.callback(value);\n      },\n      function(error) {\n        'use strict';\n        d.errback(error);\n      });\n  return d;\n};\n\n\n/**\n * Creates a Deferred that has an initial error result.\n *\n * @param {*} res The error result.\n * @return {!goog.async.Deferred} The new Deferred.\n */\ngoog.async.Deferred.fail = function(res) {\n  'use strict';\n  var d = new goog.async.Deferred();\n  d.errback(res);\n  return d;\n};\n\n\n/**\n * Creates a Deferred that has already been canceled.\n *\n * @return {!goog.async.Deferred} The new Deferred.\n */\ngoog.async.Deferred.canceled = function() {\n  'use strict';\n  var d = new goog.async.Deferred();\n  d.cancel();\n  return d;\n};\n\n\n/**\n * Normalizes values that may or may not be Deferreds.\n *\n * If the input value is a Deferred, the Deferred is branched (so the original\n * execution sequence is not modified) and the input callback added to the new\n * branch. The branch is returned to the caller.\n *\n * If the input value is not a Deferred, the callback will be executed\n * immediately and an already firing Deferred will be returned to the caller.\n *\n * In the following (contrived) example, if <code>isImmediate</code> is true\n * then 3 is alerted immediately, otherwise 6 is alerted after a 2-second delay.\n *\n * <pre>\n * var value;\n * if (isImmediate) {\n *   value = 3;\n * } else {\n *   value = new goog.async.Deferred();\n *   setTimeout(function() { value.callback(6); }, 2000);\n * }\n *\n * var d = goog.async.Deferred.when(value, alert);\n * </pre>\n *\n * @param {*} value Deferred or normal value to pass to the callback.\n * @param {function(this:T, ?):?} callback The callback to execute.\n * @param {T=} opt_scope An optional scope to call the callback in.\n * @return {!goog.async.Deferred} A new Deferred that will call the input\n *     callback with the input value.\n * @template T\n */\ngoog.async.Deferred.when = function(value, callback, opt_scope) {\n  'use strict';\n  if (value instanceof goog.async.Deferred) {\n    return value.branch(true).addCallback(callback, opt_scope);\n  } else {\n    return goog.async.Deferred.succeed(value).addCallback(callback, opt_scope);\n  }\n};\n\n\n\n/**\n * An error sub class that is used when a Deferred has already been called.\n * @param {!goog.async.Deferred} deferred The Deferred.\n *\n * @constructor\n * @extends {goog.debug.Error}\n */\ngoog.async.Deferred.AlreadyCalledError = function(deferred) {\n  'use strict';\n  goog.debug.Error.call(this);\n\n  /**\n   * The Deferred that raised this error.\n   * @type {goog.async.Deferred}\n   */\n  this.deferred = deferred;\n};\ngoog.inherits(goog.async.Deferred.AlreadyCalledError, goog.debug.Error);\n\n\n/** @override */\ngoog.async.Deferred.AlreadyCalledError.prototype.message =\n    'Deferred has already fired';\n\n\n/** @override */\ngoog.async.Deferred.AlreadyCalledError.prototype.name = 'AlreadyCalledError';\n\n\n\n/**\n * An error sub class that is used when a Deferred is canceled.\n *\n * @param {!goog.async.Deferred} deferred The Deferred object.\n * @constructor\n * @extends {goog.debug.Error}\n */\ngoog.async.Deferred.CanceledError = function(deferred) {\n  'use strict';\n  goog.debug.Error.call(this);\n\n  /**\n   * The Deferred that raised this error.\n   * @type {goog.async.Deferred}\n   */\n  this.deferred = deferred;\n};\ngoog.inherits(goog.async.Deferred.CanceledError, goog.debug.Error);\n\n\n/** @override */\ngoog.async.Deferred.CanceledError.prototype.message = 'Deferred was canceled';\n\n\n/** @override */\ngoog.async.Deferred.CanceledError.prototype.name = 'CanceledError';\n\n\n\n/**\n * Wrapper around errors that are scheduled to be thrown by failing deferreds\n * after a timeout.\n *\n * @param {*} error Error from a failing deferred.\n * @constructor\n * @final\n * @private\n * @struct\n */\ngoog.async.Deferred.Error_ = function(error) {\n  'use strict';\n  /** @const @private {number} */\n  this.id_ = goog.global.setTimeout(goog.bind(this.throwError, this), 0);\n\n  /** @const @private {*} */\n  this.error_ = error;\n};\n\n\n/**\n * Actually throws the error and removes it from the list of pending\n * deferred errors.\n */\ngoog.async.Deferred.Error_.prototype.throwError = function() {\n  'use strict';\n  goog.asserts.assert(\n      goog.async.Deferred.errorMap_[this.id_],\n      'Cannot throw an error that is not scheduled.');\n  delete goog.async.Deferred.errorMap_[this.id_];\n  goog.async.Deferred.unhandledErrorHandler_(this.error_);\n};\n\n\n/**\n * Resets the error throw timer.\n */\ngoog.async.Deferred.Error_.prototype.resetTimer = function() {\n  'use strict';\n  goog.global.clearTimeout(this.id_);\n};\n\n\n/**\n * Map of unhandled errors scheduled to be rethrown in a future timestep.\n * @private {!Object<(number|string), goog.async.Deferred.Error_>}\n */\ngoog.async.Deferred.errorMap_ = {};\n\n\n/**\n * Schedules an error to be thrown after a delay.\n * @param {*} error Error from a failing deferred.\n * @return {number} Id of the error.\n * @private\n */\ngoog.async.Deferred.scheduleError_ = function(error) {\n  'use strict';\n  var deferredError = new goog.async.Deferred.Error_(error);\n  goog.async.Deferred.errorMap_[deferredError.id_] = deferredError;\n  return deferredError.id_;\n};\n\n\n/**\n * Unschedules an error from being thrown.\n * @param {number} id Id of the deferred error to unschedule.\n * @private\n */\ngoog.async.Deferred.unscheduleError_ = function(id) {\n  'use strict';\n  var error = goog.async.Deferred.errorMap_[id];\n  if (error) {\n    error.resetTimer();\n    delete goog.async.Deferred.errorMap_[id];\n  }\n};\n\n\n/**\n * Asserts that there are no pending deferred errors. If there are any\n * scheduled errors, one will be thrown immediately to make this function fail.\n */\ngoog.async.Deferred.assertNoErrors = function() {\n  'use strict';\n  var map = goog.async.Deferred.errorMap_;\n  for (var key in map) {\n    var error = map[key];\n    error.resetTimer();\n    error.throwError();\n  }\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview A utility to load JavaScript files via DOM script tags.\n * Refactored from goog.net.Jsonp. Works cross-domain.\n */\n\ngoog.provide('goog.net.jsloader');\ngoog.provide('goog.net.jsloader.Error');\ngoog.provide('goog.net.jsloader.ErrorCode');\ngoog.provide('goog.net.jsloader.Options');\n\ngoog.require('goog.array');\ngoog.require('goog.async.Deferred');\ngoog.require('goog.debug.Error');\ngoog.require('goog.dom');\ngoog.require('goog.dom.TagName');\ngoog.require('goog.dom.safe');\ngoog.require('goog.html.TrustedResourceUrl');\ngoog.require('goog.object');\n\n\n/**\n * The name of the property of goog.global under which the JavaScript\n * verification object is stored by the loaded script.\n * @private {string}\n */\ngoog.net.jsloader.GLOBAL_VERIFY_OBJS_ = 'closure_verification';\n\n\n/**\n * The default length of time, in milliseconds, we are prepared to wait for a\n * load request to complete.\n * @type {number}\n */\ngoog.net.jsloader.DEFAULT_TIMEOUT = 5000;\n\n\n/**\n * Optional parameters for goog.net.jsloader.send.\n * timeout: The length of time, in milliseconds, we are prepared to wait\n *     for a load request to complete, or 0 or negative for no timeout. Default\n *     is 5 seconds.\n * document: The HTML document under which to load the JavaScript. Default is\n *     the current document.\n * cleanupWhenDone: If true clean up the script tag after script completes to\n *     load. This is important if you just want to read data from the JavaScript\n *     and then throw it away. Default is false.\n * attributes: Additional attributes to set on the script tag.\n *\n * @typedef {{\n *   timeout: (number|undefined),\n *   document: (HTMLDocument|undefined),\n *   cleanupWhenDone: (boolean|undefined),\n *   attributes: (!Object<string, string>|undefined)\n * }}\n */\ngoog.net.jsloader.Options;\n\n\n/**\n * Scripts (URIs) waiting to be loaded.\n * @private {!Array<!goog.html.TrustedResourceUrl>}\n */\ngoog.net.jsloader.scriptsToLoad_ = [];\n\n\n/**\n * The deferred result of loading the URIs in scriptsToLoad_.\n * We need to return this to a caller that wants to load URIs while\n * a deferred is already working on them.\n * @private {!goog.async.Deferred<null>}\n */\ngoog.net.jsloader.scriptLoadingDeferred_;\n\n\n\n/**\n * Loads and evaluates the JavaScript files at the specified URIs, guaranteeing\n * the order of script loads.\n *\n * Because we have to load the scripts in serial (load script 1, exec script 1,\n * load script 2, exec script 2, and so on), this will be slower than doing\n * the network fetches in parallel.\n *\n * If you need to load a large number of scripts but dependency order doesn't\n * matter, you should just call goog.net.jsloader.safeLoad N times.\n *\n * If you need to load a large number of scripts on the same domain,\n * you may want to use goog.module.ModuleLoader.\n *\n * @param {Array<!goog.html.TrustedResourceUrl>} trustedUris The URIs to load.\n * @param {goog.net.jsloader.Options=} opt_options Optional parameters. See\n *     goog.net.jsloader.options documentation for details.\n * @return {!goog.async.Deferred} The deferred result, that may be used to add\n *     callbacks\n */\ngoog.net.jsloader.safeLoadMany = function(trustedUris, opt_options) {\n  // Loading the scripts in serial introduces asynchronosity into the flow.\n  // Therefore, there are race conditions where client A can kick off the load\n  // sequence for client B, even though client A's scripts haven't all been\n  // loaded yet.\n  //\n  // To work around this issue, all module loads share a queue.\n  if (!trustedUris.length) {\n    return goog.async.Deferred.succeed(null);\n  }\n\n  var isAnotherModuleLoading = goog.net.jsloader.scriptsToLoad_.length;\n  goog.array.extend(goog.net.jsloader.scriptsToLoad_, trustedUris);\n  if (isAnotherModuleLoading) {\n    // jsloader is still loading some other scripts.\n    // In order to prevent the race condition noted above, we just add\n    // these URIs to the end of the scripts' queue and return the deferred\n    // result of the ongoing script load, so the caller knows when they\n    // finish loading.\n    return goog.net.jsloader.scriptLoadingDeferred_;\n  }\n\n  trustedUris = goog.net.jsloader.scriptsToLoad_;\n  var popAndLoadNextScript = function() {\n    var trustedUri = trustedUris.shift();\n    var deferred = goog.net.jsloader.safeLoad(trustedUri, opt_options);\n    if (trustedUris.length) {\n      deferred.addBoth(popAndLoadNextScript);\n    }\n    return deferred;\n  };\n  goog.net.jsloader.scriptLoadingDeferred_ = popAndLoadNextScript();\n  return goog.net.jsloader.scriptLoadingDeferred_;\n};\n\n\n/**\n * Loads and evaluates a JavaScript file.\n * When the script loads, a user callback is called.\n * It is the client's responsibility to verify that the script ran successfully.\n *\n * @param {!goog.html.TrustedResourceUrl} trustedUri The URI of the JavaScript.\n * @param {goog.net.jsloader.Options=} opt_options Optional parameters. See\n *     goog.net.jsloader.Options documentation for details.\n * @return {!goog.async.Deferred} The deferred result, that may be used to add\n *     callbacks and/or cancel the transmission.\n *     The error callback will be called with a single goog.net.jsloader.Error\n *     parameter.\n */\ngoog.net.jsloader.safeLoad = function(trustedUri, opt_options) {\n  var options = opt_options || {};\n  var doc = options.document || document;\n  var uri = goog.html.TrustedResourceUrl.unwrap(trustedUri);\n\n  var script = goog.dom.createElement(goog.dom.TagName.SCRIPT);\n  var request = {script_: script, timeout_: undefined};\n  var deferred = new goog.async.Deferred(goog.net.jsloader.cancel_, request);\n\n  // Set a timeout.\n  var timeout = null;\n  var timeoutDuration = (options.timeout != null) ?\n      options.timeout :\n      goog.net.jsloader.DEFAULT_TIMEOUT;\n  if (timeoutDuration > 0) {\n    timeout = window.setTimeout(function() {\n      goog.net.jsloader.cleanup_(script, true);\n      deferred.errback(\n          new goog.net.jsloader.Error(\n              goog.net.jsloader.ErrorCode.TIMEOUT,\n              'Timeout reached for loading script ' + uri));\n    }, timeoutDuration);\n    request.timeout_ = timeout;\n  }\n\n  // Hang the user callback to be called when the script completes to load.\n  // NOTE(user): This callback will be called in IE even upon error. In any\n  // case it is the client's responsibility to verify that the script ran\n  // successfully.\n  script.onload = script.onreadystatechange = function() {\n    if (!script.readyState || script.readyState == 'loaded' ||\n        script.readyState == 'complete') {\n      var removeScriptNode = options.cleanupWhenDone || false;\n      goog.net.jsloader.cleanup_(script, removeScriptNode, timeout);\n      deferred.callback(null);\n    }\n  };\n\n  // Add an error callback.\n  // NOTE(user): Not supported in IE.\n  script.onerror = function() {\n    goog.net.jsloader.cleanup_(script, true, timeout);\n    deferred.errback(\n        new goog.net.jsloader.Error(\n            goog.net.jsloader.ErrorCode.LOAD_ERROR,\n            'Error while loading script ' + uri));\n  };\n\n  var properties = options.attributes || {};\n  goog.object.extend(\n      properties, {'type': 'text/javascript', 'charset': 'UTF-8'});\n  goog.dom.setProperties(script, properties);\n  // NOTE(user): Safari never loads the script if we don't set the src\n  // attribute before appending.\n  goog.dom.safe.setScriptSrc(script, trustedUri);\n  var scriptParent = goog.net.jsloader.getScriptParentElement_(doc);\n  scriptParent.appendChild(script);\n\n  return deferred;\n};\n\n\n/**\n * Loads a JavaScript file and verifies it was evaluated successfully, using a\n * verification object.\n * The verification object is set by the loaded JavaScript at the end of the\n * script.\n * We verify this object was set and return its value in the success callback.\n * If the object is not defined we trigger an error callback.\n *\n * @param {!goog.html.TrustedResourceUrl} trustedUri The URI of the JavaScript.\n * @param {string} verificationObjName The name of the verification object that\n *     the loaded script should set.\n * @param {goog.net.jsloader.Options} options Optional parameters. See\n *     goog.net.jsloader.Options documentation for details.\n * @return {!goog.async.Deferred} The deferred result, that may be used to add\n *     callbacks and/or cancel the transmission.\n *     The success callback will be called with a single parameter containing\n *     the value of the verification object.\n *     The error callback will be called with a single goog.net.jsloader.Error\n *     parameter.\n */\ngoog.net.jsloader.safeLoadAndVerify = function(\n    trustedUri, verificationObjName, options) {\n  // Define the global objects variable.\n  if (!goog.global[goog.net.jsloader.GLOBAL_VERIFY_OBJS_]) {\n    goog.global[goog.net.jsloader.GLOBAL_VERIFY_OBJS_] = {};\n  }\n  var verifyObjs = goog.global[goog.net.jsloader.GLOBAL_VERIFY_OBJS_];\n  var uri = goog.html.TrustedResourceUrl.unwrap(trustedUri);\n\n  // Verify that the expected object does not exist yet.\n  if (verifyObjs[verificationObjName] !== undefined) {\n    // TODO(user): Error or reset variable?\n    return goog.async.Deferred.fail(\n        new goog.net.jsloader.Error(\n            goog.net.jsloader.ErrorCode.VERIFY_OBJECT_ALREADY_EXISTS,\n            'Verification object ' + verificationObjName +\n                ' already defined.'));\n  }\n\n  // Send request to load the JavaScript.\n  var sendDeferred = goog.net.jsloader.safeLoad(trustedUri, options);\n\n  // Create a deferred object wrapping the send result.\n  var deferred =\n      new goog.async.Deferred(goog.bind(sendDeferred.cancel, sendDeferred));\n\n  // Call user back with object that was set by the script.\n  sendDeferred.addCallback(function() {\n    var result = verifyObjs[verificationObjName];\n    if (result !== undefined) {\n      deferred.callback(result);\n      delete verifyObjs[verificationObjName];\n    } else {\n      // Error: script was not loaded properly.\n      deferred.errback(\n          new goog.net.jsloader.Error(\n              goog.net.jsloader.ErrorCode.VERIFY_ERROR, 'Script ' + uri +\n                  ' loaded, but verification object ' + verificationObjName +\n                  ' was not defined.'));\n    }\n  });\n\n  // Pass error to new deferred object.\n  sendDeferred.addErrback(function(error) {\n    if (verifyObjs[verificationObjName] !== undefined) {\n      delete verifyObjs[verificationObjName];\n    }\n    deferred.errback(error);\n  });\n\n  return deferred;\n};\n\n\n/**\n * Gets the DOM element under which we should add new script elements.\n * How? Take the first head element, and if not found take doc.documentElement,\n * which always exists.\n *\n * @param {!HTMLDocument} doc The relevant document.\n * @return {!Element} The script parent element.\n * @private\n */\ngoog.net.jsloader.getScriptParentElement_ = function(doc) {\n  var headElements = goog.dom.getElementsByTagName(goog.dom.TagName.HEAD, doc);\n  if (!headElements || goog.array.isEmpty(headElements)) {\n    return doc.documentElement;\n  } else {\n    return headElements[0];\n  }\n};\n\n\n/**\n * Cancels a given request.\n * @this {{script_: Element, timeout_: number}} The request context.\n * @private\n */\ngoog.net.jsloader.cancel_ = function() {\n  var request = this;\n  if (request && request.script_) {\n    var scriptNode = request.script_;\n    if (scriptNode && scriptNode.tagName == goog.dom.TagName.SCRIPT) {\n      goog.net.jsloader.cleanup_(scriptNode, true, request.timeout_);\n    }\n  }\n};\n\n\n/**\n * Removes the script node and the timeout.\n * @param {Node} scriptNode The node to be cleaned up.\n * @param {boolean} removeScriptNode If true completely remove the script node.\n * @param {?number=} opt_timeout The timeout handler to cleanup.\n * @private\n * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration\n */\ngoog.net.jsloader.cleanup_ = function(\n    scriptNode, removeScriptNode, opt_timeout) {\n  if (opt_timeout != null) {\n    goog.global.clearTimeout(opt_timeout);\n  }\n\n  scriptNode.onload = goog.nullFunction;\n  scriptNode.onerror = goog.nullFunction;\n  scriptNode.onreadystatechange = goog.nullFunction;\n\n  // Do this after a delay (removing the script node of a running script can\n  // confuse older IEs).\n  if (removeScriptNode) {\n    window.setTimeout(function() { goog.dom.removeNode(scriptNode); }, 0);\n  }\n};\n\n\n/**\n * Possible error codes for jsloader.\n * @enum {number}\n */\ngoog.net.jsloader.ErrorCode = {\n  LOAD_ERROR: 0,\n  TIMEOUT: 1,\n  VERIFY_ERROR: 2,\n  VERIFY_OBJECT_ALREADY_EXISTS: 3,\n};\n\n\n\n/**\n * A jsloader error.\n *\n * @param {goog.net.jsloader.ErrorCode} code The error code.\n * @param {string=} opt_message Additional message.\n * @constructor\n * @extends {goog.debug.Error}\n * @final\n */\ngoog.net.jsloader.Error = function(code, opt_message) {\n  var msg = 'Jsloader error (code #' + code + ')';\n  if (opt_message) {\n    msg += ': ' + opt_message;\n  }\n  goog.net.jsloader.Error.base(this, 'constructor', msg);\n\n  /**\n   * The code for this error.\n   *\n   * @type {goog.net.jsloader.ErrorCode}\n   */\n  this.code = code;\n};\ngoog.inherits(goog.net.jsloader.Error, goog.debug.Error);\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Error codes shared between goog.net.IframeIo and\n * goog.net.XhrIo.\n */\n\ngoog.provide('goog.net.ErrorCode');\n\n\n/**\n * Error codes\n * @enum {number}\n */\ngoog.net.ErrorCode = {\n\n  /**\n   * There is no error condition.\n   */\n  NO_ERROR: 0,\n\n  /**\n   * The most common error from iframeio, unfortunately, is that the browser\n   * responded with an error page that is classed as a different domain. The\n   * situations, are when a browser error page  is shown -- 404, access denied,\n   * DNS failure, connection reset etc.)\n   *\n   */\n  ACCESS_DENIED: 1,\n\n  /**\n   * Currently the only case where file not found will be caused is when the\n   * code is running on the local file system and a non-IE browser makes a\n   * request to a file that doesn't exist.\n   */\n  FILE_NOT_FOUND: 2,\n\n  /**\n   * If Firefox shows a browser error page, such as a connection reset by\n   * server or access denied, then it will fail silently without the error or\n   * load handlers firing.\n   */\n  FF_SILENT_ERROR: 3,\n\n  /**\n   * Custom error provided by the client through the error check hook.\n   */\n  CUSTOM_ERROR: 4,\n\n  /**\n   * Exception was thrown while processing the request.\n   */\n  EXCEPTION: 5,\n\n  /**\n   * The Http response returned a non-successful http status code.\n   */\n  HTTP_ERROR: 6,\n\n  /**\n   * The request was aborted.\n   */\n  ABORT: 7,\n\n  /**\n   * The request timed out.\n   */\n  TIMEOUT: 8,\n\n  /**\n   * The resource is not available offline.\n   */\n  OFFLINE: 9,\n};\n\n\n/**\n * Returns a friendly error message for an error code. These messages are for\n * debugging and are not localized.\n * @param {goog.net.ErrorCode} errorCode An error code.\n * @return {string} A message for debugging.\n */\ngoog.net.ErrorCode.getDebugMessage = function(errorCode) {\n  switch (errorCode) {\n    case goog.net.ErrorCode.NO_ERROR:\n      return 'No Error';\n\n    case goog.net.ErrorCode.ACCESS_DENIED:\n      return 'Access denied to content document';\n\n    case goog.net.ErrorCode.FILE_NOT_FOUND:\n      return 'File not found';\n\n    case goog.net.ErrorCode.FF_SILENT_ERROR:\n      return 'Firefox silently errored';\n\n    case goog.net.ErrorCode.CUSTOM_ERROR:\n      return 'Application custom error';\n\n    case goog.net.ErrorCode.EXCEPTION:\n      return 'An exception occurred';\n\n    case goog.net.ErrorCode.HTTP_ERROR:\n      return 'Http response at 400 or 500 level';\n\n    case goog.net.ErrorCode.ABORT:\n      return 'Request was aborted';\n\n    case goog.net.ErrorCode.TIMEOUT:\n      return 'Request timed out';\n\n    case goog.net.ErrorCode.OFFLINE:\n      return 'The resource is not available offline';\n\n    default:\n      return 'Unrecognized error code';\n  }\n};\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines fireauth.iframeclient.IframeWrapper used to communicate\n * with the hidden iframe to detect Auth events.\n */\n\ngoog.provide('fireauth.iframeclient.IframeWrapper');\n\ngoog.require('fireauth.util');\ngoog.require('goog.Promise');\ngoog.require('goog.html.TrustedResourceUrl');\ngoog.require('goog.net.jsloader');\ngoog.require('goog.string.Const');\n\n\n/**\n * Defines the hidden iframe wrapper for cross origin communications.\n * @param {string} url The hidden iframe src URL.\n * @constructor\n */\nfireauth.iframeclient.IframeWrapper = function(url) {\n  /** @private {string} The hidden iframe URL. */\n  this.url_ = url;\n\n  /**\n   * @type {?gapi.iframes.Iframe}\n   * @private\n   */\n  this.iframe_ = null;\n\n  /** @private {!goog.Promise} A promise that resolves on iframe open. */\n  this.onIframeOpen_ = this.open_();\n};\n\n\n/**\n * @typedef {{\n *   type: string\n * }}\n */\nfireauth.iframeclient.IframeWrapper.Message;\n\n/**\n * Returns URL, src of the hidden iframe.\n * @return {string}\n * @private\n */\nfireauth.iframeclient.IframeWrapper.prototype.getPath_ = function() {\n  return this.url_;\n};\n\n\n/**\n * @return {!goog.Promise} The promise that resolves when the iframe is ready.\n */\nfireauth.iframeclient.IframeWrapper.prototype.onReady = function() {\n  return this.onIframeOpen_;\n};\n\n\n/**\n * Returns options used to open the iframe.\n * @return {!gapi.iframes.OptionsBag}\n * @private\n */\nfireauth.iframeclient.IframeWrapper.prototype.getOptions_ = function() {\n  var options = /** @type {!gapi.iframes.OptionsBag} */ ({\n    'where': document.body,\n    'url': this.getPath_(),\n    'messageHandlersFilter': /** @type {!gapi.iframes.IframesFilter} */ (\n        fireauth.util.getObjectRef(\n            'gapi.iframes.CROSS_ORIGIN_IFRAMES_FILTER')),\n    'attributes': {\n      'style': {\n        'position': 'absolute',\n        'top': '-100px',\n        'width': '1px',\n        'height': '1px'\n      }\n    },\n    'dontclear': true\n  });\n  return options;\n};\n\n\n/**\n * Opens an iframe.\n * @return {!goog.Promise} A promise that resolves on successful iframe open.\n * @private\n */\nfireauth.iframeclient.IframeWrapper.prototype.open_ = function() {\n  var self = this;\n  return fireauth.iframeclient.IframeWrapper.loadGApiJs_().then(function() {\n    return new goog.Promise(function(resolve, reject) {\n      /**\n       * @param {?gapi.iframes.Iframe} iframe The new opened iframe.\n       */\n      var onOpen = function(iframe) {\n        self.iframe_ = iframe;\n        self.iframe_.restyle({\n          // Prevent iframe from closing on mouse out.\n          'setHideOnLeave': false\n        });\n        // Confirm iframe is correctly loaded.\n        // To fallback on failure, set a timeout.\n        var networkErrorTimer = setTimeout(function() {\n          reject(new Error('Network Error'));\n        }, fireauth.iframeclient.IframeWrapper.PING_TIMEOUT_.get());\n        // Clear timer and resolve pending iframe ready promise.\n        var clearTimerAndResolve = function() {\n          clearTimeout(networkErrorTimer);\n          resolve();\n        };\n        // This returns an IThenable. However the reject part does not call\n        // when the iframe is not loaded.\n        iframe.ping(clearTimerAndResolve).then(\n            clearTimerAndResolve,\n            function(error) { reject(new Error('Network Error')); });\n      };\n      /** @type {function():!gapi.iframes.Context} */ (\n          fireauth.util.getObjectRef('gapi.iframes.getContext'))().open(\n              self.getOptions_(), onOpen);\n    });\n  });\n};\n\n\n/**\n * @param {!fireauth.iframeclient.IframeWrapper.Message} message to send.\n * @return {!goog.Promise<?Object>} The promise that resolve when message is\n *     sent.\n */\nfireauth.iframeclient.IframeWrapper.prototype.sendMessage = function(message) {\n  var self = this;\n  return this.onIframeOpen_.then(function() {\n    return new goog.Promise(function(resolve, reject) {\n      self.iframe_.send(\n          message['type'],\n          message,\n          resolve,\n          /** @type {!gapi.iframes.IframesFilter} */ (\n              fireauth.util.getObjectRef(\n                  'gapi.iframes.CROSS_ORIGIN_IFRAMES_FILTER')));\n\n    });\n  });\n};\n\n\n/**\n * Registers a listener to a post message.\n * @param {string} eventName The message to register for.\n * @param {gapi.iframes.MessageHandler} handler Message handler.\n */\nfireauth.iframeclient.IframeWrapper.prototype.registerEvent =\n    function(eventName, handler) {\n  var self = this;\n  this.onIframeOpen_.then(function() {\n    self.iframe_.register(\n        eventName,\n        /** @type {function(this:gapi.iframes.Iframe,\n         *                  *, gapi.iframes.Iframe): *}\n         */ (handler),\n        /** @type {!gapi.iframes.IframesFilter} */ (\n            fireauth.util.getObjectRef(\n                'gapi.iframes.CROSS_ORIGIN_IFRAMES_FILTER')));\n  });\n};\n\n\n/**\n * Unregisters a listener to a post message.\n * @param {string} eventName The message to unregister.\n * @param {gapi.iframes.MessageHandler} handler Message handler.\n */\nfireauth.iframeclient.IframeWrapper.prototype.unregisterEvent =\n    function(eventName, handler) {\n  var self = this;\n  this.onIframeOpen_.then(function() {\n    self.iframe_.unregister(\n        eventName,\n        /** @type {(function(this:gapi.iframes.Iframe,\n         *                   *, gapi.iframes.Iframe): *|undefined)}\n         */ (handler));\n  });\n};\n\n\n/** @private @const {!goog.string.Const} The GApi loader URL. */\nfireauth.iframeclient.IframeWrapper.GAPI_LOADER_SRC_ = goog.string.Const.from(\n    'https://apis.google.com/js/api.js?onload=%{onload}');\n\n\n/**\n * @private @const {!fireauth.util.Delay} The gapi.load network error timeout\n *     delay with units in ms.\n */\nfireauth.iframeclient.IframeWrapper.NETWORK_TIMEOUT_ =\n    new fireauth.util.Delay(30000, 60000);\n\n\n/**\n * @private @const {!fireauth.util.Delay} The iframe ping error timeout delay\n *     with units in ms.\n */\nfireauth.iframeclient.IframeWrapper.PING_TIMEOUT_ =\n    new fireauth.util.Delay(5000, 15000);\n\n\n/** @private {?goog.Promise} The cached GApi loader promise. */\nfireauth.iframeclient.IframeWrapper.cachedGApiLoader_ = null;\n\n\n/** Resets the cached GApi loader. */\nfireauth.iframeclient.IframeWrapper.resetCachedGApiLoader = function() {\n  fireauth.iframeclient.IframeWrapper.cachedGApiLoader_ = null;\n};\n\n\n\n/**\n * Loads the GApi client library if it is not loaded for gapi.iframes usage.\n * @return {!goog.Promise} A promise that resolves when gapi.iframes is loaded.\n * @private\n */\nfireauth.iframeclient.IframeWrapper.loadGApiJs_ = function() {\n  // If already pending or resolved, return the cached promise.\n  if (fireauth.iframeclient.IframeWrapper.cachedGApiLoader_) {\n    return fireauth.iframeclient.IframeWrapper.cachedGApiLoader_;\n  }\n  // If there is no cached promise, initialize a new one.\n  fireauth.iframeclient.IframeWrapper.cachedGApiLoader_ =\n      new goog.Promise(function(resolve, reject) {\n    // Function to run when gapi.load is ready.\n    var onGapiLoad = function() {\n      // The developer may have tried to previously run gapi.load and failed.\n      // Run this to fix that.\n      fireauth.util.resetUnloadedGapiModules();\n      var loader = /** @type {function(string, !Object)} */ (\n          fireauth.util.getObjectRef('gapi.load'));\n      loader('gapi.iframes', {\n        'callback': resolve,\n        'ontimeout': function() {\n          // The above reset may be sufficient, but having this reset after\n          // failure ensures that if the developer calls gapi.load after the\n          // connection is re-established and before another attempt to embed\n          // the iframe, it would work and would not be broken because of our\n          // failed attempt.\n          // Timeout when gapi.iframes.Iframe not loaded.\n          fireauth.util.resetUnloadedGapiModules();\n          reject(new Error('Network Error'));\n        },\n        'timeout': fireauth.iframeclient.IframeWrapper.NETWORK_TIMEOUT_.get()\n      });\n    };\n    if (fireauth.util.getObjectRef('gapi.iframes.Iframe')) {\n      // If gapi.iframes.Iframe available, resolve.\n      resolve();\n    } else if (fireauth.util.getObjectRef('gapi.load')) {\n      // Gapi loader ready, load gapi.iframes.\n      onGapiLoad();\n    } else {\n      // Create a new iframe callback when this is called so as not to overwrite\n      // any previous defined callback. This happens if this method is called\n      // multiple times in parallel and could result in the later callback\n      // overwriting the previous one. This would end up with a iframe\n      // timeout.\n      var cbName = '__iframefcb' +\n          Math.floor(Math.random() * 1000000).toString();\n      // GApi loader not available, dynamically load platform.js.\n      goog.global[cbName] = function() {\n        // GApi loader should be ready.\n        if (fireauth.util.getObjectRef('gapi.load')) {\n          onGapiLoad();\n        } else {\n          // Gapi loader failed, throw error.\n          reject(new Error('Network Error'));\n        }\n      };\n      // Build GApi loader.\n      var url = goog.html.TrustedResourceUrl.format(\n          fireauth.iframeclient.IframeWrapper.GAPI_LOADER_SRC_,\n          {'onload': cbName});\n      // Load GApi loader.\n      var result = goog.Promise.resolve(goog.net.jsloader.safeLoad(url));\n      result.thenCatch(function(error) {\n        // In case library fails to load, typically due to a network error,\n        // reset cached loader to null to force a refresh on a retrial.\n        reject(new Error('Network Error'));\n      });\n    }\n  }).thenCatch(function(error) {\n    // Reset cached promise to allow for retrial.\n    fireauth.iframeclient.IframeWrapper.cachedGApiLoader_ = null;\n    throw error;\n  });\n  return fireauth.iframeclient.IframeWrapper.cachedGApiLoader_;\n};\n","/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines fireauth.iframeclient.IfcHandler used to communicate\n * with the serverless widget.\n */\n\ngoog.provide('fireauth.iframeclient.IfcHandler');\ngoog.provide('fireauth.iframeclient.IframeUrlBuilder');\ngoog.provide('fireauth.iframeclient.OAuthUrlBuilder');\n\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.AuthEvent');\ngoog.require('fireauth.AuthProvider');\ngoog.require('fireauth.InvalidOriginError');\ngoog.require('fireauth.OAuthSignInHandler');\ngoog.require('fireauth.RpcHandler');\ngoog.require('fireauth.authenum.Error');\ngoog.require('fireauth.constants');\ngoog.require('fireauth.iframeclient.IframeWrapper');\ngoog.require('fireauth.util');\ngoog.require('goog.Promise');\ngoog.require('goog.Timer');\ngoog.require('goog.Uri');\ngoog.require('goog.array');\ngoog.require('goog.object');\n\n\n/**\n * The OAuth handler and iframe prototcol.\n * @const {string}\n * @suppress {const|duplicate}\n */\nfireauth.iframeclient.SCHEME = 'https';\n\n\n\n/**\n * The OAuth handler and iframe port number.\n * @const {?number}\n * @suppress {const|duplicate}\n */\nfireauth.iframeclient.PORT_NUMBER = null;\n\n\n\n/**\n * The iframe URL builder used to build the iframe widget URL.\n * @param {string} authDomain The application authDomain.\n * @param {string} apiKey The API key.\n * @param {string} appName The App name.\n * @param {?fireauth.constants.EmulatorSettings=} emulatorConfig The emulator\n *     configuration.\n * @constructor\n */\nfireauth.iframeclient.IframeUrlBuilder = function(authDomain, apiKey, appName, emulatorConfig) {\n  /** @private {string} The application authDomain. */\n  this.authDomain_ = authDomain;\n  /** @private {string} The API key. */\n  this.apiKey_ = apiKey;\n  /** @private {string} The App name. */\n  this.appName_ = appName;\n  /**\n   * @private @const {?fireauth.constants.EmulatorSettings|undefined}\n   * The emulator configuration.\n   */\n  this.emulatorConfig_ = emulatorConfig;\n  /** @private {?string|undefined} The client version. */\n  this.v_ = null;\n  let uri;\n  if (this.emulatorConfig_) {\n    const emulatorUri = goog.Uri.parse(this.emulatorConfig_.url);\n    uri = goog.Uri.create(\n      emulatorUri.getScheme(),\n      null,\n      emulatorUri.getDomain(),\n      emulatorUri.getPort(),\n      '/emulator/auth/iframe',\n      null,\n      null);\n  } else {\n    uri = goog.Uri.create(\n      fireauth.iframeclient.SCHEME,\n      null,\n      this.authDomain_,\n      fireauth.iframeclient.PORT_NUMBER,\n      '/__/auth/iframe',\n      null,\n      null);\n  }\n  /**\n   * @private @const {!goog.Uri} The URI object used to build the iframe URL.\n   */\n  this.uri_ = uri;\n  this.uri_.setParameterValue('apiKey', this.apiKey_);\n  this.uri_.setParameterValue('appName', this.appName_);\n  /** @private {?string|undefined} The endpoint ID. */\n  this.endpointId_ = null;\n  /** @private {!Array<string>} The list of framework IDs. */\n  this.frameworks_ = [];\n};\n\n\n/**\n * Sets the client version.\n * @param {?string|undefined} v The client version.\n * @return {!fireauth.iframeclient.IframeUrlBuilder} The current iframe URL\n *     builder instance.\n */\nfireauth.iframeclient.IframeUrlBuilder.prototype.setVersion = function(v) {\n  this.v_ = v;\n  return this;\n};\n\n\n/**\n * Sets the endpoint ID.\n * @param {?string|undefined} eid The endpoint ID (staging, test Gaia, etc).\n * @return {!fireauth.iframeclient.IframeUrlBuilder} The current iframe URL\n *     builder instance.\n */\nfireauth.iframeclient.IframeUrlBuilder.prototype.setEndpointId = function(eid) {\n  this.endpointId_ = eid;\n  return this;\n};\n\n\n/**\n * Sets the list of frameworks to pass to the iframe.\n * @param {?Array<string>|undefined} frameworks The list of frameworks to log.\n * @return {!fireauth.iframeclient.IframeUrlBuilder} The current iframe URL\n *     builder instance.\n */\nfireauth.iframeclient.IframeUrlBuilder.prototype.setFrameworks =\n    function(frameworks) {\n  this.frameworks_ = goog.array.clone(frameworks || []);\n  return this;\n};\n\n\n/**\n * Modifes the URI with the relevant Auth provider parameters.\n * @return {string} The constructed OAuth URL string.\n * @override\n */\nfireauth.iframeclient.IframeUrlBuilder.prototype.toString = function() {\n  // Pass the client version if available.\n  if (this.v_) {\n    this.uri_.setParameterValue('v', this.v_);\n  } else {\n    this.uri_.removeParameter('v');\n  }\n  // Pass the endpoint ID if available.\n  if (this.endpointId_) {\n    this.uri_.setParameterValue('eid', this.endpointId_);\n  } else {\n    this.uri_.removeParameter('eid');\n  }\n  // Pass the list of frameworks if available.\n  if (this.frameworks_.length) {\n    this.uri_.setParameterValue('fw', this.frameworks_.join(','));\n  } else {\n    this.uri_.removeParameter('fw');\n  }\n  return this.uri_.toString();\n};\n\n\n\n/**\n * The OAuth URL builder used to build the OAuth handler widget URL.\n * @param {string} authDomain The application authDomain.\n * @param {string} apiKey The API key.\n * @param {string} appName The App name.\n * @param {string} authType The Auth operation type.\n * @param {!fireauth.AuthProvider} provider The Auth provider that the OAuth\n *     handler request is built to sign in to.\n * @param {?fireauth.constants.EmulatorSettings=} emulatorConfig The emulator\n *     configuration.\n * @constructor\n */\nfireauth.iframeclient.OAuthUrlBuilder =\n    function(authDomain, apiKey, appName, authType, provider, emulatorConfig) {\n  /** @private {string} The application authDomain. */\n  this.authDomain_ = authDomain;\n  /** @private {string} The API key. */\n  this.apiKey_ = apiKey;\n  /** @private {string} The App name. */\n  this.appName_ = appName;\n  /** @private {string} The Auth operation type. */\n  this.authType_ = authType;\n  /**\n   * @private @const {?fireauth.constants.EmulatorSettings|undefined}\n   * The emulator configuration.\n   */\n  this.emulatorConfig_ = emulatorConfig;\n  /**\n   * @private {?string|undefined} The redirect URL used in redirect operations.\n   */\n  this.redirectUrl_ = null;\n  /** @private {?string|undefined} The event ID. */\n  this.eventId_ = null;\n  /** @private {?string|undefined} The client version. */\n  this.v_ = null;\n  /**\n   * @private {!fireauth.AuthProvider} The Firebase Auth provider that the OAuth\n   *     handler request is built to sign in to.\n   */\n  this.provider_ = provider;\n  /** @private {?string|undefined} The endpoint ID. */\n  this.endpointId_ = null;\n  /** @private {?string|undefined} The tenant ID. */\n  this.tenantId_ = null;\n};\n\n\n/**\n * Sets the redirect URL.\n * @param {?string|undefined} redirectUrl The redirect URL used in redirect\n *     operations.\n * @return {!fireauth.iframeclient.OAuthUrlBuilder} The current OAuth URL\n *     builder instance.\n */\nfireauth.iframeclient.OAuthUrlBuilder.prototype.setRedirectUrl =\n    function(redirectUrl) {\n  this.redirectUrl_ = redirectUrl;\n  return this;\n};\n\n\n/**\n * Sets the event ID.\n * @param {?string|undefined} eventId The event ID.\n * @return {!fireauth.iframeclient.OAuthUrlBuilder} The current OAuth URL\n *     builder instance.\n */\nfireauth.iframeclient.OAuthUrlBuilder.prototype.setEventId = function(eventId) {\n  this.eventId_ = eventId;\n  return this;\n};\n\n\n/**\n * Sets the tenant ID.\n * @param {?string|undefined} tenantId The event ID.\n * @return {!fireauth.iframeclient.OAuthUrlBuilder} The current OAuth URL\n *     builder instance.\n */\nfireauth.iframeclient.OAuthUrlBuilder.prototype.setTenantId =\n    function(tenantId) {\n  this.tenantId_ = tenantId;\n  return this;\n};\n\n\n/**\n * Sets the client version.\n * @param {?string|undefined} v The client version.\n * @return {!fireauth.iframeclient.OAuthUrlBuilder} The current OAuth URL\n *     builder instance.\n */\nfireauth.iframeclient.OAuthUrlBuilder.prototype.setVersion = function(v) {\n  this.v_ = v;\n  return this;\n};\n\n\n/**\n * Sets the endpoint ID.\n * @param {?string|undefined} eid The endpoint ID (staging, test Gaia, etc).\n * @return {!fireauth.iframeclient.OAuthUrlBuilder} The current OAuth URL\n *     builder instance.\n */\nfireauth.iframeclient.OAuthUrlBuilder.prototype.setEndpointId = function(eid) {\n  this.endpointId_ = eid;\n  return this;\n};\n\n\n/**\n * Sets any additional optional parameters. This will overwrite any previously\n * set additional parameters.\n * @param {?Object<string, string>|undefined} additionalParams The optional\n *     additional parameters.\n * @return {!fireauth.iframeclient.OAuthUrlBuilder} The current OAuth URL\n *     builder instance.\n */\nfireauth.iframeclient.OAuthUrlBuilder.prototype.setAdditionalParameters =\n    function(additionalParams) {\n  this.additionalParams_ = goog.object.clone(additionalParams || null);\n  return this;\n};\n\n\n/**\n * Modifies the URI with the relevant Auth provider parameters.\n * @return {string} The constructed OAuth URL string.\n * @override\n */\nfireauth.iframeclient.OAuthUrlBuilder.prototype.toString = function () {\n  var uri;\n  if (this.emulatorConfig_) {\n    const emulatorUri = goog.Uri.parse(this.emulatorConfig_.url);\n    uri = goog.Uri.create(\n      emulatorUri.getScheme(),\n      null,\n      emulatorUri.getDomain(),\n      emulatorUri.getPort(),\n      '/emulator/auth/handler',\n      null,\n      null);\n  } else {\n    uri = goog.Uri.create(\n      fireauth.iframeclient.SCHEME,\n      null,\n      this.authDomain_,\n      fireauth.iframeclient.PORT_NUMBER,\n      '/__/auth/handler',\n      null,\n      null);\n  }\n  uri.setParameterValue('apiKey', this.apiKey_);\n  uri.setParameterValue('appName', this.appName_);\n  uri.setParameterValue('authType', this.authType_);\n\n  // Add custom parameters for OAuth1/OAuth2 providers.\n  if (this.provider_['isOAuthProvider']) {\n    // Set default language if available and no language already set.\n    /** @type {!fireauth.FederatedProvider} */ (this.provider_)\n        .setDefaultLanguage(this.getAuthLanguage_());\n    uri.setParameterValue('providerId', this.provider_['providerId']);\n    var customParameters = /** @type {!fireauth.FederatedProvider} */ (\n        this.provider_).getCustomParameters();\n    if (!goog.object.isEmpty(customParameters)) {\n      uri.setParameterValue(\n          'customParameters',\n          /** @type {string} */ (fireauth.util.stringifyJSON(customParameters))\n          );\n    }\n  }\n\n  // Add scopes for OAuth2 providers.\n  if (typeof this.provider_.getScopes === 'function') {\n    var scopes = this.provider_.getScopes();\n    if (scopes.length) {\n      uri.setParameterValue('scopes', scopes.join(','));\n    }\n  }\n\n  if (this.redirectUrl_) {\n    uri.setParameterValue('redirectUrl', this.redirectUrl_);\n  } else {\n    uri.removeParameter('redirectUrl');\n  }\n  if (this.eventId_) {\n    uri.setParameterValue('eventId', this.eventId_);\n  } else {\n    uri.removeParameter('eventId');\n  }\n  // Pass the client version if available.\n  if (this.v_) {\n    uri.setParameterValue('v', this.v_);\n  } else {\n    uri.removeParameter('v');\n  }\n  if (this.additionalParams_) {\n    for (var key in this.additionalParams_) {\n      if (this.additionalParams_.hasOwnProperty(key) &&\n          // Don't overwrite other existing parameters.\n          !uri.getParameterValue(key)) {\n        uri.setParameterValue(key, this.additionalParams_[key]);\n      }\n    }\n  }\n  // Pass the tenant ID if available.\n  if (this.tenantId_) {\n    uri.setParameterValue('tid', this.tenantId_);\n  } else {\n    uri.removeParameter('tid');\n  }\n  // Pass the endpoint ID if available.\n  if (this.endpointId_) {\n    uri.setParameterValue('eid', this.endpointId_);\n  } else {\n    uri.removeParameter('eid');\n  }\n  // Append any framework IDs to the handler URL to log in handler RPC requests.\n  var frameworks = this.getAuthFrameworks_();\n  if (frameworks.length) {\n    uri.setParameterValue('fw', frameworks.join(','));\n  }\n  return uri.toString();\n};\n\n\n/**\n * Returns the current Auth instance's language code.\n * @return {?string} The corresponding language code.\n * @private\n */\nfireauth.iframeclient.OAuthUrlBuilder.prototype.getAuthLanguage_ = function() {\n  try {\n    // Get the Auth instance for the current App identified by the App name.\n    // This could fail if, for example, the App instance was deleted.\n    return firebase['app'](this.appName_)['auth']().getLanguageCode();\n  } catch (e) {\n    return null;\n  }\n};\n\n\n/**\n * Returns the list of Firebase frameworks used for logging purposes.\n * @return {!Array<string>} The list of corresponding Firebase frameworks.\n * @private\n */\nfireauth.iframeclient.OAuthUrlBuilder.prototype.getAuthFrameworks_ =\n    function() {\n  return fireauth.iframeclient.OAuthUrlBuilder.getAuthFrameworksForApp_(\n      this.appName_);\n};\n\n\n/**\n * Returns the list of Firebase frameworks used for logging purposes\n * corresponding to the Firebase App name provided.\n * @param {string} appName The Firebase App name.\n * @return {!Array<string>} The list of corresponding Firebase frameworks.\n * @private\n */\nfireauth.iframeclient.OAuthUrlBuilder.getAuthFrameworksForApp_ =\n    function(appName) {\n  try {\n    // Get the Auth instance's list of Firebase framework IDs for the current\n    // App identified by the App name.\n    // This could fail if, for example, the App instance was deleted.\n    return firebase['app'](appName)['auth']().getFramework();\n  } catch (e) {\n    return [];\n  }\n};\n\n\n\n/**\n * Initializes the ifcHandler which provides the mechanism to listen to Auth\n * events on the hidden iframe.\n * @param {string} authDomain The firebase authDomain used to determine the\n *     OAuth helper page domain.\n * @param {string} apiKey The API key for sending backend Auth requests.\n * @param {string} appName The App ID for the Auth instance that triggered this\n *     request.\n * @param {?string=} opt_clientVersion The optional client version string.\n * @param {?string=} opt_endpointId The endpoint ID (staging, test Gaia, etc).\n * @param {?fireauth.constants.EmulatorSettings=} emulatorConfig The emulator\n *     configuration.\n * @constructor\n * @implements {fireauth.OAuthSignInHandler}\n */\nfireauth.iframeclient.IfcHandler = function(authDomain, apiKey, appName,\n    opt_clientVersion, opt_endpointId, emulatorConfig) {\n  /** @private {string} The Auth domain. */\n  this.authDomain_ = authDomain;\n  /** @private {string} The API key. */\n  this.apiKey_ = apiKey;\n  /** @private {string} The App name. */\n  this.appName_ = appName;\n  /**\n   * @private @const {?fireauth.constants.EmulatorSettings|undefined}\n   * The emulator configuration.\n   */\n  this.emulatorConfig_ = emulatorConfig;\n  /** @private {?string} The client version. */\n  this.clientVersion_ = opt_clientVersion || null;\n  /** @private {?string} The Auth endpoint ID. */\n  this.endpointId_ = opt_endpointId || null;\n  // Delay RPC handler and iframe URL initialization until needed to ensure\n  // logged frameworks are propagated to the iframe.\n  /** @private {?string} The full client version string. */\n  this.fullClientVersion_ = null;\n  /** @private {?string} The iframe URL. */\n  this.iframeUrl_ = null;\n  /** @private {?fireauth.RpcHandler} The RPC handler for provided API key. */\n  this.rpcHandler_ = null;\n  /**\n   * @private {!Array<!function(?fireauth.AuthEvent)>} The Auth event\n   *     listeners.\n   */\n  this.authEventListeners_ = [];\n  // Delay origin validator determination until needed, so the error is not\n  // thrown in the background. This will also prevent the getProjectConfig RPC\n  // until it is required.\n  /** @private {?goog.Promise} The origin validator. */\n  this.originValidator_ = null;\n  /** @private {?goog.Promise} The initialization promise. */\n  this.isInitialized_ = null;\n};\n\n\n/**\n * Validates the provided URL.\n * @param {!fireauth.RpcHandler} rpcHandler The RPC handler used to validate the\n *     requested origin.\n * @param {string=} opt_origin The optional page origin. If not provided, the\n *     window.location.href value is used.\n * @return {!goog.Promise} The promise that resolves if the provided origin is\n *     valid.\n * @private\n */\nfireauth.iframeclient.IfcHandler.getOriginValidator_ =\n    function(rpcHandler, opt_origin) {\n  var origin = opt_origin || fireauth.util.getCurrentUrl();\n  return rpcHandler.getAuthorizedDomains().then(function(authorizedDomains) {\n    if (!fireauth.util.isAuthorizedDomain(authorizedDomains, origin)) {\n      throw new fireauth.InvalidOriginError(fireauth.util.getCurrentUrl());\n    }\n  });\n};\n\n\n/**\n * Initializes the iframe client wrapper.\n * @return {!goog.Promise} The promise that resolves on initialization.\n */\nfireauth.iframeclient.IfcHandler.prototype.initialize = function() {\n  // Already initialized.\n  if (this.isInitialized_) {\n    return this.isInitialized_;\n  }\n  var self = this;\n  this.isInitialized_ = fireauth.util.onDomReady().then(function() {\n    /**\n     * @private {!fireauth.iframeclient.IframeWrapper} The iframe wrapper\n     *     instance.\n     */\n    self.iframeWrapper_ = new fireauth.iframeclient.IframeWrapper(\n        self.getIframeUrl());\n    // Register all event listeners to Auth event messages sent from Auth\n    // iframe.\n    self.registerEvents_();\n  });\n  return this.isInitialized_;\n};\n\n\n/**\n * Waits for popup window to close. When closed start timeout listener for popup\n * pending promise. If in the process, it was detected that the iframe does not\n * support web storage, the popup is closed and the web storage unsupported\n * error is thrown.\n * @param {!Window} popupWin The popup window.\n * @param {!function(!fireauth.AuthError)} onError The on error callback.\n * @param {number} timeoutDuration The time to wait in ms after the popup is\n *     closed before triggering the popup closed by user error.\n * @return {!goog.Promise}\n * @override\n */\nfireauth.iframeclient.IfcHandler.prototype.startPopupTimeout =\n    function(popupWin, onError, timeoutDuration) {\n  // Expire pending timeout promise for popup operation.\n  var popupClosedByUserError = new fireauth.AuthError(\n      fireauth.authenum.Error.POPUP_CLOSED_BY_USER);\n  // If web storage is disabled in the iframe, expire popup timeout quickly with\n  // this error.\n  var webStorageNotSupportedError = new fireauth.AuthError(\n      fireauth.authenum.Error.WEB_STORAGE_UNSUPPORTED);\n  var self = this;\n  var isResolved = false;\n  // Wait for the iframe to be ready first.\n  return this.initializeAndWait().then(function() {\n    // We do not return isWebStorageSupported() to ensure that this is backward\n    // compatible.\n    // Pushing the following client changes before updating the iframe to\n    // respond to these events would continue to work.\n    // The downside is that the popup could be closed before this resolves.\n    // In that case, they would get an error that the popup was closed and not\n    // the error that web storage is not supported, though that is unlikely\n    // as isWebStorageSupported should execute faster than the popup timeout.\n    // If web storage is not supported in the iframe, fail quickly.\n    self.isWebStorageSupported().then(function(isSupported) {\n      if (!isSupported) {\n        // If not supported, close window.\n        if (popupWin) {\n          fireauth.util.closeWindow(popupWin);\n        }\n        onError(webStorageNotSupportedError);\n        isResolved = true;\n      }\n    });\n  }).thenCatch(function(error) {\n    // Ignore any possible error in iframe embedding.\n    // These types of errors will be handled in processPopup which will close\n    // the popup too if that happens.\n    return;\n  }).then(function() {\n    // Skip if already resolved.\n    if (isResolved) {\n      return;\n    }\n    // After the iframe is ready, wait for popup to close and then start timeout\n    // check.\n    return fireauth.util.onPopupClose(popupWin);\n  }).then(function() {\n    // Skip if already resolved.\n    if (isResolved) {\n      return;\n    }\n    return goog.Timer.promise(timeoutDuration).then(function() {\n      // If this is already resolved or rejected, this will do nothing.\n      onError(popupClosedByUserError);\n    });\n  });\n};\n\n\n/**\n * @return {boolean} Whether the handler should be initialized early.\n * @override\n */\nfireauth.iframeclient.IfcHandler.prototype.shouldBeInitializedEarly =\n    function() {\n  var ua = fireauth.util.getUserAgentString();\n  // Cannot run in the background (can't wait for iframe to be embedded\n  // before triggering popup redirect) and is Safari (can only detect\n  // localStorage in iframe via change event) => embed iframe ASAP.\n  // Do the same for mobile browsers on iOS devices as they use the same\n  // Safari implementation underneath.\n  return !fireauth.util.runsInBackground(ua) &&\n         !fireauth.util.iframeCanSyncWebStorage(ua);\n};\n\n\n/**\n * @return {boolean} Whether the sign-in handler in the current environment\n *     has volatile session storage.\n * @override\n */\nfireauth.iframeclient.IfcHandler.prototype.hasVolatileStorage = function() {\n  // Web environment with web storage enabled has stable sessionStorage.\n  return false;\n};\n\n\n/**\n * Processes the popup request. The popup instance must be provided externally\n * and on error, the requestor must close the window.\n * @param {?Window} popupWin The popup window reference.\n * @param {!fireauth.AuthEvent.Type} mode The Auth event type.\n * @param {!fireauth.AuthProvider} provider The Auth provider to sign in with.\n * @param {function()} onInitialize The function to call on initialization.\n * @param {function(*)} onError The function to call on error.\n * @param {string=} opt_eventId The optional event ID.\n * @param {boolean=} opt_alreadyRedirected Whether popup is already redirected\n *     to final destination.\n * @param {?string=} opt_tenantId The optional tenant ID.\n * @return {!goog.Promise} The popup window promise.\n * @override\n */\nfireauth.iframeclient.IfcHandler.prototype.processPopup = function(\n    popupWin,\n    mode,\n    provider,\n    onInitialize,\n    onError,\n    opt_eventId,\n    opt_alreadyRedirected,\n    opt_tenantId) {\n  // processPopup is failing since it tries to access popup win when tab can\n  // not run in background. For now bypass processPopup which runs\n  // additional origin check not accounted above. Besides, iframe will never\n  // hand result to parent if origin not whitelisted.\n  // Error thrown by browser: Unable to establish a connection with the\n  // popup. It may have been blocked by the browser.\n  // If popup is null, startPopupTimeout will catch it without having the\n  // above error getting triggered due to popup access from opener.\n\n  // Reject immediately if the popup is blocked.\n  if (!popupWin) {\n    return goog.Promise.reject(\n        new fireauth.AuthError(fireauth.authenum.Error.POPUP_BLOCKED));\n  }\n  // Already redirected and cannot run in the background, resolve quickly while\n  // initializing.\n  if (opt_alreadyRedirected && !fireauth.util.runsInBackground()) {\n    // Initialize first before resolving.\n    this.initializeAndWait().thenCatch(function(error) {\n      fireauth.util.closeWindow(popupWin);\n      onError(error);\n    });\n    onInitialize();\n    // Already redirected.\n    return goog.Promise.resolve();\n  }\n  // If origin validator not determined yet.\n  if (!this.originValidator_) {\n    this.originValidator_ =\n        fireauth.iframeclient.IfcHandler.getOriginValidator_(\n            this.getRpcHandler_());\n  }\n  var self = this;\n  return this.originValidator_.then(function() {\n    // After origin validation, wait for iframe to be ready before redirecting.\n    var onReady = self.initializeAndWait().thenCatch(function(error) {\n      fireauth.util.closeWindow(popupWin);\n      onError(error);\n      throw error;\n    });\n    onInitialize();\n    return onReady;\n  }).then(function() {\n    // Popup and redirect operations work for OAuth providers only.\n    fireauth.AuthProvider.checkIfOAuthSupported(provider);\n    // Already redirected to intended destination, no need to redirect again.\n    if (opt_alreadyRedirected) {\n      return;\n    }\n    var oauthHelperWidgetUrl =\n        fireauth.iframeclient.IfcHandler.getOAuthHelperWidgetUrl(\n            self.authDomain_,\n            self.apiKey_,\n            self.appName_,\n            mode,\n            provider,\n            null,\n            opt_eventId,\n            self.clientVersion_,\n            undefined,\n            self.endpointId_,\n            opt_tenantId,\n            self.emulatorConfig_);\n    // Redirect popup to OAuth helper widget URL.\n    fireauth.util.goTo(oauthHelperWidgetUrl, /** @type {!Window} */ (popupWin));\n  }).thenCatch(function(e) {\n    // Force another origin validation.\n    if (e.code == 'auth/network-request-failed') {\n      self.originValidator_ = null;\n    }\n    throw e;\n  });\n};\n\n\n/**\n * @return {!fireauth.RpcHandler} The RPC handler instance with the relevant\n *     endpoints, version and frameworks.\n * @private\n */\nfireauth.iframeclient.IfcHandler.prototype.getRpcHandler_ = function() {\n  if (!this.rpcHandler_) {\n    this.fullClientVersion_ = this.clientVersion_ ?\n        fireauth.util.getClientVersion(\n            fireauth.util.ClientImplementation.JSCORE,\n            this.clientVersion_,\n            fireauth.iframeclient.OAuthUrlBuilder.getAuthFrameworksForApp_(\n                this.appName_)) :\n        null;\n    this.rpcHandler_ = new fireauth.RpcHandler(\n        this.apiKey_,\n        // Get the client Auth endpoint used.\n        fireauth.constants.getEndpointConfig(this.endpointId_),\n        this.fullClientVersion_);\n    if (this.emulatorConfig_) {\n        this.rpcHandler_.updateEmulatorConfig(this.emulatorConfig_);\n    }\n  }\n  return this.rpcHandler_;\n};\n\n\n/**\n * Processes the redirect request.\n * @param {!fireauth.AuthEvent.Type} mode The Auth event type.\n * @param {!fireauth.AuthProvider} provider The Auth provider to sign in with.\n * @param {?string=} opt_eventId The optional event ID.\n * @param {?string=} opt_tenantId The optional tenant ID.\n * @return {!goog.Promise}\n * @override\n */\nfireauth.iframeclient.IfcHandler.prototype.processRedirect =\n    function(mode, provider, opt_eventId, opt_tenantId) {\n  // If origin validator not determined yet.\n  if (!this.originValidator_) {\n    this.originValidator_ =\n        fireauth.iframeclient.IfcHandler.getOriginValidator_(\n            this.getRpcHandler_());\n  }\n  var self = this;\n  // Make sure origin is validated.\n  return this.originValidator_.then(function() {\n    fireauth.AuthProvider.checkIfOAuthSupported(provider);\n    var oauthHelperWidgetUrl =\n        fireauth.iframeclient.IfcHandler.getOAuthHelperWidgetUrl(\n            self.authDomain_,\n            self.apiKey_,\n            self.appName_,\n            mode,\n            provider,\n            fireauth.util.getCurrentUrl(),\n            opt_eventId,\n            self.clientVersion_,\n            undefined,\n            self.endpointId_,\n            opt_tenantId,\n            self.emulatorConfig_);\n    // Redirect to OAuth helper widget URL.\n    fireauth.util.goTo(oauthHelperWidgetUrl);\n  }).thenCatch(function(e) {\n    // Force another origin validation on network errors.\n    if (e.code == 'auth/network-request-failed') {\n      self.originValidator_ = null;\n    }\n    throw e;\n  });\n};\n\n\n/** @return {string} The iframe URL. */\nfireauth.iframeclient.IfcHandler.prototype.getIframeUrl = function() {\n  if (!this.iframeUrl_) {\n    this.iframeUrl_ = fireauth.iframeclient.IfcHandler.getAuthIframeUrl(\n      this.authDomain_,\n      this.apiKey_,\n      this.appName_,\n      this.clientVersion_,\n      this.endpointId_,\n      fireauth.iframeclient.OAuthUrlBuilder.getAuthFrameworksForApp_(\n        this.appName_),\n      this.emulatorConfig_);\n  }\n  return this.iframeUrl_;\n};\n\n\n/**\n * @return {!goog.Promise} The promise that resolves when the iframe is ready.\n * @override\n */\nfireauth.iframeclient.IfcHandler.prototype.initializeAndWait = function() {\n  // Initialize if not initialized yet.\n  var self = this;\n  return this.initialize().then(function() {\n    return self.iframeWrapper_.onReady();\n  }).thenCatch(function(error) {\n    // Reset origin validator.\n    self.originValidator_ = null;\n    // Reject iframe ready promise with network error.\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.NETWORK_REQUEST_FAILED);\n  });\n};\n\n\n/**\n * @return {boolean} Whether the handler will unload the current page on\n *     redirect operations.\n * @override\n */\nfireauth.iframeclient.IfcHandler.prototype.unloadsOnRedirect = function() {\n  return true;\n};\n\n\n/**\n * @param {string} authDomain The Firebase authDomain used to determine the\n *     OAuth helper page domain.\n * @param {string} apiKey The API key for sending backend Auth requests.\n * @param {string} appName The App ID for the Auth instance that triggered this\n *     request.\n * @param {?string=} opt_clientVersion The optional client version string.\n * @param {?string=} opt_endpointId The endpoint ID (staging, test Gaia, etc).\n * @param {?Array<string>=} opt_frameworks The optional list of framework IDs.\n * @param {?fireauth.constants.EmulatorSettings=} emulatorConfig The emulator\n *     configuration.\n * @return {string} The data iframe src URL.\n */\nfireauth.iframeclient.IfcHandler.getAuthIframeUrl = function(authDomain, apiKey,\n    appName, opt_clientVersion, opt_endpointId, opt_frameworks, emulatorConfig) {\n  // OAuth helper iframe URL.\n  var builder = new fireauth.iframeclient.IframeUrlBuilder(\n      authDomain, apiKey, appName, emulatorConfig);\n  return builder\n      .setVersion(opt_clientVersion)\n      .setEndpointId(opt_endpointId)\n      .setFrameworks(opt_frameworks)\n      .toString();\n};\n\n\n/**\n * @param {string} authDomain The Firebase authDomain used to determine the\n *     OAuth helper page domain.\n * @param {string} apiKey The API key for sending backend Auth requests.\n * @param {string} appName The App ID for the Auth instance that triggered this\n *     request.\n * @param {string} authType The type of operation that depends on OAuth sign in.\n * @param {!fireauth.AuthProvider} provider The provider to sign in to.\n * @param {?string=} opt_redirectUrl The optional URL to redirect to on OAuth\n *     sign in completion.\n * @param {?string=} opt_eventId The optional event ID to identify on receipt.\n * @param {?string=} opt_clientVersion The optional client version string.\n * @param {?Object<string, string>=} opt_additionalParams The optional\n *     additional parameters.\n * @param {?string=} opt_endpointId The endpoint ID (staging, test Gaia, etc).\n * @param {?string=} opt_tenantId The optional tenant ID.\n * @param {?fireauth.constants.EmulatorSettings=} emulatorConfig The emulator\n *     configuration.\n * @return {string} The OAuth helper widget URL.\n */\nfireauth.iframeclient.IfcHandler.getOAuthHelperWidgetUrl = function(\n    authDomain,\n    apiKey,\n    appName,\n    authType,\n    provider,\n    opt_redirectUrl,\n    opt_eventId,\n    opt_clientVersion,\n    opt_additionalParams,\n    opt_endpointId,\n    opt_tenantId,\n    emulatorConfig) {\n  // OAuth helper widget URL.\n  var builder = new fireauth.iframeclient.OAuthUrlBuilder(\n      authDomain, apiKey, appName, authType, provider, emulatorConfig);\n  return builder\n      .setRedirectUrl(opt_redirectUrl)\n      .setEventId(opt_eventId)\n      .setVersion(opt_clientVersion)\n      .setAdditionalParameters(opt_additionalParams)\n      .setEndpointId(opt_endpointId)\n      .setTenantId(opt_tenantId)\n      .toString();\n};\n\n\n/**\n * Post message receiver event names.\n * @enum {string}\n */\nfireauth.iframeclient.IfcHandler.ReceiverEvent = {\n  AUTH_EVENT: 'authEvent'\n};\n\n\n/**\n * Post message sender event names.\n * @enum {string}\n */\nfireauth.iframeclient.IfcHandler.SenderEvent = {\n  WEB_STORAGE_SUPPORT_EVENT: 'webStorageSupport'\n};\n\n\n/**\n * Post message response field names.\n * @enum {string}\n */\nfireauth.iframeclient.IfcHandler.Response = {\n  STATUS: 'status',\n  AUTH_EVENT: 'authEvent',\n  WEB_STORAGE_SUPPORT: 'webStorageSupport'\n};\n\n\n/**\n * Post message status values.\n * @enum {string}\n */\nfireauth.iframeclient.IfcHandler.Status = {\n  ACK: 'ACK',\n  ERROR: 'ERROR'\n};\n\n\n/**\n * Registers all event listeners.\n * @private\n */\nfireauth.iframeclient.IfcHandler.prototype.registerEvents_ = function() {\n  // Should be run in initialization.\n  if (!this.iframeWrapper_) {\n    throw new Error('IfcHandler must be initialized!');\n  }\n  var self = this;\n  // Listen to Auth change events emitted from iframe.\n  this.iframeWrapper_.registerEvent(\n      fireauth.iframeclient.IfcHandler.ReceiverEvent.AUTH_EVENT,\n      function(response) {\n        var resolveResponse = {};\n        if (response &&\n            response[fireauth.iframeclient.IfcHandler.Response.AUTH_EVENT]) {\n          var isHandled = false;\n          // Get Auth event (plain object).\n          var authEvent = fireauth.AuthEvent.fromPlainObject(\n              response[fireauth.iframeclient.IfcHandler.Response.AUTH_EVENT]);\n          // Trigger Auth change on all listeners.\n          for (var i = 0; i < self.authEventListeners_.length; i++) {\n            isHandled = self.authEventListeners_[i](authEvent) || isHandled;\n          }\n          // Return ack response to notify sender of success.\n          resolveResponse = {};\n          resolveResponse[fireauth.iframeclient.IfcHandler.Response.STATUS] =\n              isHandled ? fireauth.iframeclient.IfcHandler.Status.ACK :\n                  fireauth.iframeclient.IfcHandler.Status.ERROR;\n          return goog.Promise.resolve(resolveResponse);\n        }\n        // Return error status if the response is invalid.\n        resolveResponse[fireauth.iframeclient.IfcHandler.Response.STATUS] =\n            fireauth.iframeclient.IfcHandler.Status.ERROR;\n        return goog.Promise.resolve(resolveResponse);\n      });\n};\n\n\n/**\n * @return {!goog.Promise<boolean>} Whether web storage is supported in the\n *     iframe.\n */\nfireauth.iframeclient.IfcHandler.prototype.isWebStorageSupported = function() {\n  var webStorageSupportEvent =\n      fireauth.iframeclient.IfcHandler.SenderEvent.WEB_STORAGE_SUPPORT_EVENT;\n  var message = {\n    'type': webStorageSupportEvent\n  };\n  var self = this;\n  // Initialize if not initialized yet.\n  return this.initialize().then(function() {\n    return self.iframeWrapper_.sendMessage(message);\n  }).then(function(response) {\n    // Parse the response and return the passed web storage support status.\n    var key = fireauth.iframeclient.IfcHandler.Response.WEB_STORAGE_SUPPORT;\n    if (response &&\n        response.length &&\n        typeof response[0][key] !== 'undefined') {\n      return response[0][key];\n    }\n    // Internal error.\n    throw new Error;\n  });\n};\n\n\n/**\n * @param {!function(?fireauth.AuthEvent):boolean} listener The Auth event\n *     listener to add.\n * @override\n */\nfireauth.iframeclient.IfcHandler.prototype.addAuthEventListener =\n    function(listener) {\n  this.authEventListeners_.push(listener);\n};\n\n\n/**\n * @param {!function(?fireauth.AuthEvent):boolean} listener The Auth event\n *     listener to remove.\n * @override\n */\nfireauth.iframeclient.IfcHandler.prototype.removeAuthEventListener =\n    function(listener) {\n  goog.array.removeAllIf(this.authEventListeners_, function(ele) {\n    return ele == listener;\n  });\n};\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\ngoog.provide('fireauth.storage.AsyncStorage');\n\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.authenum.Error');\ngoog.require('fireauth.storage.Storage');\ngoog.require('fireauth.util');\ngoog.require('goog.Promise');\n\n\n/**\n * AsyncStorage provides an interface to the React Native AsyncStorage API.\n * @param {!Object=} opt_asyncStorage The AsyncStorage API. If not provided\n *     this method will attempt to fetch an implementation from\n *     firebase.INTERNAL.reactNative.\n * @constructor\n * @implements {fireauth.storage.Storage}\n * @see https://facebook.github.io/react-native/docs/asyncstorage.html\n */\nfireauth.storage.AsyncStorage = function(opt_asyncStorage) {\n  /**\n   * The underlying storage instance for persistent data.\n   * @private\n   */\n  this.storage_ =\n      opt_asyncStorage || (firebase.INTERNAL['reactNative'] &&\n                           firebase.INTERNAL['reactNative']['AsyncStorage']);\n\n  if (!this.storage_) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR,\n        'The React Native compatibility library was not found.');\n  }\n  /** @public {string} The storage type identifier. */\n  this.type = fireauth.storage.Storage.Type.ASYNC_STORAGE;\n};\n\n\n/**\n * Retrieves the value stored at the key.\n * @param {string} key\n * @return {!goog.Promise<*>}\n * @override\n */\nfireauth.storage.AsyncStorage.prototype.get = function(key) {\n  return goog.Promise.resolve(this.storage_['getItem'](key))\n      .then(function(val) {\n        return val && fireauth.util.parseJSON(val);\n      });\n};\n\n\n/**\n * Stores the value at the specified key.\n * @param {string} key\n * @param {*} value\n * @return {!goog.Promise<void>}\n * @override\n */\nfireauth.storage.AsyncStorage.prototype.set = function(key, value) {\n  return goog.Promise.resolve(\n      this.storage_['setItem'](key, fireauth.util.stringifyJSON(value)));\n};\n\n\n/**\n * Removes the value at the specified key.\n * @param {string} key\n * @return {!goog.Promise<void>}\n * @override\n */\nfireauth.storage.AsyncStorage.prototype.remove = function(key) {\n  return goog.Promise.resolve(this.storage_['removeItem'](key));\n};\n\n\n/**\n * Does nothing. AsyncStorage does not support storage events,\n * @param {function(!goog.events.BrowserEvent)} listener The storage event\n *     listener.\n * @override\n */\nfireauth.storage.AsyncStorage.prototype.addStorageListener = function(\n    listener) {};\n\n\n/**\n * Does nothing. AsyncStorage does not support storage events,\n * @param {function(!goog.events.BrowserEvent)} listener The storage event\n *     listener.\n * @override\n */\nfireauth.storage.AsyncStorage.prototype.removeStorageListener = function(\n    listener) {};\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\ngoog.provide('fireauth.storage.Storage');\n\n\n\n/**\n * Defines a generic interface to storage APIs across platforms.\n * @interface\n */\nfireauth.storage.Storage = function() {};\n\n\n/**\n * Retrieves the value stored at the key.\n * @param {string} key\n * @return {!goog.Promise<*>}\n */\nfireauth.storage.Storage.prototype.get = function(key) {};\n\n\n/**\n * Stores the value at the specified key.\n * @param {string} key\n * @param {*} value\n * @return {!goog.Promise<void>}\n */\nfireauth.storage.Storage.prototype.set = function(key, value) {};\n\n\n/**\n * Removes the value at the specified key.\n * @param {string} key\n * @return {!goog.Promise<void>}\n */\nfireauth.storage.Storage.prototype.remove = function(key) {};\n\n\n/**\n * Adds a listener to storage event change.\n * @param {function(!goog.events.BrowserEvent)|function(!Array<string>)}\n *     listener The storage event listener.\n */\nfireauth.storage.Storage.prototype.addStorageListener = function(listener) {};\n\n\n/**\n * Removes a listener to storage event change.\n * @param {function(!goog.events.BrowserEvent)|function(!Array<string>)}\n *     listener The storage event listener.\n */\nfireauth.storage.Storage.prototype.removeStorageListener = function(listener) {\n};\n\n\n/** @type {string} The storage type identifier. */\nfireauth.storage.Storage.prototype.type;\n\n\n/**\n * Enum for the identifier of the type of underlying storage.\n * @enum {string}\n */\nfireauth.storage.Storage.Type = {\n  ASYNC_STORAGE: 'asyncStorage',\n  IN_MEMORY: 'inMemory',\n  INDEXEDDB: 'indexedDB',\n  LOCAL_STORAGE: 'localStorage',\n  MOCK_STORAGE: 'mockStorage',\n  NULL_STORAGE: 'nullStorage',\n  SESSION_STORAGE: 'sessionStorage'\n};\n","/**\n * @license\n * Copyright 2018 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines the MessageChannel based wrapper for receiving\n * messages from other windows or workers.\n */\n\ngoog.provide('fireauth.messagechannel.Receiver');\n\ngoog.require('fireauth.messagechannel.Status');\ngoog.require('goog.Promise');\ngoog.require('goog.array');\ngoog.require('goog.object');\n\n\n/**\n * Initializes a channel to receive specific messages from a specified event\n * target.\n * Note receivers should not be manually instantiated. Instead `getInstance()`\n * should be used instead to get a receiver instance for a specified event\n * target.\n * @param {!EventTarget} eventTarget The event target to listen to.\n * @constructor\n */\nfireauth.messagechannel.Receiver = function(eventTarget) {\n  /**\n   * @const @private {!EventTarget} The messageChannel event target.\n   */\n  this.eventTarget_ = eventTarget;\n  /**\n   * @const @private {!Object.<string,\n   *                    !Array<function(string, *):!goog.Promise<?>|void>>}\n   *     This is the event type to handlers hash map. It is used to hold the\n   *     corresponding handlers for specified events.\n   */\n  this.eventHandlers_ = {};\n  /**\n   * @const @private {function(!Event)} The internal 'message' event handler\n   *     used to reroute the request to corresponding subscribed handlers.\n   */\n  this.messageEventHandler_ = goog.bind(this.handleEvent_, this);\n};\n\n\n/**\n * @param {!EventTarget} eventTarget The event target to check for.\n * @return {boolean} Whether the receiver is listening to the specified event\n *     target.\n */\nfireauth.messagechannel.Receiver.prototype.isListeningTo =\n    function(eventTarget) {\n  return this.eventTarget_ == eventTarget;\n};\n\n\n/**\n * @const @private {!Array<!fireauth.messagechannel.Receiver>} The list of all\n *     created `fireauth.messagechannel.Receiver` instances.\n */\nfireauth.messagechannel.Receiver.receivers_ = [];\n\n\n/**\n * Return a receiver instance for the specified event target. This is needed\n * since one instance can be available per event target. Otherwise receivers\n * could clobber each other.\n * @param {!EventTarget} eventTarget The event target to listen to.\n * @return {!fireauth.messagechannel.Receiver} The receiver instance for the\n *     specified event target.\n */\nfireauth.messagechannel.Receiver.getInstance = function(eventTarget) {\n  // The results are stored in an array since objects can't be keys for other\n  // objects. In addition, setting a unique property on an event target as a\n  // hash map key may not be allowed due to CORS restrictions.\n  var instance;\n  goog.array.forEach(\n      fireauth.messagechannel.Receiver.receivers_,\n      function(receiver) {\n        if (receiver.isListeningTo(eventTarget)) {\n          instance = receiver;\n        }\n      });\n  if (!instance) {\n    instance = new fireauth.messagechannel.Receiver(eventTarget);\n    fireauth.messagechannel.Receiver.receivers_.push(instance);\n  }\n  return instance;\n};\n\n\n/**\n * Handles a PostMessage event based on the following protocol:\n * <ul>\n * <li>When an event is first detected, check there is a subscribed handler.\n *     If not, do nothing as there could be other listeners.</li>\n * <li>If there is a subscribed event, reply with an ACK event to notify the\n *     sender that the event can be handled.</li>\n * <li>Trigger the subscribed handlers.</li>\n * <li>Reply again with the combined results of all subscribed handlers and\n *     return the response back.</li>\n * </ul>\n *\n * @param {!Event} event The PostMessage event to handle.\n * @private\n */\nfireauth.messagechannel.Receiver.prototype.handleEvent_ = function(event) {\n  // Respond to sender first with ack reply. This will let the client\n  // know that the service worker can handle this event.\n  var eventType = event.data['eventType'];\n  var eventId = event.data['eventId'];\n  var handlers = this.eventHandlers_[eventType];\n  if (handlers && handlers.length > 0) {\n    // Event can be handled.\n    event.ports[0].postMessage({\n      'status': fireauth.messagechannel.Status.ACK,\n      'eventId': eventId,\n      'eventType': eventType,\n      'response': null\n    });\n    var promises = [];\n    goog.array.forEach(handlers, function(handler) {\n      // Wrap in promise in case the handler doesn't return a promise.\n      promises.push(goog.Promise.resolve().then(function() {\n        return handler(event.origin, event.data['data']);\n      }));\n    });\n    // allSettled is more flexible as it executes all the promises passed and\n    // returns whether they succeeded or failed.\n    goog.Promise.allSettled(promises)\n        .then(function(result) {\n          // allResponse has the format:\n          // !Array<!{fulfilled: boolean, value: (*|undefined),\n          //          reason: (*|undefined)}>\n          // Respond to sender with ack reply.\n          // De-obfuscate the allSettled result.\n          var allResponses = [];\n          goog.array.forEach(result, function(item) {\n            allResponses.push({\n              'fulfilled': item.fulfilled,\n              'value': item.value,\n              // Error cannot be clone in postMessage.\n              'reason': item.reason ? item.reason.message : undefined\n            });\n          });\n          // Remove undefined fields.\n          goog.array.forEach(allResponses, function(item) {\n            for (var key in item) {\n              if (typeof item[key] === 'undefined') {\n                delete item[key];\n              }\n            }\n          });\n          event.ports[0].postMessage({\n            'status': fireauth.messagechannel.Status.DONE,\n            'eventId': eventId,\n            'eventType': eventType,\n            'response': allResponses\n          });\n        });\n  }\n  // Let unsupported events time out, as there could be external receivers\n  // that can handle them.\n};\n\n\n/**\n * Subscribes to events of the specified type.\n * @param {string} eventType The event type to listen to.\n * @param {function(string, *):!goog.Promise<?>|void} handler The async callback\n *     function to run when the event is triggered.\n */\nfireauth.messagechannel.Receiver.prototype.subscribe =\n    function(eventType, handler) {\n  if (goog.object.isEmpty(this.eventHandlers_)) {\n    this.eventTarget_.addEventListener('message', this.messageEventHandler_);\n  }\n  if (typeof this.eventHandlers_[eventType] === 'undefined') {\n    this.eventHandlers_[eventType] = [];\n  }\n  this.eventHandlers_[eventType].push(handler);\n};\n\n\n/**\n * Unsubscribes the specified handler from the specified event. If no handler\n * is specified, all handlers are unsubscribed.\n * @param {string} eventType The event type to unsubscribe from.\n * @param {?function(string, *):!goog.Promise<?>|void=} opt_handler The\n *     callback function to unsubscribe from the specified event type. If none\n *     is specified, all handlers are unsubscribed.\n */\nfireauth.messagechannel.Receiver.prototype.unsubscribe =\n    function(eventType, opt_handler) {\n  if (typeof this.eventHandlers_[eventType] !== 'undefined' && opt_handler) {\n    goog.array.removeAllIf(this.eventHandlers_[eventType], function(ele) {\n      return ele == opt_handler;\n    });\n    if (this.eventHandlers_[eventType].length == 0) {\n      delete this.eventHandlers_[eventType];\n    }\n  } else if (!opt_handler) {\n    // Unsubscribe all handlers for speficied event.\n    delete this.eventHandlers_[eventType];\n  }\n  if (goog.object.isEmpty(this.eventHandlers_)) {\n    this.eventTarget_.removeEventListener('message', this.messageEventHandler_);\n  }\n};\n","/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines a local storage interface with an indexedDB\n * implementation to be used as a fallback with browsers that do not synchronize\n * local storage changes between different windows of the same origin.\n */\n\ngoog.provide('fireauth.storage.IndexedDB');\n\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.authenum.Error');\ngoog.require('fireauth.messagechannel.Receiver');\ngoog.require('fireauth.messagechannel.Sender');\ngoog.require('fireauth.messagechannel.WorkerClientPostMessager');\ngoog.require('fireauth.storage.Storage');\ngoog.require('fireauth.util');\ngoog.require('goog.Promise');\ngoog.require('goog.Timer');\ngoog.require('goog.array');\n\n\n\n/**\n * Initialize an indexedDB local storage manager used to mimic local storage\n * using an indexedDB underlying implementation including the ability to listen\n * to storage changes by key similar to localstorage storage event.\n * @param {string} dbName The indexedDB database name where all local storage\n *     data is to be stored.\n * @param {string} objectStoreName The indexedDB object store name where all\n *     local storage data is to be stored.\n * @param {string} dataKeyPath The indexedDB object store index name used to key\n *     all local storage data.\n * @param {string} valueKeyPath The indexedDB object store value field for each\n *     entry.\n * @param {number} version The indexedDB database version number.\n * @param {?IDBFactory=} opt_indexedDB The optional IndexedDB factory object.\n * @implements {fireauth.storage.Storage}\n * @constructor\n */\nfireauth.storage.IndexedDB = function(\n    dbName,\n    objectStoreName,\n    dataKeyPath,\n    valueKeyPath,\n    version,\n    opt_indexedDB) {\n  // indexedDB not available, fail hard.\n  if (!fireauth.storage.IndexedDB.isAvailable()) {\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.WEB_STORAGE_UNSUPPORTED);\n  }\n  /**\n   * @const @private {string} The indexedDB database name where all local\n   *     storage data is to be stored.\n   */\n  this.dbName_ = dbName;\n  /**\n   * @const @private {string} The indexedDB object store name where all local\n   *     storage data is to be stored.\n   */\n  this.objectStoreName_ = objectStoreName;\n  /**\n   * @const @private {string} The indexedDB object store index name used to key\n   *     all local storage data.\n   */\n  this.dataKeyPath_ = dataKeyPath;\n  /**\n   * @const @private {string} The indexedDB object store value field for each\n   *     entry.\n   */\n  this.valueKeyPath_ = valueKeyPath;\n  /** @const @private {number} The indexedDB database version number. */\n  this.version_ = version;\n  /** @private {!Object.<string, *>} The local indexedDB map copy. */\n  this.localMap_ = {};\n  /**\n   * @private {!Array<function(!Array<string>)>} Listeners to storage events.\n   */\n  this.storageListeners_ = [];\n  /** @private {number} The indexedDB pending write operations tracker. */\n  this.pendingOpsTracker_ = 0;\n  /** @private {!IDBFactory} The indexedDB factory object. */\n  this.indexedDB_ = /** @type {!IDBFactory} */ (\n      opt_indexedDB || goog.global.indexedDB);\n  /** @public {string} The storage type identifier. */\n  this.type = fireauth.storage.Storage.Type.INDEXEDDB;\n  /**\n   * @private {?goog.Promise<void>} The pending polling promise for syncing\n   *     unprocessed indexedDB external changes.\n   */\n  this.poll_ = null;\n  /**\n   * @private {?number} The poll timer ID for syncing external indexedDB\n   *     changes.\n   */\n  this.pollTimerId_ = null;\n  /**\n   * @private {?fireauth.messagechannel.Receiver} The messageChannel receiver if\n   *     running from a serviceworker.\n   */\n  this.receiver_ = null;\n  /**\n   * @private {?fireauth.messagechannel.Sender} The messageChannel sender to\n   *     send keyChanged messages to the service worker from the client.\n   */\n  this.sender_ = null;\n  /**\n   * @private {boolean} Whether the service worker has a receiver for the\n   *     keyChanged events.\n   */\n  this.serviceWorkerReceiverAvailable_ = false;\n  /** @private {?ServiceWorker} The current active service worker. */\n  this.activeServiceWorker_ = null;\n  var scope = this;\n  if (fireauth.util.getWorkerGlobalScope()) {\n    this.receiver_ = fireauth.messagechannel.Receiver.getInstance(\n        /** @type {!WorkerGlobalScope} */ (\n            fireauth.util.getWorkerGlobalScope()));\n    // Listen to indexedDB changes.\n    this.receiver_.subscribe('keyChanged', function(origin, request) {\n      // Sync data.\n      return scope.sync_().then(function(keys) {\n        // Trigger listeners if unhandled changes are detected.\n        if (keys.length > 0) {\n          goog.array.forEach(\n              scope.storageListeners_,\n              function(listener) {\n                listener(keys);\n              });\n        }\n        // When this is false, it means the change was already\n        // detected and processed before the notification.\n        return {\n          'keyProcessed': goog.array.contains(keys, request['key'])\n        };\n      });\n    });\n    // Used to inform sender that service worker what events it supports.\n    this.receiver_.subscribe('ping', function(origin, request) {\n      return goog.Promise.resolve(['keyChanged']);\n    });\n  } else {\n    // Get active service worker when its available.\n    fireauth.util.getActiveServiceWorker()\n        .then(function(sw) {\n          scope.activeServiceWorker_ = sw;\n          if (sw) {\n            // Initialize the sender.\n            scope.sender_ = new fireauth.messagechannel.Sender(\n                new fireauth.messagechannel.WorkerClientPostMessager(sw));\n            // Ping the service worker to check what events they can handle.\n            // Use long timeout.\n            scope.sender_.send('ping', null, true)\n                .then(function(results) {\n                  // Check if keyChanged is supported.\n                  if (results[0]['fulfilled'] &&\n                      goog.array.contains(results[0]['value'], 'keyChanged')) {\n                    scope.serviceWorkerReceiverAvailable_ = true;\n                  }\n                })\n                .thenCatch(function(error) {\n                  // Ignore error.\n                });\n          }\n        });\n  }\n};\n\n\n\n/**\n * The indexedDB database name where all local storage data is to be stored.\n * @private @const {string}\n */\nfireauth.storage.IndexedDB.DB_NAME_ = 'firebaseLocalStorageDb';\n\n\n/**\n * The indexedDB object store name where all local storage data is to be stored.\n * @private @const {string}\n */\nfireauth.storage.IndexedDB.DATA_OBJECT_STORE_NAME_ = 'firebaseLocalStorage';\n\n\n/**\n * The indexedDB object store index name used to key all local storage data.\n * @private @const {string}\n */\nfireauth.storage.IndexedDB.DATA_KEY_PATH_ = 'fbase_key';\n\n\n/**\n * The indexedDB object store value field for each entry.\n * @private @const {string}\n */\nfireauth.storage.IndexedDB.VALUE_KEY_PATH_ = 'value';\n\n\n/**\n * The indexedDB database version number.\n * @private @const {number}\n */\nfireauth.storage.IndexedDB.VERSION_ = 1;\n\n\n/**\n * The indexedDB polling delay time in milliseconds.\n * @private @const {number}\n */\nfireauth.storage.IndexedDB.POLLING_DELAY_ = 800;\n\n\n/**\n * Maximum number of times to retry a transaction in the event the connection is\n * closed.\n * @private @const {number}\n */\nfireauth.storage.IndexedDB.TRANSACTION_RETRY_COUNT_ = 3;\n\n\n/**\n * The indexedDB polling stop error.\n * @private @const {string}\n */\nfireauth.storage.IndexedDB.STOP_ERROR_ = 'STOP_EVENT';\n\n\n\n/**\n * @return {!fireauth.storage.IndexedDB} The Firebase Auth indexedDB\n *     local storage manager.\n */\nfireauth.storage.IndexedDB.getFireauthManager = function() {\n  if (!fireauth.storage.IndexedDB.managerInstance_) {\n    fireauth.storage.IndexedDB.managerInstance_ =\n        new fireauth.storage.IndexedDB(\n            fireauth.storage.IndexedDB.DB_NAME_,\n            fireauth.storage.IndexedDB.DATA_OBJECT_STORE_NAME_,\n            fireauth.storage.IndexedDB.DATA_KEY_PATH_,\n            fireauth.storage.IndexedDB.VALUE_KEY_PATH_,\n            fireauth.storage.IndexedDB.VERSION_);\n  }\n  return fireauth.storage.IndexedDB.managerInstance_;\n};\n\n\n/**\n * Delete the indexedDB database.\n * @return {!goog.Promise<!IDBDatabase>} A promise that resolves on successful\n *     database deletion.\n * @private\n */\nfireauth.storage.IndexedDB.prototype.deleteDb_ = function() {\n  var self = this;\n  return new goog.Promise(function(resolve, reject) {\n    var request = self.indexedDB_.deleteDatabase(self.dbName_);\n    request.onsuccess = function(event) {\n      resolve();\n    };\n    request.onerror = function(event) {\n      reject(new Error(event.target.error));\n    };\n  });\n};\n\n\n/**\n * Initializes The indexedDB database, creates it if not already created and\n * opens it.\n * @return {!goog.Promise<!IDBDatabase>} A promise for the database object.\n * @private\n */\nfireauth.storage.IndexedDB.prototype.initializeDb_ = function() {\n  var self = this;\n  return new goog.Promise(function(resolve, reject) {\n    var request = self.indexedDB_.open(self.dbName_, self.version_);\n    request.onerror = function(event) {\n      // Suppress this from surfacing to browser console.\n      try {\n        event.preventDefault();\n      } catch (e) {}\n      reject(new Error(event.target.error));\n    };\n    request.onupgradeneeded = function(event) {\n      var db = event.target.result;\n      try {\n        db.createObjectStore(\n            self.objectStoreName_,\n            {\n              'keyPath': self.dataKeyPath_\n            });\n      } catch (e) {\n        reject(e);\n      }\n    };\n    request.onsuccess = function(event) {\n      var db = event.target.result;\n      // Strange bug that occurs in Firefox when multiple tabs are opened at the\n      // same time. The only way to recover seems to be deleting the database\n      // and re-initializing it.\n      // https://github.com/firebase/firebase-js-sdk/issues/634\n      if (!db.objectStoreNames.contains(self.objectStoreName_)) {\n        self.deleteDb_()\n            .then(function() {\n              return self.initializeDb_();\n            })\n            .then(function(newDb) {\n              resolve(newDb);\n            })\n            .thenCatch(function(e) {\n              reject(e);\n            });\n      } else {\n        resolve(db);\n      }\n    };\n  });\n};\n\n\n/**\n * Checks if indexedDB is initialized, if so, the callback is run, otherwise,\n * it waits for the db to initialize and then runs the callback function.\n * @return {!goog.Promise<!IDBDatabase>} A promise for the initialized indexedDB\n *     database.\n * @private\n */\nfireauth.storage.IndexedDB.prototype.initializeDbAndRun_ =\n    function() {\n  if (!this.initPromise_) {\n    this.initPromise_ = this.initializeDb_();\n  }\n  return this.initPromise_;\n};\n\n/**\n* Attempts to run a transaction, in the event of an error will re-initialize\n* the DB connection and retry a fixed number of times.\n* @param {function(!IDBDatabase): !goog.Promise<{T}>} transaction A method\n*     which performs a transactional operation on an IDBDatabase.\n* @template T \n* @return {!goog.Promise<T>}\n* @private\n*/\nfireauth.storage.IndexedDB.prototype.withRetry_ = function(transaction) {\n  let numAttempts = 0;\n  const attempt = (resolve, reject) => {\n    this.initializeDbAndRun_()\n      .then(transaction)\n      .then(resolve)\n      .thenCatch((error) => {\n        if (++numAttempts >\n          fireauth.storage.IndexedDB.TRANSACTION_RETRY_COUNT_) {\n          reject(error);\n          return;\n        }\n        return this.initializeDbAndRun_().then((db) => {\n          db.close();\n          this.initPromise_ = undefined;\n          return attempt(resolve, reject);\n        }).thenCatch((error) => {\n          // Make sure any errors caused by initializeDbAndRun_() or\n          // db.close() are caught as well and trigger a rejection. If at\n          // this point, we are probably in a private browsing context or\n          // environment that does not support indexedDB.\n          reject(error);\n        });\n      });\n  };\n  return new goog.Promise(attempt);\n}\n\n\n/**\n * @return {boolean} Whether indexedDB is available or not.\n */\nfireauth.storage.IndexedDB.isAvailable = function() {\n  try {\n    return !!goog.global['indexedDB'];\n  } catch (e) {\n    return false;\n  }\n};\n\n\n/**\n * Creates a reference for the local storage indexedDB object store and returns\n * it.\n * @param {!IDBTransaction} tx The IDB transaction instance.\n * @return {!IDBObjectStore} The indexedDB object store.\n * @private\n */\nfireauth.storage.IndexedDB.prototype.getDataObjectStore_ =\n    function(tx) {\n  return tx.objectStore(this.objectStoreName_);\n};\n\n\n/**\n * Creates an IDB transaction and returns it.\n * @param {!IDBDatabase} db The indexedDB instance.\n * @param {boolean} isReadWrite Whether the current indexedDB operation is a\n *     read/write operation or not.\n * @return {!IDBTransaction} The requested IDB transaction instance.\n * @private\n */\nfireauth.storage.IndexedDB.prototype.getTransaction_ =\n    function(db, isReadWrite) {\n  var tx = db.transaction(\n      [this.objectStoreName_],\n      isReadWrite ? 'readwrite' : 'readonly');\n  return tx;\n};\n\n\n/**\n * @param {!IDBRequest} request The IDB request instance.\n * @return {!goog.Promise} The promise to resolve on transaction completion.\n * @private\n */\nfireauth.storage.IndexedDB.prototype.onIDBRequest_ =\n    function(request) {\n  return new goog.Promise(function(resolve, reject) {\n    request.onsuccess = function(event) {\n      if (event && event.target) {\n        resolve(event.target.result);\n      } else {\n        resolve();\n      }\n    };\n    request.onerror = function(event) {\n      reject(event.target.error);\n    };\n  });\n};\n\n\n/**\n * Sets the item's identified by the key provided to the value passed. If the\n * item does not exist, it is created. An optional callback is run on success.\n * @param {string} key The storage key for the item to set. If the item exists,\n *     it is updated, otherwise created.\n * @param {*} value The value to store for the item to set.\n * @return {!goog.Promise<void>} A promise that resolves on operation success.\n * @override\n */\nfireauth.storage.IndexedDB.prototype.set = function(key, value) {\n  let isLocked = false;\n  return this\n    .withRetry_((db) => {\n      const objectStore =\n        this.getDataObjectStore_(this.getTransaction_(db, true));\n      return this.onIDBRequest_(objectStore.get(key));\n    })\n    .then((data) => {\n      return this.withRetry_((db) => {\n        const objectStore =\n          this.getDataObjectStore_(this.getTransaction_(db, true));\n        if (data) {\n          // Update the value(s) in the object that you want to change\n          data.value = value;\n          // Put this updated object back into the database.\n          return this.onIDBRequest_(objectStore.put(data));\n        }\n        this.pendingOpsTracker_++;\n        isLocked = true;\n        const obj = {};\n        obj[this.dataKeyPath_] = key;\n        obj[this.valueKeyPath_] = value;\n        return this.onIDBRequest_(objectStore.add(obj));\n      });\n    })\n    .then(() => {\n      // Save in local copy to avoid triggering false external event.\n      this.localMap_[key] = value;\n      // Announce change in key to service worker.\n      return this.notifySW_(key);\n    })\n    .thenAlways(() => {\n      if (isLocked) {\n        this.pendingOpsTracker_--;\n      }\n    });\n};\n\n\n/**\n * Notify the service worker of the indexeDB write operation.\n * Waits until the operation is processed.\n * @param {string} key The key which is changing.\n * @return {!goog.Promise<void>} A promise that resolves on delivery.\n * @private\n */\nfireauth.storage.IndexedDB.prototype.notifySW_ = function(key) {\n  // If sender is available.\n  // Run some sanity check to confirm no sw change occurred.\n  // For now, we support one service worker per page.\n  if (this.sender_ &&\n      this.activeServiceWorker_ &&\n      fireauth.util.getServiceWorkerController() ===\n      this.activeServiceWorker_) {\n    return this.sender_.send(\n        'keyChanged',\n        {'key': key},\n        // Use long timeout if receiver is known to be available.\n        this.serviceWorkerReceiverAvailable_)\n        .then(function(responses) {\n          // Return nothing.\n        })\n        .thenCatch(function(error) {\n          // This is a best effort approach. Ignore errors.\n        });\n  }\n  return goog.Promise.resolve();\n};\n\n\n/**\n * Retrieves a stored item identified by the key provided asynchronously.\n * The value is passed to the callback function provided.\n * @param {string} key The storage key for the item to fetch.\n * @return {!goog.Promise} A promise that resolves with the item's value, or\n *     null if the item is not found.\n * @override\n */\nfireauth.storage.IndexedDB.prototype.get = function(key) {\n  return this\n    .withRetry_((db) => {\n      return this.onIDBRequest_(\n          this.getDataObjectStore_(this.getTransaction_(db, false)).get(key));\n    })\n    .then((response) => {\n      return response && response.value;\n    }); \n};\n\n\n/**\n * Deletes the item identified by the key provided and on success, runs the\n * optional callback.\n * @param {string} key The storage key for the item to remove.\n * @return {!goog.Promise<void>} A promise that resolves on operation success.\n * @override\n */\nfireauth.storage.IndexedDB.prototype.remove = function(key) {\n  let isLocked = false;\n  return this\n    .withRetry_((db) => {\n        isLocked = true;\n        this.pendingOpsTracker_++;\n        return this.onIDBRequest_(\n            this.getDataObjectStore_(\n                this.getTransaction_(db, true))['delete'](key));\n      }).then(() => {\n        // Delete from local copy to avoid triggering false external event.\n        delete this.localMap_[key];\n        // Announce change in key to service worker.\n        return this.notifySW_(key);\n      }).thenAlways(() => {\n        if (isLocked) {\n          this.pendingOpsTracker_--;\n        }\n      });\n};\n\n\n/**\n * @return {!goog.Promise<!Array<string>>} A promise that resolved with all the\n *     storage keys that have changed.\n * @private\n */\nfireauth.storage.IndexedDB.prototype.sync_ = function() {\n  var self = this;\n  return this.initializeDbAndRun_()\n      .then(function(db) {\n        var objectStore =\n            self.getDataObjectStore_(self.getTransaction_(db, false));\n        if (objectStore['getAll']) {\n          // Get all keys and value pairs using getAll if supported.\n          return self.onIDBRequest_(objectStore['getAll']());\n        } else {\n          // If getAll isn't supported, fallback to cursor.\n          return new goog.Promise(function(resolve, reject) {\n            var res = [];\n            var request = objectStore.openCursor();\n            request.onsuccess = function(event) {\n              var cursor = event.target.result;\n              if (cursor) {\n                res.push(cursor.value);\n                cursor['continue']();\n              } else {\n                resolve(res);\n              }\n            };\n            request.onerror = function(event) {\n              reject(event.target.error);\n            };\n          });\n        }\n      }).then(function(res) {\n        var centralCopy = {};\n        // List of keys differing from central copy.\n        var diffKeys = [];\n        // Build central copy (external copy).\n        if (self.pendingOpsTracker_ == 0) {\n          for (var i = 0; i < res.length; i++) {\n            centralCopy[res[i][self.dataKeyPath_]] =\n                res[i][self.valueKeyPath_];\n          }\n          // Get diff of central copy and local copy.\n          diffKeys = fireauth.util.getKeyDiff(self.localMap_, centralCopy);\n          // Update local copy.\n          self.localMap_ = centralCopy;\n        }\n        // Return modified keys.\n        return diffKeys;\n      });\n};\n\n\n/**\n * Adds a listener to storage event change.\n * @param {function(!Array<string>)} listener The storage event listener.\n * @override\n */\nfireauth.storage.IndexedDB.prototype.addStorageListener =\n    function(listener) {\n  // First listener, start listeners.\n  if (this.storageListeners_.length == 0) {\n    this.startListeners_();\n  }\n  this.storageListeners_.push(listener);\n};\n\n\n/**\n * Removes a listener to storage event change.\n * @param {function(!Array<string>)} listener The storage event listener.\n * @override\n */\nfireauth.storage.IndexedDB.prototype.removeStorageListener =\n    function(listener) {\n  goog.array.removeAllIf(\n      this.storageListeners_,\n      function(ele) {\n        return ele == listener;\n      });\n  // No more listeners, stop.\n  if (this.storageListeners_.length == 0) {\n    this.stopListeners_();\n  }\n};\n\n\n/**\n * Removes all listeners to storage event change.\n */\nfireauth.storage.IndexedDB.prototype.removeAllStorageListeners =\n    function() {\n  this.storageListeners_ = [];\n  // No more listeners, stop.\n  this.stopListeners_();\n};\n\n\n/**\n * Starts the listener to storage events.\n * @private\n */\nfireauth.storage.IndexedDB.prototype.startListeners_ = function() {\n  var self = this;\n  // Stop any previous listeners.\n  this.stopListeners_();\n  var repeat = function() {\n    self.pollTimerId_ = setTimeout(\n        function() {\n          self.poll_ = self.sync_()\n              .then(function(keys) {\n                // If keys modified, call listeners.\n                if (keys.length > 0) {\n                  goog.array.forEach(\n                      self.storageListeners_,\n                      function(listener) {\n                        listener(keys);\n                      });\n                }\n              })\n              .then(function() {\n                repeat();\n              })\n              .thenCatch(function(error) {\n                if (error.message != fireauth.storage.IndexedDB.STOP_ERROR_) {\n                  repeat();\n                }\n              });\n        },\n        fireauth.storage.IndexedDB.POLLING_DELAY_);\n  };\n  repeat();\n};\n\n\n/**\n * Stops the listener to storage events.\n * @private\n */\nfireauth.storage.IndexedDB.prototype.stopListeners_ = function() {\n  if (this.poll_) {\n    // Cancel polling function.\n    this.poll_.cancel(fireauth.storage.IndexedDB.STOP_ERROR_);\n  }\n  // Clear any pending polling timer.\n  if (this.pollTimerId_) {\n    clearTimeout(this.pollTimerId_);\n    this.pollTimerId_ = null;\n  }\n};\n","/**\n * @license\n * Copyright 2018 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines the PostMessager interface needed for the\n * `fireauth.messagechannel.Sender`, in addition to 2 types of implementations.\n */\n\ngoog.provide('fireauth.messagechannel.PostMessager');\ngoog.provide('fireauth.messagechannel.WindowPostMessager');\ngoog.provide('fireauth.messagechannel.WorkerClientPostMessager');\n\n\n/**\n * This is the interface defining the postMessage format of a window which\n * takes an additional second parameter for target origin.\n *\n * @typedef {{\n *   postMessage: function(*, string, !Array<!Transferable>)\n * }}\n */\nfireauth.messagechannel.Window;\n\n\n/**\n * This is the interface defining the postMessage format of a worker or\n * ServiceWorkerClient, etc. which just takes a message and a list of\n * Transferables.\n *\n * @typedef {{\n *   postMessage: function(*, !Array<!Transferable>)\n * }}\n */\nfireauth.messagechannel.WorkerClient;\n\n\n/**\n * Defines a common interface to postMessage data to a specified PostMessager.\n * @interface\n */\nfireauth.messagechannel.PostMessager = function() {};\n\n\n/**\n * Sends a message to the specified context.\n * @param {*} message The message to send.\n * @param {!Array<!Transferable>} transfer The list of `Transferable` objects\n *     that are transferred with the message. The ownsership fo these objects is\n *     given to the destination side and they are no longer usable on the\n *     sending side.\n */\nfireauth.messagechannel.PostMessager.prototype.postMessage =\n    function(message, transfer) {};\n\n\n\n/**\n * Defines the implementation for postMessaging to a window context.\n * @param {!fireauth.messagechannel.Window} win The window PostMessager.\n * @param {?string=} opt_targetOrigin The target origin.\n * @constructor\n * @implements {fireauth.messagechannel.PostMessager}\n */\nfireauth.messagechannel.WindowPostMessager = function(win, opt_targetOrigin) {\n  /**\n   * @const @private {!fireauth.messagechannel.Window} The window PostMessager.\n   */\n  this.win_ = win;\n  /** @const @private {string} The postMessage target origin. */\n  this.targetOrigin_ = opt_targetOrigin || '*';\n};\n\n\n/**\n * Sends a message to the specified window context.\n * @param {*} message The message to send.\n * @param {!Array<!Transferable>} transfer The list of `Transferable` objects\n *     that are transferred with the message. The ownsership fo these objects is\n *     given to the destination side and they are no longer usable on the\n *     sending side.\n * @override\n */\nfireauth.messagechannel.WindowPostMessager.prototype.postMessage =\n    function(message, transfer) {\n  this.win_.postMessage(message, this.targetOrigin_, transfer);\n};\n\n\n/**\n * Defines the implementation for postMessaging to a worker/client context.\n * @param {!fireauth.messagechannel.WorkerClient} worker The worker/client\n *     PostMessager.\n * @constructor\n * @implements {fireauth.messagechannel.PostMessager}\n */\nfireauth.messagechannel.WorkerClientPostMessager = function(worker) {\n  /**\n   * @const @private {!fireauth.messagechannel.WorkerClient} The worker/client\n   *     PostMessager.\n   */\n  this.worker_ = worker;\n};\n\n\n/**\n * Sends a message to the specified worker/client context.\n * @param {*} message The message to send.\n * @param {!Array<!Transferable>} transfer The list of `Transferable` objects\n *     that are transferred with the message. The ownsership fo these objects is\n *     given to the destination side and they are no longer usable on the\n *     sending side.\n * @override\n */\nfireauth.messagechannel.WorkerClientPostMessager.prototype.postMessage =\n    function(message, transfer) {\n  this.worker_.postMessage(message, transfer);\n};\n","/**\n * @license\n * Copyright 2018 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines the MessageChannel based wrapper for sending messages\n * to other windows or workers.\n */\n\ngoog.provide('fireauth.messagechannel.Sender');\n\ngoog.require('fireauth.messagechannel.Error');\ngoog.require('fireauth.messagechannel.PostMessager');\ngoog.require('fireauth.messagechannel.Status');\ngoog.require('fireauth.messagechannel.TimeoutDuration');\ngoog.require('fireauth.messagechannel.utils');\ngoog.require('goog.Promise');\ngoog.require('goog.array');\n\n\n/**\n * This is the interface defining a MessageChannel/handler pair.\n *\n * @typedef {{\n *   onMessage: function(!Event),\n *   messageChannel: !MessageChannel\n * }}\n */\nfireauth.messagechannel.MessageHandler;\n\n\n/**\n * Helper static function to create messageChannel errors.\n * @param {!fireauth.messagechannel.Error} errorId The error identifier.\n * @param {?string=} opt_message The optional error message used for generic\n *     error types.\n * @return {!Error} The constructed error to return.\n * @private\n */\nfireauth.messagechannel.createError_ = function(errorId, opt_message) {\n  if (errorId != fireauth.messagechannel.Error.UNKNOWN || !opt_message) {\n    return new Error(errorId);\n  } else {\n    return new Error(opt_message);\n  }\n};\n\n\n/**\n * Initializes a channel to send specific messages to a specified PostMessage.\n * @param {!fireauth.messagechannel.PostMessager} postMessager The post messager\n *     to send messages to.\n * @constructor\n */\nfireauth.messagechannel.Sender = function(postMessager) {\n  /**\n   * @const @private {!fireauth.messagechannel.PostMessager} The messageChannel\n   *     PostMessager.\n   */\n  this.postMessager_ = postMessager;\n  /** @private {boolean} Whether the connection was closed. */\n  this.closed_ = false;\n  /**\n   * @const @private {!Array<!fireauth.messagechannel.MessageHandler>} The list\n   *     of subscribed message handlers and their corresponding MessageChannels.\n   */\n  this.messageHandlers_ = [];\n};\n\n\n/**\n * Sends a message to the receiver. The message is identified by an event\n * type and can carry additional payload data.\n * The sender protocol works as follows:\n * <ul>\n * <li>The request is constructed and postMessaged to the receiver with the port\n *     used to reply back to sender.</li>\n * <li>The operation will block until an ACK response is received. If not, it\n *     will timeout and reject with an error.</li>\n * <li>If an ACK response is received, it will wait longer for the full\n *     processed response.</li>\n * <li>Once the response is received, the operation will resolve with that\n *     result.</li>\n * </ul>\n *\n * @param {string} eventType The event type identifying the message. This is\n *     used to help the receiver handle this message.\n * @param {?Object=} opt_data The optional data to send along the message.\n * @param {?boolean=} opt_useLongTimeout Whether long timeout should be used\n *     for ACK responses.\n * @return {!goog.Promise<!Array<{fulfilled: boolean,\n *                                value: (*|undefined),\n *                                reason: (*|undefined)}>>} A promise that\n *     resolves with the receiver responses.\n */\nfireauth.messagechannel.Sender.prototype.send = function(\n    eventType, opt_data, opt_useLongTimeout) {\n  var self = this;\n  var eventId;\n  var data = opt_data || {};\n  var onMessage;\n  var ackTimer;\n  var completionTimer;\n  var entry = null;\n  if (this.closed_) {\n    return goog.Promise.reject(fireauth.messagechannel.createError_(\n        fireauth.messagechannel.Error.CONNECTION_UNAVAILABLE));\n  }\n  var ackTimeout =\n      !!opt_useLongTimeout ?\n      fireauth.messagechannel.TimeoutDuration.LONG_ACK :\n      fireauth.messagechannel.TimeoutDuration.ACK;\n  var messageChannel =\n      fireauth.messagechannel.utils.initializeMessageChannel();\n  return new goog.Promise(function(resolve, reject) {\n    // Send message along with port for reply\n    if (messageChannel) {\n      eventId = fireauth.messagechannel.utils.generateEventId();\n      // Start the connection if not already started.\n      messageChannel.port1.start();\n      // Handler for receiving message reply from receiver.\n      // Blocks promise resolution until service worker detects the change.\n      ackTimer = setTimeout(function() {\n        // The receiver may not be able to handle the response for various\n        // reasons: library not included, or an incompatible version of\n        // the library is included.\n        // Timeout after some time.\n        reject(fireauth.messagechannel.createError_(\n            fireauth.messagechannel.Error.UNSUPPORTED_EVENT));\n      }, ackTimeout);\n      onMessage = function(event) {\n        // Process only the expected events that match current event ID.\n        if (event.data['eventId'] !== eventId) {\n          return;\n        }\n        // This avoids adding a long wait when the receiver is unable to handle\n        // the event.\n        if (event.data['status'] === fireauth.messagechannel.Status.ACK) {\n          clearTimeout(ackTimer);\n          // Set longer timeout to allow receiver to process.\n          completionTimer = setTimeout(function() {\n            reject(fireauth.messagechannel.createError_(\n                fireauth.messagechannel.Error.TIMEOUT));\n          }, fireauth.messagechannel.TimeoutDuration.COMPLETION);\n          return;\n        } else if (event.data['status'] ===\n                   fireauth.messagechannel.Status.DONE) {\n          clearTimeout(completionTimer);\n          if (typeof event.data['response'] !== 'undefined') {\n            resolve(event.data['response']);\n          } else {\n            reject(fireauth.messagechannel.createError_(\n                fireauth.messagechannel.Error.UNKNOWN));\n          }\n        } else {\n          clearTimeout(ackTimer);\n          clearTimeout(completionTimer);\n          reject(fireauth.messagechannel.createError_(\n              fireauth.messagechannel.Error.INVALID_RESPONSE));\n        }\n      };\n      entry = {\n        'messageChannel': messageChannel,\n        'onMessage': onMessage\n      };\n      self.messageHandlers_.push(entry);\n      messageChannel.port1.addEventListener('message', onMessage);\n      var request = {\n        'eventType': eventType,\n        'eventId': eventId,\n        'data': data\n      };\n      // It is possible the receiver cannot handle this result.\n      // For example, the developer may not be including the library in the\n      // receiver or using an outdated version.\n      self.postMessager_.postMessage(\n          request,\n          [messageChannel.port2]);\n    } else {\n      // No connection available.\n      reject(fireauth.messagechannel.createError_(\n          fireauth.messagechannel.Error.CONNECTION_UNAVAILABLE));\n    }\n  }).then(function(result) {\n    // On completion, remove the message handler. A new one is needed for a\n    // new message.\n    self.removeMessageHandler_(entry);\n    return result;\n  }).thenCatch(function(error) {\n    // On failure, remove the message handler. A new one is needed for a new\n    // message.\n    self.removeMessageHandler_(entry);\n    throw error;\n  });\n};\n\n\n/**\n * Removes the onMessage handler for the specified messageChannel.\n * @param {?fireauth.messagechannel.MessageHandler} messageHandler\n * @private\n */\nfireauth.messagechannel.Sender.prototype.removeMessageHandler_ =\n    function(messageHandler) {\n  if (!messageHandler) {\n    return;\n  }\n  var messageChannel = messageHandler['messageChannel'];\n  var onMessage = messageHandler['onMessage'];\n  if (messageChannel) {\n    messageChannel.port1.removeEventListener('message', onMessage);\n    messageChannel.port1.close();\n  }\n  goog.array.removeAllIf(this.messageHandlers_, function(ele) {\n    return ele == messageHandler;\n  });\n};\n\n\n/** Closes the underlying MessageChannel connection. */\nfireauth.messagechannel.Sender.prototype.close = function() {\n  // Any pending event will timeout.\n  while (this.messageHandlers_.length > 0) {\n    this.removeMessageHandler_(this.messageHandlers_[0]);\n  }\n  this.closed_ = true;\n};\n\n","/**\n * @license\n * Copyright 2018 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines the MessageChannel common utilities and enums.\n */\n\ngoog.provide('fireauth.messagechannel.Error');\ngoog.provide('fireauth.messagechannel.Status');\ngoog.provide('fireauth.messagechannel.TimeoutDuration');\ngoog.provide('fireauth.messagechannel.utils');\n\n\n/**\n * Enum for the messagechannel error messages. These errors are not meant to be\n * user facing.\n * @enum {string}\n */\nfireauth.messagechannel.Error = {\n  CONNECTION_CLOSED: 'connection_closed',\n  CONNECTION_UNAVAILABLE: 'connection_unavailable',\n  INVALID_RESPONSE: 'invalid_response',\n  TIMEOUT: 'timeout',\n  UNKNOWN: 'unknown_error',\n  UNSUPPORTED_EVENT: 'unsupported_event'\n};\n\n\n/**\n * Enum for the message channel request status labels.\n * @enum {string}\n */\nfireauth.messagechannel.Status = {\n  ACK: 'ack',\n  DONE: 'done'\n};\n\n\n/**\n * Enum for the timeout durations in milliseconds for different contexts.\n * @enum {number}\n */\nfireauth.messagechannel.TimeoutDuration = {\n  ACK: 50,\n  COMPLETION: 3000,\n  // Used when a handler is confirmed to be available on the other side.\n  LONG_ACK: 800\n};\n\n\n/**\n * @param {?string=} opt_prefix An optional prefix string to prepend to ID.\n * @param {?number=} opt_digits An optional number of digits used for event ID.\n * @return {string} The generated event ID used to identify a generic event.\n */\nfireauth.messagechannel.utils.generateEventId =\n    function(opt_prefix, opt_digits) {\n  // 0, null and undefined will default to 20.\n  var digits = opt_digits || 20;\n  return opt_prefix ? opt_prefix : '' +\n      Math.floor(Math.random() * Math.pow(10, digits)).toString();\n};\n\n\n/**\n * @return {?MessageChannel} The initialized MessageChannel instance if\n *     supported.\n */\nfireauth.messagechannel.utils.initializeMessageChannel = function() {\n  return typeof MessageChannel !== 'undefined' ? new MessageChannel() : null;\n};\n","/**\n * @license\n * Copyright 2018 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\ngoog.provide('fireauth.storage.HybridIndexedDB');\n\ngoog.require('fireauth.storage.IndexedDB');\ngoog.require('fireauth.storage.Storage');\ngoog.require('fireauth.util');\ngoog.require('goog.Promise');\ngoog.require('goog.array');\n\n/**\n * HybridStorage provides an interface to indexedDB, the persistent Web\n * Storage API for browsers that support it. This will fallback to the provided\n * fallback storage when indexedDB is not supported which is determined\n * asynchronously.\n * @param {!fireauth.storage.Storage} fallbackStorage The storage to fallback to\n *     when indexedDB is not available.\n * @constructor\n * @implements {fireauth.storage.Storage}\n */\nfireauth.storage.HybridIndexedDB = function(fallbackStorage) {\n  var self = this;\n  var storage = null;\n  /**\n   * @const @private {!Array<function((!goog.events.BrowserEvent|\n   *                                   !Array<string>))>} The storage listeners.\n   */\n  this.storageListeners_ = [];\n  // This type may change if the fallback is used.\n  /** @public {string} The storage type identifier. */\n  this.type = fireauth.storage.Storage.Type.INDEXEDDB;\n  /**\n   * @const @private {!fireauth.storage.Storage} The fallback storage when\n   *     indexedDB is unavailable.\n   */\n  this.fallbackStorage_ = fallbackStorage;\n  /**\n   * @const @private {!goog.Promise<!fireauth.storage.Storage>} A promise that\n   *     resolves with the underlying indexedDB storage or a fallback when not\n   *     supported.\n   */\n  this.underlyingStoragePromise_ = goog.Promise.resolve().then(function() {\n    // Initial check shows indexedDB is available. This is not enough.\n    // Try to write/read from indexedDB. If it fails, switch to fallback.\n    if (fireauth.storage.IndexedDB.isAvailable()) {\n      // Test write/read using a random key. This is important for the following\n      // reasons:\n      // 1. Double inclusion of the firebase-auth.js library.\n      // 2. Multiple windows opened at the same time.\n      // The above may cause collision if multiple instances try to\n      // write/read/delete from the same entry.\n      var randomId = fireauth.util.generateEventId();\n      var randomKey = fireauth.storage.HybridIndexedDB.KEY_ + randomId;\n      storage = fireauth.storage.IndexedDB.getFireauthManager();\n      return storage.set(randomKey, randomId)\n          .then(function() {\n            return storage.get(randomKey);\n          })\n          .then(function(value) {\n            if (value !== randomId) {\n              throw new Error('indexedDB not supported!');\n            }\n            return storage.remove(randomKey);\n          })\n          .then(function() {\n            return storage;\n          })\n          .thenCatch(function(error) {\n            return self.fallbackStorage_;\n          });\n    } else {\n      // indexedDB not available, use fallback.\n      return self.fallbackStorage_;\n    }\n  }).then(function(storage) {\n    // Update type.\n    self.type = storage.type;\n    // Listen to all storage changes.\n    storage.addStorageListener(function(key) {\n      // Trigger all attached storage listeners.\n      goog.array.forEach(self.storageListeners_, function(listener) {\n        listener(key);\n      });\n    });\n    return storage;\n  });\n};\n\n\n/**\n * The key used to check if the storage instance is available.\n * @private {string}\n * @const\n */\nfireauth.storage.HybridIndexedDB.KEY_ = '__sak';\n\n\n/**\n * Retrieves the value stored at the key.\n * @param {string} key\n * @return {!goog.Promise<*>}\n * @override\n */\nfireauth.storage.HybridIndexedDB.prototype.get = function(key) {\n  return this.underlyingStoragePromise_.then(function(storage) {\n    return storage.get(key);\n  });\n};\n\n\n/**\n * Stores the value at the specified key.\n * @param {string} key\n * @param {*} value\n * @return {!goog.Promise<void>}\n * @override\n */\nfireauth.storage.HybridIndexedDB.prototype.set = function(key, value) {\n  return this.underlyingStoragePromise_.then(function(storage) {\n    return storage.set(key, value);\n  });\n};\n\n\n/**\n * Removes the value at the specified key.\n * @param {string} key\n * @return {!goog.Promise<void>}\n * @override\n */\nfireauth.storage.HybridIndexedDB.prototype.remove = function(key) {\n  return this.underlyingStoragePromise_.then(function(storage) {\n    return storage.remove(key);\n  });\n};\n\n\n/**\n * Adds a listener to storage event change.\n * @param {function((!goog.events.BrowserEvent|!Array<string>))} listener The\n *     storage event listener.\n * @override\n */\nfireauth.storage.HybridIndexedDB.prototype.addStorageListener =\n    function(listener) {\n  this.storageListeners_.push(listener);\n};\n\n\n/**\n * Removes a listener to storage event change.\n * @param {function(!goog.events.BrowserEvent)} listener The storage event\n *     listener.\n * @override\n */\nfireauth.storage.HybridIndexedDB.prototype.removeStorageListener =\n    function(listener) {\n  goog.array.removeAllIf(this.storageListeners_, function(ele) {\n    return ele == listener;\n  });\n};\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\ngoog.provide('fireauth.storage.InMemoryStorage');\n\ngoog.require('goog.Promise');\n\n\n\n/**\n * InMemoryStorage provides an implementation of Storage that will only persist\n * data in memory. This data is volatile and in a browser environment, will\n * be lost on page unload and will only be available in the current window.\n * This is a useful fallback for browsers where web storage is disabled or\n * environments where the preferred storage mechanism is not available or not\n * supported.\n * @constructor\n * @implements {fireauth.storage.Storage}\n */\nfireauth.storage.InMemoryStorage = function() {\n  /** @protected {!Object} The object where we store values. */\n  this.storage = {};\n  /** @public {string} The storage type identifier. */\n  this.type = fireauth.storage.Storage.Type.IN_MEMORY;\n};\n\n\n/**\n * @param {string} key\n * @return {!goog.Promise<*>}\n * @override\n */\nfireauth.storage.InMemoryStorage.prototype.get = function(key) {\n  return goog.Promise.resolve(/** @type {*} */ (this.storage[key]));\n};\n\n\n/**\n * @param {string} key\n * @param {*} value\n * @return {!goog.Promise<void>}\n * @override\n */\nfireauth.storage.InMemoryStorage.prototype.set = function(key, value) {\n  this.storage[key] = value;\n  return goog.Promise.resolve();\n};\n\n\n/**\n * @param {string} key\n * @return {!goog.Promise<void>}\n * @override\n */\nfireauth.storage.InMemoryStorage.prototype.remove = function(key) {\n  delete this.storage[key];\n  return goog.Promise.resolve();\n};\n\n\n/**\n * @param {function((!goog.events.BrowserEvent|!Array<string>))} listener The\n *     storage event listener.\n * @override\n */\nfireauth.storage.InMemoryStorage.prototype.addStorageListener =\n    function(listener) {\n};\n\n\n/**\n * @param {function((!goog.events.BrowserEvent|!Array<string>))} listener The\n *     storage event listener.\n * @override\n */\nfireauth.storage.InMemoryStorage.prototype.removeStorageListener = function(\n    listener) {};\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\ngoog.provide('fireauth.storage.LocalStorage');\n\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.authenum.Error');\ngoog.require('fireauth.storage.Storage');\ngoog.require('fireauth.util');\ngoog.require('goog.Promise');\ngoog.require('goog.events');\n\n\n\n/**\n * LocalStorage provides an interface to localStorage, the persistent Web\n * Storage API.\n * @constructor\n * @implements {fireauth.storage.Storage}\n */\nfireauth.storage.LocalStorage = function() {\n  // Check is localStorage available in the current environment.\n  if (!fireauth.storage.LocalStorage.isAvailable()) {\n    // In a Node.js environment, dom-storage module needs to be required.\n    if (fireauth.util.getEnvironment() == fireauth.util.Env.NODE) {\n      throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR,\n          'The LocalStorage compatibility library was not found.');\n    }\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.WEB_STORAGE_UNSUPPORTED);\n  }\n\n  /**\n   * The underlying storage instance for persistent data.\n   * @private {!Storage}\n   */\n  this.storage_ = /** @type {!Storage} */ (\n      fireauth.storage.LocalStorage.getGlobalStorage() ||\n      firebase.INTERNAL['node']['localStorage']);\n  /** @public {string} The storage type identifier. */\n  this.type = fireauth.storage.Storage.Type.LOCAL_STORAGE;\n};\n\n\n/** @return {?Storage|undefined} The global localStorage instance. */\nfireauth.storage.LocalStorage.getGlobalStorage = function() {\n  try {\n    var storage = goog.global['localStorage'];\n    // Try editing web storage. If an error is thrown, it may be disabled.\n    var key = fireauth.util.generateEventId();\n    if (storage) {\n      storage['setItem'](key, '1');\n      storage['removeItem'](key);\n    }\n    return storage;\n  } catch (e) {\n    // In some cases, browsers with web storage disabled throw an error simply\n    // on access.\n    return null;\n  }\n};\n\n\n/**\n * The key used to check if the storage instance is available.\n * @private {string}\n * @const\n */\nfireauth.storage.LocalStorage.STORAGE_AVAILABLE_KEY_ = '__sak';\n\n\n/** @return {boolean} Whether localStorage is available. */\nfireauth.storage.LocalStorage.isAvailable = function() {\n  // In Node.js localStorage is polyfilled.\n  var isNode = fireauth.util.getEnvironment() == fireauth.util.Env.NODE;\n  // Either window should provide this storage mechanism or in case of Node.js,\n  // firebase.INTERNAL should provide it.\n  var storage = fireauth.storage.LocalStorage.getGlobalStorage() ||\n      (isNode &&\n       firebase.INTERNAL['node'] &&\n       firebase.INTERNAL['node']['localStorage']);\n  if (!storage) {\n    return false;\n  }\n  try {\n    // setItem will throw an exception if we cannot access web storage (e.g.,\n    // Safari in private mode).\n    storage.setItem(fireauth.storage.LocalStorage.STORAGE_AVAILABLE_KEY_, '1');\n    storage.removeItem(fireauth.storage.LocalStorage.STORAGE_AVAILABLE_KEY_);\n    return true;\n  } catch (e) {\n    return false;\n  }\n};\n\n\n/**\n * Retrieves the value stored at the key.\n * @param {string} key\n * @return {!goog.Promise<*>}\n * @override\n */\nfireauth.storage.LocalStorage.prototype.get = function(key) {\n  var self = this;\n  return goog.Promise.resolve()\n      .then(function() {\n        var json = self.storage_.getItem(key);\n        return fireauth.util.parseJSON(json);\n      });\n};\n\n\n/**\n * Stores the value at the specified key.\n * @param {string} key\n * @param {*} value\n * @return {!goog.Promise<void>}\n * @override\n */\nfireauth.storage.LocalStorage.prototype.set = function(key, value) {\n  var self = this;\n  return goog.Promise.resolve()\n      .then(function() {\n        var obj = fireauth.util.stringifyJSON(value);\n        if (obj === null) {\n          self.remove(key);\n        } else {\n          self.storage_.setItem(key, obj);\n        }\n      });\n};\n\n\n/**\n * Removes the value at the specified key.\n * @param {string} key\n * @return {!goog.Promise<void>}\n * @override\n */\nfireauth.storage.LocalStorage.prototype.remove = function(key) {\n  var self = this;\n  return goog.Promise.resolve()\n      .then(function() {\n        self.storage_.removeItem(key);\n      });\n};\n\n\n/**\n * Adds a listener to storage event change.\n * @param {function(!goog.events.BrowserEvent)} listener The storage event\n *     listener.\n * @override\n */\nfireauth.storage.LocalStorage.prototype.addStorageListener = function(\n    listener) {\n  if (goog.global['window']) {\n    goog.events.listen(goog.global['window'], 'storage', listener);\n  }\n};\n\n\n/**\n * Removes a listener to storage event change.\n * @param {function(!goog.events.BrowserEvent)} listener The storage event\n *     listener.\n * @override\n */\nfireauth.storage.LocalStorage.prototype.removeStorageListener = function(\n    listener) {\n  if (goog.global['window']) {\n    goog.events.unlisten(goog.global['window'], 'storage', listener);\n  }\n};\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\ngoog.provide('fireauth.storage.NullStorage');\n\ngoog.require('fireauth.storage.Storage');\ngoog.require('goog.Promise');\n\n\n\n/**\n * NullStorage provides an implementation of Storage that does always returns\n * null. This can be used if a type of storage is unsupported on a platform.\n * @constructor\n * @implements {fireauth.storage.Storage}\n */\nfireauth.storage.NullStorage = function() {\n  /** @private {!Object} The object where we store values. */\n  this.storage_ = {};\n  /** @public {string} The storage type identifier. */\n  this.type = fireauth.storage.Storage.Type.NULL_STORAGE;\n};\n\n\n/**\n * @param {string} key\n * @return {!goog.Promise<*>}\n * @override\n */\nfireauth.storage.NullStorage.prototype.get = function(key) {\n  return goog.Promise.resolve(/** @type {*} */ (null));\n};\n\n\n/**\n * @param {string} key\n * @param {*} value\n * @return {!goog.Promise<void>}\n * @override\n */\nfireauth.storage.NullStorage.prototype.set = function(key, value) {\n  return goog.Promise.resolve();\n};\n\n\n/**\n * @param {string} key\n * @return {!goog.Promise<void>}\n * @override\n */\nfireauth.storage.NullStorage.prototype.remove = function(key) {\n  return goog.Promise.resolve();\n};\n\n\n/**\n * @param {function(!goog.events.BrowserEvent)} listener The storage event\n *     listener.\n * @override\n */\nfireauth.storage.NullStorage.prototype.addStorageListener = function(listener) {\n};\n\n\n/**\n * @param {function(!goog.events.BrowserEvent)} listener The storage event\n *     listener.\n * @override\n */\nfireauth.storage.NullStorage.prototype.removeStorageListener = function(\n    listener) {};\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\ngoog.provide('fireauth.storage.SessionStorage');\n\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.authenum.Error');\ngoog.require('fireauth.storage.Storage');\ngoog.require('fireauth.util');\ngoog.require('goog.Promise');\n\n\n\n/**\n * SessionStorage provides an interface to sessionStorage, the temporary web\n * storage API.\n * @constructor\n * @implements {fireauth.storage.Storage}\n */\nfireauth.storage.SessionStorage = function() {\n  // Check is sessionStorage available in the current environment.\n  if (!fireauth.storage.SessionStorage.isAvailable()) {\n    // In a Node.js environment, dom-storage module needs to be required.\n    if (fireauth.util.getEnvironment() == fireauth.util.Env.NODE) {\n      throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR,\n          'The SessionStorage compatibility library was not found.');\n    }\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.WEB_STORAGE_UNSUPPORTED);\n  }\n\n  /**\n   * The underlying storage instance for temporary data.\n   * @private {!Storage}\n   */\n  this.storage_ = /** @type {!Storage} */ (\n      fireauth.storage.SessionStorage.getGlobalStorage() ||\n      firebase.INTERNAL['node']['sessionStorage']);\n  /** @public {string} The storage type identifier. */\n  this.type = fireauth.storage.Storage.Type.SESSION_STORAGE;\n};\n\n\n/** @return {?Storage|undefined} The global sessionStorage instance. */\nfireauth.storage.SessionStorage.getGlobalStorage = function() {\n  try {\n    var storage = goog.global['sessionStorage'];\n    // Try editing web storage. If an error is thrown, it may be disabled.\n    var key = fireauth.util.generateEventId();\n    if (storage) {\n      storage['setItem'](key, '1');\n      storage['removeItem'](key);\n    }\n    return storage;\n  } catch (e) {\n    // In some cases, browsers with web storage disabled throw an error simply\n    // on access.\n    return null;\n  }\n};\n\n\n/**\n * The key used to check if the storage instance is available.\n * @private {string}\n * @const\n */\nfireauth.storage.SessionStorage.STORAGE_AVAILABLE_KEY_ = '__sak';\n\n\n/** @return {boolean} Whether sessionStorage is available. */\nfireauth.storage.SessionStorage.isAvailable = function() {\n  // In Node.js sessionStorage is polyfilled.\n  var isNode = fireauth.util.getEnvironment() == fireauth.util.Env.NODE;\n  // Either window should provide this storage mechanism or in case of Node.js,\n  // firebase.INTERNAL should provide it.\n  var storage = fireauth.storage.SessionStorage.getGlobalStorage() ||\n      (isNode &&\n       firebase.INTERNAL['node'] &&\n       firebase.INTERNAL['node']['sessionStorage']);\n  if (!storage) {\n    return false;\n  }\n  try {\n    // setItem will throw an exception if we cannot access web storage (e.g.,\n    // Safari in private mode).\n    storage.setItem(\n        fireauth.storage.SessionStorage.STORAGE_AVAILABLE_KEY_, '1');\n    storage.removeItem(fireauth.storage.SessionStorage.STORAGE_AVAILABLE_KEY_);\n    return true;\n  } catch (e) {\n    return false;\n  }\n};\n\n\n/**\n * Retrieves the value stored at the key.\n * @param {string} key\n * @return {!goog.Promise<*>}\n * @override\n */\nfireauth.storage.SessionStorage.prototype.get = function(key) {\n  var self = this;\n  return goog.Promise.resolve()\n      .then(function() {\n        var json = self.storage_.getItem(key);\n        return fireauth.util.parseJSON(json);\n      });\n};\n\n\n/**\n * Stores the value at the specified key.\n * @param {string} key\n * @param {*} value\n * @return {!goog.Promise<void>}\n * @override\n */\nfireauth.storage.SessionStorage.prototype.set = function(key, value) {\n  var self = this;\n  return goog.Promise.resolve()\n      .then(function() {\n        var obj = fireauth.util.stringifyJSON(value);\n        if (obj === null) {\n          self.remove(key);\n        } else {\n          self.storage_.setItem(key, obj);\n        }\n      });\n};\n\n\n/**\n * Removes the value at the specified key.\n * @param {string} key\n * @return {!goog.Promise<void>}\n * @override\n */\nfireauth.storage.SessionStorage.prototype.remove = function(key) {\n  var self = this;\n  return goog.Promise.resolve()\n      .then(function() {\n        self.storage_.removeItem(key);\n      });\n};\n\n\n/**\n * Adds a listener to storage event change.\n * @param {function(!goog.events.BrowserEvent)} listener The storage event\n *     listener.\n * @override\n */\nfireauth.storage.SessionStorage.prototype.addStorageListener = function(\n    listener) {};\n\n\n/**\n * Removes a listener to storage event change.\n * @param {function(!goog.events.BrowserEvent)} listener The storage event\n *     listener.\n * @override\n */\nfireauth.storage.SessionStorage.prototype.removeStorageListener = function(\n    listener) {};\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\ngoog.provide('fireauth.storage.Factory');\ngoog.provide('fireauth.storage.Factory.EnvConfig');\n\ngoog.require('fireauth.storage.AsyncStorage');\ngoog.require('fireauth.storage.HybridIndexedDB');\ngoog.require('fireauth.storage.InMemoryStorage');\ngoog.require('fireauth.storage.LocalStorage');\ngoog.require('fireauth.storage.NullStorage');\ngoog.require('fireauth.storage.SessionStorage');\ngoog.require('fireauth.util');\n\n\n/**\n * Factory manages the storage implementations and determines the correct one\n * for the current environment.\n * @param {!fireauth.storage.Factory.EnvConfigType} env The storage\n *     configuration for the current environment.\n * @constructor\n */\nfireauth.storage.Factory = function(env) {\n  /** @const @private {!fireauth.storage.Factory.EnvConfigType} */\n  this.env_ = env;\n};\n\n\n/**\n * Construct the singleton instance of the Factory, automatically detecting\n * the current environment.\n * @return {!fireauth.storage.Factory}\n */\nfireauth.storage.Factory.getInstance = function() {\n  if (!fireauth.storage.Factory.instance_) {\n    fireauth.storage.Factory.instance_ =\n        new fireauth.storage.Factory(fireauth.storage.Factory.getEnvConfig());\n  }\n  return fireauth.storage.Factory.instance_;\n};\n\n\n/**\n * @typedef {{\n *   persistent: function(new:fireauth.storage.Storage),\n *   temporary: function(new:fireauth.storage.Storage)\n * }}\n */\nfireauth.storage.Factory.EnvConfigType;\n\n\n/**\n * Configurations of storage for different environments.\n * @enum {!fireauth.storage.Factory.EnvConfigType}\n */\nfireauth.storage.Factory.EnvConfig = {\n  BROWSER: {\n    persistent: fireauth.storage.LocalStorage,\n    temporary: fireauth.storage.SessionStorage\n  },\n  NODE: {\n    persistent: fireauth.storage.LocalStorage,\n    temporary: fireauth.storage.SessionStorage\n  },\n  REACT_NATIVE: {\n    persistent: fireauth.storage.AsyncStorage,\n    temporary: fireauth.storage.NullStorage\n  },\n  WORKER: {\n    persistent: fireauth.storage.LocalStorage,\n    temporary: fireauth.storage.NullStorage\n  }\n};\n\n\n/**\n * Detects the current environment and returns the appropriate environment\n * configuration.\n * @return {!fireauth.storage.Factory.EnvConfigType}\n */\nfireauth.storage.Factory.getEnvConfig = function() {\n  var envMap = {};\n  envMap[fireauth.util.Env.BROWSER] =\n      fireauth.storage.Factory.EnvConfig.BROWSER;\n  envMap[fireauth.util.Env.NODE] =\n      fireauth.storage.Factory.EnvConfig.NODE;\n  envMap[fireauth.util.Env.REACT_NATIVE] =\n      fireauth.storage.Factory.EnvConfig.REACT_NATIVE;\n  envMap[fireauth.util.Env.WORKER] =\n      fireauth.storage.Factory.EnvConfig.WORKER;    \n  return envMap[fireauth.util.getEnvironment()];\n};\n\n\n/**\n * @return {!fireauth.storage.Storage} The persistent storage instance.\n */\nfireauth.storage.Factory.prototype.makePersistentStorage = function() {\n  if (fireauth.util.persistsStorageWithIndexedDB()) {\n    // If persistent storage is implemented using indexedDB, use indexedDB.\n    // Use HybridIndexedDB instead of indexedDB directly since this will\n    // fallback to a fallback storage when indexedDB is not supported (private\n    // browsing mode, etc).\n    return new fireauth.storage.HybridIndexedDB(\n        fireauth.util.isWorker() ?\n        new fireauth.storage.InMemoryStorage() : new this.env_.persistent());\n  }\n  return new this.env_.persistent();\n};\n\n\n/**\n * @return {!fireauth.storage.Storage} The temporary storage instance.\n */\nfireauth.storage.Factory.prototype.makeTemporaryStorage = function() {\n  return new this.env_.temporary();\n};\n\n\n/**\n * @return {!fireauth.storage.Storage} An in memory storage instance.\n */\nfireauth.storage.Factory.prototype.makeInMemoryStorage = function() {\n  return new fireauth.storage.InMemoryStorage();\n};\n","/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines utilities for session management.\n */\n\ngoog.provide('fireauth.authStorage');\ngoog.provide('fireauth.authStorage.Key');\ngoog.provide('fireauth.authStorage.Manager');\ngoog.provide('fireauth.authStorage.Persistence');\n\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.authenum.Error');\ngoog.require('fireauth.storage.Factory');\ngoog.require('fireauth.storage.IndexedDB');\ngoog.require('fireauth.storage.Storage');\ngoog.require('fireauth.util');\ngoog.require('goog.Promise');\ngoog.require('goog.array');\ngoog.require('goog.events');\ngoog.require('goog.object');\n\n\n\n/**\n * The namespace for Firebase Auth storage.\n * @private @const {string}\n */\nfireauth.authStorage.NAMESPACE_ = 'firebase';\n\n\n/**\n * The separator for Firebase Auth storage with App ID key.\n * @private @const {string}\n */\nfireauth.authStorage.SEPARATOR_ = ':';\n\n\n/**\n * @const {number} The IE 10 localStorage cross tab synchronization delay in\n *     milliseconds.\n */\nfireauth.authStorage.IE10_LOCAL_STORAGE_SYNC_DELAY = 10;\n\n\n/**\n * Enums for Auth state persistence.\n * @enum {string}\n */\nfireauth.authStorage.Persistence = {\n  // State will persist even when the browser window is closed or the activity\n  // is destroyed in react-native.\n  LOCAL: 'local',\n  // State is only stored in memory and will be cleared when the window or\n  // activity is refreshed.\n  NONE: 'none',\n  // State will only persist in current session/tab, relevant to web only, and\n  // will be cleared when the tab is closed.\n  SESSION: 'session'\n};\n\n\n/**\n * Validates that an argument is a valid persistence value. If an invalid type\n * is specified, an error is thrown synchronously.\n * @param {*} arg The argument to validate.\n */\nfireauth.authStorage.validatePersistenceArgument =\n    function(arg) {\n  // Invalid type error.\n  var invalidTypeError = new fireauth.AuthError(\n      fireauth.authenum.Error.INVALID_PERSISTENCE);\n  // Unsupported type error.\n  var unsupportedTypeError = new fireauth.AuthError(\n      fireauth.authenum.Error.UNSUPPORTED_PERSISTENCE);\n  // Check if the persistence type is a valid one.\n  // Throw invalid type error if not valid.\n  if (!goog.object.containsValue(fireauth.authStorage.Persistence, arg) ||\n      // goog.object.containsValue(fireauth.authStorage.Persistence, ['none'])\n      // returns true.\n      typeof arg !== 'string') {\n    throw invalidTypeError;\n  }\n  // Validate if the specified type is supported in the current environment.\n  switch (fireauth.util.getEnvironment()) {\n    case fireauth.util.Env.REACT_NATIVE:\n      // This is only supported in a browser.\n      if (arg === fireauth.authStorage.Persistence.SESSION) {\n        throw unsupportedTypeError;\n      }\n      break;\n    case fireauth.util.Env.NODE:\n      // Only none is supported in Node.js.\n      if (arg !== fireauth.authStorage.Persistence.NONE) {\n        throw unsupportedTypeError;\n      }\n      break;\n    case fireauth.util.Env.WORKER:\n      // In a worker environment, either LOCAL or NONE are supported.\n      // If indexedDB not supported and LOCAL provided, throw an error.\n      if (arg === fireauth.authStorage.Persistence.SESSION ||\n          (!fireauth.storage.IndexedDB.isAvailable() &&\n           arg !== fireauth.authStorage.Persistence.NONE)) {\n        throw unsupportedTypeError;\n      }\n      break;\n    case fireauth.util.Env.BROWSER:\n    default:\n      // This is restricted by what the browser supports.\n      if (!fireauth.util.isWebStorageSupported() &&\n          arg !== fireauth.authStorage.Persistence.NONE) {\n        throw unsupportedTypeError;\n      }\n      break;\n  }\n};\n\n\n/**\n * Storage key metadata.\n * @typedef {{name: string, persistent: !fireauth.authStorage.Persistence}}\n */\nfireauth.authStorage.Key;\n\n\n/**\n * Storage manager.\n * @param {string} namespace The optional namespace.\n * @param {string} separator The optional separator.\n * @param {boolean} safariLocalStorageNotSynced Whether browser has Safari\n *     iframe restriction with storage event triggering but storage not updated.\n * @param {boolean} runsInBackground Whether browser can detect storage event\n *     when it had already been pushed to the background. This may happen in\n *     some mobile browsers. A localStorage change in the foreground window\n *     will not be detected in the background window via the storage event.\n *     This was detected in iOS 7.x mobile browsers.\n * @param {boolean} webStorageSupported Whether browser web storage is\n *     supported.\n * @constructor @struct @final\n */\nfireauth.authStorage.Manager = function(\n    namespace,\n    separator,\n    safariLocalStorageNotSynced,\n    runsInBackground,\n    webStorageSupported) {\n  /** @const @private {string} Storage namespace. */\n  this.namespace_ = namespace;\n  /** @const @private {string} Storage namespace key separator. */\n  this.separator_ = separator;\n  /**\n   * @const @private {boolean} Whether browser has Safari iframe restriction\n   *     with storage event triggering but storage not updated.\n   */\n  this.safariLocalStorageNotSynced_ = safariLocalStorageNotSynced;\n  /**\n   *  @private {boolean} Whether browser can detect storage event when it\n   *     had already been pushed to the background. This may happen in some\n   *     mobile browsers.\n   */\n  this.runsInBackground_ = runsInBackground;\n  /**  @const @private {boolean} Whether browser web storage is supported. */\n  this.webStorageSupported_ = webStorageSupported;\n\n  /**\n   * @const @private {!Object.<string, !Array<function()>>} The storage event\n   *     key to listeners map.\n   */\n  this.listeners_ = {};\n\n  var storageFactory = fireauth.storage.Factory.getInstance();\n  try {\n    /**\n     * @private {!fireauth.storage.Storage} Persistence storage.\n     */\n    this.persistentStorage_ = storageFactory.makePersistentStorage();\n  } catch (e) {\n    // Default to in memory storage if the preferred persistent storage is not\n    // supported.\n    this.persistentStorage_ = storageFactory.makeInMemoryStorage();\n    // Do not use indexedDB fallback.\n    this.localStorageNotSynchronized_ = false;\n    // Do not set polling functions on window.localStorage.\n    this.runsInBackground_ = true;\n  }\n  try {\n    /**\n     * @private {!fireauth.storage.Storage} Temporary session storage.\n     */\n    this.temporaryStorage_ = storageFactory.makeTemporaryStorage();\n  } catch (e) {\n    // Default to in memory storage if the preferred temporary storage is not\n    // supported. This should be a different in memory instance as the\n    // persistent storage, since the same key could be available for both types\n    // of storage.\n    this.temporaryStorage_ = storageFactory.makeInMemoryStorage();\n  }\n  /**\n   * @private {!fireauth.storage.Storage} In memory storage.\n   */\n  this.inMemoryStorage_ = storageFactory.makeInMemoryStorage();\n\n  /**\n   * @const @private {function(!goog.events.BrowserEvent)|\n   *                  function(!Array<string>)} Storage change handler.\n   */\n  this.storageChangeEventHandler_ = goog.bind(this.storageChangeEvent_, this);\n  /** @private {!Object.<string, *>} Local map for localStorage. */\n  this.localMap_ = {};\n};\n\n\n/**\n * @return {!fireauth.authStorage.Manager} The default Auth storage manager\n *     instance.\n */\nfireauth.authStorage.Manager.getInstance = function() {\n  // Creates the default instance for Auth storage maanger.\n  if (!fireauth.authStorage.Manager.instance_) {\n    /**\n     * @private {?fireauth.authStorage.Manager} The default storage manager\n     *     instance.\n     */\n    fireauth.authStorage.Manager.instance_ = new fireauth.authStorage.Manager(\n        fireauth.authStorage.NAMESPACE_,\n        fireauth.authStorage.SEPARATOR_,\n        fireauth.util.isSafariLocalStorageNotSynced(),\n        fireauth.util.runsInBackground(),\n        fireauth.util.isWebStorageSupported());\n  }\n  return fireauth.authStorage.Manager.instance_;\n};\n\n\n/** Clears storage manager instances. This is used for testing. */\nfireauth.authStorage.Manager.clear = function() {\n  fireauth.authStorage.Manager.instance_ = null;\n};\n\n\n/**\n * Returns the storage corresponding to the specified persistence.\n * @param {!fireauth.authStorage.Persistence} persistent The type of storage\n *     persistence.\n * @return {!fireauth.storage.Storage} The corresponding storage instance.\n * @private\n */\nfireauth.authStorage.Manager.prototype.getStorage_ = function(persistent) {\n  switch (persistent) {\n    case fireauth.authStorage.Persistence.SESSION:\n      return this.temporaryStorage_;\n    case fireauth.authStorage.Persistence.NONE:\n      return this.inMemoryStorage_;\n    case fireauth.authStorage.Persistence.LOCAL:\n    default:\n      return this.persistentStorage_;\n  }\n};\n\n\n/**\n * Constructs the corresponding storage key name.\n * @param {fireauth.authStorage.Key} dataKey The key under which the value is\n *     stored.\n * @param {?string=} opt_id This ID associates storage values with specific\n *     apps.\n * @return {string} The corresponding key name with namespace prefixed.\n * @private\n */\nfireauth.authStorage.Manager.prototype.getKeyName_ = function(dataKey, opt_id) {\n  return this.namespace_ + this.separator_ + dataKey.name +\n      (opt_id ? this.separator_ + opt_id : '');\n};\n\n\n/**\n * Migrates window.localStorage to the provided persistent storage.\n * @param {fireauth.authStorage.Key} dataKey The key under which the persistent\n *     value is supposed to be stored.\n * @param {?string=} opt_id When operating in multiple app mode, this ID\n *     associates storage values with specific apps.\n * @return {!goog.Promise<void>} A promise that resolves when the data stored\n *     in window.localStorage is migrated to the provided persistent storage\n *     identified by the provided data key.\n */\nfireauth.authStorage.Manager.prototype.migrateFromLocalStorage =\n    function(dataKey, opt_id) {\n  var self = this;\n  var key = this.getKeyName_(dataKey, opt_id);\n  var storage = this.getStorage_(dataKey.persistent);\n  // Get data stored in the default persistent storage identified by dataKey.\n  return this.get(dataKey, opt_id).then(function(response) {\n    // Get the stored value in window.localStorage if available.\n    var oldStorageValue = null;\n    try {\n      oldStorageValue = fireauth.util.parseJSON(\n          goog.global['localStorage']['getItem'](key));\n    } catch (e) {\n      // Set value as null. This will resolve the promise immediately.\n    }\n    // If data is stored in window.localStorage but no data is available in\n    // default persistent storage, migrate data from window.localStorage to\n    // default persistent storage.\n    if (oldStorageValue && !response) {\n      // This condition may fail in situations where a user opens a tab with\n      // an old version while using a tab with a new version, or when a\n      // developer switches back and forth between and old and new version of\n      // the library.\n      goog.global['localStorage']['removeItem'](key);\n      // Migrate the value to new default persistent storage.\n      return self.set(dataKey, oldStorageValue, opt_id);\n    } else if (oldStorageValue &&\n               response &&\n               storage.type != fireauth.storage.Storage.Type.LOCAL_STORAGE) {\n      // Data stored in both localStorage and new persistent storage (eg.\n      // indexedDB) for some reason.\n      // This could happen if the developer is migrating back and forth.\n      // The new default persistent storage (eg. indexedDB) takes precedence.\n      goog.global['localStorage']['removeItem'](key);\n    }\n  });\n};\n\n\n/**\n * Gets the stored value from the corresponding storage.\n * @param {fireauth.authStorage.Key} dataKey The key under which the value is\n *     stored.\n * @param {?string=} opt_id When operating in multiple app mode, this ID\n *     associates storage values with specific apps.\n * @return {!goog.Promise} A Promise that resolves with the stored value.\n */\nfireauth.authStorage.Manager.prototype.get = function(dataKey, opt_id) {\n  var keyName = this.getKeyName_(dataKey, opt_id);\n  return this.getStorage_(dataKey.persistent).get(keyName);\n};\n\n\n/**\n * Removes the stored value from the corresponding storage.\n * @param {fireauth.authStorage.Key} dataKey The key under which the value is\n *     stored.\n * @param {?string=} opt_id When operating in multiple app mode, this ID\n *     associates storage values with specific apps.\n * @return {!goog.Promise<void>} A Promise that resolves when the operation is\n *     completed.\n */\nfireauth.authStorage.Manager.prototype.remove = function(dataKey, opt_id) {\n  var keyName = this.getKeyName_(dataKey, opt_id);\n  // Keep local map up to date for requested key if persistent storage is used.\n  if (dataKey.persistent == fireauth.authStorage.Persistence.LOCAL) {\n    this.localMap_[keyName] = null;\n  }\n  return this.getStorage_(dataKey.persistent).remove(keyName);\n};\n\n\n/**\n * Stores the value in the corresponding storage.\n * @param {fireauth.authStorage.Key} dataKey The key under which the value is\n *     stored.\n * @param {*} value The value to be stored.\n * @param {?string=} opt_id When operating in multiple app mode, this ID\n *     associates storage values with specific apps.\n * @return {!goog.Promise<void>} A Promise that resolves when the operation is\n *     completed.\n */\nfireauth.authStorage.Manager.prototype.set = function(dataKey, value, opt_id) {\n  var keyName = this.getKeyName_(dataKey, opt_id);\n  var self = this;\n  var storage = this.getStorage_(dataKey.persistent);\n  return storage.set(keyName, value)\n      .then(function() {\n        return storage.get(keyName);\n      })\n      .then(function(serializedValue) {\n        // Keep local map up to date for requested key if persistent storage is\n        // used.\n        if (dataKey.persistent == fireauth.authStorage.Persistence.LOCAL) {\n          self.localMap_[keyName] = serializedValue;\n        }\n      });\n};\n\n\n/**\n * @param {fireauth.authStorage.Key} dataKey The key under which the value is\n *     stored.\n * @param {?string} id When operating in multiple app mode, this ID associates\n *     storage values with specific apps.\n * @param {function()} listener The callback listener to run on storage event\n *     related to key.\n */\nfireauth.authStorage.Manager.prototype.addListener =\n    function(dataKey, id, listener) {\n  var key = this.getKeyName_(dataKey, id);\n  // Initialize local map for current key if web storage is supported.\n  if (this.webStorageSupported_) {\n    this.localMap_[key] = goog.global['localStorage']['getItem'](key);\n  }\n  if (goog.object.isEmpty(this.listeners_)) {\n    // Start listeners.\n    this.startListeners_();\n  }\n  if (!this.listeners_[key]) {\n    this.listeners_[key] = [];\n  }\n  this.listeners_[key].push(listener);\n};\n\n\n/**\n * @param {fireauth.authStorage.Key} dataKey The key under which the value is\n *     stored.\n * @param {?string} id When operating in multiple app mode, this ID associates\n *     storage values with specific apps.\n * @param {function()} listener The listener to remove.\n */\nfireauth.authStorage.Manager.prototype.removeListener =\n    function(dataKey, id, listener) {\n  var key = this.getKeyName_(dataKey, id);\n  if (this.listeners_[key]) {\n    goog.array.removeAllIf(\n        this.listeners_[key],\n        function(ele) {\n          return ele == listener;\n        });\n    if (this.listeners_[key].length == 0) {\n      delete this.listeners_[key];\n    }\n  }\n  if (goog.object.isEmpty(this.listeners_)) {\n    // Stop listeners.\n    this.stopListeners_();\n  }\n};\n\n\n/**\n * The delay to wait between continuous checks of localStorage on browsers where\n * tabs do not run in the background. After each interval wait, we check for\n * external changes in localStorage that were not detected in the current tab.\n * @const {number}\n * @private\n */\nfireauth.authStorage.Manager.LOCAL_STORAGE_POLLING_TIMER_ = 1000;\n\n\n/**\n * Starts all storage event listeners.\n * @private\n */\nfireauth.authStorage.Manager.prototype.startListeners_ = function() {\n  this.getStorage_(fireauth.authStorage.Persistence.LOCAL)\n      .addStorageListener(this.storageChangeEventHandler_);\n  // TODO: refactor this implementation to be handled by the underlying\n  // storage mechanism.\n  if (!this.runsInBackground_ &&\n      // Add an exception for browsers that persist storage with indexedDB, we\n      // should stick with indexedDB listener implementation in that case.\n      !fireauth.util.persistsStorageWithIndexedDB() &&\n      // Confirm browser web storage is supported as polling relies on it.\n      this.webStorageSupported_) {\n    this.startManualListeners_();\n  }\n};\n\n/**\n * Starts manual polling function to detect storage event changes.\n * @private\n */\nfireauth.authStorage.Manager.prototype.startManualListeners_ = function() {\n  var self = this;\n  this.stopManualListeners_();\n  /** @private {?number} The interval timer for manual storage checking. */\n  this.manualListenerTimer_ = setInterval(function() {\n    // Check all keys with listeners on them.\n    for (var key in self.listeners_) {\n      // Get value from localStorage.\n      var currentValue = goog.global['localStorage']['getItem'](key);\n      var oldValue = self.localMap_[key];\n      // If local map value does not match, trigger listener with storage event.\n      if (currentValue != oldValue) {\n        self.localMap_[key] = currentValue;\n        var event = new goog.events.BrowserEvent(/** @type {!Event} */ ({\n          type: 'storage',\n          key: key,\n          target: window,\n          oldValue: oldValue,\n          newValue: currentValue,\n          // Differentiate this simulated event from the real storage event.\n          poll: true\n        }));\n        self.storageChangeEvent_(event);\n      }\n    }\n  }, fireauth.authStorage.Manager.LOCAL_STORAGE_POLLING_TIMER_);\n};\n\n\n/**\n * Stops manual polling function to detect storage event changes.\n * @private\n */\nfireauth.authStorage.Manager.prototype.stopManualListeners_ = function() {\n  if (this.manualListenerTimer_) {\n    clearInterval(this.manualListenerTimer_);\n    this.manualListenerTimer_ = null;\n  }\n};\n\n\n/**\n * Stops all storage event listeners.\n * @private\n */\nfireauth.authStorage.Manager.prototype.stopListeners_ = function() {\n  this.getStorage_(fireauth.authStorage.Persistence.LOCAL)\n      .removeStorageListener(this.storageChangeEventHandler_);\n  this.stopManualListeners_();\n};\n\n\n/**\n * @param {!goog.events.BrowserEvent|!Array<string>} data The storage event\n *     triggered or the array of keys modified.\n * @private\n */\nfireauth.authStorage.Manager.prototype.storageChangeEvent_ = function(data) {\n  if (data && data.getBrowserEvent) {\n    var event = /** @type {!goog.events.BrowserEvent} */ (data);\n    var key = event.getBrowserEvent().key;\n    // Key would be null in some situations, like when localStorage is cleared\n    // from the browser developer tools.\n    if (key == null) {\n      // For all keys of interest.\n      for (var keyName in this.listeners_) {\n        // Check if something changed in this key's real value.\n        var storedValue = this.localMap_[keyName];\n        // localStorage returns null when a field is not found.\n        if (typeof storedValue === 'undefined') {\n          storedValue = null;\n        }\n        var realValue = goog.global['localStorage']['getItem'](keyName);\n        if (realValue !== storedValue) {\n          // Update local map with real value.\n          this.localMap_[keyName] = realValue;\n          // Trigger that key's listener.\n          this.callListeners_(keyName);\n        }\n      }\n      return;\n    }\n    // Check if the key is Firebase Auth related, otherwise ignore.\n    if (key.indexOf(this.namespace_ + this.separator_) != 0 ||\n        // Ignore keys that have no listeners.\n        !this.listeners_[key]) {\n      return;\n    }\n    // Check the mechanism how this event was detected.\n    // The first event will dictate the mechanism to be used.\n    // Do not use hasOwnProperty('poll') as poll gets obfuscated.\n    if (typeof event.getBrowserEvent().poll !== 'undefined') {\n      // Environment detects storage changes via polling.\n      // Remove storage event listener to prevent possible event duplication.\n      this.getStorage_(fireauth.authStorage.Persistence.LOCAL)\n          .removeStorageListener(this.storageChangeEventHandler_);\n    } else {\n      // Environment detects storage changes via storage event listener.\n      // Remove polling listener to prevent possible event duplication.\n      this.stopManualListeners_();\n    }\n    // Safari embedded iframe. Storage event will trigger with the delta changes\n    // but no changes will be applied to the iframe localStorage.\n    if (this.safariLocalStorageNotSynced_) {\n      // Get current iframe page value, old value and new value.\n      var currentValue = goog.global['localStorage']['getItem'](key);\n      var newValue = event.getBrowserEvent().newValue;\n      // Value not synchronized, synchronize manually.\n      if (newValue !== currentValue) {\n        if (newValue !== null) {\n          // Value changed from current value.\n          goog.global['localStorage']['setItem'](key, newValue);\n        } else {\n          // Current value deleted.\n          goog.global['localStorage']['removeItem'](key);\n        }\n      } else {\n        // Already detected and processed, do not trigger listeners again.\n        if (this.localMap_[key] === newValue &&\n            // Real storage event.\n            typeof event.getBrowserEvent().poll === 'undefined') {\n          return;\n        }\n      }\n    }\n    var self = this;\n    var triggerListeners = function() {\n      // Keep local map up to date in case storage event is triggered before\n      // poll.\n      if (typeof event.getBrowserEvent().poll === 'undefined' &&\n          self.localMap_[key] === goog.global['localStorage']['getItem'](key)) {\n        // Real storage event which has already been detected, do nothing.\n        // This seems to trigger in some IE browsers for some reason.\n        return;\n      }\n      self.localMap_[key] = goog.global['localStorage']['getItem'](key);\n      self.callListeners_(key);\n    };\n    if (fireauth.util.isIe10() &&\n        goog.global['localStorage']['getItem'](key) !==\n        event.getBrowserEvent().newValue &&\n        event.getBrowserEvent().newValue !== event.getBrowserEvent().oldValue) {\n      // IE 10 has this weird bug where a storage event would trigger with the\n      // correct key, oldValue and newValue but localStorage.getItem(key) does\n      // not yield the updated value until a few milliseconds. This ensures this\n      // recovers from that situation.\n      setTimeout(\n          triggerListeners, fireauth.authStorage.IE10_LOCAL_STORAGE_SYNC_DELAY);\n    } else {\n      triggerListeners();\n    }\n  } else {\n    var keys = /** @type {!Array<string>} */ (data);\n    goog.array.forEach(keys, goog.bind(this.callListeners_, this));\n  }\n};\n\n\n/**\n * Calls all listeners for specified storage event key.\n * @param {string} key The storage event key whose listeners are to be run.\n * @private\n */\nfireauth.authStorage.Manager.prototype.callListeners_ = function(key) {\n  if (this.listeners_[key]) {\n    goog.array.forEach(\n        this.listeners_[key],\n        function(listener) {\n          listener();\n        });\n  }\n};\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines the fireauth.storage.AuthEventManager class used by\n * the iframe to retrieve and delete Auth events triggered through an OAuth\n * flow.\n */\n\ngoog.provide('fireauth.storage.AuthEventManager');\ngoog.provide('fireauth.storage.AuthEventManager.Keys');\n\ngoog.require('fireauth.AuthEvent');\ngoog.require('fireauth.authStorage');\n\n\n/**\n * Defines the Auth event storage manager. It provides methods to\n * load and delete Auth events as well as listen to external OAuth changes on\n * them.\n * @param {string} appId The Auth event's application ID.\n * @param {?fireauth.authStorage.Manager=} opt_manager The underlying storage\n *     manager to use. If none is provided, the default global instance is used.\n * @constructor @struct @final\n */\nfireauth.storage.AuthEventManager = function(appId, opt_manager) {\n  /** @const @private{string} appId The Auth event's application ID. */\n  this.appId_ = appId;\n  /**\n   * @const @private{!fireauth.authStorage.Manager} The underlying storage\n   *     manager.\n   */\n  this.manager_ = opt_manager || fireauth.authStorage.Manager.getInstance();\n};\n\n\n/**\n * Valid keys for Auth event manager data.\n * @enum {!fireauth.authStorage.Key}\n */\nfireauth.storage.AuthEventManager.Keys = {\n  AUTH_EVENT: {\n    name: 'authEvent',\n    persistent: fireauth.authStorage.Persistence.LOCAL\n  },\n  REDIRECT_EVENT: {\n    name: 'redirectEvent',\n    persistent: fireauth.authStorage.Persistence.SESSION\n  }\n};\n\n\n/**\n * @return {!goog.Promise<?fireauth.AuthEvent>} A promise that resolves on\n *     success with the stored Auth event.\n */\nfireauth.storage.AuthEventManager.prototype.getAuthEvent = function() {\n  return this.manager_.get(\n      fireauth.storage.AuthEventManager.Keys.AUTH_EVENT, this.appId_)\n      .then(function(response) {\n        return fireauth.AuthEvent.fromPlainObject(response);\n      });\n};\n\n\n/**\n * Removes the identifier's Auth event if it exists.\n * @return {!goog.Promise<void>} A promise that resolves on success.\n */\nfireauth.storage.AuthEventManager.prototype.removeAuthEvent = function() {\n  return this.manager_.remove(\n      fireauth.storage.AuthEventManager.Keys.AUTH_EVENT, this.appId_);\n};\n\n\n/**\n * Adds a listener to Auth event for App ID provided.\n * @param {!function()} listener The listener to run on Auth event.\n */\nfireauth.storage.AuthEventManager.prototype.addAuthEventListener =\n    function(listener) {\n  this.manager_.addListener(\n      fireauth.storage.AuthEventManager.Keys.AUTH_EVENT, this.appId_, listener);\n};\n\n\n/**\n * Removes a listener to Auth event for App ID provided.\n * @param {!function()} listener The listener to run on Auth event.\n */\nfireauth.storage.AuthEventManager.prototype.removeAuthEventListener =\n    function(listener) {\n  this.manager_.removeListener(\n      fireauth.storage.AuthEventManager.Keys.AUTH_EVENT, this.appId_, listener);\n};\n\n\n/**\n * @return {!goog.Promise<?fireauth.AuthEvent>} A promise that resolves on\n *     success with the stored redirect Auth event.\n */\nfireauth.storage.AuthEventManager.prototype.getRedirectEvent =\n    function() {\n  return this.manager_.get(\n      fireauth.storage.AuthEventManager.Keys.REDIRECT_EVENT,\n      this.appId_).then(function(response) {\n        return fireauth.AuthEvent.fromPlainObject(response);\n      });\n};\n\n\n/**\n * Removes the identifier's redirect Auth event if it exists.\n * @return {!goog.Promise<void>} A promise that resolves on success.\n */\nfireauth.storage.AuthEventManager.prototype.removeRedirectEvent = function() {\n  return this.manager_.remove(\n      fireauth.storage.AuthEventManager.Keys.REDIRECT_EVENT, this.appId_);\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Base class for SHA-2 cryptographic hash.\n *\n * Variable names follow the notation in FIPS PUB 180-3:\n * http://csrc.nist.gov/publications/fips/fips180-3/fips180-3_final.pdf.\n *\n * Some code similar to SHA1 are borrowed from sha1.js written by mschilder@.\n */\n\ngoog.provide('goog.crypt.Sha2');\n\ngoog.require('goog.array');\ngoog.require('goog.asserts');\ngoog.require('goog.crypt.Hash');\n\n\n\n/**\n * SHA-2 cryptographic hash constructor.\n * This constructor should not be used directly to create the object. Rather,\n * one should use the constructor of the sub-classes.\n * @param {number} numHashBlocks The size of output in 16-byte blocks.\n * @param {!Array<number>} initHashBlocks The hash-specific initialization\n * @constructor\n * @extends {goog.crypt.Hash}\n * @struct\n */\ngoog.crypt.Sha2 = function(numHashBlocks, initHashBlocks) {\n  goog.crypt.Sha2.base(this, 'constructor');\n\n  this.blockSize = goog.crypt.Sha2.BLOCKSIZE_;\n\n  /**\n   * A chunk holding the currently processed message bytes. Once the chunk has\n   * 64 bytes, we feed it into computeChunk_ function and reset this.chunk_.\n   * @private {!Array<number>|!Uint8Array}\n   */\n  this.chunk_ = goog.global['Uint8Array'] ? new Uint8Array(this.blockSize) :\n                                            new Array(this.blockSize);\n\n  /**\n   * Current number of bytes in this.chunk_.\n   * @private {number}\n   */\n  this.inChunk_ = 0;\n\n  /**\n   * Total number of bytes in currently processed message.\n   * @private {number}\n   */\n  this.total_ = 0;\n\n\n  /**\n   * Holds the previous values of accumulated hash a-h in the computeChunk_\n   * function.\n   * @private {!Array<number>|!Int32Array}\n   */\n  this.hash_ = [];\n\n  /**\n   * The number of output hash blocks (each block is 4 bytes long).\n   * @private {number}\n   */\n  this.numHashBlocks_ = numHashBlocks;\n\n  /**\n   * @private {!Array<number>} initHashBlocks\n   */\n  this.initHashBlocks_ = initHashBlocks;\n\n  /**\n   * Temporary array used in chunk computation.  Allocate here as a\n   * member rather than as a local within computeChunk_() as a\n   * performance optimization to reduce the number of allocations and\n   * reduce garbage collection.\n   * @private {!Int32Array|!Array<number>}\n   */\n  this.w_ = goog.global['Int32Array'] ? new Int32Array(64) : new Array(64);\n\n  if (goog.crypt.Sha2.Kx_ === undefined) {\n    // This is the first time this constructor has been called.\n    if (goog.global['Int32Array']) {\n      // Typed arrays exist\n      goog.crypt.Sha2.Kx_ = new Int32Array(goog.crypt.Sha2.K_);\n    } else {\n      // Typed arrays do not exist\n      goog.crypt.Sha2.Kx_ = goog.crypt.Sha2.K_;\n    }\n  }\n\n  this.reset();\n};\ngoog.inherits(goog.crypt.Sha2, goog.crypt.Hash);\n\n\n/**\n * The block size\n * @private {number}\n */\ngoog.crypt.Sha2.BLOCKSIZE_ = 512 / 8;\n\n\n/**\n * Contains data needed to pad messages less than BLOCK_SIZE_ bytes.\n * @private {!Array<number>}\n */\ngoog.crypt.Sha2.PADDING_ = goog.array.concat(\n    128, goog.array.repeat(0, goog.crypt.Sha2.BLOCKSIZE_ - 1));\n\n\n/** @override */\ngoog.crypt.Sha2.prototype.reset = function() {\n  this.inChunk_ = 0;\n  this.total_ = 0;\n  this.hash_ = goog.global['Int32Array'] ?\n      new Int32Array(this.initHashBlocks_) :\n      goog.array.clone(this.initHashBlocks_);\n};\n\n\n/**\n * Helper function to compute the hashes for a given 512-bit message chunk.\n * @private\n */\ngoog.crypt.Sha2.prototype.computeChunk_ = function() {\n  var chunk = this.chunk_;\n  goog.asserts.assert(chunk.length == this.blockSize);\n  var rounds = 64;\n\n  // Divide the chunk into 16 32-bit-words.\n  var w = this.w_;\n  var index = 0;\n  var offset = 0;\n  while (offset < chunk.length) {\n    w[index++] = (chunk[offset] << 24) | (chunk[offset + 1] << 16) |\n        (chunk[offset + 2] << 8) | (chunk[offset + 3]);\n    offset = index * 4;\n  }\n\n  // Extend the w[] array to be the number of rounds.\n  for (var i = 16; i < rounds; i++) {\n    var w_15 = w[i - 15] | 0;\n    var s0 = ((w_15 >>> 7) | (w_15 << 25)) ^ ((w_15 >>> 18) | (w_15 << 14)) ^\n        (w_15 >>> 3);\n    var w_2 = w[i - 2] | 0;\n    var s1 = ((w_2 >>> 17) | (w_2 << 15)) ^ ((w_2 >>> 19) | (w_2 << 13)) ^\n        (w_2 >>> 10);\n\n    // As a performance optimization, construct the sum a pair at a time\n    // with casting to integer (bitwise OR) to eliminate unnecessary\n    // double<->integer conversions.\n    var partialSum1 = ((w[i - 16] | 0) + s0) | 0;\n    var partialSum2 = ((w[i - 7] | 0) + s1) | 0;\n    w[i] = (partialSum1 + partialSum2) | 0;\n  }\n\n  var a = this.hash_[0] | 0;\n  var b = this.hash_[1] | 0;\n  var c = this.hash_[2] | 0;\n  var d = this.hash_[3] | 0;\n  var e = this.hash_[4] | 0;\n  var f = this.hash_[5] | 0;\n  var g = this.hash_[6] | 0;\n  var h = this.hash_[7] | 0;\n  for (var i = 0; i < rounds; i++) {\n    var S0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^\n        ((a >>> 22) | (a << 10));\n    var maj = ((a & b) ^ (a & c) ^ (b & c));\n    var t2 = (S0 + maj) | 0;\n    var S1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^\n        ((e >>> 25) | (e << 7));\n    var ch = ((e & f) ^ ((~e) & g));\n\n    // As a performance optimization, construct the sum a pair at a time\n    // with casting to integer (bitwise OR) to eliminate unnecessary\n    // double<->integer conversions.\n    var partialSum1 = (h + S1) | 0;\n    var partialSum2 = (ch + (goog.crypt.Sha2.Kx_[i] | 0)) | 0;\n    var partialSum3 = (partialSum2 + (w[i] | 0)) | 0;\n    var t1 = (partialSum1 + partialSum3) | 0;\n\n    h = g;\n    g = f;\n    f = e;\n    e = (d + t1) | 0;\n    d = c;\n    c = b;\n    b = a;\n    a = (t1 + t2) | 0;\n  }\n\n  this.hash_[0] = (this.hash_[0] + a) | 0;\n  this.hash_[1] = (this.hash_[1] + b) | 0;\n  this.hash_[2] = (this.hash_[2] + c) | 0;\n  this.hash_[3] = (this.hash_[3] + d) | 0;\n  this.hash_[4] = (this.hash_[4] + e) | 0;\n  this.hash_[5] = (this.hash_[5] + f) | 0;\n  this.hash_[6] = (this.hash_[6] + g) | 0;\n  this.hash_[7] = (this.hash_[7] + h) | 0;\n};\n\n\n/** @override */\ngoog.crypt.Sha2.prototype.update = function(message, opt_length) {\n  if (opt_length === undefined) {\n    opt_length = message.length;\n  }\n  // Process the message from left to right up to |opt_length| bytes.\n  // When we get a 512-bit chunk, compute the hash of it and reset\n  // this.chunk_. The message might not be multiple of 512 bits so we\n  // might end up with a chunk that is less than 512 bits. We store\n  // such partial chunk in this.chunk_ and it will be filled up later\n  // in digest().\n  var n = 0;\n  var inChunk = this.inChunk_;\n\n  // The input message could be either byte array of string.\n  if (typeof message === 'string') {\n    while (n < opt_length) {\n      this.chunk_[inChunk++] = message.charCodeAt(n++);\n      if (inChunk == this.blockSize) {\n        this.computeChunk_();\n        inChunk = 0;\n      }\n    }\n  } else if (goog.isArrayLike(message)) {\n    while (n < opt_length) {\n      var b = message[n++];\n      if (!('number' == typeof b && 0 <= b && 255 >= b && b == (b | 0))) {\n        throw new Error('message must be a byte array');\n      }\n      this.chunk_[inChunk++] = b;\n      if (inChunk == this.blockSize) {\n        this.computeChunk_();\n        inChunk = 0;\n      }\n    }\n  } else {\n    throw new Error('message must be string or array');\n  }\n\n  // Record the current bytes in chunk to support partial update.\n  this.inChunk_ = inChunk;\n\n  // Record total message bytes we have processed so far.\n  this.total_ += opt_length;\n};\n\n\n/** @override */\ngoog.crypt.Sha2.prototype.digest = function() {\n  var digest = [];\n  var totalBits = this.total_ * 8;\n\n  // Append pad 0x80 0x00*.\n  if (this.inChunk_ < 56) {\n    this.update(goog.crypt.Sha2.PADDING_, 56 - this.inChunk_);\n  } else {\n    this.update(\n        goog.crypt.Sha2.PADDING_, this.blockSize - (this.inChunk_ - 56));\n  }\n\n  // Append # bits in the 64-bit big-endian format.\n  for (var i = 63; i >= 56; i--) {\n    this.chunk_[i] = totalBits & 255;\n    totalBits /= 256;  // Don't use bit-shifting here!\n  }\n  this.computeChunk_();\n\n  // Finally, output the result digest.\n  var n = 0;\n  for (var i = 0; i < this.numHashBlocks_; i++) {\n    for (var j = 24; j >= 0; j -= 8) {\n      digest[n++] = ((this.hash_[i] >> j) & 255);\n    }\n  }\n  return digest;\n};\n\n\n/**\n * Constants used in SHA-2.\n * @const\n * @private {!Array<number>}\n */\ngoog.crypt.Sha2.K_ = [\n  0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,\n  0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,\n  0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,\n  0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n  0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,\n  0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,\n  0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,\n  0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n  0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,\n  0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,\n  0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2\n];\n\n\n/**\n * Sha2.K as an Int32Array if this JS supports typed arrays; otherwise,\n * the same array as Sha2.K.\n *\n * The compiler cannot remove an Int32Array, even if it is not needed\n * (There are certain cases where creating an Int32Array is not\n * side-effect free).  Instead, the first time we construct a Sha2\n * instance, we convert or assign Sha2.K as appropriate.\n * @private {undefined|!Array<number>|!Int32Array}\n */\ngoog.crypt.Sha2.Kx_;\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines the fireauth.storage.OAuthHandlerManager class which\n * provides utilities to the OAuth handler widget to set Auth events after an\n * IDP sign in attempt and to store state during the OAuth handshake with IDP.\n */\n\ngoog.provide('fireauth.storage.OAuthHandlerManager');\n\ngoog.require('fireauth.AuthEvent');\ngoog.require('fireauth.OAuthHelperState');\ngoog.require('fireauth.authStorage');\ngoog.require('fireauth.storage.AuthEventManager.Keys');\n\n\n/**\n * Defines the OAuth handler storage manager. It provides methods to\n * store, load and delete OAuth handler widget state, properties and setting\n * Auth events.\n * @param {?fireauth.authStorage.Manager=} opt_manager The underlying storage\n *     manager to use. If none is provided, the default global instance is used.\n * @constructor @struct @final\n */\nfireauth.storage.OAuthHandlerManager = function(opt_manager) {\n  /**\n   * @const @private{!fireauth.authStorage.Manager} The underlying storage\n   *     manager.\n   */\n  this.manager_ = opt_manager || fireauth.authStorage.Manager.getInstance();\n};\n\n\n/**\n * Valid keys for OAuth handler manager data.\n * @private @enum {!fireauth.authStorage.Key}\n */\nfireauth.storage.OAuthHandlerManager.Keys_ = {\n  OAUTH_HELPER_STATE: {\n    name: 'oauthHelperState',\n    persistent: fireauth.authStorage.Persistence.SESSION\n  },\n  SESSION_ID: {\n    name: 'sessionId',\n    persistent: fireauth.authStorage.Persistence.SESSION\n  }\n};\n\n\n/**\n * @param {string} appId The Auth state's application ID.\n * @return {!goog.Promise<?string|undefined>} A promise that resolves on success\n *     with the stored session ID.\n */\nfireauth.storage.OAuthHandlerManager.prototype.getSessionId = function(appId) {\n  return this.manager_.get(\n      fireauth.storage.OAuthHandlerManager.Keys_.SESSION_ID, appId);\n};\n\n\n/**\n * Removes the session ID string if it exists.\n * @param {string} appId The Auth state's application ID.\n * @return {!goog.Promise<void>} A promise that resolves on success.\n */\nfireauth.storage.OAuthHandlerManager.prototype.removeSessionId =\n    function(appId) {\n  return this.manager_.remove(\n      fireauth.storage.OAuthHandlerManager.Keys_.SESSION_ID, appId);\n};\n\n\n/**\n * Stores the session ID string.\n * @param {string} appId The Auth state's application ID.\n * @param {string} sessionId The session ID string to store.\n * @return {!goog.Promise<void>} A promise that resolves on success.\n */\nfireauth.storage.OAuthHandlerManager.prototype.setSessionId =\n    function(appId, sessionId) {\n  return this.manager_.set(\n      fireauth.storage.OAuthHandlerManager.Keys_.SESSION_ID, sessionId, appId);\n};\n\n\n/**\n * @return {!goog.Promise<?fireauth.OAuthHelperState>} A promise that resolves\n *     on success with the stored OAuth helper state.\n */\nfireauth.storage.OAuthHandlerManager.prototype.getOAuthHelperState =\n    function() {\n  return this.manager_.get(\n      fireauth.storage.OAuthHandlerManager.Keys_.OAUTH_HELPER_STATE)\n      .then(function(response) {\n        return fireauth.OAuthHelperState.fromPlainObject(response);\n      });\n};\n\n\n/**\n * Removes the current OAuth helper state if it exists.\n * @return {!goog.Promise<void>} A promise that resolves on success.\n */\nfireauth.storage.OAuthHandlerManager.prototype.removeOAuthHelperState =\n    function() {\n  return this.manager_.remove(\n      fireauth.storage.OAuthHandlerManager.Keys_.OAUTH_HELPER_STATE);\n};\n\n\n/**\n * Stores the current OAuth helper state.\n * @param {!fireauth.OAuthHelperState} state The OAuth helper state.\n * @return {!goog.Promise<void>} A promise that resolves on success.\n */\nfireauth.storage.OAuthHandlerManager.prototype.setOAuthHelperState =\n    function(state) {\n  return this.manager_.set(\n      fireauth.storage.OAuthHandlerManager.Keys_.OAUTH_HELPER_STATE,\n      state.toPlainObject());\n};\n\n\n/**\n * Stores the Auth event for specified identifier.\n * @param {string} appId The Auth state's application ID.\n * @param {!fireauth.AuthEvent} authEvent The Auth event.\n * @return {!goog.Promise<void>} A promise that resolves on success.\n */\nfireauth.storage.OAuthHandlerManager.prototype.setAuthEvent =\n    function(appId, authEvent) {\n  return this.manager_.set(\n      fireauth.storage.AuthEventManager.Keys.AUTH_EVENT,\n      authEvent.toPlainObject(),\n      appId);\n};\n\n\n/**\n * Stores the redirect Auth event for specified identifier.\n * @param {string} appId The Auth state's application ID.\n * @param {!fireauth.AuthEvent} authEvent The redirect Auth event.\n * @return {!goog.Promise<void>} A promise that resolves on success.\n */\nfireauth.storage.OAuthHandlerManager.prototype.setRedirectEvent =\n    function(appId, authEvent) {\n  return this.manager_.set(\n      fireauth.storage.AuthEventManager.Keys.REDIRECT_EVENT,\n      authEvent.toPlainObject(),\n      appId);\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Abstract cryptographic hash interface.\n *\n * See goog.crypt.Sha1 and goog.crypt.Md5 for sample implementations.\n */\n\ngoog.provide('goog.crypt.Hash');\n\n\n\n/**\n * Create a cryptographic hash instance.\n *\n * @constructor\n * @struct\n */\ngoog.crypt.Hash = function() {\n  /**\n   * The block size for the hasher.\n   * @type {number}\n   */\n  this.blockSize = -1;\n};\n\n\n/**\n * Resets the internal accumulator.\n */\ngoog.crypt.Hash.prototype.reset = goog.abstractMethod;\n\n\n/**\n * Adds a byte array (array with values in [0-255] range) or a string (must\n * only contain 8-bit, i.e., Latin1 characters) to the internal accumulator.\n *\n * Many hash functions operate on blocks of data and implement optimizations\n * when a full chunk of data is readily available. Hence it is often preferable\n * to provide large chunks of data (a kilobyte or more) than to repeatedly\n * call the update method with few tens of bytes. If this is not possible, or\n * not feasible, it might be good to provide data in multiplies of hash block\n * size (often 64 bytes). Please see the implementation and performance tests\n * of your favourite hash.\n *\n * @param {Array<number>|Uint8Array|string} bytes Data used for the update.\n * @param {number=} opt_length Number of bytes to use.\n */\ngoog.crypt.Hash.prototype.update = goog.abstractMethod;\n\n\n/**\n * @return {!Array<number>} The finalized hash computed\n *     from the internal accumulator.\n */\ngoog.crypt.Hash.prototype.digest = goog.abstractMethod;\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview SHA-256 cryptographic hash.\n *\n * Usage:\n *   var sha256 = new goog.crypt.Sha256();\n *   sha256.update(bytes);\n *   var hash = sha256.digest();\n */\n\ngoog.provide('goog.crypt.Sha256');\n\ngoog.require('goog.crypt.Sha2');\n\n\n\n/**\n * SHA-256 cryptographic hash constructor.\n *\n * @constructor\n * @extends {goog.crypt.Sha2}\n * @final\n * @struct\n */\ngoog.crypt.Sha256 = function() {\n  goog.crypt.Sha256.base(\n      this, 'constructor', 8, goog.crypt.Sha256.INIT_HASH_BLOCK_);\n};\ngoog.inherits(goog.crypt.Sha256, goog.crypt.Sha2);\n\n\n/** @private {!Array<number>} */\ngoog.crypt.Sha256.INIT_HASH_BLOCK_ = [\n  0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c,\n  0x1f83d9ab, 0x5be0cd19\n];\n","/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines Cordova utility and helper functions.\n * The following plugins must be installed:\n * cordova plugin add cordova-plugin-buildinfo\n * cordova plugin add cordova-universal-links-plugin-fix\n * cordova plugin add cordova-plugin-browsertab\n * cordova plugin add cordova-plugin-inappbrowser\n * iOS custom scheme support:\n * cordova plugin add cordova-plugin-customurlscheme --variable \\\n * URL_SCHEME=com.firebase.example\n * Console logging in iOS:\n * cordova plugin add cordova-plugin-console\n */\n\ngoog.provide('fireauth.CordovaHandler');\n\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.AuthEvent');\ngoog.require('fireauth.AuthProvider');\ngoog.require('fireauth.DynamicLink');\ngoog.require('fireauth.OAuthSignInHandler');\ngoog.require('fireauth.UniversalLinkSubscriber');\ngoog.require('fireauth.authenum.Error');\ngoog.require('fireauth.constants');\ngoog.require('fireauth.iframeclient.IfcHandler');\ngoog.require('fireauth.storage.AuthEventManager');\ngoog.require('fireauth.storage.OAuthHandlerManager');\ngoog.require('fireauth.util');\ngoog.require('goog.Promise');\ngoog.require('goog.Timer');\ngoog.require('goog.Uri');\ngoog.require('goog.array');\ngoog.require('goog.crypt');\ngoog.require('goog.crypt.Sha256');\n\n\n/**\n * Cordova environment utility and helper functions.\n * @param {string} authDomain The application authDomain.\n * @param {string} apiKey The API key.\n * @param {string} appName The App name.\n * @param {?string=} clientVersion The optional client version string.\n * @param {number=} initialTimeout Initial Auth event timeout.\n * @param {number=} redirectTimeout Redirect result timeout.\n * @param {?string=} endpointId The endpoint ID (staging, test Gaia, etc).\n * @param {?fireauth.constants.EmulatorSettings=} emulatorConfig The emulator\n *     configuration\n * @constructor\n * @implements {fireauth.OAuthSignInHandler}\n */\nfireauth.CordovaHandler = function(authDomain, apiKey, appName,\n    clientVersion, initialTimeout, redirectTimeout, endpointId, emulatorConfig) {\n  /** @private {string} The application authDomain. */\n  this.authDomain_ = authDomain;\n  /** @private {string} The application API key. */\n  this.apiKey_ = apiKey;\n  /** @private {string} The application name. */\n  this.appName_ = appName;\n  /** @private {?string} The client version */\n  this.clientVersion_ = clientVersion || null;\n  /** @private {?string} The Auth endpoint ID. */\n  this.endpointId_ = endpointId || null;\n  /**\n   * @private @const {?fireauth.constants.EmulatorSettings|undefined}\n   * The emulator configuration\n   */\n  this.emulatorConfig_ = emulatorConfig;\n  /** @private {string} The storage key. */\n  this.storageKey_ = fireauth.util.createStorageKey(apiKey, appName);\n  /**\n   * @private {!fireauth.storage.OAuthHandlerManager} The OAuth handler\n   *     storage manager reference, used to save a partial Auth event when\n   *     redirect operation is triggered.\n   */\n  this.savePartialEventManager_ = new fireauth.storage.OAuthHandlerManager();\n  /**\n   * @private {!fireauth.storage.AuthEventManager} The Auth event storage\n   *     manager reference. This is used to get back the saved partial Auth\n   *     event and then delete on successful handling.\n   */\n  this.getAndDeletePartialEventManager_ =\n      new fireauth.storage.AuthEventManager(this.storageKey_);\n  /**\n   * @private {?goog.Promise<!fireauth.AuthEvent>} A promise that resolves with\n   *     the OAuth redirect URL response.\n   */\n  this.initialAuthEvent_ = null;\n  /**\n   * @private {!Array<!function(?fireauth.AuthEvent)>} The Auth event\n   *     listeners.\n   */\n  this.authEventListeners_ = [];\n  /** @private {number} The initial Auth event timeout. */\n  this.initialTimeout_ = initialTimeout ||\n      fireauth.CordovaHandler.INITIAL_TIMEOUT_MS_;\n  /** @private {number} The return to app after redirect timeout. */\n  this.redirectTimeout_ = redirectTimeout ||\n      fireauth.CordovaHandler.REDIRECT_TIMEOUT_MS_;\n  /**\n   * @private {?goog.Promise} The last pending redirect promise. This is null if\n   *     already completed.\n   */\n  this.pendingRedirect_ = null;\n  /**\n   * @private {?Object} The inAppBrowser reference window if available. This is\n   *     relevant to iOS 7 and 8 embedded webviews.\n   */\n  this.inAppBrowserRef_ = null;\n};\n\n\n/**\n * The total number of chars used to generate the session ID string.\n * @const {number}\n * @private\n */\nfireauth.CordovaHandler.SESSION_ID_TOTAL_CHARS_ = 20;\n\n\n/**\n * The default initial Auth event timeout in ms.\n * @const {number}\n * @private\n */\nfireauth.CordovaHandler.INITIAL_TIMEOUT_MS_ = 500;\n\n\n/**\n * The default timeout in milliseconds for a pending redirect operation after\n * returning to the app.\n * @const {number}\n * @private\n */\nfireauth.CordovaHandler.REDIRECT_TIMEOUT_MS_ = 2000;\n\n\n/**\n * Constructs a Cordova configuration error message.\n * @param {?string=} opt_message The optional error message to be used. This\n *     will override the existing default one.\n * @return {!fireauth.AuthError} The Cordova invalid configuration error with\n *     the custom message provided. If no message is provided, the default\n *     message is used.\n * @private\n */\nfireauth.CordovaHandler.getError_ = function(opt_message) {\n  return new fireauth.AuthError(\n      fireauth.authenum.Error.INVALID_CORDOVA_CONFIGURATION,\n      opt_message);\n};\n\n\n/**\n * Initializes the Cordova environment and waits for it to be ready.\n * @return {!goog.Promise} A promise that resolves if the current environment is\n *     a Cordova environment.\n * @override\n */\nfireauth.CordovaHandler.prototype.initializeAndWait = function() {\n  if (this.isReady_) {\n    return this.isReady_;\n  }\n  this.isReady_ = fireauth.util.checkIfCordova().then(function() {\n    // Check all dependencies installed.\n    // Note that cordova-universal-links-plugin has been abandoned.\n    // A fork with latest fixes is available at:\n    // https://www.npmjs.com/package/cordova-universal-links-plugin-fix\n    var subscribe = fireauth.util.getObjectRef(\n        'universalLinks.subscribe', goog.global);\n    if (typeof subscribe !== 'function') {\n      throw fireauth.CordovaHandler.getError_(\n          'cordova-universal-links-plugin-fix is not installed');\n    }\n    // https://www.npmjs.com/package/cordova-plugin-buildinfo\n    var appIdentifier =\n        fireauth.util.getObjectRef('BuildInfo.packageName', goog.global);\n    if (typeof appIdentifier === 'undefined') {\n      throw fireauth.CordovaHandler.getError_(\n          'cordova-plugin-buildinfo is not installed');\n    }\n    // https://github.com/google/cordova-plugin-browsertab\n    var openUrl = fireauth.util.getObjectRef(\n        'cordova.plugins.browsertab.openUrl', goog.global);\n    if (typeof openUrl !== 'function') {\n      throw fireauth.CordovaHandler.getError_(\n          'cordova-plugin-browsertab is not installed');\n    }\n    // https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-inappbrowser/\n    var openInAppBrowser = fireauth.util.getObjectRef(\n        'cordova.InAppBrowser.open', goog.global);\n    if (typeof openInAppBrowser !== 'function') {\n      throw fireauth.CordovaHandler.getError_(\n          'cordova-plugin-inappbrowser is not installed');\n    }\n  }, function(error) {\n    // If not supported.\n    throw new fireauth.AuthError(fireauth.authenum.Error.CORDOVA_NOT_READY);\n  });\n  return this.isReady_;\n};\n\n\n/**\n * Generates a session ID. Used to prevent session fixation attacks.\n * @param {number} numOfChars The number of characters to generate.\n * @return {string} The generated session ID.\n * @private\n */\nfireauth.CordovaHandler.prototype.generateSessionId_ = function(numOfChars) {\n  var chars = [];\n  var allowedChars =\n      '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';\n  while (numOfChars > 0) {\n    var index = Math.floor(Math.random() * allowedChars.length);\n    chars.push(allowedChars.charAt(index));\n    numOfChars--;\n  }\n  return chars.join('');\n};\n\n\n/**\n * Computes the sha256 hash of a session ID.\n * @param {string} str The string to hash.\n * @return {string} The hashed string.\n * @private\n */\nfireauth.CordovaHandler.prototype.computeSecureHash_ = function(str) {\n  // sha256 the sessionId. This will be passed to the OAuth backend.\n  // When exchanging the Auth code with a firebase ID token, the raw session ID\n  // needs to be provided.\n  var sha256 = new goog.crypt.Sha256();\n  sha256.update(str);\n  return goog.crypt.byteArrayToHex(sha256.digest());\n};\n\n\n/**\n * Waits for popup window to close and time out if the result is unhandled.\n * This is not supported in Cordova.\n * @param {!Window} popupWin The popup window.\n * @param {!function(!fireauth.AuthError)} onError The on error callback.\n * @return {!goog.Promise}\n * @override\n */\nfireauth.CordovaHandler.prototype.startPopupTimeout =\n    function(popupWin, onError, timeoutDuration) {\n  // Not supported operation, check processPopup for details.\n  onError(new fireauth.AuthError(\n      fireauth.authenum.Error.OPERATION_NOT_SUPPORTED));\n  return goog.Promise.resolve();\n};\n\n\n/**\n * Processes the popup request. This is not supported in Cordova.\n * @param {?Window} popupWin The popup window reference.\n * @param {!fireauth.AuthEvent.Type} mode The Auth event type.\n * @param {!fireauth.AuthProvider} provider The Auth provider to sign in with.\n * @param {function()} onInitialize The function to call on initialization.\n * @param {function(*)} onError The function to call on error.\n * @param {string=} opt_eventId The optional event ID.\n * @param {boolean=} opt_alreadyRedirected Whether popup is already redirected\n *     to final destination.\n * @param {?string=} opt_tenantId The optional tenant ID.\n * @return {!goog.Promise} The popup window promise.\n * @override\n */\nfireauth.CordovaHandler.prototype.processPopup = function(\n    popupWin,\n    mode,\n    provider,\n    onInitialize,\n    onError,\n    opt_eventId,\n    opt_alreadyRedirected,\n    opt_tenantId) {\n  // Popups not supported in Cordova as the activity could be destroyed in\n  // some cases. Redirect works better as getRedirectResult can be used as a\n  // fallback to get the result when the activity is detroyed.\n  return goog.Promise.reject(new fireauth.AuthError(\n      fireauth.authenum.Error.OPERATION_NOT_SUPPORTED));\n};\n\n\n/**\n * @return {boolean} Whether the handler will unload the current page on\n *     redirect operations.\n * @override\n */\nfireauth.CordovaHandler.prototype.unloadsOnRedirect = function() {\n  // Does not necessarily unload the page on redirect.\n  return false;\n};\n\n\n/**\n * @return {boolean} Whether the handler should be initialized early.\n * @override\n */\nfireauth.CordovaHandler.prototype.shouldBeInitializedEarly = function() {\n  // Initialize early to detect incoming link. This is not an expensive\n  // operation, unlike embedding an iframe.\n  return true;\n};\n\n\n/**\n * @return {boolean} Whether the sign-in handler in the current environment\n *     has volatile session storage.\n * @override\n */\nfireauth.CordovaHandler.prototype.hasVolatileStorage = function() {\n  // An activity can be destroyed and thereby sessionStorage wiped out.\n  return true;\n};\n\n\n/**\n * Processes the OAuth redirect request. Will resolve when the OAuth response\n * is detected in the incoming link and the corresponding Auth event is\n * triggered.\n * @param {!fireauth.AuthEvent.Type} mode The Auth event type.\n * @param {!fireauth.AuthProvider} provider The Auth provider to sign in with.\n * @param {?string=} opt_eventId The optional event ID.\n * @param {?string=} opt_tenantId The optional tenant ID.\n * @return {!goog.Promise}\n * @override\n */\nfireauth.CordovaHandler.prototype.processRedirect = function(\n    mode,\n    provider,\n    opt_eventId,\n    opt_tenantId) {\n  // If there is already a pending redirect, throw an error.\n  if (this.pendingRedirect_) {\n    return goog.Promise.reject(new fireauth.AuthError(\n        fireauth.authenum.Error.REDIRECT_OPERATION_PENDING));\n  }\n  var self = this;\n  var doc = goog.global.document;\n  // On close timer promise.\n  var onClose = null;\n  // Auth event detection callback;\n  var authEventCallback = null;\n  // On resume (return from the redirect operation).\n  var onResume = null;\n  // On visibility change used to detect return to app in certain versions,\n  // currently iOS.\n  var onVisibilityChange = null;\n  // When the processRedirect promise completes, clean up any remaining\n  // temporary listeners and timers.\n  var cleanup = function() {\n    // Remove current resume listener.\n    if (onResume) {\n      doc.removeEventListener('resume', onResume, false);\n    }\n    // Remove visibility change listener.\n    if (onVisibilityChange) {\n      doc.removeEventListener('visibilitychange', onVisibilityChange, false);\n    }\n    // Cancel onClose promise if not already cancelled.\n    if (onClose) {\n      onClose.cancel();\n    }\n    // Remove Auth event callback.\n    if (authEventCallback) {\n      self.removeAuthEventListener(authEventCallback);\n    }\n    // Clear any pending redirect now that it is completed.\n    self.pendingRedirect_ = null;\n  };\n  // Save the pending redirect promise and clear it on completion.\n  this.pendingRedirect_ = goog.Promise.resolve().then(function() {\n    // Validate provider.\n    // Fail fast in this case.\n    fireauth.AuthProvider.checkIfOAuthSupported(provider);\n    return self.getInitialAuthEvent_();\n  }).then(function() {\n    return self.processRedirectInternal_(\n        mode, provider, opt_eventId, opt_tenantId);\n  }).then(function() {\n    // Wait for result (universal link) before resolving this operation.\n    // This ensures that if the activity is not destroyed, we can still\n    // return the result of this operation.\n    return new goog.Promise(function(resolve, reject) {\n      /**\n       * @param {?fireauth.AuthEvent} event The Auth event detected.\n       * @return {boolean}\n       */\n      authEventCallback = function(event) {\n        // Auth event detected, resolve promise.\n        // Close SFSVC if still open.\n        var closeBrowsertab = fireauth.util.getObjectRef(\n            'cordova.plugins.browsertab.close', goog.global);\n        resolve();\n        // Close the SFSVC if it is still open (iOS 9+).\n        if (typeof closeBrowsertab === 'function') {\n          closeBrowsertab();\n        }\n        // Close inappbrowser emebedded webview in iOS7 and 8 case if still\n        // open.\n        if (self.inAppBrowserRef_ &&\n            typeof self.inAppBrowserRef_['close'] === 'function') {\n          self.inAppBrowserRef_['close']();\n          // Reset reference.\n          self.inAppBrowserRef_ = null;\n        }\n        return false;\n      };\n      // Wait and listen for the operation to complete (Auth event would\n      // trigger).\n      self.addAuthEventListener(authEventCallback);\n      // On resume (return from the redirect operation).\n      onResume = function() {\n        // Already resumed. Do not run again.\n        if (onClose) {\n          return;\n        }\n        // Wait for some time before throwing the error that the flow was\n        // cancelled by the user.\n        onClose = goog.Timer.promise(self.redirectTimeout_).then(function() {\n          // Throw the redirect cancelled by user error.\n          reject(new fireauth.AuthError(\n              fireauth.authenum.Error.REDIRECT_CANCELLED_BY_USER));\n        });\n      };\n      onVisibilityChange = function() {\n        // If app is visible, run onResume. Otherwise, ignore.\n        if (fireauth.util.isAppVisible()) {\n          onResume();\n        }\n      };\n      // Listen to resume event (will trigger when the user returns to the app).\n      doc.addEventListener('resume', onResume, false);\n      // Listen to visibility change. This is used for iOS Cordova Safari 7+.\n      // Does not work in Android stock browser versions older than 4.4.\n      // We rely on resume event in Android as it works reliably in all\n      // versions.\n      if (!fireauth.util.isAndroid()) {\n        doc.addEventListener('visibilitychange', onVisibilityChange, false);\n      }\n    }).thenCatch(function(error) {\n      // Remove any pending partial event.\n      return self.getPartialStoredEvent_().then(function() {\n        throw error;\n      });\n    });\n  }).thenAlways(cleanup);\n  // Return the pending redirect promise.\n  return this.pendingRedirect_;\n};\n\n/**\n * Processes the OAuth redirect request.\n * @param {!fireauth.AuthEvent.Type} mode The Auth event type.\n * @param {!fireauth.AuthProvider} provider The Auth provider to sign in with.\n * @param {?string=} opt_eventId The optional event ID.\n * @param {?string=} opt_tenantId The optional tenant ID.\n * @return {!goog.Promise}\n * @private\n */\nfireauth.CordovaHandler.prototype.processRedirectInternal_ = function(\n    mode,\n    provider,\n    opt_eventId,\n    opt_tenantId) {\n  var self = this;\n  // https://github.com/google/cordova-plugin-browsertab\n  // Opens chrome custom tab in Android if chrome is installed,\n  // SFSafariViewController in iOS if supported.\n  // If the above are not supported, opens the system browser.\n  // Opening a system browser could result in an app being rejected in the App\n  // Store. The only solution here is to use an insecure embedded UIWebView.\n  // This applies to older iOS versions 8 and under.\n  // Generate a random session ID.\n  var sessionId = this.generateSessionId_(\n      fireauth.CordovaHandler.SESSION_ID_TOTAL_CHARS_);\n  // Create the partial Auth event.\n  var event = new fireauth.AuthEvent(\n      mode,\n      opt_eventId,\n      null,\n      sessionId,\n      new fireauth.AuthError(fireauth.authenum.Error.NO_AUTH_EVENT),\n      null,\n      opt_tenantId);\n  // Use buildinfo package to get app metadata.\n  // https://www.npmjs.com/package/cordova-plugin-buildinfo\n  // Get app identifier.\n  var appIdentifier =\n      fireauth.util.getObjectRef('BuildInfo.packageName', goog.global);\n  // initializeAndWait will ensure this does not happen.\n  if (typeof appIdentifier !== 'string') {\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.INVALID_CORDOVA_CONFIGURATION);\n  }\n  // Get app display name.\n  var appDisplayName =\n      fireauth.util.getObjectRef('BuildInfo.displayName', goog.global);\n  // Construct additional params to pass to OAuth handler.\n  var additionalParams  = {};\n  // Append app identifier.\n  if (fireauth.util.isIOS()) {\n    // iOS app.\n    additionalParams['ibi'] = appIdentifier;\n  } else if (fireauth.util.isAndroid()) {\n    // Android app.\n    additionalParams['apn'] = appIdentifier;\n  } else {\n    // This should not happen as Cordova handler should not even be used in this\n    // case.\n    return goog.Promise.reject(new fireauth.AuthError(\n        fireauth.authenum.Error.OPERATION_NOT_SUPPORTED));\n  }\n  // Pass app display name.\n  if (appDisplayName) {\n    additionalParams['appDisplayName'] = appDisplayName;\n  }\n  // Hash the session ID and pass it to additional params.\n  var hashedSessionId = this.computeSecureHash_(sessionId);\n  // Append session ID.\n  additionalParams['sessionId'] = hashedSessionId;\n  // Construct OAuth handler URL.\n  var oauthHelperWidgetUrl =\n      fireauth.iframeclient.IfcHandler.getOAuthHelperWidgetUrl(\n          this.authDomain_,\n          this.apiKey_,\n          this.appName_,\n          mode,\n          provider,\n          null,\n          opt_eventId,\n          this.clientVersion_,\n          additionalParams,\n          this.endpointId_,\n          opt_tenantId,\n          this.emulatorConfig_);\n  // Make sure handler initialized and ready.\n  // This should also ensure all plugins are installed.\n  return this.initializeAndWait().then(function() {\n    // Save partial Auth event.\n    return self.savePartialEventManager_.setAuthEvent(self.storageKey_, event);\n  }).then(function() {\n    // initializeAndWait will ensure this plugin is installed.\n    var isAvailable = /** @type {!function(!function(*))} */ (\n        fireauth.util.getObjectRef(\n            'cordova.plugins.browsertab.isAvailable', goog.global));\n    if (typeof isAvailable !== 'function') {\n      throw new fireauth.AuthError(\n          fireauth.authenum.Error.INVALID_CORDOVA_CONFIGURATION);\n    }\n    var openUrl = null;\n    // Check if browsertab is supported.\n    isAvailable(function(result) {\n      if (result) {\n        // browsertab supported.\n        openUrl = /** @type {!function(string, ...*)} */ (\n            fireauth.util.getObjectRef(\n                'cordova.plugins.browsertab.openUrl', goog.global));\n        if (typeof openUrl !== 'function') {\n          throw new fireauth.AuthError(\n              fireauth.authenum.Error.INVALID_CORDOVA_CONFIGURATION);\n        }\n        // Open OAuth handler.\n        openUrl(oauthHelperWidgetUrl);\n      } else {\n        // browsertab not supported, switch to inappbrowser.\n        openUrl = /** @type {!function(string, string, string=)} */ (\n            fireauth.util.getObjectRef(\n                'cordova.InAppBrowser.open', goog.global));\n        if (typeof openUrl !== 'function') {\n          throw new fireauth.AuthError(\n              fireauth.authenum.Error.INVALID_CORDOVA_CONFIGURATION);\n        }\n        // Open in embedded webview for iOS 7 and 8 as Apple rejects apps that\n        // switch context.\n        // _blank opens an embedded webview.\n        // _system opens the system browser.\n        // _system (opens a system browser) is used as a fallback when\n        // browsertab plugin is unable to open a chromecustomtab or SFSVC.\n        // This has to exclude all iOS older versions where switching to a\n        // browser is frowned upon by Apple and embedding a UIWebView is the\n        // only option but is insecure and deprecated by Google for OAuth\n        // sign-in. This will be applicable in old versions of Android.\n        self.inAppBrowserRef_ = openUrl(\n            oauthHelperWidgetUrl,\n            fireauth.util.isIOS7Or8() ? '_blank' : '_system',\n            'location=yes');\n      }\n    });\n  });\n};\n\n\n/**\n * Dispatches the detected Auth event to all subscribed listeners.\n * @param {!fireauth.AuthEvent} event A detected Auth event.\n * @private\n */\nfireauth.CordovaHandler.prototype.dispatchEvent_ = function(event) {\n  for (var i = 0; i < this.authEventListeners_.length; i++) {\n    try {\n      this.authEventListeners_[i](event);\n    } catch (e) {\n      // If any handler fails, ignore and run next handler.\n    }\n  }\n};\n\n\n/**\n * Resolves the first redirect Auth event and caches it.\n * @return {!goog.Promise<!fireauth.AuthEvent>} A promise that resolves with the\n *     initial Auth event response from a redirect operation. Initializes the\n *     internal Auth event listener which will dispatch Auth events to all\n *     subscribed listeners.\n * @private\n */\nfireauth.CordovaHandler.prototype.getInitialAuthEvent_ = function() {\n  var self = this;\n  if (!this.initialAuthEvent_) {\n    // Cache this result so on next call, it is not triggered again.\n    this.initialAuthEvent_ = this.initializeAndWait().then(function() {\n      return new goog.Promise(function(resolve, reject) {\n        /**\n         * @param {?fireauth.AuthEvent} event The Auth event detected.\n         * @return {boolean}\n         */\n        var authEventCallback = function(event) {\n          resolve(event);\n          // Remove on completion.\n          self.removeAuthEventListener(authEventCallback);\n          return false;\n        };\n        // Listen to Auth events. If resolved, resolve promise.\n        self.addAuthEventListener(authEventCallback);\n        // This should succeed as initializeAndWait should guarantee plugins are\n        // ready.\n        self.setAuthEventListener_();\n      });\n    });\n  }\n  return this.initialAuthEvent_;\n};\n\n\n/**\n * Gets and deletes the current stored partial event from storage.\n * @return {!goog.Promise<?fireauth.AuthEvent>} A promise that resolves with the\n *     stored Auth event.\n * @private\n */\nfireauth.CordovaHandler.prototype.getPartialStoredEvent_ = function() {\n  var event = null;\n  var self = this;\n  // Get any saved partial Auth event.\n  return this.getAndDeletePartialEventManager_.getAuthEvent()\n    .then(function(authEvent) {\n      // Save partial event locally.\n      event = authEvent;\n      // Delete partial event.\n      return self.getAndDeletePartialEventManager_.removeAuthEvent();\n    }).then(function() {\n      // Return the locally saved partial event.\n      return event;\n    });\n};\n\n\n/**\n * Extracts the Auth event pertaining to the incoming URL.\n * @param {!fireauth.AuthEvent} partialEvent The partial Auth event.\n * @param {string} url The incoming universal link.\n * @return {?fireauth.AuthEvent} The resolved Auth event corresponding to the\n *     callback URL. This is null if no event is found.\n * @private\n */\nfireauth.CordovaHandler.prototype.extractAuthEventFromUrl_ =\n    function(partialEvent, url) {\n  // Default no redirect event result.\n  var authEvent = null;\n  // Parse the deep link within the dynamic link URL.\n  var callbackUrl = fireauth.DynamicLink.parseDeepLink(url);\n  // Confirm it is actually a callback URL.\n  // Currently the universal link will be of this format:\n  // https://<AUTH_DOMAIN>/__/auth/callback<OAUTH_RESPONSE>\n  // This is a fake URL but is not intended to take the user anywhere\n  // and just redirect to the app.\n  if (callbackUrl.indexOf('/__/auth/callback') != -1) {\n    // Check if there is an error in the URL.\n    // This mechanism is also used to pass errors back to the app:\n    // https://<AUTH_DOMAIN>/__/auth/callback?firebaseError=<STRINGIFIED_ERROR>\n    var uri = goog.Uri.parse(callbackUrl);\n    // Get the error object corresponding to the stringified error if found.\n    var errorObject = fireauth.util.parseJSON(\n        uri.getParameterValue('firebaseError') || null);\n    var error = typeof errorObject === 'object' ?\n        fireauth.AuthError.fromPlainObject(\n            /** @type {?Object} */ (errorObject)) :\n        null;\n    if (error) {\n      // Construct the full failed Auth event.\n      authEvent = new fireauth.AuthEvent(\n          partialEvent.getType(),\n          partialEvent.getEventId(),\n          null,\n          null,\n          error,\n          null,\n          partialEvent.getTenantId());\n    } else {\n      // Construct the full successful Auth event.\n      authEvent = new fireauth.AuthEvent(\n          partialEvent.getType(),\n          partialEvent.getEventId(),\n          callbackUrl,\n          partialEvent.getSessionId(),\n          null,\n          null,\n          partialEvent.getTenantId());\n    }\n  }\n  return authEvent;\n};\n\n\n/**\n * Sets the internal Auth event listener. This listens to incoming universal\n * links and on detection, repackages them into an Auth event and then\n * dispatches the events in all event listeners.\n * @private\n */\nfireauth.CordovaHandler.prototype.setAuthEventListener_ = function() {\n  // https://github.com/nordnet/cordova-universal-links-plugin-fix\n  var self = this;\n  // Default no redirect event result.\n  var noEvent = new fireauth.AuthEvent(\n      fireauth.AuthEvent.Type.UNKNOWN,\n      null,\n      null,\n      null,\n      new fireauth.AuthError(fireauth.authenum.Error.NO_AUTH_EVENT));\n  var initialResolve = false;\n  // On initialization, if no incoming universal link detected, trigger\n  // no Auth event (no redirect operation previously called) after waiting\n  // for a short period of time.\n  var noEventTimer = goog.Timer.promise(this.initialTimeout_).then(function() {\n    // Delete any pending unhandled event.\n    return self.getPartialStoredEvent_().then(function(event) {\n      // On timeout trigger noEvent if not already resolved in link\n      // subscriber.\n      if (!initialResolve) {\n        self.dispatchEvent_(noEvent);\n      }\n    });\n  });\n  // No event name needed, subscribe to all incoming universal links.\n  var universalLinkCb = function(eventData) {\n    initialResolve = true;\n    // Cancel no event timer.\n    if (noEventTimer) {\n      noEventTimer.cancel();\n    }\n    // Incoming link detected.\n    // Check for any stored partial event.\n    self.getPartialStoredEvent_().then(function(event) {\n      // Initialize to an unknown event.\n      var authEvent = noEvent;\n      // Confirm OAuth response included.\n      if (event && eventData && eventData['url']) {\n        // Construct complete event. Default to unknown event if none found.\n        authEvent = self.extractAuthEventFromUrl_(event, eventData['url']) ||\n            noEvent;\n      }\n      // Dispatch Auth event.\n      self.dispatchEvent_(authEvent);\n    });\n  };\n  // iOS 7 or 8 custom URL schemes.\n  // This is also the current default behavior for iOS 9+.\n  // For this to work, cordova-plugin-customurlscheme needs to be installed.\n  // https://github.com/EddyVerbruggen/Custom-URL-scheme\n  // Do not overwrite the existing developer's URL handler.\n  var existingHandlerOpenURL = goog.global['handleOpenURL'];\n  goog.global['handleOpenURL'] = function(url) {\n    var appIdentifier =\n        fireauth.util.getObjectRef('BuildInfo.packageName', goog.global);\n    // Apply case insensitive match. While bundle IDs are case sensitive,\n    // when creating a new app, Apple verifies the Bundle ID using\n    // case-insensitive search. So it is not possible that an app in the app\n    // store try to impersonate another one by lower/upper casing characters.\n    if (url.toLowerCase().indexOf(appIdentifier.toLowerCase() + '://') == 0) {\n      universalLinkCb({\n        'url': url\n      });\n    }\n    // Call the developer's handler if it is present.\n    if (typeof existingHandlerOpenURL === 'function') {\n      try {\n        existingHandlerOpenURL(url);\n      } catch(e) {\n        // This doesn't swallow the error but also does not interrupt the flow.\n        console.error(e);\n      }\n    }\n  };\n  fireauth.UniversalLinkSubscriber.getInstance().subscribe(universalLinkCb);\n};\n\n\n/**\n * @param {!function(?fireauth.AuthEvent):boolean} listener The Auth event\n *     listener to add.\n * @override\n */\nfireauth.CordovaHandler.prototype.addAuthEventListener = function(listener) {\n  // TODO: consider creating an abstract base class that OAuth handlers\n  // extend with add, remove Auth event listeners and dispatcher methods.\n  this.authEventListeners_.push(listener);\n  // Set internal listener to Auth events. This will be ignored on subsequent\n  // calls.\n  this.getInitialAuthEvent_().thenCatch(function(error) {\n    // Suppress this error as it should be caught through other actionable\n    // public methods.\n    // This would typically happen on invalid Cordova setup, when the OAuth\n    // plugins are not installed. This should still trigger the Auth event\n    // as developers are not forced to use OAuth sign-in in their Cordova app.\n    // This is needed for onAuthStateChanged listener to trigger initially.\n    if (error.code === 'auth/invalid-cordova-configuration') {\n      var noEvent = new fireauth.AuthEvent(\n          fireauth.AuthEvent.Type.UNKNOWN,\n          null,\n          null,\n          null,\n          new fireauth.AuthError(fireauth.authenum.Error.NO_AUTH_EVENT));\n      listener(noEvent);\n    }\n  });\n};\n\n\n/**\n * @param {!function(?fireauth.AuthEvent):boolean} listener The Auth event\n *     listener to remove.\n * @override\n */\nfireauth.CordovaHandler.prototype.removeAuthEventListener = function(listener) {\n  goog.array.removeAllIf(this.authEventListeners_, function(ele) {\n    return ele == listener;\n  });\n};\n\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines the fireauth.storage.PendingRedirectManager class which\n * provides utilities to store, retrieve and delete the state of whether there\n * is a pending redirect operation previously triggered.\n */\n\ngoog.provide('fireauth.storage.PendingRedirectManager');\n\ngoog.require('fireauth.authStorage');\n\n\n/**\n * Defines the pending redirect storage manager. It provides methods\n * to store, retrieve and delete the state of whether there is a pending\n * redirect operation previously triggered.\n * @param {string} appId The Auth state's application ID.\n * @param {?fireauth.authStorage.Manager=} opt_manager The underlying storage\n *     manager to use. If none is provided, the default global instance is used.\n * @constructor @struct @final\n */\nfireauth.storage.PendingRedirectManager = function(appId, opt_manager) {\n  /** @const @private{string} appId The Auth state's application ID. */\n  this.appId_ = appId;\n  /**\n   * @const @private{!fireauth.authStorage.Manager} The underlying storage\n   *     manager.\n   */\n  this.manager_ = opt_manager || fireauth.authStorage.Manager.getInstance();\n};\n\n\n/**\n * @const @private{!string} The pending redirect flag.\n */\nfireauth.storage.PendingRedirectManager.PENDING_FLAG_ = 'pending';\n\n\n/**\n * @const @private{!fireauth.authStorage.Key} The pending redirect status\n *     storage identifier key.\n */\nfireauth.storage.PendingRedirectManager.PENDING_REDIRECT_KEY_ = {\n  name: 'pendingRedirect',\n  persistent: fireauth.authStorage.Persistence.SESSION\n};\n\n\n/**\n * Stores the pending redirect operation for the provided application ID.\n * @return {!goog.Promise<void>} A promise that resolves on success.\n */\nfireauth.storage.PendingRedirectManager.prototype.setPendingStatus =\n    function() {\n  return this.manager_.set(\n      fireauth.storage.PendingRedirectManager.PENDING_REDIRECT_KEY_,\n      fireauth.storage.PendingRedirectManager.PENDING_FLAG_,\n      this.appId_);\n};\n\n\n/**\n * Removes the stored pending redirect operation for provided app ID.\n * @return {!goog.Promise<void>} A promise that resolves on success.\n */\nfireauth.storage.PendingRedirectManager.prototype.removePendingStatus =\n    function() {\n  return this.manager_.remove(\n      fireauth.storage.PendingRedirectManager.PENDING_REDIRECT_KEY_,\n      this.appId_);\n};\n\n\n/**\n * @return {!goog.Promise<boolean>} A promise that resolves with a boolean\n *     whether there is a pending redirect operaiton for the provided app ID.\n */\nfireauth.storage.PendingRedirectManager.prototype.getPendingStatus =\n    function() {\n  return this.manager_.get(\n      fireauth.storage.PendingRedirectManager.PENDING_REDIRECT_KEY_,\n      this.appId_).then(function(response) {\n        return response ==\n            fireauth.storage.PendingRedirectManager.PENDING_FLAG_;\n      });\n};\n","/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines the Auth event manager instance.\n */\n\ngoog.provide('fireauth.AuthEventHandler');\ngoog.provide('fireauth.AuthEventManager');\ngoog.provide('fireauth.AuthEventManager.Result');\ngoog.provide('fireauth.PopupAuthEventProcessor');\ngoog.provide('fireauth.RedirectAuthEventProcessor');\n\ngoog.require('fireauth.AuthCredential');\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.AuthEvent');\ngoog.require('fireauth.CordovaHandler');\ngoog.require('fireauth.authenum.Error');\ngoog.require('fireauth.constants');\ngoog.require('fireauth.iframeclient.IfcHandler');\ngoog.require('fireauth.storage.PendingRedirectManager');\ngoog.require('fireauth.util');\ngoog.require('goog.Promise');\ngoog.require('goog.Timer');\ngoog.require('goog.array');\n\n\n/**\n * Initializes the Auth event manager which provides the mechanism to connect\n * external Auth events to their corresponding listeners.\n * @param {string} authDomain The Firebase authDomain used to determine the\n *     OAuth helper page domain.\n * @param {string} apiKey The API key for sending backend Auth requests.\n * @param {string} appName The App ID for the Auth instance that triggered this\n *     request.\n *  @param {?fireauth.constants.EmulatorSettings=} emulatorConfig The emulator\n *     configuration.\n * @constructor\n */\nfireauth.AuthEventManager = function(authDomain, apiKey, appName, emulatorConfig) {\n  /**\n   * @private {!Object<string, boolean>} The map of processed auth event IDs.\n   */\n  this.processedEvents_ = {};\n  /** @private {number} The last saved processed event time in milliseconds. */\n  this.lastProcessedEventTime_ = 0;\n  /** @private {string} The Auth domain. */\n  this.authDomain_ = authDomain;\n  /** @private {string} The browser API key. */\n  this.apiKey_ = apiKey;\n  /** @private {string} The App name. */\n  this.appName_ = appName;\n  /** @private @const {?fireauth.constants.EmulatorSettings|undefined} The emulator config. */\n  this.emulatorConfig_ = emulatorConfig;\n  /**\n   * @private {!Array<!fireauth.AuthEventHandler>} List of subscribed handlers.\n   */\n  this.subscribedHandlers_ = [];\n  /**\n   * @private {boolean} Whether the Auth event manager instance is initialized.\n   */\n  this.initialized_ = false;\n  /** @private {function(?fireauth.AuthEvent)} The Auth event handler. */\n  this.authEventHandler_ = goog.bind(this.handleAuthEvent_, this);\n  /** @private {!fireauth.RedirectAuthEventProcessor} The redirect event\n   *      processor. */\n  this.redirectAuthEventProcessor_ =\n      new fireauth.RedirectAuthEventProcessor(this);\n  /** @private {!fireauth.PopupAuthEventProcessor} The popup event processor. */\n  this.popupAuthEventProcessor_ = new fireauth.PopupAuthEventProcessor(this);\n  /**\n   * @private {!fireauth.storage.PendingRedirectManager} The pending redirect\n   *     storage manager instance.\n   */\n  this.pendingRedirectStorageManager_ =\n      new fireauth.storage.PendingRedirectManager(\n          fireauth.AuthEventManager.getKey_(this.apiKey_, this.appName_));\n\n  /**\n   * @private {!Object.<!fireauth.AuthEvent.Type, !fireauth.AuthEventProcessor>}\n   *     Map containing Firebase event processor instances keyed by event type.\n   */\n  this.typeToManager_ = {};\n  this.typeToManager_[fireauth.AuthEvent.Type.UNKNOWN] =\n      this.redirectAuthEventProcessor_;\n  this.typeToManager_[fireauth.AuthEvent.Type.SIGN_IN_VIA_REDIRECT] =\n      this.redirectAuthEventProcessor_;\n  this.typeToManager_[fireauth.AuthEvent.Type.LINK_VIA_REDIRECT] =\n      this.redirectAuthEventProcessor_;\n  this.typeToManager_[fireauth.AuthEvent.Type.REAUTH_VIA_REDIRECT] =\n      this.redirectAuthEventProcessor_;\n  this.typeToManager_[fireauth.AuthEvent.Type.SIGN_IN_VIA_POPUP] =\n      this.popupAuthEventProcessor_;\n  this.typeToManager_[fireauth.AuthEvent.Type.LINK_VIA_POPUP] =\n      this.popupAuthEventProcessor_;\n  this.typeToManager_[fireauth.AuthEvent.Type.REAUTH_VIA_POPUP] =\n      this.popupAuthEventProcessor_;\n  /**\n   * @private {!fireauth.OAuthSignInHandler} The OAuth sign in handler depending\n   *     on the current environment.\n   */\n  this.oauthSignInHandler_ =\n      fireauth.AuthEventManager.instantiateOAuthSignInHandler(\n        this.authDomain_,\n        this.apiKey_,\n        this.appName_,\n        firebase.SDK_VERSION || null,\n        fireauth.constants.clientEndpoint,\n        this.emulatorConfig_);\n};\n\n\n/**\n * @const {number} The number of milliseconds since the last processed\n *     event before the event duplication cache is cleared. This is currently\n *     10 minutes.\n */\nfireauth.AuthEventManager.EVENT_DUPLICATION_CACHE_DURATION = 10 * 60 * 1000;\n\n\n/**\n * @return {!fireauth.RedirectAuthEventProcessor} The redirect event processor.\n */\nfireauth.AuthEventManager.prototype.getRedirectAuthEventProcessor = function() {\n  return this.redirectAuthEventProcessor_;\n};\n\n\n/** @return {!fireauth.PopupAuthEventProcessor} The popup event processor. */\nfireauth.AuthEventManager.prototype.getPopupAuthEventProcessor = function() {\n  return this.popupAuthEventProcessor_;\n};\n\n\n/**\n * Instantiates an OAuth sign-in handler depending on the current environment\n * and returns it.\n * @param {string} authDomain The Firebase authDomain used to determine the\n *     OAuth helper page domain.\n * @param {string} apiKey The API key for sending backend Auth requests.\n * @param {string} appName The App ID for the Auth instance that triggered this\n *     request.\n * @param {?string} version The SDK client version.\n * @param {?string=} opt_endpointId The endpoint ID (staging, test Gaia, etc).\n *  @param {?fireauth.constants.EmulatorSettings=} emulatorConfig The emulator\n *     configuration.\n * @return {!fireauth.OAuthSignInHandler} The OAuth sign in handler depending\n *     on the current environment.\n */\nfireauth.AuthEventManager.instantiateOAuthSignInHandler =\n    function(authDomain, apiKey, appName, version, opt_endpointId, emulatorConfig) {\n  // This assumes that android/iOS file environment must be a Cordova\n  // environment which is not true. This is the best way currently available\n  // to instantiate this synchronously without waiting for checkIfCordova to\n  // resolve. If it is determined that the Cordova was falsely detected, it will\n  // be caught via actionable public popup and redirect methods.\n  return fireauth.util.isAndroidOrIosCordovaScheme() ?\n      new fireauth.CordovaHandler(\n        authDomain,\n        apiKey,\n        appName,\n        version,\n        undefined,\n        undefined,\n        opt_endpointId,\n        emulatorConfig) :\n      new fireauth.iframeclient.IfcHandler(\n        authDomain,\n        apiKey,\n        appName,\n        version,\n        opt_endpointId,\n        emulatorConfig);\n};\n\n\n/** Reset iframe. This will require reinitializing it.*/\nfireauth.AuthEventManager.prototype.reset = function() {\n  // Reset initialized status. This will force a popup request to re-initialize\n  // the iframe.\n  this.initialized_ = false;\n  // Remove any previous existing Auth event listener.\n  this.oauthSignInHandler_.removeAuthEventListener(this.authEventHandler_);\n  // Construct a new instance of OAuth sign in handler.\n\n  this.oauthSignInHandler_ =\n      fireauth.AuthEventManager.instantiateOAuthSignInHandler(\n        this.authDomain_,\n        this.apiKey_,\n        this.appName_,\n        firebase.SDK_VERSION || null,\n        null,\n        this.emulatorConfig_);\n  this.processedEvents_ = {};\n};\n\n\n/**\n * Clears the cached redirect result as long as there is no pending redirect\n * result being processed. Unrecoverable errors will not be cleared.\n */\nfireauth.AuthEventManager.prototype.clearRedirectResult = function() {\n  this.redirectAuthEventProcessor_.clearRedirectResult();\n};\n\n\n/**\n * @typedef {{\n *   user: (?fireauth.AuthUser|undefined),\n *   credential: (?fireauth.AuthCredential|undefined),\n *   operationType: (?string|undefined),\n *   additionalUserInfo: (?fireauth.AdditionalUserInfo|undefined)\n * }}\n */\nfireauth.AuthEventManager.Result;\n\n\n/**\n * Whether to enable Auth event manager subscription.\n * @const {boolean}\n */\nfireauth.AuthEventManager.ENABLED = true;\n\n\n/**\n * Initializes the ifchandler and add Auth event listener on it.\n * @return {!goog.Promise} The promise that resolves when the iframe is ready.\n */\nfireauth.AuthEventManager.prototype.initialize = function() {\n  var self = this;\n  // Initialize once.\n  if (!this.initialized_) {\n    this.initialized_ = true;\n    // Listen to Auth events on iframe.\n    this.oauthSignInHandler_.addAuthEventListener(this.authEventHandler_);\n  }\n  var previousOauthSignInHandler = this.oauthSignInHandler_;\n  // This should initialize ifchandler underneath.\n  // Return on OAuth handler ready promise.\n  // Check for error in ifcHandler used to embed the iframe.\n  return this.oauthSignInHandler_.initializeAndWait()\n      .thenCatch(function(error) {\n        // Force ifchandler to reinitialize on retrial.\n        if (self.oauthSignInHandler_ == previousOauthSignInHandler) {\n          // If a new OAuth sign in handler was already created, do not reset.\n          self.reset();\n        }\n        throw error;\n      });\n};\n\n\n/**\n * Called after it is determined that there is no pending redirect result.\n * Will populate the redirect result if it is guaranteed to be null and will\n * force an early initialization of the OAuth sign in handler if the\n * environment requires it.\n * @private\n */\nfireauth.AuthEventManager.prototype.initializeWithNoPendingRedirectResult_ =\n    function() {\n  var self = this;\n  // Check if the OAuth sign in handler should be initialized early in all\n  // cases.\n  if (this.oauthSignInHandler_.shouldBeInitializedEarly()) {\n    this.initialize().thenCatch(function(error) {\n      // Current environment was falsely detected as Cordova, trigger a fake\n      // Auth event to notify getRedirectResult that operation is not supported.\n      var notSupportedEvent = new fireauth.AuthEvent(\n          fireauth.AuthEvent.Type.UNKNOWN,\n          null,\n          null,\n          null,\n          new fireauth.AuthError(\n              fireauth.authenum.Error.OPERATION_NOT_SUPPORTED));\n      if (fireauth.AuthEventManager.isCordovaFalsePositive_(\n          /** @type {?fireauth.AuthError} */ (error))) {\n        self.handleAuthEvent_(notSupportedEvent);\n      }\n    });\n  }\n  // For environments where storage is volatile, we can't determine that\n  // there is no pending redirect response. This is true in Cordova\n  // where an activity would be destroyed in some cases and the\n  // sessionStorage is lost.\n  if (!this.oauthSignInHandler_.hasVolatileStorage()) {\n    // Since there is no redirect result, it is safe to default to empty\n    // redirect result instead of blocking on this.\n    // The downside here is that on iOS devices, calling signInWithPopup\n    // after getRedirectResult resolves and the iframe does not finish\n    // loading, the popup event propagating to the iframe would not be\n    // detected. This is because in iOS devices, storage events only trigger\n    // in iframes but are not actually saved in web storage. The iframe must\n    // be embedded and ready before the storage event propagates. Otherwise\n    // it won't be detected.\n    this.redirectAuthEventProcessor_.defaultToEmptyResponse();\n  }\n};\n\n\n/**\n * Subscribes an Auth event handler to list of handlers.\n * @param {!fireauth.AuthEventHandler} handler The instance to subscribe.\n */\nfireauth.AuthEventManager.prototype.subscribe = function(handler) {\n  if (!goog.array.contains(this.subscribedHandlers_, handler)) {\n    this.subscribedHandlers_.push(handler);\n  }\n  if (this.initialized_) {\n    return;\n  }\n  var self = this;\n  // Check pending redirect status.\n  this.pendingRedirectStorageManager_.getPendingStatus()\n      .then(function(status) {\n    // Pending redirect detected.\n    if (status) {\n      // Remove pending status and initialize.\n      self.pendingRedirectStorageManager_.removePendingStatus()\n          .then(function() {\n            self.initialize().thenCatch(function(error) {\n              // Current environment was falsely detected as Cordova, trigger a\n              // fake Auth event to notify getRedirectResult that operation is\n              // not supported.\n              var notSupportedEvent = new fireauth.AuthEvent(\n                 fireauth.AuthEvent.Type.UNKNOWN,\n                 null,\n                 null,\n                 null,\n                 new fireauth.AuthError(\n                     fireauth.authenum.Error.OPERATION_NOT_SUPPORTED));\n              if (fireauth.AuthEventManager.isCordovaFalsePositive_(\n                  /** @type {?fireauth.AuthError} */ (error))) {\n                self.handleAuthEvent_(notSupportedEvent);\n              }\n            });\n          });\n    } else {\n      // No previous redirect, default to empty response.\n      self.initializeWithNoPendingRedirectResult_();\n    }\n  }).thenCatch(function(error) {\n    // Error checking pending status, default to empty response.\n    self.initializeWithNoPendingRedirectResult_();\n  });\n};\n\n\n/**\n * @param {!fireauth.AuthEventHandler} handler The possible subscriber.\n * @return {boolean} Whether the handle is subscribed.\n */\nfireauth.AuthEventManager.prototype.isSubscribed = function(handler) {\n  return goog.array.contains(this.subscribedHandlers_, handler);\n};\n\n\n/**\n * Unsubscribes an Auth event handler to list of handlers.\n * @param {!fireauth.AuthEventHandler} handler The instance to unsubscribe.\n */\nfireauth.AuthEventManager.prototype.unsubscribe = function(handler) {\n  goog.array.removeAllIf(this.subscribedHandlers_, function(ele) {\n    return ele == handler;\n  });\n};\n\n\n/**\n * @param {?fireauth.AuthEvent} authEvent External Auth event to check.\n * @return {boolean} Whether the event was previously processed.\n * @private\n */\nfireauth.AuthEventManager.prototype.hasProcessedAuthEvent_ =\n    function(authEvent) {\n  // Prevent duplicate event tracker from growing too large.\n  if (Date.now() - this.lastProcessedEventTime_ >=\n      fireauth.AuthEventManager.EVENT_DUPLICATION_CACHE_DURATION) {\n    this.processedEvents_ = {};\n    this.lastProcessedEventTime_ = 0;\n  }\n  if (authEvent && authEvent.getUid() &&\n      this.processedEvents_.hasOwnProperty(authEvent.getUid())) {\n    // If event is already processed, ignore it.\n    return true;\n  }\n  return false;\n};\n\n\n/**\n * Saves the provided event uid to prevent processing duplication.\n * @param {?fireauth.AuthEvent} authEvent External Auth event to track in\n *     processed list of events.\n * @private\n */\nfireauth.AuthEventManager.prototype.saveProcessedAuthEvent_ =\n    function(authEvent) {\n  if (authEvent &&\n      (authEvent.getSessionId() || authEvent.getEventId())) {\n    // Save processed event ID. We keep the cache for 10 minutes to prevent it\n    // from growing too large.\n    this.processedEvents_[\n        /** @type {string} */ (authEvent.getUid())] = true;\n    // Save last processing time.\n    this.lastProcessedEventTime_ = Date.now();\n  }\n};\n\n\n/**\n * Handles external Auth event detected by the OAuth sign-in handler.\n * @param {?fireauth.AuthEvent} authEvent External Auth event detected by\n *     iframe.\n * @return {boolean} Whether the event found an appropriate owner that can\n *     handle it. This signals to the OAuth helper iframe that the event is safe\n *     to delete.\n * @private\n */\nfireauth.AuthEventManager.prototype.handleAuthEvent_ = function(authEvent) {\n  // This should not happen as fireauth.iframe.AuthRelay will not send null\n  // events.\n  if (!authEvent) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INVALID_AUTH_EVENT);\n  }\n  if (this.hasProcessedAuthEvent_(authEvent)) {\n    // If event is already processed, ignore it.\n    return false;\n  }\n  // Initialize event processed status to false. When set to false, the event is\n  // not clear to delete in the OAuth helper iframe as the owner of this event\n  // could be a user in another tab.\n  var processed = false;\n  // Lookup a potential handler for this event.\n  for (var i = 0; i < this.subscribedHandlers_.length; i++) {\n    var potentialHandler = this.subscribedHandlers_[i];\n    if (potentialHandler.canHandleAuthEvent(\n        authEvent.getType(), authEvent.getEventId())) {\n      var eventManager = this.typeToManager_[authEvent.getType()];\n      if (eventManager) {\n        eventManager.processAuthEvent(authEvent, potentialHandler);\n        // Prevent events with event IDs or session IDs from duplicate\n        // processing.\n        this.saveProcessedAuthEvent_(authEvent);\n      }\n      // Event has been processed, free to clear in OAuth helper.\n      processed = true;\n      break;\n    }\n  }\n  // If no redirect response ready yet, default to an empty response.\n  this.redirectAuthEventProcessor_.defaultToEmptyResponse();\n  // Notify iframe of processed status.\n  return processed;\n};\n\n\n/**\n * The popup promise timeout delay with units in ms between the time the iframe\n * is ready (successfully embedded on the page) and the time the popup Auth\n * event is detected in the parent container.\n * @const {!fireauth.util.Delay}\n * @private\n */\nfireauth.AuthEventManager.POPUP_TIMEOUT_MS_ =\n    new fireauth.util.Delay(2000, 10000);\n\n\n/**\n * The redirect promise timeout delay with units in ms. Unlike the popup\n * timeout, this covers the entire duration from start to getRedirectResult\n * resolution.\n * @const {!fireauth.util.Delay}\n * @private\n */\nfireauth.AuthEventManager.REDIRECT_TIMEOUT_MS_ =\n    new fireauth.util.Delay(30000, 60000);\n\n\n/**\n * Returns the redirect result. If coming back from a successful redirect sign\n * in, will resolve to the signed in user. If coming back from an unsuccessful\n * redirect sign, will reject with the proper error. If no redirect operation\n * called, resolves with null.\n * @return {!goog.Promise<!fireauth.AuthEventManager.Result>}\n */\nfireauth.AuthEventManager.prototype.getRedirectResult = function() {\n  return this.redirectAuthEventProcessor_.getRedirectResult();\n};\n\n\n/**\n * Processes the popup request. The popup instance must be provided externally\n * and on error, the requestor must close the window.\n * @param {?Window} popupWin The popup window reference.\n * @param {!fireauth.AuthEvent.Type} mode The Auth event type.\n * @param {!fireauth.AuthProvider} provider The Auth provider to sign in with.\n * @param {string=} opt_eventId The optional event ID.\n * @param {boolean=} opt_alreadyRedirected Whether popup is already redirected\n *     to final destination.\n * @param {?string=} opt_tenantId The optional tenant ID.\n * @return {!goog.Promise} The popup window promise.\n */\nfireauth.AuthEventManager.prototype.processPopup =\n    function(popupWin, mode, provider, opt_eventId, opt_alreadyRedirected,\n             opt_tenantId) {\n  var self = this;\n  return this.oauthSignInHandler_.processPopup(\n      popupWin,\n      mode,\n      provider,\n      // On initialization, add Auth event listener if not already added.\n      function() {\n        if (!self.initialized_) {\n          self.initialized_ = true;\n          // Listen to Auth events on iframe.\n          self.oauthSignInHandler_.addAuthEventListener(self.authEventHandler_);\n        }\n      },\n      // On error, reset to force re-initialization on retrial.\n      function(error) {\n        self.reset();\n      },\n      opt_eventId,\n      opt_alreadyRedirected,\n      opt_tenantId);\n};\n\n\n/**\n * @param {?fireauth.AuthError} error The error to check for Cordova false\n *     positive.\n * @return {boolean} Whether the current environment was falsely identified as\n *     Cordova.\n * @private\n */\nfireauth.AuthEventManager.isCordovaFalsePositive_ = function(error) {\n  if (error && error['code'] == 'auth/cordova-not-ready') {\n    return true;\n  }\n  return false;\n};\n\n\n/**\n * Processes the redirect request.\n * @param {!fireauth.AuthEvent.Type} mode The Auth event type.\n * @param {!fireauth.AuthProvider} provider The Auth provider to sign in with.\n * @param {string=} opt_eventId The optional event ID.\n * @param {?string=} opt_tenantId The optional tenant ID.\n * @return {!goog.Promise}\n */\nfireauth.AuthEventManager.prototype.processRedirect =\n    function(mode, provider, opt_eventId, opt_tenantId) {\n  var self = this;\n  var error;\n  // Save pending status first.\n  return this.pendingRedirectStorageManager_.setPendingStatus()\n    .then(function() {\n      // Try to redirect.\n      return self.oauthSignInHandler_.processRedirect(\n          mode, provider, opt_eventId, opt_tenantId)\n        .thenCatch(function(e) {\n          if (fireauth.AuthEventManager.isCordovaFalsePositive_(\n              /** @type {?fireauth.AuthError} */ (e))) {\n            throw new fireauth.AuthError(\n                fireauth.authenum.Error.OPERATION_NOT_SUPPORTED);\n          }\n          // On failure, remove pending status and rethrow the error.\n          error = e;\n          return self.pendingRedirectStorageManager_.removePendingStatus()\n            .then(function() {\n              throw error;\n            });\n        })\n        .then(function() {\n          // Resolve, if the OAuth handler unloads the page on redirect.\n          if (!self.oauthSignInHandler_.unloadsOnRedirect()) {\n            // Relevant to Cordova case, will not matter in web case where\n            // browser redirects.\n            // In Cordova, the activity could still be running in the background\n            // so we need to wait for getRedirectResult to resolve before\n            // resolving this current promise.\n            // Otherwise, if the activity is destroyed, getRedirectResult would\n            // be used.\n            // At this point, authEvent should have been triggered.\n            // When this promise resolves, the developer should be able to\n            // call getRedirectResult to get the result of this operation.\n            // Remove pending status as result should be resolved.\n            return self.pendingRedirectStorageManager_.removePendingStatus()\n                .then(function() {\n                  // Ensure redirect result ready before resolving.\n                  return self.getRedirectResult();\n                }).then(function(result) {\n                  // Do nothing. Developer expected to call getRedirectResult to\n                  // get result.\n                }).thenCatch(function(error) {\n                  // Do nothing. Developer expected to call getRedirectResult to\n                  // get result.\n                });\n          } else {\n            // For environments that will unload the page on redirect, keep\n            // the promise pending on success. This makes it easier to reuse\n            // the same code for Cordova environment and browser environment.\n            // The developer can always add getRedirectResult on promise\n            // resolution and expect that when it runs, the redirect operation\n            // was completed.\n            return new goog.Promise(function(resolve, reject) {\n              // Keep this pending.\n            });\n          }\n        });\n    });\n};\n\n\n/**\n * Waits for popup window to close. When closed start timeout listener for popup\n * pending promise. If in the process, it was detected that the iframe does not\n * support web storage, the popup is closed and the web storage unsupported\n * error is thrown.\n * @param {!fireauth.AuthEventHandler} owner The owner of the event.\n * @param {!fireauth.AuthEvent.Type} mode The Auth event type.\n * @param {!Window} popupWin The popup window.\n * @param {?string=} opt_eventId The event ID.\n * @return {!goog.Promise}\n */\nfireauth.AuthEventManager.prototype.startPopupTimeout =\n    function(owner, mode, popupWin, opt_eventId) {\n  return this.oauthSignInHandler_.startPopupTimeout(\n      popupWin,\n      // On popup error such as popup closed by user or web storage not\n      // supported.\n      function(error) {\n        // Notify owner of the error.\n        owner.resolvePendingPopupEvent(mode, null, error, opt_eventId);\n      },\n      fireauth.AuthEventManager.POPUP_TIMEOUT_MS_.get());\n};\n\n\n\n/**\n * @private {!Object.<string, !fireauth.AuthEventManager>} Map containing\n *     Firebase event manager instances keyed by Auth event manager ID.\n */\nfireauth.AuthEventManager.manager_ = {};\n\n\n/**\n * The separator for manager keys to concatenate app name and apiKey.\n * @const {string}\n * @private\n */\nfireauth.AuthEventManager.KEY_SEPARATOR_ = ':';\n\n\n/**\n * @param {string} apiKey The API key for sending backend Auth requests.\n * @param {string} appName The Auth instance that initiated the Auth event.\n   * @param {?fireauth.constants.EmulatorSettings=} emulatorConfig The emulator\n   *   configuration.\n   * @return {string} The key identifying the Auth event manager instance.\n   * @private\n   */\nfireauth.AuthEventManager.getKey_ = function(apiKey, appName, emulatorConfig) {\n  var key = apiKey + fireauth.AuthEventManager.KEY_SEPARATOR_ + appName;\n  if (emulatorConfig) {\n    key = key + fireauth.AuthEventManager.KEY_SEPARATOR_ + emulatorConfig.url;\n  }\n  return key;\n}\n\n\n/**\n * @param {string} authDomain The Firebase authDomain used to determine the\n *     OAuth helper page domain.\n * @param {string} apiKey The API key for sending backend Auth requests.\n * @param {string} appName The Auth instance that initiated the Auth event\n *     manager.\n * @param {?fireauth.constants.EmulatorSettings=} emulatorConfig The emulator\n *     configuration.\n * @return {!fireauth.AuthEventManager} the requested manager instance.\n */\nfireauth.AuthEventManager.getManager = function (authDomain, apiKey, appName, emulatorConfig) {\n  // Construct storage key.\n  var key = fireauth.AuthEventManager.getKey_(\n    apiKey,\n    appName,\n    emulatorConfig\n  );\n  if (!fireauth.AuthEventManager.manager_[key]) {\n    fireauth.AuthEventManager.manager_[key] =\n      new fireauth.AuthEventManager(\n        authDomain,\n        apiKey,\n        appName,\n        emulatorConfig\n      );\n  }\n  return fireauth.AuthEventManager.manager_[key];\n};\n\n\n/**\n * The interface that represents a specific type of Auth event processor.\n * @interface\n */\nfireauth.AuthEventProcessor = function() {};\n\n\n/**\n * Completes the processing of an external Auth event detected by the embedded\n * iframe.\n * @param {?fireauth.AuthEvent} authEvent External Auth event detected by\n *     iframe.\n * @param {!fireauth.AuthEventHandler} owner The owner of the event.\n * @return {!goog.Promise<undefined>}\n */\nfireauth.AuthEventProcessor.prototype.processAuthEvent =\n    function(authEvent, owner) {};\n\n\n\n/**\n * Redirect Auth event manager.\n * @param {!fireauth.AuthEventManager} manager The parent Auth event manager.\n * @constructor\n * @implements {fireauth.AuthEventProcessor}\n */\nfireauth.RedirectAuthEventProcessor = function(manager) {\n  this.manager_ = manager;\n  // Only one redirect result can be tracked on first load.\n  /**\n   * @private {?function():!goog.Promise<!fireauth.AuthEventManager.Result>}\n   *     Redirect result resolver. This will be used to resolve the\n   *     getRedirectResult promise. When the redirect result is obtained, this\n   *     field will be set.\n   */\n  this.redirectedUserPromise_ = null;\n  /**\n   * @private {!Array<function(!fireauth.AuthEventManager.Result)>} Pending\n   *     promise redirect resolver. When the redirect result is obtained and the\n   *     user is detected, this will be called.\n   */\n  this.redirectResolve_ = [];\n  /**\n   * @private {!Array<function(*)>} Pending Promise redirect rejecter. When the\n   *     redirect result is obtained and an error is detected, this will be\n   *     called.\n   */\n  this.redirectReject_ = [];\n  /** @private {?goog.Promise} Pending timeout promise for redirect. */\n  this.redirectTimeoutPromise_ = null;\n  /**\n   * @private {boolean} Whether redirect result is resolved. This is true\n   *     when a valid Auth event has been triggered.\n   */\n  this.redirectResultResolved_ = false;\n  /**\n   * @private {boolean} Whether an unrecoverable error was detected. This\n   *     includes web storage unsupported or operation not allowed errors.\n   */\n  this.unrecoverableErrorDetected_ = false;\n};\n\n\n/** Reset any previous redirect result. */\nfireauth.RedirectAuthEventProcessor.prototype.reset = function() {\n  // Reset to allow override getRedirectResult. This is relevant for Cordova\n  // environment where redirect events do not necessarily unload the current\n  // page.\n  this.redirectedUserPromise_ = null;\n  if (this.redirectTimeoutPromise_) {\n    this.redirectTimeoutPromise_.cancel();\n    this.redirectTimeoutPromise_ = null;\n  }\n};\n\n\n/**\n * Completes the processing of an external Auth event detected by the embedded\n * iframe.\n * @param {?fireauth.AuthEvent} authEvent External Auth event detected by\n *     iframe.\n * @param {!fireauth.AuthEventHandler} owner The owner of the event.\n * @return {!goog.Promise<undefined>}\n * @override\n */\nfireauth.RedirectAuthEventProcessor.prototype.processAuthEvent =\n    function(authEvent, owner) {\n  // This should not happen as fireauth.iframe.AuthRelay will not send null\n  // events.\n  if (!authEvent) {\n    return goog.Promise.reject(\n        new fireauth.AuthError(fireauth.authenum.Error.INVALID_AUTH_EVENT));\n  }\n  // Reset any pending redirect result. This event will overwrite it.\n  this.reset();\n  this.redirectResultResolved_ = true;\n  var mode = authEvent.getType();\n  var eventId = authEvent.getEventId();\n  // Check if web storage is not supported in the iframe.\n  var isWebStorageNotSupported =\n      authEvent.getError() &&\n      authEvent.getError()['code'] == 'auth/web-storage-unsupported';\n  /// Check if operation is supported in this environment.\n  var isOperationNotSupported =\n      authEvent.getError() &&\n      authEvent.getError()['code'] == 'auth/operation-not-supported-in-this-' +\n          'environment';\n  this.unrecoverableErrorDetected_ =\n      !!(isWebStorageNotSupported || isOperationNotSupported);\n  // UNKNOWN mode is always triggered on load by iframe when no popup/redirect\n  // data is available. If web storage unsupported error is thrown, process as\n  // error and not as unknown event. If the operation is not supported in this\n  // environment, also treat as an error and not as an unknown event.\n  if (mode == fireauth.AuthEvent.Type.UNKNOWN &&\n      !isWebStorageNotSupported &&\n      !isOperationNotSupported) {\n    return this.processUnknownEvent_();\n  } else if (authEvent.hasError()) {\n    return this.processErrorEvent_(authEvent, owner);\n  } else if (owner.getAuthEventHandlerFinisher(mode, eventId)) {\n    return this.processSuccessEvent_(authEvent, owner);\n  } else {\n    return goog.Promise.reject(\n        new fireauth.AuthError(fireauth.authenum.Error.INVALID_AUTH_EVENT));\n  }\n};\n\n\n/**\n * Sets an empty redirect result response when no redirect result is available.\n */\nfireauth.RedirectAuthEventProcessor.prototype.defaultToEmptyResponse =\n    function() {\n  // If the first event does not resolve redirectResult and no subscriber can\n  // handle it, set redirect result to null.\n  // An example of this scenario would be a link via redirect that was triggered\n  // by a user that was not logged in. canHandleAuthEvent will be false for all\n  // subscribers. So make sure getRedirectResult when called will resolve to a\n  // null user.\n  if (!this.redirectResultResolved_) {\n    this.redirectResultResolved_ = true;\n    // No Auth event available, getRedirectResult should resolve with null.\n    this.setRedirectResult_(false, null, null);\n  }\n};\n\n\n/**\n * Clears the cached redirect result as long as there is no pending redirect\n * result being processed. Unrecoverable errors will not be cleared.\n */\nfireauth.RedirectAuthEventProcessor.prototype.clearRedirectResult = function() {\n  // Clear the result if it is already resolved and no unrecoverable errors are\n  // detected.\n  if (this.redirectResultResolved_ && !this.unrecoverableErrorDetected_) {\n    this.setRedirectResult_(false, null, null);\n  }\n};\n\n\n/**\n * Processes the unknown event.\n * @return {!goog.Promise<undefined>}\n * @private\n */\nfireauth.RedirectAuthEventProcessor.prototype.processUnknownEvent_ =\n    function() {\n  // No Auth event available, getRedirectResult should resolve with null.\n  this.setRedirectResult_(false, null, null);\n  return goog.Promise.resolve();\n};\n\n\n/**\n * Processes an error event.\n * @param {?fireauth.AuthEvent} authEvent External Auth event detected by\n *     iframe.\n * @param {!fireauth.AuthEventHandler} owner The owner of the event.\n * @return {!goog.Promise<undefined>}\n * @private\n */\nfireauth.RedirectAuthEventProcessor.prototype.processErrorEvent_ =\n    function(authEvent, owner) {\n  // Set redirect result to resolve with null if event is not a redirect or\n  // reject with error if event is an error.\n  this.setRedirectResult_(true, null, authEvent.getError());\n  return goog.Promise.resolve();\n};\n\n\n/**\n * Processes a successful event.\n * @param {?fireauth.AuthEvent} authEvent External Auth event detected by\n *     iframe.\n * @param {!fireauth.AuthEventHandler} owner The owner of the event.\n * @return {!goog.Promise<undefined>}\n * @private\n */\nfireauth.RedirectAuthEventProcessor.prototype.processSuccessEvent_ =\n    function(authEvent, owner) {\n  var self = this;\n  var eventId = authEvent.getEventId();\n  var mode = authEvent.getType();\n  var handler = owner.getAuthEventHandlerFinisher(mode, eventId);\n  var requestUri = /** @type {string} */ (authEvent.getUrlResponse());\n  var sessionId = /** @type {string} */ (authEvent.getSessionId());\n  var postBody = /** @type {?string} */ (authEvent.getPostBody());\n  var tenantId = /** @type {?string} */ (authEvent.getTenantId());\n  var isRedirect = fireauth.AuthEvent.isRedirect(authEvent);\n  // Complete sign in or link account operation and then pass result to\n  // relevant pending popup promise.\n  return handler(requestUri, sessionId, tenantId, postBody)\n      .then(function(popupRedirectResponse) {\n    // Flow completed.\n    // For a redirect operation resolve with the popupRedirectResponse,\n    // otherwise resolve with null.\n    self.setRedirectResult_(isRedirect, popupRedirectResponse, null);\n  }).thenCatch(function(error) {\n    // Flow not completed due to error.\n    // For a redirect operation reject with the error, otherwise resolve\n    // with null.\n    self.setRedirectResult_(\n        isRedirect, null, /** @type {!fireauth.AuthError} */ (error));\n    // Always resolve.\n    return;\n  });\n};\n\n\n/**\n * Sets redirect error result.\n * @param {!fireauth.AuthError} error The redirect operation error.\n * @private\n */\nfireauth.RedirectAuthEventProcessor.prototype.setRedirectReject_ =\n    function(error) {\n  // If a redirect error detected, reject getRedirectResult with that error.\n  this.redirectedUserPromise_ = function() {\n    return goog.Promise.reject(error);\n  };\n  // Reject all pending getRedirectResult promises.\n  if (this.redirectReject_.length) {\n    for (var i = 0; i < this.redirectReject_.length; i++) {\n      this.redirectReject_[i](error);\n    }\n  }\n};\n\n\n/**\n * Sets redirect success result.\n * @param {!fireauth.AuthEventManager.Result} popupRedirectResult The\n *     resolved user for a successful or null user redirect.\n * @private\n */\nfireauth.RedirectAuthEventProcessor.prototype.setRedirectResolve_ =\n    function(popupRedirectResult) {\n  // If a redirect user detected, resolve getRedirectResult with the\n  // popupRedirectResult.\n  // Result should not be null in this case.\n  this.redirectedUserPromise_ = function() {\n    return goog.Promise.resolve(\n        /** @type {!fireauth.AuthEventManager.Result} */ (popupRedirectResult));\n  };\n  // Resolve all pending getRedirectResult promises.\n  if (this.redirectResolve_.length) {\n    for (var i = 0; i < this.redirectResolve_.length; i++) {\n      this.redirectResolve_[i](\n          /** @type {!fireauth.AuthEventManager.Result} */ (\n              popupRedirectResult));\n    }\n  }\n};\n\n\n/**\n * @param {boolean} isRedirect Whether Auth event is a redirect event.\n * @param {?fireauth.AuthEventManager.Result} popupRedirectResult The\n *     resolved user for a successful redirect. This user is null if no redirect\n *     operation run.\n * @param {?fireauth.AuthError} error The redirect operation error.\n * @private\n */\nfireauth.RedirectAuthEventProcessor.prototype.setRedirectResult_ =\n    function(isRedirect, popupRedirectResult, error) {\n  if (isRedirect) {\n    // This is a redirect operation, either resolves with user or error.\n    if (error) {\n      // If a redirect error detected, reject getRedirectResult with that error.\n      this.setRedirectReject_(error);\n    } else {\n      // If a redirect user detected, resolve getRedirectResult with the\n      // popupRedirectResult.\n      // Result should not be null in this case.\n      this.setRedirectResolve_(\n          /** @type {!fireauth.AuthEventManager.Result} */ (\n              popupRedirectResult));\n    }\n  } else {\n    // Not a redirect, set redirectUser_ to return null.\n    this.setRedirectResolve_({\n      'user': null\n    });\n  }\n  // Reset all pending promises.\n  this.redirectResolve_ = [];\n  this.redirectReject_ = [];\n};\n\n\n/**\n * Returns the redirect result. If coming back from a successful redirect sign\n * in, will resolve to the signed in user. If coming back from an unsuccessful\n * redirect sign, will reject with the proper error. If no redirect operation\n * called, resolves with null.\n * @return {!goog.Promise<!fireauth.AuthEventManager.Result>}\n */\nfireauth.RedirectAuthEventProcessor.prototype.getRedirectResult = function() {\n  var self = this;\n  // Initial result could be overridden in the case of Cordova.\n  // Auth domain must be included for this to resolve.\n  // If still pending just return the pending promise.\n  var p = new goog.Promise(function(resolve, reject) {\n    // The following logic works if this method was called before Auth event\n    // is triggered.\n    if (!self.redirectedUserPromise_) {\n      // Save resolves and rejects of pending promise for redirect operation.\n      self.redirectResolve_.push(resolve);\n      self.redirectReject_.push(reject);\n      // Start timeout listener to getRedirectResult pending promise.\n      // Call this only when redirectedUserPromise_ is not determined.\n      self.startRedirectTimeout_();\n    } else {\n      // Called after Auth event is triggered.\n      self.redirectedUserPromise_().then(resolve, reject);\n    }\n  });\n  return /** @type {!goog.Promise<!fireauth.AuthEventManager.Result>} */ (p);\n};\n\n\n/**\n * Starts timeout listener for getRedirectResult pending promise. This method\n * should not be called again after getRedirectResult's redirectedUserPromise_\n * is determined.\n * @private\n */\nfireauth.RedirectAuthEventProcessor.prototype.startRedirectTimeout_ =\n    function() {\n  // Expire pending timeout promise for popup operation.\n  var self = this;\n  var error = new fireauth.AuthError(\n      fireauth.authenum.Error.TIMEOUT);\n  if (this.redirectTimeoutPromise_) {\n    this.redirectTimeoutPromise_.cancel();\n  }\n  // For redirect mode.\n  this.redirectTimeoutPromise_ =\n      goog.Timer.promise(fireauth.AuthEventManager.REDIRECT_TIMEOUT_MS_.get())\n      .then(function() {\n        // If not resolved yet, reject with timeout error.\n        if (!self.redirectedUserPromise_) {\n          // Consider redirect result resolved.\n          self.redirectResultResolved_ = true;\n          self.setRedirectResult_(true, null, error);\n        }\n      });\n\n};\n\n\n\n/**\n * Popup Auth event manager.\n * @param {!fireauth.AuthEventManager} manager The parent Auth event manager.\n * @constructor\n * @implements {fireauth.AuthEventProcessor}\n */\nfireauth.PopupAuthEventProcessor = function(manager) {\n  this.manager_ = manager;\n};\n\n\n/**\n * Completes the processing of an external Auth event detected by the embedded\n * iframe.\n * @param {?fireauth.AuthEvent} authEvent External Auth event detected by\n *     iframe.\n * @param {!fireauth.AuthEventHandler} owner The owner of the event.\n * @return {!goog.Promise<undefined>}\n * @override\n */\nfireauth.PopupAuthEventProcessor.prototype.processAuthEvent =\n    function(authEvent, owner) {\n  // This should not happen as fireauth.iframe.AuthRelay will not send null\n  // events.\n  if (!authEvent) {\n    return goog.Promise.reject(\n        new fireauth.AuthError(fireauth.authenum.Error.INVALID_AUTH_EVENT));\n  }\n  var mode = authEvent.getType();\n  var eventId = authEvent.getEventId();\n  if (authEvent.hasError()) {\n    return this.processErrorEvent_(authEvent, owner);\n  } else if (owner.getAuthEventHandlerFinisher(mode, eventId)) {\n    return this.processSuccessEvent_(authEvent, owner);\n  } else {\n    return goog.Promise.reject(\n        new fireauth.AuthError(fireauth.authenum.Error.INVALID_AUTH_EVENT));\n  }\n};\n\n\n/**\n * Processes an error event.\n * @param {?fireauth.AuthEvent} authEvent External Auth event detected by\n *     iframe.\n * @param {!fireauth.AuthEventHandler} owner The owner of the event.\n * @return {!goog.Promise<undefined>}\n * @private\n */\nfireauth.PopupAuthEventProcessor.prototype.processErrorEvent_ =\n    function(authEvent, owner) {\n  var eventId = authEvent.getEventId();\n  var mode = authEvent.getType();\n  // For pending popup promises trigger rejects with the error.\n  owner.resolvePendingPopupEvent(mode, null, authEvent.getError(), eventId);\n  return goog.Promise.resolve();\n};\n\n\n/**\n * Processes a successful event.\n * @param {?fireauth.AuthEvent} authEvent External Auth event detected by\n *     iframe.\n * @param {!fireauth.AuthEventHandler} owner The owner of the event.\n * @return {!goog.Promise<undefined>}\n * @private\n */\nfireauth.PopupAuthEventProcessor.prototype.processSuccessEvent_ =\n    function(authEvent, owner) {\n  var eventId = authEvent.getEventId();\n  var mode = authEvent.getType();\n  var handler = owner.getAuthEventHandlerFinisher(mode, eventId);\n  // Successful operation, complete the exchange for an ID token.\n  var requestUri = /** @type {string} */ (authEvent.getUrlResponse());\n  var sessionId = /** @type {string} */ (authEvent.getSessionId());\n  var postBody = /** @type {?string} */ (authEvent.getPostBody());\n  var tenantId = /** @type {?string} */ (authEvent.getTenantId());\n  // Complete sign in or link account operation and then pass result to\n  // relevant pending popup promise.\n  return handler(requestUri, sessionId, tenantId, postBody)\n      .then(function(popupRedirectResponse) {\n    // Flow completed.\n    // Resolve pending popup promise if it exists.\n    owner.resolvePendingPopupEvent(mode, popupRedirectResponse, null, eventId);\n  }).thenCatch(function(error) {\n    // Flow not completed due to error.\n    // Resolve pending promise if it exists.\n    owner.resolvePendingPopupEvent(\n        mode, null, /** @type {!fireauth.AuthError} */ (error), eventId);\n    // Always resolve.\n    return;\n  });\n};\n\n\n\n/**\n * The interface that represents an Auth event handler. It provides the\n * ability for the Auth event manager to determine the owner of an Auth event,\n * the ability to resolve a pending popup event and the appropriate handler for\n * an event.\n * @interface\n */\nfireauth.AuthEventHandler = function() {};\n\n\n/**\n * @param {!fireauth.AuthEvent.Type} mode The Auth type mode.\n * @param {?string=} opt_eventId The event ID.\n * @return {boolean} Whether the Auth event handler can handler the provided\n *     event.\n */\nfireauth.AuthEventHandler.prototype.canHandleAuthEvent =\n    function(mode, opt_eventId) {};\n\n\n/**\n * Completes the pending popup operation. If error is not null, rejects with the\n * error. Otherwise, it resolves with the popup redirect result.\n * @param {!fireauth.AuthEvent.Type} mode The Auth type mode.\n * @param {?fireauth.AuthEventManager.Result} popupRedirectResult The result\n *     to resolve with when no error supplied.\n * @param {?fireauth.AuthError} error When supplied, the promise will reject.\n * @param {?string=} opt_eventId The event ID.\n */\nfireauth.AuthEventHandler.prototype.resolvePendingPopupEvent =\n    function(mode, popupRedirectResult, error, opt_eventId) {};\n\n\n/**\n * Returns the handler's appropriate popup and redirect sign in operation\n * finisher.\n * @param {!fireauth.AuthEvent.Type} mode The Auth type mode.\n * @param {?string=} opt_eventId The optional event ID.\n * @return {?function(string, string, ?string,\n *     ?string=):!goog.Promise<!fireauth.AuthEventManager.Result>}\n */\nfireauth.AuthEventHandler.prototype.getAuthEventHandlerFinisher =\n    function(mode, opt_eventId) {};\n","/**\n * @license\n * Copyright 2018 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n /**\n * @fileoverview Defines the firebase.auth.AuthSettings structure.\n */\n\ngoog.provide('fireauth.AuthSettings');\n\n\n/**\n * The class used to initialize an Auth settings object currently used to\n * enable or disable app verification for testing.\n * @constructor\n */\nfireauth.AuthSettings = function() {\n  this.appVerificationDisabledForTesting_ = false;\n  Object.defineProperty(\n      /** @type {!Object} */ (this),\n      'appVerificationDisabled',\n      {\n        /**\n         * @this {!Object}\n         * @return {boolean} The current status.\n         */\n        get: function() {\n          return this.getAppVerificationDisabledForTesting();\n        },\n        /**\n         * @this {!Object}\n         * @param {boolean} value The new status.\n         */\n        set: function(value) {\n          this.setAppVerificationDisabledForTesting(value);\n        },\n        enumerable: false\n      });\n};\n\n\n/**\n * Sets whether app verification is disable for testing.\n * @param {boolean} status App verification status for testing.\n */\nfireauth.AuthSettings.prototype.setAppVerificationDisabledForTesting =\n    function(status) {\n  this.appVerificationDisabledForTesting_ = status;\n};\n\n\n/**\n * @return {boolean} Whether app verification is enabled or disabled for\n *     testing.\n */\nfireauth.AuthSettings.prototype.getAppVerificationDisabledForTesting =\n    function() {\n  return this.appVerificationDisabledForTesting_;\n};\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines the firebase.auth.ConfirmationResult. This is needed\n * to provide first class support for phone Auth API: signInWithPhoneNumber,\n * linkWithPhoneNumber and reauthenticateWithPhoneNumber.\n */\n\ngoog.provide('fireauth.ConfirmationResult');\n\ngoog.require('fireauth.PhoneAuthProvider');\ngoog.require('fireauth.object');\ngoog.require('goog.Promise');\n\n\n/**\n * The confirmation result class. This takes in the verification ID returned\n * from the phone Auth provider and the credential resolver to run when\n * confirming with a verification code.\n * @param {string} verificationId The verification ID returned from the Phone\n *     Auth provider after sending the verification code.\n * @param {!function(!fireauth.AuthCredential):\n *     !goog.Promise<!fireauth.AuthEventManager.Result>} credentialResolver a\n *     function that takes in an AuthCredential and returns a promise that\n *     resolves with a UserCredential object.\n * @constructor\n */\nfireauth.ConfirmationResult = function(verificationId, credentialResolver) {\n  /**\n   * @const @private {!function(!fireauth.AuthCredential):\n   *     !goog.Promise<!fireauth.AuthEventManager.Result>} A function that takes\n   *     in an AuthCredential and returns a promise that resolves with a\n   *     UserCredential object.\n   */\n  this.credentialResolver_ = credentialResolver;\n  // Set verificationId as read-only property.\n  fireauth.object.setReadonlyProperty(this, 'verificationId', verificationId);\n};\n\n\n/**\n * Confirms the verification code and returns a promise that resolves with the\n * User Credential object.\n * @param {string} verificationCode The phone Auth verification code to use to\n *     complete the Auth flow.\n * @return {!goog.Promise<!fireauth.AuthEventManager.Result>}\n */\nfireauth.ConfirmationResult.prototype.confirm = function(verificationCode) {\n  // Initialize a phone Auth credential with the verification ID and code.\n  var credential = fireauth.PhoneAuthProvider.credential(\n      this['verificationId'], verificationCode);\n  // Run the credential resolver with the phone Auth credential and return its\n  // result.\n  return this.credentialResolver_(credential);\n};\n\n\n/**\n * Initializes a ConfirmationResult using the provided phone number, app\n * verifier and returns it asynchronously. On code confirmation, the result will\n * resolve using the credential resolver provided.\n * @param {!fireauth.Auth} auth The corresponding Auth instance.\n * @param {string} phoneNumber The phone number to authenticate with.\n * @param {!firebase.auth.ApplicationVerifier} appVerifier The application\n *     verifier.\n * @param {!function(!fireauth.AuthCredential):\n *     !goog.Promise<!fireauth.AuthEventManager.Result>} credentialResolver a\n *     function that takes in an AuthCredential and returns a promise that\n *     resolves with a UserCredential object.\n * @return {!goog.Promise<!fireauth.ConfirmationResult>}\n */\nfireauth.ConfirmationResult.initialize =\n    function(auth, phoneNumber, appVerifier, credentialResolver) {\n  // Initialize a phone Auth provider instance using the provided Auth\n  // instance.\n  var phoneAuthProvider = new fireauth.PhoneAuthProvider(auth);\n  // Verify the phone number.\n  return phoneAuthProvider.verifyPhoneNumber(phoneNumber, appVerifier)\n      .then(function(verificationId) {\n        // When code is sent and verification ID is returned, initialize a\n        // ConfirmationResult with the returned verification ID and credential\n        // resolver, and return that instance.\n        return new fireauth.ConfirmationResult(\n            verificationId, credentialResolver);\n      });\n};\n","/**\n * @license\n * Copyright 2018 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines the firebase.auth.IdTokenResult class that is obtained\n * from getIdTokenResult. It contains the ID token JWT string and other helper\n * properties for getting different data associated with the token as well as\n * all the decoded payload claims.\n */\n\ngoog.provide('fireauth.IdTokenResult');\n\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.IdToken');\ngoog.require('fireauth.authenum.Error');\ngoog.require('fireauth.object');\ngoog.require('fireauth.util');\n\n\n\n/**\n * This is the ID token result object obtained from getIdTokenResult. It\n * contains the ID token JWT string and other helper properties for getting\n * different data associated with the token as well as all the decoded payload\n * claims.\n * @param {string} tokenString The JWT token.\n * @constructor\n */\nfireauth.IdTokenResult = function(tokenString) {\n  var idToken = fireauth.IdToken.parseIdTokenClaims(tokenString);\n  if (!idToken || !idToken['exp'] || !idToken['auth_time'] || !idToken['iat']) {\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.INTERNAL_ERROR,\n        'An internal error occurred. The token obtained by Firebase appears ' +\n        'to be malformed. Please retry the operation.');\n  }\n  fireauth.object.setReadonlyProperties(this, {\n    'token': tokenString,\n    'expirationTime': fireauth.util.utcTimestampToDateString(\n        idToken['exp'] * 1000),\n    'authTime': fireauth.util.utcTimestampToDateString(\n        idToken['auth_time'] * 1000),\n    'issuedAtTime': fireauth.util.utcTimestampToDateString(\n        idToken['iat'] * 1000),\n    'signInProvider': (idToken['firebase'] &&\n                       idToken['firebase']['sign_in_provider']) ?\n                      idToken['firebase']['sign_in_provider'] : null,\n    'signInSecondFactor': (idToken['firebase'] &&\n                           idToken['firebase']['sign_in_second_factor']) ?\n                          idToken['firebase']['sign_in_second_factor'] : null,\n    'claims': idToken\n  });\n};\n","/**\n * @license\n * Copyright 2019 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines the multi-factor resolver class used to facilitate\n *     recovery when a multi-factor user tries to sign-in with a first factor.\n */\n\ngoog.provide('fireauth.MultiFactorResolver');\n\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.MultiFactorInfo');\ngoog.require('fireauth.MultiFactorSession');\ngoog.require('fireauth.authenum.Error');\ngoog.require('fireauth.object');\ngoog.require('goog.array');\ngoog.require('goog.object');\n\n\n/**\n * Initializes a `MultiFactorResolver` instance. This is used when a\n * multi-factor user signs in with the first factor but is required to provide\n * a second factor assertion before completing sign-in.\n *\n * @param {!fireauth.Auth} auth The Auth instance.\n * @param {!fireauth.MultiFactorResolver.ErrorResponse} errorResponse The server\n *     error response containing the pending multi-factor credential.\n * @param {function({idToken: string, refreshToken: string}):\n *         !goog.Promise<!fireauth.AuthEventManager.Result>} onIdTokenResolver\n *     A function that takes the assertion token response and any previous\n *     information returned with the error and completes sign in with a\n *     `UserCredential`.\n * @constructor\n */\nfireauth.MultiFactorResolver = function(\n    auth, errorResponse, onIdTokenResolver) {\n  var pendingCredential = errorResponse && errorResponse[\n      fireauth.MultiFactorResolver.SignInResponseField.MFA_PENDING_CREDENTIAL];\n  if (!pendingCredential) {\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.ARGUMENT_ERROR,\n        'Internal assert: Invalid MultiFactorResolver');\n  }\n  /** @const @private {!fireauth.Auth} The Auth instance. */\n  this.auth_ = auth;\n  /**\n   * @const @private {!fireauth.MultiFactorResolver.ErrorResponse} The server\n   *     error response with the pending credential.\n   */\n  this.errorResponse_ = goog.object.clone(errorResponse);\n  /**\n   * @const @private {function({idToken: string, refreshToken: string}):\n   *                  !goog.Promise<!fireauth.AuthEventManager.Result>} The ID\n   *     token resolver.\n   */\n  this.onIdTokenResolver_ = onIdTokenResolver;\n  /**\n   * @const @private {!fireauth.MultiFactorSession} The corresponding\n   *     multi-factor session.\n   */\n  this.session_ = new fireauth.MultiFactorSession(\n      null,\n      pendingCredential);\n  /**\n   * @const @private {!Array<!fireauth.MultiFactorInfo>} The list of\n   *     multi-factor hints corresponding to the current user session.\n   */\n  this.hints_ = [];\n  var enrollmentList = errorResponse[\n      fireauth.MultiFactorResolver.SignInResponseField.MFA_INFO] || [];\n  var self = this;\n  goog.array.forEach(enrollmentList, function(mfaEnrollment) {\n    var info = fireauth.MultiFactorInfo.fromServerResponse(mfaEnrollment);\n    if (info) {\n      self.hints_.push(info);\n    }\n  });\n  fireauth.object.setReadonlyProperty(this, 'auth', this.auth_);\n  fireauth.object.setReadonlyProperty(this, 'session', this.session_);\n  fireauth.object.setReadonlyProperty(\n      this, 'hints', this.hints_);\n};\n\n\n/**\n * The server side error response on multi-factor sign-in.\n * @typedef {{\n *   mfaInfo: (?Array<!Object>|undefined),\n *   mfaPendingCredential: (?string|undefined)\n * }}\n */\nfireauth.MultiFactorResolver.ErrorResponse;\n\n/**\n * Sign in response fields for multi-factor sign-in.\n * @enum {string}\n */\nfireauth.MultiFactorResolver.SignInResponseField = {\n  MFA_INFO: 'mfaInfo',\n  MFA_PENDING_CREDENTIAL: 'mfaPendingCredential'\n};\n\n\n/**\n * Completes the second factor sign-in with the multi-factor assertion provided\n * and returns a promise that resolves with the `UserCredential` object.\n *\n * @param {!fireauth.MultiFactorAssertion} assertion The multi-factor assertion\n *     to resolve sign-in with.\n * @return {!goog.Promise<!fireauth.AuthEventManager.Result>} A promise that\n *     resolves with the `UserCredential` after ID token processing.\n */\nfireauth.MultiFactorResolver.prototype.resolveSignIn = function(assertion) {\n  var self = this;\n  return assertion.process(this.auth_.getRpcHandler(), this.session_)\n      .then(function(result) {\n        var newSignInResponse = goog.object.clone(self.errorResponse_);\n        // These fields are no longer needed.\n        delete newSignInResponse[\n            fireauth.MultiFactorResolver.SignInResponseField.MFA_INFO];\n        delete newSignInResponse[fireauth.MultiFactorResolver\n            .SignInResponseField.MFA_PENDING_CREDENTIAL];\n        goog.object.extend(newSignInResponse, result);\n        // Return ID token/refresh token result and the original error response.\n        // This is needed as the original server response may contain additional\n        // data such as OAuth credentials, raw user info, etc that needs to be\n        // returned to the developer on successful sign-in.\n        return self.onIdTokenResolver_(\n            /** @type {{idToken: string, refreshToken: string}} */ (\n                newSignInResponse));\n      });\n};\n","/**\n * @license\n * Copyright 2019 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n \n/**\n * @fileoverview Defines the MultiFactorError class, a subclass of\n * fireauth.AuthError.\n */\n\n\ngoog.provide('fireauth.MultiFactorError');\n\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.MultiFactorResolver');\ngoog.require('fireauth.authenum.Error');\ngoog.require('fireauth.object');\ngoog.require('goog.object');\n\n\n/**\n * Multi-factor error with resolver, used to resolve sign-in after a two-factor\n * user signs in with a first factor and is required to prove ownership of the\n * second factor.\n * @param {!fireauth.Auth} auth The Auth instance.\n * @param {!fireauth.MultiFactorResolver.ErrorResponse} errorResponse The server\n *     error response containing the pending multi-factor credential.\n * @param {function({idToken: string, refreshToken: string}):\n *         !goog.Promise<!fireauth.AuthEventManager.Result>} onIdTokenResolver\n *     A function that takes the assertion token response and any previous\n *     information returned with the error and completes sign in with a\n *     `UserCredential`.\n * @param {string=} message The optional custom human-readable message. If not\n *     provided, a default message will be used.\n * @constructor\n * @extends {fireauth.AuthError}\n */\nfireauth.MultiFactorError = function(\n    auth, errorResponse, onIdTokenResolver, message) {\n  fireauth.MultiFactorError.base(\n      this,\n      'constructor',\n      fireauth.authenum.Error.MFA_REQUIRED,\n      message,\n      errorResponse);\n  this.serverResponse_ = goog.object.clone(errorResponse);\n  /**\n   * @const @private {!fireauth.MultiFactorResolver} The multi-factor resolver\n   *     instance.\n   */\n  this.resolver_ =\n      new fireauth.MultiFactorResolver(auth, errorResponse, onIdTokenResolver);\n  fireauth.object.setReadonlyProperty(this, 'resolver', this.resolver_);\n};\ngoog.inherits(fireauth.MultiFactorError, fireauth.AuthError);\n\n\n/**\n * Initializes a `MultiFactorError` from the plain object provided. If the\n * object is not a valid `MultiFactorError`, null is returned.\n * @param {?Object|undefined} response The object response to convert to a\n *     fireauth.MultiFactorError.\n * @param {!fireauth.Auth} auth The Auth instance.\n * @param {function({idToken: string, refreshToken: string}):\n *         !goog.Promise<!fireauth.AuthEventManager.Result>} onIdTokenResolver\n *     A function that takes the assertion token response and any previous\n *     information returned with the error and completes sign in with a\n *     `UserCredential`.\n * @return {?fireauth.MultiFactorError} The `MultiFactorError` error\n *     representation of the response. null is returned if the response is not\n *     a valid MultiFactorError plain object representation.\n */\nfireauth.MultiFactorError.fromPlainObject =\n    function(response, auth, onIdTokenResolver) {\n  if (response &&\n      goog.isObject(response['serverResponse']) &&\n      response['code'] === 'auth/' + fireauth.authenum.Error.MFA_REQUIRED) {\n    try {\n      return new fireauth.MultiFactorError(\n          auth,\n          /** @type {!fireauth.MultiFactorResolver.ErrorResponse} */ (\n              response['serverResponse']),\n          onIdTokenResolver,\n          response['message']);\n    } catch (e) {\n      return null;\n    }\n  }\n  return null;\n};\n","/**\n * @license\n * Copyright 2019 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n \n/**\n * @fileoverview Defines the `firebase.auth.MultiFactorAssertion` abstract class\n * and all its subclasses, such as PhoneMultiFactorAssertion.\n */\n\ngoog.provide('fireauth.AuthCredentialMultiFactorAssertion');\ngoog.provide('fireauth.MultiFactorAssertion');\ngoog.provide('fireauth.PhoneMultiFactorAssertion');\n\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.MultiFactorAuthCredential');\ngoog.require('fireauth.MultiFactorSession');\ngoog.require('fireauth.PhoneAuthProvider');\ngoog.require('fireauth.authenum.Error');\ngoog.require('fireauth.object');\n\n\n/**\n * Abstract class representing a `firebase.auth.MultiFactorAssertion` interface.\n * This is used to facilitate enrollment of a second factor on an existing user\n * or sign-in of a user who already verified the first factor.\n * @abstract\n * @constructor\n */\nfireauth.MultiFactorAssertion = function() {};\n\n\n/**\n * Finalizes the 2nd factor enrollment flow with the current\n * MultiFactorAssertion.\n * @param {!fireauth.RpcHandler} rpcHandler The RPC handler instance.\n * @param {!fireauth.MultiFactorEnrollmentRequestIdentifier} enrollmentRequest\n *     The enrollment request identifying the user.\n * @return {!goog.Promise<{idToken: string, refreshToken: string}>} A promise\n *     that resolves with the updated ID and refresh tokens.\n * @protected\n */\nfireauth.MultiFactorAssertion.prototype.finalizeEnrollmentWithVerificationInfo =\n    goog.abstractMethod;\n\n\n/**\n * Finalizes the 2nd factor sign-in flow with the current MultiFactorAssertion.\n * @param {!fireauth.RpcHandler} rpcHandler The RPC handler instance.\n * @param {!fireauth.MultiFactorSignInRequestIdentifier} signInRequest\n *     The sign-in request identifying the user.\n * @return {!goog.Promise<{idToken: string, refreshToken: string}>} A promise\n *     that resolves with the signed in user's ID and refresh tokens.\n * @protected\n */\nfireauth.MultiFactorAssertion.prototype.finalizeSignInWithVerificationInfo =\n    goog.abstractMethod;\n\n\n/**\n * Processes the `MultiFactorAssertion` instance using the `MultiFactorSession`\n * provided and optional display name for enrollment flows.\n * @param {!fireauth.RpcHandler} rpcHandler The RPC handler instance.\n * @param {!fireauth.MultiFactorSession} session The multi-factor session\n *     instance.\n * @param {?string=} displayName The optional display name for a multi-factor\n *     enrollment.\n * @return {!goog.Promise<{idToken: string, refreshToken: string}>} A promise\n *     that resolves with the signed in or enrolled user's ID and refresh\n *     tokens.\n */\nfireauth.MultiFactorAssertion.prototype.process =\n    function(rpcHandler, session, displayName) {\n  // Session obtained from user in enroll.\n  // It is obtained from error in resolver.\n  if (session.type == fireauth.MultiFactorSession.Type.ENROLL) {\n    return this.finalizeMfaEnrollment_(rpcHandler, session, displayName);\n  } else {\n    return this.finalizeMfaSignIn_(rpcHandler, session);\n  }\n};\n\n\n/**\n * Finalizes the multi-factor enrollment.\n * @param {!fireauth.RpcHandler} rpcHandler The RPC handler instance.\n * @param {!fireauth.MultiFactorSession} session The multi-factor session for\n *     the current signed in user.\n * @param {?string=} displayName The optional display name for a multi-factor\n *     enrollment.\n * @return {!goog.Promise<{idToken: string, refreshToken: string}>} A promise\n *     that resolves with the enrolled user's ID and refresh tokens.\n * @private\n */\nfireauth.MultiFactorAssertion.prototype.finalizeMfaEnrollment_ =\n    function(rpcHandler, session, displayName) {\n  var self = this;\n  return session.getRawSession().then(function(rawSession) {\n    var request = {\n      'idToken': rawSession\n    };\n    if (typeof displayName !== 'undefined') {\n      request['displayName'] = displayName;\n    }\n    return self.finalizeEnrollmentWithVerificationInfo(rpcHandler, request);\n  });\n};\n\n\n/**\n * Finalizes the multi-factor sign-in.\n * @param {!fireauth.RpcHandler} rpcHandler The RPC handler instance.\n * @param {!fireauth.MultiFactorSession} session The multi-factor session for\n *     the multi-factor enrolled user trying to sign-in.\n * @return {!goog.Promise<{idToken: string, refreshToken: string}>} A promise\n *     that resolves with the signed in user's ID and refresh tokens.\n * @private\n */\nfireauth.MultiFactorAssertion.prototype.finalizeMfaSignIn_ =\n    function(rpcHandler, session) {\n  var self = this;\n  return session.getRawSession().then(function(rawSession) {\n    var request = {\n      'mfaPendingCredential': rawSession,\n    };\n    return self.finalizeSignInWithVerificationInfo(rpcHandler, request);\n  });\n};\n\n\n/**\n * Defines a class for handling MultiFactorAssertions based on\n * `MultiFactorAuthCredentials`.\n * @param {!fireauth.MultiFactorAuthCredential} multiFactorAuthCredential The\n *     multi-factor AuthCredential.\n * @constructor\n * @extends {fireauth.MultiFactorAssertion}\n */\nfireauth.AuthCredentialMultiFactorAssertion =\n    function(multiFactorAuthCredential) {\n  // This assumes the factor ID matches the credential providerId.\n  // If this is ever not true, the subclass can overwrite that.\n  fireauth.object.setReadonlyProperty(\n      this, 'factorId', multiFactorAuthCredential.providerId);\n  /**\n   * @protected {!fireauth.MultiFactorAuthCredential} The underlying\n   *     multi-factor AuthCredential.\n   */\n  this.multiFactorAuthCredential = multiFactorAuthCredential;\n};\ngoog.inherits(\n    fireauth.AuthCredentialMultiFactorAssertion, fireauth.MultiFactorAssertion);\n\n\n/**\n * Finalizes the 2nd factor enrollment flow with the current\n * MultiFactorAssertion.\n * @param {!fireauth.RpcHandler} rpcHandler The RPC handler instance.\n * @param {!fireauth.MultiFactorEnrollmentRequestIdentifier} enrollmentRequest\n *     The enrollment request identifying the user.\n * @return {!goog.Promise<{idToken: string, refreshToken: string}>} A promise\n *     that resolves with the updated ID and refresh tokens.\n * @protected\n * @override\n */\nfireauth.AuthCredentialMultiFactorAssertion.prototype\n    .finalizeEnrollmentWithVerificationInfo = function(rpcHandler,\n                                                       enrollmentRequest) {\n  return this.multiFactorAuthCredential.finalizeMfaEnrollment(\n      rpcHandler, enrollmentRequest);\n};\n\n\n/**\n * Finalizes the 2nd factor sign-in flow with the current MultiFactorAssertion.\n * @param {!fireauth.RpcHandler} rpcHandler The RPC handler instance.\n * @param {!fireauth.MultiFactorSignInRequestIdentifier} signInRequest\n *     The sign-in request identifying the user.\n * @return {!goog.Promise<{idToken: string, refreshToken: string}>} A promise\n *     that resolves with the signed in user's ID and refresh tokens.\n * @protected\n * @override\n */\nfireauth.AuthCredentialMultiFactorAssertion.prototype\n    .finalizeSignInWithVerificationInfo = function(rpcHandler,\n                                                   signInRequest) {\n  return this.multiFactorAuthCredential.finalizeMfaSignIn(\n      rpcHandler, signInRequest);\n};\n\n\n/**\n * Defines a class for handling MultiFactorAssertions based on\n * PhoneAuthCredentials. This class extends `AuthCredentialMultiFactorAssertion`\n * but for `PhoneAuthCredentials` only.\n * @param {!fireauth.PhoneAuthCredential} phoneAuthCredential\n * @constructor\n * @extends {fireauth.AuthCredentialMultiFactorAssertion}\n */\nfireauth.PhoneMultiFactorAssertion = function(phoneAuthCredential) {\n  fireauth.PhoneMultiFactorAssertion.base(\n      this, 'constructor', phoneAuthCredential);\n  // This class supports phone credentials only.\n  if (this.multiFactorAuthCredential.providerId !=\n      fireauth.PhoneAuthProvider['PROVIDER_ID']) {\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.ARGUMENT_ERROR,\n        'firebase.auth.PhoneMultiFactorAssertion requires a valid ' +\n        'firebase.auth.PhoneAuthCredential');\n  }\n};\ngoog.inherits(\n    fireauth.PhoneMultiFactorAssertion,\n    fireauth.AuthCredentialMultiFactorAssertion);\n","/**\n * @license\n * Copyright 2019 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n /**\n * @fileoverview Defines fireauth.UserEvent and fireauth.UserEventType.\n */\n\ngoog.provide('fireauth.UserEvent');\ngoog.provide('fireauth.UserEventType');\n\ngoog.require('goog.events');\ngoog.require('goog.events.Event');\n\n\n/**\n * User custom event.\n * @param {string} type The event type.\n * @param {?Object=} properties The optional properties to set to the custom\n *     event using same keys as object provided.\n * @constructor\n * @extends {goog.events.Event}\n */\nfireauth.UserEvent = function(type, properties) {\n  goog.events.Event.call(this, type);\n  // If optional properties provided.\n  // Add each property to custom event.\n  for (var key in properties) {\n    this[key] = properties[key];\n  }\n};\ngoog.inherits(fireauth.UserEvent, goog.events.Event);\n\n\n/**\n * Events dispatched by the user.\n * @enum {string}\n */\nfireauth.UserEventType = {\n  /** Dispatched when token is changed due to Auth event. */\n  TOKEN_CHANGED: 'tokenChanged',\n\n  /** Dispatched when user is deleted. */\n  USER_DELETED: 'userDeleted',\n\n  /**\n   * Dispatched when user session is invalidated. This could happen when the\n   * following errors occur: user-disabled or user-token-expired.\n   */\n  USER_INVALIDATED: 'userInvalidated',\n\n  /** Dispatched when the user is reloaded. */\n  USER_RELOADED: 'userReloaded'\n};\n","/**\n * @license\n * Copyright 2019 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines the `MultiFactorUser` class used to retrieve the\n * enrolled second factors on a user and to facilitate enrollment and\n * unenrollment of second factors.\n */\n\ngoog.provide('fireauth.MultiFactorUser');\n\ngoog.require('fireauth.MultiFactorAssertion');\ngoog.require('fireauth.MultiFactorInfo');\ngoog.require('fireauth.MultiFactorSession');\ngoog.require('fireauth.UserEventType');\ngoog.require('fireauth.object');\ngoog.require('goog.array');\ngoog.require('goog.events');\n\n\n/**\n * Initializes the multi-factor instance corresponding to a signed in user.\n * This provides the ability to retrieve the enrolled second factors for that\n * user, as well as the ability to enroll new second factors or unenroll\n * existing ones.\n * @param {!fireauth.AuthUser} user The user the multi-factor instance\n *     represents.\n * @param {?fireauth.AuthUser.AccountInfo=} accountInfo The optional user\n *     account info.\n * @constructor\n */\nfireauth.MultiFactorUser = function(user, accountInfo) {\n  /**\n   * @private {!fireauth.AuthUser} The user this multi-factor instance\n   *     represents.\n   */\n  this.user_ = user;\n  /** @private {!Array<!fireauth.MultiFactorInfo>} The enrolled factors. */\n  this.enrolledFactors_ = [];\n  /**\n   * @const @private {function({userServerResponse: !Object})} The handler for\n   *     user reload events.\n   */\n  this.userReloadedListener_ = goog.bind(this.handleUserReload_, this);\n  goog.events.listen(\n      this.user_,\n      fireauth.UserEventType.USER_RELOADED,\n      this.userReloadedListener_);\n  var enrolledFactors = [];\n  // AccountInfo is typically loaded from storage where it is stored in plain\n  // object format. Otherwise, the enrolled factors will be loaded from\n  // getAccountInfo response triggered by user reload event.\n  if (accountInfo &&\n      accountInfo['multiFactor'] &&\n      accountInfo['multiFactor']['enrolledFactors']) {\n    var enrolledFactorsPlainObject =\n        accountInfo['multiFactor']['enrolledFactors'];\n    goog.array.forEach(enrolledFactorsPlainObject, function(mfaEnrollment) {\n      var info = fireauth.MultiFactorInfo.fromPlainObject(mfaEnrollment);\n      if (info) {\n        enrolledFactors.push(info);\n      }\n    });\n  }\n  this.updateEnrolledFactors_(enrolledFactors);\n};\n\n\n/**\n * @const @private {string} The key for the list of second factor enrollments in\n *     the GetAccountInfo server response.\n */\nfireauth.MultiFactorUser.GET_ACCOUNT_INFO_MFA_INFO_ = 'mfaInfo';\n\n\n/** @return {!fireauth.AuthUser} The corresponding user. */\nfireauth.MultiFactorUser.prototype.getUser = function() {\n  return this.user_;\n};\n\n\n/**\n * Extracts the enrolled factors from getAccountInfo response and returns an\n * array of corresponding multi-factor info data.\n * @param {!Object} resp The GetAccountInfo response object.\n * @return {!Array<!fireauth.MultiFactorInfo>} The enrolled factors.\n * @private\n */\nfireauth.MultiFactorUser.extractEnrolledFactors_ = function(resp) {\n  // Parse MFA enrollments.\n  var mfaInfo = resp[fireauth.MultiFactorUser.GET_ACCOUNT_INFO_MFA_INFO_] || [];\n  var enrolledFactors = [];\n  goog.array.forEach(mfaInfo, function(mfaEnrollment) {\n    var info = fireauth.MultiFactorInfo.fromServerResponse(mfaEnrollment);\n    if (info) {\n      enrolledFactors.push(info);\n    }\n  });\n  return enrolledFactors;\n};\n\n\n/**\n * Handles user reload event. This will parse the enrollments from the\n * response and update them on the current multi-factor instance.\n * @param {{userServerResponse: !Object}} event The user reload event.\n * @private\n */\nfireauth.MultiFactorUser.prototype.handleUserReload_ = function(event) {\n  this.updateEnrolledFactors_(fireauth.MultiFactorUser.extractEnrolledFactors_(\n      event.userServerResponse));\n};\n\n\n/**\n * Updates the enrolledFactors property.\n * @param {!Array<!fireauth.MultiFactorInfo>} enrolledFactors The new list of\n *     `MultiFactorInfo` objects on the user.\n * @private\n */\nfireauth.MultiFactorUser.prototype.updateEnrolledFactors_ =\n    function(enrolledFactors) {\n  this.enrolledFactors_ = enrolledFactors;\n  fireauth.object.setReadonlyProperty(\n      this, 'enrolledFactors', enrolledFactors);\n};\n\n\n/**\n * Copies the list of enrolled factors on the user. This facilitates copying a\n * user to another user. The underlying user reference is not updated.\n * @param {!fireauth.MultiFactorUser} multiFactorUser The instance to copy.\n */\nfireauth.MultiFactorUser.prototype.copy = function(multiFactorUser) {\n  this.updateEnrolledFactors_(multiFactorUser.enrolledFactors_);\n};\n\n\n/**\n * Provides a multi-factor session used to start a multi-factor enrollment flow.\n * @return {!goog.Promise<!fireauth.MultiFactorSession>} A promise that resolves\n *     with a multi-factor session.\n */\nfireauth.MultiFactorUser.prototype.getSession = function() {\n  return this.user_.getIdToken()\n      .then(function(idToken) {\n        return new fireauth.MultiFactorSession(idToken, null);\n      });\n};\n\n\n/**\n * Enrolls a second factor as identified by the multi-factor assertion for\n * the current user.\n * @param {!fireauth.MultiFactorAssertion} assertion The multi-factor assertion.\n * @param {?string=} displayName The optional display name used to identify\n *     the 2nd factor to the end user.\n * @return {!goog.Promise<void>} A promise that resolves when the second factor\n *     is enrolled.\n */\nfireauth.MultiFactorUser.prototype.enroll = function(assertion, displayName) {\n  var self = this;\n  var rpcHandler = this.user_.getRpcHandler();\n  return this.getSession().then(function(session) {\n    return assertion.process(rpcHandler, session, displayName);\n  }).then(function(tokenResponse) {\n    // New tokens will be issued after enrollment of the new second factors.\n    // They need to be updated on the user.\n    self.user_.updateTokensIfPresent(tokenResponse);\n    // The user needs to be reloaded to get the new multi-factor information\n    // from server. USER_RELOADED event will be triggered and `enrolledFactors`\n    // will be updated.\n    return self.user_.reload();\n  });\n};\n\n\n/**\n * Removes a second factor from the current user. The factor to be removed can\n * either be identified with the corresponding MultiFactorInfo object or with\n * the second factor's uid string.\n *\n * As part of the unenrollment process, the backend may decide to log the user\n * out. If so, this will still succeed and invalidate the user's state.\n * @param {!fireauth.MultiFactorInfo|string} target The second factor to remove.\n * @return {!goog.Promise<void>}\n */\nfireauth.MultiFactorUser.prototype.unenroll = function(target) {\n  var self = this;\n  var uid = typeof target === 'string' ? target : target['uid'];\n  var rpcHandler = this.user_.getRpcHandler();\n  return this.user_.getIdToken().then(function(idToken) {\n    return rpcHandler.withdrawMfa(idToken, uid);\n  }).then(function(tokenResponse) {\n    // Remove the second factor from the user's list.\n    var enrolledFactors = goog.array.filter(self.enrolledFactors_,\n      function(info) {\n        return info['uid'] != uid;\n      });\n    self.updateEnrolledFactors_(enrolledFactors);\n    // Depending on whether the backend decided to revoke the user's session,\n    // the tokenResponse may be empty. If the tokens were not updated (and they\n    // are now invalid), reloading the user will discover this and invalidate\n    // the user's state accordingly.\n    self.user_.updateTokensIfPresent(tokenResponse);\n    return self.user_.reload().thenCatch(function(error) {\n      if (error['code'] != 'auth/user-token-expired') {\n        throw error;\n      }\n    });\n  });\n};\n\n\n/**\n * @return {!Object} The plain object representation of the `MultiFactorUser`.\n */\nfireauth.MultiFactorUser.prototype.toPlainObject = function() {\n  return {\n    'multiFactor': {\n      'enrolledFactors': goog.array.map(this.enrolledFactors_, function(info) {\n        return info.toPlainObject();\n      })\n    }\n  };\n};\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Utility for proactive refresh with exponential backoff\n * algorithm typically used to define a retry policy for certain async\n * operations.\n */\n\ngoog.provide('fireauth.ProactiveRefresh');\n\ngoog.require('fireauth.util');\ngoog.require('goog.Promise');\ngoog.require('goog.Timer');\n\n\n/**\n * The helper utility used to proactively refresh a certain operation based on\n * certain constraints with an exponential backoff retrial policy when\n * specific recoverable errors occur. Typically this is used to retry an\n * operation on network errors.\n * @param {!function():!goog.Promise} operation The promise returning operation\n *     to run.\n * @param {!function(*):boolean} retryPolicy A function that takes in an error\n *     and returns whether a retry policy should be implemented based on the\n *     error. If not, the operation will not run again.\n * @param {!function():number} getWaitDuration A function that returns the wait\n *     period before running again.\n * @param {number} lowerBound The lower bound duration to wait when an error\n *     occurs. This is the first interval to wait before rerunning after an\n *     error is detected.\n * @param {number} upperBound The upper bound duration to wait when an error\n *     keeps occurring. This upper bound should not be exceeded.\n * @param {boolean=} opt_runInBackground Whether to run in the background, when\n *     the tab is not visible. If the refresh should only run when the app is\n *     visible, the operation will block until the app is visible and then run.\n * @constructor @struct @final\n */\nfireauth.ProactiveRefresh = function(\n    operation,\n    retryPolicy,\n    getWaitDuration,\n    lowerBound,\n    upperBound,\n    opt_runInBackground) {\n  /**\n   * @const @private {!function():!goog.Promise} The promise returning operation\n   *     to run.\n   */\n  this.operation_ = operation;\n  /**\n   * @const @private {!function(*):boolean} The function that takes in an error\n   *     and returns whether a retry policy should be implemented based on the\n   *     error caught.\n   */\n  this.retryPolicy_ = retryPolicy;\n  /**\n   * @const @private {!function():number} The function that returns the wait\n   *     period after a successful operation before running again.\n   */\n  this.getWaitDuration_ = getWaitDuration;\n  /**\n   * @const @private {number} The lower bound duration to wait when an error\n   *     first occurs.\n   */\n  this.lowerBound_ = lowerBound;\n  /**\n   * @const @private {number} The upper bound duration to wait when an error\n   *     keeps occurring. This upper bound should not be exceeded.\n   */\n  this.upperBound_ = upperBound;\n  /**\n   * @const @private {boolean} Whether to run in the background, when the tab is\n   *     not visible.\n   */\n  this.runInBackground_ = !!opt_runInBackground;\n  /**\n   * @private {?goog.Promise} The pending promise for the next operation to run.\n   */\n  this.pending_ = null;\n  /**\n   * @private {number} The first wait interval when a new error occurs.\n   */\n  this.nextErrorWaitInterval_ = this.lowerBound_;\n  // Throw an error if the lower bound is greater than upper bound.\n  if (this.upperBound_ < this.lowerBound_) {\n    throw new Error('Proactive refresh lower bound greater than upper bound!');\n  }\n};\n\n\n/** Starts the proactive refresh based on the current configuration. */\nfireauth.ProactiveRefresh.prototype.start = function() {\n  // Set the next error wait interval to the lower bound. On each consecutive\n  // error, this will double in value until it reaches the upper bound.\n  this.nextErrorWaitInterval_ = this.lowerBound_;\n  // Start proactive refresh with clean slate (successful status).\n  this.process_(true);\n};\n\n\n/**\n * Returns the wait duration before the next run depending on the last run\n * status. If the last operation has succeeded, returns the getWaitDuration()\n * response. Otherwise, doubles the last error wait interval starting from\n * lowerBound and up to upperBound.\n * @param {boolean} hasSucceeded Whether last run succeeded.\n * @return {number} The wait time for the next run.\n * @private\n */\nfireauth.ProactiveRefresh.prototype.getNextRun_ = function(hasSucceeded) {\n  if (hasSucceeded) {\n    // If last operation succeeded, reset next error wait interval and return\n    // the default wait duration.\n    this.nextErrorWaitInterval_ = this.lowerBound_;\n    // Return typical wait duration interval after a successful operation.\n    return this.getWaitDuration_();\n  } else {\n    // Get next error wait interval.\n    var currentErrorWaitInterval = this.nextErrorWaitInterval_;\n    // Double interval for next consecutive error.\n    this.nextErrorWaitInterval_ *= 2;\n    // Make sure next wait interval does not exceed the maximum upper bound.\n    if (this.nextErrorWaitInterval_  > this.upperBound_) {\n      this.nextErrorWaitInterval_  = this.upperBound_;\n    }\n    return currentErrorWaitInterval;\n  }\n};\n\n\n/**\n * Processes one refresh call and sets the timer for the next call based on\n * the last recorded result.\n * @param {boolean} hasSucceeded Whether last run succeeded.\n * @private\n */\nfireauth.ProactiveRefresh.prototype.process_ = function(hasSucceeded) {\n  var self = this;\n  // Stop any other pending operation.\n  this.stop();\n  // Wait for next scheduled run based on whether an error occurred during last\n  // run.\n  this.pending_ = goog.Timer.promise(this.getNextRun_(hasSucceeded))\n      .then(function() {\n        // Block for conditions (if app is required to be visible) to be ready.\n        return self.waitUntilReady_();\n       })\n       .then(function() {\n         // Run the operation.\n         return self.operation_();\n       })\n      .then(function() {\n         // If successful, try again on next cycle with no previous error\n         // passed.\n         self.process_(true);\n       })\n      .thenCatch(function(error) {\n         // If an error occurs, only rerun when the error meets the retry\n         // policy.\n         if (self.retryPolicy_(error)) {\n           // Should retry with error to trigger exponentional backoff.\n           self.process_(false);\n         }\n         // Any other error is considered unrecoverable. Do not try again.\n       });\n};\n\n\n/**\n * Returns a promise which resolves when the current tab is visible.\n * This resolves quickly if refresh is supposed to run in the background too.\n * @return {!goog.Promise} The promise that resolves when the tab is visible or\n *     that requirement is not needed.\n * @private\n */\nfireauth.ProactiveRefresh.prototype.waitUntilReady_ = function() {\n  // Wait until app is in foreground if required.\n  if (this.runInBackground_) {\n    // If runs in background, resolve quickly.\n    return goog.Promise.resolve();\n  } else {\n    // Wait for the app to be visible before resolving the promise.\n    return fireauth.util.onAppVisible();\n  }\n};\n\n\n/** Stops the proactive refresh from running again. */\nfireauth.ProactiveRefresh.prototype.stop = function() {\n  // If there is a pending promise.\n  if (this.pending_) {\n    // Cancel the pending promise and nullify it.\n    this.pending_.cancel();\n    this.pending_ = null;\n  }\n};\n\n\n/** @return {boolean} Whether the proactive refresh is running or not. */\nfireauth.ProactiveRefresh.prototype.isRunning = function() {\n  return !!this.pending_;\n};\n","/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines the user info pertaining to an identity provider and\n * the Firebase user object.\n */\n\ngoog.provide('fireauth.AuthUser');\ngoog.provide('fireauth.AuthUser.AccountInfo');\ngoog.provide('fireauth.AuthUserInfo');\ngoog.provide('fireauth.TokenRefreshTime');\ngoog.provide('fireauth.UserMetadata');\n\ngoog.require('fireauth.ActionCodeSettings');\ngoog.require('fireauth.AdditionalUserInfo');\ngoog.require('fireauth.AuthCredential');\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.AuthEvent');\ngoog.require('fireauth.AuthEventHandler');\ngoog.require('fireauth.AuthEventManager');\ngoog.require('fireauth.AuthProvider');\ngoog.require('fireauth.ConfirmationResult');\ngoog.require('fireauth.IdTokenResult');\ngoog.require('fireauth.MultiFactorError');\ngoog.require('fireauth.MultiFactorUser');\ngoog.require('fireauth.PhoneAuthProvider');\ngoog.require('fireauth.ProactiveRefresh');\ngoog.require('fireauth.RpcHandler');\ngoog.require('fireauth.StsTokenManager');\ngoog.require('fireauth.UserEvent');\ngoog.require('fireauth.UserEventType');\ngoog.require('fireauth.authenum.Error');\ngoog.require('fireauth.constants');\ngoog.require('fireauth.constants.AuthEventType');\ngoog.require('fireauth.deprecation');\ngoog.require('fireauth.idp');\ngoog.require('fireauth.iframeclient.IfcHandler');\ngoog.require('fireauth.object');\ngoog.require('fireauth.util');\ngoog.require('goog.Promise');\ngoog.require('goog.array');\ngoog.require('goog.events');\ngoog.require('goog.events.Event');\ngoog.require('goog.events.EventTarget');\ngoog.require('goog.object');\n\n\n\n/**\n * Initializes an instance of a user metadata object.\n * @param {?string=} opt_createdAt The optional creation date UTC timestamp.\n * @param {?string=} opt_lastLoginAt The optional last login date UTC timestamp.\n * @constructor\n */\nfireauth.UserMetadata = function(opt_createdAt, opt_lastLoginAt) {\n  /** @private {?string} The created at UTC timestamp. */\n  this.createdAt_ = opt_createdAt || null;\n  /** @private {?string} The last login at UTC timestamp. */\n  this.lastLoginAt_ = opt_lastLoginAt || null;\n  fireauth.object.setReadonlyProperties(this, {\n    'lastSignInTime': fireauth.util.utcTimestampToDateString(\n        opt_lastLoginAt || null),\n    'creationTime': fireauth.util.utcTimestampToDateString(\n        opt_createdAt || null),\n  });\n};\n\n\n/**\n * @return {!fireauth.UserMetadata} A clone of the current user metadata object.\n */\nfireauth.UserMetadata.prototype.clone = function() {\n  return new fireauth.UserMetadata(this.createdAt_, this.lastLoginAt_);\n};\n\n\n/**\n * @return {!Object} The object representation of the user metadata instance.\n */\nfireauth.UserMetadata.prototype.toPlainObject = function() {\n  return {\n    'lastLoginAt': this.lastLoginAt_,\n    'createdAt': this.createdAt_\n  };\n};\n\n\n/**\n * Initializes an instance of the user info for an identity provider.\n * @param {string} uid The user ID.\n * @param {!fireauth.idp.ProviderId} providerId The provider ID.\n * @param {?string=} opt_email The optional user email.\n * @param {?string=} opt_displayName The optional display name.\n * @param {?string=} opt_photoURL The optional photo URL.\n * @param {?string=} opt_phoneNumber The optional phone number.\n * @constructor\n */\nfireauth.AuthUserInfo = function(\n    uid,\n    providerId,\n    opt_email,\n    opt_displayName,\n    opt_photoURL,\n    opt_phoneNumber) {\n  fireauth.object.setReadonlyProperties(this, {\n    'uid': uid,\n    'displayName': opt_displayName || null,\n    'photoURL': opt_photoURL || null,\n    'email': opt_email || null,\n    'phoneNumber': opt_phoneNumber || null,\n    'providerId': providerId\n  });\n};\n\n\n/**\n * Defines the proactive token refresh time constraints in milliseconds.\n * @enum {number}\n */\nfireauth.TokenRefreshTime = {\n  /**\n   * The offset time before token natural expiration to run the refresh.\n   * This is currently 5 minutes.\n   */\n  OFFSET_DURATION: 5 * 60 * 1000,\n  /**\n   * This is the first retrial wait after an error. This is currently\n   * 30 seconds.\n   */\n  RETRIAL_MIN_WAIT: 30 * 1000,\n  /**\n   * This is the maximum retrial wait, currently 16 minutes.\n   */\n  RETRIAL_MAX_WAIT: 16 * 60 * 1000\n};\n\n\n\n/**\n * The Firebase user.\n * @param {!Object} appOptions The application options.\n * @param {!Object} stsTokenResponse The server STS token response.\n * @param {?Object=} opt_accountInfo The optional user account info.\n * @constructor\n * @extends {goog.events.EventTarget}\n * @implements {fireauth.AuthEventHandler}\n */\nfireauth.AuthUser =\n    function(appOptions, stsTokenResponse, opt_accountInfo) {\n  /** @private {!Array<!goog.Promise<*, *>|!goog.Promise<void>>} List of pending\n   *      promises. */\n  this.pendingPromises_ = [];\n  // User is only created via Auth so API key should always be available.\n  /** @private {string} The API key. */\n  this.apiKey_ = /** @type {string} */ (appOptions['apiKey']);\n  // This is needed to associate a user to the corresponding Auth instance.\n  /** @private {string} The App name. */\n  this.appName_ = /** @type {string} */ (appOptions['appName']);\n  /** @private {?string} The Auth domain. */\n  this.authDomain_ = appOptions['authDomain'] || null;\n  var clientFullVersion = firebase.SDK_VERSION ?\n      fireauth.util.getClientVersion(\n          fireauth.util.ClientImplementation.JSCORE, firebase.SDK_VERSION) :\n      null;\n  /** @private {!fireauth.RpcHandler} The RPC handler instance. */\n  this.rpcHandler_ = new fireauth.RpcHandler(\n      this.apiKey_,\n      // Get the client Auth endpoint used.\n      fireauth.constants.getEndpointConfig(fireauth.constants.clientEndpoint),\n    clientFullVersion);\n  /** @private {?fireauth.constants.EmulatorSettings} The emulator config */\n  this.emulatorConfig_ = appOptions['emulatorConfig'] || null;\n  if (this.emulatorConfig_) {\n    this.rpcHandler_.updateEmulatorConfig(this.emulatorConfig_);\n  }\n  // TODO: Consider having AuthUser take a fireauth.StsTokenManager\n  // instance instead of a token response but make sure lastAccessToken_ also\n  // initialized at the right time. In this case initializeFromIdTokenResponse\n  // will take in a token response object and convert it to an instance of\n  // fireauth.StsTokenManager to properly initialize user.\n  /** @private {!fireauth.StsTokenManager} The STS token manager instance. */\n  this.stsTokenManager_ = new fireauth.StsTokenManager(this.rpcHandler_);\n\n  this.setLastAccessToken_(\n      stsTokenResponse[fireauth.RpcHandler.AuthServerField.ID_TOKEN]);\n  // STS token manager will always be populated using server response.\n  this.stsTokenManager_.parseServerResponse(stsTokenResponse);\n  fireauth.object.setReadonlyProperty(\n      this, 'refreshToken', this.stsTokenManager_.getRefreshToken());\n  this.setAccountInfo(/** @type {!fireauth.AuthUser.AccountInfo} */ (\n      opt_accountInfo || {}));\n  // Add call to superclass constructor.\n  fireauth.AuthUser.base(this, 'constructor');\n  /** @private {boolean} Whether popup and redirect is enabled on the user. */\n  this.popupRedirectEnabled_ = false;\n  if (this.authDomain_ &&\n      fireauth.AuthEventManager.ENABLED &&\n      // Make sure popup and redirects are supported in the current environment.\n      fireauth.util.isPopupRedirectSupported()) {\n    // Get the Auth event manager associated with this user.\n    this.authEventManager_ = fireauth.AuthEventManager.getManager(\n        this.authDomain_, this.apiKey_, this.appName_, this.emulatorConfig_);\n  }\n  /** @private {!Array<!function(!fireauth.AuthUser):!goog.Promise>} The list of\n   *      state change listeners. This is needed to make sure state changes are\n   *      resolved before resolving user API promises. For example redirect\n   *      operations should make sure the associated event ID is saved before\n   *      redirecting.\n   */\n  this.stateChangeListeners_ = [];\n  /**\n   * @private {?fireauth.AuthError} The user invalidation error if it exists.\n   */\n  this.userInvalidatedError_ = null;\n  /**\n   * @private {!fireauth.ProactiveRefresh} The reference to the proactive token\n   *     refresher utility for the current user.\n   */\n  this.proactiveRefresh_ = this.initializeProactiveRefreshUtility_();\n  /**\n   * @private {!function(!Object)} The handler for user token changes used to\n   *     realign the proactive token refresh with external token refresh calls.\n   */\n  this.userTokenChangeListener_ = goog.bind(this.handleUserTokenChange_, this);\n  var self = this;\n  /** @private {?string} The current user's language code. */\n  this.languageCode_ = null;\n  /**\n   * @private {function(!goog.events.Event)} The on language code changed event\n   *     handler.\n   */\n  this.onLanguageCodeChanged_ = function(event) {\n    // Update the user language code.\n    self.setLanguageCode(event.languageCode);\n  };\n  /**\n   * @private {?goog.events.EventTarget} The language code change event\n   *     dispatcher.\n   */\n  this.languageCodeChangeEventDispatcher_ = null;\n\n  /**\n   * @private {function(!goog.events.Event)} The on emulator config changed\n   *     event handler.\n   */\n  this.onEmulatorConfigChanged_ = function (event) {\n    // Update the emulator config.\n    self.setEmulatorConfig(event.emulatorConfig);\n  };\n  /**\n   * @private {?goog.events.EventTarget} The emulator code change event\n   *     dispatcher.\n   */\n  this.emulatorConfigChangeEventDispatcher_ = null;\n\n  /** @private {!Array<string>} The current Firebase frameworks. */\n  this.frameworks_ = [];\n  /**\n   * @private {function(!goog.events.Event)} The on framework list changed event\n   *     handler.\n   */\n  this.onFrameworkChanged_ = function(event) {\n    // Update the Firebase frameworks.\n    self.setFramework(event.frameworks);\n  };\n  /**\n   * @private {?goog.events.EventTarget} The framework change event dispatcher.\n   */\n  this.frameworkChangeEventDispatcher_ = null;\n  /**\n   * @const @private {!fireauth.MultiFactorUser} The multifactor user instance.\n   */\n  this.multiFactorUser_ = new fireauth.MultiFactorUser(\n      this, /** @type {?fireauth.AuthUser.AccountInfo|undefined} */ (\n          opt_accountInfo));\n  fireauth.object.setReadonlyProperty(\n      this, 'multiFactor', this.multiFactorUser_);\n};\ngoog.inherits(fireauth.AuthUser, goog.events.EventTarget);\n\n\n/**\n * Updates the user language code.\n * @param {?string} languageCode The current language code to use in user\n *     requests.\n */\nfireauth.AuthUser.prototype.setLanguageCode = function(languageCode) {\n  // Save current language.\n  this.languageCode_ = languageCode;\n  // Update the custom locale header.\n  this.rpcHandler_.updateCustomLocaleHeader(languageCode);\n};\n\n\n/**\n * Updates the emulator config.\n * @param {?fireauth.constants.EmulatorSettings} emulatorConfig The current\n *     emulator config to use in user requests.\n */\nfireauth.AuthUser.prototype.setEmulatorConfig = function(emulatorConfig) {\n  // Update the emulator config.\n  this.emulatorConfig_ = emulatorConfig;\n  this.rpcHandler_.updateEmulatorConfig(emulatorConfig);\n\n  if (this.authEventManager_) {\n    // We need to get a new auth event manager keyed with the new emulator\n    // config.\n    const oldManager = this.authEventManager_;\n\n    // If authEventManager_ was previously set, we know authDomain_ is set as\n    // well.\n    this.authEventManager_ = fireauth.AuthEventManager.getManager(\n        /** @type {string} */ (this.authDomain_), this.apiKey_, this.appName_,\n        this.emulatorConfig_);\n    if (this.popupRedirectEnabled_) {\n      oldManager.unsubscribe(this);\n      this.authEventManager_.subscribe(this);\n    }\n  }\n};\n\n\n/** @return {?string} The current user's language code. */\nfireauth.AuthUser.prototype.getLanguageCode = function() {\n  return this.languageCode_;\n};\n\n\n/**\n * Listens to language code changes triggered by the provided dispatcher.\n * @param {?goog.events.EventTarget} dispatcher The language code changed event\n *     dispatcher.\n */\nfireauth.AuthUser.prototype.setLanguageCodeChangeDispatcher =\n    function(dispatcher) {\n  // Remove any previous listener.\n  if (this.languageCodeChangeEventDispatcher_) {\n    goog.events.unlisten(\n        this.languageCodeChangeEventDispatcher_,\n        fireauth.constants.AuthEventType.LANGUAGE_CODE_CHANGED,\n        this.onLanguageCodeChanged_);\n  }\n  // Update current dispatcher.\n  this.languageCodeChangeEventDispatcher_ = dispatcher;\n  // Using an event listener makes it easy for non-currentUsers to detect\n  // language changes on the parent Auth instance. A developer could still call\n  // APIs that require localization on signed out user references.\n  if (dispatcher) {\n    goog.events.listen(\n        dispatcher,\n        fireauth.constants.AuthEventType.LANGUAGE_CODE_CHANGED,\n        this.onLanguageCodeChanged_);\n  }\n};\n\n\n/**\n  * Listens to emulator config changes triggered by the provided dispatcher.\n  * @param {?goog.events.EventTarget} dispatcher The emulator config changed\n  *     event dispatcher.\n  */\nfireauth.AuthUser.prototype.setEmulatorConfigChangeDispatcher = function(dispatcher) {\n  // Remove any previous listener.\n  if (this.emulatorConfigChangeEventDispatcher_) {\n    goog.events.unlisten(\n      this.emulatorConfigChangeEventDispatcher_,\n      fireauth.constants.AuthEventType.EMULATOR_CONFIG_CHANGED,\n      this.onEmulatorConfigChanged_);\n  }\n  // Update current dispatcher.\n  this.emulatorConfigChangeEventDispatcher_ = dispatcher;\n  // Using an event listener makes it easy for non-currentUsers to detect\n  // emulator changes on the parent Auth instance. A developer could still\n  // call APIs that require emulation on signed out user references.\n  if (dispatcher) {\n    goog.events.listen(\n      dispatcher, fireauth.constants.AuthEventType.EMULATOR_CONFIG_CHANGED,\n      this.onEmulatorConfigChanged_);\n  }\n}\n\n\n/**\n * Updates the Firebase frameworks on the current user.\n * @param {!Array<string>} framework The list of Firebase frameworks.\n */\nfireauth.AuthUser.prototype.setFramework = function(framework) {\n  // Save current frameworks.\n  this.frameworks_ = framework;\n  // Update the client version in RPC handler with the new frameworks.\n  this.rpcHandler_.updateClientVersion(firebase.SDK_VERSION ?\n        fireauth.util.getClientVersion(\n            fireauth.util.ClientImplementation.JSCORE, firebase.SDK_VERSION,\n            this.frameworks_) :\n        null);\n};\n\n\n/** @return {!Array<string>} The current Firebase frameworks. */\nfireauth.AuthUser.prototype.getFramework = function() {\n  return goog.array.clone(this.frameworks_);\n};\n\n\n/**\n * Listens to framework changes triggered by the provided dispatcher.\n * @param {?goog.events.EventTarget} dispatcher The framework changed event\n *     dispatcher.\n */\nfireauth.AuthUser.prototype.setFrameworkChangeDispatcher =\n    function(dispatcher) {\n  // Remove any previous listener.\n  if (this.frameworkChangeEventDispatcher_) {\n    goog.events.unlisten(\n        this.frameworkChangeEventDispatcher_,\n        fireauth.constants.AuthEventType.FRAMEWORK_CHANGED,\n        this.onFrameworkChanged_);\n  }\n  // Update current dispatcher.\n  this.frameworkChangeEventDispatcher_ = dispatcher;\n  // Using an event listener makes it easy for non-currentUsers to detect\n  // framework changes on the parent Auth instance.\n  if (dispatcher) {\n    goog.events.listen(\n        dispatcher,\n        fireauth.constants.AuthEventType.FRAMEWORK_CHANGED,\n        this.onFrameworkChanged_);\n  }\n};\n\n\n/**\n * Handles user token changes. Currently used to realign the proactive token\n * refresh internal timing with successful external token refreshes.\n * @param {!Object} event The token change event.\n * @private\n */\nfireauth.AuthUser.prototype.handleUserTokenChange_ = function(event) {\n  // If an external service refreshes the token, reset the proactive token\n  // refresh utility in case it is still running so the next run time is\n  // up to date.\n  // This will currently also trigger when the proactive refresh succeeds.\n  // This is not ideal but should not have any downsides. It just adds a\n  // redundant reset which can be optimized not to run in the future.\n  if (this.proactiveRefresh_.isRunning()) {\n    this.proactiveRefresh_.stop();\n    this.proactiveRefresh_.start();\n  }\n};\n\n\n/**\n * @return {!fireauth.Auth} The corresponding Auth instance that created the\n *     current user.\n * @private\n */\nfireauth.AuthUser.prototype.getAuth_ = function() {\n  try {\n    // Get the Auth instance for the current app identified by the App name.\n    // This could fail if, for example, the App instance was deleted.\n    return firebase['app'](this.appName_)['auth']();\n  } catch (e) {\n    // Throw appropriate error.\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.INTERNAL_ERROR,\n        'No firebase.auth.Auth instance is available for the Firebase App ' +\n        '\\'' + this.appName_ + '\\'!');\n  }\n};\n\n\n/**\n * @return {string} The user's API key.\n */\nfireauth.AuthUser.prototype.getApiKey = function() {\n  return this.apiKey_;\n};\n\n\n/**\n * Returns the RPC handler of the user.\n * @return {!fireauth.RpcHandler} The RPC handler.\n */\nfireauth.AuthUser.prototype.getRpcHandler = function() {\n  return this.rpcHandler_;\n};\n\n\n/**\n * Used to initialize the current user's proactive token refresher utility.\n * @return {!fireauth.ProactiveRefresh} The user's proactive token refresh\n *     utility.\n * @private\n */\nfireauth.AuthUser.prototype.initializeProactiveRefreshUtility_ = function() {\n  var self = this;\n  return new fireauth.ProactiveRefresh(\n      // Force ID token refresh right before expiration.\n      function() {\n        // Keep in mind when this fails for any reason other than a network\n        // error, it will effectively stop the proactive refresh.\n        return self.getIdToken(true);\n      },\n      // Retry only on network errors.\n      function(error) {\n        if (error && error.code == 'auth/network-request-failed') {\n          return true;\n        }\n        return false;\n      },\n      // Return next time to run with offset applied.\n      function() {\n        // Get time until expiration minus the refresh offset.\n        var waitInterval =\n            self.stsTokenManager_.getExpirationTime() - Date.now() -\n            fireauth.TokenRefreshTime.OFFSET_DURATION;\n        // Set to zero if wait interval is negative.\n        return waitInterval > 0 ? waitInterval : 0;\n      },\n      // Retrial minimum wait.\n      fireauth.TokenRefreshTime.RETRIAL_MIN_WAIT,\n      // Retrial maximum wait.\n      fireauth.TokenRefreshTime.RETRIAL_MAX_WAIT,\n      // Do not run in background as it is common to have multiple tabs open\n      // in a browser and this could increase QPS on server.\n      false);\n};\n\n\n/** Starts token proactive refresh. */\nfireauth.AuthUser.prototype.startProactiveRefresh = function() {\n  // Only allow if not destroyed and not already started.\n  if (!this.destroyed_ && !this.proactiveRefresh_.isRunning()) {\n    this.proactiveRefresh_.start();\n    // Unlisten any previous token change listener.\n    goog.events.unlisten(\n        this,\n        fireauth.UserEventType.TOKEN_CHANGED,\n        this.userTokenChangeListener_);\n    // Listen to token changes to reset the token refresher.\n    goog.events.listen(\n        this,\n        fireauth.UserEventType.TOKEN_CHANGED,\n        this.userTokenChangeListener_);\n  }\n};\n\n\n/** Stops token proactive refresh. */\nfireauth.AuthUser.prototype.stopProactiveRefresh = function() {\n  // Remove internal token change listener.\n  goog.events.unlisten(\n      this,\n      fireauth.UserEventType.TOKEN_CHANGED,\n      this.userTokenChangeListener_);\n  // Stop proactive token refresh.\n  this.proactiveRefresh_.stop();\n};\n\n\n/**\n * Sets latest access token for the AuthUser object.\n * @param {string} lastAccessToken\n * @private\n */\nfireauth.AuthUser.prototype.setLastAccessToken_ = function(lastAccessToken) {\n  /** @private {?string} Latest access token. */\n  this.lastAccessToken_ = lastAccessToken;\n  fireauth.object.setReadonlyProperty(this, '_lat', lastAccessToken);\n};\n\n\n/**\n * @param {function(!fireauth.AuthUser):!goog.Promise} listener The listener\n *     to state changes to add.\n */\nfireauth.AuthUser.prototype.addStateChangeListener = function(listener) {\n  this.stateChangeListeners_.push(listener);\n};\n\n\n/**\n * @param {function(!fireauth.AuthUser):!goog.Promise} listener The listener\n *     to state changes to remove.\n */\nfireauth.AuthUser.prototype.removeStateChangeListener = function(listener) {\n  goog.array.removeAllIf(this.stateChangeListeners_, function(ele) {\n    return ele == listener;\n  });\n};\n\n\n/**\n * Executes all state change listener promises and when all fulfilled, resolves\n * with the current user.\n * @return {!goog.Promise} A promise that resolves when all state listeners\n *     fulfilled.\n * @private\n */\nfireauth.AuthUser.prototype.notifyStateChangeListeners_ = function() {\n  var promises = [];\n  var self = this;\n  for (var i = 0; i < this.stateChangeListeners_.length; i++) {\n    // Run listener with Auth user instance and add to list of promises.\n    promises.push(this.stateChangeListeners_[i](this));\n  }\n  return goog.Promise.allSettled(promises).then(function(results) {\n    // State change errors should be recoverable even if errors occur.\n    return self;\n  });\n};\n\n\n/**\n * Sets the user current pending popup event ID.\n * @param {string} eventId The pending popup event ID.\n */\nfireauth.AuthUser.prototype.setPopupEventId = function(eventId) {\n  // Saving a popup event in a separate property other than redirectEventId\n  // would prevent a pending redirect event from being overwritten by a newly\n  // called popup operation.\n  this.popupEventId_ = eventId;\n};\n\n\n/**\n * @return {?string} The pending popup event ID.\n */\nfireauth.AuthUser.prototype.getPopupEventId = function() {\n  return this.popupEventId_ || null;\n};\n\n\n/**\n * Sets the user current pending redirect event ID.\n * @param {string} eventId The pending redirect event ID.\n */\nfireauth.AuthUser.prototype.setRedirectEventId = function(eventId) {\n  this.redirectEventId_ = eventId;\n};\n\n\n/**\n * @return {?string} The pending redirect event ID.\n */\nfireauth.AuthUser.prototype.getRedirectEventId = function() {\n  return this.redirectEventId_ || null;\n};\n\n\n/**\n * Subscribes to Auth event manager to handle popup and redirect events.\n * This is an explicit operation as users could exist in temporary states. For\n * example a user change could be detected in another tab. When syncing to those\n * changes, a temporary user is retrieved from storage and then copied to\n * existing user. The temporary user should not subscribe to Auth event changes.\n */\nfireauth.AuthUser.prototype.enablePopupRedirect = function() {\n  // Subscribe to Auth event manager if available.\n  if (this.authEventManager_ && !this.popupRedirectEnabled_) {\n    this.popupRedirectEnabled_ = true;\n    this.authEventManager_.subscribe(this);\n  }\n};\n\n\n/**\n * getAccountInfo users field.\n * @const {string}\n */\nfireauth.AuthUser.GET_ACCOUNT_INFO_USERS = 'users';\n\n\n/**\n * getAccountInfo response user fields.\n * @enum {string}\n */\nfireauth.AuthUser.GetAccountInfoField = {\n  CREATED_AT: 'createdAt',\n  DISPLAY_NAME: 'displayName',\n  EMAIL: 'email',\n  EMAIL_VERIFIED: 'emailVerified',\n  LAST_LOGIN_AT: 'lastLoginAt',\n  LOCAL_ID: 'localId',\n  PASSWORD_HASH: 'passwordHash',\n  PASSWORD_UPDATED_AT: 'passwordUpdatedAt',\n  PHONE_NUMBER: 'phoneNumber',\n  PHOTO_URL: 'photoUrl',\n  PROVIDER_USER_INFO: 'providerUserInfo',\n  TENANT_ID: 'tenantId'\n};\n\n\n/**\n * setAccountInfo response user fields.\n * @enum {string}\n */\nfireauth.AuthUser.SetAccountInfoField = {\n  DISPLAY_NAME: 'displayName',\n  EMAIL: 'email',\n  PHOTO_URL: 'photoUrl',\n  PROVIDER_ID: 'providerId',\n  PROVIDER_USER_INFO: 'providerUserInfo'\n};\n\n\n/**\n * getAccountInfo response provider user info fields.\n * @enum {string}\n */\nfireauth.AuthUser.GetAccountInfoProviderField = {\n  DISPLAY_NAME: 'displayName',\n  EMAIL: 'email',\n  PHOTO_URL: 'photoUrl',\n  PHONE_NUMBER: 'phoneNumber',\n  PROVIDER_ID: 'providerId',\n  RAW_ID: 'rawId'\n};\n\n\n/**\n * verifyAssertion response fields.\n * @enum {string}\n */\nfireauth.AuthUser.VerifyAssertionField = {\n  ID_TOKEN: 'idToken',\n  PROVIDER_ID: 'providerId'\n};\n\n\n/** @return {!fireauth.StsTokenManager} The STS token manager instance */\nfireauth.AuthUser.prototype.getStsTokenManager = function() {\n  return this.stsTokenManager_;\n};\n\n\n/**\n * Sets the user account info.\n * @param {!fireauth.AuthUser.AccountInfo} accountInfo The account information\n *     from the default provider.\n */\nfireauth.AuthUser.prototype.setAccountInfo = function(accountInfo) {\n  fireauth.object.setReadonlyProperties(this, {\n    'uid': accountInfo['uid'],\n    'displayName': accountInfo['displayName'] || null,\n    'photoURL': accountInfo['photoURL'] || null,\n    'email': accountInfo['email'] || null,\n    'emailVerified': accountInfo['emailVerified'] || false,\n    'phoneNumber': accountInfo['phoneNumber'] || null,\n    'isAnonymous': accountInfo['isAnonymous'] || false,\n    'tenantId': accountInfo['tenantId'] || null,\n    'metadata': new fireauth.UserMetadata(\n        accountInfo['createdAt'], accountInfo['lastLoginAt']),\n    'providerData': []\n  });\n  // Sets the tenant ID on RPC handler. For requests with ID tokens, the source\n  // of truth is the tenant ID in the ID token. If the request body has a\n  // tenant ID (optional here), the backend will confirm it matches the\n  // tenant ID in the ID token, otherwise throw an error. If no tenant ID is\n  // passed in the request, it will be determined from the ID token.\n  this.rpcHandler_.updateTenantId(this['tenantId']);\n};\n\n\n/**\n * Type specifying the parameters that can be passed to the\n * {@code fireauth.AuthUser} constructor.\n * @typedef {{\n *   uid: (?string|undefined),\n *   displayName: (?string|undefined),\n *   photoURL: (?string|undefined),\n *   email: (?string|undefined),\n *   emailVerified: ?boolean,\n *   phoneNumber: (?string|undefined),\n *   isAnonymous: ?boolean,\n *   createdAt: (?string|undefined),\n *   lastLoginAt: (?string|undefined),\n *   tenantId: (?string|undefined),\n *   multiFactor: ({\n *     enrolledFactors: (?Array<!fireauth.MultiFactorInfo>|undefined)\n *   }|undefined)\n * }}\n */\nfireauth.AuthUser.AccountInfo;\n\n\n/**\n * The provider for all fireauth.AuthUser objects is 'firebase'.\n */\nfireauth.object.setReadonlyProperty(fireauth.AuthUser.prototype, 'providerId',\n    fireauth.idp.ProviderId.FIREBASE);\n\n\n/**\n * Returns nothing. This can be used to consume the output of a Promise.\n * @private\n */\nfireauth.AuthUser.returnNothing_ = function() {\n  // Return nothing. Intentionally left empty.\n};\n\n\n/**\n * Ensures the user is still logged in before moving to the next promise\n * resolution.\n * @return {!goog.Promise<undefined,undefined>}\n * @private\n */\nfireauth.AuthUser.prototype.checkDestroyed_ = function() {\n  var self = this;\n  return goog.Promise.resolve().then(function() {\n    if (self.destroyed_) {\n      throw new fireauth.AuthError(fireauth.authenum.Error.MODULE_DESTROYED);\n    }\n  });\n};\n\n\n/**\n * @return {!Array<!fireauth.idp.ProviderId>} The list of provider IDs.\n */\nfireauth.AuthUser.prototype.getProviderIds = function() {\n  return goog.array.map(this['providerData'], function(userInfo) {\n    return userInfo['providerId'];\n  });\n};\n\n\n/**\n * Adds the provided user info to list of providers' data.\n * @param {?fireauth.AuthUserInfo} providerData Provider data to store for user.\n */\nfireauth.AuthUser.prototype.addProviderData = function(providerData) {\n  if (!providerData) {\n    return;\n  }\n  this.removeProviderData(providerData['providerId']);\n  this['providerData'].push(providerData);\n};\n\n\n/**\n * @param {!fireauth.idp.ProviderId} providerId The provider ID whose\n *     data should be removed.\n */\nfireauth.AuthUser.prototype.removeProviderData = function(providerId) {\n  goog.array.removeAllIf(this['providerData'], function(userInfo) {\n    return userInfo['providerId'] == providerId;\n  });\n};\n\n\n/**\n * @param {string} propName The property name to modify.\n * @param {?string|boolean} value The new value to set.\n */\nfireauth.AuthUser.prototype.updateProperty = function(propName, value) {\n  // User ID is required.\n  if (propName == 'uid' && !value) {\n    return;\n  }\n  if (this.hasOwnProperty(propName)) {\n    fireauth.object.setReadonlyProperty(this, propName, value);\n  }\n};\n\n\n/**\n * @param {!fireauth.AuthUser} otherUser The other user to compare to.\n * @return {boolean} True if both User objects have the same user ID.\n */\nfireauth.AuthUser.prototype.hasSameUserIdAs = function(otherUser) {\n  var thisId = this['uid'];\n  var thatId = otherUser['uid'];\n  if (thisId === undefined || thisId === null || thisId === '' ||\n      thatId === undefined || thatId === null || thatId === '') {\n    return false;\n  }\n  return thisId == thatId;\n};\n\n\n/**\n * Copies all properties and STS token manager instance from userToCopy to\n * current user without triggering any Auth state change or token change\n * listener.\n * @param {!fireauth.AuthUser} userToCopy The updated user to overwrite current\n *     user.\n */\nfireauth.AuthUser.prototype.copy = function(userToCopy) {\n  var self = this;\n  // Copy to self.\n  if (self == userToCopy) {\n    return;\n  }\n  fireauth.object.setReadonlyProperties(this, {\n    'uid': userToCopy['uid'],\n    'displayName': userToCopy['displayName'],\n    'photoURL': userToCopy['photoURL'],\n    'email': userToCopy['email'],\n    'emailVerified': userToCopy['emailVerified'],\n    'phoneNumber': userToCopy['phoneNumber'],\n    'isAnonymous': userToCopy['isAnonymous'],\n    'tenantId': userToCopy['tenantId'],\n    'providerData': []\n  });\n  // This should always be available but just in case there is a conflict with\n  // a user from an older version.\n  if (userToCopy['metadata']) {\n    fireauth.object.setReadonlyProperty(\n        this,\n        'metadata',\n        /** @type{!fireauth.UserMetadata} */ (userToCopy['metadata']).clone());\n  } else {\n    // User to copy has no metadata. Align with that.\n    fireauth.object.setReadonlyProperty(\n        this, 'metadata', new fireauth.UserMetadata());\n  }\n  goog.array.forEach(userToCopy['providerData'], function(userInfo) {\n    self.addProviderData(userInfo);\n  });\n  this.stsTokenManager_.copy(userToCopy.getStsTokenManager());\n  fireauth.object.setReadonlyProperty(\n      this, 'refreshToken', this.stsTokenManager_.getRefreshToken());\n  // Copy multi-factor info to current user.\n  // This should be backward compatible.\n  // If the userToCopy is loaded from an older version, multiFactorUser\n  // enrolled factors will be initialized empty and copied empty to current\n  // multiFactorUser.\n  this.multiFactorUser_.copy(userToCopy.multiFactorUser_);\n};\n\n\n/**\n * Set the Auth user redirect storage manager.\n * @param {?fireauth.storage.RedirectUserManager} redirectStorageManager The\n *     utility used to store or delete the user on redirect.\n */\nfireauth.AuthUser.prototype.setRedirectStorageManager =\n    function(redirectStorageManager) {\n  /**\n   * @private {?fireauth.storage.RedirectUserManager} The redirect user storage\n   *     manager.\n   */\n  this.redirectStorageManager_ = redirectStorageManager;\n};\n\n\n/**\n * Refreshes the current user, if signed in.\n * @return {!goog.Promise<void>}\n */\nfireauth.AuthUser.prototype.reload = function() {\n  var self = this;\n  // Register this pending promise. This will also check for user invalidation.\n  return this.registerPendingPromise_(this.checkDestroyed_().then(function() {\n    return self.reloadWithoutSaving_()\n        .then(function() {\n          return self.notifyStateChangeListeners_();\n        })\n        .then(fireauth.AuthUser.returnNothing_);\n  }));\n};\n\n\n/**\n * Refreshes the current user, if signed in.\n * @return {!goog.Promise<string>} Promise that resolves with the idToken.\n * @private\n */\nfireauth.AuthUser.prototype.reloadWithoutSaving_ = function() {\n  var self = this;\n  // ID token is required to refresh the user's data.\n  // If this is called after invalidation, getToken will throw the cached error.\n  return this.getIdToken().then(function(idToken) {\n    var isAnonymous = self['isAnonymous'];\n    return self.setUserAccountInfoFromToken_(idToken)\n        .then(function(user) {\n          if (!isAnonymous) {\n            // Preserves the not anonymous status of the stored user,\n            // even if no more credentials (federated or email/password)\n            // linked to the user.\n            self.updateProperty('isAnonymous', false);\n          }\n          return idToken;\n        });\n  });\n};\n\n\n/**\n * This operation resolves with the Firebase ID token result which contains\n * the entire payload claims.\n * @param {boolean=} opt_forceRefresh Whether to force refresh token exchange.\n * @return {!goog.Promise<!fireauth.IdTokenResult>} A Promise that resolves with\n *     the ID token result.\n */\nfireauth.AuthUser.prototype.getIdTokenResult = function(opt_forceRefresh) {\n  return this.getIdToken(opt_forceRefresh).then(function(idToken) {\n    return new fireauth.IdTokenResult(idToken);\n  });\n};\n\n\n/**\n * This operation resolves with the Firebase ID token.\n * @param {boolean=} opt_forceRefresh Whether to force refresh token exchange.\n * @return {!goog.Promise<string>} A Promise that resolves with the ID token.\n */\nfireauth.AuthUser.prototype.getIdToken = function(opt_forceRefresh) {\n  var self = this;\n  // Register this pending promise. This will also check for user invalidation.\n  return this.registerPendingPromise_(this.checkDestroyed_().then(function() {\n    return self.stsTokenManager_.getToken(opt_forceRefresh);\n  }).then(function(response) {\n    if (!response) {\n      // If the user exists, the token manager should be initialized.\n      throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR);\n    }\n    // Only if the access token is refreshed, notify Auth listeners.\n    if (response['accessToken'] != self.lastAccessToken_) {\n      self.setLastAccessToken_(response['accessToken']);\n      // Auth state change, notify listeners.\n      self.notifyAuthListeners_();\n    }\n    self.updateProperty('refreshToken', response['refreshToken']);\n    return response['accessToken'];\n  }));\n};\n\n\n/**\n * Checks if the error corresponds to a user invalidation action.\n * @param {*} error The error returned by a user operation.\n * @return {boolean} Whether the user is invalidated based on the error\n *     provided.\n * @private\n */\nfireauth.AuthUser.isUserInvalidated_ = function(error) {\n  return !!(error &&\n      (error.code == 'auth/user-disabled' ||\n       error.code == 'auth/user-token-expired'));\n};\n\n\n/**\n * Updates the current tokens using a server response, if new tokens are\n * present and are different from the current ones, and notify the Auth\n * listeners.\n * @param {!Object} response The response from the server.\n */\nfireauth.AuthUser.prototype.updateTokensIfPresent = function(response) {\n  if (response[fireauth.RpcHandler.AuthServerField.ID_TOKEN] &&\n      this.lastAccessToken_ != response[\n          fireauth.RpcHandler.AuthServerField.ID_TOKEN]) {\n    this.stsTokenManager_.parseServerResponse(response);\n    this.notifyAuthListeners_();\n    this.setLastAccessToken_(response[\n        fireauth.RpcHandler.AuthServerField.ID_TOKEN]);\n    // Update refresh token property.\n    this.updateProperty(\n        'refreshToken', this.stsTokenManager_.getRefreshToken());\n  }\n};\n\n\n/**\n * Called internally on Auth (access token) changes to notify listeners.\n * @private\n */\nfireauth.AuthUser.prototype.notifyAuthListeners_ = function() {\n  this.dispatchEvent(\n      new fireauth.UserEvent(fireauth.UserEventType.TOKEN_CHANGED));\n};\n\n\n/**\n * Called internally on user deletion to notify listeners.\n * @private\n */\nfireauth.AuthUser.prototype.notifyUserDeletedListeners_ = function() {\n  this.dispatchEvent(\n      new fireauth.UserEvent(fireauth.UserEventType.USER_DELETED));\n};\n\n\n/**\n * Called internally on user session invalidation to notify listeners.\n * @private\n */\nfireauth.AuthUser.prototype.notifyUserInvalidatedListeners_ = function() {\n  this.dispatchEvent(\n      new fireauth.UserEvent(fireauth.UserEventType.USER_INVALIDATED));\n};\n\n\n/**\n * Queries the backend using the provided ID token for all linked accounts to\n * build the Firebase user object.\n * @param {string} idToken The ID token string.\n * @return {!goog.Promise<undefined>}\n * @private\n */\nfireauth.AuthUser.prototype.setUserAccountInfoFromToken_ = function (idToken) {\n  return this.rpcHandler_.getAccountInfoByIdToken(idToken)\n      .then(goog.bind(this.parseAccountInfo_, this));\n};\n\n\n/**\n * Parses the response from the getAccountInfo endpoint.\n * @param {!Object} resp The backend response.\n * @private\n */\nfireauth.AuthUser.prototype.parseAccountInfo_ = function(resp) {\n  var users = resp[fireauth.AuthUser.GET_ACCOUNT_INFO_USERS];\n  if (!users || !users.length) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR);\n  }\n  var user = users[0];\n  var accountInfo = /** @type {!fireauth.AuthUser.AccountInfo} */ ({\n    'uid': /** @type {string} */ (\n        user[fireauth.AuthUser.GetAccountInfoField.LOCAL_ID]),\n    'displayName': /** @type {?string|undefined} */ (\n        user[fireauth.AuthUser.GetAccountInfoField.DISPLAY_NAME]),\n    'photoURL': /** @type {?string|undefined} */ (\n        user[fireauth.AuthUser.GetAccountInfoField.PHOTO_URL]),\n    'email': /** @type {?string|undefined} */ (\n        user[fireauth.AuthUser.GetAccountInfoField.EMAIL]),\n    'emailVerified':\n        !!user[fireauth.AuthUser.GetAccountInfoField.EMAIL_VERIFIED],\n    'phoneNumber': /** @type {?string|undefined} */ (\n        user[fireauth.AuthUser.GetAccountInfoField.PHONE_NUMBER]),\n    'lastLoginAt': /** @type {?string|undefined} */ (\n        user[fireauth.AuthUser.GetAccountInfoField.LAST_LOGIN_AT]),\n    'createdAt': /** @type {?string|undefined} */ (\n        user[fireauth.AuthUser.GetAccountInfoField.CREATED_AT]),\n    'tenantId': /** @type {?string|undefined} */ (\n        user[fireauth.AuthUser.GetAccountInfoField.TENANT_ID])\n  });\n  this.setAccountInfo(accountInfo);\n  var linkedAccounts = this.extractLinkedAccounts_(user);\n  for (var i = 0; i < linkedAccounts.length; i++) {\n    this.addProviderData(linkedAccounts[i]);\n  }\n  // Sets the isAnonymous flag based on email, passwordHash and providerData.\n  var isAnonymous = !(this['email'] &&\n      user[fireauth.AuthUser.GetAccountInfoField.PASSWORD_HASH]) &&\n      !(this['providerData'] && this['providerData'].length);\n  this.updateProperty('isAnonymous', isAnonymous);\n  // Notify external listeners of the reload.\n  this.dispatchEvent(new fireauth.UserEvent(\n      fireauth.UserEventType.USER_RELOADED,\n      {userServerResponse: user}));\n};\n\n\n/**\n * Extracts the linked accounts from getAccountInfo response and returns an\n * array of corresponding provider data.\n * @param {!Object} resp The response object.\n * @return {!Array<!fireauth.AuthUserInfo>} The linked accounts.\n * @private\n */\nfireauth.AuthUser.prototype.extractLinkedAccounts_ = function(resp) {\n  var providerInfo =\n      resp[fireauth.AuthUser.GetAccountInfoField.PROVIDER_USER_INFO];\n  if (!providerInfo || !providerInfo.length) {\n    return [];\n  }\n\n  return goog.array.map(providerInfo, function(info) {\n    return new fireauth.AuthUserInfo(\n        info[fireauth.AuthUser.GetAccountInfoProviderField.RAW_ID],\n        info[fireauth.AuthUser.GetAccountInfoProviderField.PROVIDER_ID],\n        info[fireauth.AuthUser.GetAccountInfoProviderField.EMAIL],\n        info[fireauth.AuthUser.GetAccountInfoProviderField.DISPLAY_NAME],\n        info[fireauth.AuthUser.GetAccountInfoProviderField.PHOTO_URL],\n        info[fireauth.AuthUser.GetAccountInfoProviderField.PHONE_NUMBER]);\n  });\n};\n\n\n/**\n * Reauthenticates a user using a fresh credential, to be used before operations\n * such as updatePassword that require tokens from recent login attempts. It\n * also returns any additional user info data or credentials returned form the\n * backend. It has been deprecated in favor of reauthenticateWithCredential.\n * @param {!fireauth.AuthCredential} credential\n * @return {!goog.Promise<!fireauth.AuthEventManager.Result>}\n */\nfireauth.AuthUser.prototype.reauthenticateAndRetrieveDataWithCredential =\n    function(credential) {\n  fireauth.deprecation.log(\n      fireauth.deprecation.Deprecations.REAUTH_WITH_CREDENTIAL);\n  return this.reauthenticateWithCredential(credential);\n};\n\n\n/**\n * Reauthenticates a user using a fresh credential, to be used before operations\n * such as updatePassword that require tokens from recent login attempts. It\n * also returns any additional user info data or credentials returned form the\n * backend.\n * @param {!fireauth.AuthCredential} credential\n * @return {!goog.Promise<!fireauth.AuthEventManager.Result>}\n */\nfireauth.AuthUser.prototype.reauthenticateWithCredential =\n    function(credential) {\n  var self = this;\n  var userCredential = null;\n  // Register this pending promise but bypass user invalidation check.\n  return this.registerPendingPromise_(\n      // Match ID token from credential with the current user UID.\n      credential.matchIdTokenWithUid(this.rpcHandler_, this['uid'])\n      .then(function(response) {\n        // If the credential is valid and matches the current user ID, then\n        // update the tokens accordingly.\n        self.updateTokensIfPresent(response);\n        // Get user credential.\n        userCredential = self.getUserCredential_(\n            response, fireauth.constants.OperationType.REAUTHENTICATE);\n        // This could potentially validate an invalidated user. This happens in\n        // the case a password reset was applied. The refresh token is expired.\n        // Reauthentication should revalidate the user.\n        // User would remain non current if already signed out, but should be\n        // enabled again.\n        self.userInvalidatedError_ = null;\n        return self.reload();\n      }).then(function() {\n        // Return user credential after reauthenticated user is reloaded.\n        return userCredential;\n      }),\n      // Skip invalidation check as reauthentication could revalidate a user.\n      true);\n};\n\n\n/**\n * Reloads the user and then checks if a provider is already linked. If so,\n * this returns a Promise that rejects. Note that state change listeners are not\n * notified on success, so that operations using this can make changes and then\n * do one final listener notification.\n * @param {string} providerId\n * @return {!goog.Promise<void>}\n * @private\n */\nfireauth.AuthUser.prototype.checkIfAlreadyLinked_ =\n    function(providerId) {\n  var self = this;\n  // Reload first in case the user was updated elsewhere.\n  return this.reloadWithoutSaving_()\n      .then(function() {\n        if (goog.array.contains(self.getProviderIds(), providerId)) {\n          return self.notifyStateChangeListeners_()\n              .then(function() {\n                  throw new fireauth.AuthError(\n                      fireauth.authenum.Error.PROVIDER_ALREADY_LINKED);\n              });\n        }\n      });\n};\n\n\n/**\n * Links a provider to the current user and returns any additional user info\n * data or credentials returned form the backend. It has been deprecated in\n * favor of linkWithCredential.\n * @param {!fireauth.AuthCredential} credential The credential from the Auth\n *     provider.\n * @return {!goog.Promise<!fireauth.AuthEventManager.Result>}\n */\nfireauth.AuthUser.prototype.linkAndRetrieveDataWithCredential =\n    function(credential) {\n  fireauth.deprecation.log(\n      fireauth.deprecation.Deprecations.LINK_WITH_CREDENTIAL);\n  return this.linkWithCredential(credential);\n};\n\n\n/**\n * Links a provider to the current user and returns any additional user info\n * data or credentials returned form the backend.\n * @param {!fireauth.AuthCredential} credential The credential from the Auth\n *     provider.\n * @return {!goog.Promise<!fireauth.AuthEventManager.Result>}\n */\nfireauth.AuthUser.prototype.linkWithCredential = function(credential) {\n  var self = this;\n  var userCredential = null;\n  // Register this pending promise. This will also check for user invalidation.\n  return this.registerPendingPromise_(\n      this.checkIfAlreadyLinked_(credential['providerId'])\n      .then(function() {\n        return self.getIdToken();\n      })\n      .then(function(idToken) {\n        return credential.linkToIdToken(self.rpcHandler_, idToken);\n      })\n      .then(function(response) {\n        // Get user credential.\n        userCredential = self.getUserCredential_(\n            response, fireauth.constants.OperationType.LINK);\n        // Finalize linking.\n        return self.finalizeLinking_(response);\n      })\n      .then(function(user) {\n        // Return user credential after finalizing linking.\n        return userCredential;\n      })\n  );\n};\n\n\n/**\n * Links a phone number using the App verifier instance and returns a\n * promise that resolves with the confirmation result which on confirmation\n * will resolve with the UserCredential object.\n * @param {string} phoneNumber The phone number to authenticate with.\n * @param {!firebase.auth.ApplicationVerifier} appVerifier The application\n *     verifier.\n * @return {!goog.Promise<!fireauth.ConfirmationResult>}\n */\nfireauth.AuthUser.prototype.linkWithPhoneNumber =\n    function(phoneNumber, appVerifier) {\n  var self = this;\n  return /** @type {!goog.Promise<!fireauth.ConfirmationResult>} */ (\n      this.registerPendingPromise_(\n          // Check if linked already. If so, throw an error.\n          // This is redundant but is needed to prevent the need to send the\n          // SMS (worth the cost).\n          this.checkIfAlreadyLinked_(fireauth.idp.ProviderId.PHONE)\n              .then(function() {\n                return fireauth.ConfirmationResult.initialize(\n                    self.getAuth_(),\n                    phoneNumber,\n                    appVerifier,\n                    // This will check again if the credential is linked.\n                    goog.bind(self.linkWithCredential, self));\n              })));\n};\n\n\n/**\n * Reauthenticates a user with a phone number using the App verifier instance\n * and returns a promise that resolves with the confirmation result which on\n * confirmation will resolve with the UserCredential object.\n * @param {string} phoneNumber The phone number to authenticate with.\n * @param {!firebase.auth.ApplicationVerifier} appVerifier The application\n *     verifier.\n * @return {!goog.Promise<!fireauth.ConfirmationResult>}\n */\nfireauth.AuthUser.prototype.reauthenticateWithPhoneNumber =\n    function(phoneNumber, appVerifier) {\n  var self = this;\n  return /** @type {!goog.Promise<!fireauth.ConfirmationResult>} */ (\n      this.registerPendingPromise_(\n          // Wrap this operation in a Promise since self.getAuth_() may throw an\n          // error synchronously.\n          goog.Promise.resolve().then(function() {\n            return fireauth.ConfirmationResult.initialize(\n                // Get corresponding Auth instance.\n                self.getAuth_(),\n                phoneNumber,\n                appVerifier,\n                goog.bind(self.reauthenticateWithCredential,\n                    self));\n          }),\n          // Skip invalidation check as reauthentication could revalidate a\n          // user.\n          true));\n};\n\n\n/**\n * Converts an ID token response (eg. verifyAssertion) to a UserCredential\n * object.\n * @param {!Object} idTokenResponse The ID token response.\n * @param {!fireauth.constants.OperationType} operationType The operation type\n *     to set in the user credential.\n * @return {!fireauth.AuthEventManager.Result} The UserCredential object\n *     constructed from the response.\n * @private\n */\nfireauth.AuthUser.prototype.getUserCredential_ =\n    function(idTokenResponse, operationType) {\n  // Get credential if available in the response.\n  var credential = fireauth.AuthProvider.getCredentialFromResponse(\n      idTokenResponse);\n  // Get additional user info data if available in the response.\n  var additionalUserInfo = fireauth.AdditionalUserInfo.fromPlainObject(\n      idTokenResponse);\n  // Return the readonly copy of the user credential object.\n  return fireauth.object.makeReadonlyCopy({\n    // Return the current user reference.\n    'user': this,\n    // Return any credential passed from the backend.\n    'credential': credential,\n    // Return any additional IdP data passed from the backend.\n    'additionalUserInfo': additionalUserInfo,\n    // Return the operation type in the user credential object.\n    'operationType': operationType\n  });\n};\n\n\n/**\n * Finalizes a linking flow, updating idToken and user's data using the\n * RPC linking response.\n * @param {!Object} response The RPC linking response.\n * @return {!goog.Promise<!fireauth.AuthUser>}\n * @private\n */\nfireauth.AuthUser.prototype.finalizeLinking_ = function(response) {\n  // The response may contain a new access token,\n  // so we should update them just like a new sign in.\n  this.updateTokensIfPresent(response);\n  // This will take care of saving the updated state.\n  var self = this;\n  return this.reload().then(function() {\n    return self;\n  });\n};\n\n\n/**\n * Updates the user's email.\n * @param {string} newEmail The new email.\n * @return {!goog.Promise<void>}\n */\nfireauth.AuthUser.prototype.updateEmail = function(newEmail) {\n  var self = this;\n  // Register this pending promise. This will also check for user invalidation.\n  return this.registerPendingPromise_(this.getIdToken()\n      .then(function(idToken) {\n        return self.rpcHandler_.updateEmail(idToken, newEmail);\n      })\n      .then(function(response) {\n        // Calls to SetAccountInfo may invalidate old tokens.\n        self.updateTokensIfPresent(response);\n        // Reloads the user to update emailVerified.\n        return self.reload();\n      }));\n};\n\n\n/**\n * Updates the user's phone number.\n * @param {!fireauth.PhoneAuthCredential} phoneCredential\n * @return {!goog.Promise<void>}\n */\nfireauth.AuthUser.prototype.updatePhoneNumber = function(phoneCredential) {\n  var self = this;\n  return this.registerPendingPromise_(this.getIdToken()\n      .then(function(idToken) {\n        // The backend always overwrites the existing phone number during a\n        // link operation.\n        return phoneCredential.linkToIdToken(self.rpcHandler_, idToken);\n      })\n      .then(function(response) {\n        self.updateTokensIfPresent(response);\n        return self.reload();\n      }));\n};\n\n\n/**\n * Updates the user's password.\n * @param {string} newPassword The new password.\n * @return {!goog.Promise<void>}\n */\nfireauth.AuthUser.prototype.updatePassword = function(newPassword) {\n  var self = this;\n  // Register this pending promise. This will also check for user invalidation.\n  return this.registerPendingPromise_(\n      this.getIdToken()\n      .then(function(idToken) {\n        return self.rpcHandler_.updatePassword(idToken, newPassword);\n      })\n      .then(function(response) {\n        self.updateTokensIfPresent(response);\n        // Reloads the user in case email has also been updated and the user\n        // was anonymous.\n        return self.reload();\n      }));\n};\n\n\n/**\n * Updates the user's profile data.\n * @param {!Object} profile The profile data to update.\n * @return {!goog.Promise<undefined>}\n */\nfireauth.AuthUser.prototype.updateProfile = function(profile) {\n  if (profile['displayName'] === undefined &&\n      profile['photoURL'] === undefined) {\n    // No change, directly return.\n    return this.checkDestroyed_();\n  }\n  var self = this;\n  // Register this pending promise. This will also check for user invalidation.\n  return this.registerPendingPromise_(\n      this.getIdToken().then(function(idToken) {\n        // Translate the request into one that the backend accepts.\n        var profileRequest = {\n          'displayName': profile['displayName'],\n          'photoUrl': profile['photoURL']\n        };\n        return self.rpcHandler_.updateProfile(idToken, profileRequest);\n      })\n      .then(function(response) {\n        // Calls to SetAccountInfo may invalidate old tokens.\n        self.updateTokensIfPresent(response);\n        // Update properties.\n        self.updateProperty('displayName',\n            response[fireauth.AuthUser.SetAccountInfoField.DISPLAY_NAME] ||\n            null);\n        self.updateProperty('photoURL',\n            response[fireauth.AuthUser.SetAccountInfoField.PHOTO_URL] || null);\n        goog.array.forEach(self['providerData'], function(userInfo) {\n          // Check if password provider is linked.\n          if (userInfo['providerId'] === fireauth.idp.ProviderId.PASSWORD) {\n            // If so, update both fields in that provider.\n            fireauth.object.setReadonlyProperty(\n                userInfo, 'displayName', self['displayName']);\n            fireauth.object.setReadonlyProperty(\n                userInfo, 'photoURL', self['photoURL']);\n          }\n        });\n        // Notify changes and resolve.\n        return self.notifyStateChangeListeners_();\n      })\n      .then(fireauth.AuthUser.returnNothing_));\n};\n\n\n/**\n * Unlinks a provider from an account.\n * @param {!fireauth.idp.ProviderId} providerId The ID of the provider to\n *     unlink.\n * @return {!goog.Promise<!fireauth.AuthUser>}\n */\nfireauth.AuthUser.prototype.unlink = function(providerId) {\n  var self = this;\n  // Make sure we have updated user providers to avoid removing a linked\n  // provider that hasn't been updated in current copy of user.\n  // Register this pending promise. This will also check for user invalidation.\n  return this.registerPendingPromise_(\n      this.reloadWithoutSaving_()\n      .then(function(idToken) {\n        // Provider already unlinked.\n        if (!goog.array.contains(self.getProviderIds(), providerId)) {\n          return self.notifyStateChangeListeners_()\n              .then(function() {\n                throw new fireauth.AuthError(\n                    fireauth.authenum.Error.NO_SUCH_PROVIDER);\n              });\n        }\n        // We delete the providerId given.\n        return self.rpcHandler_\n            .deleteLinkedAccounts(idToken, [providerId])\n            .then(function(resp) {\n              // Construct the set of provider IDs returned by server.\n              var remainingProviderIds = {};\n              var userInfo = resp[fireauth.AuthUser.SetAccountInfoField.\n                  PROVIDER_USER_INFO] || [];\n              goog.array.forEach(userInfo, function(obj) {\n                remainingProviderIds[\n                    obj[fireauth.AuthUser.SetAccountInfoField.PROVIDER_ID]] =\n                    true;\n              });\n\n              // Remove all provider data objects where the provider ID no\n              // longer exists in this user.\n              goog.array.forEach(self.getProviderIds(), function(pId) {\n                if (!remainingProviderIds[pId]) {\n                  // This provider no longer linked, remove it from user.\n                  self.removeProviderData(pId);\n                }\n              });\n\n              // Remove the phone number if the phone provider was unlinked.\n              if (!remainingProviderIds[fireauth.PhoneAuthProvider[\n                      'PROVIDER_ID']]) {\n                fireauth.object.setReadonlyProperty(self, 'phoneNumber', null);\n              }\n\n              return self.notifyStateChangeListeners_();\n            });\n      }));\n};\n\n\n/**\n * Deletes the user, triggering an Auth token change if successful.\n * @return {!goog.Promise<void>}\n */\nfireauth.AuthUser.prototype.delete = function() {\n  // Notice the way of declaring the method, it's to avoid a weird bug on IE8.\n  var self = this;\n  // Register this pending promise. This will also check for user invalidation.\n  return this.registerPendingPromise_(\n      this.getIdToken()\n      .then(function(idToken) {\n        return self.rpcHandler_.deleteAccount(idToken);\n      })\n      .then(function() {\n        self.notifyUserDeletedListeners_();\n      }))\n      .then(function() {\n        // Destroying after the registered promise is handled ensures it won't\n        // be canceled.\n        self.destroy();\n      });\n};\n\n\n/**\n * Tells the Auth event manager if this user is the owner of a detected Auth\n * event. A user can handle linkWithPopup and linkWithRedirect operations.\n * In addition, the event ID should match the user's event IDs.\n * @param {!fireauth.AuthEvent.Type} mode The Auth operation mode (popup,\n *     redirect).\n * @param {?string=} opt_eventId The event ID.\n * @return {boolean} Whether the Auth event handler can handler the provided\n *     event.\n * @override\n */\nfireauth.AuthUser.prototype.canHandleAuthEvent = function(mode, opt_eventId) {\n  if (mode == fireauth.AuthEvent.Type.LINK_VIA_POPUP &&\n      this.getPopupEventId() == opt_eventId &&\n      this.pendingPopupResolvePromise_) {\n    // The link via popup event's ID matches the user's popup event ID which\n    // makes this user the owner of this event.\n    return true;\n  } else if (mode == fireauth.AuthEvent.Type.REAUTH_VIA_POPUP &&\n      this.getPopupEventId() == opt_eventId &&\n      this.pendingPopupResolvePromise_) {\n    // The reauth via popup event's ID matches the user's popup event ID which\n    // makes this user the owner of this event.\n    return true;\n  } else if (mode == fireauth.AuthEvent.Type.LINK_VIA_REDIRECT &&\n             this.getRedirectEventId() == opt_eventId) {\n    // The link via redirect event's ID matches the user's redirect event ID\n    // which makes this user the owner of this event.\n    return true;\n  } else if (mode == fireauth.AuthEvent.Type.REAUTH_VIA_REDIRECT &&\n             this.getRedirectEventId() == opt_eventId) {\n    // The reauth via redirect event's ID matches the user's redirect event ID\n    // which makes this user the owner of this event.\n    return true;\n  }\n  return false;\n};\n\n\n/**\n * Completes the pending popup operation. If error is not null, rejects with the\n * error. Otherwise, it resolves with the popup redirect result.\n * @param {!fireauth.AuthEvent.Type} mode The Auth operation mode (popup,\n *     redirect).\n * @param {?fireauth.AuthEventManager.Result} popupRedirectResult The result\n *     to resolve with when no error supplied.\n * @param {?fireauth.AuthError} error When supplied, the promise will reject.\n * @param {?string=} opt_eventId The event ID.\n * @override\n */\nfireauth.AuthUser.prototype.resolvePendingPopupEvent =\n    function(mode, popupRedirectResult, error, opt_eventId) {\n  // Only handles popup events with event IDs that match a pending popup ID.\n  if ((mode != fireauth.AuthEvent.Type.LINK_VIA_POPUP &&\n       mode != fireauth.AuthEvent.Type.REAUTH_VIA_POPUP) ||\n      opt_eventId != this.getPopupEventId()) {\n    return;\n  }\n  if (error && this.pendingPopupRejectPromise_) {\n    // Reject with error for supplied mode.\n    this.pendingPopupRejectPromise_(error);\n  } else if (popupRedirectResult &&\n             !error &&\n             this.pendingPopupResolvePromise_) {\n    // Resolve with result for supplied mode.\n    this.pendingPopupResolvePromise_(popupRedirectResult);\n  }\n  // Now that event is resolved, delete timeout promise.\n  if (this.popupTimeoutPromise_) {\n    this.popupTimeoutPromise_.cancel();\n    this.popupTimeoutPromise_ = null;\n  }\n  // Delete pending promises.\n  delete this.pendingPopupResolvePromise_;\n  delete this.pendingPopupRejectPromise_;\n};\n\n\n/**\n * Returns the handler's appropriate popup and redirect sign in operation\n * finisher. Can handle link or reauth events that match existing event IDs.\n * @param {!fireauth.AuthEvent.Type} mode The Auth operation mode (popup,\n *     redirect).\n * @param {?string=} opt_eventId The optional event ID.\n * @return {?function(string, string, ?string,\n *     ?string=):!goog.Promise<!fireauth.AuthEventManager.Result>}\n * @override\n */\nfireauth.AuthUser.prototype.getAuthEventHandlerFinisher =\n    function(mode, opt_eventId) {\n  if (mode == fireauth.AuthEvent.Type.LINK_VIA_POPUP &&\n      opt_eventId == this.getPopupEventId()) {\n    // Link with popup ID matches popup event ID.\n    return goog.bind(this.finishPopupAndRedirectLink, this);\n  } else if (mode == fireauth.AuthEvent.Type.REAUTH_VIA_POPUP &&\n      opt_eventId == this.getPopupEventId()) {\n    // Reauth with popup ID matches popup event ID.\n    return goog.bind(this.finishPopupAndRedirectReauth, this);\n  } else if (mode == fireauth.AuthEvent.Type.LINK_VIA_REDIRECT &&\n             this.getRedirectEventId() == opt_eventId) {\n    // Link with redirect ID matches redirect event ID.\n    return goog.bind(this.finishPopupAndRedirectLink, this);\n  } else if (mode == fireauth.AuthEvent.Type.REAUTH_VIA_REDIRECT &&\n             this.getRedirectEventId() == opt_eventId) {\n    // Reauth with redirect ID matches redirect event ID.\n    return goog.bind(this.finishPopupAndRedirectReauth, this);\n  }\n  return null;\n};\n\n\n/**\n * @return {string} The generated event ID used to identify a popup or redirect\n *     event.\n * @private\n */\nfireauth.AuthUser.prototype.generateEventId_ = function() {\n  return fireauth.util.generateEventId(this['uid'] + ':::');\n};\n\n\n/**\n * Links to Auth provider via popup.\n * @param {!fireauth.AuthProvider} provider The Auth provider to sign in with.\n * @return {!goog.Promise<!fireauth.AuthEventManager.Result>}\n */\nfireauth.AuthUser.prototype.linkWithPopup = function(provider) {\n  var self = this;\n  // Additional check to return to fail when the provider is already linked.\n  var additionalCheck = function() {\n    return self.checkIfAlreadyLinked_(provider['providerId'])\n        .then(function() {\n          // Notify state listeners after the check as it will update the user\n          // state.\n          return self.notifyStateChangeListeners_();\n        });\n  };\n  return this.runOperationWithPopup_(\n      fireauth.AuthEvent.Type.LINK_VIA_POPUP, provider, additionalCheck, false);\n};\n\n\n/**\n * Reauthenticate to Auth provider via popup.\n * @param {!fireauth.AuthProvider} provider The Auth provider to sign in with.\n * @return {!goog.Promise<!fireauth.AuthEventManager.Result>}\n */\nfireauth.AuthUser.prototype.reauthenticateWithPopup = function(provider) {\n  // No additional check needed before running this operation.\n  var additionalCheck = function() {\n    return goog.Promise.resolve();\n  };\n  return this.runOperationWithPopup_(\n      fireauth.AuthEvent.Type.REAUTH_VIA_POPUP,\n      provider,\n      additionalCheck,\n      // Do not update token and skip session invalidation check.\n      true);\n};\n\n\n/**\n * Runs a specific OAuth operation using the Auth provider via popup.\n * @param {!fireauth.AuthEvent.Type} mode The mode of operation (link or\n *     reauth).\n * @param {!fireauth.AuthProvider} provider The Auth provider to sign in with.\n * @param {function():!goog.Promise} additionalCheck The additional check to\n *     run before proceeding with the popup processing.\n * @param {boolean} isReauthOperation whether this is a reauth operation or not.\n * @return {!goog.Promise<!fireauth.AuthEventManager.Result>}\n * @private\n */\nfireauth.AuthUser.prototype.runOperationWithPopup_ =\n    function(mode, provider, additionalCheck, isReauthOperation) {\n  // Check if popup and redirect are supported in this environment.\n  if (!fireauth.util.isPopupRedirectSupported()) {\n    return goog.Promise.reject(new fireauth.AuthError(\n        fireauth.authenum.Error.OPERATION_NOT_SUPPORTED));\n  }\n  // Quickly throw user invalidation error if already invalidated.\n  if (this.userInvalidatedError_ &&\n      // Skip invalidation check as reauthentication could revalidate a user.\n      !isReauthOperation) {\n    return goog.Promise.reject(this.userInvalidatedError_);\n  }\n  var self = this;\n  // Popup the window immediately to make sure the browser associates the\n  // popup with the click that triggered it.\n\n  // Get provider settings.\n  var settings = fireauth.idp.getIdpSettings(provider['providerId']);\n  // There could multiple users at the same time and multiple users could have\n  // the same UID. So try to ensure event ID uniqueness.\n  var eventId = this.generateEventId_();\n  // If incapable of redirecting popup from opener, popup destination URL\n  // directly. This could also happen in a sandboxed iframe.\n  var oauthHelperWidgetUrl = null;\n  if ((!fireauth.util.runsInBackground() || fireauth.util.isIframe()) &&\n      this.authDomain_ &&\n      provider['isOAuthProvider']) {\n    oauthHelperWidgetUrl =\n        fireauth.iframeclient.IfcHandler.getOAuthHelperWidgetUrl(\n            this.authDomain_,\n            this.apiKey_,\n            this.appName_,\n            mode,\n            provider,\n            null,\n            eventId,\n            firebase.SDK_VERSION || null,\n            null,\n            null,\n            this['tenantId'],\n            this.emulatorConfig_);\n  }\n  // The popup must have a name, otherwise when successive popups are triggered\n  // they will all render in the same instance and none will succeed since the\n  // popup cancel of first window will close the shared popup window instance.\n  var popupWin =\n      fireauth.util.popup(\n          oauthHelperWidgetUrl,\n          fireauth.util.generateRandomString(),\n          settings && settings.popupWidth,\n          settings && settings.popupHeight);\n  var p = additionalCheck().then(function() {\n    // Auth event manager must be available for account linking or\n    // reauthentication to be possible.\n    self.getAuthEventManager();\n    if (!isReauthOperation) {\n      // Some operations like reauthenticate do not require this.\n      return self.getIdToken().then(function(idToken) {});\n    }\n  }).then(function() {\n    // Process popup request.\n    return self.authEventManager_.processPopup(\n        popupWin, mode, provider, eventId, !!oauthHelperWidgetUrl,\n        self['tenantId']);\n  }).then(function() {\n    return new goog.Promise(function(resolve, reject) {\n      // Expire other pending promises if still available.\n      self.resolvePendingPopupEvent(\n          mode,\n          null,\n          new fireauth.AuthError(fireauth.authenum.Error.EXPIRED_POPUP_REQUEST),\n          // Existing popup event ID.\n          self.getPopupEventId());\n      // Save current pending promises.\n      self.pendingPopupResolvePromise_ = resolve;\n      self.pendingPopupRejectPromise_ = reject;\n      // Overwrite popup event ID with new one.\n      self.setPopupEventId(eventId);\n      // Keep track of timeout promise to cancel it on promise resolution before\n      // it times out.\n      self.popupTimeoutPromise_ =\n          self.authEventManager_.startPopupTimeout(\n              self, mode, /** @type {!Window} */ (popupWin), eventId);\n    });\n  }).then(function(result) {\n    // On resolution, close popup if still opened and pass result through.\n    if (popupWin) {\n      fireauth.util.closeWindow(popupWin);\n    }\n    if (result) {\n      return fireauth.object.makeReadonlyCopy(result);\n    }\n    return null;\n  }).thenCatch(function(error) {\n    if (popupWin) {\n      fireauth.util.closeWindow(popupWin);\n    }\n    throw error;\n  });\n  // Register this pending promise. This will also check for user invalidation.\n  return /** @type {!goog.Promise<!fireauth.AuthEventManager.Result>} */ (\n      this.registerPendingPromise_(\n          p,\n          // Skip invalidation check as reauthentication could revalidate a\n          // user.\n          isReauthOperation));\n};\n\n\n/**\n * Links to Auth provider via redirect.\n * @param {!fireauth.AuthProvider} provider The Auth provider to sign in with.\n * @return {!goog.Promise<void>}\n */\nfireauth.AuthUser.prototype.linkWithRedirect = function(provider) {\n  var mode = fireauth.AuthEvent.Type.LINK_VIA_REDIRECT;\n  var self = this;\n  // Additional check to return to fail when the provider is already linked.\n  var additionalCheck = function() {\n    return self.checkIfAlreadyLinked_(provider['providerId']);\n  };\n  return this.runOperationWithRedirect_(mode, provider, additionalCheck, false);\n};\n\n\n/**\n * Reauthenticates to Auth provider via redirect.\n * @param {!fireauth.AuthProvider} provider The Auth provider to sign in with.\n * @return {!goog.Promise<void>}\n */\nfireauth.AuthUser.prototype.reauthenticateWithRedirect = function(provider) {\n  // No additional check needed.\n  var additionalCheck = function() {\n    return goog.Promise.resolve();\n  };\n  return this.runOperationWithRedirect_(\n      fireauth.AuthEvent.Type.REAUTH_VIA_REDIRECT,\n      provider,\n      additionalCheck,\n      // Do not update token and skip session invalidation check.\n      true);\n};\n\n\n\n/**\n * Runs a specific OAuth operation using the Auth provider via redirect.\n * @param {!fireauth.AuthEvent.Type} mode The mode of operation (link or\n *     reauth).\n * @param {!fireauth.AuthProvider} provider The Auth provider to sign in with.\n * @param {function():!goog.Promise} additionalCheck The additional check to\n *     run before proceeding with the redirect processing.\n * @param {boolean} isReauthOperation whether this is a reauth operation or not.\n * @return {!goog.Promise<void>}\n * @private\n */\nfireauth.AuthUser.prototype.runOperationWithRedirect_ =\n    function(mode, provider, additionalCheck, isReauthOperation) {\n  // Check if popup and redirect are supported in this environment.\n  if (!fireauth.util.isPopupRedirectSupported()) {\n    return goog.Promise.reject(new fireauth.AuthError(\n        fireauth.authenum.Error.OPERATION_NOT_SUPPORTED));\n  }\n  // Quickly throw user invalidation error if already invalidated.\n  if (this.userInvalidatedError_ &&\n      // Skip invalidation check as reauthentication could revalidate a user.\n      !isReauthOperation) {\n    return goog.Promise.reject(this.userInvalidatedError_);\n  }\n  var self = this;\n  var errorThrown = null;\n  // There could multiple users at the same time and multiple users could have\n  // the same UID. So try to ensure event ID uniqueness.\n  var eventId = this.generateEventId_();\n  var p = additionalCheck().then(function() {\n    // Auth event manager must be available for account linking or\n    // reauthentication to be possible.\n    self.getAuthEventManager();\n    if (!isReauthOperation) {\n      // Some operations like reauthenticate do not require this.\n      return self.getIdToken().then(function(idToken) {});\n    }\n  }).then(function() {\n    // Process redirect operation.\n    self.setRedirectEventId(eventId);\n    // Before redirecting save the event ID.\n    // It is important that the user redirect event ID is updated in storage\n    // before redirecting.\n    return self.notifyStateChangeListeners_();\n  }).then(function(user) {\n    if (self.redirectStorageManager_) {\n      // Save the user before redirecting in case it is not current so that it\n      // can be retrieved after reloading for linking or reauthentication to\n      // succeed.\n      return self.redirectStorageManager_.setRedirectUser(self);\n    }\n    return user;\n  }).then(function(user) {\n    // Complete the redirect operation.\n    return self.authEventManager_.processRedirect(\n        mode, provider, eventId, self['tenantId']);\n  }).thenCatch(function(error) {\n    // Catch error if any is generated.\n    errorThrown = error;\n    if (self.redirectStorageManager_) {\n      // If an error is detected, delete the redirected user from storage.\n      return self.redirectStorageManager_.removeRedirectUser();\n    }\n    // No storage manager, just throw error.\n    throw errorThrown;\n  }).then(function() {\n    // Rethrow the error.\n    if (errorThrown) {\n      throw errorThrown;\n    }\n  });\n  // Register this pending promise. This will also check for user invalidation.\n  return /** @type {!goog.Promise<void>} */ (this.registerPendingPromise_(\n      p,\n      // Skip invalidation check as reauthentication could revalidate a user.\n      isReauthOperation));\n};\n\n\n/**\n * @return {!fireauth.AuthEventManager} The user's Auth event manager.\n */\nfireauth.AuthUser.prototype.getAuthEventManager = function() {\n  // Either return the manager instance if available, otherwise throw an error.\n  if (this.authEventManager_ && this.popupRedirectEnabled_) {\n    return this.authEventManager_;\n  } else if (this.authEventManager_ && !this.popupRedirectEnabled_) {\n    // This should not happen as Auth will enable a user after it is created.\n    throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR);\n  }\n  throw new fireauth.AuthError(fireauth.authenum.Error.MISSING_AUTH_DOMAIN);\n};\n\n\n/**\n * Finishes the popup and redirect account linking operations.\n * @param {string} requestUri The callback URL with the OAuth response.\n * @param {string} sessionId The session ID used to generate the authUri.\n * @param {?string} tenantId The tenant ID.\n * @param {?string=} opt_postBody The optional POST body content.\n * @return {!goog.Promise<!fireauth.AuthEventManager.Result>}\n */\nfireauth.AuthUser.prototype.finishPopupAndRedirectLink =\n    function(requestUri, sessionId, tenantId, opt_postBody) {\n  var self = this;\n  // Now that popup has responded, delete popup timeout promise.\n  if (this.popupTimeoutPromise_) {\n    this.popupTimeoutPromise_.cancel();\n    this.popupTimeoutPromise_ = null;\n  }\n  var userCredential = null;\n  // This routine could be run before init state, make sure it waits for that to\n  // complete otherwise this would fail as user not loaded from storage yet.\n  var p = this.getIdToken()\n      .then(function(idToken) {\n        var request = {\n          'requestUri': requestUri,\n          'postBody': opt_postBody,\n          'sessionId': sessionId,\n          // To link a tenant user, the tenant ID will be passed to the\n          // backend as part of the ID token.\n          'idToken': idToken\n        };\n        // This operation should fail if new ID token differs from old one.\n        // So this can be treate as a profile update operation.\n        return self.rpcHandler_.verifyAssertionForLinking(request);\n      })\n      .then(function(response) {\n        // Get user credential.\n        userCredential = self.getUserCredential_(\n            response, fireauth.constants.OperationType.LINK);\n        // Finalizes the linking process.\n        return self.finalizeLinking_(response);\n      })\n      .then(function(user) {\n        // Return the user credential response.\n        return userCredential;\n      });\n  return /** @type {!goog.Promise<!fireauth.AuthEventManager.Result>} */ (\n      this.registerPendingPromise_(p));\n};\n\n\n/**\n * Finishes the popup and redirect account reauthentication operations.\n * @param {string} requestUri The callback URL with the OAuth response.\n * @param {string} sessionId The session ID used to generate the authUri.\n * @param {?string} tenantId The tenant ID.\n * @param {?string=} opt_postBody The optional POST body content.\n * @return {!goog.Promise<!fireauth.AuthEventManager.Result>}\n */\nfireauth.AuthUser.prototype.finishPopupAndRedirectReauth =\n    function(requestUri, sessionId, tenantId, opt_postBody) {\n  var self = this;\n  // Now that popup has responded, delete popup timeout promise.\n  if (this.popupTimeoutPromise_) {\n    this.popupTimeoutPromise_.cancel();\n    this.popupTimeoutPromise_ = null;\n  }\n  var userCredential = null;\n  // This routine could be run before init state, make sure it waits for that to\n  // complete otherwise this would fail as user not loaded from storage yet.\n  var p = goog.Promise.resolve()\n      .then(function() {\n        var request = {\n          'requestUri': requestUri,\n          'sessionId': sessionId,\n          'postBody': opt_postBody,\n          // To reauthenticate a tenant user, the tenant ID will be passed to\n          // the backend explicitly.\n          // Even if tenant ID is null, still pass it to RPC handler explicitly\n          // so that it won't be overridden by RPC handler's tenant ID.\n          'tenantId': tenantId\n        };\n        // Finish sign in by calling verifyAssertionForExisting and then\n        // matching the returned ID token's UID with the current user's.\n        return fireauth.AuthCredential.verifyTokenResponseUid(\n            self.rpcHandler_.verifyAssertionForExisting(request),\n            self['uid']);\n      }).then(function(response) {\n        // Get credential from response if available.\n        // Get user credential.\n        userCredential = self.getUserCredential_(\n            response, fireauth.constants.OperationType.REAUTHENTICATE);\n        // If the credential is valid and matches the current user ID, then\n        // update the tokens accordingly.\n        self.updateTokensIfPresent(response);\n        // This could potentially validate an invalidated user. This happens in\n        // the case a password reset was applied. The refresh token is expired.\n        // Reauthentication should revalidate the user.\n        // User would remain non current if already signed out, but should be\n        // enabled again.\n        self.userInvalidatedError_ = null;\n        return self.reload();\n      })\n      .then(function() {\n        // Return the user credential response.\n        return userCredential;\n      });\n  return /** @type {!goog.Promise<!fireauth.AuthEventManager.Result>} */ (\n      this.registerPendingPromise_(\n          p,\n          // Skip invalidation check as reauthentication could revalidate a\n          // user.\n          true));\n};\n\n\n/**\n * Sends the verification email to the email in the user's account.\n * @param {?Object=} opt_actionCodeSettings The optional action code settings\n *     object.\n * @return {!goog.Promise<void>}\n */\nfireauth.AuthUser.prototype.sendEmailVerification =\n    function(opt_actionCodeSettings) {\n  var self = this;\n  var idToken = null;\n  // Register this pending promise. This will also check for user invalidation.\n  return this.registerPendingPromise_(\n      // Wrap in promise as ActionCodeSettings constructor could throw a\n      // synchronous error if invalid arguments are specified.\n      this.getIdToken().then(function(latestIdToken) {\n        idToken = latestIdToken;\n        if (typeof opt_actionCodeSettings !== 'undefined' &&\n            // Ignore empty objects.\n            !goog.object.isEmpty(opt_actionCodeSettings)) {\n          return new fireauth.ActionCodeSettings(\n              /** @type {!Object} */ (opt_actionCodeSettings)).buildRequest();\n        }\n        return {};\n      })\n      .then(function(additionalRequestData) {\n        return self.rpcHandler_.sendEmailVerification(\n            /** @type {string} */ (idToken), additionalRequestData);\n      })\n      .then(function(email) {\n        if (self['email'] != email) {\n          // Our local copy does not have an email. If the email changed,\n          // reload the user.\n          return self.reload();\n        }\n      })\n      .then(function() {\n        // Return nothing.\n      }));\n};\n\n\n/**\n * Sends the verification email before updating the email on the user.\n * @param {string} newEmail The new email.\n * @param {?Object=} opt_actionCodeSettings The optional action code settings\n *     object.\n * @return {!goog.Promise<void>}\n */\nfireauth.AuthUser.prototype.verifyBeforeUpdateEmail =\n    function(newEmail, opt_actionCodeSettings) {\n  var self = this;\n  var idToken = null;\n  // Register this pending promise. This will also check for user invalidation.\n  return this.registerPendingPromise_(\n      // Wrap in promise as ActionCodeSettings constructor could throw a\n      // synchronous error if invalid arguments are specified.\n      this.getIdToken().then(function(latestIdToken) {\n        idToken = latestIdToken;\n        if (typeof opt_actionCodeSettings !== 'undefined' &&\n            // Ignore empty objects.\n            !goog.object.isEmpty(opt_actionCodeSettings)) {\n          return new fireauth.ActionCodeSettings(\n              /** @type {!Object} */ (opt_actionCodeSettings)).buildRequest();\n        }\n        return {};\n      })\n      .then(function(additionalRequestData) {\n        return self.rpcHandler_.verifyBeforeUpdateEmail(\n            /** @type {string} */ (idToken), newEmail, additionalRequestData);\n      })\n      .then(function(email) {\n        if (self['email'] != email) {\n          // If the local copy of the email on user is outdated, reload the\n          // user.\n          return self.reload();\n        }\n      })\n      .then(function() {\n        // Return nothing.\n      }));\n};\n\n\n/**\n * Destroys the user object and makes further operations invalid. Sensitive\n * fields (refreshToken) are also cleared.\n */\nfireauth.AuthUser.prototype.destroy = function() {\n  // Cancel all pending promises.\n  for (var i = 0; i < this.pendingPromises_.length; i++) {\n    this.pendingPromises_[i].cancel(fireauth.authenum.Error.MODULE_DESTROYED);\n  }\n  // Stop listening to language code changes.\n  this.setLanguageCodeChangeDispatcher(null);\n  // Stop listening to emulator config changes.\n  this.setEmulatorConfigChangeDispatcher(null);\n  // Stop listening to framework changes.\n  this.setFrameworkChangeDispatcher(null);\n  // Empty pending promises array.\n  this.pendingPromises_ = [];\n  this.destroyed_ = true;\n  // Stop proactive refresh if running.\n  this.stopProactiveRefresh();\n  fireauth.object.setReadonlyProperty(this, 'refreshToken', null);\n  // Make sure the destroyed user is unsubscribed from Auth event handling.\n  if (this.authEventManager_) {\n    this.authEventManager_.unsubscribe(this);\n  }\n};\n\n\n/**\n * Takes in a pending promise, saves it and adds a clean up callback which\n * forgets the pending promise after it is fulfilled and echoes the promise\n * back. If in the process, a user invalidation error is detected, caches the\n * error so next time a call is made on the user, the operation will fail with\n * the cached error.\n * @param {!goog.Promise<*, *>|!goog.Promise<void>} p The pending promise.\n * @param {boolean=} opt_skipInvalidationCheck Whether to skip invalidation\n *     check.\n * @return {!goog.Promise<*, *>|!goog.Promise<void>}\n * @private\n */\nfireauth.AuthUser.prototype.registerPendingPromise_ =\n    function(p, opt_skipInvalidationCheck) {\n  var self = this;\n  // Check if user invalidation occurs.\n  var processedP = this.checkIfInvalidated_(p, opt_skipInvalidationCheck);\n  // Save created promise in pending list.\n  this.pendingPromises_.push(processedP);\n  processedP.thenAlways(function() {\n    // When fulfilled, remove from pending list.\n    goog.array.remove(self.pendingPromises_, processedP);\n  });\n  // Return the promise.\n  return processedP\n      .thenCatch(function(error) {\n        var multiFactorError = null;\n        if (error && error['code'] === 'auth/multi-factor-auth-required') {\n          multiFactorError = fireauth.MultiFactorError.fromPlainObject(\n              error.toPlainObject(),\n              self.getAuth_(),\n              goog.bind(self.handleMultiFactorIdTokenResolver_, self));\n        }\n        throw multiFactorError || error;\n      });\n};\n\n\n/**\n * Completes multi-factor sign-in. This is only relevant for re-authentication\n * flows.\n * @param {{idToken: string, refreshToken: string}} response The successful\n *     sign-in response containing the new ID tokens.\n * @return {!goog.Promise<!fireauth.AuthEventManager.Result>} A Promise that\n *     resolves with the updated `UserCredential`.\n * @private\n */\nfireauth.AuthUser.prototype.handleMultiFactorIdTokenResolver_ =\n    function(response) {\n  var userCredential = null;\n  var self = this;\n  // Validate token response matches current user ID.\n  var p = fireauth.AuthCredential.verifyTokenResponseUid(\n      goog.Promise.resolve(response),\n      self['uid'])\n      .then(function(response) {\n        // Get credential from response if available.\n        userCredential = self.getUserCredential_(\n            response, fireauth.constants.OperationType.REAUTHENTICATE);\n        // If the credential is valid and matches the current user ID, then\n        // update the tokens accordingly.\n        self.updateTokensIfPresent(response);\n        // This could potentially validate an invalidated user.\n        self.userInvalidatedError_ = null;\n        // Reload the user with the latest profile.\n        return self.reload();\n      })\n      .then(function() {\n        // Return the user credential response.\n        return userCredential;\n      });\n  return /** @type {!goog.Promise<!fireauth.AuthEventManager.Result>} */ (\n      this.registerPendingPromise_(\n          p,\n          // Skip invalidation check as reauthentication could revalidate a\n          // user.\n          true));\n};\n\n\n/**\n * Check if user invalidation occurs. If so, it caches the error so it can be\n * thrown immediately the next time an operation is run on the user.\n * @param {!goog.Promise<*, *>|!goog.Promise<void>} p The pending promise.\n * @param {boolean=} opt_skipInvalidationCheck Whether to skip invalidation\n *     check.\n * @return {!goog.Promise<*, *>|!goog.Promise<void>}\n * @private\n */\nfireauth.AuthUser.prototype.checkIfInvalidated_ =\n    function(p, opt_skipInvalidationCheck) {\n  var self = this;\n  // Already invalidated, reject with token expired error.\n  // Unless invalidation check is to be skipped.\n  if (this.userInvalidatedError_ && !opt_skipInvalidationCheck) {\n    // Cancel pending promise.\n    p.cancel();\n    // Reject with cached error.\n    return goog.Promise.reject(this.userInvalidatedError_);\n  }\n  return p.thenCatch(function(error) {\n    // Session invalidated.\n    if (fireauth.AuthUser.isUserInvalidated_(error)) {\n      // Notify listeners of invalidated session.\n      if (!self.userInvalidatedError_) {\n        self.notifyUserInvalidatedListeners_();\n      }\n      // Cache the invalidation error.\n      self.userInvalidatedError_ = /** @type {!fireauth.AuthError} */ (error);\n    }\n    // Rethrow the error.\n    throw error;\n  });\n};\n\n\n/**\n * @return {!Object} The object representation of the user instance.\n * @override\n */\nfireauth.AuthUser.prototype.toJSON = function() {\n  // Return the plain object representation in case JSON.stringify is called on\n  // a user instance.\n  return this.toPlainObject();\n};\n\n\n/**\n * @return {!Object} The object representation of the user instance.\n */\nfireauth.AuthUser.prototype.toPlainObject = function() {\n  var obj = {\n    'uid': this['uid'],\n    'displayName': this['displayName'],\n    'photoURL': this['photoURL'],\n    'email': this['email'],\n    'emailVerified': this['emailVerified'],\n    'phoneNumber': this['phoneNumber'],\n    'isAnonymous': this['isAnonymous'],\n    'tenantId': this['tenantId'],\n    'providerData': [],\n    'apiKey': this.apiKey_,\n    'appName': this.appName_,\n    'authDomain': this.authDomain_,\n    'stsTokenManager': this.stsTokenManager_.toPlainObject(),\n    // Redirect event ID must be maintained in case there is a pending redirect\n    // event.\n    'redirectEventId': this.getRedirectEventId()\n  };\n  // Extend user plain object with metadata object.\n  if (this['metadata']) {\n    goog.object.extend(obj, this['metadata'].toPlainObject());\n  }\n  goog.array.forEach(this['providerData'], function(userInfo) {\n    obj['providerData'].push(fireauth.object.makeWritableCopy(userInfo));\n  });\n  // Extend plain object with multi-factor user data.\n  goog.object.extend(obj, this.multiFactorUser_.toPlainObject());\n  return obj;\n};\n\n\n/**\n * Converts a plain user object to {@code fireauth.AuthUser}.\n * @param {!Object} user The object representation of the user instance.\n * @return {?fireauth.AuthUser} The Firebase user object corresponding to\n *     object.\n */\nfireauth.AuthUser.fromPlainObject = function(user) {\n  if (!user['apiKey']) {\n    return null;\n  }\n  var options = {\n    'apiKey': user['apiKey'],\n    'authDomain': user['authDomain'],\n    'appName': user['appName'],\n    'emulatorConfig': user['emulatorConfig']\n  };\n  // Convert to server response format. Constructor does not take\n  // stsTokenManager toPlainObject as that format is different than the return\n  // server response which is always used to initialize a user instance.\n  var stsTokenManagerResponse = {};\n  if (user['stsTokenManager'] &&\n      user['stsTokenManager']['accessToken']) {\n    stsTokenManagerResponse[fireauth.RpcHandler.AuthServerField.ID_TOKEN] =\n        user['stsTokenManager']['accessToken'];\n    // Refresh token could be expired.\n    stsTokenManagerResponse[fireauth.RpcHandler.AuthServerField.REFRESH_TOKEN] =\n        user['stsTokenManager']['refreshToken'] || null;\n    const expirationTime = user['stsTokenManager']['expirationTime'];\n    if (expirationTime) {\n      stsTokenManagerResponse[fireauth.RpcHandler.AuthServerField\n                                  .EXPIRES_IN] =\n          (expirationTime - Date.now()) / 1000;\n    }\n  } else {\n    // Token response is a required field.\n    return null;\n  }\n  var firebaseUser = new fireauth.AuthUser(options,\n      stsTokenManagerResponse,\n      /** @type {!fireauth.AuthUser.AccountInfo} */ (user));\n  if (user['providerData']) {\n    goog.array.forEach(user['providerData'], function(userInfo) {\n      if (userInfo) {\n        firebaseUser.addProviderData(/** @type {!fireauth.AuthUserInfo} */ (\n            fireauth.object.makeReadonlyCopy(userInfo)));\n      }\n    });\n  }\n  // Redirect event ID must be restored to complete any pending link with\n  // redirect operation owned by this user.\n  if (user['redirectEventId']) {\n    firebaseUser.setRedirectEventId(user['redirectEventId']);\n  }\n  return firebaseUser;\n};\n\n\n\n/**\n * Factory method for initializing a Firebase user object and populating its\n * user info. This is the recommended way for initializing a user externally.\n * On sign in/up operation, the server returns a token response. The response is\n * all that is needed to initialize this user.\n * @param {!Object} appOptions The application options.\n * @param {!Object} stsTokenResponse The server STS token response.\n * @param {?fireauth.storage.RedirectUserManager=}\n *     opt_redirectStorageManager The utility used to store and delete a user on\n *     link with redirect.\n * @param {?Array<string>=} opt_frameworks The list of frameworks to log on the\n *     user on initialization.\n * @return {!goog.Promise<!fireauth.AuthUser>}\n */\nfireauth.AuthUser.initializeFromIdTokenResponse = function(appOptions,\n    stsTokenResponse, opt_redirectStorageManager, opt_frameworks) {\n  // Initialize the Firebase Auth user.\n  var user = new fireauth.AuthUser(\n      appOptions, stsTokenResponse);\n  // If redirect storage manager provided, set it.\n  if (opt_redirectStorageManager) {\n    user.setRedirectStorageManager(opt_redirectStorageManager);\n  }\n  // If frameworks provided, set it.\n  if (opt_frameworks) {\n    user.setFramework(opt_frameworks);\n  }\n  // Updates the user info and data and resolves with a user instance.\n  return user.reload().then(function() {\n    return user;\n  });\n};\n\n\n/**\n * Returns an AuthUser copy of the provided user using the provided parameters\n * without making any network request.\n * @param {!fireauth.AuthUser} user The user to be copied.\n * @param {?Object=} opt_appOptions The application options.\n * @param {?fireauth.storage.RedirectUserManager=}\n *     opt_redirectStorageManager The utility used to store and delete a user on\n *     link with redirect.\n * @param {?Array<string>=} opt_frameworks The list of frameworks to log on the\n *     user on initialization.\n * @return {!fireauth.AuthUser}\n */\nfireauth.AuthUser.copyUser = function(user, opt_appOptions,\n    opt_redirectStorageManager, opt_frameworks) {\n  var appOptions = opt_appOptions || {\n    'apiKey': user.apiKey_,\n    'authDomain': user.authDomain_,\n    'appName': user.appName_\n  };\n  var newUser = new fireauth.AuthUser(\n      appOptions, user.getStsTokenManager().toServerResponse());\n  // If redirect storage manager provided, set it.\n  if (opt_redirectStorageManager) {\n    newUser.setRedirectStorageManager(opt_redirectStorageManager);\n  }\n  // If frameworks provided, set it.\n  if (opt_frameworks) {\n    newUser.setFramework(opt_frameworks);\n  }\n  // Copy remaining properties.\n  newUser.copy(user);\n  return newUser;\n};\n","/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Utility class to retrieve and cache STS token.\n */\ngoog.provide('fireauth.StsTokenManager');\ngoog.provide('fireauth.StsTokenManager.Response');\ngoog.provide('fireauth.StsTokenManager.ResponseData');\n\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.IdToken');\ngoog.require('fireauth.RpcHandler');\ngoog.require('fireauth.authenum.Error');\ngoog.require('goog.Promise');\ngoog.require('goog.asserts');\n\n\n\n/**\n * Creates STS token manager.\n *\n * @param {!fireauth.RpcHandler} rpcHandler Handler for RPC requests.\n * @constructor\n */\nfireauth.StsTokenManager = function(rpcHandler) {\n  /**\n   * @const @private {!fireauth.RpcHandler} The RPC handler used to request STS\n   *     tokens.\n   */\n  this.rpcHandler_ = rpcHandler;\n  /** @private {?string} The STS refresh token. */\n  this.refreshToken_ = null;\n  /** @private {?fireauth.IdToken} The STS ID token. */\n  this.accessToken_ = null;\n  /** @private {number} The expiration time of the token in epoch millis. */\n  this.expiresAt_ = Date.now();\n};\n\n\n/**\n * @return {!Object} The plain object representation of the STS token manager.\n */\nfireauth.StsTokenManager.prototype.toPlainObject = function() {\n  return {\n    'apiKey': this.rpcHandler_.getApiKey(),\n    'refreshToken': this.refreshToken_,\n    'accessToken': this.accessToken_ && this.accessToken_.toString(),\n    // To support downgrade flows, return expiration time.\n    'expirationTime': this.getExpirationTime()\n  };\n};\n\n\n/**\n * @param {!fireauth.RpcHandler} rpcHandler The RPC handler for the token\n *     manager.\n * @param {?Object} obj The plain object whose STS token manager instance is to\n *     be returned.\n * @return {?fireauth.StsTokenManager} The STS token manager instance from the\n *     plain object provided using the RPC handler provided.\n */\nfireauth.StsTokenManager.fromPlainObject = function(rpcHandler, obj) {\n  let stsTokenManager = null;\n  if (obj && obj['apiKey']) {\n    // These should be always equals and must be enforced in internal use.\n    goog.asserts.assert(obj['apiKey'] == rpcHandler.getApiKey());\n    stsTokenManager = new fireauth.StsTokenManager(rpcHandler);\n    stsTokenManager.setRefreshToken(obj['refreshToken']);\n    stsTokenManager.setAccessToken(obj['accessToken']);\n    stsTokenManager.setExpiresAt(obj['expirationTime']);\n  }\n  return stsTokenManager;\n};\n\n\n/**\n * @typedef {{\n *   accessToken: (?string),\n *   refreshToken: (?string)\n * }}\n */\nfireauth.StsTokenManager.Response;\n\n\n/**\n * @typedef {{\n *   access_token: (?string|undefined),\n *   refresh_token: (?string|undefined)\n * }}\n */\nfireauth.StsTokenManager.ResponseData;\n\n\n/**\n * @param {?string} refreshToken The STS refresh token.\n */\nfireauth.StsTokenManager.prototype.setRefreshToken = function(refreshToken) {\n  this.refreshToken_ = refreshToken;\n};\n\n\n/**\n * @param {?string} accessToken The STS access token.\n */\nfireauth.StsTokenManager.prototype.setAccessToken = function(accessToken) {\n  this.accessToken_ = fireauth.IdToken.parse(accessToken || '');\n};\n\n/**\n * To account for client side clock skew, we try to set the expiration time\n * using the local clock by adding the server TTL. If not provided, expiresAt\n * will be set from the accessToken by taking the difference between the exp\n * and iat fields.\n *\n * @param {number=} expiresIn The expiration TTL in seconds.\n */\nfireauth.StsTokenManager.prototype.setExpiresIn = function(expiresIn) {\n  expiresIn = typeof expiresIn !== 'undefined' ? expiresIn :\n      this.accessToken_ ? this.accessToken_.getExpiresIn() :\n                          0;\n  this.expiresAt_ = Date.now() + expiresIn * 1000;\n}\n\n/**\n * Allow setting expiresAt directly when we know the time is already in the\n * local clock.\n *\n * @param {number} expiresAt The expiration time in epoch millis.\n */\nfireauth.StsTokenManager.prototype.setExpiresAt = function(expiresAt) {\n  this.expiresAt_ = expiresAt;\n}\n\n/**\n * @return {?string} The refresh token.\n */\nfireauth.StsTokenManager.prototype.getRefreshToken = function() {\n  return this.refreshToken_;\n};\n\n\n/**\n * @return {number} The STS access token expiration time in milliseconds.\n */\nfireauth.StsTokenManager.prototype.getExpirationTime = function() {\n  return this.expiresAt_;\n};\n\n\n/**\n * The number of milliseconds before the official expiration time of a token\n * to refresh that token, to provide a buffer for RPCs to complete.\n * @const {number}\n */\nfireauth.StsTokenManager.TOKEN_REFRESH_BUFFER = 30 * 1000;\n\n\n/**\n * @return {boolean} Whether the STS access token is expired or not.\n * @private\n */\nfireauth.StsTokenManager.prototype.isExpired_ = function() {\n  return Date.now() >\n      this.getExpirationTime() - fireauth.StsTokenManager.TOKEN_REFRESH_BUFFER;\n};\n\n\n/**\n * Parses a response from the server that contains STS tokens (e.g. from\n * VerifyAssertion or VerifyPassword) and save the access token, refresh token,\n * and expiration time.\n * @param {!Object} response The backend response.\n * @return {string} The STS access token.\n */\nfireauth.StsTokenManager.prototype.parseServerResponse = function(response) {\n  const idToken = response[fireauth.RpcHandler.AuthServerField.ID_TOKEN];\n  this.setAccessToken(idToken);\n  this.setRefreshToken(\n      response[fireauth.RpcHandler.AuthServerField.REFRESH_TOKEN]);\n  // Not all IDP server responses come with expiresIn, some like MFA omit it.\n  const expiresIn = response[fireauth.RpcHandler.AuthServerField.EXPIRES_IN];\n  this.setExpiresIn(\n      typeof expiresIn !== 'undefined' ? Number(expiresIn) : undefined);\n  return idToken;\n};\n\n\n/**\n * Converts STS token manager instance to server response object.\n * @return {!Object}\n */\nfireauth.StsTokenManager.prototype.toServerResponse = function() {\n  const stsTokenManagerResponse = {};\n  stsTokenManagerResponse[fireauth.RpcHandler.AuthServerField.ID_TOKEN] =\n      this.accessToken_ && this.accessToken_.toString();\n  // Refresh token could be expired.\n  stsTokenManagerResponse[fireauth.RpcHandler.AuthServerField.REFRESH_TOKEN] =\n      this.getRefreshToken();\n  return stsTokenManagerResponse;\n};\n\n\n/**\n * Copies IdToken, refreshToken and expirationTime from tokenManagerToCopy.\n * @param {!fireauth.StsTokenManager} tokenManagerToCopy\n */\nfireauth.StsTokenManager.prototype.copy = function(tokenManagerToCopy) {\n  this.accessToken_ = tokenManagerToCopy.accessToken_;\n  this.refreshToken_ = tokenManagerToCopy.refreshToken_;\n  this.expiresAt_ = tokenManagerToCopy.expiresAt_;\n};\n\n\n/**\n * Exchanges the current refresh token with an access and refresh token.\n * @return {!goog.Promise<?fireauth.StsTokenManager.Response>}\n * @private\n */\nfireauth.StsTokenManager.prototype.exchangeRefreshToken_ = function() {\n  const data = {\n    'grant_type': 'refresh_token',\n    'refresh_token': this.refreshToken_\n  };\n  return this.requestToken_(data);\n};\n\n\n/**\n * Sends a request to STS token endpoint for an access/refresh token.\n * @param {!Object} data The request data to send to STS token endpoint.\n * @return {!goog.Promise<?fireauth.StsTokenManager.Response>}\n * @private\n */\nfireauth.StsTokenManager.prototype.requestToken_ = function(data) {\n  // Send RPC request to STS token endpoint.\n  return this.rpcHandler_.requestStsToken(data)\n      .then((resp) => {\n        const response =\n            /** @type {!fireauth.StsTokenManager.ResponseData} */ (resp);\n        this.accessToken_ = fireauth.IdToken.parse(\n            response[fireauth.RpcHandler.StsServerField.ACCESS_TOKEN]);\n        this.refreshToken_ =\n            response[fireauth.RpcHandler.StsServerField.REFRESH_TOKEN];\n        this.setExpiresIn(\n            response[fireauth.RpcHandler.StsServerField.EXPIRES_IN]);\n        return /** @type {!fireauth.StsTokenManager.Response} */ ({\n          'accessToken': this.accessToken_.toString(),\n          'refreshToken': this.refreshToken_\n        });\n      })\n      .thenCatch((error) => {\n        // Refresh token expired or user deleted. In this case, reset refresh\n        // token to prevent sending the request again to the STS server unless\n        // the token is manually updated, perhaps via successful\n        // reauthentication.\n        if (error['code'] == 'auth/user-token-expired') {\n          this.refreshToken_ = null;\n        }\n        throw error;\n      });\n};\n\n\n/** @return {boolean} Whether the refresh token is expired. */\nfireauth.StsTokenManager.prototype.isRefreshTokenExpired = function() {\n  return !!(this.accessToken_ && !this.refreshToken_);\n};\n\n\n/**\n * Returns an STS token. If the cached one is unexpired it is directly returned.\n * Otherwise the existing ID token or refresh token is exchanged for a new one.\n * If there is no user signed in, returns null.\n *\n * @param {boolean=} forceRefresh Whether to force refresh token exchange.\n * @return {!goog.Promise<?fireauth.StsTokenManager.Response>}\n */\nfireauth.StsTokenManager.prototype.getToken = function(forceRefresh) {\n  forceRefresh = !!forceRefresh;\n  // Refresh token is expired.\n  if (this.isRefreshTokenExpired()) {\n    return goog.Promise.reject(\n        new fireauth.AuthError(fireauth.authenum.Error.TOKEN_EXPIRED));\n  }\n  if (!forceRefresh && this.accessToken_ && !this.isExpired_()) {\n    // Cached STS access token not expired, return it.\n    return /** @type {!goog.Promise} */ (goog.Promise.resolve({\n      'accessToken': this.accessToken_.toString(),\n      'refreshToken': this.refreshToken_\n    }));\n  } else if (this.refreshToken_) {\n    // Expired but refresh token available, exchange refresh token for STS\n    // token.\n    return this.exchangeRefreshToken_();\n  } else {\n    // No token, return null token.\n    return goog.Promise.resolve(\n        /** @type {?fireauth.StsTokenManager.Response} */ (null));\n  }\n};\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines the fireauth.storage.RedirectUserManager class which\n * provides utilities to store, retrieve and delete an Auth user during a\n * redirect operation.\n */\n\ngoog.provide('fireauth.storage.RedirectUserManager');\n\ngoog.require('fireauth.AuthUser');\ngoog.require('fireauth.authStorage');\n\n\n/**\n * Defines the Auth redirect user storage manager. It provides methods\n * to store, load and delete a user going through a link with redirect\n * operation.\n * @param {string} appId The Auth state's application ID.\n * @param {?fireauth.authStorage.Manager=} opt_manager The underlying storage\n *     manager to use. If none is provided, the default global instance is used.\n * @constructor @struct @final\n */\nfireauth.storage.RedirectUserManager = function(appId, opt_manager) {\n  /** @const @private{string} appId The Auth state's application ID. */\n  this.appId_ = appId;\n  /**\n   * @const @private{!fireauth.authStorage.Manager} The underlying storage\n   *     manager.\n   */\n  this.manager_ = opt_manager || fireauth.authStorage.Manager.getInstance();\n};\n\n\n/**\n * @const @private{!fireauth.authStorage.Key} The Auth redirect user storage\n *     identifier.\n */\nfireauth.storage.RedirectUserManager.REDIRECT_USER_KEY_ = {\n  name: 'redirectUser',\n  persistent: fireauth.authStorage.Persistence.SESSION\n};\n\n\n/**\n * Stores the user being redirected for the provided application ID.\n * @param {!fireauth.AuthUser} redirectUser The user being redirected.\n * @return {!goog.Promise<void>} A promise that resolves on success.\n */\nfireauth.storage.RedirectUserManager.prototype.setRedirectUser =\n    function(redirectUser) {\n  return this.manager_.set(\n      fireauth.storage.RedirectUserManager.REDIRECT_USER_KEY_,\n      redirectUser.toPlainObject(),\n      this.appId_);\n};\n\n\n/**\n * Removes the stored redirected user for provided app ID.\n * @return {!goog.Promise<void>} A promise that resolves on success.\n */\nfireauth.storage.RedirectUserManager.prototype.removeRedirectUser =\n    function() {\n  return this.manager_.remove(\n      fireauth.storage.RedirectUserManager.REDIRECT_USER_KEY_, this.appId_);\n};\n\n\n/**\n * @param {?string=} opt_authDomain The optional Auth domain to override if\n *     provided.\n * @return {!goog.Promise<?fireauth.AuthUser>} A promise that resolves with\n *     the stored redirected user for the provided app ID.\n */\nfireauth.storage.RedirectUserManager.prototype.getRedirectUser =\n    function(opt_authDomain) {\n  return this.manager_.get(\n      fireauth.storage.RedirectUserManager.REDIRECT_USER_KEY_, this.appId_)\n      .then(function(response) {\n        // If potential user saved, override Auth domain if authDomain is\n        // provided.\n        if (response && opt_authDomain) {\n          response['authDomain'] = opt_authDomain;\n        }\n        return fireauth.AuthUser.fromPlainObject(response || {});\n      });\n};\n","/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines the fireauth.storage.UserManager class which provides\n * utilities to retrieve, store and delete the currently logged in user and to\n * listen to external authentication changes for the same app.\n * With the ability to modify Auth state persistence. The behavior is as\n * follows:\n * Common cases:\n * <ul>\n * <li>Initially, local and session storage will be checked and the state will\n *     be loaded from there without changing it unless the developer calls\n *     setPersistence explicitly. The requirement is that at any time, Auth\n *     state can be saved using one type only of persistence and never more than\n *     one.</li>\n * <li>If the developer tries to sign in with no persistence specified, the\n *     default setting will be used (local in a browser).</li>\n * <li>If the user is not signed in and persistence is set, any future sign-in\n *     attempt will use that type of persistence.</li>\n * <li>If the user is signed in and the developer then switches persistence,\n *     that existing signed in user will change persistence to the new one. All\n *     future sign-in attempts will use that same persistence.</li>\n * <li>When signInWithRedirect is called, the current persistence type is passed\n *     along with that request and on redirect back to app will pass that type\n *     to determine how that state is saved (overriding the default). If the\n *     persistence is explicitly specified on that page, it will change that\n *     redirected Auth state persistence. This is the only time the persistence\n *     is passed from one page to another.\n *     So internally, on redirect, the redirect state is retrieved and then we\n *     check: If the persistence was explicitly provided, we override the\n *     previous type and save the Auth state using that. If no persistence was\n *     explicitly provided, we use the previous persistence type that was passed\n *     in the redirect response.</li>\n * </ul>\n * Behavior across tabs:\n * <ul>\n * <li>User can sign in using session storage on multiple tabs. Each tab cannot\n *     see the state of the other tab.</li>\n * <li>Any attempt to sign in using local storage will be detected and\n *     synchronized on all tabs. If the user was previously signed in on a\n *     specific tab using session storage, that state will be cleared.</li>\n * <li>If the user was previously signed in using local storage and then signs\n *     in using session storage, the user will be signed in on the current tab\n *     only and signed out on all other tabs.</li>\n * <li>Similar logic is applied to the ‘none’ state. In one tab, switching to\n *     ‘none’ state will delete any previously saved state in ‘local’\n *     persistence in other tabs.</li>\n * </ul>\n */\n\ngoog.provide('fireauth.storage.UserManager');\n\ngoog.require('fireauth.AuthUser');\ngoog.require('fireauth.authStorage');\ngoog.require('fireauth.constants');\ngoog.require('goog.Promise');\n\n\n/**\n * Defines the Auth user storage manager. It provides methods to\n * store, load and delete an authenticated current user. It also provides\n * methods to listen to external user changes (updates, sign in, sign out, etc.)\n * @param {string} appId The Auth state's application ID.\n * @param {?fireauth.authStorage.Manager=} opt_manager The underlying storage\n *     manager to use. If none is provided, the default global instance is used.\n * @constructor @struct @final\n */\nfireauth.storage.UserManager = function(appId, opt_manager) {\n  /** @const @private{string} appId The Auth state's application ID. */\n  this.appId_ = appId;\n  /**\n   * @const @private{!fireauth.authStorage.Manager} The underlying storage\n   *     manager.\n   */\n  this.manager_ = opt_manager || fireauth.authStorage.Manager.getInstance();\n  /**\n   * @private {?fireauth.authStorage.Key} The current Auth user storage\n   *     identifier.\n   */\n  this.currentAuthUserKey_ = null;\n  /**\n   * @private {!goog.Promise} Storage operation serializer promise. This will\n   *     initialize the current persistence used and clean up any duplicate\n   *     states or temporary values (persistence for pending redirect).\n   *     Afterwards this is used to queue storage requests to make sure\n   *     storage operations are always synchronized and read/write events are\n   *     processed on the same storage.\n   */\n  this.onReady_ = this.initialize_();\n  // This internal listener will always run before the external ones.\n  // This is needed to queue processing of this first before any getCurrentUser\n  // is called from external listeners.\n  this.manager_.addListener(\n      fireauth.storage.UserManager.getAuthUserKey_(\n          fireauth.authStorage.Persistence.LOCAL),\n      this.appId_,\n      goog.bind(this.switchToLocalOnExternalEvent_, this));\n};\n\n\n/**\n * Switches to local storage on external storage event. This will happen when\n * state is specified as local in an external tab while it is none or session\n * in the current one. If a user signs in in an external tab, the current window\n * should detect this, clear existing storage and switch to local storage.\n * @private\n */\nfireauth.storage.UserManager.prototype.switchToLocalOnExternalEvent_ =\n    function() {\n  var self = this;\n  var localKey = fireauth.storage.UserManager.getAuthUserKey_(\n      fireauth.authStorage.Persistence.LOCAL);\n  // Wait for any pending operation to finish first.\n  // Block next read/write operation until persistence is transitioned to\n  // local.\n  this.waitForReady_(function() {\n    return goog.Promise.resolve().then(function() {\n      // If current persistence is not already local.\n      if (self.currentAuthUserKey_ &&\n          self.currentAuthUserKey_.persistent !=\n          fireauth.authStorage.Persistence.LOCAL) {\n        // Check if new current user is available in local storage.\n        return self.manager_.get(localKey, self.appId_);\n      }\n      return null;\n    }).then(function(response) {\n      // Sign in on an external tab.\n      if (response) {\n        // Remove any existing non-local user.\n        return self.removeAllExcept_(\n            fireauth.authStorage.Persistence.LOCAL).then(function() {\n              // Set persistence to local.\n              self.currentAuthUserKey_ = localKey;\n            });\n      }\n    });\n  });\n};\n\n\n/**\n * Removes all states stored in all supported persistence types excluding the\n * specified one.\n * @param {?fireauth.authStorage.Persistence} persistence The type of storage\n *     persistence to switch to.\n * @return {!goog.Promise} The promise that resolves when all stored values are\n *     removed for types of storage excluding specified persistence. This helps\n *     ensure there is always one type of persistence at any time.\n * @private\n */\nfireauth.storage.UserManager.prototype.removeAllExcept_ =\n    function(persistence) {\n  var promises = [];\n  // Queue all promises to remove current user in any other persistence type.\n  for (var key in fireauth.authStorage.Persistence) {\n    // Skip specified persistence.\n    if (fireauth.authStorage.Persistence[key] !== persistence) {\n      var storageKey = fireauth.storage.UserManager.getAuthUserKey_(\n          fireauth.authStorage.Persistence[key]);\n      promises.push(this.manager_.remove(\n          /** @type {!fireauth.authStorage.Key} */ (storageKey),\n          this.appId_));\n    }\n  }\n  // Clear persistence key (only useful for initial load upon returning from a\n  // a redirect sign-in operation).\n  promises.push(this.manager_.remove(\n      fireauth.storage.UserManager.PERSISTENCE_KEY_,\n      this.appId_));\n  return goog.Promise.all(promises);\n};\n\n\n/**\n * Initializes the current persistence state. This will check the 3 supported\n * types. The first one that is found will be the current persistence. All\n * others will be cleared. If none is found we check PERSISTENCE_KEY_ which\n * when specified means that the operation is returning from a\n * signInWithRedirect call. This persistence will be applied.\n * Otherwise the default local persistence is used.\n * @return {!goog.Promise} A promise that resolves when the current persistence\n *     is resolved.\n * @private\n */\nfireauth.storage.UserManager.prototype.initialize_ = function() {\n  var self = this;\n  // Local key.\n  var localKey = fireauth.storage.UserManager.getAuthUserKey_(\n      fireauth.authStorage.Persistence.LOCAL);\n  // Session key.\n  var sessionKey = fireauth.storage.UserManager.getAuthUserKey_(\n      fireauth.authStorage.Persistence.SESSION);\n  // In memory key. This is unlikely to contain anything on load.\n  var inMemoryKey = fireauth.storage.UserManager.getAuthUserKey_(\n      fireauth.authStorage.Persistence.NONE);\n  // Migrate any old currentUser from localStorage to indexedDB.\n  // This keeps any user signed in without the need for reauthentication and\n  // minimizes risks of dangling Auth states.\n  return this.manager_.migrateFromLocalStorage(\n      localKey, this.appId_).then(function() {\n    // Check if state is stored in session storage.\n    return self.manager_.get(sessionKey, self.appId_);\n  }).then(function(response) {\n    if (response) {\n      // Session storage is being used.\n      return sessionKey;\n    } else {\n      // Session storage is empty. Check in memory storage.\n      return self.manager_.get(inMemoryKey, self.appId_)\n          .then(function(response) {\n            if (response) {\n              // In memory storage being used.\n              return inMemoryKey;\n            } else {\n              // Check local storage.\n              return self.manager_.get(localKey, self.appId_)\n                  .then(function(response) {\n                    if (response) {\n                      // Local storage being used.\n                      return localKey;\n                    } else {\n                      // Nothing found in any supported storage.\n                      // Check current user persistence in storage.\n                      return self.manager_.get(\n                          fireauth.storage.UserManager.PERSISTENCE_KEY_,\n                          self.appId_).then(function(persistence) {\n                            if (persistence) {\n                              // Sign in with redirect operation, apply this\n                              // persistence to any current user.\n                              return fireauth.storage.UserManager\n                                  .getAuthUserKey_(persistence);\n                            } else {\n                              // No persistence found, use the default.\n                              return localKey;\n                            }\n                          });\n                    }\n                  });\n            }\n          });\n    }\n  }).then(function(currentKey) {\n    // Set current key according to the persistence detected.\n    self.currentAuthUserKey_ = currentKey;\n    // Make sure only one state available. Clean up everything else.\n    return self.removeAllExcept_(currentKey.persistent);\n  }).thenCatch(function(error) {\n    // If an error occurs in the process and no current key detected, set to\n    // persistence value to default.\n    if (!self.currentAuthUserKey_) {\n      self.currentAuthUserKey_ = localKey;\n    }\n  });\n};\n\n\n/**\n * @const @private {string} The Auth current user storage identifier name.\n */\nfireauth.storage.UserManager.AUTH_USER_KEY_NAME_ = 'authUser';\n\n\n/**\n * @const @private{!fireauth.authStorage.Key} The Auth user storage persistence\n *     identifier. This is needed to remember the previous persistence state for\n *     sign-in with redirect.\n */\nfireauth.storage.UserManager.PERSISTENCE_KEY_ = {\n  name: 'persistence',\n  persistent: fireauth.authStorage.Persistence.SESSION\n};\n\n\n/**\n * Returns the Auth user key corresponding to the persistence type provided.\n * @param {!fireauth.authStorage.Persistence} persistence The key for the\n *     specified type of persistence.\n * @return {!fireauth.authStorage.Key} The corresponding Auth user storage\n *     identifier.\n * @private\n */\nfireauth.storage.UserManager.getAuthUserKey_ = function(persistence) {\n  return {\n    name: fireauth.storage.UserManager.AUTH_USER_KEY_NAME_,\n    persistent: persistence\n  };\n};\n\n\n/**\n * Sets the persistence to the specified type.\n * If an existing user already is in storage, it copies that value to the new\n * storage and clears all the others.\n * @param {!fireauth.authStorage.Persistence} persistence The type of storage\n *     persistence to switch to.\n * @return {!goog.Promise} A promise that resolves when persistence change is\n *     applied.\n */\nfireauth.storage.UserManager.prototype.setPersistence = function(persistence) {\n  var currentUser = null;\n  var self = this;\n  // Validate the persistence type provided. This will throw a synchronous error\n  // if invalid.\n  fireauth.authStorage.validatePersistenceArgument(persistence);\n  // Wait for turn in queue.\n  return this.waitForReady_(function() {\n    // If persistence hasn't changed, do nothing.\n    if (persistence != self.currentAuthUserKey_.persistent) {\n      // Persistence changed. Copy from current storage to new one.\n      return self.manager_.get(\n        /** @type {!fireauth.authStorage.Key} */ (self.currentAuthUserKey_),\n        self.appId_).then(function(result) {\n        // Save current user.\n        currentUser = result;\n        // Clear from current storage.\n        return self.removeAllExcept_(persistence);\n      }).then(function() {\n        // Update persistence key to the new one.\n        self.currentAuthUserKey_ =\n            fireauth.storage.UserManager.getAuthUserKey_(persistence);\n        // Copy current storage type to the new one.\n        if (currentUser) {\n          return self.manager_.set(\n              /** @type {!fireauth.authStorage.Key} */ (\n                  self.currentAuthUserKey_),\n              currentUser,\n              self.appId_);\n        }\n      });\n    }\n    // No change in persistence type.\n    return goog.Promise.resolve();\n  });\n};\n\n\n/**\n * Saves the current persistence type so it can be retrieved after a page\n * redirect. This is relevant for signInWithRedirect.\n * @return {!goog.Promise} Promise that resolve when current persistence is\n *     saved.\n */\nfireauth.storage.UserManager.prototype.savePersistenceForRedirect = function() {\n  var self = this;\n  return this.waitForReady_(function() {\n    // Save persistence to survive redirect.\n    return self.manager_.set(\n        fireauth.storage.UserManager.PERSISTENCE_KEY_,\n        self.currentAuthUserKey_.persistent,\n        self.appId_);\n  });\n};\n\n\n/**\n * Stores the current Auth user for the provided application ID.\n * @param {!fireauth.AuthUser} currentUser The app current Auth user to save.\n * @return {!goog.Promise<void>} A promise that resolves on success.\n */\nfireauth.storage.UserManager.prototype.setCurrentUser = function(currentUser) {\n  var self = this;\n  // Wait for any pending persistence change to be resolved.\n  return this.waitForReady_(function() {\n    return self.manager_.set(\n        /** @type {!fireauth.authStorage.Key} */ (self.currentAuthUserKey_),\n        currentUser.toPlainObject(),\n        self.appId_);\n  });\n};\n\n\n/**\n * Removes the stored current user for provided app ID.\n * @return {!goog.Promise<void>} A promise that resolves on success.\n */\nfireauth.storage.UserManager.prototype.removeCurrentUser = function() {\n  var self = this;\n  // Wait for any pending persistence change to be resolved.\n  return this.waitForReady_(function() {\n    return self.manager_.remove(\n        /** @type {!fireauth.authStorage.Key} */ (self.currentAuthUserKey_),\n        self.appId_);\n  });\n};\n\n\n/**\n * @param {?string=} authDomain The optional Auth domain to override if\n *     provided.\n * @param {?fireauth.constants.EmulatorSettings=} emulatorConfig The current\n *     emulator config to use in user requests.\n * @return {!goog.Promise<?fireauth.AuthUser>} A promise that resolves with\n *     the stored current user for the provided app ID.\n */\nfireauth.storage.UserManager.prototype.getCurrentUser = function(authDomain, emulatorConfig) {\n  var self = this;\n  // Wait for any pending persistence change to be resolved.\n  return this.waitForReady_(function() {\n    return self.manager_.get(\n        /** @type {!fireauth.authStorage.Key} */ (self.currentAuthUserKey_),\n        self.appId_).then(function(response) {\n          // If potential user saved, override Auth domain if authDomain is\n          // provided.\n          // This is useful in cases where on one page the developer initializes\n          // the Auth instance without authDomain and signs in user using\n          // headless methods. On another page, Auth is initialized with\n          // authDomain for the purpose of linking with a popup. The loaded user\n          // (stored without the authDomain) must have this field updated with\n          // the current authDomain.\n          if (response && authDomain) {\n            response['authDomain'] = authDomain;\n          }\n          if (response && emulatorConfig) {\n            response['emulatorConfig'] = emulatorConfig;\n          }\n          return fireauth.AuthUser.fromPlainObject(response || {});\n        });\n  });\n};\n\n\n/**\n * Serializes storage access operations especially since persistence\n * could be updated from one type to the other while read/write operations\n * occur.\n * @param {function():!goog.Promise<T>} cb The promise return callback to chain\n *     when pending operations are resolved.\n * @return {!goog.Promise<T>} The resulting promise that resolves when provided\n *     promise finally resolves.\n * @template T\n * @private\n */\nfireauth.storage.UserManager.prototype.waitForReady_ = function(cb) {\n  // Wait for any pending persistence change to be resolved before running\n  // storage related operation. Chain to onReady so next call will wait for\n  // this operation to resolve.\n  // While an error is unlikely, run callback even if it happens, otherwise\n  // no storage related event will be allowed to complete after an error.\n  this.onReady_ = this.onReady_.then(cb, cb);\n  return this.onReady_;\n};\n\n\n/**\n * Adds a listener to Auth current user change event for app ID provided.\n * @param {!function()} listener The listener to run on current user change\n *     event.\n */\nfireauth.storage.UserManager.prototype.addCurrentUserChangeListener =\n    function(listener) {\n  // When this is triggered, getCurrentUser is called, that will have to wait\n  // for switchToLocalOnExternalEvent_ to resolve which is ahead of it in the\n  // queue.\n  this.manager_.addListener(\n      fireauth.storage.UserManager.getAuthUserKey_(\n          fireauth.authStorage.Persistence.LOCAL),\n      this.appId_,\n      listener);\n};\n\n\n/**\n * Removes a listener to Auth current user change event for app ID provided.\n * @param {!function()} listener The listener to remove from current user change\n *     event changes.\n */\nfireauth.storage.UserManager.prototype.removeCurrentUserChangeListener =\n    function(listener) {\n  this.manager_.removeListener(\n      fireauth.storage.UserManager.getAuthUserKey_(\n          fireauth.authStorage.Persistence.LOCAL),\n      this.appId_,\n      listener);\n};\n","/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview The headless Auth class used for authenticating Firebase users.\n */\n\ngoog.provide('fireauth.Auth');\n\ngoog.require('fireauth.ActionCodeInfo');\ngoog.require('fireauth.ActionCodeSettings');\ngoog.require('fireauth.AdditionalUserInfo');\ngoog.require('fireauth.AuthCredential');\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.AuthEvent');\ngoog.require('fireauth.AuthEventHandler');\ngoog.require('fireauth.AuthEventManager');\ngoog.require('fireauth.AuthProvider');\ngoog.require('fireauth.AuthSettings');\ngoog.require('fireauth.AuthUser');\ngoog.require('fireauth.ConfirmationResult');\ngoog.require('fireauth.EmailAuthProvider');\ngoog.require('fireauth.MultiFactorError');\ngoog.require('fireauth.RpcHandler');\ngoog.require('fireauth.UserEventType');\ngoog.require('fireauth.authenum.Error');\ngoog.require('fireauth.constants');\ngoog.require('fireauth.deprecation');\ngoog.require('fireauth.idp');\ngoog.require('fireauth.iframeclient.IfcHandler');\ngoog.require('fireauth.object');\ngoog.require('fireauth.storage.RedirectUserManager');\ngoog.require('fireauth.storage.UserManager');\ngoog.require('fireauth.util');\ngoog.require('goog.Promise');\ngoog.require('goog.array');\ngoog.require('goog.events');\ngoog.require('goog.events.Event');\ngoog.require('goog.events.EventTarget');\ngoog.require('goog.object');\n\n\n\n/**\n * Creates the Firebase Auth corresponding for the App provided.\n *\n * @param {!firebase.app.App} app The corresponding Firebase App.\n * @constructor\n * @implements {fireauth.AuthEventHandler}\n * @implements {firebase.Service}\n * @extends {goog.events.EventTarget}\n */\nfireauth.Auth = function(app) {\n  /** @private {boolean} Whether this instance is deleted. */\n  this.deleted_ = false;\n  /** The Auth instance's settings object. */\n  fireauth.object.setReadonlyProperty(\n      this, 'settings', new fireauth.AuthSettings());\n  /** Auth's corresponding App. */\n  fireauth.object.setReadonlyProperty(this, 'app', app);\n  // Initialize RPC handler.\n  // API key is required for web client RPC calls.\n  if (this.app_().options && this.app_().options['apiKey']) {\n    var clientFullVersion = firebase.SDK_VERSION ?\n        fireauth.util.getClientVersion(\n            fireauth.util.ClientImplementation.JSCORE, firebase.SDK_VERSION) :\n        null;\n    this.rpcHandler_ = new fireauth.RpcHandler(\n        this.app_().options && this.app_().options['apiKey'],\n        // Get the client Auth endpoint used.\n        fireauth.constants.getEndpointConfig(fireauth.constants.clientEndpoint),\n        clientFullVersion);\n  } else {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INVALID_API_KEY);\n  }\n  /** @private {!Array<!goog.Promise<*, *>|!goog.Promise<void>>} List of\n   *      pending promises. */\n  this.pendingPromises_ = [];\n  /** @private {!Array<!function(?string)>} Auth token listeners. */\n  this.authListeners_ = [];\n  /** @private {!Array<!function(?string)>} User change listeners. */\n  this.userChangeListeners_ = [];\n  /**\n   * @private {!firebase.Subscribe} The subscribe function to the Auth ID token\n   *     change observer. This will trigger on ID token changes, including\n   *     token refresh on the same user.\n   */\n  this.onIdTokenChanged_ = firebase.INTERNAL.createSubscribe(\n      goog.bind(this.initIdTokenChangeObserver_, this));\n  /**\n   * @private {?string|undefined} The UID of the user that last triggered the\n   *     user state change listener.\n   */\n  this.userStateChangeUid_ = undefined;\n  /**\n   * @private {!firebase.Subscribe} The subscribe function to the user state\n   *     change observer.\n   */\n  this.onUserStateChanged_ = firebase.INTERNAL.createSubscribe(\n      goog.bind(this.initUserStateObserver_, this));\n  // Set currentUser to null.\n  this.setCurrentUser_(null);\n  /**\n   * @private {!fireauth.storage.UserManager} The Auth user storage\n   *     manager instance.\n   */\n  this.userStorageManager_ =\n      new fireauth.storage.UserManager(this.getStorageKey());\n  /**\n   * @private {!fireauth.storage.RedirectUserManager} The redirect user\n   *     storagemanager instance.\n   */\n  this.redirectUserStorageManager_ =\n      new fireauth.storage.RedirectUserManager(this.getStorageKey());\n  /**\n   * @private {!goog.Promise<undefined>} Promise that resolves when initial\n   *     state is loaded from storage.\n   */\n  this.authStateLoaded_ = this.registerPendingPromise_(this.initAuthState_());\n  /**\n   * @private {!goog.Promise<undefined>} Promise that resolves when state and\n   *     redirect result is ready, after which sign in and sign out operations\n   *     are safe to execute.\n   */\n  this.redirectStateIsReady_ = this.registerPendingPromise_(\n      this.initAuthRedirectState_());\n  /** @private {boolean} Whether initial state has already been resolved. */\n  this.isStateResolved_ = false;\n  /**\n   * @private {!function()} The syncAuthChanges function with context set to\n   *     auth instance.\n   */\n  this.getSyncAuthUserChanges_ = goog.bind(this.syncAuthUserChanges_, this);\n  /** @private {!function(!fireauth.AuthUser):!goog.Promise} The handler for\n   *      user state changes. */\n  this.userStateChangeListener_ =\n      goog.bind(this.handleUserStateChange_, this);\n  /** @private {!function(!Object)} The handler for user token changes. */\n  this.userTokenChangeListener_ =\n      goog.bind(this.handleUserTokenChange_, this);\n  /** @private {!function(!Object)} The handler for user deletion. */\n  this.userDeleteListener_ = goog.bind(this.handleUserDelete_, this);\n  /** @private {!function(!Object)} The handler for user invalidation. */\n  this.userInvalidatedListener_ = goog.bind(this.handleUserInvalidated_, this);\n  /**\n   * @private {?fireauth.AuthEventManager} The Auth event manager instance.\n   */\n  this.authEventManager_ = null;\n  // TODO: find better way to enable or disable auth event manager.\n  if (fireauth.AuthEventManager.ENABLED) {\n    // Initialize Auth event manager to handle popup and redirect operations.\n    this.initAuthEventManager_();\n  }\n\n  // Export INTERNAL namespace.\n  this.INTERNAL = {};\n  this.INTERNAL['delete'] = goog.bind(this.delete, this);\n  this.INTERNAL['logFramework'] = goog.bind(this.logFramework, this);\n  /**\n   * @private {number} The number of Firebase services subscribed to Auth\n   *     changes.\n   */\n  this.firebaseServices_ = 0;\n  // Add call to superclass constructor.\n  fireauth.Auth.base(this, 'constructor');\n  // Initialize readable/writable Auth properties.\n  this.initializeReadableWritableProps_();\n  /**\n   * @private {!Array<string>} List of Firebase frameworks/libraries used. This\n   *     is currently only used to log FirebaseUI.\n   */\n  this.frameworks_ = [];\n\n  /**\n   * @private {?fireauth.constants.EmulatorSettings} The current\n   *     emulator settings.\n   */\n  this.emulatorConfig_ = null;\n};\ngoog.inherits(fireauth.Auth, goog.events.EventTarget);\n\n\n/**\n * Language code change custom event.\n * @param {?string} languageCode The new language code.\n * @constructor\n * @extends {goog.events.Event}\n */\nfireauth.Auth.LanguageCodeChangeEvent = function(languageCode) {\n  goog.events.Event.call(\n      this, fireauth.constants.AuthEventType.LANGUAGE_CODE_CHANGED);\n  this.languageCode = languageCode;\n};\ngoog.inherits(fireauth.Auth.LanguageCodeChangeEvent, goog.events.Event);\n\n\n/**\n * Emulator config change custom event.\n * @param {?fireauth.constants.EmulatorSettings} emulatorConfig The new\n *     emulator settings.\n * @constructor\n * @extends {goog.events.Event}\n */\nfireauth.Auth.EmulatorConfigChangeEvent = function(emulatorConfig) {\n  goog.events.Event.call(this, fireauth.constants.AuthEventType.EMULATOR_CONFIG_CHANGED);\n  this.emulatorConfig = emulatorConfig;\n};\ngoog.inherits(fireauth.Auth.EmulatorConfigChangeEvent, goog.events.Event);\n\n\n/**\n * Framework change custom event.\n * @param {!Array<string>} frameworks The new frameworks array.\n * @constructor\n * @extends {goog.events.Event}\n */\nfireauth.Auth.FrameworkChangeEvent = function(frameworks) {\n  goog.events.Event.call(\n      this, fireauth.constants.AuthEventType.FRAMEWORK_CHANGED);\n  this.frameworks = frameworks;\n};\ngoog.inherits(fireauth.Auth.FrameworkChangeEvent, goog.events.Event);\n\n\n/**\n * Changes the Auth state persistence to the specified one.\n * @param {!fireauth.authStorage.Persistence} persistence The Auth state\n *     persistence mechanism.\n * @return {!goog.Promise<void>}\n */\nfireauth.Auth.prototype.setPersistence = function(persistence) {\n  // TODO: fix auth.delete() behavior and how this affects persistence\n  // change after deletion.\n  // Throw an error if already destroyed.\n  // Set current persistence.\n  var p = this.userStorageManager_.setPersistence(persistence);\n  return /** @type {!goog.Promise<void>} */ (this.registerPendingPromise_(p));\n};\n\n\n/**\n * Get rid of Closure warning - the property is adding in the constructor.\n * @type {!firebase.app.App}\n */\nfireauth.Auth.prototype.app;\n\n\n/**\n * Sets the language code.\n * @param {?string} languageCode\n */\nfireauth.Auth.prototype.setLanguageCode = function(languageCode) {\n  // Don't do anything if no change detected.\n  if (this.languageCode_ !== languageCode && !this.deleted_) {\n    this.languageCode_ = languageCode;\n    // Update custom Firebase locale field.\n    this.rpcHandler_.updateCustomLocaleHeader(this.languageCode_);\n    // Notify external language code change listeners.\n    this.notifyLanguageCodeListeners_();\n  }\n};\n\n\n/**\n * Returns the current auth instance's language code if available.\n * @return {?string}\n */\nfireauth.Auth.prototype.getLanguageCode = function() {\n  return this.languageCode_;\n};\n\n\n/**\n * Sets the current language to the default device/browser preference.\n */\nfireauth.Auth.prototype.useDeviceLanguage = function() {\n  this.setLanguageCode(fireauth.util.getUserLanguage());\n};\n\n\n/**\n * Sets the emulator configuration (go/firebase-emulator-connection-api).\n * @param {string} url The url for the Auth emulator.\n * @param {?Object=} options Optional options to specify emulator settings.\n */\nfireauth.Auth.prototype.useEmulator = function(url, options) {\n  // Emulator config can only be set once.\n  if (!this.emulatorConfig_) {\n    if (!/^https?:\\/\\//.test(url)) {\n      throw new fireauth.AuthError(\n          fireauth.authenum.Error.ARGUMENT_ERROR,\n          'Emulator URL must start with a valid scheme (http:// or https://).');\n    }\n    // Emit a warning so dev knows we are now in test mode.\n    const disableBanner = options ? !!options['disableWarnings'] : false;\n    this.emitEmulatorWarning_(disableBanner);\n    // Persist the config.\n    this.emulatorConfig_ = {url, disableWarnings: disableBanner};\n    // Disable app verification.\n    this.settings_().setAppVerificationDisabledForTesting(true);\n    // Update RPC handler endpoints.\n    this.rpcHandler_.updateEmulatorConfig(this.emulatorConfig_);\n    // Notify external event listeners.\n    this.notifyEmulatorConfigListeners_();\n  }\n}\n\n\n/**\n * Emits a console info and a visual banner if emulator integration is\n * enabled.\n * @param {boolean} disableBanner Whether visual banner should be disabled.\n * @private\n */\nfireauth.Auth.prototype.emitEmulatorWarning_ = function(disableBanner) {\n  fireauth.util.consoleInfo('WARNING: You are using the Auth Emulator,' +\n    ' which is intended for local testing only.  Do not use with' +\n    ' production credentials.');\n  if (goog.global.document && !disableBanner) {\n    fireauth.util.onDomReady().then(() => {\n      const ele = goog.global.document.createElement('div');\n      ele.innerText = 'Running in emulator mode. Do not use with production' +\n        ' credentials.';\n      ele.style.position = 'fixed';\n      ele.style.width = '100%';\n      ele.style.backgroundColor = '#ffffff';\n      ele.style.border = '.1em solid #000000';\n      ele.style.color = '#b50000';\n      ele.style.bottom = '0px';\n      ele.style.left = '0px';\n      ele.style.margin = '0px';\n      ele.style.zIndex = 10000;\n      ele.style.textAlign = 'center';\n      ele.classList.add('firebase-emulator-warning');\n      goog.global.document.body.appendChild(ele);\n    });\n  }\n}\n\n\n/**\n * @return {?fireauth.constants.EmulatorConfig}\n */\nfireauth.Auth.prototype.getEmulatorConfig = function() {\n  if (!this.emulatorConfig_) {\n    return null;\n  }\n  const uri = goog.Uri.parse(this.emulatorConfig_.url);\n  return /** @type {!fireauth.constants.EmulatorConfig} */ (\n      fireauth.object.makeReadonlyCopy({\n        'protocol': uri.getScheme(),\n        'host': uri.getDomain(),\n        'port': uri.getPort(),\n        'options': fireauth.object.makeReadonlyCopy({\n          'disableWarnings': this.emulatorConfig_.disableWarnings,\n        }),\n      }));\n}\n\n\n/**\n * @param {string} frameworkId The framework identifier.\n */\nfireauth.Auth.prototype.logFramework = function(frameworkId) {\n  // Theoretically multiple frameworks could be used\n  // (angularfire and FirebaseUI). Once a framework is used, it is not going\n  // to be unused, so no point adding a method to remove the framework ID.\n  this.frameworks_.push(frameworkId);\n  // Update the client version in RPC handler with the new frameworks.\n  this.rpcHandler_.updateClientVersion(firebase.SDK_VERSION ?\n        fireauth.util.getClientVersion(\n            fireauth.util.ClientImplementation.JSCORE, firebase.SDK_VERSION,\n            this.frameworks_) :\n        null);\n  this.dispatchEvent(new fireauth.Auth.FrameworkChangeEvent(\n      this.frameworks_));\n};\n\n\n/** @return {!Array<string>} The current Firebase frameworks. */\nfireauth.Auth.prototype.getFramework = function() {\n  return goog.array.clone(this.frameworks_);\n};\n\n\n/**\n * Updates the framework list on the provided user and configures the user to\n * listen to the Auth instance for any framework ID changes.\n * @param {!fireauth.AuthUser} user The user to whose framework list needs to be\n *     updated.\n * @private\n */\nfireauth.Auth.prototype.setUserFramework_ = function(user) {\n  // Sets the framework ID on the user.\n  user.setFramework(this.frameworks_);\n  // Sets current Auth instance as framework list change dispatcher on the user.\n  user.setFrameworkChangeDispatcher(this);\n};\n\n\n/**\n * Sets the tenant ID.\n * @param {?string} tenantId The tenant ID of the tenant project if available.\n */\nfireauth.Auth.prototype.setTenantId = function(tenantId) {\n  // Don't do anything if no change detected.\n  if (this.tenantId_ !== tenantId && !this.deleted_) {\n    this.tenantId_ = tenantId;\n    this.rpcHandler_.updateTenantId(this.tenantId_);\n  }\n};\n\n\n/**\n * Returns the current Auth instance's tenant ID.\n * @return {?string}\n */\nfireauth.Auth.prototype.getTenantId = function() {\n  return this.tenantId_;\n};\n\n\n/**\n * Initializes readable/writable properties on Auth.\n * @suppress {invalidCasts}\n * @private\n */\nfireauth.Auth.prototype.initializeReadableWritableProps_ = function() {\n  Object.defineProperty(/** @type {!Object} */ (this), 'lc', {\n    /**\n     * @this {!Object}\n     * @return {?string} The current language code.\n     */\n    get: function() {\n      return this.getLanguageCode();\n    },\n    /**\n     * @this {!Object}\n     * @param {string} value The new language code.\n     */\n    set: function(value) {\n      this.setLanguageCode(value);\n    },\n    enumerable: false\n  });\n  // Initialize to null.\n  /** @private {?string} The current Auth instance's language code. */\n  this.languageCode_ = null;\n\n  // Initialize tenant ID.\n  Object.defineProperty(/** @type {!Object} */ (this), 'ti', {\n    /**\n     * @this {!Object}\n     * @return {?string} The current tenant ID.\n     */\n    get: function() {\n      return this.getTenantId();\n    },\n    /**\n     * @this {!Object}\n     * @param {?string} value The new tenant ID.\n     */\n    set: function(value) {\n      this.setTenantId(value);\n    },\n    enumerable: false\n  });\n  // Initialize to null.\n  /** @private {?string} The current Auth instance's tenant ID. */\n  this.tenantId_ = null;\n\n  // Add the emulator configuration property (readonly).\n  Object.defineProperty(/** @type {!Object} */ (this), 'emulatorConfig', {\n    /**\n     * @this {!Object}\n     * @return {?fireauth.constants.EmulatorConfig} The emulator config if\n     * enabled.\n     */\n    get: function() {\n      return this.getEmulatorConfig();\n    },\n    enumerable: false\n  });\n};\n\n\n/**\n * Notifies all external listeners of the language code change.\n * @private\n */\nfireauth.Auth.prototype.notifyLanguageCodeListeners_ = function() {\n  // Notify external listeners on the language code change.\n  this.dispatchEvent(new fireauth.Auth.LanguageCodeChangeEvent(\n      this.getLanguageCode()));\n};\n\n\n/**\n * Notifies all external listeners of the emulator config change.\n * @private\n */\nfireauth.Auth.prototype.notifyEmulatorConfigListeners_ = function() {\n  // Notify external listeners on the emulator config change.\n  this.dispatchEvent(\n    new fireauth.Auth.EmulatorConfigChangeEvent(this.emulatorConfig_));\n}\n\n\n/**\n * @return {!Object} The object representation of the Auth instance.\n * @override\n */\nfireauth.Auth.prototype.toJSON = function() {\n  // Return the plain object representation in case JSON.stringify is called on\n  // an Auth instance.\n  return {\n    'apiKey': this.app_().options['apiKey'],\n    'authDomain': this.app_().options['authDomain'],\n    'appName': this.app_().name,\n    'currentUser': this.currentUser_() && this.currentUser_().toPlainObject()\n  };\n};\n\n\n/**\n * Returns the Auth event manager promise.\n * @return {!goog.Promise<!fireauth.AuthEventManager>}\n * @private\n */\nfireauth.Auth.prototype.getAuthEventManager_ = function() {\n  // Either return cached Auth event manager promise provider if available or a\n  // promise that rejects with missing Auth domain error.\n  return this.eventManagerProviderPromise_ ||\n      goog.Promise.reject(\n          new fireauth.AuthError(fireauth.authenum.Error.MISSING_AUTH_DOMAIN));\n};\n\n\n/**\n * Initializes the Auth event manager when state is ready.\n * @private\n */\nfireauth.Auth.prototype.initAuthEventManager_ = function() {\n  // Initialize Auth event manager on initState.\n  var self = this;\n  var authDomain = this.app_().options['authDomain'];\n  var apiKey = this.app_().options['apiKey'];\n  // Make sure environment also supports popup and redirect.\n  if (authDomain && fireauth.util.isPopupRedirectSupported()) {\n    // Auth domain is required for Auth event manager to resolve.\n    // Auth state has to be loaded first. One reason is to process link events.\n    this.eventManagerProviderPromise_ = this.authStateLoaded_.then(function() {\n      if (self.deleted_) {\n        return;\n      }\n      // By this time currentUser should be ready if available and will be able\n      // to resolve linkWithRedirect if detected.\n      self.authEventManager_ = fireauth.AuthEventManager.getManager(\n        authDomain,\n        apiKey,\n        self.app_().name,\n        self.emulatorConfig_);\n      // Subscribe Auth instance.\n      self.authEventManager_.subscribe(self);\n      // Subscribe current user by enabling popup and redirect on that user.\n      if (self.currentUser_()) {\n        self.currentUser_().enablePopupRedirect();\n      }\n      // If a redirect user is present, subscribe to popup and redirect events.\n      // In case current user was not available and the developer called link\n      // with redirect on a signed out user, this will work and the linked\n      // logged out user will be returned in getRedirectResult.\n      // current user and redirect user are the same (was already logged in),\n      // currentUser will have priority as it is subscribed before redirect\n      // user. This change will also allow further popup and redirect events on\n      // the redirect user going forward.\n      if (self.redirectUser_) {\n        self.redirectUser_.enablePopupRedirect();\n        // Set the user language for the redirect user.\n        self.setUserLanguage_(\n            /** @type {!fireauth.AuthUser} */ (self.redirectUser_));\n        // Set the user Firebase frameworks for the redirect user.\n        self.setUserFramework_(\n            /** @type {!fireauth.AuthUser} */(self.redirectUser_));\n        // Set the user Emulator configuration for the redirect user.\n        self.setUserEmulatorConfig_(\n            /** @type {!fireauth.AuthUser} */(self.redirectUser_));\n        // Reference to redirect user no longer needed.\n        self.redirectUser_ = null;\n      }\n      return self.authEventManager_;\n    });\n  }\n};\n\n\n/**\n * @param {!fireauth.AuthEvent.Type} mode The Auth type mode.\n * @param {?string=} opt_eventId The event ID.\n * @return {boolean} Whether the auth event handler can handler the provided\n *     event.\n * @override\n */\nfireauth.Auth.prototype.canHandleAuthEvent = function(mode, opt_eventId) {\n  // Only sign in events are handled.\n  switch (mode) {\n    // Accept all general sign in with redirect and unknowns.\n    // Migrating redirect events to use session storage will prevent this event\n    // from leaking to other tabs.\n    case fireauth.AuthEvent.Type.UNKNOWN:\n    case fireauth.AuthEvent.Type.SIGN_IN_VIA_REDIRECT:\n      return true;\n    case fireauth.AuthEvent.Type. SIGN_IN_VIA_POPUP:\n      // Pending sign in with popup event must match the stored popup event ID.\n      return this.popupEventId_ == opt_eventId &&\n          !!this.pendingPopupResolvePromise_;\n    default:\n      return false;\n  }\n};\n\n\n/**\n * Completes the pending popup operation. If error is not null, rejects with the\n * error. Otherwise, it resolves with the popup redirect result.\n * @param {!fireauth.AuthEvent.Type} mode The Auth type mode.\n * @param {?fireauth.AuthEventManager.Result} popupRedirectResult The result\n *     to resolve with when no error supplied.\n * @param {?fireauth.AuthError} error When supplied, the promise will reject.\n * @param {?string=} opt_eventId The event ID.\n * @override\n */\nfireauth.Auth.prototype.resolvePendingPopupEvent =\n    function(mode, popupRedirectResult, error, opt_eventId) {\n  // Only handles popup events of type sign in and which match popup event ID.\n  if (mode != fireauth.AuthEvent.Type.SIGN_IN_VIA_POPUP ||\n      this.popupEventId_ != opt_eventId) {\n    return;\n  }\n  if (error && this.pendingPopupRejectPromise_) {\n    // Reject with error for supplied mode.\n    this.pendingPopupRejectPromise_(error);\n  } else if (popupRedirectResult &&\n             !error &&\n             this.pendingPopupResolvePromise_) {\n    // Resolve with result for supplied mode.\n    this.pendingPopupResolvePromise_(popupRedirectResult);\n  }\n  // Now that event is resolved, delete popup timeout promise.\n  if (this.popupTimeoutPromise_) {\n    this.popupTimeoutPromise_.cancel();\n    this.popupTimeoutPromise_ = null;\n  }\n  // Delete pending promises.\n  delete this.pendingPopupResolvePromise_;\n  delete this.pendingPopupRejectPromise_;\n};\n\n\n/**\n * Returns the handler's appropriate popup and redirect sign in operation\n * finisher.\n * @param {!fireauth.AuthEvent.Type} mode The Auth type mode.\n * @param {?string=} opt_eventId The optional event ID.\n * @return {?function(string, string, ?string,\n *     ?string=):!goog.Promise<!fireauth.AuthEventManager.Result>}\n * @override\n */\nfireauth.Auth.prototype.getAuthEventHandlerFinisher =\n    function(mode, opt_eventId) {\n  // Sign in events will be completed by finishPopupAndRedirectSignIn.\n  if (mode == fireauth.AuthEvent.Type.SIGN_IN_VIA_REDIRECT) {\n    return goog.bind(this.finishPopupAndRedirectSignIn, this);\n  } else if (mode == fireauth.AuthEvent.Type.SIGN_IN_VIA_POPUP &&\n             this.popupEventId_ == opt_eventId &&\n             this.pendingPopupResolvePromise_) {\n    return goog.bind(this.finishPopupAndRedirectSignIn, this);\n  }\n  return null;\n};\n\n\n/**\n * Finishes the popup and redirect sign in operations.\n * @param {string} requestUri The callback url with the oauth response.\n * @param {string} sessionId The session id used to generate the authUri.\n * @param {?string} tenantId The tenant ID.\n * @param {?string=} opt_postBody The optional POST body content.\n * @return {!goog.Promise<!fireauth.AuthEventManager.Result>}\n */\nfireauth.Auth.prototype.finishPopupAndRedirectSignIn =\n    function(requestUri, sessionId, tenantId, opt_postBody) {\n  var self = this;\n  // Verify assertion request.\n  var request = {\n    'requestUri': requestUri,\n    'postBody': opt_postBody,\n    'sessionId': sessionId,\n    // Even if tenant ID is null, still pass it to RPC handler explicitly so\n    // that it won't be overridden by RPC handler's tenant ID.\n    'tenantId': tenantId\n  };\n  // Now that popup has responded, delete popup timeout promise.\n  if (this.popupTimeoutPromise_) {\n    this.popupTimeoutPromise_.cancel();\n    this.popupTimeoutPromise_ = null;\n  }\n  // When state is ready, run verify assertion request.\n  // This will only run either after initial and redirect state is ready for\n  // popups or after initial state is ready for redirect resolution.\n  return self.authStateLoaded_.then(function() {\n    return self.signInWithIdTokenProvider_(\n        self.rpcHandler_.verifyAssertion(request));\n  });\n};\n\n\n/**\n * @return {string} The generated event ID used to identify a popup event.\n * @private\n */\nfireauth.Auth.prototype.generateEventId_ = function() {\n  return fireauth.util.generateEventId();\n};\n\n\n/**\n * Signs in to Auth provider via popup.\n * @param {!fireauth.AuthProvider} provider The Auth provider to sign in with.\n * @return {!goog.Promise<!fireauth.AuthEventManager.Result>}\n */\nfireauth.Auth.prototype.signInWithPopup = function(provider) {\n  // Check if popup and redirect are supported in this environment.\n  if (!fireauth.util.isPopupRedirectSupported()) {\n    return goog.Promise.reject(new fireauth.AuthError(\n        fireauth.authenum.Error.OPERATION_NOT_SUPPORTED));\n  }\n  var mode = fireauth.AuthEvent.Type.SIGN_IN_VIA_POPUP;\n  var self = this;\n  // Popup the window immediately to make sure the browser associates the\n  // popup with the click that triggered it.\n\n  // Get provider settings.\n  var settings = fireauth.idp.getIdpSettings(provider['providerId']);\n  // There could multiple sign in with popup events in different windows.\n  // We need to match the correct popup to the correct pending promise.\n  var eventId = this.generateEventId_();\n  // If incapable of redirecting popup from opener, popup destination URL\n  // directly. This could also happen in a sandboxed iframe.\n  var oauthHelperWidgetUrl = null;\n  if ((!fireauth.util.runsInBackground() || fireauth.util.isIframe()) &&\n      this.app_().options['authDomain'] &&\n      provider['isOAuthProvider']) {\n    oauthHelperWidgetUrl =\n        fireauth.iframeclient.IfcHandler.getOAuthHelperWidgetUrl(\n            this.app_().options['authDomain'],\n            this.app_().options['apiKey'],\n            this.app_().name,\n            mode,\n            provider,\n            null,\n            eventId,\n            firebase.SDK_VERSION || null,\n            null,\n            null,\n            this.getTenantId(),\n            this.emulatorConfig_);\n  }\n  // The popup must have a name, otherwise when successive popups are triggered\n  // they will all render in the same instance and none will succeed since the\n  // popup cancel of first window will close the shared popup window instance.\n  var popupWin =\n      fireauth.util.popup(\n          oauthHelperWidgetUrl,\n          fireauth.util.generateRandomString(),\n          settings && settings.popupWidth,\n          settings && settings.popupHeight);\n  // Auth event manager must be available for popup sign in to be possible.\n  var p = this.getAuthEventManager_().then(function(manager) {\n    // Process popup request tagging it with newly created event ID.\n    return manager.processPopup(\n        popupWin, mode, provider, eventId, !!oauthHelperWidgetUrl,\n        self.getTenantId());\n  }).then(function() {\n    return new goog.Promise(function(resolve, reject) {\n      // Expire other pending promises if still available..\n      self.resolvePendingPopupEvent(\n          mode,\n          null,\n          new fireauth.AuthError(fireauth.authenum.Error.EXPIRED_POPUP_REQUEST),\n          // Existing pending popup event ID.\n          self.popupEventId_);\n      // Save current pending promises.\n      self.pendingPopupResolvePromise_ = resolve;\n      self.pendingPopupRejectPromise_ = reject;\n      // Overwrite popup event ID with new one corresponding to popup.\n      self.popupEventId_ = eventId;\n      // Keep track of timeout promise to cancel it on promise resolution before\n      // it times out.\n      self.popupTimeoutPromise_ =\n          self.authEventManager_.startPopupTimeout(\n              self, mode, /** @type {!Window} */ (popupWin), eventId);\n    });\n  }).then(function(result) {\n    // On resolution, close popup if still opened and pass result through.\n    if (popupWin) {\n      fireauth.util.closeWindow(popupWin);\n    }\n    if (result) {\n      return fireauth.object.makeReadonlyCopy(result);\n    }\n    return null;\n  }).thenCatch(function(error) {\n    if (popupWin) {\n      fireauth.util.closeWindow(popupWin);\n    }\n    throw error;\n  });\n  return /** @type {!goog.Promise<!fireauth.AuthEventManager.Result>} */ (\n      this.registerPendingPromise_(p));\n};\n\n\n/**\n * Signs in to Auth provider via redirect.\n * @param {!fireauth.AuthProvider} provider The Auth provider to sign in with.\n * @return {!goog.Promise<void>}\n */\nfireauth.Auth.prototype.signInWithRedirect = function(provider) {\n  // Check if popup and redirect are supported in this environment.\n  if (!fireauth.util.isPopupRedirectSupported()) {\n    return goog.Promise.reject(new fireauth.AuthError(\n        fireauth.authenum.Error.OPERATION_NOT_SUPPORTED));\n  }\n  var self = this;\n  var mode = fireauth.AuthEvent.Type.SIGN_IN_VIA_REDIRECT;\n  // Auth event manager must be available for sign in via redirect to be\n  // possible.\n  var p = this.getAuthEventManager_().then(function(manager) {\n    // Remember current persistence to apply it on the next page.\n    // This is the only time the state is passed to the next page (when user is\n    // not already logged in).\n    // This is not needed for link and reauthenticate as the user is already\n    // stored with specified persistence.\n    return self.userStorageManager_.savePersistenceForRedirect();\n  }).then(function() {\n    // Process redirect operation.\n    return self.authEventManager_.processRedirect(\n        mode, provider, undefined, self.getTenantId());\n  });\n  return /** @type {!goog.Promise<void>} */ (this.registerPendingPromise_(p));\n};\n\n\n/**\n * Returns the redirect result. If coming back from a successful redirect sign\n * in, will resolve to the signed in user. If coming back from an unsuccessful\n * redirect sign, will reject with the proper error. If no redirect operation\n * called, resolves with null.\n * @return {!goog.Promise<!fireauth.AuthEventManager.Result>}\n * @private\n */\nfireauth.Auth.prototype.getRedirectResultWithoutClearing_ = function() {\n  // Check if popup and redirect are supported in this environment.\n  if (!fireauth.util.isPopupRedirectSupported()) {\n    return goog.Promise.reject(new fireauth.AuthError(\n        fireauth.authenum.Error.OPERATION_NOT_SUPPORTED));\n  }\n  var self = this;\n  // Auth event manager must be available for get redirect result to be\n  // possible.\n  var p = this.getAuthEventManager_().then(function(manager) {\n    // Return redirect result when resolved.\n    return self.authEventManager_.getRedirectResult();\n  }).then(function(result) {\n    if (result) {\n      return fireauth.object.makeReadonlyCopy(result);\n    }\n    return null;\n  });\n  return /** @type {!goog.Promise<!fireauth.AuthEventManager.Result>} */ (\n      this.registerPendingPromise_(p));\n};\n\n\n/**\n * In addition to returning the redirect result as in\n * `getRedirectResultWithoutClearing_`, this will also clear the cached\n * redirect result for security reasons.\n * @return {!goog.Promise<!fireauth.AuthEventManager.Result>}\n */\nfireauth.Auth.prototype.getRedirectResult = function() {\n  return this.getRedirectResultWithoutClearing_()\n        .then((result) => {\n          if (this.authEventManager_) {\n            this.authEventManager_.clearRedirectResult();\n          }\n          return result;\n        })\n        .thenCatch((error) => {\n          if (this.authEventManager_) {\n            this.authEventManager_.clearRedirectResult();\n          }\n          throw error;\n        });\n};\n\n\n/**\n * Asynchronously sets the provided user as currentUser on the current Auth\n * instance.\n * @param {?fireauth.AuthUser} user The user to be copied to Auth instance.\n * @return {!goog.Promise<void>}\n */\nfireauth.Auth.prototype.updateCurrentUser = function(user) {\n  if (!user) {\n    return goog.Promise.reject(new fireauth.AuthError(\n        fireauth.authenum.Error.NULL_USER));\n  }\n  if (this.tenantId_ != user['tenantId']) {\n    return goog.Promise.reject(new fireauth.AuthError(\n        fireauth.authenum.Error.TENANT_ID_MISMATCH));\n  }\n  var self = this;\n  var options = {};\n  options['apiKey'] = this.app_().options['apiKey'];\n  options['authDomain'] = this.app_().options['authDomain'];\n  options['appName'] = this.app_().name;\n  var newUser = fireauth.AuthUser.copyUser(user, options,\n      self.redirectUserStorageManager_, self.getFramework());\n  return this.registerPendingPromise_(\n      this.redirectStateIsReady_.then(function() {\n        if (self.app_().options['apiKey'] != user.getApiKey()) {\n          // Throws auth/invalid-user-token if user doesn't belong to app.\n          // Throws auth/user-token-expired if token expires.\n          return newUser.reload();\n        }\n      }).then(function() {\n        if (self.currentUser_() && user['uid'] == self.currentUser_()['uid']) {\n          // Same user signed in. Update user data and notify Auth listeners.\n          // No need to resubscribe to user events.\n          // TODO: Check if the user to copy is older than current user and skip\n          // the copy logic in that case.\n          self.currentUser_().copy(user);\n          return self.handleUserStateChange_(user);\n        }\n        self.setCurrentUser_(newUser);\n        // Enable popup and redirect events.\n        newUser.enablePopupRedirect();\n        // Save user changes.\n        return self.handleUserStateChange_(newUser);\n      }).then(function(user) {\n        self.notifyAuthListeners_();\n      }));\n};\n\n\n/**\n * Completes the headless sign in with the server response containing the STS\n * access and refresh tokens, and sets the Auth user as current user while\n * setting all listeners to it and saving it to storage.\n * @param {!Object<string, string>} idTokenResponse The ID token response from\n *     the server.\n * @return {!goog.Promise<void>}\n */\nfireauth.Auth.prototype.signInWithIdTokenResponse =\n    function(idTokenResponse) {\n  var self = this;\n  var options = {};\n  options['apiKey'] = self.app_().options['apiKey'];\n  options['authDomain'] = self.app_().options['authDomain'];\n  options['appName'] = self.app_().name;\n  if (self.emulatorConfig_) {\n      options['emulatorConfig'] = self.emulatorConfig_;\n  }\n  // Wait for state to be ready.\n  // This is used internally and is also used for redirect sign in so there is\n  // no need for waiting for redirect result to resolve since redirect result\n  // depends on it.\n  return this.authStateLoaded_.then(function() {\n    // Initialize an Auth user using the provided ID token response.\n    return fireauth.AuthUser.initializeFromIdTokenResponse(\n        options,\n        idTokenResponse,\n        /** @type {!fireauth.storage.RedirectUserManager} */ (\n            self.redirectUserStorageManager_),\n        // Pass frameworks so they are logged in getAccountInfo while populating\n        // the user info.\n        self.getFramework());\n  }).then(function(user) {\n    // Check if the same user is already signed in.\n    if (self.currentUser_() &&\n        user['uid'] == self.currentUser_()['uid']) {\n      // Same user signed in. Update user data and notify Auth listeners.\n      // No need to resubscribe to user events.\n      self.currentUser_().copy(user);\n      return self.handleUserStateChange_(user);\n    }\n    // New user.\n    // Set current user and attach all listeners to it.\n    self.setCurrentUser_(user);\n    // Enable popup and redirect events.\n    user.enablePopupRedirect();\n    // Save user changes.\n    return self.handleUserStateChange_(user);\n  }).then(function() {\n    // Notify external Auth listeners only when state is ready.\n    self.notifyAuthListeners_();\n  });\n};\n\n\n/**\n * Updates the current auth user and attaches event listeners to changes on it.\n * Also removes all event listeners from previously signed in user.\n * @param {?fireauth.AuthUser} user The current user instance.\n * @private\n */\nfireauth.Auth.prototype.setCurrentUser_ = function(user) {\n  // Must be called first before updating currentUser reference.\n  this.attachEventListeners_(user);\n  // Update currentUser property.\n  fireauth.object.setReadonlyProperty(this, 'currentUser', user);\n  if (user) {\n    // If a user is available, set the language code on it and set current Auth\n    // instance as language code change dispatcher.\n    this.setUserLanguage_(user);\n    // Set the current frameworks used on the user and set current Auth instance\n    // as the framework change dispatcher.\n    this.setUserFramework_(user);\n    // If a user is available, set the emulator config on it and set current\n    // Auth instance as emulator config change dispatcher.\n    this.setUserEmulatorConfig_(user);\n  }\n};\n\n\n/**\n * Signs out the current user while deleting the Auth user from storage and\n * removing all listeners from it.\n * @return {!goog.Promise<void>}\n */\nfireauth.Auth.prototype.signOut = function() {\n  var self = this;\n  // Wait for final state to be ready first, otherwise a signed out user could\n  // come back to life.\n  var p = this.redirectStateIsReady_.then(function() {\n    // Clear any cached redirect result on sign out, even if user is already\n    // signed out. For example, sign in could fail due to account conflict\n    // error, the error in redirect result should still be cleared. There is\n    // also the use case where you keep a reference to a signed out user and\n    // call signedOutUser.linkWithRedirect(provider). Even though the user is\n    // signed out, getRedirectResult() will resolve with the modified signed\n    // out user. This could also throw an error\n    // (provider already linked, etc).\n    if (self.authEventManager_) {\n      self.authEventManager_.clearRedirectResult();\n    }\n    // Ignore if already signed out.\n    if (!self.currentUser_()) {\n      return goog.Promise.resolve();\n    }\n    // Detach all event listeners.\n    // Set current user to null.\n    self.setCurrentUser_(null);\n    // Remove current user from storage\n    return /** @type {!fireauth.storage.UserManager} */ (\n        self.userStorageManager_).removeCurrentUser()\n        .then(function() {\n          // Notify external Auth listeners of this Auth change event.\n          self.notifyAuthListeners_();\n        });\n  });\n  return /** @type {!goog.Promise<void>} */ (this.registerPendingPromise_(p));\n};\n\n\n/**\n * @return {!goog.Promise} A promise that resolved when any stored redirect user\n *     is loaded and removed from session storage and then stored locally.\n * @private\n */\nfireauth.Auth.prototype.initRedirectUser_ = function() {\n  var self = this;\n  var authDomain = this.app_().options['authDomain'];\n  // Get any saved redirect user and delete from session storage.\n  // Override user's authDomain with app's authDomain if there is a mismatch.\n  var p = /** @type {!fireauth.storage.RedirectUserManager} */ (\n      this.redirectUserStorageManager_).getRedirectUser(authDomain)\n          .then(function(user) {\n            // Save redirect user.\n            self.redirectUser_ = user;\n            if (user) {\n              // Set redirect storage manager on user.\n              user.setRedirectStorageManager(\n                  /** @type {!fireauth.storage.RedirectUserManager} */ (\n                      self.redirectUserStorageManager_));\n            }\n            // Delete redirect user.\n            return /** @type {!fireauth.storage.RedirectUserManager} */ (\n                self.redirectUserStorageManager_).removeRedirectUser();\n          });\n  return /** @type {!goog.Promise<undefined>} */ (\n      this.registerPendingPromise_(p));\n};\n\n\n/**\n * Loads the initial Auth state for current application from web storage and\n * initializes Auth user accordingly to reflect that state. This routine does\n * not wait for any pending redirect result to be resolved.\n * @return {!goog.Promise<undefined>} Promise that resolves when state is ready,\n *     loaded from storage.\n * @private\n */\nfireauth.Auth.prototype.initAuthState_ = function() {\n  // Load current user from storage.\n  var self = this;\n  var authDomain = this.app_().options['authDomain'];\n  // Get any saved redirected user first.\n  var p = this.initRedirectUser_().then(function() {\n    // Override user's authDomain with app's authDomain if there is a mismatch.\n    return /** @type {!fireauth.storage.UserManager} */ (\n      self.userStorageManager_).getCurrentUser(authDomain, self.emulatorConfig_);\n  }).then(function(user) {\n    // Logged in user.\n    if (user) {\n      // Set redirect storage manager on user.\n      user.setRedirectStorageManager(\n          /** @type {!fireauth.storage.RedirectUserManager} */ (\n              self.redirectUserStorageManager_));\n      // If the current user is undergoing a redirect operation, do not reload\n      // as that could could potentially delete the user if the token is\n      // expired. Instead any token problems will be detected via the\n      // verifyAssertion flow or the remaining flow. This is critical for\n      // reauthenticateWithRedirect as this flow is potentially used to recover\n      // from a token expiration error.\n      if (self.redirectUser_ &&\n          self.redirectUser_.getRedirectEventId() ==\n          user.getRedirectEventId()) {\n        return user;\n      }\n      // Confirm user valid first before setting listeners.\n      return user.reload().then(function() {\n        // Force user saving after reload as state change listeners not\n        // subscribed yet below via setCurrentUser_. Changes may have happened\n        // externally such as email actions or changes on another device.\n        return self.userStorageManager_.setCurrentUser(user).then(function() {\n          return user;\n        });\n      }).thenCatch(function(error) {\n        if (error['code'] == 'auth/network-request-failed') {\n          // Do not delete the user from storage if connection is lost or app is\n          // offline.\n          return user;\n        }\n        // Invalid user, could be deleted, remove from storage and resolve with\n        // null.\n        return /** @type {!fireauth.storage.UserManager} */(\n            self.userStorageManager_).removeCurrentUser();\n      });\n    }\n    // No logged in user, resolve with null;\n    return null;\n  }).then(function(user) {\n    // Even though state not ready yet pending any redirect result.\n    // Current user needs to be available for link with redirect to complete.\n    // This will also set listener on the user changes in case state changes\n    // occur they would get updated in storage too.\n    self.setCurrentUser_(user || null);\n  });\n  // In case the app is deleted before it is initialized with state from\n  // storage.\n  return /** @type {!goog.Promise<undefined>} */ (\n      this.registerPendingPromise_(p));\n};\n\n\n/**\n * After initial Auth state is loaded, waits for any pending redirect result,\n * resolves it and then adds the external Auth state change listeners and\n * triggers first state of all observers.\n * @return {!goog.Promise<undefined>} Promise that resolves when state is ready\n *     taking into account any pending redirect result.\n * @private\n */\nfireauth.Auth.prototype.initAuthRedirectState_ = function() {\n  var self = this;\n  // Wait first for state to be loaded from storage.\n  return this.authStateLoaded_.then(function() {\n    // Resolve any pending redirect result.\n    return self.getRedirectResultWithoutClearing_();\n  }).thenCatch(function(error) {\n    // Ignore any error in the process. Redirect could be not supported.\n    return;\n  }).then(function() {\n    // Make sure instance was not deleted before proceeding.\n    if (self.deleted_) {\n      return;\n    }\n    // Between init Auth state and get redirect result resolution there\n    // could have been a sign in attempt in another window.\n    // Force sync and then add listener to run sync on change below.\n    return self.getSyncAuthUserChanges_();\n  }).thenCatch(function(error) {\n    // Ignore any error in the process.\n    return;\n  }).then(function() {\n    // Now that final state is ready, make sure instance was not deleted before\n    // proceeding.\n    if (self.deleted_) {\n      return;\n    }\n    // Initial state has been resolved.\n    self.isStateResolved_ = true;\n    // Add user state change listener so changes are synchronized with\n    // other windows and tabs.\n    /** @type {!fireauth.storage.UserManager} */ (self.userStorageManager_\n        ).addCurrentUserChangeListener(self.getSyncAuthUserChanges_);\n  });\n};\n\n\n/**\n * Synchronizes current Auth to stored auth state, used when external state\n * changes occur.\n * @return {!goog.Promise<void>}\n * @private\n */\nfireauth.Auth.prototype.syncAuthUserChanges_ = function() {\n  // Get Auth user state from storage and compare to current state.\n  // Safe to run when no external change is detected.\n  var self = this;\n  var authDomain = this.app_().options['authDomain'];\n  // Override user's authDomain with app's authDomain if there is a mismatch.\n  return /** @type {!fireauth.storage.UserManager} */ (\n      this.userStorageManager_).getCurrentUser(authDomain)\n      .then(function(user) {\n        // In case this was deleted.\n        if (self.deleted_) {\n          return;\n        }\n        // Since the authDomain could be modified here, saving to storage here\n        // could trigger an infinite loop of changes between this tab and\n        // another tab using different Auth domain but since sync Auth user\n        // changes does not save changes to storage, except for getToken below\n        // if the token needs refreshing but will stop triggering the first time\n        // the token is refreshed on one of the first tab that refreshes it.\n        // The latter should not happen anyway since getToken should be valid\n        // at all times since anything that triggers the storage change should\n        // have communicated with the backend and that requires a valid token.\n        // In addition, authDomain difference is an edge case to begin with.\n\n        // If the same user is to be synchronized.\n        if (self.currentUser_() &&\n            user &&\n            self.currentUser_().hasSameUserIdAs(user)) {\n          // Data update, simply copy data changes.\n          self.currentUser_().copy(user);\n          // If tokens changed from previous user tokens, this will trigger\n          // notifyAuthListeners_.\n          return self.currentUser_().getIdToken();\n        } else if (!self.currentUser_() && !user) {\n          // No change, do nothing (was signed out and remained signed out).\n          return;\n        } else {\n          // Update current Auth state. Either a new login or logout.\n          self.setCurrentUser_(user);\n          // If a new user is signed in, enabled popup and redirect on that\n          // user.\n          if (user) {\n            user.enablePopupRedirect();\n            // Set redirect storage manager on user.\n            user.setRedirectStorageManager(\n                /** @type {!fireauth.storage.RedirectUserManager} */ (\n                    self.redirectUserStorageManager_));\n          }\n          if (self.authEventManager_) {\n            self.authEventManager_.subscribe(self);\n          }\n          // Notify external Auth changes of Auth change event.\n          self.notifyAuthListeners_();\n        }\n      });\n};\n\n\n/**\n * Updates the language code on the provided user and configures the user to\n * listen to the Auth instance for any language code changes.\n * @param {!fireauth.AuthUser} user The user to whose language needs to be set.\n * @private\n */\nfireauth.Auth.prototype.setUserLanguage_ = function(user) {\n  // Sets the current language code on the user.\n  user.setLanguageCode(this.getLanguageCode());\n  // Sets current Auth instance as language code change dispatcher on the user.\n  user.setLanguageCodeChangeDispatcher(this);\n};\n\n\n/**\n * Updates the emulator config on the provided user and configures the user\n *   to listen to the Auth instance for any emulator config changes.\n * @param {!fireauth.AuthUser} user The user to whose emulator config needs\n *   to be set.\n * @private\n */\nfireauth.Auth.prototype.setUserEmulatorConfig_ = function(user) {\n  // Sets the current emulator config on the user.\n  user.setEmulatorConfig(this.emulatorConfig_);\n  // Sets current Auth instance as emulator config change dispatcher on the\n  // user.\n  user.setEmulatorConfigChangeDispatcher(this);\n}\n\n\n/**\n * Handles user state changes.\n * @param {!fireauth.AuthUser} user The user which triggered the state changes.\n * @return {!goog.Promise} The promise that resolves when state changes are\n *     handled.\n * @private\n */\nfireauth.Auth.prototype.handleUserStateChange_ = function(user) {\n  // Save Auth user state changes.\n  return /** @type {!fireauth.storage.UserManager} */ (\n      this.userStorageManager_).setCurrentUser(user);\n};\n\n\n/**\n * Handles user token changes.\n * @param {!Object} event The token change event.\n * @private\n */\nfireauth.Auth.prototype.handleUserTokenChange_ = function(event) {\n  // This is only called when user is ready and Auth state has been resolved.\n  // Notify external Auth change listeners.\n  this.notifyAuthListeners_();\n  // Save user token changes.\n  this.handleUserStateChange_(/** @type {!fireauth.AuthUser} */ (\n      this.currentUser_()));\n};\n\n\n/**\n * Handles user deletion events.\n * @param {!Object} event The user delete event.\n * @private\n */\nfireauth.Auth.prototype.handleUserDelete_ = function(event) {\n  // A deleted user will be treated like a sign out event.\n  this.signOut();\n};\n\n\n/**\n * Handles user invalidation events.\n * @param {!Object} event The user invalidation event.\n * @private\n */\nfireauth.Auth.prototype.handleUserInvalidated_ = function(event) {\n  // An invalidated user will be treated like a sign out event.\n  this.signOut();\n};\n\n\n/**\n * Detaches all previous listeners on current user and reattach new listeners to\n * provided user if not null.\n * @param {?fireauth.AuthUser} user The user to attach event listeners to.\n * @private\n */\nfireauth.Auth.prototype.attachEventListeners_ = function(user) {\n  // Remove existing event listeners from previous current user if available.\n  if (this.currentUser_()) {\n    this.currentUser_().removeStateChangeListener(\n        this.userStateChangeListener_);\n    goog.events.unlisten(\n        this.currentUser_(),\n        fireauth.UserEventType.TOKEN_CHANGED,\n        this.userTokenChangeListener_);\n    goog.events.unlisten(\n        this.currentUser_(),\n        fireauth.UserEventType.USER_DELETED,\n        this.userDeleteListener_);\n    goog.events.unlisten(\n        this.currentUser_(),\n        fireauth.UserEventType.USER_INVALIDATED,\n        this.userInvalidatedListener_);\n    // Stop proactive token refresh on the current user.\n    this.currentUser_().stopProactiveRefresh();\n  }\n  // If a new user is provided, attach event listeners to state, token, user\n  // invalidation and delete events.\n  if (user) {\n    user.addStateChangeListener(this.userStateChangeListener_);\n    goog.events.listen(\n        user,\n        fireauth.UserEventType.TOKEN_CHANGED,\n        this.userTokenChangeListener_);\n    goog.events.listen(\n        user,\n        fireauth.UserEventType.USER_DELETED,\n        this.userDeleteListener_);\n     goog.events.listen(\n        user,\n        fireauth.UserEventType.USER_INVALIDATED,\n        this.userInvalidatedListener_);\n    // Start proactive token refresh on new user if there is at least one\n    // Firebase service subscribed to Auth changes.\n    if (this.firebaseServices_ > 0) {\n      user.startProactiveRefresh();\n    }\n  }\n};\n\n\n/**\n * Signs in with ID token promise provider.\n * @param {!goog.Promise<!Object>} idTokenPromise\n *     The rpc handler method that returns a promise which resolves with an ID\n *     token.\n * @return {!goog.Promise<!fireauth.AuthEventManager.Result>}\n * @private\n */\nfireauth.Auth.prototype.signInWithIdTokenProvider_ = function(idTokenPromise) {\n  var self = this;\n  var credential = null;\n  var additionalUserInfo = null;\n  return /** @type {!goog.Promise<!fireauth.AuthEventManager.Result>} */ (\n      this.registerPendingPromise_(\n          idTokenPromise\n          .then(function(idTokenResponse) {\n            // Get credential if available in the response.\n            credential = fireauth.AuthProvider.getCredentialFromResponse(\n                idTokenResponse);\n            // Get additional IdP data if available in the response.\n            additionalUserInfo = fireauth.AdditionalUserInfo.fromPlainObject(\n                idTokenResponse);\n            // When custom token is exchanged for idToken, continue sign in with\n            // ID token and return firebase Auth user.\n            return self.signInWithIdTokenResponse(idTokenResponse);\n          }, function(error) {\n            // Catch the MFA_REQUIRED error rejected in ID token promise and\n            // repackage it into a multi-factor error with additional IdP data\n            // if available.\n            var multiFactorError = null;\n            if (error && error['code'] === 'auth/multi-factor-auth-required') {\n              multiFactorError = fireauth.MultiFactorError.fromPlainObject(\n                  error.toPlainObject(),\n                  self,\n                  goog.bind(self.handleMultiFactorIdTokenResolver_, self));\n            }\n            throw multiFactorError || error;\n          })\n          .then(function() {\n            // Resolve promise with a readonly user credential object.\n            return fireauth.object.makeReadonlyCopy({\n              // Return the current user reference.\n              'user': self.currentUser_(),\n              // Return any credential passed from the backend.\n              'credential': credential,\n              // Return any additional IdP data passed from the backend.\n              'additionalUserInfo': additionalUserInfo,\n              // Sign in operation type.\n              'operationType': fireauth.constants.OperationType.SIGN_IN\n            });\n          })));\n};\n\n\n/**\n * Completes multi-factor sign-in with ID token response and additional IdP data\n * if available.\n * @param {{idToken: string, refreshToken: string}} response The successful\n *     sign-in response containing the new ID tokens.\n * @return {!goog.Promise<!fireauth.AuthEventManager.Result>} A Promise that\n *     resolves with the updated `UserCredential`.\n * @private\n */\nfireauth.Auth.prototype.handleMultiFactorIdTokenResolver_ =\n    function(response) {\n  var self = this;\n  // Wait for state to be ready and then finish sign-in.\n  return this.redirectStateIsReady_.then(function() {\n    return self.signInWithIdTokenProvider_(goog.Promise.resolve(response));\n  });\n};\n\n\n/**\n * Initializes the Auth state change observer returned by the\n * firebase.INTERNAL.createSubscribe helper.\n * @param {!firebase.Observer} observer The Auth state change observer.\n * @private\n */\nfireauth.Auth.prototype.initIdTokenChangeObserver_ = function(observer) {\n  var self = this;\n  // Adds a listener that will transmit the event everytime it's called.\n  this.addAuthTokenListener(function(accessToken) {\n    observer.next(self.currentUser_());\n  });\n};\n\n\n/**\n * Initializes the user state change observer returned by the\n * firebase.INTERNAL.createSubscribe helper.\n * @param {!firebase.Observer} observer The user state change observer.\n * @private\n */\nfireauth.Auth.prototype.initUserStateObserver_ = function(observer) {\n  var self = this;\n  // Adds a listener that will transmit the event everytime it's called.\n  this.addUserChangeListener_(function(accessToken) {\n    observer.next(self.currentUser_());\n  });\n};\n\n\n/**\n * Adds an observer for Auth state changes, we need to wrap the call as\n * the args checking code needs a method defined on the prototype this way,\n * not within the constructor, and we also have to implement the behavior\n * that will trigger an observer right away if state is ready.\n * @param {!firebase.Observer|function(?fireauth.AuthUser)}\n *     nextOrObserver An observer object or a function triggered on change.\n * @param {function(!fireauth.AuthError)=} opt_error Optional A function\n *     triggered on Auth error.\n * @param {function()=} opt_completed Optional A function triggered when the\n *     observer is removed.\n * @return {!function()} The unsubscribe function for the observer.\n */\nfireauth.Auth.prototype.onIdTokenChanged = function(\n    nextOrObserver, opt_error, opt_completed) {\n  var self = this;\n  // State already determined. Trigger immediately, otherwise initState will\n  // take care of notifying all pending listeners on initialization.\n  // In this case we do not trigger synchronously and trigger via a resolved\n  // promise as required by specs.\n  if (this.isStateResolved_) {\n    // The observer cannot be called synchronously. We're using the\n    // native Promise implementation as otherwise it creates weird behavior\n    // where the order of promises resolution would not be as expected.\n    // It is due to the fact fireauth and firebase.app use their own\n    // and different promises library and this leads to calls resolutions order\n    // being different from the promises registration order.\n    Promise.resolve().then(function() {\n      if (typeof nextOrObserver === 'function') {\n        nextOrObserver(self.currentUser_());\n      } else if (typeof nextOrObserver['next'] === 'function') {\n        nextOrObserver['next'](self.currentUser_());\n      }\n    });\n  }\n  return this.onIdTokenChanged_(\n      /** @type {!firebase.Observer|function(*)|undefined} */ (nextOrObserver),\n      /** @type {function(!Error)|undefined} */ (opt_error),\n      opt_completed);\n};\n\n\n/**\n * Adds an observer for user state changes, we need to wrap the call as\n * the args checking code needs a method defined on the prototype this way,\n * not within the constructor, and we also have to implement the behavior\n * that will trigger an observer right away if state is ready.\n * @param {!firebase.Observer|function(?fireauth.AuthUser)}\n *     nextOrObserver An observer object or a function triggered on change.\n * @param {function(!fireauth.AuthError)=} opt_error Optional A function\n *     triggered on Auth error.\n * @param {function()=} opt_completed Optional A function triggered when the\n *     observer is removed.\n * @return {!function()} The unsubscribe function for the observer.\n */\nfireauth.Auth.prototype.onAuthStateChanged = function(\n    nextOrObserver, opt_error, opt_completed) {\n  var self = this;\n  // State already determined. Trigger immediately, otherwise initState will\n  // take care of notifying all pending listeners on initialization.\n  // In this case we do not trigger synchronously and trigger via a resolved\n  // promise as required by specs.\n  if (this.isStateResolved_) {\n    // The observer cannot be called synchronously. We're using the\n    // native Promise implementation as otherwise it creates weird behavior\n    // where the order of promises resolution would not be as expected.\n    // It is due to the fact fireauth and firebase.app use their own\n    // and different promises library and this leads to calls resolutions order\n    // being different from the promises registration order.\n    Promise.resolve().then(function() {\n      // This ensures that the first time notifyAuthListeners_ is triggered,\n      // it has the correct UID before triggering the user state change\n      // listeners.\n      self.userStateChangeUid_ = self.getUid();\n      if (typeof nextOrObserver === 'function') {\n        nextOrObserver(self.currentUser_());\n      } else if (typeof nextOrObserver['next'] === 'function') {\n        nextOrObserver['next'](self.currentUser_());\n      }\n    });\n  }\n  return this.onUserStateChanged_(\n      /** @type {!firebase.Observer|function(*)|undefined} */ (nextOrObserver),\n      /** @type {function(!Error)|undefined} */ (opt_error),\n      opt_completed);\n};\n\n\n/**\n * Returns an STS token. If the cached one is unexpired it is directly returned.\n * Otherwise the existing ID token or refresh token is exchanged for a new one.\n * If there is no user signed in, returns null.\n *\n * This method is called getIdTokenInternal as the symbol getIdToken is not\n * obfuscated, which could lead to developers incorrectly calling\n * firebase.auth().getIdToken().\n *\n * @param {boolean=} opt_forceRefresh Whether to force refresh token exchange.\n * @return {!goog.Promise<?Object>}\n */\nfireauth.Auth.prototype.getIdTokenInternal = function(opt_forceRefresh) {\n  var self = this;\n  // Wait for state to be ready.\n  var p = this.redirectStateIsReady_.then(function() {\n    // Call user's underlying getIdToken method.\n    if (self.currentUser_()) {\n      return self.currentUser_().getIdToken(opt_forceRefresh)\n          .then(function(stsAccessToken) {\n            // This is used internally by other services which expect the access\n            // token to be returned in an object.\n            return {\n              'accessToken': stsAccessToken\n            };\n          });\n    }\n    // No logged in user, return null token.\n    return null;\n  });\n  return /** @type {!goog.Promise<?Object>} */ (\n      this.registerPendingPromise_(p));\n};\n\n\n/**\n * Signs in a user asynchronously using a custom token and returns any\n * additional user info data or credentials returned form the backend.\n * @param {string} token The custom token to sign in with.\n * @return {!goog.Promise<!fireauth.AuthEventManager.Result>}\n */\nfireauth.Auth.prototype.signInWithCustomToken = function(token) {\n  var self = this;\n  // Wait for the redirect state to be determined before proceeding. If critical\n  // errors like web storage unsupported are detected, fail before RPC, instead\n  // of after.\n  return this.redirectStateIsReady_.then(function() {\n    return self.signInWithIdTokenProvider_(\n        self.getRpcHandler().verifyCustomToken(token));\n  }).then(function(result) {\n    var user = result['user'];\n    // Manually sets the isAnonymous flag to false as the GetAccountInfo\n    // response will look like an anonymous user (no credentials visible).\n    user.updateProperty('isAnonymous', false);\n    // Save isAnonymous flag changes to current user in storage.\n    self.handleUserStateChange_(user);\n    return result;\n  });\n};\n\n\n/**\n * Sign in using an email and password and returns any additional user info\n * data or credentials returned form the backend.\n * @param {string} email The email to sign in with.\n * @param {string} password The password to sign in with.\n * @return {!goog.Promise<!fireauth.AuthEventManager.Result>}\n */\nfireauth.Auth.prototype.signInWithEmailAndPassword =\n    function(email, password) {\n  var self = this;\n  // Wait for the redirect state to be determined before proceeding. If critical\n  // errors like web storage unsupported are detected, fail before RPC, instead\n  // of after.\n  return this.redirectStateIsReady_.then(function() {\n    return self.signInWithIdTokenProvider_(\n        self.getRpcHandler().verifyPassword(email, password));\n  });\n};\n\n\n/**\n * Creates a new email and password account and returns any additional user\n * info data or credentials returned form the backend.\n * @param {string} email The email to sign up with.\n * @param {string} password The password to sign up with.\n * @return {!goog.Promise<!fireauth.AuthEventManager.Result>}\n */\nfireauth.Auth.prototype.createUserWithEmailAndPassword =\n    function(email, password) {\n  var self = this;\n  // Wait for the redirect state to be determined before proceeding. If critical\n  // errors like web storage unsupported are detected, fail before RPC, instead\n  // of after.\n  return this.redirectStateIsReady_.then(function() {\n    return self.signInWithIdTokenProvider_(\n        self.getRpcHandler().createAccount(email, password));\n  });\n};\n\n\n/**\n * Logs into Firebase with the given 3rd party credentials and returns any\n * additional user info data or credentials returned form the backend.\n * @param {!fireauth.AuthCredential} credential The Auth credential.\n * @return {!goog.Promise<!fireauth.AuthEventManager.Result>}\n */\nfireauth.Auth.prototype.signInWithCredential = function(credential) {\n  // Credential could be extended in the future, so leave it to credential to\n  // decide how to retrieve ID token.\n  var self = this;\n  // Wait for the redirect state to be determined before proceeding. If critical\n  // errors like web storage unsupported are detected, fail before RPC, instead\n  // of after.\n  return this.redirectStateIsReady_.then(function() {\n    // Return the full response object and not just the user.\n    return self.signInWithIdTokenProvider_(\n        credential.getIdTokenProvider(self.getRpcHandler()));\n  });\n};\n\n\n/**\n * Logs into Firebase with the given 3rd party credentials and returns any\n * additional user info data or credentials returned form the backend. It has\n * been deprecated in favor of signInWithCredential.\n * @param {!fireauth.AuthCredential} credential The Auth credential.\n * @return {!goog.Promise<!fireauth.AuthEventManager.Result>}\n */\nfireauth.Auth.prototype.signInAndRetrieveDataWithCredential =\n    function(credential) {\n  fireauth.deprecation.log(\n      fireauth.deprecation.Deprecations.SIGN_IN_WITH_CREDENTIAL);\n  return this.signInWithCredential(credential);\n};\n\n\n/**\n * Signs in a user anonymously and returns any additional user info data or\n * credentials returned form the backend.\n * @return {!goog.Promise<!fireauth.AuthEventManager.Result>}\n */\nfireauth.Auth.prototype.signInAnonymously = function() {\n  var self = this;\n  // Wait for the redirect state to be determined before proceeding. If critical\n  // errors like web storage unsupported are detected, fail before RPC, instead\n  // of after.\n  return this.redirectStateIsReady_.then(function() {\n    var user = self.currentUser_();\n    // If an anonymous user is already signed in, no need to sign him again.\n    if (user && user['isAnonymous']) {\n      var additionalUserInfo = fireauth.object.makeReadonlyCopy({\n        'providerId': null,\n        'isNewUser': false\n      });\n      return fireauth.object.makeReadonlyCopy({\n        // Return the signed in user reference.\n        'user': user,\n        // Do not return credential for anonymous user.\n        'credential': null,\n        // Return any additional IdP data.\n        'additionalUserInfo': additionalUserInfo,\n        // Sign in operation type.\n        'operationType': fireauth.constants.OperationType.SIGN_IN\n      });\n    } else {\n      // No anonymous user currently signed in.\n      return self.signInWithIdTokenProvider_(\n          self.getRpcHandler().signInAnonymously())\n          .then(function(result) {\n            var user = result['user'];\n            // Manually sets the isAnonymous flag to true as\n            // initializeFromIdTokenResponse uses the default value of false and\n            // even though getAccountInfo sets that to true, it will be reverted\n            // to false in reloadWithoutSaving.\n            // TODO: consider optimizing this and cleaning these manual\n            // overwrites.\n            user.updateProperty('isAnonymous', true);\n            // Save isAnonymous flag changes to current user in storage.\n            self.handleUserStateChange_(user);\n            return result;\n          });\n    }\n  });\n};\n\n\n/**\n * @return {string} The key used for storing Auth state.\n */\nfireauth.Auth.prototype.getStorageKey = function() {\n  return fireauth.util.createStorageKey(\n      this.app_().options['apiKey'],\n      this.app_().name);\n};\n\n\n/**\n * @return {!firebase.app.App} The Firebase App this auth object is connected\n *     to.\n * @private\n */\nfireauth.Auth.prototype.app_ = function() {\n  return this['app'];\n};\n\n\n/**\n * @return {!fireauth.AuthSettings} The settings for this Auth object.\n * @private\n */\nfireauth.Auth.prototype.settings_ = function () {\n  return this['settings'];\n};\n\n\n/**\n * @return {!fireauth.RpcHandler} The RPC handler.\n */\nfireauth.Auth.prototype.getRpcHandler = function() {\n  return this.rpcHandler_;\n};\n\n\n/**\n * @return {?fireauth.AuthUser} The currently logged in user.\n * @private\n */\nfireauth.Auth.prototype.currentUser_ = function() {\n  return this['currentUser'];\n};\n\n\n/** @return {?string} The current user UID if available, null if not. */\nfireauth.Auth.prototype.getUid = function() {\n  return (this.currentUser_() && this.currentUser_()['uid']) || null;\n};\n\n\n/**\n * @return {?string} The last cached access token.\n * @private\n */\nfireauth.Auth.prototype.getLastAccessToken_ = function() {\n  return (this.currentUser_() && this.currentUser_()['_lat']) || null;\n};\n\n\n\n/**\n * Called internally on Auth state change to notify listeners.\n * @private\n */\nfireauth.Auth.prototype.notifyAuthListeners_ = function() {\n  // Only run when state is resolved. After state is resolved, the Auth listener\n  // will always trigger.\n  if (this.isStateResolved_) {\n    for (var i = 0; i < this.authListeners_.length; i++) {\n      if (this.authListeners_[i]) {\n        this.authListeners_[i](this.getLastAccessToken_());\n      }\n    }\n    // Trigger user change only if UID changed.\n    if (this.userStateChangeUid_ !== this.getUid() &&\n        this.userChangeListeners_.length) {\n      // Update user state change UID.\n      this.userStateChangeUid_ = this.getUid();\n      // Trigger all subscribed user state change listeners.\n      for (var i = 0; i < this.userChangeListeners_.length; i++) {\n        if (this.userChangeListeners_[i]) {\n          this.userChangeListeners_[i](this.getLastAccessToken_());\n        }\n      }\n    }\n  }\n};\n\n\n/**\n * Attaches a listener function to Auth state change.\n * This is used only by internal Firebase services.\n * @param {!function(?string)} listener The auth state change listener.\n */\nfireauth.Auth.prototype.addAuthTokenListenerInternal = function(listener) {\n  this.addAuthTokenListener(listener);\n  // This is not exact science but should be good enough to detect Firebase\n  // services subscribing to Auth token changes.\n  // This is needed to start proactive token refresh on a user.\n  this.firebaseServices_++;\n  if (this.firebaseServices_ > 0 && this.currentUser_()) {\n    // Start proactive token refresh on the current user.\n    this.currentUser_().startProactiveRefresh();\n  }\n};\n\n\n/**\n * Detaches the provided listener from Auth state change event.\n * This is used only by internal Firebase services.\n * @param {!function(?string)} listener The Auth state change listener.\n */\nfireauth.Auth.prototype.removeAuthTokenListenerInternal = function(listener) {\n  // This is unlikely to be called by Firebase services. Services are unlikely\n  // to remove Auth token listeners.\n  // Make sure listener is still subscribed before decrementing.\n  var self = this;\n  goog.array.forEach(this.authListeners_, function(ele) {\n    // This covers the case where the same listener is subscribed more than\n    // once.\n    if (ele == listener) {\n      self.firebaseServices_--;\n    }\n  });\n  if (this.firebaseServices_ < 0) {\n    this.firebaseServices_ = 0;\n  }\n  if (this.firebaseServices_ == 0 && this.currentUser_()) {\n    // Stop proactive token refresh on the current user.\n    this.currentUser_().stopProactiveRefresh();\n  }\n  this.removeAuthTokenListener(listener);\n};\n\n\n/**\n * Attaches a listener function to Auth state change.\n * @param {!function(?string)} listener The Auth state change listener.\n */\nfireauth.Auth.prototype.addAuthTokenListener = function(listener) {\n  var self = this;\n  // Save listener.\n  this.authListeners_.push(listener);\n  // Make sure redirect state is ready and then trigger listener.\n  this.registerPendingPromise_(this.redirectStateIsReady_.then(function() {\n    // Do nothing if deleted.\n    if (self.deleted_) {\n      return;\n    }\n    // Make sure listener is still subscribed.\n    if (goog.array.contains(self.authListeners_, listener)) {\n      // Trigger the first call for this now that redirect state is resolved.\n      listener(self.getLastAccessToken_());\n    }\n  }));\n};\n\n\n/**\n * Detaches the provided listener from Auth state change event.\n * @param {!function(?string)} listener The Auth state change listener.\n */\nfireauth.Auth.prototype.removeAuthTokenListener = function(listener) {\n  // Remove from Auth listeners.\n  goog.array.removeAllIf(this.authListeners_, function(ele) {\n    return ele == listener;\n  });\n};\n\n\n/**\n * Attaches a listener function to user state change.\n * @param {!function(?string)} listener The user state change listener.\n * @private\n */\nfireauth.Auth.prototype.addUserChangeListener_ = function(listener) {\n  var self = this;\n  // Save listener.\n  this.userChangeListeners_.push(listener);\n  // Make sure redirect state is ready and then trigger listener.\n  this.registerPendingPromise_(this.redirectStateIsReady_.then(function() {\n    // Do nothing if deleted.\n    if (self.deleted_) {\n      return;\n    }\n    // Make sure listener is still subscribed.\n    if (goog.array.contains(self.userChangeListeners_, listener)) {\n      // Confirm UID change before triggering.\n      if (self.userStateChangeUid_ !== self.getUid()) {\n        self.userStateChangeUid_ = self.getUid();\n        // Trigger the first call for this now that redirect state is resolved.\n        listener(self.getLastAccessToken_());\n      }\n    }\n  }));\n};\n\n\n/**\n * Deletes the Auth instance, handling cancellation of all pending async Auth\n * operations.\n * @return {!Promise<void>}\n */\nfireauth.Auth.prototype.delete = function() {\n  this.deleted_ = true;\n  // Cancel all pending promises.\n  for (var i = 0; i < this.pendingPromises_.length; i++) {\n    this.pendingPromises_[i].cancel(fireauth.authenum.Error.MODULE_DESTROYED);\n  }\n\n  // Empty pending promises array.\n  this.pendingPromises_ = [];\n  // Remove current user change listener.\n  if (this.userStorageManager_) {\n    this.userStorageManager_.removeCurrentUserChangeListener(\n        this.getSyncAuthUserChanges_);\n  }\n  // Unsubscribe from Auth event handling.\n  if (this.authEventManager_) {\n    this.authEventManager_.unsubscribe(this);\n    this.authEventManager_.clearRedirectResult();\n  }\n  return Promise.resolve();\n};\n\n\n/** @return {boolean} Whether Auth instance has pending promises. */\nfireauth.Auth.prototype.hasPendingPromises = function() {\n  return this.pendingPromises_.length != 0;\n};\n\n\n/**\n * Takes in a pending promise, saves it and adds a clean up callback which\n * forgets the pending promise after it is fulfilled and echoes the promise\n * back.\n * @param {!goog.Promise<*, *>|!goog.Promise<void>} p The pending promise.\n * @return {!goog.Promise<*, *>|!goog.Promise<void>}\n * @private\n */\nfireauth.Auth.prototype.registerPendingPromise_ = function(p) {\n  var self = this;\n  // Save created promise in pending list.\n  this.pendingPromises_.push(p);\n  p.thenAlways(function() {\n    // When fulfilled, remove from pending list.\n    goog.array.remove(self.pendingPromises_, p);\n  });\n  // Return the promise.\n  return p;\n};\n\n\n/**\n * Gets the list of possible sign in methods for the given email address.\n * @param {string} email The email address.\n * @return {!goog.Promise<!Array<string>>}\n */\nfireauth.Auth.prototype.fetchSignInMethodsForEmail = function(email) {\n  return /** @type {!goog.Promise<!Array<string>>} */ (\n      this.registerPendingPromise_(\n          this.getRpcHandler().fetchSignInMethodsForIdentifier(email)));\n};\n\n\n/**\n * @param {string} emailLink The email link.\n * @return {boolean} Whether the link is a sign in with email link.\n */\nfireauth.Auth.prototype.isSignInWithEmailLink = function(emailLink) {\n  return  !!fireauth.EmailAuthProvider\n      .getActionCodeUrlFromSignInEmailLink(emailLink);\n};\n\n\n/**\n * Sends the sign-in with email link for the email account provided.\n * @param {string} email The email account to sign in with.\n * @param {!Object} actionCodeSettings The action code settings object.\n * @return {!goog.Promise<void>}\n */\nfireauth.Auth.prototype.sendSignInLinkToEmail = function(\n    email, actionCodeSettings) {\n  var self = this;\n  return this.registerPendingPromise_(\n      // Wrap in promise as ActionCodeSettings constructor could throw a\n      // synchronous error if invalid arguments are specified.\n      goog.Promise.resolve()\n          .then(function() {\n            var actionCodeSettingsBuilder =\n                new fireauth.ActionCodeSettings(actionCodeSettings);\n            if (!actionCodeSettingsBuilder.canHandleCodeInApp()) {\n              throw new fireauth.AuthError(\n                  fireauth.authenum.Error.ARGUMENT_ERROR,\n                  fireauth.ActionCodeSettings.RawField.HANDLE_CODE_IN_APP +\n                  ' must be true when sending sign in link to email');\n            }\n            return actionCodeSettingsBuilder.buildRequest();\n          }).then(function(additionalRequestData) {\n            return self.getRpcHandler().sendSignInLinkToEmail(\n                email, additionalRequestData);\n          }).then(function(email) {\n            // Do not return the email.\n          }));\n};\n\n\n/**\n * Verifies an email action code for password reset and returns a promise\n * that resolves with the associated email if verified.\n * @param {string} code The email action code to verify for password reset.\n * @return {!goog.Promise<string>}\n */\nfireauth.Auth.prototype.verifyPasswordResetCode = function(code) {\n  return this.checkActionCode(code).then(function(info) {\n    return info['data']['email'];\n  });\n};\n\n\n/**\n * Requests resetPassword endpoint for password reset, verifies the action code\n * and updates the new password, returns an empty promise.\n * @param {string} code The email action code to confirm for password reset.\n * @param {string} newPassword The new password.\n * @return {!goog.Promise<undefined, !fireauth.AuthError>}\n */\nfireauth.Auth.prototype.confirmPasswordReset = function(code, newPassword) {\n  return this.registerPendingPromise_(\n      this.getRpcHandler().confirmPasswordReset(code, newPassword)\n      .then(function(email) {\n        // Do not return the email.\n      }));\n};\n\n\n/**\n * Verifies an email action code and returns an empty promise if verified.\n * @param {string} code The email action code to verify for password reset.\n * @return {!goog.Promise<!Object>}\n */\nfireauth.Auth.prototype.checkActionCode = function(code) {\n  return this.registerPendingPromise_(\n      this.getRpcHandler().checkActionCode(code)\n      .then(function(response) {\n        return new fireauth.ActionCodeInfo(response);\n      }));\n};\n\n\n/**\n * Applies an out-of-band email action code, such as an email verification code.\n * @param {string} code The email action code.\n * @return {!goog.Promise<void>}\n */\nfireauth.Auth.prototype.applyActionCode = function(code) {\n  return this.registerPendingPromise_(\n      this.getRpcHandler().applyActionCode(code)\n      .then(function(email) {\n        // Returns nothing.\n      }));\n};\n\n\n/**\n * Sends the password reset email for the email account provided.\n * @param {string} email The email account with the password to be reset.\n * @param {?Object=} opt_actionCodeSettings The optional action code settings\n *     object.\n * @return {!goog.Promise<void>}\n */\nfireauth.Auth.prototype.sendPasswordResetEmail =\n    function(email, opt_actionCodeSettings) {\n  var self = this;\n  return this.registerPendingPromise_(\n      // Wrap in promise as ActionCodeSettings constructor could throw a\n      // synchronous error if invalid arguments are specified.\n      goog.Promise.resolve().then(function() {\n        if (typeof opt_actionCodeSettings !== 'undefined' &&\n            // Ignore empty objects.\n            !goog.object.isEmpty(opt_actionCodeSettings)) {\n          return new fireauth.ActionCodeSettings(\n              /** @type {!Object} */ (opt_actionCodeSettings)).buildRequest();\n        }\n        return {};\n      })\n      .then(function(additionalRequestData) {\n        return self.getRpcHandler().sendPasswordResetEmail(\n            email, additionalRequestData);\n      }).then(function(email) {\n        // Do not return the email.\n      }));\n};\n\n\n/**\n * Signs in with a phone number using the app verifier instance and returns a\n * promise that resolves with the confirmation result which on confirmation\n * will resolve with the UserCredential object.\n * @param {string} phoneNumber The phone number to authenticate with.\n * @param {!firebase.auth.ApplicationVerifier} appVerifier The application\n *     verifier.\n * @return {!goog.Promise<!fireauth.ConfirmationResult>}\n */\nfireauth.Auth.prototype.signInWithPhoneNumber =\n    function(phoneNumber, appVerifier) {\n  return /** @type {!goog.Promise<!fireauth.ConfirmationResult>} */ (\n      this.registerPendingPromise_(fireauth.ConfirmationResult.initialize(\n          this,\n          phoneNumber,\n          appVerifier,\n          // This will wait for redirectStateIsReady to resolve first.\n          goog.bind(this.signInWithCredential, this))));\n};\n\n\n/**\n * Signs in a Firebase User with the provided email and the passwordless\n * sign-in email link.\n * @param {string} email The email account to sign in with.\n * @param {?string=} opt_link The optional link which contains the OTP needed\n *     to complete the sign in with email link. If not specified, the current\n *     URL is used instead.\n * @return {!goog.Promise<!fireauth.AuthEventManager.Result>}\n */\nfireauth.Auth.prototype.signInWithEmailLink = function(email, opt_link) {\n  return this.registerPendingPromise_(goog.Promise.resolve().then(() => {\n      const link = opt_link || fireauth.util.getCurrentUrl();\n      const credential = fireauth.EmailAuthProvider.credentialWithLink(\n          email, link);\n      // Check if the tenant ID in the email link matches the tenant ID on Auth\n      // instance.\n      const actionCodeUrl =\n          fireauth.EmailAuthProvider.getActionCodeUrlFromSignInEmailLink(link);\n      if (!actionCodeUrl) {\n        throw new fireauth.AuthError(\n            fireauth.authenum.Error.ARGUMENT_ERROR, 'Invalid email link!');\n      }\n      if (actionCodeUrl['tenantId'] !== this.getTenantId()) {\n        throw new fireauth.AuthError(\n            fireauth.authenum.Error.TENANT_ID_MISMATCH);\n      }\n      return this.signInWithCredential(credential);\n  }));\n};\n","/**\n * @license\n * Copyright 2018 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n /**\n * @fileoverview Defines the grecaptcha interface.\n */\n\ngoog.provide('fireauth.grecaptcha');\n\n\n/**\n * The reCAPTCHA interface for initializing and managing visible v2\n * reCAPTCHAs as well as invisible ones.\n * @interface\n */\nfireauth.grecaptcha = function() {};\n\n\n/**\n * Creates a new instance of the recaptcha client.\n *\n * @param {!Element|string} elementOrId Element or element ID for the\n *     placeholder to render the reCAPTCHA client.\n * @param {!Object} params Parameters for the recaptcha client.\n * @return {number} The client ID.\n */\nfireauth.grecaptcha.prototype.render = function(elementOrId, params) {};\n\n\n/**\n * Resets a client with the given ID.  If an ID is not provided, resets the\n * default client.\n *\n * @param {number=} id The ID of the recaptcha client.\n * @param {?Object=} params Parameters for the recaptcha client.\n */\nfireauth.grecaptcha.prototype.reset = function(id, params) {};\n\n\n/**\n * Gets the response for the client with the given ID.  If an ID is not\n * provided, gets the response for the default client.\n *\n * @param {number=} id The ID of the recaptcha client.\n * @return {?string}\n */\nfireauth.grecaptcha.prototype.getResponse = function(id) {};\n\n\n/**\n * Programmatically triggers the invisible reCAPTCHA.\n *\n * @param {number=} opt_id The ID of the recaptcha client. Defaults to the\n *     first widget created if unspecified.\n */\nfireauth.grecaptcha.prototype.execute = function(opt_id) {};\n","/**\n * @license\n * Copyright 2018 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n /**\n * @fileoverview Defines the mock grecaptcha utilities used for development\n * testing.\n */\ngoog.provide('fireauth.GRecaptchaMockFactory');\ngoog.provide('fireauth.RecaptchaMock');\n\ngoog.require('fireauth.grecaptcha');\ngoog.require('fireauth.util');\ngoog.require('goog.dom');\ngoog.require('goog.events');\ngoog.require('goog.events.EventType');\n\n\n/**\n * The mock grecaptcha factory.\n * @constructor\n * @implements {fireauth.grecaptcha}\n */\nfireauth.GRecaptchaMockFactory = function() {\n  /**\n   * @const @private {!Object<string, !fireauth.RecaptchaMock>} The hash map\n   *     that stores the widget ID to mock reCAPTCHA instances.\n   */\n  this.map_ = {};\n  /**\n   * @private {number} The current widget ID counter, incremented each time\n   *     a new mock reCAPTCHA is created.\n   */\n  this.counter_ = fireauth.GRecaptchaMockFactory.START_INSTANCE_ID;\n};\n\n\n/**\n * @const {number} The start ID of the first created mock reCAPTCHA.\n */\nfireauth.GRecaptchaMockFactory.START_INSTANCE_ID = 1000000000000;\n\n\n/** @const {number} The reCAPTCHA expiration time in milliseconds. */\nfireauth.GRecaptchaMockFactory.EXPIRATION_TIME_MS = 60000;\n\n\n/** @const {number} The reCAPTCHA auto solving time in milliseconds. */\nfireauth.GRecaptchaMockFactory.SOLVE_TIME_MS = 500;\n\n\n/**\n * @private {?fireauth.GRecaptchaMockFactory} The singleton instance\n *     for grecaptcha mock object.\n */\nfireauth.GRecaptchaMockFactory.instance_ = null;\n\n\n/**\n * @return {!fireauth.GRecaptchaMockFactory} The singleton grecaptcha mock\n *     instance.\n */\nfireauth.GRecaptchaMockFactory.getInstance = function() {\n  // Check if there is an existing instance. Otherwise, create one and cache it.\n  if (!fireauth.GRecaptchaMockFactory.instance_) {\n    fireauth.GRecaptchaMockFactory.instance_ =\n        new fireauth.GRecaptchaMockFactory();\n  }\n  return fireauth.GRecaptchaMockFactory.instance_;\n};\n\n\n/**\n * Creates a new instance of the mock reCAPTCHA widget.\n *\n * @param {(!Element|string)} elementOrId Element or element ID for the\n *     placeholder to render the reCAPTCHA client.\n * @param {!Object} params Parameters for the reCAPTCHA client.\n * @return {number} The client ID.\n * @override\n */\nfireauth.GRecaptchaMockFactory.prototype.render =\n    function(elementOrId, params) {\n  this.map_[this.counter_.toString()] =\n      new fireauth.RecaptchaMock(elementOrId, params);\n  return this.counter_++;\n};\n\n\n/**\n * Resets a reCAPTCHA with the given ID. If an ID is not provided, resets the\n * first instance.\n *\n * @param {number=} opt_id The id of the reCAPTCHA client. Defaults to the first\n *     widget created if unspecified.\n * @override\n */\nfireauth.GRecaptchaMockFactory.prototype.reset = function(opt_id) {\n  var mock = this.getMock_(opt_id);\n  var id = this.getId_(opt_id);\n  if (mock && id) {\n    mock.delete();\n    delete this.map_[/** @type {string} */ (id)];\n  }\n};\n\n\n/**\n * Gets the response for the client with the given ID. If an ID is not\n * provided, gets the response for the default client.\n *\n * @param {number=} opt_id The ID of the reCAPTCHA widget. Defaults to the first\n *     widget created if unspecified.\n * @return {?string}\n * @override\n */\nfireauth.GRecaptchaMockFactory.prototype.getResponse = function(opt_id) {\n  var mock = this.getMock_(opt_id);\n  return mock ? mock.getResponse() : null;\n};\n\n\n/**\n * Programmatically triggers the invisible reCAPTCHA.\n *\n * @param {number=} opt_id The ID of the recaptcha client. Defaults to the first\n *     widget created if unspecified.\n * @override\n */\nfireauth.GRecaptchaMockFactory.prototype.execute = function(opt_id) {\n  var mock = this.getMock_(opt_id);\n  if (mock) {\n    mock.execute();\n  }\n};\n\n\n/**\n * @param {number=} opt_id The optional ID to lookup.\n * @return {?fireauth.RecaptchaMock} The corresponding reCAPTCHA mock if found.\n * @private\n */\nfireauth.GRecaptchaMockFactory.prototype.getMock_ = function(opt_id) {\n  var id = this.getId_(opt_id);\n  return id ? this.map_[id] || null : null;\n};\n\n\n/**\n * @param {number=} opt_id The optional ID to lookup.\n * @return {?string} The corresponding reCAPTCHA mock ID if found.\n * @private\n */\nfireauth.GRecaptchaMockFactory.prototype.getId_ = function(opt_id) {\n  var id = typeof opt_id === 'undefined' ?\n      fireauth.GRecaptchaMockFactory.START_INSTANCE_ID : opt_id;\n  return id ? id.toString() : null;\n};\n\n\n/**\n * Mock single reCAPTCHA instance.\n * @param {(!Element|string)} elementOrId Element or element ID for the\n *     placeholder to render the reCAPTCHA client.\n * @param {!Object} params Parameters for the reCAPTCHA client.\n * @constructor\n */\nfireauth.RecaptchaMock = function(elementOrId, params) {\n  /** @private {boolean} Whether the instance was deleted. */\n  this.deleted_ = false;\n  /** @const @private {!Object} The reCAPTCHA parameters. */\n  this.params_ = params;\n  /** @private {?string} The simulated response token if available. */\n  this.responseToken_ = null;\n  /**\n   * @private {?number} The timer ID for response callback/expiration callback\n   *     to trigger.\n   */\n  this.timerId_ = null;\n  /** @const @private {boolean} Whether the reCAPTCHA is visible or not. */\n  this.isVisible_ = this.params_['size'] !== 'invisible';\n  /**\n   * @const @private {?Element} The container or button trigger of the\n   *     reCAPTCHA.\n   */\n  this.element_ = goog.dom.getElement(elementOrId);\n  var self = this;\n  /** @private {function(?)} The on click handler for invisible reCAPTCHAs. */\n  this.onClickHandler_ = function(event) {\n    self.execute();\n  };\n  if (this.isVisible_) {\n    // For a visible reCAPTCHA, simulate reCAPTCHA continuously solved\n    // and then expired.\n    this.execute();\n  } else {\n    // Trigger on button click on when execute is directly called.\n    goog.events.listen(\n        this.element_,\n        goog.events.EventType.CLICK,\n        this.onClickHandler_);\n  }\n};\n\n\n/** @return {?string} The current reCAPTCHA response. */\nfireauth.RecaptchaMock.prototype.getResponse = function() {\n  this.checkIfDeleted_();\n  return this.responseToken_;\n};\n\n\n/** Starts the reCAPTCHA mock solving/expiration cycle. */\nfireauth.RecaptchaMock.prototype.execute = function() {\n  this.checkIfDeleted_();\n  var self = this;\n  if (this.timerId_) {\n    return;\n  }\n  // Wait for expected delay before auto-solving.\n  this.timerId_ = setTimeout(function() {\n    // Generate random string as reCAPTCHA response token.\n    self.responseToken_ = fireauth.util.generateRandomAlphaNumericString(50);\n    // Trigger developer's callbacks.\n    var callback = self.params_['callback'];\n    var expirationCallback = self.params_['expired-callback'];\n    if (callback) {\n      try {\n        callback(self.responseToken_);\n      } catch (e) {}\n    }\n    // Wait for token to expire before triggering expiration callback and\n    // resetting token response.\n    self.timerId_ = setTimeout(function() {\n      self.timerId_ = null;\n      self.responseToken_ = null;\n      if (expirationCallback) {\n        try {\n          expirationCallback();\n        } catch (e) {}\n      }\n      if (self.isVisible_) {\n        self.execute();\n      }\n    }, fireauth.GRecaptchaMockFactory.EXPIRATION_TIME_MS);\n  }, fireauth.GRecaptchaMockFactory.SOLVE_TIME_MS);\n};\n\n\n/** Deletes the current mock instance. */\nfireauth.RecaptchaMock.prototype.delete = function() {\n  this.checkIfDeleted_();\n  this.deleted_ = true;\n  clearTimeout(this.timerId_);\n  this.timerId_ = null;\n  goog.events.unlisten(\n      this.element_,\n      goog.events.EventType.CLICK,\n      this.onClickHandler_);\n};\n\n\n/**\n * Checks whether the instance was deleted.\n * @private\n */\nfireauth.RecaptchaMock.prototype.checkIfDeleted_ = function() {\n  // This error should never be thrown externally.\n  // GRecaptchaMockFactory will ensure that a deleted instance is removed.\n  if (this.deleted_) {\n    throw new Error('reCAPTCHA mock was already deleted!');\n  }\n};\n","/**\n * @license\n * Copyright 2019 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n \n/**\n * @fileoverview Defines the MultiFactorGenerators used to generate\n * MultiFactorAssertions. This currently covers only PhoneMultiFactorGenerator.\n */\n\ngoog.provide('fireauth.PhoneMultiFactorGenerator');\n\ngoog.require('fireauth.PhoneMultiFactorAssertion');\ngoog.require('fireauth.constants');\ngoog.require('fireauth.object');\n\n\n/**\n * Defines the multi-factor generator for PhoneMultiFactorAssertions.\n * This class acts only as a namespace and defines some static methods and\n * properties.\n * @constructor @struct @final\n */\nfireauth.PhoneMultiFactorGenerator = function() {};\nfireauth.object.setReadonlyProperty(fireauth.PhoneMultiFactorGenerator,\n    'FACTOR_ID', fireauth.constants.SecondFactorType.PHONE);\n\n\n/**\n * Initializes a `PhoneMultiFactorAssertion` given a `PhoneAuthCredential`.\n * @param {!fireauth.PhoneAuthCredential} phoneAuthCredential\n * @return {!fireauth.PhoneMultiFactorAssertion} The `MultiFactorAssertion`\n *     corresponding to the provided `PhoneAuthCredential`.\n */\nfireauth.PhoneMultiFactorGenerator.assertion = function(phoneAuthCredential) {\n  return new fireauth.PhoneMultiFactorAssertion(phoneAuthCredential);\n};\n","/**\n * @license\n * Copyright 2018 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n /**\n * @fileoverview Defines the RecaptchaLoader implementation used to mock loading\n * of grecaptcha dependencies.\n */\n\ngoog.provide('fireauth.RecaptchaMockLoader');\n\ngoog.require('fireauth.GRecaptchaMockFactory');\ngoog.require('fireauth.RecaptchaLoader');\ngoog.require('goog.Promise');\n\n\n/**\n * Defines a mock reCAPTCHA loader by implementing the fireauth.RecaptchaLoader\n * interface.\n * @constructor\n * @implements {fireauth.RecaptchaLoader}\n */\nfireauth.RecaptchaMockLoader = function() {};\n\n\n/**\n * Loads the grecaptcha mock library if it is not loaded and returns a promise\n * that resolves on success. If the right conditions are available, will reload\n * the dependencies for a specified language code.\n * @param {?string} hl The reCAPTCHA language code.\n * @return {!goog.Promise<!fireauth.grecaptcha>} A promise that resolves when\n *     grecaptcha is loaded.\n * @override\n */\nfireauth.RecaptchaMockLoader.prototype.loadRecaptchaDeps =\n    function(hl) {\n  return goog.Promise.resolve(\n  \t/** @type {!fireauth.grecaptcha} */ (fireauth.GRecaptchaMockFactory.getInstance()));\n};\n\n\n/**\n * Decrements the reCAPTCHA instance counter.\n * @override\n */\nfireauth.RecaptchaMockLoader.prototype.clearSingleRecaptcha =\n    function() {};\n\n\n/**\n * @private {?fireauth.RecaptchaMockLoader} The singleton instance\n *     for the mock reCAPTCHA dependency loader.\n */\nfireauth.RecaptchaMockLoader.instance_ = null;\n\n\n/**\n * @return {!fireauth.RecaptchaMockLoader} The singleton mock reCAPTCHA\n *     dependency loader instance.\n */\nfireauth.RecaptchaMockLoader.getInstance = function() {\n  // Check if there is an existing instance. Otherwise create one and cache it.\n  if (!fireauth.RecaptchaMockLoader.instance_) {\n    fireauth.RecaptchaMockLoader.instance_ =\n        new fireauth.RecaptchaMockLoader();\n  }\n  return fireauth.RecaptchaMockLoader.instance_;\n};\n","/**\n * @license\n * Copyright 2018 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n /**\n * @fileoverview Defines the RecaptchaLoader implementation used to load all\n * the grecaptcha dependencies.\n */\n\ngoog.provide('fireauth.RecaptchaRealLoader');\n\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.RecaptchaLoader');\ngoog.require('fireauth.authenum.Error');\ngoog.require('fireauth.util');\ngoog.require('goog.Promise');\ngoog.require('goog.html.TrustedResourceUrl');\ngoog.require('goog.net.jsloader');\ngoog.require('goog.string.Const');\n\n\n/**\n * Utility to help load reCAPTCHA dependencies for specified languages.\n * @constructor\n * @implements {fireauth.RecaptchaLoader}\n */\nfireauth.RecaptchaRealLoader = function() {\n  /**\n   * @private {number} The reCAPTCHA instance counter. This is used to track the\n   *     number of reCAPTCHAs rendered on the page. This is needed to allow\n   *     localization of the reCAPTCHA. Localization is applied by loading the\n   *     grecaptcha SDK with the hl field provided. However, this will break\n   *     existing reCAPTCHAs. So we should only support i18n when there are no\n   *     other widgets rendered on this screen. If the developer is already\n   *     using reCAPTCHA in another context, we will disable localization so we\n   *     don't accidentally break existing reCAPTCHA widgets.\n   */\n  this.counter_ = goog.global['grecaptcha'] ? Infinity : 0;\n  /** @private {?string} The current reCAPTCHA language code. */\n  this.hl_ = null;\n  /** @const @private {string} The reCAPTCHA callback name. */\n  this.cbName_ = '__rcb' + Math.floor(Math.random() * 1000000).toString();\n};\n\n\n/** @private @const {!goog.string.Const} The reCAPTCHA javascript source URL. */\nfireauth.RecaptchaRealLoader.RECAPTCHA_SRC_ = goog.string.Const.from(\n    'https://www.google.com/recaptcha/api.js?onload=%{onload}&render=explicit' +\n    '&hl=%{hl}');\n\n\n/**\n * The default timeout delay (units in milliseconds) for requests loading\n * the external reCAPTCHA dependencies.\n * @const {!fireauth.util.Delay}\n * @private\n */\nfireauth.RecaptchaRealLoader.DEFAULT_DEPENDENCY_TIMEOUT_ =\n    new fireauth.util.Delay(30000, 60000);\n\n\n/**\n * Loads the grecaptcha client library if it is not loaded and returns a promise\n * that resolves on success. If the right conditions are available, will reload\n * the dependencies for a specified language code.\n * @param {?string} hl The reCAPTCHA language code.\n * @return {!goog.Promise<!fireauth.grecaptcha>} A promise that resolves when\n *     grecaptcha is loaded.\n * @override\n */\nfireauth.RecaptchaRealLoader.prototype.loadRecaptchaDeps =\n    function(hl) {\n  var self = this;\n  return new goog.Promise(function(resolve, reject) {\n    var timer = setTimeout(\n        function() {\n          reject(new fireauth.AuthError(\n              fireauth.authenum.Error.NETWORK_REQUEST_FAILED));\n        },\n        fireauth.RecaptchaRealLoader.DEFAULT_DEPENDENCY_TIMEOUT_\n            .get()\n    );\n    // Load grecaptcha SDK if not already loaded or language changed since last\n    // load and no other rendered reCAPTCHA is visible,\n    if (!goog.global['grecaptcha'] || (hl !== self.hl_ && !self.counter_)) {\n      // reCAPTCHA saves the onload function and applies it on subsequent\n      // reloads. This means that the callback name has to remain the same.\n      goog.global[self.cbName_] = function() {\n        if (!goog.global['grecaptcha']) {\n          clearTimeout(timer);\n          // This should not happen.\n          reject(new fireauth.AuthError(\n              fireauth.authenum.Error.INTERNAL_ERROR));\n        } else {\n          // Update the current language code.\n          self.hl_ = hl;\n          var render = goog.global['grecaptcha']['render'];\n          // Wrap grecaptcha.render to keep track of rendered grecaptcha. This\n          // helps detect if the developer rendered a non\n          // firebase.auth.RecaptchaVerifier reCAPTCHA.\n          goog.global['grecaptcha']['render'] =\n              function(container, parameters) {\n            var widgetId = render(container, parameters);\n            // Increment only after render succeeds, in case an error is thrown\n            // during rendering.\n            self.counter_++;\n            return widgetId;\n          };\n          clearTimeout(timer);\n          resolve(goog.global['grecaptcha']);\n        }\n        delete goog.global[self.cbName_];\n      };\n      // Construct reCAPTCHA URL and on load, run the temporary function.\n      var url = goog.html.TrustedResourceUrl.format(\n          fireauth.RecaptchaRealLoader.RECAPTCHA_SRC_,\n          {'onload': self.cbName_, 'hl': hl || ''});\n      // TODO: eventually, replace all dependencies on goog.net.jsloader.\n      goog.Promise.resolve(goog.net.jsloader.safeLoad(url))\n          .thenCatch(function(error) {\n            clearTimeout(timer);\n            // In case library fails to load, typically due to a network error,\n            // reset cached loader to null to force a refresh on a retrial.\n            reject(new fireauth.AuthError(\n                fireauth.authenum.Error.INTERNAL_ERROR,\n                'Unable to load external reCAPTCHA dependencies!'));\n          });\n    } else {\n      clearTimeout(timer);\n      resolve(goog.global['grecaptcha']);\n    }\n  });\n};\n\n\n/**\n * Decrements the reCAPTCHA instance counter.\n * @override\n */\nfireauth.RecaptchaRealLoader.prototype.clearSingleRecaptcha =\n    function() {\n  this.counter_--;\n};\n\n\n/**\n * @private {?fireauth.RecaptchaRealLoader} The singleton instance\n *     for reCAPTCHA dependency loader.\n */\nfireauth.RecaptchaRealLoader.instance_ = null;\n\n\n/**\n * @return {!fireauth.RecaptchaRealLoader} The singleton reCAPTCHA\n *     dependency loader instance.\n */\nfireauth.RecaptchaRealLoader.getInstance = function() {\n  // Check if there is an existing instance. Otherwise create one and cache it.\n  if (!fireauth.RecaptchaRealLoader.instance_) {\n    fireauth.RecaptchaRealLoader.instance_ =\n        new fireauth.RecaptchaRealLoader();\n  }\n  return fireauth.RecaptchaRealLoader.instance_;\n};\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Defines the reCAPTCHA app verifier and its base class. The\n * former is currently used for web phone authentication whereas the latter is\n * used for the mobile app verification web fallback.\n */\ngoog.provide('fireauth.BaseRecaptchaVerifier');\ngoog.provide('fireauth.RecaptchaVerifier');\n\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.RecaptchaMockLoader');\ngoog.require('fireauth.RecaptchaRealLoader');\ngoog.require('fireauth.RpcHandler');\ngoog.require('fireauth.authenum.Error');\ngoog.require('fireauth.constants');\ngoog.require('fireauth.object');\ngoog.require('fireauth.util');\ngoog.require('goog.Promise');\ngoog.require('goog.array');\ngoog.require('goog.dom');\n\n\n/**\n * Creates the firebase base reCAPTCHA app verifier independent of Firebase\n * App or Auth instances.\n *\n * @param {string} apiKey The API key used to initialize the RPC handler for\n *     querying the Auth backend.\n * @param {!Element|string} container The reCAPTCHA container parameter. This\n *     has different meaning depending on whether the reCAPTCHA is hidden or\n *     visible.\n * @param {?Object=} opt_parameters The optional reCAPTCHA parameters.\n * @param {?(function():?string)=} opt_getLanguageCode The language code getter\n *     function.\n * @param {?string=} opt_clientVersion The optional client version to append to\n *     RPC header.\n * @param {?Object=} opt_rpcHandlerConfig The optional RPC handler\n *     configuration, typically passed when different Auth endpoints are to be\n *     used.\n * @param {boolean=} opt_isTestingMode Whether the reCAPTCHA is to be rendered\n *     in testing mode.\n * @constructor\n */\nfireauth.BaseRecaptchaVerifier = function(apiKey, container, opt_parameters,\n    opt_getLanguageCode, opt_clientVersion, opt_rpcHandlerConfig,\n    opt_isTestingMode) {\n  // Set the type readonly property needed for full implementation of the\n  // firebase.auth.ApplicationVerifier interface.\n  fireauth.object.setReadonlyProperty(this, 'type', 'recaptcha');\n  /**\n   * @private {?goog.Promise<void>} The cached reCAPTCHA ready response. This is\n   *     null until the first time it is triggered or when an error occurs in\n   *     getting ready.\n   */\n  this.cachedReadyPromise_ = null;\n  /** @private {?number} The reCAPTCHA widget ID. Null when not rendered. */\n  this.widgetId_ = null;\n  /** @private {boolean} Whether the instance is already destroyed. */\n  this.destroyed_ = false;\n  /** @private {!Element|string} The reCAPTCHA container. */\n  this.container_ = container;\n  /**\n   * @private {?fireauth.grecaptcha} The reCAPTCHA client library namespace.\n   */\n  this.grecaptcha_ = null;\n  /**\n   * @const @private {!fireauth.RecaptchaLoader} The grecaptcha loader.\n   */\n  this.recaptchaLoader_ = !!opt_isTestingMode ?\n      fireauth.RecaptchaMockLoader.getInstance() :\n      fireauth.RecaptchaRealLoader.getInstance();\n  // If no parameters passed, use default settings.\n  // Currently, visible recaptcha is the default setting as invisible reCAPTCHA\n  // is not yet supported by the backend.\n  /** @private {!Object} The reCAPTCHA parameters. */\n  this.parameters_ = opt_parameters || {\n    'theme': 'light',\n    'type': 'image'\n  };\n  /** @private {!Array<!goog.Promise<*>|!goog.Promise<void>>} List of\n   *      pending promises. */\n  this.pendingPromises_ = [];\n  if (this.parameters_[fireauth.BaseRecaptchaVerifier.ParamName.SITEKEY]) {\n    // sitekey should not be provided.\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.ARGUMENT_ERROR,\n        'sitekey should not be provided for reCAPTCHA as one is ' +\n        'automatically provisioned for the current project.');\n  }\n  /** @private {boolean} Whether the reCAPTCHA is invisible or not. */\n  this.isInvisible_ =\n      this.parameters_[fireauth.BaseRecaptchaVerifier.ParamName.SIZE] ===\n      'invisible';\n  // Check if DOM is supported.\n  if (!fireauth.util.isDOMSupported()) {\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.OPERATION_NOT_SUPPORTED,\n        'RecaptchaVerifier is only supported in a browser HTTP/HTTPS ' +\n        'environment with DOM support.');\n  }\n  // reCAPTCHA container must be valid and if visible, not empty.\n  // An invisible reCAPTCHA will not render in its container. That container\n  // will execute the reCAPTCHA when it is clicked.\n  if (!goog.dom.getElement(container) ||\n      (!this.isInvisible_ && goog.dom.getElement(container).hasChildNodes())) {\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.ARGUMENT_ERROR,\n        'reCAPTCHA container is either not found or already contains inner ' +\n        'elements!');\n  }\n  /**\n   * @private {!fireauth.RpcHandler} The RPC handler for querying the auth\n   *     backend.\n   */\n  this.rpcHandler_ = new fireauth.RpcHandler(\n      apiKey,\n      opt_rpcHandlerConfig || null,\n      opt_clientVersion || null);\n  /**\n   * @private {function():?string} Current language code getter.\n   */\n  this.getLanguageCode_ = opt_getLanguageCode || function() {return null;};\n  var self = this;\n  /**\n   * @private {!Array<function(?string)>} The token change listeners.\n   */\n  this.tokenListeners_ = [];\n  // Wrap token callback.\n  var existingCallback =\n      this.parameters_[fireauth.BaseRecaptchaVerifier.ParamName.CALLBACK];\n  this.parameters_[fireauth.BaseRecaptchaVerifier.ParamName.CALLBACK] =\n      function(response) {\n    // Dispatch internal event for the token response.\n    self.dispatchEvent_(response);\n    if (typeof existingCallback === 'function') {\n      existingCallback(response);\n    } else if (typeof existingCallback === 'string') {\n      // Check if the provided callback is a global function name.\n      var cb = fireauth.util.getObjectRef(existingCallback, goog.global);\n      if (typeof cb === 'function') {\n        // If so, trigger it.\n        cb(response);\n      }\n    }\n  };\n  // Wrap expired token callback.\n  var existingExpiredCallback = this.parameters_[\n    fireauth.BaseRecaptchaVerifier.ParamName.EXPIRED_CALLBACK];\n  this.parameters_[fireauth.BaseRecaptchaVerifier.ParamName.EXPIRED_CALLBACK] =\n      function() {\n    // Dispatch internal event for the token expiration.\n    self.dispatchEvent_(null);\n    if (typeof existingExpiredCallback === 'function') {\n      existingExpiredCallback();\n    } else if (typeof existingExpiredCallback === 'string') {\n      // Check if the provided expired callback is a global function name.\n      var cb = fireauth.util.getObjectRef(existingExpiredCallback, goog.global);\n      if (typeof cb === 'function') {\n        // If so, trigger it.\n        cb();\n      }\n    }\n  };\n};\n\n\n/**\n * grecaptcha parameter names.\n * @enum {string}\n */\nfireauth.BaseRecaptchaVerifier.ParamName = {\n  CALLBACK: 'callback',\n  EXPIRED_CALLBACK: 'expired-callback',\n  SITEKEY: 'sitekey',\n  SIZE: 'size'\n};\n\n\n/**\n * Dispatches the token change event to all subscribed listeners.\n * @param {?string} token The current detected token, null for none.\n * @private\n */\nfireauth.BaseRecaptchaVerifier.prototype.dispatchEvent_ = function(token) {\n  for (var i = 0; i < this.tokenListeners_.length; i++) {\n    try {\n      this.tokenListeners_[i](token);\n    } catch (e) {\n      // If any handler fails, ignore and run next handler.\n    }\n  }\n};\n\n\n/**\n * Add a reCAPTCHA token change listener.\n * @param {function(?string)} listener The token listener to add.\n * @private\n */\nfireauth.BaseRecaptchaVerifier.prototype.addTokenChangeListener_ =\n    function(listener) {\n  this.tokenListeners_.push(listener);\n};\n\n\n/**\n * Remove a reCAPTCHA token change listener.\n * @param {function(?string)} listener The token listener to remove.\n * @private\n */\nfireauth.BaseRecaptchaVerifier.prototype.removeTokenChangeListener_ =\n    function(listener) {\n  goog.array.removeAllIf(this.tokenListeners_, function(ele) {\n    return ele == listener;\n  });\n};\n\n\n/**\n * Takes in a pending promise, saves it and adds a clean up callback which\n * forgets the pending promise after it is fulfilled and echoes the promise\n * back.\n * @param {!goog.Promise<*, *>|!goog.Promise<void>} p The pending promise.\n * @return {!goog.Promise<*, *>|!goog.Promise<void>}\n * @private\n */\nfireauth.BaseRecaptchaVerifier.prototype.registerPendingPromise_ = function(p) {\n  var self = this;\n  // Save created promise in pending list.\n  this.pendingPromises_.push(p);\n  p.thenAlways(function() {\n    // When fulfilled, remove from pending list.\n    goog.array.remove(self.pendingPromises_, p);\n  });\n  // Return the promise.\n  return p;\n};\n\n\n/** @return {boolean} Whether verifier instance has pending promises. */\nfireauth.BaseRecaptchaVerifier.prototype.hasPendingPromises = function() {\n  return this.pendingPromises_.length != 0;\n};\n\n\n/**\n * Gets the current RecaptchaVerifier in a ready state for rendering by first\n * checking that the environment supports a reCAPTCHA, loading reCAPTCHA\n * dependencies if not already available and then getting the Firebase project's\n * provisioned reCAPTCHA configuration.\n * @return {!goog.Promise<void>} The promise that resolves when recaptcha\n *     is ready for rendering.\n * @private\n */\nfireauth.BaseRecaptchaVerifier.prototype.isReady_ = function() {\n  var self = this;\n  // If previously called, return the cached response.\n  if (this.cachedReadyPromise_) {\n    return this.cachedReadyPromise_;\n  }\n  this.cachedReadyPromise_ = this.registerPendingPromise_(goog.Promise.resolve()\n      .then(function() {\n        // Verify environment first.\n        // Fail quickly from a worker environment or non-HTTP/HTTPS environment.\n        if (fireauth.util.isHttpOrHttps() && !fireauth.util.isWorker()) {\n          // Wait for DOM to be ready as this feature depends on that.\n          return fireauth.util.onDomReady();\n        } else {\n          throw new fireauth.AuthError(\n              fireauth.authenum.Error.OPERATION_NOT_SUPPORTED,\n              'RecaptchaVerifier is only supported in a browser HTTP/HTTPS ' +\n              'environment.');\n        }\n      })\n      .then(function() {\n        // Load external reCAPTCHA dependencies if not already there, taking\n        // into account the current language code.\n        return self.recaptchaLoader_.loadRecaptchaDeps(self.getLanguageCode_());\n      })\n      .then(function(grecaptcha) {\n        self.grecaptcha_ = grecaptcha;\n        // Load Firebase project's reCAPTCHA configuration.\n        return self.rpcHandler_.getRecaptchaParam();\n      })\n      .then(function(result) {\n        // Update the reCAPTCHA parameters.\n        self.parameters_[fireauth.BaseRecaptchaVerifier.ParamName.SITEKEY] =\n            result[fireauth.RpcHandler.AuthServerField.RECAPTCHA_SITE_KEY];\n      }).thenCatch(function(error) {\n        // Anytime an error occurs, reset the cached ready promise to rerun on\n        // retrial.\n        self.cachedReadyPromise_ = null;\n        // Rethrow the error.\n        throw error;\n      }));\n  // Return the cached/pending ready promise.\n  return this.cachedReadyPromise_;\n};\n\n/**\n * Renders the reCAPTCHA and returns the allocated widget ID.\n * @return {!goog.Promise<number>} The promise that resolves with the reCAPTCHA\n *     widget ID when it is rendered.\n */\nfireauth.BaseRecaptchaVerifier.prototype.render = function() {\n  this.checkIfDestroyed_();\n  var self = this;\n  // Get reCAPTCHA ready.\n  return this.registerPendingPromise_(this.isReady_().then(function() {\n    if (self.widgetId_ === null) {\n      // For a visible reCAPTCHA, embed in a wrapper DIV container to allow\n      // re-rendering in the same developer provided container.\n      var container = self.container_;\n      if (!self.isInvisible_) {\n        // Get outer container (the developer provided container).\n        var outerContainer = goog.dom.getElement(container);\n        // Create wrapper temp DIV container.\n        container = goog.dom.createDom(goog.dom.TagName.DIV);\n        // Add temp DIV to outer container.\n        outerContainer.appendChild(container);\n      }\n      // If not initialized, initialize reCAPTCHA and return its widget ID.\n      self.widgetId_ = self.grecaptcha_.render(container, self.parameters_);\n    }\n    return self.widgetId_;\n  }));\n};\n\n\n/**\n * Gets the reCAPTCHA ready and waits for the reCAPTCHA token to be available\n * before resolving the promise returned.\n * @return {!goog.Promise<string>} The promise that resolves with the reCAPTCHA\n *     token when reCAPTCHA challenge is solved.\n */\nfireauth.BaseRecaptchaVerifier.prototype.verify = function() {\n  // Fail if reCAPTCHA is already destroyed.\n  this.checkIfDestroyed_();\n  var self = this;\n  // Render reCAPTCHA.\n  return this.registerPendingPromise_(this.render().then(function(widgetId) {\n    return new goog.Promise(function(resolve, reject) {\n      // Get current reCAPTCHA token.\n      var recaptchaToken = self.grecaptcha_.getResponse(widgetId);\n      if (recaptchaToken) {\n        // Unexpired token already available. Resolve pending promise with that\n        // token.\n        resolve(recaptchaToken);\n      } else {\n        // No token available. Listen to token change.\n        var cb = function(token) {\n          if (!token) {\n            // Ignore token expirations.\n            return;\n          }\n          // Remove temporary token change listener.\n          self.removeTokenChangeListener_(cb);\n          // Resolve with new token.\n          resolve(token);\n        };\n        // Add temporary token change listener.\n        self.addTokenChangeListener_(cb);\n        if (self.isInvisible_) {\n          // Execute invisible reCAPTCHA to force a challenge.\n          // This should do nothing if already triggered either by developer or\n          // by a button click.\n          self.grecaptcha_.execute(/** @type {number} */ (self.widgetId_));\n        }\n      }\n    });\n  }));\n};\n\n\n/**\n * Resets the reCAPTCHA widget.\n */\nfireauth.BaseRecaptchaVerifier.prototype.reset = function() {\n  this.checkIfDestroyed_();\n  if (this.widgetId_ !== null) {\n    this.grecaptcha_.reset(this.widgetId_);\n  }\n};\n\n\n/**\n * Throws an error if the reCAPTCHA verifier is already cleared.\n * @private\n */\nfireauth.BaseRecaptchaVerifier.prototype.checkIfDestroyed_ = function() {\n  if (this.destroyed_) {\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.INTERNAL_ERROR,\n        'RecaptchaVerifier instance has been destroyed.');\n  }\n};\n\n\n/**\n * Removes the reCAPTCHA from the DOM.\n */\nfireauth.BaseRecaptchaVerifier.prototype.clear = function() {\n  this.checkIfDestroyed_();\n  this.destroyed_ = true;\n  // Decrement reCAPTCHA instance counter.\n  this.recaptchaLoader_.clearSingleRecaptcha();\n  // Cancel all pending promises.\n  for (var i = 0; i < this.pendingPromises_.length; i++) {\n    this.pendingPromises_[i].cancel(\n        'RecaptchaVerifier instance has been destroyed.');\n  }\n  if (!this.isInvisible_) {\n    goog.dom.removeChildren(goog.dom.getElement(this.container_));\n  }\n};\n\n\n/**\n * Creates the Firebase reCAPTCHA app verifier, publicly available, for the\n * Firebase app provided, used for web phone authentication.\n * This is a subclass of fireauth.BaseRecaptchaVerifier.\n *\n * @param {!Element|string} container The reCAPTCHA container parameter. This\n *     has different meaning depending on whether the reCAPTCHA is hidden or\n *     visible.\n * @param {?Object=} opt_parameters The optional reCAPTCHA parameters.\n * @param {?firebase.app.App=} opt_app The corresponding Firebase app.\n * @constructor\n * @extends {fireauth.BaseRecaptchaVerifier}\n */\nfireauth.RecaptchaVerifier = function(container, opt_parameters, opt_app) {\n  var isTestingMode = false;\n  var apiKey;\n  try {\n    /** @private {!firebase.app.App} The corresponding Firebase app instance. */\n    this.app_ = opt_app || firebase.app();\n  } catch (error) {\n    throw new fireauth.AuthError(\n        fireauth.authenum.Error.ARGUMENT_ERROR,\n        'No firebase.app.App instance is currently initialized.');\n  }\n  // API key is required for web client RPC calls.\n  if (this.app_.options && this.app_.options['apiKey']) {\n    apiKey = this.app_.options['apiKey'];\n  } else {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INVALID_API_KEY);\n  }\n  var self = this;\n  // Construct the language code getter based on the underlying Auth instance.\n  var getLanguageCode = function() {\n    var languageCode;\n    // Get the latest language setting.\n    // reCAPTCHA does not support updating the language of an already\n    // rendered reCAPTCHA. Reloading the SDK with the new hl will break\n    // the existing rendered localized reCAPTCHA. We will need to\n    // document that a new fireauth.BaseRecaptchaVerifier instance needs\n    // to be instantiated after the language is updated. Otherwise, the\n    // old language code will remain active on the existing instance.\n    try {\n      languageCode = self.app_['auth']().getLanguageCode();\n    } catch (e) {\n      languageCode = null;\n    }\n    return languageCode;\n  };\n  // Get the framework version from Auth instance.\n  var frameworkVersion = null;\n  try {\n    frameworkVersion = this.app_['auth']().getFramework();\n  } catch (e) {\n    // Do nothing.\n  }\n  try {\n    isTestingMode =\n        this.app_['auth']()['settings']['appVerificationDisabledForTesting'];\n  } catch (e) {\n    // Do nothing.\n  }\n  // Get the client version based on the Firebase JS version.\n  var clientFullVersion = firebase.SDK_VERSION ?\n      fireauth.util.getClientVersion(\n          fireauth.util.ClientImplementation.JSCORE, firebase.SDK_VERSION,\n          frameworkVersion) :\n      null;\n  // Call the superclass constructor with the computed API key, reCAPTCHA\n  // container, optional parameters, language code getter, Firebase JS client\n  // version and the current client configuration endpoints.\n  fireauth.RecaptchaVerifier.base(this, 'constructor', apiKey,\n      container, opt_parameters, getLanguageCode, clientFullVersion,\n      fireauth.constants.getEndpointConfig(fireauth.constants.clientEndpoint),\n      isTestingMode);\n};\ngoog.inherits(fireauth.RecaptchaVerifier, fireauth.BaseRecaptchaVerifier);\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Provides function argument validation for third-party calls\n * that cannot be validated with Closure compiler.\n */\n\ngoog.provide('fireauth.args');\ngoog.provide('fireauth.args.Argument');\n\ngoog.require('fireauth.Auth');\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.AuthUser');\ngoog.require('fireauth.MultiFactorSession');\ngoog.require('fireauth.authenum.Error');\n\n\n/**\n * Represents an argument to a function. Fields:\n * <ul>\n * <li> name: A label for the argument. For example, the names of the arguments\n *      to a signIn() function might be \"email\" and \"password\".\n * <li> typeLabel: A label for the expected type of the argument, starting with\n *      an article, for example, \"an object\" or \"a valid credential\".\n * <li> optional: Whether or not this argument is optional. Optional arguments\n *      cannot come after non-optional arguments in the input to validate().\n * <li> validator: A function that takes the passed value of this argument\n *      and returns whether the value is valid or not.\n * </ul>\n * @typedef {{\n *   name: string,\n *   typeLabel: string,\n *   optional: boolean,\n *   validator: function (*) : boolean,\n * }}\n */\nfireauth.args.Argument;\n\n\n/**\n * Validates the arguments to a method call and throws an error if invalid. This\n * can be used to validate external calls where the Closure compiler cannot\n * detect errors.\n *\n * Example usage:\n * function greet(recipient, opt_useFormalLanguage) {\n *   fireauth.args.validate('greet', [\n *     fireauth.args.string('recipient'),\n *     fireauth.args.bool('opt_useFormalLanguage', true)\n *   ], arguments);\n *   if (opt_useFormalLanguage) {\n *     console.log('Good day, ' + recipient + '.');\n *   } else {\n *     console.log('Wassup, ' + recipient + '?');\n *   }\n * }\n * greet('Mr. Manager', true); // Prints 'Good day, Mr. Manager.'\n * greet('Billy Bob'); // Prints 'Wassup, Billy Bob?'\n * greet(133); // Throws 'greet failed: First argument \"recipient\" must be a\n *             // valid string.'\n * greet(); // Throws 'greet failed: Expected 1-2 arguments but got 0.'\n * greet('Mr. Manager', true, 'ohno'); // Throws 'greet failed: Expected 1-2\n *                                     // arguments but got 3.'\n *\n * This can also be used to validate setters by passing an additional true\n * argument to fireauth.args.validate. This modifies the error message to be\n * relevant for that setter.\n *\n * @param {string} apiName The name of the method being called, to display in\n *     the error message for debugging purposes.\n * @param {!Array<!fireauth.args.Argument>} expected The expected arguments.\n * @param {!IArrayLike} actual The arguments object of the function whose\n *     parameters we want to validate.\n * @param {boolean=} opt_isSetter Whether the function is a setter which takes\n *     a single argument.\n */\nfireauth.args.validate = function(apiName, expected, actual, opt_isSetter) {\n  // Convert the arguments object into a real array.\n  var actualAsArray = Array.prototype.slice.call(actual);\n  var errorMessage = fireauth.args.validateAndGetMessage_(\n      expected, actualAsArray, opt_isSetter);\n  if (errorMessage) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.ARGUMENT_ERROR,\n        apiName + ' failed: ' + errorMessage);\n  }\n};\n\n\n/**\n * @param {!Array<!fireauth.args.Argument>} expected\n * @param {!Array<*>} actual\n * @param {boolean=} opt_isSetter Whether the function is a setter which takes\n *     a single argument.\n * @return {?string} The error message if there is an error, or otherwise\n *     null.\n * @private\n */\nfireauth.args.validateAndGetMessage_ =\n    function(expected, actual, opt_isSetter) {\n  var minNumArgs = fireauth.args.calcNumRequiredArgs_(expected);\n  var maxNumArgs = expected.length;\n  if (actual.length < minNumArgs || maxNumArgs < actual.length) {\n    return fireauth.args.makeLengthError_(minNumArgs, maxNumArgs,\n        actual.length);\n  }\n\n  for (var i = 0; i < actual.length; i++) {\n    // Argument is optional and undefined is explicitly passed.\n    var optionalUndefined = expected[i].optional && actual[i] === undefined;\n    // Check if invalid argument and the argument is not optional with undefined\n    // passed.\n    if (!expected[i].validator(actual[i]) && !optionalUndefined) {\n      return fireauth.args.makeErrorAtPosition_(i, expected[i], opt_isSetter);\n    }\n  }\n\n  return null;\n};\n\n\n/**\n * @param {!Array<!fireauth.args.Argument>} expected\n * @return {number} The number of required arguments.\n * @private\n */\nfireauth.args.calcNumRequiredArgs_ = function(expected) {\n  var numRequiredArgs = 0;\n  var isOptionalSection = false;\n  for (var i = 0; i < expected.length; i++) {\n    if (expected[i].optional) {\n      isOptionalSection = true;\n    } else {\n      if (isOptionalSection) {\n        throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR,\n            'Argument validator encountered a required argument after an ' +\n            'optional argument.');\n      }\n      numRequiredArgs++;\n    }\n  }\n  return numRequiredArgs;\n};\n\n\n/**\n * @param {number} min The minimum number of arguments to the function,\n *     inclusive.\n * @param {number} max The maximum number of arguments to the function,\n *     inclusive.\n * @param {number} actual The actual number of arguments received.\n * @return {string} The error message.\n * @private\n */\nfireauth.args.makeLengthError_ = function(min, max, actual) {\n  var numExpectedString;\n  if (min == max) {\n    if (min == 1) {\n      numExpectedString = '1 argument';\n    } else {\n      numExpectedString = min + ' arguments';\n    }\n  } else {\n    numExpectedString = min + '-' + max + ' arguments';\n  }\n  return 'Expected ' + numExpectedString + ' but got ' + actual + '.';\n};\n\n\n/**\n * @param {number} position The position at which there was an error.\n * @param {!fireauth.args.Argument} expectedType The expected type of the\n *     argument, which was violated.\n * @param {boolean=} opt_isSetter Whether the function is a setter which takes\n *     a single argument.\n * @return {string} The error message.\n * @private\n */\nfireauth.args.makeErrorAtPosition_ =\n    function(position, expectedType, opt_isSetter) {\n  var ordinal = fireauth.args.makeOrdinal_(position);\n  var argName = expectedType.name ?\n      fireauth.args.quoteString_(expectedType.name) + ' ' : '';\n  // Add support to setters for readable/writable properties which take a\n  // required single argument.\n  var errorPrefix = !!opt_isSetter ? '' : ordinal + ' argument ';\n  return errorPrefix + argName + 'must be ' +\n      expectedType.typeLabel + '.';\n};\n\n\n/** @private {!Array<string>} The first few ordinal numbers. */\nfireauth.args.ORDINAL_NUMBERS_ = ['First', 'Second', 'Third', 'Fourth',\n  'Fifth', 'Sixth', 'Seventh', 'Eighth', 'Ninth'];\n\n\n/**\n * @param {number} cardinal An integer.\n * @return {string} The integer converted to an ordinal number, starting at\n *     \"First\". That is, makeOrdinal_(0) returns \"First\" and makeOrdinal_(1)\n *     returns \"Second\", etc.\n * @private\n */\nfireauth.args.makeOrdinal_ = function(cardinal) {\n  // We only support the first few ordinal numbers. We could provide a more\n  // robust solution, but it is unlikely that a function would need more than\n  // nine arguments.\n  if (cardinal < 0 || cardinal >= fireauth.args.ORDINAL_NUMBERS_.length) {\n    throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR,\n        'Argument validator received an unsupported number of arguments.');\n  }\n  return fireauth.args.ORDINAL_NUMBERS_[cardinal];\n};\n\n\n/**\n * Specifies a string argument.\n * @param {?string=} opt_name The name of the argument.\n * @param {?boolean=} opt_optional Whether or not this argument is optional.\n *     Defaults to false.\n * @return {!fireauth.args.Argument}\n */\nfireauth.args.string = function(opt_name, opt_optional) {\n  return {\n    name: opt_name || '',\n    typeLabel: 'a valid string',\n    optional: !!opt_optional,\n    validator: x => typeof x === 'string'\n  };\n};\n\n\n/**\n * Specifies a boolean argument.\n * @param {?string=} opt_name The name of the argument.\n * @param {?boolean=} opt_optional Whether or not this argument is optional.\n *     Defaults to false.\n * @return {!fireauth.args.Argument}\n */\nfireauth.args.bool = function(opt_name, opt_optional) {\n  return {\n    name: opt_name || '',\n    typeLabel: 'a boolean',\n    optional: !!opt_optional,\n    validator: x => typeof x === 'boolean'\n  };\n};\n\n\n/**\n * Specifies a number argument.\n * @param {?string=} opt_name The name of the argument.\n * @param {?boolean=} opt_optional Whether or not this argument is optional.\n *     Defaults to false.\n * @return {!fireauth.args.Argument}\n */\nfireauth.args.number = function(opt_name, opt_optional) {\n  return {\n    name: opt_name || '',\n    typeLabel: 'a valid number',\n    optional: !!opt_optional,\n    validator: x => typeof x === 'number'\n  };\n};\n\n\n/**\n * Specifies an object argument.\n * @param {?string=} opt_name The name of the argument.\n * @param {?boolean=} opt_optional Whether or not this argument is optional.\n *     Defaults to false.\n * @return {!fireauth.args.Argument}\n */\nfireauth.args.object = function(opt_name, opt_optional) {\n  return {\n    name: opt_name || '',\n    typeLabel: 'a valid object',\n    optional: !!opt_optional,\n    validator: goog.isObject\n  };\n};\n\n\n/**\n * Specifies a function argument.\n * @param {?string=} opt_name The name of the argument.\n * @param {?boolean=} opt_optional Whether or not this argument is optional.\n *     Defaults to false.\n * @return {!fireauth.args.Argument}\n */\nfireauth.args.func = function(opt_name, opt_optional) {\n  return {\n    name: opt_name || '',\n    typeLabel: 'a function',\n    optional: !!opt_optional,\n    validator: x => typeof x === 'function'\n  };\n};\n\n\n/**\n * Specifies a null argument.\n * @param {?string=} opt_name The name of the argument.\n * @param {?boolean=} opt_optional Whether or not this argument is optional.\n *     Defaults to false.\n * @return {!fireauth.args.Argument}\n */\nfireauth.args.null = function(opt_name, opt_optional) {\n  return {\n    name: opt_name || '',\n    typeLabel: 'null',\n    optional: !!opt_optional,\n    validator: x => x === null\n  };\n};\n\n\n/**\n * Specifies an HTML element argument.\n * @param {?string=} opt_name The name of the argument.\n * @param {?boolean=} opt_optional Whether or not this argument is optional.\n *     Defaults to false.\n * @return {!fireauth.args.Argument}\n */\nfireauth.args.element = function(opt_name, opt_optional) {\n   return /** @type {!fireauth.args.Argument} */ ({\n    name: opt_name || '',\n    typeLabel: 'an HTML element',\n    optional: !!opt_optional,\n    validator: /** @type {function(!Element) : boolean} */ (\n        function(element) {\n          return !!(element && element instanceof Element);\n        })\n  });\n};\n\n\n/**\n * Specifies an instance of Firebase Auth.\n * @param {?boolean=} opt_optional Whether or not this argument is optional.\n *     Defaults to false.\n * @return {!fireauth.args.Argument}\n */\nfireauth.args.firebaseAuth = function(opt_optional) {\n  return /** @type {!fireauth.args.Argument} */ ({\n    name: 'auth',\n    typeLabel: 'an instance of Firebase Auth',\n    optional: !!opt_optional,\n    validator: /** @type {function(!fireauth.Auth) : boolean} */ (\n        function(auth) {\n          return !!(auth && auth instanceof fireauth.Auth);\n        })\n  });\n};\n\n\n/**\n * Specifies an instance of Firebase User.\n * @param {?boolean=} opt_optional Whether or not this argument is optional.\n *     Defaults to false.\n * @return {!fireauth.args.Argument}\n */\nfireauth.args.firebaseUser = function(opt_optional) {\n  return /** @type {!fireauth.args.Argument} */ ({\n    name: 'user',\n    typeLabel: 'an instance of Firebase User',\n    optional: !!opt_optional,\n    validator: /** @type {function(!fireauth.AuthUser) : boolean} */ (\n        function(user) {\n          return !!(user && user instanceof fireauth.AuthUser);\n        })\n  });\n};\n\n\n/**\n * Specifies an instance of Firebase App.\n * @param {?boolean=} opt_optional Whether or not this argument is optional.\n *     Defaults to false.\n * @return {!fireauth.args.Argument}\n */\nfireauth.args.firebaseApp = function(opt_optional) {\n  return /** @type {!fireauth.args.Argument} */ ({\n    name: 'app',\n    typeLabel: 'an instance of Firebase App',\n    optional: !!opt_optional,\n    validator: /** @type {function(!firebase.app.App) : boolean} */ (\n        function(app) {\n          return !!(app && app instanceof firebase.app.App);\n        })\n  });\n};\n\n\n/**\n * Specifies an argument that implements the fireauth.AuthCredential interface.\n * @param {?fireauth.idp.ProviderId=} opt_requiredProviderId The required type\n *     of provider.\n * @param {?string=} opt_name The name of the argument.\n * @param {?boolean=} opt_optional Whether or not this argument is optional.\n *     Defaults to false.\n * @return {!fireauth.args.Argument}\n */\nfireauth.args.authCredential =\n    function(opt_requiredProviderId, opt_name, opt_optional) {\n  var name = opt_name ||\n      (opt_requiredProviderId ?\n       opt_requiredProviderId + 'Credential' :\n       'credential');\n  var typeLabel = opt_requiredProviderId ?\n      'a valid ' + opt_requiredProviderId + ' credential' :\n      'a valid credential';\n  return /** @type {!fireauth.args.Argument} */ ({\n    name: name,\n    typeLabel: typeLabel,\n    optional: !!opt_optional,\n    validator: /** @type {function(!fireauth.AuthCredential) : boolean} */ (\n        function(credential) {\n          if (!credential) {\n            return false;\n          }\n          // If opt_requiredProviderId is set, make sure it matches the\n          // credential's providerId.\n          var matchesRequiredProvider = !opt_requiredProviderId ||\n              (credential['providerId'] === opt_requiredProviderId);\n          return !!(credential.getIdTokenProvider && matchesRequiredProvider);\n        })\n  });\n};\n\n\n/**\n * Specifies an argument that implements the fireauth.MultiFactorAssertion\n * interface.\n * @param {?string=} requiredFactorId The required type of second factor.\n * @param {?string=} optionalName The name of the argument.\n * @param {?boolean=} optionalArg Whether or not this argument is optional.\n *     Defaults to false.\n * @return {!fireauth.args.Argument}\n */\nfireauth.args.multiFactorAssertion =\n    function(requiredFactorId, optionalName, optionalArg) {\n  var name = optionalName ||\n      (requiredFactorId ?\n       requiredFactorId + 'MultiFactorAssertion' : 'multiFactorAssertion');\n  var typeLabel = requiredFactorId ?\n      'a valid ' + requiredFactorId + ' multiFactorAssertion' :\n      'a valid multiFactorAssertion';\n  return /** @type {!fireauth.args.Argument} */ ({\n    name: name,\n    typeLabel: typeLabel,\n    optional: !!optionalArg,\n    validator:\n        /** @type {function(!fireauth.MultiFactorAssertion) : boolean} */ (\n            function(assertion) {\n              if (!assertion) {\n                return false;\n              }\n              // If requiredFactorId is set, make sure it matches the\n              // assertion's factorId.\n              var matchesRequiredFactor = !requiredFactorId ||\n                  (assertion['factorId'] === requiredFactorId);\n              return !!(assertion.process && matchesRequiredFactor);\n            })\n  });\n};\n\n\n/**\n * Specifies an argument that implements the fireauth.AuthProvider interface.\n * @param {?string=} opt_name The name of the argument.\n * @param {?boolean=} opt_optional Whether or not this argument is optional.\n *     Defaults to false.\n * @return {!fireauth.args.Argument}\n */\nfireauth.args.authProvider = function(opt_name, opt_optional) {\n  return /** @type {!fireauth.args.Argument} */ ({\n    name: opt_name || 'authProvider',\n    typeLabel: 'a valid Auth provider',\n    optional: !!opt_optional,\n    validator: /** @type {function(!fireauth.AuthProvider) : boolean} */ (\n        function(provider) {\n          return !!(provider &&\n                    provider['providerId'] &&\n                    provider.hasOwnProperty &&\n                    provider.hasOwnProperty('isOAuthProvider'));\n        })\n  });\n};\n\n\n/**\n * Specifies a phone info options argument.\n * @param {?string=} name The name of the argument.\n * @param {?boolean=} optional Whether or not this argument is optional.\n *     Defaults to false.\n * @return {!fireauth.args.Argument}\n */\nfireauth.args.phoneInfoOptions = function(name, optional) {\n  return /** @type {!fireauth.args.Argument} */ ({\n    name: name || 'phoneInfoOptions',\n    typeLabel: 'valid phone info options',\n    optional: !!optional,\n    validator: /** @type {function(!Object) : boolean} */ (\n        function(phoneInfoOptions) {\n          if (!phoneInfoOptions) {\n            return false;\n          }\n          // For multi-factor enrollment, phone number and MFA session should\n          // be provided.\n          if (phoneInfoOptions['session'] &&\n              phoneInfoOptions['phoneNumber']) {\n            return fireauth.args.validateMultiFactorSession_(\n                       phoneInfoOptions['session'],\n                       fireauth.MultiFactorSession.Type.ENROLL) &&\n                   typeof phoneInfoOptions['phoneNumber'] === 'string';\n          // For multi-factor sign-in, phone multi-factor hint and MFA session\n          // are provided.\n          } else if (phoneInfoOptions['session'] &&\n                     phoneInfoOptions['multiFactorHint']) {\n            return fireauth.args.validateMultiFactorSession_(\n                       phoneInfoOptions['session'],\n                       fireauth.MultiFactorSession.Type.SIGN_IN) &&\n                   fireauth.args.validateMultiFactorInfo_(\n                       phoneInfoOptions['multiFactorHint']);\n          // For multi-factor sign-in, phone multi-factor UID and MFA session\n          // are provided.\n          } else if (phoneInfoOptions['session'] &&\n                     phoneInfoOptions['multiFactorUid']) {\n            return fireauth.args.validateMultiFactorSession_(\n                       phoneInfoOptions['session'],\n                       fireauth.MultiFactorSession.Type.SIGN_IN) &&\n                   typeof phoneInfoOptions['multiFactorUid'] === 'string';\n          // For single-factor sign-in, only phone number needs to be provided.\n          } else if (phoneInfoOptions['phoneNumber']) {\n            return typeof phoneInfoOptions['phoneNumber'] === 'string';\n          }\n          return false;\n        })\n  });\n};\n\n\n/**\n * @param {*} session The multi-factor session object.\n * @param {!fireauth.MultiFactorSession.Type} type The session type.\n * @return {boolean} Whether the seesion is a valid multi-factor session.\n * @private\n */\nfireauth.args.validateMultiFactorSession_ = function(session, type) {\n  return goog.isObject(session) && typeof session.type === 'string' &&\n      session.type === type &&\n      typeof session.getRawSession === 'function';\n};\n\n\n/**\n * @param {*} info The multi-factor info object.\n * @return {boolean} Whether the info is a valid multi-factor info.\n * @private\n */\nfireauth.args.validateMultiFactorInfo_ = function(info) {\n  return goog.isObject(info) && typeof info['uid'] === 'string';\n};\n\n\n/**\n * Specifies an argument that implements the fireauth.MultiFactorInfo\n * interface.\n * @param {?string=} name The name of the argument.\n * @param {?boolean=} optional Whether or not this argument is optional.\n *     Defaults to false.\n * @return {!fireauth.args.Argument}\n */\nfireauth.args.multiFactorInfo = function(name, optional) {\n  return /** @type {!fireauth.args.Argument} */ ({\n    name: name || 'multiFactorInfo',\n    typeLabel: 'a valid multiFactorInfo',\n    optional: !!optional,\n    validator: fireauth.args.validateMultiFactorInfo_\n  });\n};\n\n\n/**\n * Specifies an argument that implements the firebase.auth.ApplicationVerifier\n * interface.\n * @param {?boolean=} opt_optional Whether or not this argument is optional.\n *     Defaults to false.\n * @return {!fireauth.args.Argument}\n */\nfireauth.args.applicationVerifier = function(opt_optional) {\n  return /** @type {!fireauth.args.Argument} */ ({\n    name: 'applicationVerifier',\n    typeLabel: 'an implementation of firebase.auth.ApplicationVerifier',\n    optional: !!opt_optional,\n    validator:\n        /** @type {function(!firebase.auth.ApplicationVerifier) : boolean} */ (\n        function(applicationVerifier) {\n          return !!(applicationVerifier &&\n                    typeof applicationVerifier.type === 'string' &&\n                    typeof applicationVerifier.verify === 'function');\n        })\n  });\n};\n\n\n/**\n * Specifies an argument that can be either of two argument types.\n * @param {!fireauth.args.Argument} optionA\n * @param {!fireauth.args.Argument} optionB\n * @param {?string=} opt_name The name of the argument.\n * @param {?boolean=} opt_optional Whether or not this argument is optional.\n *     Defaults to false.\n * @return {!fireauth.args.Argument}\n */\nfireauth.args.or = function(optionA, optionB, opt_name, opt_optional) {\n  return {\n    name: opt_name || '',\n    typeLabel: optionA.typeLabel + ' or ' + optionB.typeLabel,\n    optional: !!opt_optional,\n    validator: function(value) {\n      return optionA.validator(value) || optionB.validator(value);\n    }\n  };\n};\n\n\n/**\n * @param {string} str\n * @return {string} The string surrounded with quotes.\n * @private\n */\nfireauth.args.quoteString_ = function(str) {\n  return '\"' + str + '\"';\n};\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Provides utilities for exporting public APIs, with error\n *     checking.\n */\n\ngoog.provide('fireauth.exportlib');\ngoog.provide('fireauth.exportlib.ExportedMethod');\n\ngoog.require('fireauth.args');\n\n\n/**\n * Type constant for Firebase Auth.\n * @const {string}\n */\nfireauth.exportlib.AUTH_TYPE = 'auth';\n\n\n/**\n * Represents an exported method, with the exported name of the method and the\n * expected arguments to that method.\n * @typedef {{\n *   name: string,\n *   args: (Array<!fireauth.args.Argument>|null|undefined)\n * }}\n */\nfireauth.exportlib.ExportedMethod;\n\n\n/**\n * Represents an exported property, with the exported name of the property and\n * the expected argument to the setter of this property.\n * @typedef {{\n *   name: string,\n *   arg: !fireauth.args.Argument\n * }}\n */\nfireauth.exportlib.ExportedProperty;\n\n\n/**\n * Exports prototype methods of an object.\n * @param {!Object} protObj The prototype of an object.\n * @param {!Object<string, fireauth.exportlib.ExportedMethod>} fnMap The map of\n *     prototype functions to their export name and expected arguments.\n */\nfireauth.exportlib.exportPrototypeMethods = function(protObj, fnMap) {\n  // This method exports methods by aliasing the unobfuscated function name\n  // (specified as a string in the \"name\" field of ExportedMethod) to the\n  // obfuscated function name (specified as a key of the fnMap object).\n  //\n  // To give a concrete example, let's say that we have this method:\n  // fireauth.Auth.prototype.fetchProvidersForEmail = function() { ... };\n  //\n  // In the exports file, we export as follows:\n  // fireauth.exportlib.exportPrototypeMethods(fireauth.Auth.prototype, {\n  //   fetchProvidersForEmail: {name: 'fetchProvidersForEmail', args: ...}\n  // });\n  //\n  // When the compiler obfuscates the code, the code above will become something\n  // like this:\n  // fireauth.Auth.prototype.qZ = function() { ... };\n  // fireauth.exportlib.exportPrototypeMethods(fireauth.Auth.prototype, {\n  //   qZ: {name: 'fetchProvidersForEmail', args: ...}\n  // });\n  //\n  // (Of course, fireauth.Auth and fireauth.exportlib.exportPrototypeMethods\n  // would also be obfuscated). Note that the key in fnMap is obfuscated but the\n  // \"name\" field in the ExportedMethod is not. Now, exportPrototypeMethods can\n  // export fetchProvidersForEmail by reading the key (\"qZ\") and the \"name\"\n  // field (\"fetchProvidersForEmail\") and essentially executing this:\n  // fireauth.Auth.prototype['fetchProvidersForEmail'] =\n  //     fireauth.Auth.prototype['qZ'];\n  for (var obfuscatedFnName in fnMap) {\n    var unobfuscatedFnName = fnMap[obfuscatedFnName].name;\n    protObj[unobfuscatedFnName] =\n        fireauth.exportlib.wrapMethodWithArgumentVerifier_(\n        unobfuscatedFnName, protObj[obfuscatedFnName],\n        fnMap[obfuscatedFnName].args);\n  }\n};\n\n\n/**\n * Exports properties of an object. See the docs for exportPrototypeMethods for\n * more information about how this works.\n * @param {!Object} protObj The prototype of an object.\n * @param {!Object<string, !fireauth.exportlib.ExportedProperty>} propMap The\n *     map of properties to their export names.\n */\nfireauth.exportlib.exportPrototypeProperties = function(protObj, propMap) {\n  for (var obfuscatedPropName in propMap) {\n    var unobfuscatedPropName = propMap[obfuscatedPropName].name;\n    // Don't alias a property to itself.\n    // Downside is that argument validation will not be possible. For now, to\n    // get around it, ensure unobfuscated property names are different\n    // than the corresponding obfuscated property names.\n    if (unobfuscatedPropName === obfuscatedPropName) {\n      continue;\n    }\n    /**\n     * @this {!Object}\n     * @param {string} obfuscatedPropName The obfuscated property name.\n     * @return {*} The value of the property.\n     */\n    var getter = function(obfuscatedPropName) {\n      return this[obfuscatedPropName];\n    };\n    /**\n     * @this {!Object}\n     * @param {string} unobfuscatedPropName The unobfuscated property name.\n     * @param {string} obfuscatedPropName The obfuscated property name.\n     * @param {!fireauth.args.Argument} expectedArg The expected argument to the\n     *     setter of this property.\n     * @param {*} value The new value of the property.\n     */\n    var setter = function(unobfuscatedPropName, obfuscatedPropName,\n                          expectedArg, value) {\n      // Validate the argument before setting it.\n      fireauth.args.validate(\n          unobfuscatedPropName, [expectedArg], [value], true);\n      this[obfuscatedPropName] = value;\n    };\n    // Get the expected argument.\n    var expectedArg = propMap[obfuscatedPropName].arg;\n    Object.defineProperty(protObj, unobfuscatedPropName, {\n      /**\n       * @this {!Object}\n       * @return {*} The value of the property.\n       */\n      get: goog.partial(getter, obfuscatedPropName),\n      /**\n       * @this {!Object}\n       * @param {*} value The new value of the property.\n       */\n      set: goog.partial(setter, unobfuscatedPropName, obfuscatedPropName,\n                        expectedArg),\n      enumerable: true\n    });\n  }\n};\n\n\n/**\n * Export a static method as a public API.\n * @param {!Object} parentObj The parent object to patch.\n * @param {string} name The public name of the method.\n * @param {!Function} func The method.\n * @param {?Array<!fireauth.args.Argument>=} opt_expectedArgs The expected\n *     arguments to the method.\n */\nfireauth.exportlib.exportFunction = function(parentObj, name, func,\n    opt_expectedArgs) {\n  parentObj[name] = fireauth.exportlib.wrapMethodWithArgumentVerifier_(\n      name, func, opt_expectedArgs);\n};\n\n\n/**\n * Wraps a method with a function that first verifies the arguments to the\n * method and then calls the original method.\n * @param {string} methodName The name of the method, which will be displayed\n *     on the error message if the arguments are not valid.\n * @param {!Function} method The method to be wrapped.\n * @param {?Array<!fireauth.args.Argument>=} opt_expectedArgs The expected\n *     arguments.\n * @return {!Function} The wrapped method.\n * @private\n */\nfireauth.exportlib.wrapMethodWithArgumentVerifier_ = function(methodName,\n    method, opt_expectedArgs) {\n  if (!opt_expectedArgs) {\n    return method;\n  }\n  var shortName = fireauth.exportlib.extractMethodNameFromFullPath_(methodName);\n  var wrapper = function() {\n    var argumentsAsArray = Array.prototype.slice.call(arguments);\n    fireauth.args.validate(shortName,\n        /** @type {!Array<!fireauth.args.Argument>} */ (opt_expectedArgs),\n        argumentsAsArray);\n    return method.apply(this, argumentsAsArray);\n  };\n  // Reattach all static stuff to wrapper.\n  for (var key in method) {\n    wrapper[key] = method[key];\n  }\n  // Reattach all prototype stuff to wrapper.\n  for (var key in method.prototype) {\n    wrapper.prototype[key] = method.prototype[key];\n  }\n  // Return wrapper with all of method's static and prototype methods and\n  // properties.\n  return wrapper;\n};\n\n\n/**\n * From a full path to a method (e.g. \"fireauth.GoogleAuthProvider.credential\"),\n * get just the method name (\"credential\").\n * @param {string} path The full path.\n * @return {string} The method name.\n * @private\n */\nfireauth.exportlib.extractMethodNameFromFullPath_ = function(path) {\n  var parts = path.split('.');\n  return parts[parts.length - 1];\n};\n","/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\ngoog.provide('fireauth.exports');\n\ngoog.require('fireauth.ActionCodeInfo');\ngoog.require('fireauth.ActionCodeURL');\ngoog.require('fireauth.Auth');\ngoog.require('fireauth.AuthCredential');\ngoog.require('fireauth.AuthError');\ngoog.require('fireauth.AuthErrorWithCredential');\ngoog.require('fireauth.AuthSettings');\ngoog.require('fireauth.AuthUser');\ngoog.require('fireauth.ConfirmationResult');\ngoog.require('fireauth.EmailAuthCredential');\ngoog.require('fireauth.EmailAuthProvider');\ngoog.require('fireauth.FacebookAuthProvider');\ngoog.require('fireauth.GRecaptchaMockFactory');\ngoog.require('fireauth.GithubAuthProvider');\ngoog.require('fireauth.GoogleAuthProvider');\ngoog.require('fireauth.InvalidOriginError');\ngoog.require('fireauth.MultiFactorError');\ngoog.require('fireauth.MultiFactorResolver');\ngoog.require('fireauth.MultiFactorUser');\ngoog.require('fireauth.OAuthCredential');\ngoog.require('fireauth.OAuthProvider');\ngoog.require('fireauth.PhoneAuthCredential');\ngoog.require('fireauth.PhoneAuthProvider');\ngoog.require('fireauth.PhoneMultiFactorGenerator');\ngoog.require('fireauth.RecaptchaVerifier');\ngoog.require('fireauth.SAMLAuthCredential');\ngoog.require('fireauth.SAMLAuthProvider');\ngoog.require('fireauth.TwitterAuthProvider');\ngoog.require('fireauth.args');\ngoog.require('fireauth.authStorage.Persistence');\ngoog.require('fireauth.exportlib');\ngoog.require('fireauth.grecaptcha');\ngoog.require('fireauth.idp.ProviderId');\ngoog.require('goog.Promise');\n\n/** @define {string} */\nconst AUTH_NPM_PACKAGE_VERSION = '';\n\nfireauth.exportlib.exportPrototypeMethods(\n    fireauth.Auth.prototype, {\n      applyActionCode: {\n        name: 'applyActionCode',\n        args: [fireauth.args.string('code')]\n      },\n      checkActionCode: {\n        name: 'checkActionCode',\n        args: [fireauth.args.string('code')]\n      },\n      confirmPasswordReset: {\n        name: 'confirmPasswordReset',\n        args: [\n          fireauth.args.string('code'),\n          fireauth.args.string('newPassword')\n        ]\n      },\n      createUserWithEmailAndPassword: {\n        name: 'createUserWithEmailAndPassword',\n        args: [fireauth.args.string('email'), fireauth.args.string('password')]\n      },\n      fetchSignInMethodsForEmail: {\n        name: 'fetchSignInMethodsForEmail',\n        args: [fireauth.args.string('email')]\n      },\n      getRedirectResult: {\n        name: 'getRedirectResult',\n        args: []\n      },\n      isSignInWithEmailLink: {\n        name: 'isSignInWithEmailLink',\n        args: [fireauth.args.string('emailLink')]\n      },\n      onAuthStateChanged: {\n        name: 'onAuthStateChanged',\n        args: [\n          fireauth.args.or(\n              fireauth.args.object(),\n              fireauth.args.func(),\n              'nextOrObserver'),\n          fireauth.args.func('opt_error', true),\n          fireauth.args.func('opt_completed', true)\n        ]\n      },\n      onIdTokenChanged: {\n        name: 'onIdTokenChanged',\n        args: [\n          fireauth.args.or(\n              fireauth.args.object(),\n              fireauth.args.func(),\n              'nextOrObserver'),\n          fireauth.args.func('opt_error', true),\n          fireauth.args.func('opt_completed', true)\n        ]\n      },\n      sendPasswordResetEmail: {\n        name: 'sendPasswordResetEmail',\n        args: [\n          fireauth.args.string('email'),\n          fireauth.args.or(\n              fireauth.args.object('opt_actionCodeSettings', true),\n              fireauth.args.null(null, true),\n              'opt_actionCodeSettings',\n              true)\n        ]\n      },\n      sendSignInLinkToEmail: {\n        name: 'sendSignInLinkToEmail',\n        args: [\n          fireauth.args.string('email'),\n          fireauth.args.object('actionCodeSettings')\n        ]\n      },\n      setPersistence: {\n        name: 'setPersistence',\n        args:  [fireauth.args.string('persistence')]\n      },\n      signInAndRetrieveDataWithCredential: {\n        name: 'signInAndRetrieveDataWithCredential',\n        args: [fireauth.args.authCredential()]\n      },\n      signInAnonymously: {\n        name: 'signInAnonymously',\n        args: []\n      },\n      signInWithCredential: {\n        name: 'signInWithCredential',\n        args: [fireauth.args.authCredential()]\n      },\n      signInWithCustomToken: {\n        name: 'signInWithCustomToken',\n        args: [fireauth.args.string('token')]\n      },\n      signInWithEmailAndPassword: {\n        name: 'signInWithEmailAndPassword',\n        args: [fireauth.args.string('email'), fireauth.args.string('password')]\n      },\n      signInWithEmailLink: {\n        name: 'signInWithEmailLink',\n        args: [\n          fireauth.args.string('email'), fireauth.args.string('emailLink', true)\n        ]\n      },\n      signInWithPhoneNumber: {\n        name: 'signInWithPhoneNumber',\n        args: [\n          fireauth.args.string('phoneNumber'),\n          fireauth.args.applicationVerifier()\n        ]\n      },\n      signInWithPopup: {\n        name: 'signInWithPopup',\n        args: [fireauth.args.authProvider()]\n      },\n      signInWithRedirect: {\n        name: 'signInWithRedirect',\n        args: [fireauth.args.authProvider()]\n      },\n      updateCurrentUser: {\n        name: 'updateCurrentUser',\n        args: [\n          fireauth.args.or(\n            fireauth.args.firebaseUser(),\n            fireauth.args.null(),\n            'user')\n        ]\n      },\n      signOut: {\n        name: 'signOut',\n        args: []\n      },\n      toJSON: {\n        name: 'toJSON',\n        // This shouldn't take an argument but a blank string is being passed\n        // on JSON.stringify and causing this to fail with an argument error.\n        // So allow an optional string.\n        args: [fireauth.args.string(null, true)]\n      },\n      useDeviceLanguage: {\n        name: 'useDeviceLanguage',\n        args: []\n      },\n      useEmulator: {\n        name: 'useEmulator',\n        args: [\n          fireauth.args.string('url'),\n          fireauth.args.object('options', true)\n        ]\n      },\n      verifyPasswordResetCode: {\n        name: 'verifyPasswordResetCode',\n        args: [fireauth.args.string('code')]\n      }\n    });\n\nfireauth.exportlib.exportPrototypeProperties(\n    fireauth.Auth.prototype, {\n      'lc': {\n        name: 'languageCode',\n        arg: fireauth.args.or(\n            fireauth.args.string(),\n            fireauth.args.null(),\n            'languageCode')\n      },\n      'ti': {\n        name: 'tenantId',\n        arg: fireauth.args.or(\n            fireauth.args.string(),\n            fireauth.args.null(),\n            'tenantId')\n      },\n      // emulatorConfig is omitted here as it is readonly and therefore does not\n      // need argument validation.\n    });\n\n// Exports firebase.auth.Auth.Persistence.\nfireauth.Auth['Persistence'] = fireauth.authStorage.Persistence;\nfireauth.Auth['Persistence']['LOCAL'] = fireauth.authStorage.Persistence.LOCAL;\nfireauth.Auth['Persistence']['SESSION'] =\n    fireauth.authStorage.Persistence.SESSION;\nfireauth.Auth['Persistence']['NONE'] = fireauth.authStorage.Persistence.NONE;\n\n\nfireauth.exportlib.exportPrototypeMethods(\n    fireauth.AuthUser.prototype, {\n      'delete': {\n        name: 'delete',\n        args: []\n      },\n      getIdTokenResult: {\n        name: 'getIdTokenResult',\n        args: [fireauth.args.bool('opt_forceRefresh', true)]\n      },\n      getIdToken: {\n        name: 'getIdToken',\n        args: [fireauth.args.bool('opt_forceRefresh', true)]\n      },\n      linkAndRetrieveDataWithCredential: {\n        name: 'linkAndRetrieveDataWithCredential',\n        args: [fireauth.args.authCredential()]\n      },\n      linkWithCredential: {\n        name: 'linkWithCredential',\n        args: [fireauth.args.authCredential()]\n      },\n      linkWithPhoneNumber: {\n        name: 'linkWithPhoneNumber',\n        args: [\n          fireauth.args.string('phoneNumber'),\n          fireauth.args.applicationVerifier()\n        ]\n      },\n      linkWithPopup: {\n        name: 'linkWithPopup',\n        args: [fireauth.args.authProvider()]\n      },\n      linkWithRedirect: {\n        name: 'linkWithRedirect',\n        args: [fireauth.args.authProvider()]\n      },\n      reauthenticateAndRetrieveDataWithCredential: {\n        name: 'reauthenticateAndRetrieveDataWithCredential',\n        args: [fireauth.args.authCredential()]\n      },\n      reauthenticateWithCredential: {\n        name: 'reauthenticateWithCredential',\n        args: [fireauth.args.authCredential()]\n      },\n      reauthenticateWithPhoneNumber: {\n        name: 'reauthenticateWithPhoneNumber',\n        args: [\n          fireauth.args.string('phoneNumber'),\n          fireauth.args.applicationVerifier()\n        ]\n      },\n      reauthenticateWithPopup: {\n        name: 'reauthenticateWithPopup',\n        args: [fireauth.args.authProvider()]\n      },\n      reauthenticateWithRedirect: {\n        name: 'reauthenticateWithRedirect',\n        args: [fireauth.args.authProvider()]\n      },\n      reload: {\n        name: 'reload',\n        args: []\n      },\n      sendEmailVerification: {\n        name: 'sendEmailVerification',\n        args: [\n          fireauth.args.or(\n              fireauth.args.object('opt_actionCodeSettings', true),\n              fireauth.args.null(null, true),\n              'opt_actionCodeSettings',\n              true)\n        ]\n      },\n      toJSON: {\n        name: 'toJSON',\n        // This shouldn't take an argument but a blank string is being passed\n        // on JSON.stringify and causing this to fail with an argument error.\n        // So allow an optional string.\n        args: [fireauth.args.string(null, true)]\n      },\n      unlink: {\n        name: 'unlink',\n        args: [fireauth.args.string('provider')]\n      },\n      updateEmail: {\n        name: 'updateEmail',\n        args: [fireauth.args.string('email')]\n      },\n      updatePassword: {\n        name: 'updatePassword',\n        args: [fireauth.args.string('password')]\n      },\n      updatePhoneNumber: {\n        name: 'updatePhoneNumber',\n        args: [fireauth.args.authCredential(fireauth.idp.ProviderId.PHONE)]\n      },\n      updateProfile: {\n        name: 'updateProfile',\n        args: [fireauth.args.object('profile')]\n      },\n      verifyBeforeUpdateEmail: {\n        name: 'verifyBeforeUpdateEmail',\n        args: [\n          fireauth.args.string('email'),\n          fireauth.args.or(\n              fireauth.args.object('opt_actionCodeSettings', true),\n              fireauth.args.null(null, true),\n              'opt_actionCodeSettings',\n              true)\n        ]\n      }\n    });\n\n// Ensure internal grecaptcha mock API do not get obfuscated.\nfireauth.exportlib.exportPrototypeMethods(\n    fireauth.GRecaptchaMockFactory.prototype, {\n      execute: {\n        name: 'execute'\n      },\n      render: {\n        name: 'render'\n      },\n      reset: {\n        name: 'reset'\n      },\n      getResponse: {\n        name: 'getResponse'\n      }\n    });\n\nfireauth.exportlib.exportPrototypeMethods(\n    fireauth.grecaptcha.prototype, {\n      execute: {\n        name: 'execute'\n      },\n      render: {\n        name: 'render'\n      },\n      reset: {\n        name: 'reset'\n      },\n      getResponse: {\n        name: 'getResponse'\n      }\n    });\n\nfireauth.exportlib.exportPrototypeMethods(\n    goog.Promise.prototype, {\n      thenAlways: {\n        name: 'finally'\n      },\n      thenCatch: {\n        name: 'catch'\n      },\n      then: {\n        name: 'then'\n      }\n    });\n\nfireauth.exportlib.exportPrototypeProperties(\n    fireauth.AuthSettings.prototype, {\n      'appVerificationDisabled': {\n        name: 'appVerificationDisabledForTesting',\n        arg: fireauth.args.bool('appVerificationDisabledForTesting')\n      }\n    });\n\nfireauth.exportlib.exportPrototypeMethods(\n   fireauth.ConfirmationResult.prototype, {\n      confirm: {\n        name: 'confirm',\n        args: [\n          fireauth.args.string('verificationCode')\n        ]\n      }\n    });\n\nfireauth.exportlib.exportFunction(\n    fireauth.AuthCredential, 'fromJSON',\n    fireauth.AuthCredential.fromPlainObject, [\n      fireauth.args.or(fireauth.args.string(), fireauth.args.object(), 'json')\n    ]);\n\nfireauth.exportlib.exportFunction(\n    fireauth.EmailAuthProvider, 'credential',\n    fireauth.EmailAuthProvider.credential, [\n      fireauth.args.string('email'),\n      fireauth.args.string('password')\n    ]);\n\nfireauth.exportlib.exportPrototypeMethods(\n    fireauth.EmailAuthCredential.prototype, {\n     toPlainObject: {\n        name: 'toJSON',\n        // This shouldn't take an argument but a blank string is being passed\n        // on JSON.stringify and causing this to fail with an argument error.\n        // So allow an optional string.\n        args: [fireauth.args.string(null, true)]\n      }\n    });\n\nfireauth.exportlib.exportPrototypeMethods(\n    fireauth.FacebookAuthProvider.prototype, {\n      addScope: {\n        name: 'addScope',\n        args: [fireauth.args.string('scope')]\n      },\n      setCustomParameters: {\n        name: 'setCustomParameters',\n        args: [fireauth.args.object('customOAuthParameters')]\n      }\n    });\nfireauth.exportlib.exportFunction(\n    fireauth.FacebookAuthProvider, 'credential',\n    fireauth.FacebookAuthProvider.credential, [\n      fireauth.args.or(fireauth.args.string(), fireauth.args.object(),\n          'token')\n    ]);\nfireauth.exportlib.exportFunction(\n    fireauth.EmailAuthProvider, 'credentialWithLink',\n    fireauth.EmailAuthProvider.credentialWithLink, [\n      fireauth.args.string('email'),\n      fireauth.args.string('emailLink')\n    ]);\n\nfireauth.exportlib.exportPrototypeMethods(\n    fireauth.GithubAuthProvider.prototype, {\n      addScope: {\n        name: 'addScope',\n        args: [fireauth.args.string('scope')]\n      },\n      setCustomParameters: {\n        name: 'setCustomParameters',\n        args: [fireauth.args.object('customOAuthParameters')]\n      }\n    });\nfireauth.exportlib.exportFunction(\n    fireauth.GithubAuthProvider, 'credential',\n    fireauth.GithubAuthProvider.credential, [\n      fireauth.args.or(fireauth.args.string(), fireauth.args.object(),\n          'token')\n    ]);\n\nfireauth.exportlib.exportPrototypeMethods(\n    fireauth.GoogleAuthProvider.prototype, {\n      addScope: {\n        name: 'addScope',\n        args: [fireauth.args.string('scope')]\n      },\n      setCustomParameters: {\n        name: 'setCustomParameters',\n        args: [fireauth.args.object('customOAuthParameters')]\n      }\n    });\nfireauth.exportlib.exportFunction(\n    fireauth.GoogleAuthProvider, 'credential',\n    fireauth.GoogleAuthProvider.credential, [\n      fireauth.args.or(fireauth.args.string(),\n          fireauth.args.or(fireauth.args.object(), fireauth.args.null()),\n          'idToken'),\n      fireauth.args.or(fireauth.args.string(), fireauth.args.null(),\n          'accessToken', true)\n    ]);\n\nfireauth.exportlib.exportPrototypeMethods(\n    fireauth.TwitterAuthProvider.prototype, {\n      setCustomParameters: {\n        name: 'setCustomParameters',\n        args: [fireauth.args.object('customOAuthParameters')]\n      }\n    });\nfireauth.exportlib.exportFunction(\n    fireauth.TwitterAuthProvider, 'credential',\n    fireauth.TwitterAuthProvider.credential, [\n      fireauth.args.or(fireauth.args.string(), fireauth.args.object(),\n          'token'),\n      fireauth.args.string('secret', true)\n    ]);\nfireauth.exportlib.exportPrototypeMethods(\n    fireauth.OAuthProvider.prototype, {\n      addScope: {\n        name: 'addScope',\n        args: [fireauth.args.string('scope')]\n      },\n      credential: {\n        name: 'credential',\n        args: [\n          fireauth.args.or(\n              fireauth.args.string(),\n              fireauth.args.or(fireauth.args.object(), fireauth.args.null()),\n              'optionsOrIdToken'),\n          fireauth.args.or(fireauth.args.string(), fireauth.args.null(),\n              'accessToken', true)\n        ]\n      },\n      setCustomParameters: {\n        name: 'setCustomParameters',\n        args: [fireauth.args.object('customOAuthParameters')]\n      }\n    });\n\nfireauth.exportlib.exportPrototypeMethods(\n    fireauth.OAuthCredential.prototype, {\n     toPlainObject: {\n        name: 'toJSON',\n        // This shouldn't take an argument but a blank string is being passed\n        // on JSON.stringify and causing this to fail with an argument error.\n        // So allow an optional string.\n        args: [fireauth.args.string(null, true)]\n      }\n    });\n\nfireauth.exportlib.exportPrototypeMethods(\n    fireauth.SAMLAuthCredential.prototype, {\n     toPlainObject: {\n        name: 'toJSON',\n        // This shouldn't take an argument but a blank string is being passed\n        // on JSON.stringify and causing this to fail with an argument error.\n        // So allow an optional string.\n        args: [fireauth.args.string(null, true)]\n      }\n    });\n\nfireauth.exportlib.exportFunction(\n    fireauth.PhoneAuthProvider, 'credential',\n    fireauth.PhoneAuthProvider.credential, [\n      fireauth.args.string('verificationId'),\n      fireauth.args.string('verificationCode')\n    ]);\nfireauth.exportlib.exportPrototypeMethods(\n    fireauth.PhoneAuthProvider.prototype, {\n      verifyPhoneNumber: {\n        name: 'verifyPhoneNumber',\n        args: [\n          fireauth.args.or(\n              fireauth.args.string(),\n              fireauth.args.phoneInfoOptions(),\n              'phoneInfoOptions'),\n          fireauth.args.applicationVerifier()\n        ]\n      }\n    });\n\nfireauth.exportlib.exportPrototypeMethods(\n    fireauth.PhoneAuthCredential.prototype, {\n     toPlainObject: {\n        name: 'toJSON',\n        // This shouldn't take an argument but a blank string is being passed\n        // on JSON.stringify and causing this to fail with an argument error.\n        // So allow an optional string.\n        args: [fireauth.args.string(null, true)]\n      }\n    });\n\nfireauth.exportlib.exportPrototypeMethods(\n    fireauth.AuthError.prototype, {\n      toJSON: {\n        name: 'toJSON',\n        // This shouldn't take an argument but a blank string is being passed\n        // on JSON.stringify and causing this to fail with an argument error.\n        // So allow an optional string.\n        args: [fireauth.args.string(null, true)]\n      }\n    });\nfireauth.exportlib.exportPrototypeMethods(\n    fireauth.AuthErrorWithCredential.prototype, {\n      toJSON: {\n        name: 'toJSON',\n        // This shouldn't take an argument but a blank string is being passed\n        // on JSON.stringify and causing this to fail with an argument error.\n        // So allow an optional string.\n        args: [fireauth.args.string(null, true)]\n      }\n    });\nfireauth.exportlib.exportPrototypeMethods(\n    fireauth.InvalidOriginError.prototype, {\n      toJSON: {\n        name: 'toJSON',\n        // This shouldn't take an argument but a blank string is being passed\n        // on JSON.stringify and causing this to fail with an argument error.\n        // So allow an optional string.\n        args: [fireauth.args.string(null, true)]\n      }\n    });\nfireauth.exportlib.exportPrototypeMethods(\n    fireauth.MultiFactorError.prototype, {\n      toJSON: {\n        name: 'toJSON',\n        // This shouldn't take an argument but a blank string is being passed\n        // on JSON.stringify and causing this to fail with an argument error.\n        // So allow an optional string.\n        args: [fireauth.args.string(null, true)]\n      }\n    });\nfireauth.exportlib.exportPrototypeMethods(\n    fireauth.MultiFactorResolver.prototype, {\n      resolveSignIn: {\n        name: 'resolveSignIn',\n        args: [fireauth.args.multiFactorAssertion()]\n      }\n    });\nfireauth.exportlib.exportPrototypeMethods(\n    fireauth.MultiFactorUser.prototype, {\n      getSession: {\n        name: 'getSession',\n        args: []\n      },\n      enroll: {\n        name: 'enroll',\n        args: [\n          fireauth.args.multiFactorAssertion(),\n          fireauth.args.string('displayName', true)\n        ]\n      },\n      unenroll: {\n        name: 'unenroll',\n        args: [\n          fireauth.args.or(\n              fireauth.args.multiFactorInfo(),\n              fireauth.args.string(),\n              'multiFactorInfoIdentifier')\n        ]\n      }\n    });\n\nfireauth.exportlib.exportPrototypeMethods(\n    fireauth.RecaptchaVerifier.prototype, {\n      clear: {\n        name: 'clear',\n        args: []\n      },\n      render: {\n        name: 'render',\n        args: []\n      },\n      verify: {\n        name: 'verify',\n        args: []\n      }\n    });\n\nfireauth.exportlib.exportFunction(\n    fireauth.ActionCodeURL, 'parseLink',\n    fireauth.ActionCodeURL.parseLink, [fireauth.args.string('link')]);\n\nfireauth.exportlib.exportFunction(\n    fireauth.PhoneMultiFactorGenerator, 'assertion',\n    fireauth.PhoneMultiFactorGenerator.assertion,\n    [fireauth.args.authCredential(fireauth.idp.ProviderId.PHONE)]);\n\n\n(function() {\n  if (typeof firebase === 'undefined' || !firebase.INTERNAL ||\n      !firebase.INTERNAL.registerComponent) {\n    throw new Error('Cannot find the firebase namespace; be sure to include ' +\n        'firebase-app.js before this library.');\n  } else {\n    var namespace = {\n      // Exports firebase.auth.ActionCodeInfo.Operation.\n      'ActionCodeInfo': {\n        'Operation': {\n          'EMAIL_SIGNIN': fireauth.ActionCodeInfo.Operation.EMAIL_SIGNIN,\n          'PASSWORD_RESET': fireauth.ActionCodeInfo.Operation.PASSWORD_RESET,\n          'RECOVER_EMAIL': fireauth.ActionCodeInfo.Operation.RECOVER_EMAIL,\n          'REVERT_SECOND_FACTOR_ADDITION':\n              fireauth.ActionCodeInfo.Operation.REVERT_SECOND_FACTOR_ADDITION,\n          'VERIFY_AND_CHANGE_EMAIL':\n              fireauth.ActionCodeInfo.Operation.VERIFY_AND_CHANGE_EMAIL,\n          'VERIFY_EMAIL': fireauth.ActionCodeInfo.Operation.VERIFY_EMAIL\n        }\n      },\n      'Auth': fireauth.Auth,\n      'AuthCredential': fireauth.AuthCredential,\n      'Error': fireauth.AuthError\n    };\n    fireauth.exportlib.exportFunction(namespace,\n        'EmailAuthProvider', fireauth.EmailAuthProvider, []);\n    fireauth.exportlib.exportFunction(namespace,\n        'FacebookAuthProvider', fireauth.FacebookAuthProvider, []);\n    fireauth.exportlib.exportFunction(namespace,\n        'GithubAuthProvider', fireauth.GithubAuthProvider, []);\n    fireauth.exportlib.exportFunction(namespace,\n        'GoogleAuthProvider', fireauth.GoogleAuthProvider, []);\n    fireauth.exportlib.exportFunction(namespace,\n        'TwitterAuthProvider', fireauth.TwitterAuthProvider, []);\n    fireauth.exportlib.exportFunction(namespace,\n        'OAuthProvider', fireauth.OAuthProvider, [\n          fireauth.args.string('providerId')\n        ]);\n    fireauth.exportlib.exportFunction(namespace,\n        'SAMLAuthProvider', fireauth.SAMLAuthProvider, [\n          fireauth.args.string('providerId')\n        ]);\n    fireauth.exportlib.exportFunction(namespace,\n        'PhoneAuthProvider', fireauth.PhoneAuthProvider, [\n          fireauth.args.firebaseAuth(true)\n        ]);\n    fireauth.exportlib.exportFunction(namespace,\n        'RecaptchaVerifier', fireauth.RecaptchaVerifier, [\n          fireauth.args.or(\n              fireauth.args.string(),\n              fireauth.args.element(),\n              'recaptchaContainer'),\n          fireauth.args.object('recaptchaParameters', true),\n          fireauth.args.firebaseApp(true)\n        ]);\n    fireauth.exportlib.exportFunction(namespace,\n        'ActionCodeURL', fireauth.ActionCodeURL, []);\n    fireauth.exportlib.exportFunction(namespace,\n        'PhoneMultiFactorGenerator', fireauth.PhoneMultiFactorGenerator, []);\n\n    // Create auth components to register with firebase.\n    // Provides Auth public APIs.\n    const authComponent = { \n      'name': fireauth.exportlib.AUTH_TYPE,\n      'instanceFactory': function(container) {\n        var app = container['getProvider']('app')['getImmediate']();\n        return new fireauth.Auth(app);\n      },\n      'multipleInstances': false,\n      'serviceProps': namespace,\n      'instantiationMode': 'LAZY',\n      'type':  'PUBLIC',\n      /**\n       * Initialize auth-internal after auth is initialized to make auth available to other firebase products.\n       */\n      'onInstanceCreated': function (container, _instanceIdentifier, _instance) {\n          const authInternalProvider = container['getProvider'](\n            'auth-internal'\n          );\n          authInternalProvider['initialize']();\n        }\n    };\n  \n    // Provides Auth internal APIs.\n    const authInteropComponent = { \n      'name': 'auth-internal',\n      'instanceFactory': function(container) {\n        var auth = container['getProvider'](fireauth.exportlib.AUTH_TYPE)['getImmediate']();\n        return {\n          'getUid': goog.bind(auth.getUid, auth),\n          'getToken': goog.bind(auth.getIdTokenInternal, auth),\n          'addAuthTokenListener':\n            goog.bind(auth.addAuthTokenListenerInternal, auth),\n          'removeAuthTokenListener':\n            goog.bind(auth.removeAuthTokenListenerInternal, auth)\n        };\n      },\n      'multipleInstances': false,\n      'instantiationMode': 'LAZY',\n      'type':  'PRIVATE'\n    };\n  \n    firebase.INTERNAL.registerComponent(authComponent);\n    firebase.INTERNAL.registerComponent(authInteropComponent);\n    firebase.registerVersion('@firebase/auth', AUTH_NPM_PACKAGE_VERSION);\n\n    // Expose User as firebase.User.\n    firebase.INTERNAL.extendNamespace({\n      'User': fireauth.AuthUser\n    });\n  }\n})();"],"names":["k","$jscomp.defineProperty","Object","a","b","c","Array","$jscomp.global","window","self","global","Math","$jscomp.getGlobal","this","Symbol","next","done","value","d","e","configurable","writable","$jscomp.polyfill","g","h","m","p","v","resolve","reject","TypeError","f","A","Q","ya","Ad","B","$jscomp.makeIterator","goog","goog.global","goog.NONCE_PATTERN_","goog.cspNonce_","doc","script","nonce","goog.NONCE_PATTERN_.test","goog.nullFunction","goog.typeOf","s","goog.isArrayLike","val","type","goog.isFunction","goog.isObject","goog.UID_PROPERTY_","goog.uidCounter_","fn","selfObj","var_args","arguments","goog.bindJs_","boundArgs","newArgs","goog.bind","Function","goog.bindNative_","goog.partial","args","childCtor","parentCtor","tempCtor","goog.identity_","fireauth.AuthError","code","message","serverResponse","fireauth.AuthError.ERROR_CODE_PREFIX","fireauth.AuthError.MESSAGES_","response","fullCode","fireauth.AuthError.ERROR_CODE_PREFIX.length","goog.inherits","Error","fireauth.AuthError.prototype.toPlainObject","obj","fireauth.AuthError.prototype.toJSON","Da","ADMIN_ONLY_OPERATION","ARGUMENT_ERROR","APP_NOT_AUTHORIZED","APP_NOT_INSTALLED","CAPTCHA_CHECK_FAILED","CODE_EXPIRED","CORDOVA_NOT_READY","CORS_UNSUPPORTED","CREDENTIAL_ALREADY_IN_USE","CREDENTIAL_MISMATCH","CREDENTIAL_TOO_OLD_LOGIN_AGAIN","DYNAMIC_LINK_NOT_ACTIVATED","EMAIL_CHANGE_NEEDS_VERIFICATION","EMAIL_EXISTS","EXPIRED_OOB_CODE","EXPIRED_POPUP_REQUEST","INTERNAL_ERROR","INVALID_APP_CREDENTIAL","INVALID_APP_ID","INVALID_AUTH","INVALID_AUTH_EVENT","INVALID_CODE","INVALID_CONTINUE_URI","INVALID_CORDOVA_CONFIGURATION","INVALID_CUSTOM_TOKEN","INVALID_DYNAMIC_LINK_DOMAIN","INVALID_EMAIL","INVALID_API_KEY","INVALID_CERT_HASH","INVALID_IDP_RESPONSE","INVALID_MESSAGE_PAYLOAD","INVALID_MFA_PENDING_CREDENTIAL","INVALID_OAUTH_PROVIDER","INVALID_OAUTH_CLIENT_ID","INVALID_ORIGIN","INVALID_OOB_CODE","INVALID_PASSWORD","INVALID_PERSISTENCE","INVALID_PHONE_NUMBER","INVALID_PROVIDER_ID","INVALID_RECIPIENT_EMAIL","INVALID_SENDER","INVALID_SESSION_INFO","INVALID_TENANT_ID","MFA_ENROLLMENT_NOT_FOUND","MFA_REQUIRED","MISSING_ANDROID_PACKAGE_NAME","MISSING_AUTH_DOMAIN","MISSING_APP_CREDENTIAL","MISSING_CODE","MISSING_CONTINUE_URI","MISSING_IFRAME_START","MISSING_IOS_BUNDLE_ID","MISSING_MFA_ENROLLMENT_ID","MISSING_MFA_PENDING_CREDENTIAL","MISSING_OR_INVALID_NONCE","MISSING_PHONE_NUMBER","MISSING_SESSION_INFO","MODULE_DESTROYED","NEED_CONFIRMATION","NETWORK_REQUEST_FAILED","NO_AUTH_EVENT","NO_SUCH_PROVIDER","NULL_USER","OPERATION_NOT_ALLOWED","OPERATION_NOT_SUPPORTED","POPUP_BLOCKED","POPUP_CLOSED_BY_USER","PROVIDER_ALREADY_LINKED","QUOTA_EXCEEDED","REDIRECT_CANCELLED_BY_USER","REDIRECT_OPERATION_PENDING","REJECTED_CREDENTIAL","SECOND_FACTOR_EXISTS","SECOND_FACTOR_LIMIT_EXCEEDED","TENANT_ID_MISMATCH","TIMEOUT","TOKEN_EXPIRED","TOO_MANY_ATTEMPTS_TRY_LATER","UNAUTHORIZED_DOMAIN","UNSUPPORTED_FIRST_FACTOR","UNSUPPORTED_PERSISTENCE","UNSUPPORTED_TENANT_OPERATION","UNVERIFIED_EMAIL","USER_CANCELLED","USER_DELETED","USER_DISABLED","USER_MISMATCH","USER_SIGNED_OUT","WEAK_PASSWORD","WEB_STORAGE_UNSUPPORTED","fireauth.constants.Endpoint","BOQ","firebaseAuthEndpoint","secureTokenEndpoint","identityPlatformEndpoint","id","PRODUCTION","STAGING","TEST","opt_id","endpointKey","firebaseEndpoint","endpoint","goog.Thenable.isImplementedBy","object","goog.debug.Error","opt_msg","stack","String","goog.asserts.AssertionError","messagePattern","messageArgs","returnString","subLast","splitParts","i","opt_message","constructor","create","reset","put","item","goog.async.WorkQueue","fireauth.constants.getEndpointConfig","goog.async.FreeList","get","goog.async.WorkQueue.freelist_","goog.async.WorkItem","goog.async.WorkQueue.prototype.add","scope","goog.async.WorkQueue.freelist_.get","goog.async.WorkItem.prototype.set","goog.async.WorkItem.prototype.reset","indexOf","arr","opt_fromIndex","fromIndex","forEach","opt_obj","l","arr2","filter","res","resLength","map","some","contains","remove","rv","removeAllIf","index","removedCount","concat","toArray","length","goog.labs.userAgent.util.userAgent_","goog.string.internal.trim","str","goog.string.internal.AMP_RE_","goog.string.internal.LT_RE_","goog.string.internal.GT_RE_","goog.string.internal.QUOT_RE_","goog.string.internal.SINGLE_QUOTE_RE_","goog.string.internal.NULL_RE_","goog.string.internal.ALL_RE_","subString","goog.string.internal.compareElements_","left","right","navigator","goog.global.navigator","userAgent","ib","goog.labs.userAgent.util.matchUserAgent","goog.object.forEach","key","goog.object.isEmpty","goog.object.clone","goog.object.PROTOTYPE_FIELDS_","target","source","j","goog.object.PROTOTYPE_FIELDS_.length","goog.dom.asserts.assertIsElementType_","o","typename","win","ex","undefined","goog.string.Const","opt_token","opt_content","goog.string.Const.GOOG_STRING_CONSTRUCTOR_TOKEN_PRIVATE_","goog.string.Const.TYPE_MARKER_","stringConst","goog.string.Const.prototype.getTypedStringValue","goog.string.Const.prototype.toString","ub","goog.html.trustedtypes.cachedPolicy_","policy","policyFactory","goog.global.trustedTypes","name","createHTML","createScript","createScriptURL","goog.global.console","token","goog.html.TrustedResourceUrl.CONSTRUCTOR_TOKEN_PRIVATE_","trustedResourceUrl","goog.html.TrustedResourceUrl","goog.html.TrustedResourceUrl.format","format","url","formatStr","goog.string.Const.unwrap","goog.html.TrustedResourceUrl.BASE_URL_.test","result","goog.html.TrustedResourceUrl.FORMAT_MARKER_","match","JSON","arg","encodeURIComponent","goog.html.trustedtypes.getPolicyPrivateDoNotAccessOrElse","goog.html.TrustedResourceUrl.prototype.getTypedStringValue","goog.html.TrustedResourceUrl.prototype.toString","goog.html.TrustedResourceUrl.BASE_URL_","goog.html.SafeUrl.CONSTRUCTOR_TOKEN_PRIVATE_","safeUrl","goog.html.SafeUrl","goog.html.SafeUrl.prototype.getTypedStringValue","goog.html.SafeUrl.prototype.toString","goog.html.SAFE_MIME_TYPE_PATTERN_","goog.html.DATA_URL_PATTERN_","goog.html.SAFE_URL_PATTERN_","goog.html.SAFE_URL_PATTERN_.test","dataUrl","goog.html.SAFE_MIME_TYPE_PATTERN_.test","goog.html.SafeUrl.sanitizeAssertUnchanged","goog.html.SafeUrl.INNOCUOUS_STRING","goog.html.SafeUrl.INNOCUOUS_URL","dir","goog.html.SafeHtml.CONSTRUCTOR_TOKEN_PRIVATE_","goog.html.SafeHtml","goog.html.SafeHtml.prototype.getTypedStringValue","goog.html.SafeHtml.prototype.toString","goog.dom.safe.openInWindow","opt_openerWin","opt_name","opt_specs","goog.html.SafeUrl.unwrap","opt_replace","goog.string.subs","subsArguments","goog.string.htmlEscape","goog.string.internal.ALL_RE_.test","goog.reflect.sinkValue","x","goog.userAgent.VERSION","goog.userAgent.OPERA","goog.userAgent.IE","goog.userAgent.EDGE","goog.userAgent.EDGE_OR_IE","goog.userAgent.GECKO","goog.string.internal.contains","goog.userAgent.WEBKIT","version","docMode","goog.userAgent.getDocumentMode_","parseFloat","bc","goog.userAgent.isVersionOrHigherCache_","valueFn","order","v1Subs","v2Subs","subCount","subIdx","v1Sub","v2Sub","v1Comp","v2Comp","parseInt","cacheObj","goog.userAgent.DOCUMENT_MODE","getContext","contextName","goog.dom.BrowserFeature.CAN_ADD_NAME_OR_TYPE_ATTRIBUTES","documentMode","Number","goog.dom.getElementHelper_","element","document","goog.dom.setProperties","properties","goog.dom.DIRECT_ATTRIBUTE_MAP_.hasOwnProperty","goog.dom.DIRECT_ATTRIBUTE_MAP_","prefix","rc","uc","cellpadding","cellspacing","colspan","frameborder","height","maxlength","role","rowspan","usemap","valign","width","clone","tagName","attributes","tagNameArr","goog.dom.createElement_","goog.dom.append_","parent","childHandler","child","startIndex","goog.async.throwException","exception","goog.global.setTimeout","goog.async.run","callback","opt_context","goog.async.run.schedule_","promise","goog.global.Promise","goog.global.Promise.resolve","goog.async.run.processWorkQueue","cb","goog.global.setImmediate","goog.global.Window","goog.async.nextTick.setImmediate_","Channel","iframe","goog.dom.TagName.IFRAME","origin","onmessage","postMessage","channel","head","tail","goog.async.nextTick.getSetImmediateEmulator_","goog.async.run.initializeRunner_","goog.async.run.workQueueScheduled_","goog.async.run.workQueue_","goog.async.WorkQueue.prototype.remove","goog.async.run.workQueue_.remove","goog.async.WorkQueue.freelist_.put","goog.Promise","resolver","goog.Promise.State_.PENDING","resolve_","goog.Promise.State_.FULFILLED","reason","goog.Promise.State_.REJECTED","PENDING","FULFILLED","REJECTED","goog.Promise.CallbackEntry_","goog.Promise.CallbackEntry_.prototype.reset","goog.Promise.freelist_","onFulfilled","onRejected","context","entry","goog.Promise.freelist_.get","goog.Promise.resolve","opt_value","goog.Promise.reject","opt_reason","goog.Promise.resolveThen_","goog.Promise.maybeThen_","goog.Promise.allSettled","promises","toSettle","results","onSettled","fulfilled","goog.Promise.prototype.addCallbackEntry_","callbackEntry","hasEntry_","scheduleCallbacks_","queueEntry_","goog.Promise.prototype.addChildPromise_","goog.Promise.getCallbackEntry_","err","goog.Promise.prototype.resolve_","state","goog.Promise.addUnhandledRejection_","BLOCKED","goog.Promise.handleRejection_.call","then","goog.Promise.tryThen_","thenable","called","goog.Promise.prototype.scheduleCallbacks_","goog.Promise.prototype.popEntry_","goog.Promise.prototype.executeCallback_","goog.Promise.invokeCallback_","goog.Promise.freelist_.put","goog.Promise.prototype.then","opt_onFulfilled","opt_onRejected","addChildPromise_","D","goog.Promise.prototype.thenAlways","addCallbackEntry_","goog.Promise.prototype.thenCatch","goog.Promise.prototype.cancel","goog.Promise.CancellationError","goog.Promise.prototype.cancelInternal_","parent_","childCount","childEntry","beforeChildEntry","childPromise","cancelInternal_","removeEntryAfter_","previous","popEntry_","executeCallback_","goog.Promise.prototype","goog.Promise.prototype.unblockAndFulfill_","goog.Promise.prototype.unblockAndReject_","goog.Promise.prototype.executeCallbacks_","goog.Promise.handleRejection_","goog.debug.Error.call","goog.Disposable","OFF","goog.Disposable.instances_","goog.Disposable.prototype.dispose","goog.Disposable.MONITORING_MODE","goog.Disposable.MonitoringMode.OFF","uid","goog.Disposable.prototype.disposeInternal","goog.debug.freezeInternal_","HAS_W3C_EVENT_SUPPORT","SET_KEY_CODE_TO_PREVENT_DEFAULT","goog.userAgent.isVersionOrHigher","PASSIVE_EVENTS","goog.global.addEventListener","passive","options","goog.global.removeEventListener","goog.events.Event","opt_target","goog.events.BrowserEvent","opt_e","opt_currentTarget","goog.events.Event.call","init","relevantTouch","relatedTarget","MOUSEOVER","MOUSEOUT","goog.events.BrowserEvent.IE_POINTER_TYPE_MAP","goog.events.Event.prototype.preventDefault","2","TOUCH","3","PEN","4","MOUSE","goog.events.BrowserEvent.prototype.preventDefault","goog.events.BrowserEvent.superClass_.preventDefault.call","be","goog.events.BrowserFeature.SET_KEY_CODE_TO_PREVENT_DEFAULT","VK_F1","VK_F12","goog.events.BrowserEvent.prototype.getBrowserEvent","goog.events.Listenable.IMPLEMENTED_BY_PROP","goog.events.ListenableKey.counter_","goog.events.Listener","listener","src","capture","opt_handler","proxy","goog.events.Listener.prototype.markAsRemoved","goog.events.ListenerMap","goog.events.ListenerMap.prototype.removeByKey","markAsRemoved","goog.events.ListenerMap.findListenerIndex_","listenerArray","opt_useCapture","opt_listenerScope","listenerObj","goog.events.ListenerMap.prototype.add","callOnce","typeStr","goog.events.LISTENER_MAP_PROP_","goog.events.onStringMap_","opt_options","goog.events.listenOnce","goog.events.listen","goog.events.wrapListener","listen","goog.events.listen_","proxyCallbackFunction","listenerMap","goog.events.getListenerMap_","goog.events.handleBrowserEvent_","goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT","eventObject","goog.events.BrowserFeature.PASSIVE_EVENTS","goog.events.getOnString_","listenOnce","goog.events.unlisten","eventTargetListeners_","goog.events.unlistenByKey","removeByKey","goog.events.onString_","goog.events.fireListeners_","retval","goog.events.fireListener","listenerFn","listenerHandler","opt_evt","parts","cur","ieEvent","useReturnValue","ancestors","evt","goog.events.LISTENER_WRAPPER_PROP_","goog.events.EventTarget","goog.Disposable.call","goog.events.EventTarget.prototype.listen","goog.events.EventTarget.prototype.listenOnce","goog.events.EventTarget.prototype.fireListeners","unlistenByKey","goog.Timer.callOnce","opt_delay","goog.Timer.promise","delay","timerKey","thenCatch","opt_result","error","goog.global.clearTimeout","goog.structs.getValues","col","goog.structs.getKeys","goog.structs.Map","opt_map","argLength","keys","addAll","goog.structs.Map.prototype.cleanupKeysArray_","srcIndex","destIndex","seen","goog.structs.Map.hasKey_","goog.events.EventTarget.prototype.addEventListener","handler","opt_capture","opt_handlerScope","goog.events.EventTarget.prototype.removeEventListener","goog.events.EventTarget.prototype.dispatchEvent","ancestorsTree","ancestor","getParentEventTarget","actualEventTarget_","oldEvent","opt_ancestorsTree","currentTarget","fireListeners","goog.events.EventTarget.prototype.disposeInternal","goog.events.EventTarget.superClass_.disposeInternal.call","removeAllListeners","Jd","goog.structs.Map.prototype.getValues","cleanupKeysArray_","goog.structs.Map.prototype.getKeys","goog.structs.Map.prototype.clear","goog.structs.Map.prototype","goog.structs.Map.prototype.get","opt_val","goog.structs.Map.prototype.set","goog.structs.Map.prototype.forEach","goog.uri.utils.splitRe_","goog.Uri","opt_uri","opt_ignoreCase","setScheme","setUserInfo","setDomain","setPort","setPath","setQueryData","setFragment","SCHEME","goog.Uri.decodeOrEmpty_","USER_INFO","DOMAIN","PORT","PATH","QUERY_DATA","FRAGMENT","goog.Uri.QueryData","goog.Uri.prototype.setScheme","newScheme","opt_decode","goog.Uri.prototype.setPort","newPort","isNaN","goog.Uri.prototype.setQueryData","queryData","goog.Uri.QueryData.prototype.setIgnoreCase","ignoreCase","ensureKeyMapInitialized_","invalidateCache_","lowerCase","setValues","goog.Uri.encodeSpecialChars_","goog.Uri.reDisallowedInQuery_","goog.Uri.prototype.setParameterValue","goog.Uri.prototype.getParameterValue","paramName","goog.Uri.parse","uri","goog.Uri.create","opt_scheme","opt_domain","opt_port","opt_path","opt_preserveReserved","decodeURI","decodeURIComponent","unescapedPart","extra","opt_removeDoubleEncoding","encoded","encodeURI","goog.Uri.encodeChar_","ch","n","goog.Uri.prototype.toString","out","scheme","getScheme","goog.Uri.reDisallowedInSchemeOrUserInfo_","domain","getDomain","userInfo","getUserInfo","port","getPort","path","getPath","hasDomain","goog.Uri.reDisallowedInAbsolutePath_","goog.Uri.reDisallowedInRelativePath_","query","getEncodedQuery","fragment","getFragment","goog.Uri.reDisallowedInFragment_","goog.Uri.prototype.resolve","relativeUri","absoluteUri","overridden","hasPath","lastSlashIndex","leadingSlash","pos","segments","segment","opt_query","goog.Uri.QueryData.prototype.ensureKeyMapInitialized_","encodedQuery","pairs","indexOfEquals","goog.uri.utils.parseQueryData","goog.Uri.QueryData.createFromMap","values","goog.Uri.QueryData.prototype.remove","getKeyName_","keyMap_","goog.Uri.QueryData.prototype.containsKey","goog.Uri.QueryData.prototype.setValues","goog.Uri.QueryData.prototype.clone","goog.Uri.QueryData.prototype.getKeyName_","keyName","sb","goog.json.Serializer.prototype.serializeInternal","sep","serializeInternal","serializeArray","serializeString_","serializeObject_","isFinite","serialize","goog.json.Serializer","Ud","goog.Uri.QueryData.prototype.add","goog.Uri.QueryData.prototype","goog.Uri.QueryData.prototype.clear","goog.Uri.QueryData.prototype.forEach","opt_scope","goog.Uri.QueryData.prototype.getKeys","vals","goog.Uri.QueryData.prototype.getValues","opt_key","containsKey","goog.Uri.QueryData.prototype.set","goog.Uri.QueryData.prototype.get","opt_default","goog.Uri.QueryData.prototype.toString","encodedKey","param","goog.json.Serializer.charToJsonCharCache_","\"","\\","/","\b","\f","\n","\r","\t","\u000b","goog.json.Serializer.charsToReplace_","fireauth.util.isLocalStorageNotSynchronized","ua","fireauth.util.getUserAgentString","fireauth.util.getCurrentUrl","fireauth.util.goTo","opt_window","finalUrl","goog.html.SafeUrl.trySanitize","fireauth.util.isMobileBrowser","opt_userAgent","uaLower","fireauth.util.closeWindow","fireauth.util.popup","opt_url","opt_width","opt_height","top","option","location","resizable","statusbar","toolbar","fireauth.util.BrowserName.FIREFOX","linkRef","optionString","parentWin","goog.dom.TagName.A","safeLinkRef","click","newWin","sanitizedLinkRef","html","trustedHtml","newDoc","safeHtml","fireauth.util.IP_ADDRESS_REGEXP_","fireauth.util.EMAIL_ADDRESS_REGEXP_","goog.global.document","LOAD","fireauth.util.isAndroidOrIosCordovaScheme","fireauth.util.getCurrentScheme","fireauth.util.isIframe","fireauth.util.isWorker","fireauth.util.getEnvironment","REACT_NATIVE","firebase","NODE","WORKER","BROWSER","fireauth.util.isNativeEnvironment","environment","FIREFOX","CHROME","OPERA","IEMOBILE","IE","EDGE","SILK","BLACKBERRY","WEBOS","ANDROID","matches","re","OTHER","SAFARI","fireauth.util.Framework","DEFAULT","FIREBASEUI","clientVersion","opt_frameworkVersion","frameworkVersion","frameworkSet","providedFrameworks","reportedEnvironment","fireauth.util.getBrowserName","fireauth.util.getObjectRef","varStrName","pieces","last","fireauth.util.isWebStorageSupported","storage","fireauth.util.generateEventId","fireauth.util.isPopupRedirectSupported","fireauth.util.isHttpOrHttps","fireauth.util.runsInBackground","fireauth.util.stringifyJSON","goog.json.serialize","fireauth.util.copyWithoutNullsOrUndefined","trimmedObj","fireauth.util.parseJSON","json","opt_prefix","fireauth.util.iframeCanSyncWebStorage","fireauth.util.resetUnloadedGapiModules","beacon","hint","fireauth.util.Delay","shortDelay","longDelay","fireauth.util.onAppVisible","onVisibilityChange","fireauth.util.isAppVisible","fireauth.util.utcTimestampToDateString","utcTimestamp","date","Date","fireauth.util.isAuthHandlerOrIframe","fireauth.util.Delay.prototype.get","fireauth.util.Delay.OFFLINE_DELAY_MS_","fireauth.object.readonlyConfigurable_","fireauth.deprecation.shownMessages_","console","enumerable","hf","fireauth.object.setReadonlyProperty","fireauth.object.setReadonlyProperties","props","fireauth.object.makeReadonlyCopy","output","fireauth.object.unsafeCreateReadOnlyCopy","copy","fireauth.MultiFactorInfo","resp","factorId","fireauth.MultiFactorInfo.MfaEnrollmentField.PHONE_INFO","PHONE","fireauth.MultiFactorInfo.MfaEnrollmentField.MFA_ENROLLMENT_ID","fireauth.MultiFactorInfo.MfaEnrollmentField.DISPLAY_NAME","enrollmentTime","fireauth.MultiFactorInfo.MfaEnrollmentField.ENROLLED_AT","toUTCString","multiFactorInfo","fireauth.PhoneMultiFactorInfo","fireauth.MultiFactorInfo.prototype.toPlainObject","displayName","DISPLAY_NAME","ENROLLED_AT","MFA_ENROLLMENT_ID","PHONE_INFO","fireauth.MultiFactorInfo.call","fireauth.ActionCodeInfo","data","email","fireauth.ActionCodeInfo.ServerFieldName.EMAIL","newEmail","fireauth.ActionCodeInfo.ServerFieldName.NEW_EMAIL","operation","fireauth.ActionCodeInfo.ServerFieldName.REQUEST_TYPE","fireauth.MultiFactorInfo.fromServerResponse","fireauth.ActionCodeInfo.ServerFieldName.MFA_INFO","fireauth.ActionCodeInfo.Operation.EMAIL_SIGNIN","fireauth.ActionCodeInfo.Operation.VERIFY_AND_CHANGE_EMAIL","fireauth.ActionCodeInfo.Operation.REVERT_SECOND_FACTOR_ADDITION","mfaInfo","fireauth.ActionCodeInfo.DataField.FROM_EMAIL","fireauth.ActionCodeInfo.DataField.PREVIOUS_EMAIL","fireauth.ActionCodeInfo.DataField.EMAIL","fireauth.ActionCodeInfo.DataField.MULTI_FACTOR_INFO","fireauth.ActionCodeInfo.PropertyName.OPERATION","fireauth.ActionCodeInfo.PropertyName.DATA","fireauth.PhoneMultiFactorInfo.prototype.toPlainObject","fireauth.PhoneMultiFactorInfo.superClass_.toPlainObject.call","REVERT_SECOND_FACTOR_ADDITION","EMAIL_SIGNIN","VERIFY_AND_CHANGE_EMAIL","EMAIL","MFA_INFO","NEW_EMAIL","REQUEST_TYPE","FROM_EMAIL","MULTI_FACTOR_INFO","PREVIOUS_EMAIL","DATA","OPERATION","fireauth.ActionCodeURL","actionLink","apiKey","getParameterValue","fireauth.ActionCodeURL.QueryField.API_KEY","fireauth.ActionCodeURL.QueryField.CODE","mode","fireauth.ActionCodeURL.QueryField.MODE","fireauth.ActionCodeURL.ModeToOperationMap_","continueUrl","fireauth.ActionCodeURL.QueryField.CONTINUE_URL","languageCode","fireauth.ActionCodeURL.QueryField.LANGUAGE_CODE","tenantId","fireauth.ActionCodeURL.QueryField.TENANT_ID","API_KEY","CODE","CONTINUE_URL","LANGUAGE_CODE","MODE","TENANT_ID","recoverEmail","RECOVER_EMAIL","resetPassword","PASSWORD_RESET","revertSecondFactorAddition","signIn","verifyAndChangeEmail","verifyEmail","VERIFY_EMAIL","fireauth.ActionCodeSettings","settingsObj","fireauth.ActionCodeSettings.RawField.URL","initialize_","androidSettings","fireauth.ActionCodeSettings.RawField.ANDROID","apn","fireauth.ActionCodeSettings.AndroidRawField.PACKAGE_NAME","installApp","fireauth.ActionCodeSettings.AndroidRawField.INSTALL_APP","fireauth.ActionCodeSettings.AndroidRawField.MINIMUM_VERSION","amv","iosSettings","fireauth.ActionCodeSettings.RawField.IOS","ibi","fireauth.ActionCodeSettings.IosRawField.BUNDLE_ID","canHandleCodeInApp","fireauth.ActionCodeSettings.RawField.HANDLE_CODE_IN_APP","fireauth.ActionCodeSettings.RawField.DYNAMIC_LINK_DOMAIN","dynamicLinkDomain","DYNAMIC_LINK_DOMAIN","HANDLE_CODE_IN_APP","IOS","URL","INSTALL_APP","MINIMUM_VERSION","PACKAGE_NAME","BUNDLE_ID","fireauth.ActionCodeSettings.prototype.buildRequest","request","goog.crypt.base64.charToByteMap_","input","goog.crypt.base64.decodeStringInternal_","pushByte","getByte","default_val","nextCharIndex","goog.crypt.base64.init_","commonChars","specialChars","chars","char","byte1","byte2","byte3","byte4","fireauth.IdToken","tokenString","fireauth.IdToken.parseIdTokenClaims","now","padLen","fields","jsonInfo","goog.crypt.base64.decodeStringToByteArray","bytes","c2","c3","u","c1","fireauth.IdToken.prototype.getTenantId","fireauth.IdToken.prototype.isAnonymous","fireauth.IdToken.prototype.toString","fireauth.idp.RESERVED_OAUTH1_PARAMS","fireauth.idp.RESERVED_OAUTH2_PARAMS","fireauth.idp.Settings","FACEBOOK","languageParam","popupWidth","popupHeight","providerId","reservedOAuthParameters","GITHUB","GOOGLE","TWITTER","APPLE","fireauth.AdditionalUserInfo.fromPlainObject","factory","fireauth.FacebookAdditionalUserInfo","fireauth.GoogleAdditionalUserInfo","fireauth.GithubAdditionalUserInfo","fireauth.TwitterAdditionalUserInfo","fireauth.AdditionalUserInfo.VerifyAssertionField.PROVIDER_ID","fireauth.FederatedAdditionalUserInfo","fireauth.AdditionalUserInfo.VerifyAssertionField.ID_TOKEN","fireauth.GenericAdditionalUserInfo","ID_TOKEN","PROVIDER_ID","info","idToken","fireauth.IdToken.parse","isNewUser","ANONYMOUS","CUSTOM","fireauth.GenericAdditionalUserInfo.call","fireauth.FederatedAdditionalUserInfo.call","fireauth.DynamicLink.parseDeepLink","link","doubleDeepLink","iOSdeepLink","fireauth.MultiFactorSession","mfaPendingCredential","fireauth.MultiFactorSession.Type.ENROLL","fireauth.MultiFactorSession.Type.SIGN_IN","ENROLL","SIGN_IN","fireauth.AuthCredential","idTokenResolver","fireauth.RpcHandler.AuthServerField.ID_TOKEN","parsedIdToken","fireauth.SAMLAuthCredential","pendingToken","fireauth.SAMLAuthCredential.prototype.makeVerifyAssertionRequest_","requestUri","fireauth.constants.SAML_PREFIX","fireauth.OAuthCredential","oauthResponse","signInMethod","fireauth.OAuthCredential.prototype.makeVerifyAssertionRequest_","postBody","accessToken","oauthTokenSecret","oauthToken","fireauth.FederatedProvider","opt_reservedParams","isOAuthProvider","fireauth.idp.getIdpSettings","fireauth.FederatedProvider.call","fireauth.OAuthProvider.call","accessTokenOrObject","credential","fireauth.FacebookAuthProvider","fireauth.GithubAuthProvider","fireauth.GoogleAuthProvider","idTokenOrObject","fireauth.TwitterAuthProvider","tokenOrObject","secret","tokenObject","fireauth.EmailAuthCredential","password","opt_signInMethod","PASSWORD","fireauth.EmailAuthProvider","fireauth.EmailAuthProvider.credentialWithLink","emailLink","actionCodeUrl","fireauth.EmailAuthProvider.getActionCodeUrlFromSignInEmailLink","fireauth.ActionCodeURL.parseLink","params","allowedKeys","fireauth.PhoneAuthCredential","fireauth.PhoneAuthCredential.prototype.makeVerifyPhoneNumberRequest_","temporaryProof","phoneNumber","sessionInfo","fireauth.PhoneAuthProvider","opt_auth","verificationId","verificationCode","accessTokenSecret","rawNonce","fireauth.OAuthProvider","fireauth.AuthProvider.checkIfOAuthSupported","provider","fireauth.AuthEvent","opt_eventId","opt_urlResponse","opt_sessionId","opt_error","opt_postBody","opt_tenantId","rawResponse","fireauth.AuthError.fromPlainObject","fireauth.UniversalLinkSubscriber","fireauth.MultiFactorSession.prototype.getRawSession","fireauth.MultiFactorSession.prototype.toPlainObject","multiFactorSession","pendingCredential","fireauth.AuthCredential.prototype.getIdTokenProvider","fireauth.AuthCredential.prototype.linkToIdToken","fireauth.AuthCredential.prototype.matchIdTokenWithUid","fireauth.AuthCredential.prototype.toPlainObject","fireauth.SAMLAuthCredential.prototype.getIdTokenProvider","rpcHandler","makeVerifyAssertionRequest_","fireauth.SAMLAuthCredential.prototype.linkToIdToken","fireauth.SAMLAuthCredential.prototype.matchIdTokenWithUid","verifyAssertionForExisting","fireauth.SAMLAuthCredential.prototype.toPlainObject","fireauth.OAuthCredential.prototype.getIdTokenProvider","fireauth.OAuthCredential.prototype.linkToIdToken","fireauth.OAuthCredential.prototype.matchIdTokenWithUid","fireauth.OAuthCredential.prototype.toPlainObject","fireauth.FederatedProvider.prototype.setCustomParameters","customParameters","fireauth.SAMLAuthProvider","fireauth.OAuthProvider.prototype.addScope","fireauth.OAuthProvider.prototype.getScopes","fireauth.OAuthProvider.prototype.credential","optionsOrIdToken","opt_accessToken","fireauth.EmailAuthCredential.prototype.getIdTokenProvider","invokeRpc","fireauth.RpcHandler.ApiMethod.EMAIL_LINK_SIGNIN","oobCode","fireauth.RpcHandler.ApiMethod.VERIFY_PASSWORD","fireauth.EmailAuthCredential.prototype.linkToIdToken","fireauth.RpcHandler.ApiMethod.EMAIL_LINK_SIGNIN_FOR_LINKING","fireauth.RpcHandler.ApiMethod.SET_ACCOUNT_INFO_SENSITIVE","fireauth.EmailAuthCredential.prototype.matchIdTokenWithUid","fireauth.EmailAuthCredential.prototype.toPlainObject","EMAIL_LINK_SIGN_IN_METHOD","EMAIL_LINK","EMAIL_PASSWORD_SIGN_IN_METHOD","EMAIL_PASSWORD","fireauth.PhoneAuthCredential.prototype.getIdTokenProvider","makeVerifyPhoneNumberRequest_","fireauth.PhoneAuthCredential.prototype.linkToIdToken","fireauth.RpcHandler.ApiMethod.VERIFY_PHONE_NUMBER_FOR_LINKING","fireauth.PhoneAuthCredential.prototype.matchIdTokenWithUid","fireauth.RpcHandler.ApiMethod.VERIFY_PHONE_NUMBER_FOR_EXISTING","fireauth.PhoneAuthCredential.prototype.toPlainObject","fireauth.PhoneAuthProvider.prototype.verifyPhoneNumber","phoneInfoOptions","applicationVerifier","assertion","session","verifyPromise","rawSession","fireauth.RpcHandler.ApiMethod.START_PHONE_MFA_ENROLLMENT","phoneEnrollmentInfo","recaptchaToken","mfaEnrollmentId","phoneSignInInfo","fireauth.RpcHandler.ApiMethod.START_PHONE_MFA_SIGN_IN","fireauth.RpcHandler.ApiMethod.SEND_VERIFICATION_CODE","PHONE_SIGN_IN_METHOD","fireauth.AuthEvent.prototype.getUid","components","fireauth.AuthEvent.prototype.getTenantId","fireauth.AuthEvent.prototype.toPlainObject","eventId","urlResponse","sessionId","Ch","fireauth.UniversalLinkSubscriber.instance_","fireauth.InvalidOriginError","fireauth.InvalidOriginError.CHROME_EXTENSION_MESSAGE_TEMPLATE_","fireauth.InvalidOriginError.HTTP_MESSAGE_TEMPLATE_","fireauth.AuthErrorWithCredential","opt_credentialInfo","fireauth.AuthError.call","credentialInfo","fireauth.AuthProvider.getCredentialFromResponse","goog.net.XmlHttpFactory","goog.net.XmlHttpFactory.prototype.getOptions","goog.net.DefaultXmlHttpFactory.prototype.getProgId_","ACTIVE_X_IDENTS","candidate","ActiveXObject","goog.net.CorsXmlHttpFactory","XDomainRequest","UNINITIALIZED","goog.net.IeCorsXhrAdapter.prototype.setReadyState_","readyState","goog.debug.LogRecord","level","msg","loggerName","opt_time","opt_sequenceNumber","goog.debug.Logger","goog.debug.Logger.Level","fireauth.AuthErrorWithCredential.prototype.toPlainObject","goog.object.extend","fireauth.AuthErrorWithCredential.prototype.toJSON","goog.net.DefaultXmlHttpFactory","goog.net.DefaultXmlHttpFactory.prototype.createInstance","progId","getProgId_","XMLHttpRequest","goog.net.DefaultXmlHttpFactory.prototype.internalGetOptions","USE_NULL_FUNCTION","LOCAL_REQUEST_ERROR","goog.net.XmlHttp.factory_","goog.net.CorsXmlHttpFactory.prototype.createInstance","xhr","goog.net.IeCorsXhrAdapter","goog.net.CorsXmlHttpFactory.prototype.internalGetOptions","Gh","goog.net.IeCorsXhrAdapter.prototype.open","method","opt_async","goog.net.IeCorsXhrAdapter.prototype.send","goog.net.IeCorsXhrAdapter.prototype.abort","goog.net.IeCorsXhrAdapter.prototype.setRequestHeader","goog.net.IeCorsXhrAdapter.prototype.getResponseHeader","goog.net.IeCorsXhrAdapter.prototype.handleLoad_","OK","setReadyState_","COMPLETE","goog.net.IeCorsXhrAdapter.prototype.handleError_","INTERNAL_SERVER_ERROR","goog.net.IeCorsXhrAdapter.prototype.handleTimeout_","goog.net.IeCorsXhrAdapter.prototype.handleProgress_","LOADING","goog.net.IeCorsXhrAdapter.prototype","goog.net.IeCorsXhrAdapter.prototype.getAllResponseHeaders","goog.debug.LogRecord.prototype.reset","goog.debug.Logger.Level.prototype.toString","goog.debug.Logger.Level.SEVERE","goog.debug.Logger.Level.WARNING","goog.debug.Logger.Level.CONFIG","goog.debug.Logger.Level.FINE","goog.debug.Logger.prototype.log","opt_exception","goog.debug.Logger.prototype.getEffectiveLevel","getEffectiveLevel","isLoggable","logRecord","Ih","doLogRecord_","retValue","goog.debug.LogManager.loggers_","goog.debug.LogManager.rootLogger_","logger","leafName","goog.debug.Logger.ROOT_LOGGER_NAME","ret","lastDotIndex","goog.debug.LogManager.getLogger","parentLogger","goog.log.fine","goog.net.FetchXmlHttpFactory","worker","goog.events.EventTarget.call","goog.net.FetchXmlHttp.RequestState.UNSENT","Headers","goog.net.FetchXmlHttpFactory.prototype.createInstance","goog.net.FetchXmlHttp","UNSENT","goog.net.FetchXmlHttp.prototype.readInputFromFetch_","goog.net.FetchXmlHttp.prototype.requestDone_","DONE","dispatchCallback_","goog.net.FetchXmlHttp.prototype.dispatchCallback_","goog.net.XhrIo","opt_xmlHttpFactory","goog.net.XhrIo.ResponseType.DEFAULT","Wh","goog.net.FetchXmlHttp.prototype.open","OPENED","goog.net.FetchXmlHttp.prototype.send","opt_data","requestInit","headers","credentials","cache","Request","goog.net.FetchXmlHttp.prototype.abort","requestDone_","goog.net.FetchXmlHttp.prototype.handleResponse_","HEADER_RECEIVED","TextDecoder","readInputFromFetch_","goog.net.FetchXmlHttp.prototype","goog.net.FetchXmlHttp.prototype.handleDataFromStream_","newText","Uint8Array","stream","goog.net.FetchXmlHttp.prototype.handleResponseText_","responseText","goog.net.FetchXmlHttp.prototype.handleResponseArrayBuffer_","responseArrayBuffer","goog.net.FetchXmlHttp.prototype.handleSendFailure_","logger_","goog.net.FetchXmlHttp.prototype.setRequestHeader","header","goog.net.FetchXmlHttp.prototype.getResponseHeader","goog.net.FetchXmlHttp.prototype.getAllResponseHeaders","lines","iter","pair","getCredentialsMode","set","setCredentialsMode","goog.net.XhrIo.HTTP_SCHEME_PATTERN","goog.net.XhrIo.METHODS_WITH_FORM_DATA","goog.net.XhrIo.prototype.send","opt_method","opt_headers","createXhr","goog.net.XmlHttp.factory_.createInstance","getOptions","goog.net.XmlHttp.factory_.getOptions","formatMsg_","error_","content","goog.structs.forEach","goog.net.XhrIo.isContentTypeHeader_","find","contentTypeKey","contentIsFormData","goog.net.XhrIo.CONTENT_TYPE_HEADER","goog.net.XhrIo.FORM_CONTENT_TYPE","cleanUpTimeoutTimer_","goog.net.XhrIo.prototype.error_","dispatchErrors_","cleanUpXhr_","goog.net.XhrIo.prototype.dispatchErrors_","ERROR","goog.net.XhrIo.prototype.onReadyStateChangeHelper_","getReadyState","getStatus","READY_STATE_CHANGE","isComplete","protocol","status","isSuccess","CREATED","ACCEPTED","NO_CONTENT","PARTIAL_CONTENT","NOT_MODIFIED","QUIRK_IE_NO_CONTENT","goog.global.self","goog.global.self.location","goog.global.self.location.protocol","goog.net.XhrIo.HTTP_SCHEME_PATTERN.test","SUCCESS","LOADED","getStatusText","goog.net.XhrIo.prototype.cleanUpXhr_","opt_fromDispose","clearedOnReadyStateChange","READY","goog.net.XhrIo.prototype.cleanUpTimeoutTimer_","goog.net.XhrIo.prototype.getReadyState","goog.net.XhrIo.prototype.getStatus","goog.net.XhrIo.prototype.formatMsg_","goog.async.Deferred","opt_defaultScope","opt_onCancelFunction","goog.net.jsloader.cancel_","goog.async.Deferred.prototype.updateResult_","fire_","goog.async.Deferred.prototype.check_","hasFired","goog.async.Deferred.AlreadyCalledError","goog.async.Deferred.prototype.addCallbacks","eb","goog.async.Deferred.prototype.hasErrback_","sequenceRow","goog.async.Deferred.prototype.fire_","hasErrback_","unhandledErrorId_","goog.async.Deferred.errorMap_","isNewlyBlocked","unhandledException","sequenceEntry","errback","onCallback","onErrback","addCallbacks","deferredError","goog.async.Deferred.Error_","ai","goog.net.XhrIo.prototype.timeout_","goog.net.XhrIo.prototype","goog.net.XhrIo.prototype.abort","ABORT","goog.net.XhrIo.prototype.disposeInternal","goog.net.XhrIo.prototype.onReadyStateChange_","isDisposed","onReadyStateChangeHelper_","goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_","goog.net.XhrIo.prototype.getResponse","TEXT","ARRAY_BUFFER","goog.async.Deferred.prototype.cancel","opt_deepCancel","goog.async.Deferred.CanceledError","check_","updateResult_","goog.async.Deferred.prototype.continue_","goog.async.Deferred.prototype.then","rej","goog.async.Deferred.Error_.prototype.throwError","goog.net.jsloader.safeLoad","trustedUri","goog.html.TrustedResourceUrl.unwrapTrustedScriptURL","goog.dom.TagName.SCRIPT","script_","timeout_","deferred","timeout","goog.net.jsloader.cleanup_","goog.net.jsloader.Error","goog.net.jsloader.ErrorCode.TIMEOUT","goog.net.jsloader.DEFAULT_TIMEOUT","script.onreadystatechange","script.onerror","goog.net.jsloader.ErrorCode.LOAD_ERROR","charset","goog.getScriptNonce_","headElements","scriptNode","removeScriptNode","opt_timeout","LOAD_ERROR","fireauth.XmlHttpFactory","xmlHttpRequest","opt_config","opt_firebaseClientVersion","fireauth.RpcHandler.SECURE_TOKEN_ENDPOINT_","config","fireauth.RpcHandler.DEFAULT_SECURE_TOKEN_TIMEOUT_","fireauth.RpcHandler.DEFAULT_SECURE_TOKEN_HEADERS_","fireauth.RpcHandler.FIREBASE_ENDPOINT_","fireauth.RpcHandler.IDENTITY_PLATFORM_ENDPOINT_","fireauth.RpcHandler.DEFAULT_FIREBASE_TIMEOUT_","fireauth.RpcHandler.DEFAULT_FIREBASE_HEADERS_","isNode","fireauth.XmlHttpFactory.prototype.createInstance","fireauth.XmlHttpFactory.prototype.internalGetOptions","Ni","Content-Type","fireauth.RpcHandler.prototype.updateCustomLocaleHeader","fireauth.RpcHandler.FIREBASE_LOCALE_KEY_","fireauth.RpcHandler.prototype.updateEmulatorConfig","emulatorConfig","fireauth.RpcHandler.generateEmululatorEndpointUrl_","emulatorUri","fireauth.RpcHandler.prototype.updateClientVersion","fireauth.RpcHandler.prototype.sendXhr_","opt_callback","opt_httpMethod","fireauth.util.supportsCors","sendXhr","fireauth.util.BrowserName.CHROME","chromeVersion","fireauth.RpcHandler.loadGApi_","fireauth.RpcHandler.GAPI_CALLBACK_NAME_","goog.async.Deferred.prototype.addErrback","fireauth.RpcHandler.GAPI_SRC_","onload","fireauth.RpcHandler","fireauth.RpcHandler.prototype.getTenantId","fireauth.RpcHandler.prototype.sendXhrUsingXhrIo_","requestTimeout","xhrIo","setTimeout","clearTimeout","goog.net.XhrIo.prototype.getResponseText","getResponseText","dispose","fireauth.RpcHandler.prototype.requestAuthEndpoint_","httpMethod","opt_customErrorMap","opt_cachebuster","getApiKey","setParameterValue","isGet","GET","sendXhr_","fireauth.RpcHandler.getDeveloperError_","fireauth.RpcHandler.validateRequestHasEmail_","fireauth.util.EMAIL_ADDRESS_REGEXP_.test","fireauth.RpcHandler.validateEmailIfPresent_","fireauth.RpcHandler.validateIdTokenResponse_","fireauth.RpcHandler.validateVerifyPhoneNumberRequest_","fireauth.RpcHandler.prototype.sendXhrUsingGApiClient_","oauth2Token","body","authType","fireauth.RpcHandler.prototype.signInAnonymously","fireauth.RpcHandler.ApiMethod.SIGN_IN_ANONYMOUSLY","fireauth.RpcHandler.prototype.updateEmail","fireauth.RpcHandler.ApiMethod.SET_ACCOUNT_INFO","fireauth.RpcHandler.prototype.updatePassword","newPassword","fireauth.RpcHandler.PROFILE_FIELD_TO_ENUM_NAME_","photoUrl","fireauth.RpcHandler.validateFinalizePhoneMfaRequest_","fireauth.RpcHandler.validateVerifyAssertionRequest_","fireauth.RpcHandler.processVerifyAssertionResponse_","fireauth.constants.OIDC_PREFIX","NONCE","fireauth.RpcHandler.validateVerifyAssertionResponse_","fireauth.AuthErrorWithCredential.fromPlainObject","FEDERATED_USER_ID_ALREADY_LINKED","fireauth.RpcHandler.getDeveloperErrorFromCode_","fireauth.RpcHandler.prototype.verifyAssertion","fireauth.RpcHandler.ApiMethod.VERIFY_ASSERTION","fireauth.RpcHandler.prototype.verifyAssertionForLinking","fireauth.RpcHandler.ApiMethod.VERIFY_ASSERTION_FOR_LINKING","fireauth.RpcHandler.prototype.verifyAssertionForExisting","fireauth.RpcHandler.ApiMethod.VERIFY_ASSERTION_FOR_EXISTING","fireauth.RpcHandler.validateApplyActionCodeRequest_","Ii","fireauth.RpcHandler.prototype.updateProfile","profileData","fieldsToDelete","enumName","fieldName","fieldValue","fireauth.RpcHandler.prototype.sendPasswordResetEmail","additionalRequestData","requestType","fireauth.RpcHandler.ApiMethod.GET_OOB_CODE","fireauth.RpcHandler.prototype.sendSignInLinkToEmail","fireauth.RpcHandler.ApiMethod.GET_EMAIL_SIGNIN_CODE","fireauth.RpcHandler.prototype.sendEmailVerification","fireauth.RpcHandler.ApiMethod.GET_EMAIL_VERIFICATION_CODE","fireauth.RpcHandler.prototype.verifyBeforeUpdateEmail","fireauth.RpcHandler.ApiMethod.GET_EMAIL_VERIFICATION_CODE_BEFORE_UPDATE","fireauth.RpcHandler.prototype","fireauth.RpcHandler.prototype.verifyPhoneNumber","fireauth.RpcHandler.ApiMethod.VERIFY_PHONE_NUMBER","fireauth.RpcHandler.prototype.confirmPasswordReset","fireauth.RpcHandler.ApiMethod.RESET_PASSWORD","fireauth.RpcHandler.prototype.checkActionCode","fireauth.RpcHandler.ApiMethod.CHECK_ACTION_CODE","fireauth.RpcHandler.prototype.applyActionCode","fireauth.RpcHandler.ApiMethod.APPLY_OOB_CODE","APPLY_OOB_CODE","requestValidator","responseField","requireTenantId","CHECK_ACTION_CODE","responseValidator","fireauth.RpcHandler.validateCheckActionCodeResponse_","CREATE_ACCOUNT","fireauth.RpcHandler.validateCreateAccountRequest_","returnSecureToken","CREATE_AUTH_URI","DELETE_ACCOUNT","requestRequiredFields","DELETE_LINKED_ACCOUNTS","fireauth.RpcHandler.validateDeleteLinkedAccountsRequest_","EMAIL_LINK_SIGNIN","EMAIL_LINK_SIGNIN_FOR_LINKING","FINALIZE_PHONE_MFA_ENROLLMENT","useIdentityPlatformEndpoint","FINALIZE_PHONE_MFA_SIGN_IN","GET_ACCOUNT_INFO","GET_EMAIL_SIGNIN_CODE","fireauth.RpcHandler.validateEmailSignInCodeRequest_","GET_EMAIL_VERIFICATION_CODE","fireauth.RpcHandler.validateEmailVerificationCodeRequest_","GET_EMAIL_VERIFICATION_CODE_BEFORE_UPDATE","fireauth.RpcHandler.validateEmailVerificationCodeBeforeUpdateRequest_","GET_OOB_CODE","fireauth.RpcHandler.validateOobCodeRequest_","GET_PROJECT_CONFIG","cachebuster","GET_RECAPTCHA_PARAM","fireauth.RpcHandler.validateGetRecaptchaParamResponse_","RESET_PASSWORD","SEND_VERIFICATION_CODE","SESSION_INFO","SET_ACCOUNT_INFO","SET_ACCOUNT_INFO_SENSITIVE","fireauth.RpcHandler.validateSetAccountInfoSensitive_","SIGN_IN_ANONYMOUSLY","START_PHONE_MFA_ENROLLMENT","fireauth.RpcHandler.validateStartPhoneMfaEnrollmentRequest_","fireauth.RpcHandler.validateStartPhoneMfaEnrollmentResponse_","START_PHONE_MFA_SIGN_IN","fireauth.RpcHandler.validateStartPhoneMfaSignInRequest_","fireauth.RpcHandler.validateStartPhoneMfaSignInResponse_","VERIFY_ASSERTION","responsePreprocessor","VERIFY_ASSERTION_FOR_EXISTING","fireauth.RpcHandler.validateVerifyAssertionForExistingResponse_","USER_NOT_FOUND","VERIFY_ASSERTION_FOR_LINKING","fireauth.RpcHandler.validateVerifyAssertionLinkRequest_","VERIFY_CUSTOM_TOKEN","fireauth.RpcHandler.validateVerifyCustomTokenRequest_","VERIFY_PASSWORD","fireauth.RpcHandler.validateVerifyPasswordRequest_","VERIFY_PHONE_NUMBER","VERIFY_PHONE_NUMBER_FOR_LINKING","fireauth.RpcHandler.validateVerifyPhoneNumberLinkRequest_","fireauth.RpcHandler.validateVerifyPhoneNumberForLinkingResponse_","VERIFY_PHONE_NUMBER_FOR_EXISTING","customErrorMap","fireauth.RpcHandler.verifyPhoneNumberForExistingErrorMap_","WITHDRAW_MFA","fireauth.RpcHandler.validateWithdrawMfaResponse_","fireauth.RpcHandler.prototype.invokeRpc","fireauth.object.hasNonEmptyFields","opt_fields","field","POST","requestAuthEndpoint_","tempResponse","serverErrorCode","errors","prefixCode","errorReasonMap","keyInvalid","ipRefererBlocked","MISSING_CUSTOM_TOKEN","INVALID_IDENTIFIER","MISSING_PASSWORD","PASSWORD_LOGIN_DISABLED","INVALID_PENDING_TOKEN","EMAIL_NOT_FOUND","RESET_PASSWORD_EXCEED_LIMIT","MISSING_OOB_CODE","INVALID_ID_TOKEN","INVALID_TEMPORARY_PROOF","SESSION_EXPIRED","errorMap","errorMessage","fireauth.iframeclient.IframeWrapper","fireauth.iframeclient.IframeWrapper.prototype.open_","open_","fireauth.iframeclient.IframeWrapper.cachedGApiLoader_","onGapiLoad","ontimeout","fireauth.iframeclient.IframeWrapper.NETWORK_TIMEOUT_.get","cbName","fireauth.iframeclient.IframeWrapper.GAPI_LOADER_SRC_","where","messageHandlersFilter","style","position","dontclear","onOpen","clearTimerAndResolve","networkErrorTimer","setHideOnLeave","fireauth.iframeclient.IframeWrapper.PING_TIMEOUT_.get","fireauth.iframeclient.IframeWrapper.NETWORK_TIMEOUT_","fireauth.iframeclient.IframeWrapper.PING_TIMEOUT_","fireauth.iframeclient.IframeUrlBuilder","authDomain","appName","fireauth.iframeclient.SCHEME","fireauth.iframeclient.PORT_NUMBER","fireauth.iframeclient.IfcHandler","opt_clientVersion","opt_endpointId","fireauth.iframeclient.IfcHandler.getOriginValidator_","fireauth.RpcHandler.ApiMethod.GET_PROJECT_CONFIG","authorizedDomains","domainPattern","fireauth.util.IP_ADDRESS_REGEXP_.test","escapedDomainPattern","test","RegExp","fireauth.iframeclient.IfcHandler.prototype.initialize","fireauth.util.onDomReady","clientVersion_","endpointId_","builder","fireauth.iframeclient.OAuthUrlBuilder.getAuthFrameworksForApp_","opt_frameworks","setEndpointId","fireauth.iframeclient.IfcHandler.prototype.registerEvents_","fireauth.iframeclient.IframeWrapper.prototype.registerEvent","AUTH_EVENT","registerEvent","resolveResponse","isHandled","fireauth.AuthEvent.fromPlainObject","authEvent","ACK","fireauth.iframeclient.IfcHandler.prototype.getRpcHandler_","fireauth.util.getClientVersion","updateEmulatorConfig","opt_redirectUrl","opt_additionalParams","fireauth.iframeclient.OAuthUrlBuilder","fireauth.storage.AsyncStorage","opt_asyncStorage","ASYNC_STORAGE","fireauth.messagechannel.Receiver","eventTarget","fireauth.iframeclient.IframeUrlBuilder.prototype.toString","fireauth.iframeclient.OAuthUrlBuilder.prototype.setTenantId","fireauth.iframeclient.OAuthUrlBuilder.prototype.toString","provider_","getAuthLanguage_","reservedParams_","scopes","getAuthFrameworks_","frameworks","Yj","fireauth.iframeclient.IfcHandler.prototype.startPopupTimeout","popupWin","onError","timeoutDuration","popupClosedByUserError","webStorageNotSupportedError","isResolved","fireauth.iframeclient.IfcHandler.prototype.isWebStorageSupported","WEB_STORAGE_SUPPORT_EVENT","fireauth.iframeclient.IframeWrapper.prototype.sendMessage","isSupported","repeat","fireauth.util.POPUP_WAIT_CYCLE_MS_","fireauth.util.onPopupClose","fireauth.iframeclient.IfcHandler.prototype.shouldBeInitializedEarly","fireauth.iframeclient.IfcHandler.prototype.hasVolatileStorage","fireauth.iframeclient.IfcHandler.prototype.processPopup","onInitialize","opt_alreadyRedirected","getRpcHandler_","onReady","fireauth.iframeclient.IfcHandler.getOAuthHelperWidgetUrl","fireauth.iframeclient.IfcHandler.prototype","fireauth.iframeclient.IfcHandler.prototype.processRedirect","fireauth.iframeclient.IfcHandler.prototype.initializeAndWait","initialize","fireauth.iframeclient.IfcHandler.prototype.unloadsOnRedirect","fireauth.iframeclient.IfcHandler.prototype.addAuthEventListener","fireauth.iframeclient.IfcHandler.prototype.removeAuthEventListener","ele","fk","fireauth.storage.AsyncStorage.prototype.get","fireauth.storage.AsyncStorage.prototype.set","fireauth.storage.AsyncStorage.prototype.remove","fireauth.storage.AsyncStorage.prototype.addStorageListener","fireauth.storage.AsyncStorage.prototype.removeStorageListener","rk","fireauth.messagechannel.Receiver.receivers_","fireauth.messagechannel.Receiver.prototype.subscribe","eventType","fireauth.messagechannel.WorkerClientPostMessager","fireauth.messagechannel.Sender","postMessager","fireauth.messagechannel.Sender.prototype.send","opt_useLongTimeout","onMessage","ackTimer","completionTimer","CONNECTION_UNAVAILABLE","ackTimeout","LONG_ACK","messageChannel","MessageChannel","digits","UNSUPPORTED_EVENT","event","COMPLETION","UNKNOWN","INVALID_RESPONSE","removeMessageHandler_","fireauth.messagechannel.Sender.prototype.removeMessageHandler_","messageHandler","fireauth.storage.IndexedDB","fireauth.storage.IndexedDB.isAvailable","goog.global.indexedDB","INDEXEDDB","instance","receiver","subscribe","keyProcessed","registration","sw","send","fireauth.storage.IndexedDB.prototype.initializeDb_","fireauth.storage.IndexedDB.DB_NAME_","fireauth.storage.IndexedDB.VERSION_","request.onerror","request.onupgradeneeded","db","fireauth.storage.IndexedDB.DATA_OBJECT_STORE_NAME_","keyPath","fireauth.storage.IndexedDB.DATA_KEY_PATH_","request.onsuccess","fireauth.storage.IndexedDB.prototype.deleteDb_","newDb","fireauth.storage.IndexedDB.prototype.initializeDbAndRun_","initializeDb_","fireauth.storage.IndexedDB.prototype.withRetry_","transaction","numAttempts","attempt","initializeDbAndRun_","fireauth.storage.IndexedDB.TRANSACTION_RETRY_COUNT_","fireauth.storage.IndexedDB.prototype.getDataObjectStore_","tx","fireauth.storage.IndexedDB.prototype.getTransaction_","isReadWrite","fireauth.storage.IndexedDB.prototype.onIDBRequest_","fireauth.storage.IndexedDB.prototype.notifySW_","fireauth.util.getServiceWorkerController","fireauth.storage.IndexedDB.prototype.sync_","objectStore","getDataObjectStore_","getTransaction_","onIDBRequest_","cursor","centralCopy","diffKeys","fireauth.util.getKeyDiff","diff","fireauth.storage.IndexedDB.prototype.stopListeners_","fireauth.storage.IndexedDB.STOP_ERROR_","fireauth.storage.HybridIndexedDB","fallbackStorage","randomId","randomKey","fireauth.storage.HybridIndexedDB.KEY_","fireauth.storage.IndexedDB.managerInstance_","fireauth.storage.InMemoryStorage","IN_MEMORY","fireauth.storage.LocalStorage","fireauth.storage.LocalStorage.isAvailable","fireauth.storage.LocalStorage.getGlobalStorage","fireauth.storage.LocalStorage.STORAGE_AVAILABLE_KEY_","LOCAL_STORAGE","fireauth.storage.NullStorage","NULL_STORAGE","fireauth.storage.SessionStorage","fireauth.storage.SessionStorage.isAvailable","fireauth.storage.SessionStorage.getGlobalStorage","fireauth.storage.SessionStorage.STORAGE_AVAILABLE_KEY_","SESSION_STORAGE","fireauth.storage.Factory","envMap","fireauth.storage.Factory.EnvConfig.BROWSER","fireauth.storage.Factory.EnvConfig.NODE","fireauth.storage.Factory.EnvConfig.REACT_NATIVE","fireauth.storage.Factory.EnvConfig.WORKER","fireauth.messagechannel.Receiver.prototype.handleEvent_","handlers","allResponses","fireauth.messagechannel.WorkerClientPostMessager.prototype.postMessage","transfer","fireauth.messagechannel.Sender.prototype.close","ok","fireauth.storage.IndexedDB.prototype.set","isLocked","withRetry_","notifySW_","fireauth.storage.IndexedDB.prototype","fireauth.storage.IndexedDB.prototype.get","fireauth.storage.IndexedDB.prototype.remove","fireauth.storage.IndexedDB.prototype.addStorageListener","fireauth.storage.IndexedDB.prototype.startListeners_","stopListeners_","startListeners_","sync_","fireauth.storage.IndexedDB.POLLING_DELAY_","fireauth.storage.IndexedDB.prototype.removeStorageListener","Ck","fireauth.storage.HybridIndexedDB.prototype.get","fireauth.storage.HybridIndexedDB.prototype.set","fireauth.storage.HybridIndexedDB.prototype.remove","fireauth.storage.HybridIndexedDB.prototype.addStorageListener","fireauth.storage.HybridIndexedDB.prototype.removeStorageListener","Dk","fireauth.storage.InMemoryStorage.prototype.get","fireauth.storage.InMemoryStorage.prototype.set","fireauth.storage.InMemoryStorage.prototype.remove","fireauth.storage.InMemoryStorage.prototype.addStorageListener","fireauth.storage.InMemoryStorage.prototype.removeStorageListener","Ek","fireauth.storage.LocalStorage.prototype.get","fireauth.storage.LocalStorage.prototype.set","fireauth.storage.LocalStorage.prototype.remove","fireauth.storage.LocalStorage.prototype.addStorageListener","fireauth.storage.LocalStorage.prototype.removeStorageListener","Hk","fireauth.storage.NullStorage.prototype.get","fireauth.storage.NullStorage.prototype.set","fireauth.storage.NullStorage.prototype.remove","fireauth.storage.NullStorage.prototype.addStorageListener","fireauth.storage.NullStorage.prototype.removeStorageListener","Ik","fireauth.storage.SessionStorage.prototype.get","fireauth.storage.SessionStorage.prototype.set","fireauth.storage.SessionStorage.prototype.remove","fireauth.storage.SessionStorage.prototype.addStorageListener","fireauth.storage.SessionStorage.prototype.removeStorageListener","Qk","Uk","persistent","temporary","fireauth.authStorage.Persistence","LOCAL","NONE","SESSION","fireauth.authStorage.Manager","safariLocalStorageNotSynced","runsInBackground","webStorageSupported","fireauth.storage.Factory.instance_","storageFactory","fireauth.authStorage.Manager.instance_","fireauth.authStorage.Manager.prototype.getStorage_","fireauth.authStorage.Manager.prototype.getKeyName_","dataKey","fireauth.authStorage.SEPARATOR_","fireauth.authStorage.Manager.prototype.remove","getStorage_","fireauth.authStorage.Manager.prototype.stopManualListeners_","clearInterval","fireauth.storage.AuthEventManager","appId","fireauth.authStorage.Manager.getInstance","Tk","fireauth.authStorage.Manager.prototype.get","fireauth.authStorage.Manager.prototype","fireauth.authStorage.Manager.prototype.set","serializedValue","fireauth.authStorage.Manager.prototype.addListener","fireauth.authStorage.Manager.prototype.startManualListeners_","stopManualListeners_","setInterval","currentValue","oldValue","newValue","poll","fireauth.authStorage.Manager.LOCAL_STORAGE_POLLING_TIMER_","fireauth.authStorage.Manager.prototype.removeListener","fireauth.authStorage.Manager.prototype.storageChangeEvent_","storedValue","realValue","triggerListeners","fireauth.authStorage.IE10_LOCAL_STORAGE_SYNC_DELAY","fireauth.authStorage.Manager.prototype.callListeners_","il","fireauth.storage.OAuthHandlerManager","goog.crypt.Sha2","numHashBlocks","initHashBlocks","goog.crypt.Sha2.BLOCKSIZE_","Int32Array","goog.crypt.Sha2.Kx_","goog.crypt.Sha2.K_","goog.crypt.Hash","kl","array","goog.crypt.Sha2.PADDING_","goog.crypt.Sha2.prototype.computeChunk_","chunk","w","offset","rounds","w_15","partialSum1","partialSum2","w_2","t2","t1","goog.crypt.Sha2.prototype.update","opt_length","inChunk","computeChunk_","goog.crypt.Sha2.prototype.reset","goog.crypt.Sha256","goog.crypt.Sha2.call","goog.crypt.Sha256.INIT_HASH_BLOCK_","fireauth.CordovaHandler","endpointId","fireauth.util.STORAGE_KEY_SEPARATOR_","fireauth.CordovaHandler.getError_","fireauth.CordovaHandler.prototype.computeSecureHash_","sha256","totalBits","update","digest","numByte","hexByte","fireauth.CordovaHandler.prototype.dispatchEvent_","fireauth.CordovaHandler.prototype.getInitialAuthEvent_","universalLinkCb","eventData","initialResolve","noEventTimer","noEvent","errorObject","callbackUrl","dispatchEvent_","fireauth.CordovaHandler.prototype.setAuthEventListener_","existingHandlerOpenURL","authEventCallback","fireauth.CordovaHandler.INITIAL_TIMEOUT_MS_","fireauth.UniversalLinkSubscriber.prototype.subscribe","this.masterCb_","fireauth.CordovaHandler.prototype.getPartialStoredEvent_","fireauth.storage.AuthEventManager.prototype.getAuthEvent","fireauth.storage.AuthEventManager.Keys.AUTH_EVENT","fireauth.storage.PendingRedirectManager","sl","fireauth.CordovaHandler.prototype.initializeAndWait","timeoutId","fireauth.util.CORDOVA_ONDEVICEREADY_TIMEOUT_MS_","fireauth.CordovaHandler.prototype","fireauth.CordovaHandler.prototype.startPopupTimeout","fireauth.CordovaHandler.prototype.processPopup","fireauth.CordovaHandler.prototype.unloadsOnRedirect","fireauth.CordovaHandler.prototype.shouldBeInitializedEarly","fireauth.CordovaHandler.prototype.hasVolatileStorage","fireauth.CordovaHandler.prototype.processRedirect","onClose","onResume","fireauth.CordovaHandler.prototype.processRedirectInternal_","numOfChars","fireauth.CordovaHandler.SESSION_ID_TOTAL_CHARS_","allowedChars","generateSessionId_","appIdentifier","appDisplayName","additionalParams","computeSecureHash_","hashedSessionId","oauthHelperWidgetUrl","storageKey_","isAvailable","openUrl","closeBrowsertab","fireauth.CordovaHandler.REDIRECT_TIMEOUT_MS_","cleanup","fireauth.CordovaHandler.prototype.addAuthEventListener","getInitialAuthEvent_","fireauth.CordovaHandler.prototype.removeAuthEventListener","fireauth.storage.PendingRedirectManager.PENDING_REDIRECT_KEY_","fireauth.storage.PendingRedirectManager.prototype.removePendingStatus","fireauth.AuthEventManager","fireauth.RedirectAuthEventProcessor","fireauth.PopupAuthEventProcessor","fireauth.AuthEventManager.getKey_","fireauth.AuthEventManager.instantiateOAuthSignInHandler","fireauth.constants.clientEndpoint","fireauth.AuthEventManager.prototype.initialize","previousOauthSignInHandler","fireauth.AuthEventManager.prototype.initializeWithNoPendingRedirectResult_","notSupportedEvent","defaultToEmptyResponse","fireauth.AuthEventManager.prototype.subscribe","fireauth.storage.PendingRedirectManager.prototype.getPendingStatus","fireauth.storage.PendingRedirectManager.PENDING_FLAG_","removePendingStatus","initializeWithNoPendingRedirectResult_","fireauth.AuthEventManager.prototype.unsubscribe","fireauth.AuthEventManager.prototype.reset","fireauth.AuthEventManager.prototype.handleAuthEvent_","fireauth.AuthEventManager.EVENT_DUPLICATION_CACHE_DURATION","hasProcessedAuthEvent_","processed","potentialHandler","eventManager","saveProcessedAuthEvent_","fireauth.AuthEventManager.POPUP_TIMEOUT_MS_","fireauth.AuthEventManager.REDIRECT_TIMEOUT_MS_","fireauth.AuthEventManager.prototype.processPopup","fireauth.AuthEventManager.isCordovaFalsePositive_","fireauth.AuthEventManager.prototype.processRedirect","fireauth.storage.PendingRedirectManager.prototype.setPendingStatus","fireauth.AuthEventManager.prototype.startPopupTimeout","owner","fireauth.AuthEventManager.POPUP_TIMEOUT_MS_.get","fireauth.AuthEventManager.prototype.getRedirectResult","fireauth.AuthEventManager.manager_","fireauth.AuthEventManager.KEY_SEPARATOR_","fireauth.AuthEventManager.getManager","fireauth.RedirectAuthEventProcessor.prototype.defaultToEmptyResponse","setRedirectResult_","fireauth.RedirectAuthEventProcessor.prototype.clearRedirectResult","fireauth.RedirectAuthEventProcessor.prototype.setRedirectResolve_","popupRedirectResult","this.redirectedUserPromise_","fireauth.RedirectAuthEventProcessor.prototype.setRedirectResult_","isRedirect","fireauth.RedirectAuthEventProcessor.prototype.setRedirectReject_","setRedirectReject_","setRedirectResolve_","user","fireauth.AuthSettings","setAppVerificationDisabledForTesting","fireauth.ConfirmationResult","credentialResolver","auth","appVerifier","verifyPhoneNumber","fireauth.IdTokenResult","expirationTime","authTime","issuedAtTime","signInProvider","signInSecondFactor","claims","fireauth.MultiFactorResolver","errorResponse","onIdTokenResolver","fireauth.MultiFactorResolver.SignInResponseField.MFA_PENDING_CREDENTIAL","fireauth.MultiFactorResolver.SignInResponseField.MFA_INFO","mfaEnrollment","fireauth.RedirectAuthEventProcessor.prototype.reset","fireauth.RedirectAuthEventProcessor.prototype.processAuthEvent","isWebStorageNotSupported","isOperationNotSupported","processErrorEvent_","fireauth.RedirectAuthEventProcessor.prototype.processSuccessEvent_","popupRedirectResponse","processSuccessEvent_","processUnknownEvent_","fireauth.RedirectAuthEventProcessor.prototype.getRedirectResult","fireauth.RedirectAuthEventProcessor.prototype.startRedirectTimeout_","fireauth.AuthEventManager.REDIRECT_TIMEOUT_MS_.get","fireauth.PopupAuthEventProcessor.prototype.processAuthEvent","fireauth.ConfirmationResult.prototype.confirm","fireauth.PhoneAuthProvider.credential","MFA_PENDING_CREDENTIAL","fireauth.MultiFactorError","fireauth.MultiFactorAssertion","fireauth.AuthCredentialMultiFactorAssertion","multiFactorAuthCredential","phoneAuthCredential","fireauth.AuthCredentialMultiFactorAssertion.call","fireauth.UserEvent","fireauth.MultiFactorUser","accountInfo","USER_RELOADED","enrolledFactors","toISOString","updateEnrolledFactors_","fireauth.MultiFactorUser.prototype.updateEnrolledFactors_","fireauth.ProactiveRefresh","retryPolicy","getWaitDuration","RETRIAL_MIN_WAIT","RETRIAL_MAX_WAIT","fireauth.ProactiveRefresh.prototype.getNextRun_","hasSucceeded","fireauth.StsTokenManager","fireauth.StsTokenManager.prototype.setExpiresIn","expiresIn","fireauth.StsTokenManager.prototype.parseServerResponse","setAccessToken","setExpiresIn","fireauth.StsTokenManager.prototype.requestToken_","fireauth.RpcHandler.prototype.requestStsToken","refreshToken","fireauth.UserMetadata","opt_createdAt","opt_lastLoginAt","lastSignInTime","creationTime","opt_email","opt_displayName","opt_photoURL","opt_phoneNumber","photoURL","fireauth.AuthUser","appOptions","stsTokenResponse","opt_accountInfo","fireauth.AuthUser.prototype.initializeProactiveRefreshUtility_","clientFullVersion","setLastAccessToken_","setAccountInfo","initializeProactiveRefreshUtility_","waitInterval","OFFSET_DURATION","this.onLanguageCodeChanged_","this.onEmulatorConfigChanged_","setEmulatorConfig","this.onFrameworkChanged_","setFramework","fireauth.AuthUser.prototype.setEmulatorConfig","oldManager","unsubscribe","fireauth.AuthUser.prototype.setLanguageCodeChangeDispatcher","dispatcher","LANGUAGE_CODE_CHANGED","fireauth.AuthUser.prototype.setEmulatorConfigChangeDispatcher","EMULATOR_CONFIG_CHANGED","fireauth.AuthUser.prototype.setFramework","framework","fireauth.AuthUser.prototype.setFrameworkChangeDispatcher","FRAMEWORK_CHANGED","fireauth.AuthUser.prototype.getAuth_","fireauth.AuthUser.prototype.startProactiveRefresh","TOKEN_CHANGED","fireauth.AuthUser.prototype.stopProactiveRefresh","fireauth.AuthUser.prototype.setLastAccessToken_","lastAccessToken","fireauth.AuthUser.prototype.notifyStateChangeListeners_","fireauth.AuthUser.prototype.enablePopupRedirect","fireauth.AuthUser.prototype.setAccountInfo","emailVerified","isAnonymous","metadata","providerData","fireauth.AuthUser.prototype.checkDestroyed_","fireauth.AuthUser.prototype.getProviderIds","fireauth.AuthUser.prototype.addProviderData","removeProviderData","fireauth.AuthUser.prototype.removeProviderData","fireauth.AuthUser.prototype.updateProperty","propName","fireauth.AuthUser.prototype.copy","userToCopy","fireauth.StsTokenManager.prototype.copy","fireauth.UserMetadata.prototype.clone","addProviderData","tokenManagerToCopy","fireauth.AuthUser.prototype.reloadWithoutSaving_","fireauth.AuthUser.prototype.setUserAccountInfoFromToken_","fireauth.RpcHandler.ApiMethod.GET_ACCOUNT_INFO","updateProperty","fireauth.AuthUser.prototype.updateTokensIfPresent","parseServerResponse","notifyAuthListeners_","fireauth.AuthUser.prototype.checkIfAlreadyLinked_","reloadWithoutSaving_","getProviderIds","fireauth.AuthUser.prototype.getUserCredential_","idTokenResponse","operationType","additionalUserInfo","fireauth.AuthUser.prototype.finalizeLinking_","updateTokensIfPresent","fireauth.AuthUser.prototype.runOperationWithPopup_","additionalCheck","isReauthOperation","settings","generateEventId_","getAuthEventManager","startPopupTimeout","registerPendingPromise_","fireauth.AuthUser.prototype.runOperationWithRedirect_","errorThrown","fireauth.storage.RedirectUserManager.REDIRECT_USER_KEY_","redirectUser","fireauth.AuthUser.prototype.getAuthEventManager","fireauth.AuthUser.prototype.registerPendingPromise_","opt_skipInvalidationCheck","fireauth.AuthUser.prototype.checkIfInvalidated_","processedP","checkIfInvalidated_","USER_INVALIDATED","multiFactorError","fireauth.MultiFactorError.fromPlainObject","getAuth_","stsTokenManagerResponse","firebaseUser","fireauth.storage.RedirectUserManager","fireauth.MultiFactorResolver.prototype.resolveSignIn","newSignInResponse","fireauth.MultiFactorAssertion.prototype.process","fireauth.MultiFactorAssertion.prototype.finalizeMfaEnrollment_","finalizeMfaEnrollment_","phoneVerificationInfo","fireauth.RpcHandler.ApiMethod.FINALIZE_PHONE_MFA_ENROLLMENT","fireauth.MultiFactorAssertion.prototype.finalizeMfaSignIn_","finalizeMfaSignIn_","fireauth.RpcHandler.ApiMethod.FINALIZE_PHONE_MFA_SIGN_IN","signInRequest","fireauth.PhoneMultiFactorAssertion","tm","fireauth.MultiFactorUser.prototype.handleUserReload_","fireauth.MultiFactorUser.extractEnrolledFactors_","fireauth.MultiFactorUser.prototype","fireauth.MultiFactorUser.prototype.getSession","fireauth.MultiFactorUser.prototype.enroll","tokenResponse","fireauth.MultiFactorUser.prototype.unenroll","fireauth.RpcHandler.ApiMethod.WITHDRAW_MFA","fireauth.MultiFactorUser.prototype.toPlainObject","multiFactor","fireauth.ProactiveRefresh.prototype.start","fireauth.ProactiveRefresh.prototype.process_","getNextRun_","process_","fireauth.ProactiveRefresh.prototype.stop","fireauth.StsTokenManager.prototype.toPlainObject","getExpirationTime","fireauth.StsTokenManager.prototype.getToken","forceRefresh","isRefreshTokenExpired","isExpired_","fireauth.StsTokenManager.TOKEN_REFRESH_BUFFER","requestToken_","exchangeRefreshToken_","grant_type","refresh_token","fireauth.UserMetadata.prototype.toPlainObject","lastLoginAt","createdAt","fireauth.AuthUser.prototype.setLanguageCode","fireauth.AuthUser.prototype.getLanguageCode","fireauth.AuthUser.prototype.getFramework","fireauth.AuthUser.prototype.handleUserTokenChange_","FIREBASE","Im","fireauth.AuthUser.prototype.reload","checkDestroyed_","fireauth.AuthUser.returnNothing_","fireauth.AuthUser.prototype","fireauth.AuthUser.prototype.getIdTokenResult","opt_forceRefresh","fireauth.AuthUser.prototype.getIdToken","fireauth.AuthUser.prototype.parseAccountInfo_","users","linkedAccounts","providerInfo","fireauth.AuthUserInfo","userServerResponse","fireauth.AuthUser.prototype.reauthenticateAndRetrieveDataWithCredential","fireauth.deprecation.log","REAUTH_WITH_CREDENTIAL","fireauth.AuthUser.prototype.reauthenticateWithCredential","userCredential","getUserCredential_","REAUTHENTICATE","fireauth.AuthUser.prototype.linkAndRetrieveDataWithCredential","LINK_WITH_CREDENTIAL","fireauth.AuthUser.prototype.linkWithCredential","checkIfAlreadyLinked_","LINK","fireauth.AuthUser.prototype.linkWithPhoneNumber","fireauth.AuthUser.prototype.reauthenticateWithPhoneNumber","fireauth.AuthUser.prototype.updateEmail","fireauth.AuthUser.prototype.updatePhoneNumber","phoneCredential","fireauth.AuthUser.prototype.updatePassword","fireauth.AuthUser.prototype.updateProfile","profile","profileRequest","fireauth.AuthUser.prototype.unlink","fireauth.RpcHandler.ApiMethod.DELETE_LINKED_ACCOUNTS","deleteProvider","remainingProviderIds","pId","notifyStateChangeListeners_","fireauth.AuthUser.prototype.delete","fireauth.RpcHandler.ApiMethod.DELETE_ACCOUNT","fireauth.AuthUser.prototype.canHandleAuthEvent","LINK_VIA_POPUP","getPopupEventId","REAUTH_VIA_POPUP","LINK_VIA_REDIRECT","getRedirectEventId","REAUTH_VIA_REDIRECT","fireauth.AuthUser.prototype.resolvePendingPopupEvent","fireauth.AuthUser.prototype.getAuthEventHandlerFinisher","fireauth.AuthUser.prototype.linkWithPopup","runOperationWithPopup_","fireauth.AuthUser.prototype.reauthenticateWithPopup","fireauth.AuthUser.prototype.linkWithRedirect","runOperationWithRedirect_","fireauth.AuthUser.prototype.reauthenticateWithRedirect","fireauth.AuthUser.prototype.finishPopupAndRedirectLink","fireauth.AuthUser.prototype.finishPopupAndRedirectReauth","fireauth.AuthUser.prototype.sendEmailVerification","opt_actionCodeSettings","latestIdToken","buildRequest","fireauth.AuthUser.prototype.verifyBeforeUpdateEmail","fireauth.AuthUser.prototype.handleMultiFactorIdTokenResolver_","fireauth.AuthCredential.verifyTokenResponseUid","fireauth.AuthUser.prototype.toJSON","fireauth.AuthUser.prototype.toPlainObject","stsTokenManager","redirectEventId","fireauth.object.makeWritableCopy","fireauth.storage.RedirectUserManager.prototype.removeRedirectUser","fireauth.storage.UserManager","fireauth.storage.UserManager.prototype.initialize_","localKey","sessionKey","inMemoryKey","fireauth.storage.UserManager.getAuthUserKey_","fireauth.authStorage.Manager.prototype.migrateFromLocalStorage","oldStorageValue","fireauth.storage.UserManager.PERSISTENCE_KEY_","persistence","currentKey","fireauth.storage.UserManager.prototype.removeAllExcept_","toFulfill","onFulfill","onReject","fireauth.storage.UserManager.prototype.switchToLocalOnExternalEvent_","waitForReady_","fireauth.storage.UserManager.AUTH_USER_KEY_NAME_","fireauth.storage.UserManager.prototype.setCurrentUser","currentUser","fireauth.storage.UserManager.prototype.removeCurrentUser","fireauth.storage.UserManager.prototype.getCurrentUser","fireauth.storage.UserManager.prototype.waitForReady_","fireauth.Auth","app","app_","fireauth.Auth.prototype.initAuthState_","fireauth.Auth.prototype.initAuthRedirectState_","fireauth.Auth.prototype.initAuthEventManager_","setCurrentUser_","getStorageKey","initAuthState_","fireauth.Auth.prototype.initRedirectUser_","fireauth.storage.RedirectUserManager.prototype.getRedirectUser","opt_authDomain","getRedirectUser","initRedirectUser_","removeCurrentUser","initAuthRedirectState_","userStorageManager_","initAuthEventManager_","redirectUser_","enablePopupRedirect","currentUser_","fireauth.Auth.prototype.initializeReadableWritableProps_","initializeReadableWritableProps_","getEmulatorConfig","host","disableWarnings","fireauth.Auth.prototype.getAuthEventManager_","fireauth.Auth.prototype.getRedirectResultWithoutClearing_","getAuthEventManager_","fireauth.Auth.prototype.signInWithIdTokenResponse","opt_redirectStorageManager","fireauth.AuthUser.initializeFromIdTokenResponse","fireauth.Auth.prototype.setCurrentUser_","fireauth.AuthUser.prototype.removeStateChangeListener","attachEventListeners_","stopProactiveRefresh","startProactiveRefresh","setUserLanguage_","setLanguageCodeChangeDispatcher","setUserFramework_","setFrameworkChangeDispatcher","setUserEmulatorConfig_","setEmulatorConfigChangeDispatcher","fireauth.Auth.prototype.signInWithIdTokenProvider_","idTokenPromise","fireauth.Auth.prototype.currentUser_","fireauth.Auth.prototype.getLastAccessToken_","fireauth.Auth.prototype.notifyAuthListeners_","getLastAccessToken_","fireauth.Auth.prototype.registerPendingPromise_","fireauth.grecaptcha","fireauth.GRecaptchaMockFactory","fireauth.GRecaptchaMockFactory.START_INSTANCE_ID","fireauth.storage.UserManager.prototype.setPersistence","invalidTypeError","unsupportedTypeError","fireauth.Auth.LanguageCodeChangeEvent","fireauth.Auth.EmulatorConfigChangeEvent","fireauth.Auth.FrameworkChangeEvent","Fn","fireauth.Auth.prototype.setPersistence","fireauth.Auth.prototype.setLanguageCode","updateCustomLocaleHeader","notifyLanguageCodeListeners_","fireauth.Auth.prototype.getLanguageCode","fireauth.Auth.prototype.useDeviceLanguage","fireauth.Auth.prototype.useEmulator","disableBanner","goog.global.document.createElement","notifyEmulatorConfigListeners_","fireauth.Auth.prototype","fireauth.Auth.prototype.logFramework","frameworkId","fireauth.Auth.prototype.getFramework","fireauth.Auth.prototype.setTenantId","fireauth.Auth.prototype.getTenantId","fireauth.Auth.prototype.toJSON","fireauth.Auth.prototype.canHandleAuthEvent","SIGN_IN_VIA_REDIRECT","SIGN_IN_VIA_POPUP","fireauth.Auth.prototype.resolvePendingPopupEvent","fireauth.Auth.prototype.getAuthEventHandlerFinisher","fireauth.Auth.prototype.finishPopupAndRedirectSignIn","verifyAssertion","fireauth.Auth.prototype.signInWithPopup","manager","fireauth.Auth.prototype.signInWithRedirect","fireauth.storage.UserManager.prototype.savePersistenceForRedirect","fireauth.Auth.prototype.getRedirectResult","getRedirectResultWithoutClearing_","clearRedirectResult","fireauth.Auth.prototype.updateCurrentUser","opt_appOptions","stsTokenManager_","newUser","getRefreshToken","fireauth.Auth.prototype.signOut","fireauth.Auth.prototype.syncAuthUserChanges_","thisId","thatId","U","otherUser","fireauth.Auth.prototype.handleUserStateChange_","fireauth.Auth.prototype.handleUserTokenChange_","fireauth.Auth.prototype.handleUserDelete_","fireauth.Auth.prototype.handleUserInvalidated_","fireauth.Auth.prototype.handleMultiFactorIdTokenResolver_","fireauth.Auth.prototype.initIdTokenChangeObserver_","observer","fireauth.Auth.prototype.initUserStateObserver_","fireauth.Auth.prototype.addUserChangeListener_","addUserChangeListener_","fireauth.Auth.prototype.onIdTokenChanged","nextOrObserver","opt_completed","Promise","fireauth.Auth.prototype.onAuthStateChanged","fireauth.Auth.prototype.getIdTokenInternal","stsAccessToken","fireauth.Auth.prototype.signInWithCustomToken","fireauth.RpcHandler.ApiMethod.VERIFY_CUSTOM_TOKEN","fireauth.Auth.prototype.signInWithEmailAndPassword","fireauth.Auth.prototype.createUserWithEmailAndPassword","fireauth.RpcHandler.ApiMethod.CREATE_ACCOUNT","fireauth.Auth.prototype.signInWithCredential","fireauth.Auth.prototype.signInAndRetrieveDataWithCredential","SIGN_IN_WITH_CREDENTIAL","fireauth.Auth.prototype.signInAnonymously","fireauth.Auth.prototype.getUid","fireauth.Auth.prototype.addAuthTokenListenerInternal","fireauth.Auth.prototype.removeAuthTokenListenerInternal","fireauth.Auth.prototype.addAuthTokenListener","fireauth.Auth.prototype.removeAuthTokenListener","fireauth.Auth.prototype.delete","fireauth.Auth.prototype.fetchSignInMethodsForEmail","getRpcHandler","fireauth.RpcHandler.ApiMethod.CREATE_AUTH_URI","identifier","continueUri","fireauth.Auth.prototype.isSignInWithEmailLink","fireauth.Auth.prototype.sendSignInLinkToEmail","actionCodeSettings","actionCodeSettingsBuilder","fireauth.Auth.prototype.verifyPasswordResetCode","fireauth.Auth.prototype.confirmPasswordReset","fireauth.Auth.prototype.checkActionCode","fireauth.Auth.prototype.applyActionCode","fireauth.Auth.prototype.sendPasswordResetEmail","fireauth.Auth.prototype.signInWithPhoneNumber","fireauth.ConfirmationResult.initialize","fireauth.Auth.prototype.signInWithEmailLink","opt_link","fireauth.grecaptcha.prototype.render","fireauth.grecaptcha.prototype.reset","fireauth.grecaptcha.prototype.getResponse","fireauth.grecaptcha.prototype.execute","fireauth.GRecaptchaMockFactory.instance_","fireauth.GRecaptchaMockFactory.prototype.getMock_","getId_","fireauth.GRecaptchaMockFactory.prototype.getId_","fireauth.RecaptchaMock","elementOrId","this.onClickHandler_","CLICK","fireauth.RecaptchaMock.prototype.checkIfDeleted_","fireauth.PhoneMultiFactorGenerator","fireauth.RecaptchaMockLoader","fireauth.GRecaptchaMockFactory.prototype.render","fireauth.GRecaptchaMockFactory.prototype.reset","mock","getMock_","fireauth.GRecaptchaMockFactory.prototype.getResponse","fireauth.GRecaptchaMockFactory.prototype.execute","fireauth.RecaptchaMock.prototype.getResponse","checkIfDeleted_","fireauth.RecaptchaMock.prototype.execute","fireauth.util.generateRandomAlphaNumericString","expirationCallback","fireauth.GRecaptchaMockFactory.EXPIRATION_TIME_MS","fireauth.GRecaptchaMockFactory.SOLVE_TIME_MS","fireauth.RecaptchaMock.prototype.delete","fireauth.RecaptchaMockLoader.prototype.loadRecaptchaDeps","fireauth.RecaptchaMockLoader.prototype.clearSingleRecaptcha","fireauth.RecaptchaMockLoader.instance_","fireauth.RecaptchaRealLoader","Infinity","fireauth.RecaptchaRealLoader.RECAPTCHA_SRC_","fireauth.RecaptchaRealLoader.DEFAULT_DEPENDENCY_TIMEOUT_","fireauth.RecaptchaRealLoader.prototype.loadRecaptchaDeps","hl","timer","fireauth.RecaptchaRealLoader.DEFAULT_DEPENDENCY_TIMEOUT_.get","render","container","parameters","widgetId","fireauth.RecaptchaRealLoader.prototype.clearSingleRecaptcha","fireauth.RecaptchaRealLoader.instance_","fireauth.BaseRecaptchaVerifier","opt_parameters","opt_getLanguageCode","opt_rpcHandlerConfig","opt_isTestingMode","theme","fireauth.BaseRecaptchaVerifier.ParamName.SITEKEY","fireauth.BaseRecaptchaVerifier.ParamName.SIZE","existingCallback","fireauth.BaseRecaptchaVerifier.ParamName.CALLBACK","existingExpiredCallback","fireauth.BaseRecaptchaVerifier.ParamName.EXPIRED_CALLBACK","CALLBACK","EXPIRED_CALLBACK","SITEKEY","SIZE","fireauth.BaseRecaptchaVerifier.prototype.dispatchEvent_","fireauth.BaseRecaptchaVerifier.prototype.registerPendingPromise_","fireauth.BaseRecaptchaVerifier.prototype.checkIfDestroyed_","opt_app","isTestingMode","getLanguageCode","fireauth.args.validate","apiName","expected","actual","opt_isSetter","actualAsArray","numRequiredArgs","isOptionalSection","maxNumArgs","minNumArgs","optionalUndefined","fireauth.args.ORDINAL_NUMBERS_.length","fireauth.args.ORDINAL_NUMBERS_","ordinal","expectedType","lo","fireauth.BaseRecaptchaVerifier.prototype.isReady_","grecaptcha","fireauth.RpcHandler.ApiMethod.GET_RECAPTCHA_PARAM","fireauth.BaseRecaptchaVerifier.prototype.render","checkIfDestroyed_","outerContainer","goog.dom.createDom","goog.dom.TagName.DIV","fireauth.BaseRecaptchaVerifier.prototype.verify","fireauth.BaseRecaptchaVerifier.prototype.removeTokenChangeListener_","fireauth.BaseRecaptchaVerifier.prototype.reset","fireauth.BaseRecaptchaVerifier.prototype","fireauth.BaseRecaptchaVerifier.prototype.clear","node","fireauth.RecaptchaVerifier","opt_optional","typeLabel","optional","validator","fireauth.args.bool","fireauth.args.object","fireauth.args.func","fireauth.args.null","fireauth.args.authCredential","opt_requiredProviderId","matchesRequiredProvider","fireauth.args.multiFactorAssertion","fireauth.args.authProvider","fireauth.args.validateMultiFactorSession_","fireauth.args.validateMultiFactorInfo_","fireauth.args.applicationVerifier","fireauth.args.or","optionA","optionB","fireauth.exportlib.exportPrototypeMethods","protObj","fnMap","obfuscatedFnName","unobfuscatedFnName","fireauth.exportlib.wrapMethodWithArgumentVerifier_","fireauth.exportlib.exportPrototypeProperties","propMap","obfuscatedPropName","unobfuscatedPropName","getter","setter","expectedArg","fireauth.exportlib.exportFunction","parentObj","func","opt_expectedArgs","methodName","wrapper","argumentsAsArray","shortName","applyActionCode","fireauth.args.string","checkActionCode","confirmPasswordReset","createUserWithEmailAndPassword","fetchSignInMethodsForEmail","getRedirectResult","isSignInWithEmailLink","onAuthStateChanged","onIdTokenChanged","sendPasswordResetEmail","sendSignInLinkToEmail","setPersistence","signInAndRetrieveDataWithCredential","signInAnonymously","signInWithCredential","signInWithCustomToken","signInWithEmailAndPassword","signInWithEmailLink","signInWithPhoneNumber","signInWithPopup","signInWithRedirect","updateCurrentUser","signOut","toJSON","useDeviceLanguage","useEmulator","verifyPasswordResetCode","lc","ti","delete","getIdTokenResult","getIdToken","linkAndRetrieveDataWithCredential","linkWithCredential","linkWithPhoneNumber","linkWithPopup","linkWithRedirect","reauthenticateAndRetrieveDataWithCredential","reauthenticateWithCredential","reauthenticateWithPhoneNumber","reauthenticateWithPopup","reauthenticateWithRedirect","reload","sendEmailVerification","unlink","updateEmail","updatePassword","updatePhoneNumber","updateProfile","verifyBeforeUpdateEmail","execute","getResponse","thenAlways","appVerificationDisabled","confirm","fireauth.AuthProvider.getCredentialFromJSON","fromJSON","fireauth.OAuthCredential.fromJSON","fireauth.EmailAuthCredential.fromJSON","fireauth.PhoneAuthCredential.fromJSON","fireauth.SAMLAuthCredential.fromJSON","fireauth.EmailAuthProvider.credential","toPlainObject","addScope","setCustomParameters","fireauth.FacebookAuthProvider.credential","fireauth.GithubAuthProvider.credential","fireauth.GoogleAuthProvider.credential","fireauth.TwitterAuthProvider.credential","resolveSignIn","getSession","enroll","unenroll","clear","verify","fireauth.PhoneMultiFactorGenerator.assertion","namespace","ActionCodeInfo","Operation","Auth","AuthCredential","authComponent","fireauth.exportlib.AUTH_TYPE","instanceFactory","multipleInstances","serviceProps","instantiationMode","onInstanceCreated","authInteropComponent","getUid","getToken","addAuthTokenListener","removeAuthTokenListener","AUTH_NPM_PACKAGE_VERSION","User"],"mappings":"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,iBlEh0FkBhmD,yBkEm0FpBpF,EAAIkrD,EAAe1vC,GACV,IAAIrX,EAAmB+mD,EAAe1vC,IAGxC,KAlQL,aAyMOyvC,OAtM+CxmD,SAAAA,iBA0QF,QAxQvC,CAp/EOiC,qBlE9DAA,uBkEiDDf,oBlElEAA,wBkEuGC0lD,qBlEhGNnlD,iBkE6EIolD,mBlEjEL1kD,gBkEmFOwB,qBlE/FNlC,iBkE0EDU,clE9DAA,gBkEsEGU,iBlE9DAA,iBkEqGHkD,clEnDAA,gBkEgCG+gD,iBlEtGFrlD,iBkE+DFH,alElEAA,uBkE6GWylD,wBlE1DFtiD,wBkE4BDnC,qBlE/DAA,qBkEsEC0kD,sBlEtED1kD,qBkEqDYo9C,iClE1EPz+C,4BkE6GD+C,yBlE9DAA,2BkEuCDzB,wBlEhEAA,0BkEwEAU,wBlE9DAA,0BkE+DTC,elE9DAA,iBkEuCC+jD,gBlEKHnhD,iBkEuCeohD,4BlE9CA3hD,oBkEGXhE,iBlEnEAA,sBkEmFAqB,iBlE/DAA,sBkEkFAukD,iBlEpGF1lD,iBkEqFKuB,oBlE9DAA,sBkEqCW7B,+BlElEAA,wBkEiFdimD,iBlEvEJxlD,qBkE8GC0D,clErDAA,qBkE6DCy/C,elE7DDz/C,qBkEHGtE,iBlEnEAA,mBkEsEUI,2BlElEAA,6BkEyEZO,elEjEAA,iBkEgHa4D,4BlErDAA,oBkE6DdW,clElDAA,gBkEgCQzB,sBlEzDAA,wBkEwEPoB,elEpDAA,iBkEXMhF,qBlErEAA,uBkE+EEa,uBlEjEAA,yBkEoEVI,alE/DAA,4BkE6EQiB,qBlE9DAA,uBkEkEAI,qBlE9DAA,0BkE+DGkkD,wBlE5EH/kD,qBkEgFEmB,uBlE9DAA,yBkE+DVC,alE7DAA,4BkEsEQO,qBlE/DAA,uBkEgEAC,qBlE/DAA,0BkEkENY,elEtDAA,iBkE2DCwiD,gBlE3HHxmD,ekEwHOmE,oBlErDAA,sBkEeClD,qBlE/DAA,uBkEoFQwB,6BlE9DAA,2BkEmEPM,sBlE7DAA,wBkEmFF2B,oBlExDAA,4BkEUQtD,4BlE9DAA,8BkEqEJQ,wBlEhEAA,0BkEqDNL,kBlE/DAA,oBkE+GWsD,6BlEnDAA,+BkEwBXvC,kBl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"}

Anon7 - 2022
AnonSec Team