diff --git a/closure/goog/base.js b/closure/goog/base.js index 52a15dbcb..310db20f7 100644 --- a/closure/goog/base.js +++ b/closure/goog/base.js @@ -1,16 +1,8 @@ -// Copyright 2006 The Closure Library Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS-IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/** + * @license + * Copyright The Closure Library Authors. + * SPDX-License-Identifier: Apache-2.0 + */ /** * @fileoverview Bootstrap for the Google JS Library (Closure). @@ -26,6 +18,12 @@ */ +/** + * @define {boolean} Overridden to true by the compiler. + */ +var COMPILED = false; + + /** * Base namespace for the Closure library. Checks to see goog is already * defined in the current scope before assigning to prevent clobbering if @@ -57,17 +55,65 @@ goog.global = // For in-page browser environments and workers. self; + +/** + * A hook for overriding the define values in uncompiled mode. + * + * In uncompiled mode, `CLOSURE_UNCOMPILED_DEFINES` may be defined before + * loading base.js. If a key is defined in `CLOSURE_UNCOMPILED_DEFINES`, + * `goog.define` will use the value instead of the default value. This + * allows flags to be overwritten without compilation (this is normally + * accomplished with the compiler's "define" flag). + * + * Example: + *
+ *   var CLOSURE_UNCOMPILED_DEFINES = {'goog.DEBUG': false};
+ * 
+ * + * @type {Object|undefined} + */ +goog.global.CLOSURE_UNCOMPILED_DEFINES; + + +/** + * A hook for overriding the define values in uncompiled or compiled mode, + * like CLOSURE_UNCOMPILED_DEFINES but effective in compiled code. In + * uncompiled code CLOSURE_UNCOMPILED_DEFINES takes precedence. + * + * Also unlike CLOSURE_UNCOMPILED_DEFINES the values must be number, boolean or + * string literals or the compiler will emit an error. + * + * While any @define value may be set, only those set with goog.define will be + * effective for uncompiled code. + * + * Example: + *
+ *   var CLOSURE_DEFINES = {'goog.DEBUG': false} ;
+ * 
+ * + * @type {Object|undefined} + */ +goog.global.CLOSURE_DEFINES; + + /** * Builds an object structure for the provided namespace path, ensuring that * names that already exist are not overwritten. For example: * "a.b.c" -> a = {};a.b={};a.b.c={}; * Used by goog.provide and goog.exportSymbol. - * @param {string} name name of the object that this file defines. + * @param {string} name The name of the object that this file defines. + * @param {*=} object The object to expose at the end of the path. + * @param {boolean=} overwriteImplicit If object is set and a previous call + * implicitly constructed the namespace given by name, this parameter + * controls whether object should overwrite the implicitly constructed + * namespace or be merged into it. Defaults to false. + * @param {?Object=} objectToExportTo The object to add the path to; if this + * field is not specified, its value defaults to `goog.global`. * @private */ -goog.exportPath_ = function(name) { +goog.exportPath_ = function(name, object, overwriteImplicit, objectToExportTo) { var parts = name.split('.'); - var cur = goog.global; + var cur = objectToExportTo || goog.global; // Internet Explorer exhibits strange behavior when throwing errors from // methods externed in this manner. See the testExportSymbolExceptions in @@ -77,7 +123,24 @@ goog.exportPath_ = function(name) { } for (var part; parts.length && (part = parts.shift());) { - if (cur[part] && cur[part] !== Object.prototype[part]) { + if (!parts.length && object !== undefined) { + if (!overwriteImplicit && goog.isObject(object) && + goog.isObject(cur[part])) { + // Merge properties on object (the input parameter) with the existing + // implicitly defined namespace, so as to not clobber previously + // defined child namespaces. + for (var prop in object) { + if (object.hasOwnProperty(prop)) { + cur[part][prop] = object[prop]; + } + } + } else { + // Either there is no existing implicit namespace, or overwriteImplicit + // is set to true, so directly assign object (the input parameter) to + // the namespace. + cur[part] = object; + } + } else if (cur[part] && cur[part] !== Object.prototype[part]) { cur = cur[part]; } else { cur = cur[part] = {}; @@ -85,6 +148,152 @@ goog.exportPath_ = function(name) { } }; + +/** + * Defines a named value. In uncompiled mode, the value is retrieved from + * CLOSURE_DEFINES or CLOSURE_UNCOMPILED_DEFINES if the object is defined and + * has the property specified, and otherwise used the defined defaultValue. + * When compiled the default can be overridden using the compiler options or the + * value set in the CLOSURE_DEFINES object. Returns the defined value so that it + * can be used safely in modules. Note that the value type MUST be either + * boolean, number, or string. + * + * @param {string} name The distinguished name to provide. + * @param {T} defaultValue + * @return {T} The defined value. + * @template T + */ +goog.define = function(name, defaultValue) { + var value = defaultValue; + if (!COMPILED) { + var uncompiledDefines = goog.global.CLOSURE_UNCOMPILED_DEFINES; + var defines = goog.global.CLOSURE_DEFINES; + if (uncompiledDefines && + // Anti DOM-clobbering runtime check (b/37736576). + /** @type {?} */ (uncompiledDefines).nodeType === undefined && + Object.prototype.hasOwnProperty.call(uncompiledDefines, name)) { + value = uncompiledDefines[name]; + } else if ( + defines && + // Anti DOM-clobbering runtime check (b/37736576). + /** @type {?} */ (defines).nodeType === undefined && + Object.prototype.hasOwnProperty.call(defines, name)) { + value = defines[name]; + } + } + return value; +}; + + +/** + * @define {number} Integer year indicating the set of browser features that are + * guaranteed to be present. This is defined to include exactly features that + * work correctly on all "modern" browsers that are stable on January 1 of the + * specified year. For example, + * ```js + * if (goog.FEATURESET_YEAR >= 2019) { + * // use APIs known to be available on all major stable browsers Jan 1, 2019 + * } else { + * // polyfill for older browsers + * } + * ``` + * This is intended to be the primary define for removing + * unnecessary browser compatibility code (such as ponyfills and workarounds), + * and should inform the default value for most other defines: + * ```js + * const ASSUME_NATIVE_PROMISE = + * goog.define('ASSUME_NATIVE_PROMISE', goog.FEATURESET_YEAR >= 2016); + * ``` + * + * The default assumption is that IE9 is the lowest supported browser, which was + * first available Jan 1, 2012. + * + * TODO(user): Reference more thorough documentation when it's available. + */ +goog.FEATURESET_YEAR = goog.define('goog.FEATURESET_YEAR', 2012); + + +/** + * @define {boolean} DEBUG is provided as a convenience so that debugging code + * that should not be included in a production. It can be easily stripped + * by specifying --define goog.DEBUG=false to the Closure Compiler aka + * JSCompiler. For example, most toString() methods should be declared inside an + * "if (goog.DEBUG)" conditional because they are generally used for debugging + * purposes and it is difficult for the JSCompiler to statically determine + * whether they are used. + */ +goog.DEBUG = goog.define('goog.DEBUG', true); + + +/** + * @define {string} LOCALE defines the locale being used for compilation. It is + * used to select locale specific data to be compiled in js binary. BUILD rule + * can specify this value by "--define goog.LOCALE=" as a compiler + * option. + * + * Take into account that the locale code format is important. You should use + * the canonical Unicode format with hyphen as a delimiter. Language must be + * lowercase, Language Script - Capitalized, Region - UPPERCASE. + * There are few examples: pt-BR, en, en-US, sr-Latin-BO, zh-Hans-CN. + * + * See more info about locale codes here: + * http://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers + * + * For language codes you should use values defined by ISO 693-1. See it here + * http://www.w3.org/WAI/ER/IG/ert/iso639.htm. There is only one exception from + * this rule: the Hebrew language. For legacy reasons the old code (iw) should + * be used instead of the new code (he). + * + */ +goog.LOCALE = goog.define('goog.LOCALE', 'en'); // default to en + + +/** + * This method is intended to be used for bookkeeping purposes. We would + * like to distinguish uses of goog.LOCALE used for code stripping purposes + * and uses of goog.LOCALE for other uses (such as URL parameters). + * + * This allows us to ban direct uses of goog.LOCALE and to ensure that all + * code has been transformed to our new localization build scheme. + * + * @return {string} + * + */ +goog.getLocale = function() { + return goog.LOCALE; +}; + + +/** + * @define {boolean} Whether this code is running on trusted sites. + * + * On untrusted sites, several native functions can be defined or overridden by + * external libraries like Prototype, Datejs, and JQuery and setting this flag + * to false forces closure to use its own implementations when possible. + * + * If your JavaScript can be loaded by a third party site and you are wary about + * relying on non-standard implementations, specify + * "--define goog.TRUSTED_SITE=false" to the compiler. + */ +goog.TRUSTED_SITE = goog.define('goog.TRUSTED_SITE', true); + + +/** + * @define {boolean} Whether code that calls {@link goog.setTestOnly} should + * be disallowed in the compilation unit. + */ +goog.DISALLOW_TEST_ONLY_CODE = + goog.define('goog.DISALLOW_TEST_ONLY_CODE', COMPILED && !goog.DEBUG); + + +/** + * @define {boolean} Whether to use a Chrome app CSP-compliant method for + * loading scripts via goog.require. @see appendScriptSrcNode_. + */ +goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING = + goog.define('goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING', false); + + /** * Defines a namespace in Closure. * @@ -106,27 +315,199 @@ goog.exportPath_ = function(name) { * @see goog.module * @param {string} name Namespace provided by this file in the form * "goog.package.part". + * deprecated Use goog.module (see b/159289405) */ goog.provide = function(name) { - // Ensure that the same namespace isn't provided twice. - // A goog.module/goog.provide maps a goog.require to a specific file - if (goog.isProvided_(name)) { - throw Error('Namespace "' + name + '" already declared.'); + if (goog.isInModuleLoader_()) { + throw new Error('goog.provide cannot be used within a module.'); } - - delete goog.implicitNamespaces_[name]; - - var namespace = name; - while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) { - if (goog.getObjectByName(namespace)) { - break; + if (!COMPILED) { + // Ensure that the same namespace isn't provided twice. + // A goog.module/goog.provide maps a goog.require to a specific file + if (goog.isProvided_(name)) { + throw new Error('Namespace "' + name + '" already declared.'); } - goog.implicitNamespaces_[namespace] = true; } - goog.exportPath_(name); + goog.constructNamespace_(name); }; + +/** + * @param {string} name Namespace provided by this file in the form + * "goog.package.part". + * @param {?Object=} object The object to embed in the namespace. + * @param {boolean=} overwriteImplicit If object is set and a previous call + * implicitly constructed the namespace given by name, this parameter + * controls whether opt_obj should overwrite the implicitly constructed + * namespace or be merged into it. Defaults to false. + * @private + */ +goog.constructNamespace_ = function(name, object, overwriteImplicit) { + if (!COMPILED) { + delete goog.implicitNamespaces_[name]; + + var namespace = name; + while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) { + if (goog.getObjectByName(namespace)) { + break; + } + goog.implicitNamespaces_[namespace] = true; + } + } + + goog.exportPath_(name, object, overwriteImplicit); +}; + + +/** + * According to the CSP3 spec a nonce must be a valid base64 string. + * @see https://www.w3.org/TR/CSP3/#grammardef-base64-value + * @private @const + */ +goog.NONCE_PATTERN_ = /^[\w+/_-]+[=]{0,2}$/; + + +/** + * Returns CSP nonce, if set for any script tag. + * @param {?Window=} opt_window The window context used to retrieve the nonce. + * Defaults to global context. + * @return {string} CSP nonce or empty string if no nonce is present. + * @private + */ +goog.getScriptNonce_ = function(opt_window) { + var doc = (opt_window || goog.global).document; + var script = doc.querySelector && doc.querySelector('script[nonce]'); + if (script) { + // Try to get the nonce from the IDL property first, because browsers that + // implement additional nonce protection features (currently only Chrome) to + // prevent nonce stealing via CSS do not expose the nonce via attributes. + // See https://github.com/whatwg/html/issues/2369 + var nonce = script['nonce'] || script.getAttribute('nonce'); + if (nonce && goog.NONCE_PATTERN_.test(nonce)) { + return nonce; + } + } + return ''; +}; + + +/** + * Module identifier validation regexp. + * Note: This is a conservative check, it is very possible to be more lenient, + * the primary exclusion here is "/" and "\" and a leading ".", these + * restrictions are intended to leave the door open for using goog.require + * with relative file paths rather than module identifiers. + * @private + */ +goog.VALID_MODULE_RE_ = /^[a-zA-Z_$][a-zA-Z0-9._$]*$/; + + +/** + * Defines a module in Closure. + * + * Marks that this file must be loaded as a module and claims the namespace. + * + * A namespace may only be defined once in a codebase. It may be defined using + * goog.provide() or goog.module(). + * + * goog.module() has three requirements: + * - goog.module may not be used in the same file as goog.provide. + * - goog.module must be the first statement in the file. + * - only one goog.module is allowed per file. + * + * When a goog.module annotated file is loaded, it is enclosed in + * a strict function closure. This means that: + * - any variables declared in a goog.module file are private to the file + * (not global), though the compiler is expected to inline the module. + * - The code must obey all the rules of "strict" JavaScript. + * - the file will be marked as "use strict" + * + * NOTE: unlike goog.provide, goog.module does not declare any symbols by + * itself. If declared symbols are desired, use + * goog.module.declareLegacyNamespace(). + * + * + * See the public goog.module proposal: http://goo.gl/Va1hin + * + * @param {string} name Namespace provided by this file in the form + * "goog.package.part", is expected but not required. + * @return {void} + */ +goog.module = function(name) { + if (typeof name !== 'string' || !name || + name.search(goog.VALID_MODULE_RE_) == -1) { + throw new Error('Invalid module identifier'); + } + if (!goog.isInGoogModuleLoader_()) { + throw new Error( + 'Module ' + name + ' has been loaded incorrectly. Note, ' + + 'modules cannot be loaded as normal scripts. They require some kind of ' + + 'pre-processing step. You\'re likely trying to load a module via a ' + + 'script tag or as a part of a concatenated bundle without rewriting the ' + + 'module. For more info see: ' + + 'https://github.com/google/closure-library/wiki/goog.module:-an-ES6-module-like-alternative-to-goog.provide.'); + } + if (goog.moduleLoaderState_.moduleName) { + throw new Error('goog.module may only be called once per module.'); + } + + // Store the module name for the loader. + goog.moduleLoaderState_.moduleName = name; + if (!COMPILED) { + // Ensure that the same namespace isn't provided twice. + // A goog.module/goog.provide maps a goog.require to a specific file + if (goog.isProvided_(name)) { + throw new Error('Namespace "' + name + '" already declared.'); + } + delete goog.implicitNamespaces_[name]; + } +}; + + +/** + * @param {string} name The module identifier. + * @return {?} The module exports for an already loaded module or null. + * + * Note: This is not an alternative to goog.require, it does not + * indicate a hard dependency, instead it is used to indicate + * an optional dependency or to access the exports of a module + * that has already been loaded. + * @suppress {missingProvide} + */ +goog.module.get = function(name) { + return goog.module.getInternal_(name); +}; + + +/** + * @param {string} name The module identifier. + * @return {?} The module exports for an already loaded module or null. + * @private + */ +goog.module.getInternal_ = function(name) { + if (!COMPILED) { + if (name in goog.loadedModules_) { + return goog.loadedModules_[name].exports; + } else if (!goog.implicitNamespaces_[name]) { + var ns = goog.getObjectByName(name); + return ns != null ? ns : null; + } + } + return null; +}; + + +/** + * Types of modules the debug loader can load. + * @enum {string} + */ +goog.ModuleType = { + ES6: 'es6', + GOOG: 'goog' +}; + + /** * @private {?{ * moduleName: (string|undefined), @@ -136,32 +517,210 @@ goog.provide = function(name) { */ goog.moduleLoaderState_ = null; + /** - * Check if the given name has been goog.provided. This will return false for - * names that are available only as implicit namespaces. - * @param {string} name name of the object to look for. - * @return {boolean} Whether the name has been provided. * @private + * @return {boolean} Whether a goog.module or an es6 module is currently being + * initialized. */ -goog.isProvided_ = function(name) { - return (!goog.implicitNamespaces_[name] && - goog.isDefAndNotNull(goog.getObjectByName(name))); +goog.isInModuleLoader_ = function() { + return goog.isInGoogModuleLoader_() || goog.isInEs6ModuleLoader_(); }; -/** - * Namespaces implicitly defined by goog.provide. For example, - * goog.provide('goog.events.Event') implicitly declares that 'goog' and - * 'goog.events' must be namespaces. - * - * @type {!Object} - * @private - */ -goog.implicitNamespaces_ = {}; -// NOTE: We add goog.module as an implicit namespace as goog.module is defined -// here and because the existing module package has not been moved yet out of -// the goog.module namespace. This satisfies both the debug loader and -// ahead-of-time dependency management. +/** + * @private + * @return {boolean} Whether a goog.module is currently being initialized. + */ +goog.isInGoogModuleLoader_ = function() { + return !!goog.moduleLoaderState_ && + goog.moduleLoaderState_.type == goog.ModuleType.GOOG; +}; + + +/** + * @private + * @return {boolean} Whether an es6 module is currently being initialized. + */ +goog.isInEs6ModuleLoader_ = function() { + var inLoader = !!goog.moduleLoaderState_ && + goog.moduleLoaderState_.type == goog.ModuleType.ES6; + + if (inLoader) { + return true; + } + + var jscomp = goog.global['$jscomp']; + + if (jscomp) { + // jscomp may not have getCurrentModulePath if this is a compiled bundle + // that has some of the runtime, but not all of it. This can happen if + // optimizations are turned on so the unused runtime is removed but renaming + // and Closure pass are off (so $jscomp is still named $jscomp and the + // goog.provide/require calls still exist). + if (typeof jscomp.getCurrentModulePath != 'function') { + return false; + } + + // Bundled ES6 module. + return !!jscomp.getCurrentModulePath(); + } + + return false; +}; + + +/** + * Provide the module's exports as a globally accessible object under the + * module's declared name. This is intended to ease migration to goog.module + * for files that have existing usages. + * @suppress {missingProvide} + */ +goog.module.declareLegacyNamespace = function() { + if (!COMPILED && !goog.isInGoogModuleLoader_()) { + throw new Error( + 'goog.module.declareLegacyNamespace must be called from ' + + 'within a goog.module'); + } + if (!COMPILED && !goog.moduleLoaderState_.moduleName) { + throw new Error( + 'goog.module must be called prior to ' + + 'goog.module.declareLegacyNamespace.'); + } + goog.moduleLoaderState_.declareLegacyNamespace = true; +}; + + +/** + * Associates an ES6 module with a Closure module ID so that is available via + * goog.require. The associated ID acts like a goog.module ID - it does not + * create any global names, it is merely available via goog.require / + * goog.module.get / goog.forwardDeclare / goog.requireType. goog.require and + * goog.module.get will return the entire module as if it was import *'d. This + * allows Closure files to reference ES6 modules for the sake of migration. + * + * @param {string} namespace + * @suppress {missingProvide} + */ +goog.declareModuleId = function(namespace) { + if (!COMPILED) { + if (!goog.isInEs6ModuleLoader_()) { + throw new Error( + 'goog.declareModuleId may only be called from ' + + 'within an ES6 module'); + } + if (goog.moduleLoaderState_ && goog.moduleLoaderState_.moduleName) { + throw new Error( + 'goog.declareModuleId may only be called once per module.'); + } + if (namespace in goog.loadedModules_) { + throw new Error( + 'Module with namespace "' + namespace + '" already exists.'); + } + } + if (goog.moduleLoaderState_) { + // Not bundled - debug loading. + goog.moduleLoaderState_.moduleName = namespace; + } else { + // Bundled - not debug loading, no module loader state. + var jscomp = goog.global['$jscomp']; + if (!jscomp || typeof jscomp.getCurrentModulePath != 'function') { + throw new Error( + 'Module with namespace "' + namespace + + '" has been loaded incorrectly.'); + } + var exports = jscomp.require(jscomp.getCurrentModulePath()); + goog.loadedModules_[namespace] = { + exports: exports, + type: goog.ModuleType.ES6, + moduleId: namespace + }; + } +}; + + +/** + * Marks that the current file should only be used for testing, and never for + * live code in production. + * + * In the case of unit tests, the message may optionally be an exact namespace + * for the test (e.g. 'goog.stringTest'). The linter will then ignore the extra + * provide (if not explicitly defined in the code). + * + * @param {string=} opt_message Optional message to add to the error that's + * raised when used in production code. + */ +goog.setTestOnly = function(opt_message) { + if (goog.DISALLOW_TEST_ONLY_CODE) { + opt_message = opt_message || ''; + throw new Error( + 'Importing test-only code into non-debug environment' + + (opt_message ? ': ' + opt_message : '.')); + } +}; + + +/** + * Forward declares a symbol. This is an indication to the compiler that the + * symbol may be used in the source yet is not required and may not be provided + * in compilation. + * + * The most common usage of forward declaration is code that takes a type as a + * function parameter but does not need to require it. By forward declaring + * instead of requiring, no hard dependency is made, and (if not required + * elsewhere) the namespace may never be required and thus, not be pulled + * into the JavaScript binary. If it is required elsewhere, it will be type + * checked as normal. + * + * Before using goog.forwardDeclare, please read the documentation at + * https://github.com/google/closure-compiler/wiki/Bad-Type-Annotation to + * understand the options and tradeoffs when working with forward declarations. + * + * @param {string} name The namespace to forward declare in the form of + * "goog.package.part". + * @deprecated See go/noforwarddeclaration, Use `goog.requireType` instead. + */ +goog.forwardDeclare = function(name) {}; + + +/** + * Forward declare type information. Used to assign types to goog.global + * referenced object that would otherwise result in unknown type references + * and thus block property disambiguation. + */ +goog.forwardDeclare('Document'); +goog.forwardDeclare('HTMLScriptElement'); +goog.forwardDeclare('XMLHttpRequest'); + + +if (!COMPILED) { + /** + * Check if the given name has been goog.provided. This will return false for + * names that are available only as implicit namespaces. + * @param {string} name name of the object to look for. + * @return {boolean} Whether the name has been provided. + * @private + */ + goog.isProvided_ = function(name) { + return (name in goog.loadedModules_) || + (!goog.implicitNamespaces_[name] && goog.getObjectByName(name) != null); + }; + + /** + * Namespaces implicitly defined by goog.provide. For example, + * goog.provide('goog.events.Event') implicitly declares that 'goog' and + * 'goog.events' must be namespaces. + * + * @type {!Object} + * @private + */ + goog.implicitNamespaces_ = {'goog.module': true}; + + // NOTE: We add goog.module as an implicit namespace as goog.module is defined + // here and because the existing module package has not been moved yet out of + // the goog.module namespace. This satisifies both the debug loader and + // ahead-of-time dependency management. +} /** @@ -180,7 +739,7 @@ goog.getObjectByName = function(name, opt_obj) { var cur = opt_obj || goog.global; for (var i = 0; i < parts.length; i++) { cur = cur[parts[i]]; - if (!goog.isDefAndNotNull(cur)) { + if (cur == null) { return null; } } @@ -195,14 +754,18 @@ goog.getObjectByName = function(name, opt_obj) { * the names of the objects this file provides. * @param {!Array} requires An array of strings with * the names of the objects this file requires. + * @param {boolean|!Object=} opt_loadFlags Parameters indicating + * how the file must be loaded. The boolean 'true' is equivalent + * to {'module': 'goog'} for backwards-compatibility. Valid properties + * and values include {'module': 'goog'} and {'lang': 'es6'}. */ -goog.addDependency = function(relPath, provides, requires) { - goog.debugLoader_.addDependency(relPath, provides, requires); +goog.addDependency = function(relPath, provides, requires, opt_loadFlags) { + if (!COMPILED && goog.DEPENDENCIES_ENABLED) { + goog.debugLoader_.addDependency(relPath, provides, requires, opt_loadFlags); + } }; - - // NOTE(nnaze): The debug DOM loader was included in base.js as an original way // to do "debug-mode" development. The dependency system can sometimes be // confusing, as can the debug DOM loader's asynchronous nature. @@ -222,6 +785,30 @@ goog.addDependency = function(relPath, provides, requires) { // for example). See bootstrap/ for more information. +/** + * @define {boolean} Whether to enable the debug loader. + * + * If enabled, a call to goog.require() will attempt to load the namespace by + * appending a script tag to the DOM (if the namespace has been registered). + * + * If disabled, goog.require() will simply assert that the namespace has been + * provided (and depend on the fact that some outside tool correctly ordered + * the script). + */ +goog.ENABLE_DEBUG_LOADER = goog.define('goog.ENABLE_DEBUG_LOADER', true); + + +/** + * @param {string} msg + * @private + */ +goog.logToConsole_ = function(msg) { + if (goog.global.console) { + goog.global.console['error'](msg); + } +}; + + /** * Implements a system for the dynamic resolution of dependencies that works in * parallel with the BUILD system. @@ -236,20 +823,32 @@ goog.addDependency = function(relPath, provides, requires) { * namespace or module otherwise null. */ goog.require = function(namespace) { - // If the object already exists we do not need to do anything. - if (!goog.isProvided_(namespace)) { - var moduleLoaderState = goog.moduleLoaderState_; - goog.moduleLoaderState_ = null; - try { - goog.debugLoader_.load_(namespace); - } finally { - goog.moduleLoaderState_ = moduleLoaderState; + if (!COMPILED) { + // Might need to lazy load on old IE. + if (goog.ENABLE_DEBUG_LOADER) { + goog.debugLoader_.requested(namespace); } - } - return null; + // If the object already exists we do not need to do anything. + if (goog.isProvided_(namespace)) { + if (goog.isInModuleLoader_()) { + return goog.module.getInternal_(namespace); + } + } else if (goog.ENABLE_DEBUG_LOADER) { + var moduleLoaderState = goog.moduleLoaderState_; + goog.moduleLoaderState_ = null; + try { + goog.debugLoader_.load_(namespace); + } finally { + goog.moduleLoaderState_ = moduleLoaderState; + } + } + + return null; + } }; + /** * Requires a symbol for its type information. This is an indication to the * compiler that the symbol may appear in type annotations, yet it is not @@ -273,12 +872,275 @@ goog.requireType = function(namespace) { return {}; }; + /** * Path for included scripts. * @type {string} */ goog.basePath = ''; + +/** + * A hook for overriding the base path. + * @type {string|undefined} + */ +goog.global.CLOSURE_BASE_PATH; + + +/** + * Whether to attempt to load Closure's deps file. By default, when uncompiled, + * deps files will attempt to be loaded. + * @type {boolean|undefined} + */ +goog.global.CLOSURE_NO_DEPS; + + +/** + * A function to import a single script. This is meant to be overridden when + * Closure is being run in non-HTML contexts, such as web workers. It's defined + * in the global scope so that it can be set before base.js is loaded, which + * allows deps.js to be imported properly. + * + * The first parameter the script source, which is a relative URI. The second, + * optional parameter is the script contents, in the event the script needed + * transformation. It should return true if the script was imported, false + * otherwise. + * @type {(function(string, string=): boolean)|undefined} + */ +goog.global.CLOSURE_IMPORT_SCRIPT; + + +/** + * Null function used for default values of callbacks, etc. + * @return {void} Nothing. + * @deprecated use '()=>{}' or 'function(){}' instead. + */ +goog.nullFunction = function() {}; + + +/** + * When defining a class Foo with an abstract method bar(), you can do: + * Foo.prototype.bar = goog.abstractMethod + * + * Now if a subclass of Foo fails to override bar(), an error will be thrown + * when bar() is invoked. + * + * @type {!Function} + * @throws {Error} when invoked to indicate the method should be overridden. + * @deprecated Use "@abstract" annotation instead of goog.abstractMethod in new + * code. See + * https://github.com/google/closure-compiler/wiki/@abstract-classes-and-methods + */ +goog.abstractMethod = function() { + throw new Error('unimplemented abstract method'); +}; + + +/** + * Adds a `getInstance` static method that always returns the same + * instance object. + * @param {!Function} ctor The constructor for the class to add the static + * method to. + * @suppress {missingProperties} 'instance_' isn't a property on 'Function' + * but we don't have a better type to use here. + */ +goog.addSingletonGetter = function(ctor) { + // instance_ is immediately set to prevent issues with sealed constructors + // such as are encountered when a constructor is returned as the export object + // of a goog.module in unoptimized code. + // Delcare type to avoid conformance violations that ctor.instance_ is unknown + /** @type {undefined|!Object} @suppress {underscore} */ + ctor.instance_ = undefined; + ctor.getInstance = function() { + if (ctor.instance_) { + return ctor.instance_; + } + if (goog.DEBUG) { + // NOTE: JSCompiler can't optimize away Array#push. + goog.instantiatedSingletons_[goog.instantiatedSingletons_.length] = ctor; + } + // Cast to avoid conformance violations that ctor.instance_ is unknown + return /** @type {!Object|undefined} */ (ctor.instance_) = new ctor; + }; +}; + + +/** + * All singleton classes that have been instantiated, for testing. Don't read + * it directly, use the `goog.testing.singleton` module. The compiler + * removes this variable if unused. + * @type {!Array} + * @private + */ +goog.instantiatedSingletons_ = []; + + +/** + * @define {boolean} Whether to load goog.modules using `eval` when using + * the debug loader. This provides a better debugging experience as the + * source is unmodified and can be edited using Chrome Workspaces or similar. + * However in some environments the use of `eval` is banned + * so we provide an alternative. + */ +goog.LOAD_MODULE_USING_EVAL = goog.define('goog.LOAD_MODULE_USING_EVAL', true); + + +/** + * @define {boolean} Whether the exports of goog.modules should be sealed when + * possible. + */ +goog.SEAL_MODULE_EXPORTS = goog.define('goog.SEAL_MODULE_EXPORTS', goog.DEBUG); + + +/** + * The registry of initialized modules: + * The module identifier or path to module exports map. + * @private @const {!Object} + */ +goog.loadedModules_ = {}; + + +/** + * True if the debug loader enabled and used. + * @const {boolean} + */ +goog.DEPENDENCIES_ENABLED = !COMPILED && goog.ENABLE_DEBUG_LOADER; + + +/** + * @define {string} How to decide whether to transpile. Valid values + * are 'always', 'never', and 'detect'. The default ('detect') is to + * use feature detection to determine which language levels need + * transpilation. + */ +// NOTE(sdh): we could expand this to accept a language level to bypass +// detection: e.g. goog.TRANSPILE == 'es5' would transpile ES6 files but +// would leave ES3 and ES5 files alone. +goog.TRANSPILE = goog.define('goog.TRANSPILE', 'detect'); + +/** + * @define {boolean} If true assume that ES modules have already been + * transpiled by the jscompiler (in the same way that transpile.js would + * transpile them - to jscomp modules). Useful only for servers that wish to use + * the debug loader and transpile server side. Thus this is only respected if + * goog.TRANSPILE is "never". + */ +goog.ASSUME_ES_MODULES_TRANSPILED = + goog.define('goog.ASSUME_ES_MODULES_TRANSPILED', false); + + +/** + * @define {string} If a file needs to be transpiled what the output language + * should be. By default this is the highest language level this file detects + * the current environment supports. Generally this flag should not be set, but + * it could be useful to override. Example: If the current environment supports + * ES6 then by default ES7+ files will be transpiled to ES6, unless this is + * overridden. + * + * Valid values include: es3, es5, es6, es7, and es8. Anything not recognized + * is treated as es3. + * + * Note that setting this value does not force transpilation. Just if + * transpilation occurs this will be the output. So this is most useful when + * goog.TRANSPILE is set to 'always' and then forcing the language level to be + * something lower than what the environment detects. + */ +goog.TRANSPILE_TO_LANGUAGE = goog.define('goog.TRANSPILE_TO_LANGUAGE', ''); + + +/** + * @define {string} Path to the transpiler. Executing the script at this + * path (relative to base.js) should define a function $jscomp.transpile. + */ +goog.TRANSPILER = goog.define('goog.TRANSPILER', 'transpile.js'); + + +/** + * @define {string} Trusted Types policy name. If non-empty then Closure will + * use Trusted Types. + */ +goog.TRUSTED_TYPES_POLICY_NAME = + goog.define('goog.TRUSTED_TYPES_POLICY_NAME', 'goog'); + + +/** + * @package {?boolean} + * Visible for testing. + */ +goog.hasBadLetScoping = null; + + +/** + * @param {function(?):?|string} moduleDef The module definition. + */ +goog.loadModule = function(moduleDef) { + // NOTE: we allow function definitions to be either in the from + // of a string to eval (which keeps the original source intact) or + // in a eval forbidden environment (CSP) we allow a function definition + // which in its body must call `goog.module`, and return the exports + // of the module. + var previousState = goog.moduleLoaderState_; + try { + goog.moduleLoaderState_ = { + moduleName: '', + declareLegacyNamespace: false, + type: goog.ModuleType.GOOG + }; + var origExports = {}; + var exports = origExports; + if (typeof moduleDef === 'function') { + exports = moduleDef.call(undefined, exports); + } else if (typeof moduleDef === 'string') { + exports = goog.loadModuleFromSource_.call(undefined, exports, moduleDef); + } else { + throw new Error('Invalid module definition'); + } + + var moduleName = goog.moduleLoaderState_.moduleName; + if (typeof moduleName === 'string' && moduleName) { + // Don't seal legacy namespaces as they may be used as a parent of + // another namespace + if (goog.moduleLoaderState_.declareLegacyNamespace) { + // Whether exports was overwritten via default export assignment. + // This is important for legacy namespaces as it dictates whether + // previously a previously loaded implicit namespace should be clobbered + // or not. + var isDefaultExport = origExports !== exports; + goog.constructNamespace_(moduleName, exports, isDefaultExport); + } else if ( + goog.SEAL_MODULE_EXPORTS && Object.seal && + typeof exports == 'object' && exports != null) { + Object.seal(exports); + } + + var data = { + exports: exports, + type: goog.ModuleType.GOOG, + moduleId: goog.moduleLoaderState_.moduleName + }; + goog.loadedModules_[moduleName] = data; + } else { + throw new Error('Invalid module name \"' + moduleName + '\"'); + } + } finally { + goog.moduleLoaderState_ = previousState; + } +}; + + +/** + * @private @const + */ +goog.loadModuleFromSource_ = + /** @type {function(!Object, string):?} */ (function(exports) { + // NOTE: we avoid declaring parameters or local variables here to avoid + // masking globals or leaking values into the module definition. + 'use strict'; + eval(goog.CLOSURE_EVAL_PREFILTER_.createScript(arguments[1])); + return exports; + }); + + /** * Normalize a file path by removing redundant ".." and extraneous "." file * path components. @@ -304,228 +1166,2717 @@ goog.normalizePath_ = function(path) { }; +/** + * Provides a hook for loading a file when using Closure's goog.require() API + * with goog.modules. In particular this hook is provided to support Node.js. + * + * @type {(function(string):string)|undefined} + */ +goog.global.CLOSURE_LOAD_FILE_SYNC; + + +/** + * Loads file by synchronous XHR. Should not be used in production environments. + * @param {string} src Source URL. + * @return {?string} File contents, or null if load failed. + * @private + */ +goog.loadFileSync_ = function(src) { + if (goog.global.CLOSURE_LOAD_FILE_SYNC) { + return goog.global.CLOSURE_LOAD_FILE_SYNC(src); + } else { + try { + /** @type {XMLHttpRequest} */ + var xhr = new goog.global['XMLHttpRequest'](); + xhr.open('get', src, false); + xhr.send(); + // NOTE: Successful http: requests have a status of 200, but successful + // file: requests may have a status of zero. Any other status, or a + // thrown exception (particularly in case of file: requests) indicates + // some sort of error, which we treat as a missing or unavailable file. + return xhr.status == 0 || xhr.status == 200 ? xhr.responseText : null; + } catch (err) { + // No need to rethrow or log, since errors should show up on their own. + return null; + } + } +}; + + +/** + * Lazily retrieves the transpiler and applies it to the source. + * @param {string} code JS code. + * @param {string} path Path to the code. + * @param {string} target Language level output. + * @return {string} The transpiled code. + * @private + */ +goog.transpile_ = function(code, path, target) { + var jscomp = goog.global['$jscomp']; + if (!jscomp) { + goog.global['$jscomp'] = jscomp = {}; + } + var transpile = jscomp.transpile; + if (!transpile) { + var transpilerPath = goog.basePath + goog.TRANSPILER; + var transpilerCode = goog.loadFileSync_(transpilerPath); + if (transpilerCode) { + // This must be executed synchronously, since by the time we know we + // need it, we're about to load and write the ES6 code synchronously, + // so a normal script-tag load will be too slow. Wrapped in a function + // so that code is eval'd in the global scope. + (function() { + (0, eval)(transpilerCode + '\n//# sourceURL=' + transpilerPath); + }).call(goog.global); + // Even though the transpiler is optional, if $gwtExport is found, it's + // a sign the transpiler was loaded and the $jscomp.transpile *should* + // be there. + if (goog.global['$gwtExport'] && goog.global['$gwtExport']['$jscomp'] && + !goog.global['$gwtExport']['$jscomp']['transpile']) { + throw new Error( + 'The transpiler did not properly export the "transpile" ' + + 'method. $gwtExport: ' + JSON.stringify(goog.global['$gwtExport'])); + } + // transpile.js only exports a single $jscomp function, transpile. We + // grab just that and add it to the existing definition of $jscomp which + // contains the polyfills. + goog.global['$jscomp'].transpile = + goog.global['$gwtExport']['$jscomp']['transpile']; + jscomp = goog.global['$jscomp']; + transpile = jscomp.transpile; + } + } + if (!transpile) { + // The transpiler is an optional component. If it's not available then + // replace it with a pass-through function that simply logs. + var suffix = ' requires transpilation but no transpiler was found.'; + transpile = jscomp.transpile = function(code, path) { + // TODO(sdh): figure out some way to get this error to show up + // in test results, noting that the failure may occur in many + // different ways, including in loadModule() before the test + // runner even comes up. + goog.logToConsole_(path + suffix); + return code; + }; + } + // Note: any transpilation errors/warnings will be logged to the console. + return transpile(code, path, target); +}; + //============================================================================== // Language Enhancements //============================================================================== /** - * Returns true if the specified value is defined and not null. - * @param {?} val Variable to test. - * @return {boolean} Whether variable is defined and not null. + * This is a "fixed" version of the typeof operator. It differs from the typeof + * operator in such a way that null returns 'null' and arrays return 'array'. + * @param {?} value The value to get the type of. + * @return {string} The name of the type. */ -goog.isDefAndNotNull = function(val) { - // Note that undefined == null. - return val != null; +goog.typeOf = function(value) { + var s = typeof value; + + if (s != 'object') { + return s; + } + + if (!value) { + return 'null'; + } + + if (Array.isArray(value)) { + return 'array'; + } + return s; }; + +/** + * Returns true if the object looks like an array. To qualify as array like + * the value needs to be either a NodeList or an object with a Number length + * property. Note that for this function neither strings nor functions are + * considered "array-like". + * + * @param {?} val Variable to test. + * @return {boolean} Whether variable is an array. + */ +goog.isArrayLike = function(val) { + var type = goog.typeOf(val); + // We do not use goog.isObject here in order to exclude function values. + return type == 'array' || type == 'object' && typeof val.length == 'number'; +}; + + +/** + * Returns true if the object looks like a Date. To qualify as Date-like the + * value needs to be an object and have a getFullYear() function. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is a like a Date. + */ +goog.isDateLike = function(val) { + return goog.isObject(val) && typeof val.getFullYear == 'function'; +}; + + +/** + * Returns true if the specified value is an object. This includes arrays and + * functions. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is an object. + */ +goog.isObject = function(val) { + var type = typeof val; + return type == 'object' && val != null || type == 'function'; + // return Object(val) === val also works, but is slower, especially if val is + // not an object. +}; + + +/** + * Gets a unique ID for an object. This mutates the object so that further calls + * with the same object as a parameter returns the same value. The unique ID is + * guaranteed to be unique across the current session amongst objects that are + * passed into `getUid`. There is no guarantee that the ID is unique or + * consistent across sessions. It is unsafe to generate unique ID for function + * prototypes. + * + * @param {Object} obj The object to get the unique ID for. + * @return {number} The unique ID for the object. + */ +goog.getUid = function(obj) { + // TODO(arv): Make the type stricter, do not accept null. + return Object.prototype.hasOwnProperty.call(obj, goog.UID_PROPERTY_) && + obj[goog.UID_PROPERTY_] || + (obj[goog.UID_PROPERTY_] = ++goog.uidCounter_); +}; + + +/** + * Whether the given object is already assigned a unique ID. + * + * This does not modify the object. + * + * @param {!Object} obj The object to check. + * @return {boolean} Whether there is an assigned unique id for the object. + */ +goog.hasUid = function(obj) { + return !!obj[goog.UID_PROPERTY_]; +}; + + +/** + * Removes the unique ID from an object. This is useful if the object was + * previously mutated using `goog.getUid` in which case the mutation is + * undone. + * @param {Object} obj The object to remove the unique ID field from. + */ +goog.removeUid = function(obj) { + // TODO(arv): Make the type stricter, do not accept null. + + // In IE, DOM nodes are not instances of Object and throw an exception if we + // try to delete. Instead we try to use removeAttribute. + if (obj !== null && 'removeAttribute' in obj) { + obj.removeAttribute(goog.UID_PROPERTY_); + } + + try { + delete obj[goog.UID_PROPERTY_]; + } catch (ex) { + } +}; + + +/** + * Name for unique ID property. Initialized in a way to help avoid collisions + * with other closure JavaScript on the same page. + * @type {string} + * @private + */ +goog.UID_PROPERTY_ = 'closure_uid_' + ((Math.random() * 1e9) >>> 0); + + +/** + * Counter for UID. + * @type {number} + * @private + */ +goog.uidCounter_ = 0; + + +/** + * Clones a value. The input may be an Object, Array, or basic type. Objects and + * arrays will be cloned recursively. + * + * WARNINGS: + * goog.cloneObject does not detect reference loops. Objects that + * refer to themselves will cause infinite recursion. + * + * goog.cloneObject is unaware of unique identifiers, and copies + * UIDs created by getUid into cloned results. + * + * @param {*} obj The value to clone. + * @return {*} A clone of the input value. + * @deprecated goog.cloneObject is unsafe. Prefer the goog.object methods. + */ +goog.cloneObject = function(obj) { + var type = goog.typeOf(obj); + if (type == 'object' || type == 'array') { + if (typeof obj.clone === 'function') { + return obj.clone(); + } + if (typeof Map !== 'undefined' && obj instanceof Map) { + return new Map(obj); + } else if (typeof Set !== 'undefined' && obj instanceof Set) { + return new Set(obj); + } + var clone = type == 'array' ? [] : {}; + for (var key in obj) { + clone[key] = goog.cloneObject(obj[key]); + } + return clone; + } + + return obj; +}; + + +/** + * A native implementation of goog.bind. + * @param {?function(this:T, ...)} fn A function to partially apply. + * @param {T} selfObj Specifies the object which this should point to when the + * function is run. + * @param {...*} var_args Additional arguments that are partially applied to the + * function. + * @return {!Function} A partially-applied form of the function goog.bind() was + * invoked as a method of. + * @template T + * @private + */ +goog.bindNative_ = function(fn, selfObj, var_args) { + return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments)); +}; + + +/** + * A pure-JS implementation of goog.bind. + * @param {?function(this:T, ...)} fn A function to partially apply. + * @param {T} selfObj Specifies the object which this should point to when the + * function is run. + * @param {...*} var_args Additional arguments that are partially applied to the + * function. + * @return {!Function} A partially-applied form of the function goog.bind() was + * invoked as a method of. + * @template T + * @private + */ +goog.bindJs_ = function(fn, selfObj, var_args) { + if (!fn) { + throw new Error(); + } + + if (arguments.length > 2) { + var boundArgs = Array.prototype.slice.call(arguments, 2); + return function() { + // Prepend the bound arguments to the current arguments. + var newArgs = Array.prototype.slice.call(arguments); + Array.prototype.unshift.apply(newArgs, boundArgs); + return fn.apply(selfObj, newArgs); + }; + + } else { + return function() { + return fn.apply(selfObj, arguments); + }; + } +}; + + +/** + * Partially applies this function to a particular 'this object' and zero or + * more arguments. The result is a new function with some arguments of the first + * function pre-filled and the value of this 'pre-specified'. + * + * Remaining arguments specified at call-time are appended to the pre-specified + * ones. + * + * Also see: {@link #partial}. + * + * Usage: + *
var barMethBound = goog.bind(myFunction, myObj, 'arg1', 'arg2');
+ * barMethBound('arg3', 'arg4');
+ * + * @param {?function(this:T, ...)} fn A function to partially apply. + * @param {T} selfObj Specifies the object which this should point to when the + * function is run. + * @param {...*} var_args Additional arguments that are partially applied to the + * function. + * @return {!Function} A partially-applied form of the function goog.bind() was + * invoked as a method of. + * @template T + * @suppress {deprecated} See above. + * @deprecated use `=> {}` or Function.prototype.bind instead. + */ +goog.bind = function(fn, selfObj, var_args) { + // TODO(nicksantos): narrow the type signature. + if (Function.prototype.bind && + // NOTE(nicksantos): Somebody pulled base.js into the default Chrome + // extension environment. This means that for Chrome extensions, they get + // the implementation of Function.prototype.bind that calls goog.bind + // instead of the native one. Even worse, we don't want to introduce a + // circular dependency between goog.bind and Function.prototype.bind, so + // we have to hack this to make sure it works correctly. + Function.prototype.bind.toString().indexOf('native code') != -1) { + goog.bind = goog.bindNative_; + } else { + goog.bind = goog.bindJs_; + } + return goog.bind.apply(null, arguments); +}; + + +/** + * Like goog.bind(), except that a 'this object' is not required. Useful when + * the target function is already bound. + * + * Usage: + * var g = goog.partial(f, arg1, arg2); + * g(arg3, arg4); + * + * @param {Function} fn A function to partially apply. + * @param {...*} var_args Additional arguments that are partially applied to fn. + * @return {!Function} A partially-applied form of the function goog.partial() + * was invoked as a method of. + */ +goog.partial = function(fn, var_args) { + var args = Array.prototype.slice.call(arguments, 1); + return function() { + // Clone the array (with slice()) and append additional arguments + // to the existing arguments. + var newArgs = args.slice(); + newArgs.push.apply(newArgs, arguments); + return fn.apply(/** @type {?} */ (this), newArgs); + }; +}; + + +/** + * Copies all the members of a source object to a target object. This method + * does not work on all browsers for all objects that contain keys such as + * toString or hasOwnProperty. Use goog.object.extend for this purpose. + * + * NOTE: Some have advocated for the use of goog.mixin to setup classes + * with multiple inheritence (traits, mixins, etc). However, as it simply + * uses "for in", this is not compatible with ES6 classes whose methods are + * non-enumerable. Changing this, would break cases where non-enumerable + * properties are not expected. + * + * @param {Object} target Target. + * @param {Object} source Source. + * @deprecated Prefer Object.assign + */ +goog.mixin = function(target, source) { + for (var x in source) { + target[x] = source[x]; + } + + // For IE7 or lower, the for-in-loop does not contain any properties that are + // not enumerable on the prototype object (for example, isPrototypeOf from + // Object.prototype) but also it will not include 'replace' on objects that + // extend String and change 'replace' (not that it is common for anyone to + // extend anything except Object). +}; + + +/** + * @return {number} An integer value representing the number of milliseconds + * between midnight, January 1, 1970 and the current time. + * @deprecated Use Date.now + */ +goog.now = function() { + return Date.now(); +}; + + +/** + * Evals JavaScript in the global scope. + * + * Throws an exception if neither execScript or eval is defined. + * @param {string|!TrustedScript} script JavaScript string. + */ +goog.globalEval = function(script) { + (0, eval)(script); +}; + + +/** + * Optional map of CSS class names to obfuscated names used with + * goog.getCssName(). + * @private {!Object|undefined} + * @see goog.setCssNameMapping + */ +goog.cssNameMapping_; + + +/** + * Optional obfuscation style for CSS class names. Should be set to either + * 'BY_WHOLE' or 'BY_PART' if defined. + * @type {string|undefined} + * @private + * @see goog.setCssNameMapping + */ +goog.cssNameMappingStyle_; + + + +/** + * A hook for modifying the default behavior goog.getCssName. The function + * if present, will receive the standard output of the goog.getCssName as + * its input. + * + * @type {(function(string):string)|undefined} + */ +goog.global.CLOSURE_CSS_NAME_MAP_FN; + + +/** + * Handles strings that are intended to be used as CSS class names. + * + * This function works in tandem with @see goog.setCssNameMapping. + * + * Without any mapping set, the arguments are simple joined with a hyphen and + * passed through unaltered. + * + * When there is a mapping, there are two possible styles in which these + * mappings are used. In the BY_PART style, each part (i.e. in between hyphens) + * of the passed in css name is rewritten according to the map. In the BY_WHOLE + * style, the full css name is looked up in the map directly. If a rewrite is + * not specified by the map, the compiler will output a warning. + * + * When the mapping is passed to the compiler, it will replace calls to + * goog.getCssName with the strings from the mapping, e.g. + * var x = goog.getCssName('foo'); + * var y = goog.getCssName(this.baseClass, 'active'); + * becomes: + * var x = 'foo'; + * var y = this.baseClass + '-active'; + * + * If one argument is passed it will be processed, if two are passed only the + * modifier will be processed, as it is assumed the first argument was generated + * as a result of calling goog.getCssName. + * + * @param {string} className The class name. + * @param {string=} opt_modifier A modifier to be appended to the class name. + * @return {string} The class name or the concatenation of the class name and + * the modifier. + */ +goog.getCssName = function(className, opt_modifier) { + // String() is used for compatibility with compiled soy where the passed + // className can be non-string objects. + if (String(className).charAt(0) == '.') { + throw new Error( + 'className passed in goog.getCssName must not start with ".".' + + ' You passed: ' + className); + } + + var getMapping = function(cssName) { + return goog.cssNameMapping_[cssName] || cssName; + }; + + var renameByParts = function(cssName) { + // Remap all the parts individually. + var parts = cssName.split('-'); + var mapped = []; + for (var i = 0; i < parts.length; i++) { + mapped.push(getMapping(parts[i])); + } + return mapped.join('-'); + }; + + var rename; + if (goog.cssNameMapping_) { + rename = + goog.cssNameMappingStyle_ == 'BY_WHOLE' ? getMapping : renameByParts; + } else { + rename = function(a) { + return a; + }; + } + + var result = + opt_modifier ? className + '-' + rename(opt_modifier) : rename(className); + + // The special CLOSURE_CSS_NAME_MAP_FN allows users to specify further + // processing of the class name. + if (goog.global.CLOSURE_CSS_NAME_MAP_FN) { + return goog.global.CLOSURE_CSS_NAME_MAP_FN(result); + } + + return result; +}; + + +/** + * Sets the map to check when returning a value from goog.getCssName(). Example: + *
+ * goog.setCssNameMapping({
+ *   "goog": "a",
+ *   "disabled": "b",
+ * });
+ *
+ * var x = goog.getCssName('goog');
+ * // The following evaluates to: "a a-b".
+ * goog.getCssName('goog') + ' ' + goog.getCssName(x, 'disabled')
+ * 
+ * When declared as a map of string literals to string literals, the JSCompiler + * will replace all calls to goog.getCssName() using the supplied map if the + * --process_closure_primitives flag is set. + * + * @param {!Object} mapping A map of strings to strings where keys are possible + * arguments to goog.getCssName() and values are the corresponding values + * that should be returned. + * @param {string=} opt_style The style of css name mapping. There are two valid + * options: 'BY_PART', and 'BY_WHOLE'. + * @see goog.getCssName for a description. + */ +goog.setCssNameMapping = function(mapping, opt_style) { + goog.cssNameMapping_ = mapping; + goog.cssNameMappingStyle_ = opt_style; +}; + + +/** + * To use CSS renaming in compiled mode, one of the input files should have a + * call to goog.setCssNameMapping() with an object literal that the JSCompiler + * can extract and use to replace all calls to goog.getCssName(). In uncompiled + * mode, JavaScript code should be loaded before this base.js file that declares + * a global variable, CLOSURE_CSS_NAME_MAPPING, which is used below. This is + * to ensure that the mapping is loaded before any calls to goog.getCssName() + * are made in uncompiled mode. + * + * A hook for overriding the CSS name mapping. + * @type {!Object|undefined} + */ +goog.global.CLOSURE_CSS_NAME_MAPPING; + + +if (!COMPILED && goog.global.CLOSURE_CSS_NAME_MAPPING) { + // This does not call goog.setCssNameMapping() because the JSCompiler + // requires that goog.setCssNameMapping() be called with an object literal. + goog.cssNameMapping_ = goog.global.CLOSURE_CSS_NAME_MAPPING; +} + + +/** + * Gets a localized message. + * + * This function is a compiler primitive. If you give the compiler a localized + * message bundle, it will replace the string at compile-time with a localized + * version, and expand goog.getMsg call to a concatenated string. + * + * Messages must be initialized in the form: + * + * var MSG_NAME = goog.getMsg('Hello {$placeholder}', {'placeholder': 'world'}); + * + * + * This function produces a string which should be treated as plain text. Use + * {@link goog.html.SafeHtmlFormatter} in conjunction with goog.getMsg to + * produce SafeHtml. + * + * @param {string} str Translatable string, places holders in the form {$foo}. + * @param {Object=} opt_values Maps place holder name to value. + * @param {{html: (boolean|undefined), + * unescapeHtmlEntities: (boolean|undefined)}=} opt_options Options: + * html: Escape '<' in str to '<'. Used by Closure Templates where the + * generated code size and performance is critical which is why {@link + * goog.html.SafeHtmlFormatter} is not used. The value must be literal true + * or false. + * unescapeHtmlEntities: Unescape common html entities: >, <, ', + * " and &. Used for messages not in HTML context, such as with + * `textContent` property. + * @return {string} message with placeholders filled. + */ +goog.getMsg = function(str, opt_values, opt_options) { + if (opt_options && opt_options.html) { + // Note that '&' is not replaced because the translation can contain HTML + // entities. + str = str.replace(/') + .replace(/'/g, '\'') + .replace(/"/g, '"') + .replace(/&/g, '&'); + } + if (opt_values) { + str = str.replace(/\{\$([^}]+)}/g, function(match, key) { + return (opt_values != null && key in opt_values) ? opt_values[key] : + match; + }); + } + return str; +}; + + +/** + * Gets a localized message. If the message does not have a translation, gives a + * fallback message. + * + * This is useful when introducing a new message that has not yet been + * translated into all languages. + * + * This function is a compiler primitive. Must be used in the form: + * var x = goog.getMsgWithFallback(MSG_A, MSG_B); + * where MSG_A and MSG_B were initialized with goog.getMsg. + * + * @param {string} a The preferred message. + * @param {string} b The fallback message. + * @return {string} The best translated message. + */ +goog.getMsgWithFallback = function(a, b) { + return a; +}; + + +/** + * Exposes an unobfuscated global namespace path for the given object. + * Note that fields of the exported object *will* be obfuscated, unless they are + * exported in turn via this function or goog.exportProperty. + * + * Also handy for making public items that are defined in anonymous closures. + * + * ex. goog.exportSymbol('public.path.Foo', Foo); + * + * ex. goog.exportSymbol('public.path.Foo.staticFunction', Foo.staticFunction); + * public.path.Foo.staticFunction(); + * + * ex. goog.exportSymbol('public.path.Foo.prototype.myMethod', + * Foo.prototype.myMethod); + * new public.path.Foo().myMethod(); + * + * @param {string} publicPath Unobfuscated name to export. + * @param {*} object Object the name should point to. + * @param {?Object=} objectToExportTo The object to add the path to; default + * is goog.global. + */ +goog.exportSymbol = function(publicPath, object, objectToExportTo) { + goog.exportPath_( + publicPath, object, /* overwriteImplicit= */ true, objectToExportTo); +}; + + +/** + * Exports a property unobfuscated into the object's namespace. + * ex. goog.exportProperty(Foo, 'staticFunction', Foo.staticFunction); + * ex. goog.exportProperty(Foo.prototype, 'myMethod', Foo.prototype.myMethod); + * @param {Object} object Object whose static property is being exported. + * @param {string} publicName Unobfuscated name to export. + * @param {*} symbol Object the name should point to. + */ +goog.exportProperty = function(object, publicName, symbol) { + object[publicName] = symbol; +}; + + +/** + * Inherit the prototype methods from one constructor into another. + * + * Usage: + *
+ * function ParentClass(a, b) { }
+ * ParentClass.prototype.foo = function(a) { };
+ *
+ * function ChildClass(a, b, c) {
+ *   ChildClass.base(this, 'constructor', a, b);
+ * }
+ * goog.inherits(ChildClass, ParentClass);
+ *
+ * var child = new ChildClass('a', 'b', 'see');
+ * child.foo(); // This works.
+ * 
+ * + * @param {!Function} childCtor Child class. + * @param {!Function} parentCtor Parent class. + * @suppress {strictMissingProperties} superClass_ and base is not defined on + * Function. + * @deprecated Use ECMAScript class syntax instead. + */ +goog.inherits = function(childCtor, parentCtor) { + /** @constructor */ + function tempCtor() {} + tempCtor.prototype = parentCtor.prototype; + childCtor.superClass_ = parentCtor.prototype; + childCtor.prototype = new tempCtor(); + /** @override */ + childCtor.prototype.constructor = childCtor; + + /** + * Calls superclass constructor/method. + * + * This function is only available if you use goog.inherits to + * express inheritance relationships between classes. + * + * NOTE: This is a replacement for goog.base and for superClass_ + * property defined in childCtor. + * + * @param {!Object} me Should always be "this". + * @param {string} methodName The method name to call. Calling + * superclass constructor can be done with the special string + * 'constructor'. + * @param {...*} var_args The arguments to pass to superclass + * method/constructor. + * @return {*} The return value of the superclass method/constructor. + */ + childCtor.base = function(me, methodName, var_args) { + // Copying using loop to avoid deop due to passing arguments object to + // function. This is faster in many JS engines as of late 2014. + var args = new Array(arguments.length - 2); + for (var i = 2; i < arguments.length; i++) { + args[i - 2] = arguments[i]; + } + return parentCtor.prototype[methodName].apply(me, args); + }; +}; + + +/** + * Allow for aliasing within scope functions. This function exists for + * uncompiled code - in compiled code the calls will be inlined and the aliases + * applied. In uncompiled code the function is simply run since the aliases as + * written are valid JavaScript. + * + * + * @param {function()} fn Function to call. This function can contain aliases + * to namespaces (e.g. "var dom = goog.dom") or classes + * (e.g. "var Timer = goog.Timer"). + * @deprecated Use goog.module instead. + */ +goog.scope = function(fn) { + if (goog.isInModuleLoader_()) { + throw new Error('goog.scope is not supported within a module.'); + } + fn.call(goog.global); +}; + + +/* + * To support uncompiled, strict mode bundles that use eval to divide source + * like so: + * eval('someSource;//# sourceUrl sourcefile.js'); + * We need to export the globally defined symbols "goog" and "COMPILED". + * Exporting "goog" breaks the compiler optimizations, so we required that + * be defined externally. + * NOTE: We don't use goog.exportSymbol here because we don't want to trigger + * extern generation when that compiler option is enabled. + */ +if (!COMPILED) { + goog.global['COMPILED'] = COMPILED; +} + + //============================================================================== // goog.defineClass implementation //============================================================================== +/** + * Creates a restricted form of a Closure "class": + * - from the compiler's perspective, the instance returned from the + * constructor is sealed (no new properties may be added). This enables + * better checks. + * - the compiler will rewrite this definition to a form that is optimal + * for type checking and optimization (initially this will be a more + * traditional form). + * + * @param {Function} superClass The superclass, Object or null. + * @param {goog.defineClass.ClassDescriptor} def + * An object literal describing + * the class. It may have the following properties: + * "constructor": the constructor function + * "statics": an object literal containing methods to add to the constructor + * as "static" methods or a function that will receive the constructor + * function as its only parameter to which static properties can + * be added. + * all other properties are added to the prototype. + * @return {!Function} The class constructor. + * @deprecated Use ECMAScript class syntax instead. + */ +goog.defineClass = function(superClass, def) { + // TODO(johnlenz): consider making the superClass an optional parameter. + var constructor = def.constructor; + var statics = def.statics; + // Wrap the constructor prior to setting up the prototype and static methods. + if (!constructor || constructor == Object.prototype.constructor) { + constructor = function() { + throw new Error( + 'cannot instantiate an interface (no constructor defined).'); + }; + } + + var cls = goog.defineClass.createSealingConstructor_(constructor, superClass); + if (superClass) { + goog.inherits(cls, superClass); + } + + // Remove all the properties that should not be copied to the prototype. + delete def.constructor; + delete def.statics; + + goog.defineClass.applyProperties_(cls.prototype, def); + if (statics != null) { + if (statics instanceof Function) { + statics(cls); + } else { + goog.defineClass.applyProperties_(cls, statics); + } + } + + return cls; +}; + + +/** + * @typedef {{ + * constructor: (!Function|undefined), + * statics: (Object|undefined|function(Function):void) + * }} + */ +goog.defineClass.ClassDescriptor; + + +/** + * @define {boolean} Whether the instances returned by goog.defineClass should + * be sealed when possible. + * + * When sealing is disabled the constructor function will not be wrapped by + * goog.defineClass, making it incompatible with ES6 class methods. + */ +goog.defineClass.SEAL_CLASS_INSTANCES = + goog.define('goog.defineClass.SEAL_CLASS_INSTANCES', goog.DEBUG); + + +/** + * If goog.defineClass.SEAL_CLASS_INSTANCES is enabled and Object.seal is + * defined, this function will wrap the constructor in a function that seals the + * results of the provided constructor function. + * + * @param {!Function} ctr The constructor whose results maybe be sealed. + * @param {Function} superClass The superclass constructor. + * @return {!Function} The replacement constructor. + * @private + */ +goog.defineClass.createSealingConstructor_ = function(ctr, superClass) { + if (!goog.defineClass.SEAL_CLASS_INSTANCES) { + // Do now wrap the constructor when sealing is disabled. Angular code + // depends on this for injection to work properly. + return ctr; + } + + // NOTE: The sealing behavior has been removed + + /** + * @this {Object} + * @return {?} + */ + var wrappedCtr = function() { + // Don't seal an instance of a subclass when it calls the constructor of + // its super class as there is most likely still setup to do. + var instance = ctr.apply(this, arguments) || this; + instance[goog.UID_PROPERTY_] = instance[goog.UID_PROPERTY_]; + + return instance; + }; + + return wrappedCtr; +}; + + + +// TODO(johnlenz): share these values with the goog.object +/** + * The names of the fields that are defined on Object.prototype. + * @type {!Array} + * @private + * @const + */ +goog.defineClass.OBJECT_PROTOTYPE_FIELDS_ = [ + 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', + 'toLocaleString', 'toString', 'valueOf' +]; + + +// TODO(johnlenz): share this function with the goog.object +/** + * @param {!Object} target The object to add properties to. + * @param {!Object} source The object to copy properties from. + * @private + */ +goog.defineClass.applyProperties_ = function(target, source) { + // TODO(johnlenz): update this to support ES5 getters/setters + + var key; + for (key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + + // For IE the for-in-loop does not contain any properties that are not + // enumerable on the prototype object (for example isPrototypeOf from + // Object.prototype) and it will also not include 'replace' on objects that + // extend String and change 'replace' (not that it is common for anyone to + // extend anything except Object). + for (var i = 0; i < goog.defineClass.OBJECT_PROTOTYPE_FIELDS_.length; i++) { + key = goog.defineClass.OBJECT_PROTOTYPE_FIELDS_[i]; + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } +}; + +/** + * Returns the parameter. + * @param {string} s + * @return {string} + * @private + */ +goog.identity_ = function(s) { + return s; +}; + + +/** + * Creates Trusted Types policy if Trusted Types are supported by the browser. + * The policy just blesses any string as a Trusted Type. It is not visibility + * restricted because anyone can also call trustedTypes.createPolicy directly. + * However, the allowed names should be restricted by a HTTP header and the + * reference to the created policy should be visibility restricted. + * @param {string} name + * @return {?TrustedTypePolicy} + */ +goog.createTrustedTypesPolicy = function(name) { + var policy = null; + var policyFactory = goog.global.trustedTypes; + if (!policyFactory || !policyFactory.createPolicy) { + return policy; + } + // trustedTypes.createPolicy throws if called with a name that is already + // registered, even in report-only mode. Until the API changes, catch the + // error not to break the applications functionally. In such case, the code + // will fall back to using regular Safe Types. + // TODO(koto): Remove catching once createPolicy API stops throwing. + try { + policy = policyFactory.createPolicy(name, { + createHTML: goog.identity_, + createScript: goog.identity_, + createScriptURL: goog.identity_ + }); + } catch (e) { + goog.logToConsole_(e.message); + } + return policy; +}; + // There's a bug in the compiler where without collapse properties the // Closure namespace defines do not guard code correctly. To help reduce code // size also check for !COMPILED even though it redundant until this is fixed. +if (!COMPILED && goog.DEPENDENCIES_ENABLED) { -/** - * Tries to detect the base path of base.js script that bootstraps Closure. - * @private - */ -goog.findBasePath_ = function() { - /** @type {!Document} */ - var doc = goog.global.document; - // If we have a currentScript available, use it exclusively. - var currentScript = doc.currentScript; - if (currentScript) { - var scripts = [currentScript]; - } else { - var scripts = doc.getElementsByTagName('SCRIPT'); - } - // Search backwards since the current script is in almost all cases the one - // that has base.js. - for (var i = scripts.length - 1; i >= 0; --i) { - var script = /** @type {!HTMLScriptElement} */ (scripts[i]); - var src = script.src; - var qmark = src.lastIndexOf('?'); - var l = qmark == -1 ? src.length : qmark; - if (src.substr(l - 7, 7) == 'base.js') { - goog.basePath = src.substr(0, l - 7); + + /** + * Tries to detect whether the current browser is Edge, based on the user + * agent. This matches only pre-Chromium Edge. + * @see https://docs.microsoft.com/en-us/microsoft-edge/web-platform/user-agent-string + * @return {boolean} True if the current browser is Edge. + * @private + */ + goog.isEdge_ = function() { + var userAgent = goog.global.navigator && goog.global.navigator.userAgent ? + goog.global.navigator.userAgent : + ''; + var edgeRe = /Edge\/(\d+)(\.\d)*/i; + return !!userAgent.match(edgeRe); + }; + + + /** + * Tries to detect whether is in the context of an HTML document. + * @return {boolean} True if it looks like HTML document. + * @private + */ + goog.inHtmlDocument_ = function() { + /** @type {!Document} */ + var doc = goog.global.document; + return doc != null && 'write' in doc; // XULDocument misses write. + }; + + + /** + * We'd like to check for if the document readyState is 'loading'; however + * there are bugs on IE 10 and below where the readyState being anything other + * than 'complete' is not reliable. + * @return {boolean} + * @private + */ + goog.isDocumentLoading_ = function() { + // attachEvent is available on IE 6 thru 10 only, and thus can be used to + // detect those browsers. + /** @type {!HTMLDocument} */ + var doc = goog.global.document; + return doc.attachEvent ? doc.readyState != 'complete' : + doc.readyState == 'loading'; + }; + + + /** + * Tries to detect the base path of base.js script that bootstraps Closure. + * @private + */ + goog.findBasePath_ = function() { + if (goog.global.CLOSURE_BASE_PATH != undefined && + // Anti DOM-clobbering runtime check (b/37736576). + typeof goog.global.CLOSURE_BASE_PATH === 'string') { + goog.basePath = goog.global.CLOSURE_BASE_PATH; + return; + } else if (!goog.inHtmlDocument_()) { return; } - } -}; + /** @type {!Document} */ + var doc = goog.global.document; + // If we have a currentScript available, use it exclusively. + var currentScript = doc.currentScript; + if (currentScript) { + var scripts = [currentScript]; + } else { + var scripts = doc.getElementsByTagName('SCRIPT'); + } + // Search backwards since the current script is in almost all cases the one + // that has base.js. + for (var i = scripts.length - 1; i >= 0; --i) { + var script = /** @type {!HTMLScriptElement} */ (scripts[i]); + var src = script.src; + var qmark = src.lastIndexOf('?'); + var l = qmark == -1 ? src.length : qmark; + if (src.substr(l - 7, 7) == 'base.js') { + goog.basePath = src.substr(0, l - 7); + return; + } + } + }; -goog.findBasePath_(); + goog.findBasePath_(); -/** - * A debug loader is responsible for downloading and executing javascript - * files in an unbundled, uncompiled environment. - * - * @struct @constructor @final @private - */ -goog.DebugLoader_ = function() { - /** @private @const {!Object} */ - this.dependencies_ = {}; - /** @private @const {!Object} */ - this.idToPath_ = {}; - /** @private @const {!Object} */ - this.written_ = {}; - /** @private {!Array} */ - this.depsToLoad_ = []; -}; + /** @struct @constructor @final */ + goog.Transpiler = function() { + /** @private {?Object} */ + this.requiresTranspilation_ = null; + /** @private {string} */ + this.transpilationTarget_ = goog.TRANSPILE_TO_LANGUAGE; + }; + /** + * Returns a newly created map from language mode string to a boolean + * indicating whether transpilation should be done for that mode as well as + * the highest level language that this environment supports. + * + * Guaranteed invariant: + * For any two modes, l1 and l2 where l2 is a newer mode than l1, + * `map[l1] == true` implies that `map[l2] == true`. + * + * Note this method is extracted and used elsewhere, so it cannot rely on + * anything external (it should easily be able to be transformed into a + * standalone, top level function). + * + * @private + * @return {{ + * target: string, + * map: !Object + * }} + */ + goog.Transpiler.prototype.createRequiresTranspilation_ = function() { + var transpilationTarget = 'es3'; + var /** !Object */ requiresTranspilation = {'es3': false}; + var transpilationRequiredForAllLaterModes = false; + + /** + * Adds an entry to requiresTranspliation for the given language mode. + * + * IMPORTANT: Calls must be made in order from oldest to newest language + * mode. + * @param {string} modeName + * @param {function(): boolean} isSupported Returns true if the JS engine + * supports the given mode. + */ + function addNewerLanguageTranspilationCheck(modeName, isSupported) { + if (transpilationRequiredForAllLaterModes) { + requiresTranspilation[modeName] = true; + } else if (isSupported()) { + transpilationTarget = modeName; + requiresTranspilation[modeName] = false; + } else { + requiresTranspilation[modeName] = true; + transpilationRequiredForAllLaterModes = true; + } + } + + /** + * Does the given code evaluate without syntax errors and return a truthy + * result? + */ + function /** boolean */ evalCheck(/** string */ code) { + try { + return !!eval(goog.CLOSURE_EVAL_PREFILTER_.createScript(code)); + } catch (ignored) { + return false; + } + } + + // Identify ES3-only browsers by their incorrect treatment of commas. + addNewerLanguageTranspilationCheck('es5', function() { + return evalCheck('[1,].length==1'); + }); + addNewerLanguageTranspilationCheck('es6', function() { + // Edge has a non-deterministic (i.e., not reproducible) bug with ES6: + // https://github.com/Microsoft/ChakraCore/issues/1496. + if (goog.isEdge_()) { + // The Reflect.construct test below is flaky on Edge. It can sometimes + // pass or fail on 40 15.15063, so just exit early for Edge and treat + // it as ES5. Until we're on a more up to date version just always use + // ES5. See https://github.com/Microsoft/ChakraCore/issues/3217. + return false; + } + // Test es6: [FF50 (?), Edge 14 (?), Chrome 50] + // (a) default params (specifically shadowing locals), + // (b) destructuring, (c) block-scoped functions, + // (d) for-of (const), (e) new.target/Reflect.construct + var es6fullTest = + 'class X{constructor(){if(new.target!=String)throw 1;this.x=42}}' + + 'let q=Reflect.construct(X,[],String);if(q.x!=42||!(q instanceof ' + + 'String))throw 1;for(const a of[2,3]){if(a==2)continue;function ' + + 'f(z={a}){let a=0;return z.a}{function f(){return 0;}}return f()' + + '==3}'; + + return evalCheck('(()=>{"use strict";' + es6fullTest + '})()'); + }); + // ** and **= are the only new features in 'es7' + addNewerLanguageTranspilationCheck('es7', function() { + return evalCheck('2**3==8'); + }); + // async functions are the only new features in 'es8' + addNewerLanguageTranspilationCheck('es8', function() { + return evalCheck('async()=>1,1'); + }); + addNewerLanguageTranspilationCheck('es9', function() { + return evalCheck('({...rest}={}),1'); + }); + // optional catch binding, unescaped unicode paragraph separator in strings + addNewerLanguageTranspilationCheck('es_2019', function() { + return evalCheck('let r;try{r="\u2029"}catch{};r'); + }); + // optional chaining, nullish coalescing + // untested/unsupported: bigint, import meta + addNewerLanguageTranspilationCheck('es_2020', function() { + return evalCheck('null?.x??1'); + }); + addNewerLanguageTranspilationCheck('es_next', function() { + return false; // assume it always need to transpile + }); + return {target: transpilationTarget, map: requiresTranspilation}; + }; -/** - * Travserses the dependency graph and queues the given dependency, and all of - * its transitive dependencies, for loading and then starts loading if not - * paused. - * - * @param {string} namespace - * @private - */ -goog.DebugLoader_.prototype.load_ = function(namespace) { - if (!this.getPathFromDeps_(namespace)) { - throw Error('goog.require could not find: ' + namespace); - } else { - var loader = this; + /** + * Determines whether the given language needs to be transpiled. + * @param {string} lang + * @param {string|undefined} module + * @return {boolean} + */ + goog.Transpiler.prototype.needsTranspile = function(lang, module) { + if (goog.TRANSPILE == 'always') { + return true; + } else if (goog.TRANSPILE == 'never') { + return false; + } else if (!this.requiresTranspilation_) { + var obj = this.createRequiresTranspilation_(); + this.requiresTranspilation_ = obj.map; + this.transpilationTarget_ = this.transpilationTarget_ || obj.target; + } + if (lang in this.requiresTranspilation_) { + if (this.requiresTranspilation_[lang]) { + return true; + } else if ( + goog.inHtmlDocument_() && module == 'es6' && + !('noModule' in goog.global.document.createElement('script'))) { + return true; + } else { + return false; + } + } else { + throw new Error('Unknown language mode: ' + lang); + } + }; + + + /** + * Lazily retrieves the transpiler and applies it to the source. + * @param {string} code JS code. + * @param {string} path Path to the code. + * @return {string} The transpiled code. + */ + goog.Transpiler.prototype.transpile = function(code, path) { + // TODO(johnplaisted): We should delete goog.transpile_ and just have this + // function. But there's some compile error atm where goog.global is being + // stripped incorrectly without this. + return goog.transpile_(code, path, this.transpilationTarget_); + }; + + + /** @private @final {!goog.Transpiler} */ + goog.transpiler_ = new goog.Transpiler(); + + /** + * Rewrites closing script tags in input to avoid ending an enclosing script + * tag. + * + * @param {string} str + * @return {string} + * @private + */ + goog.protectScriptTag_ = function(str) { + return str.replace(/<\/(SCRIPT)/ig, '\\x3c/$1'); + }; + + + /** + * A debug loader is responsible for downloading and executing javascript + * files in an unbundled, uncompiled environment. + * + * This can be custimized via the setDependencyFactory method, or by + * CLOSURE_IMPORT_SCRIPT/CLOSURE_LOAD_FILE_SYNC. + * + * @struct @constructor @final @private + */ + goog.DebugLoader_ = function() { + /** @private @const {!Object} */ + this.dependencies_ = {}; + /** @private @const {!Object} */ + this.idToPath_ = {}; + /** @private @const {!Object} */ + this.written_ = {}; + /** @private @const {!Array} */ + this.loadingDeps_ = []; + /** @private {!Array} */ + this.depsToLoad_ = []; + /** @private {boolean} */ + this.paused_ = false; + /** @private {!goog.DependencyFactory} */ + this.factory_ = new goog.DependencyFactory(goog.transpiler_); + /** @private @const {!Object} */ + this.deferredCallbacks_ = {}; + /** @private @const {!Array} */ + this.deferredQueue_ = []; + }; + + /** + * @param {!Array} namespaces + * @param {function(): undefined} callback Function to call once all the + * namespaces have loaded. + */ + goog.DebugLoader_.prototype.bootstrap = function(namespaces, callback) { + var cb = callback; + function resolve() { + if (cb) { + goog.global.setTimeout(cb, 0); + cb = null; + } + } + + if (!namespaces.length) { + resolve(); + return; + } var deps = []; - - /** @param {string} namespace */ - var visit = function(namespace) { - var path = loader.getPathFromDeps_(namespace); - + for (var i = 0; i < namespaces.length; i++) { + var path = this.getPathFromDeps_(namespaces[i]); if (!path) { - throw Error('Bad dependency path or symbol: ' + namespace); + throw new Error('Unregonized namespace: ' + namespaces[i]); + } + deps.push(this.dependencies_[path]); + } + + var require = goog.require; + var loaded = 0; + for (var i = 0; i < namespaces.length; i++) { + require(namespaces[i]); + deps[i].onLoad(function() { + if (++loaded == namespaces.length) { + resolve(); + } + }); + } + }; + + + /** + * Loads the Closure Dependency file. + * + * Exposed a public function so CLOSURE_NO_DEPS can be set to false, base + * loaded, setDependencyFactory called, and then this called. i.e. allows + * custom loading of the deps file. + */ + goog.DebugLoader_.prototype.loadClosureDeps = function() { + // Circumvent addDependency, which would try to transpile deps.js if + // transpile is set to always. + var relPath = 'deps.js'; + this.depsToLoad_.push(this.factory_.createDependency( + goog.normalizePath_(goog.basePath + relPath), relPath, [], [], {}, + false)); + this.loadDeps_(); + }; + + + /** + * Notifies the debug loader when a dependency has been requested. + * + * @param {string} absPathOrId Path of the dependency or goog id. + * @param {boolean=} opt_force + */ + goog.DebugLoader_.prototype.requested = function(absPathOrId, opt_force) { + var path = this.getPathFromDeps_(absPathOrId); + if (path && + (opt_force || this.areDepsLoaded_(this.dependencies_[path].requires))) { + var callback = this.deferredCallbacks_[path]; + if (callback) { + delete this.deferredCallbacks_[path]; + callback(); + } + } + }; + + + /** + * Sets the dependency factory, which can be used to create custom + * goog.Dependency implementations to control how dependencies are loaded. + * + * @param {!goog.DependencyFactory} factory + */ + goog.DebugLoader_.prototype.setDependencyFactory = function(factory) { + this.factory_ = factory; + }; + + + /** + * Travserses the dependency graph and queues the given dependency, and all of + * its transitive dependencies, for loading and then starts loading if not + * paused. + * + * @param {string} namespace + * @private + */ + goog.DebugLoader_.prototype.load_ = function(namespace) { + if (!this.getPathFromDeps_(namespace)) { + var errorMessage = 'goog.require could not find: ' + namespace; + goog.logToConsole_(errorMessage); + } else { + var loader = this; + + var deps = []; + + /** @param {string} namespace */ + var visit = function(namespace) { + var path = loader.getPathFromDeps_(namespace); + + if (!path) { + throw new Error('Bad dependency path or symbol: ' + namespace); + } + + if (loader.written_[path]) { + return; + } + + loader.written_[path] = true; + + var dep = loader.dependencies_[path]; + for (var i = 0; i < dep.requires.length; i++) { + if (!goog.isProvided_(dep.requires[i])) { + visit(dep.requires[i]); + } + } + + deps.push(dep); + }; + + visit(namespace); + + var wasLoading = !!this.depsToLoad_.length; + this.depsToLoad_ = this.depsToLoad_.concat(deps); + + if (!this.paused_ && !wasLoading) { + this.loadDeps_(); + } + } + }; + + + /** + * Loads any queued dependencies until they are all loaded or paused. + * + * @private + */ + goog.DebugLoader_.prototype.loadDeps_ = function() { + var loader = this; + var paused = this.paused_; + + while (this.depsToLoad_.length && !paused) { + (function() { + var loadCallDone = false; + var dep = loader.depsToLoad_.shift(); + + var loaded = false; + loader.loading_(dep); + + var controller = { + pause: function() { + if (loadCallDone) { + throw new Error('Cannot call pause after the call to load.'); + } else { + paused = true; + } + }, + resume: function() { + if (loadCallDone) { + loader.resume_(); + } else { + // Some dep called pause and then resume in the same load call. + // Just keep running this same loop. + paused = false; + } + }, + loaded: function() { + if (loaded) { + throw new Error('Double call to loaded.'); + } + + loaded = true; + loader.loaded_(dep); + }, + pending: function() { + // Defensive copy. + var pending = []; + for (var i = 0; i < loader.loadingDeps_.length; i++) { + pending.push(loader.loadingDeps_[i]); + } + return pending; + }, + /** + * @param {goog.ModuleType} type + */ + setModuleState: function(type) { + goog.moduleLoaderState_ = { + type: type, + moduleName: '', + declareLegacyNamespace: false + }; + }, + /** @type {function(string, string, string=)} */ + registerEs6ModuleExports: function( + path, exports, opt_closureNamespace) { + if (opt_closureNamespace) { + goog.loadedModules_[opt_closureNamespace] = { + exports: exports, + type: goog.ModuleType.ES6, + moduleId: opt_closureNamespace || '' + }; + } + }, + /** @type {function(string, ?)} */ + registerGoogModuleExports: function(moduleId, exports) { + goog.loadedModules_[moduleId] = { + exports: exports, + type: goog.ModuleType.GOOG, + moduleId: moduleId + }; + }, + clearModuleState: function() { + goog.moduleLoaderState_ = null; + }, + defer: function(callback) { + if (loadCallDone) { + throw new Error( + 'Cannot register with defer after the call to load.'); + } + loader.defer_(dep, callback); + }, + areDepsLoaded: function() { + return loader.areDepsLoaded_(dep.requires); + } + }; + + try { + dep.load(controller); + } finally { + loadCallDone = true; + } + })(); + } + + if (paused) { + this.pause_(); + } + }; + + + /** @private */ + goog.DebugLoader_.prototype.pause_ = function() { + this.paused_ = true; + }; + + + /** @private */ + goog.DebugLoader_.prototype.resume_ = function() { + if (this.paused_) { + this.paused_ = false; + this.loadDeps_(); + } + }; + + + /** + * Marks the given dependency as loading (load has been called but it has not + * yet marked itself as finished). Useful for dependencies that want to know + * what else is loading. Example: goog.modules cannot eval if there are + * loading dependencies. + * + * @param {!goog.Dependency} dep + * @private + */ + goog.DebugLoader_.prototype.loading_ = function(dep) { + this.loadingDeps_.push(dep); + }; + + + /** + * Marks the given dependency as having finished loading and being available + * for require. + * + * @param {!goog.Dependency} dep + * @private + */ + goog.DebugLoader_.prototype.loaded_ = function(dep) { + for (var i = 0; i < this.loadingDeps_.length; i++) { + if (this.loadingDeps_[i] == dep) { + this.loadingDeps_.splice(i, 1); + break; + } + } + + for (var i = 0; i < this.deferredQueue_.length; i++) { + if (this.deferredQueue_[i] == dep.path) { + this.deferredQueue_.splice(i, 1); + break; + } + } + + if (this.loadingDeps_.length == this.deferredQueue_.length && + !this.depsToLoad_.length) { + // Something has asked to load these, but they may not be directly + // required again later, so load them now that we know we're done loading + // everything else. e.g. a goog module entry point. + while (this.deferredQueue_.length) { + this.requested(this.deferredQueue_.shift(), true); + } + } + + dep.loaded(); + }; + + + /** + * @param {!Array} pathsOrIds + * @return {boolean} + * @private + */ + goog.DebugLoader_.prototype.areDepsLoaded_ = function(pathsOrIds) { + for (var i = 0; i < pathsOrIds.length; i++) { + var path = this.getPathFromDeps_(pathsOrIds[i]); + if (!path || + (!(path in this.deferredCallbacks_) && + !goog.isProvided_(pathsOrIds[i]))) { + return false; + } + } + + return true; + }; + + + /** + * @param {string} absPathOrId + * @return {?string} + * @private + */ + goog.DebugLoader_.prototype.getPathFromDeps_ = function(absPathOrId) { + if (absPathOrId in this.idToPath_) { + return this.idToPath_[absPathOrId]; + } else if (absPathOrId in this.dependencies_) { + return absPathOrId; + } else { + return null; + } + }; + + + /** + * @param {!goog.Dependency} dependency + * @param {!Function} callback + * @private + */ + goog.DebugLoader_.prototype.defer_ = function(dependency, callback) { + this.deferredCallbacks_[dependency.path] = callback; + this.deferredQueue_.push(dependency.path); + }; + + + /** + * Interface for goog.Dependency implementations to have some control over + * loading of dependencies. + * + * @record + */ + goog.LoadController = function() {}; + + + /** + * Tells the controller to halt loading of more dependencies. + */ + goog.LoadController.prototype.pause = function() {}; + + + /** + * Tells the controller to resume loading of more dependencies if paused. + */ + goog.LoadController.prototype.resume = function() {}; + + + /** + * Tells the controller that this dependency has finished loading. + * + * This causes this to be removed from pending() and any load callbacks to + * fire. + */ + goog.LoadController.prototype.loaded = function() {}; + + + /** + * List of dependencies on which load has been called but which have not + * called loaded on their controller. This includes the current dependency. + * + * @return {!Array} + */ + goog.LoadController.prototype.pending = function() {}; + + + /** + * Registers an object as an ES6 module's exports so that goog.modules may + * require it by path. + * + * @param {string} path Full path of the module. + * @param {?} exports + * @param {string=} opt_closureNamespace Closure namespace to associate with + * this module. + */ + goog.LoadController.prototype.registerEs6ModuleExports = function( + path, exports, opt_closureNamespace) {}; + + + /** + * Sets the current module state. + * + * @param {goog.ModuleType} type Type of module. + */ + goog.LoadController.prototype.setModuleState = function(type) {}; + + + /** + * Clears the current module state. + */ + goog.LoadController.prototype.clearModuleState = function() {}; + + + /** + * Registers a callback to call once the dependency is actually requested + * via goog.require + all of the immediate dependencies have been loaded or + * all other files have been loaded. Allows for lazy loading until + * require'd without pausing dependency loading, which is needed on old IE. + * + * @param {!Function} callback + */ + goog.LoadController.prototype.defer = function(callback) {}; + + + /** + * @return {boolean} + */ + goog.LoadController.prototype.areDepsLoaded = function() {}; + + + /** + * Basic super class for all dependencies Closure Library can load. + * + * This default implementation is designed to load untranspiled, non-module + * scripts in a web broswer. + * + * For transpiled non-goog.module files {@see goog.TranspiledDependency}. + * For goog.modules see {@see goog.GoogModuleDependency}. + * For untranspiled ES6 modules {@see goog.Es6ModuleDependency}. + * + * @param {string} path Absolute path of this script. + * @param {string} relativePath Path of this script relative to goog.basePath. + * @param {!Array} provides goog.provided or goog.module symbols + * in this file. + * @param {!Array} requires goog symbols or relative paths to Closure + * this depends on. + * @param {!Object} loadFlags + * @struct @constructor + */ + goog.Dependency = function( + path, relativePath, provides, requires, loadFlags) { + /** @const */ + this.path = path; + /** @const */ + this.relativePath = relativePath; + /** @const */ + this.provides = provides; + /** @const */ + this.requires = requires; + /** @const */ + this.loadFlags = loadFlags; + /** @private {boolean} */ + this.loaded_ = false; + /** @private {!Array} */ + this.loadCallbacks_ = []; + }; + + + /** + * @return {string} The pathname part of this dependency's path if it is a + * URI. + */ + goog.Dependency.prototype.getPathName = function() { + var pathName = this.path; + var protocolIndex = pathName.indexOf('://'); + if (protocolIndex >= 0) { + pathName = pathName.substring(protocolIndex + 3); + var slashIndex = pathName.indexOf('/'); + if (slashIndex >= 0) { + pathName = pathName.substring(slashIndex + 1); + } + } + return pathName; + }; + + + /** + * @param {function()} callback Callback to fire as soon as this has loaded. + * @final + */ + goog.Dependency.prototype.onLoad = function(callback) { + if (this.loaded_) { + callback(); + } else { + this.loadCallbacks_.push(callback); + } + }; + + + /** + * Marks this dependency as loaded and fires any callbacks registered with + * onLoad. + * @final + */ + goog.Dependency.prototype.loaded = function() { + this.loaded_ = true; + var callbacks = this.loadCallbacks_; + this.loadCallbacks_ = []; + for (var i = 0; i < callbacks.length; i++) { + callbacks[i](); + } + }; + + + /** + * Whether or not document.written / appended script tags should be deferred. + * + * @private {boolean} + */ + goog.Dependency.defer_ = false; + + + /** + * Map of script ready / state change callbacks. Old IE cannot handle putting + * these properties on goog.global. + * + * @private @const {!Object} + */ + goog.Dependency.callbackMap_ = {}; + + + /** + * @param {function(...?):?} callback + * @return {string} + * @private + */ + goog.Dependency.registerCallback_ = function(callback) { + var key = Math.random().toString(32); + goog.Dependency.callbackMap_[key] = callback; + return key; + }; + + + /** + * @param {string} key + * @private + */ + goog.Dependency.unregisterCallback_ = function(key) { + delete goog.Dependency.callbackMap_[key]; + }; + + + /** + * @param {string} key + * @param {...?} var_args + * @private + * @suppress {unusedPrivateMembers} + */ + goog.Dependency.callback_ = function(key, var_args) { + if (key in goog.Dependency.callbackMap_) { + var callback = goog.Dependency.callbackMap_[key]; + var args = []; + for (var i = 1; i < arguments.length; i++) { + args.push(arguments[i]); + } + callback.apply(undefined, args); + } else { + var errorMessage = 'Callback key ' + key + + ' does not exist (was base.js loaded more than once?).'; + throw Error(errorMessage); + } + }; + + + /** + * Starts loading this dependency. This dependency can pause loading if it + * needs to and resume it later via the controller interface. + * + * When this is loaded it should call controller.loaded(). Note that this will + * end up calling the loaded method of this dependency; there is no need to + * call it explicitly. + * + * @param {!goog.LoadController} controller + */ + goog.Dependency.prototype.load = function(controller) { + if (goog.global.CLOSURE_IMPORT_SCRIPT) { + if (goog.global.CLOSURE_IMPORT_SCRIPT(this.path)) { + controller.loaded(); + } else { + controller.pause(); + } + return; + } + + if (!goog.inHtmlDocument_()) { + goog.logToConsole_( + 'Cannot use default debug loader outside of HTML documents.'); + if (this.relativePath == 'deps.js') { + // Some old code is relying on base.js auto loading deps.js failing with + // no error before later setting CLOSURE_IMPORT_SCRIPT. + // CLOSURE_IMPORT_SCRIPT should be set *before* base.js is loaded, or + // CLOSURE_NO_DEPS set to true. + goog.logToConsole_( + 'Consider setting CLOSURE_IMPORT_SCRIPT before loading base.js, ' + + 'or setting CLOSURE_NO_DEPS to true.'); + controller.loaded(); + } else { + controller.pause(); + } + return; + } + + /** @type {!HTMLDocument} */ + var doc = goog.global.document; + + // If the user tries to require a new symbol after document load, + // something has gone terribly wrong. Doing a document.write would + // wipe out the page. This does not apply to the CSP-compliant method + // of writing script tags. + if (doc.readyState == 'complete' && + !goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING) { + // Certain test frameworks load base.js multiple times, which tries + // to write deps.js each time. If that happens, just fail silently. + // These frameworks wipe the page between each load of base.js, so this + // is OK. + var isDeps = /\bdeps.js$/.test(this.path); + if (isDeps) { + controller.loaded(); + return; + } else { + throw Error('Cannot write "' + this.path + '" after document load'); + } + } + + var nonce = goog.getScriptNonce_(); + if (!goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING && + goog.isDocumentLoading_()) { + var key; + var callback = function(script) { + if (script.readyState && script.readyState != 'complete') { + script.onload = callback; + return; + } + goog.Dependency.unregisterCallback_(key); + controller.loaded(); + }; + key = goog.Dependency.registerCallback_(callback); + + var defer = goog.Dependency.defer_ ? ' defer' : ''; + var nonceAttr = nonce ? ' nonce="' + nonce + '"' : ''; + var script = '