// 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.

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


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

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

/**
 * 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.
 * @private
 */
goog.exportPath_ = function(name) {
  var parts = name.split('.');
  var cur = goog.global;

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

  for (var part; parts.length && (part = parts.shift());) {
    if (cur[part] && cur[part] !== Object.prototype[part]) {
      cur = cur[part];
    } else {
      cur = cur[part] = {};
    }
  }
};

/**
 * Defines a namespace in Closure.
 *
 * A namespace may only be defined once in a codebase. It may be defined using
 * goog.provide() or goog.module().
 *
 * The presence of one or more goog.provide() calls in a file indicates
 * that the file defines the given objects/namespaces.
 * Provided symbols must not be null or undefined.
 *
 * In addition, goog.provide() creates the object stubs for a namespace
 * (for example, goog.provide("goog.foo.bar") will create the object
 * goog.foo.bar if it does not already exist).
 *
 * Build tools also scan for provide/require/module statements
 * to discern dependencies, build dependency files (see deps.js), etc.
 *
 * @see goog.require
 * @see goog.module
 * @param {string} name Namespace provided by this file in the form
 *     "goog.package.part".
 */
goog.provide = function(name) {
  // 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.');
  }

  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);
};

/**
 * @private {?{
 *   moduleName: (string|undefined),
 *   declareLegacyNamespace:boolean,
 *   type: ?goog.ModuleType
 * }}
 */
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
 */
goog.isProvided_ = function(name) {
  return (!goog.implicitNamespaces_[name] &&
       goog.isDefAndNotNull(goog.getObjectByName(name)));
};

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

// 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.


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


/**
 * Adds a dependency from a file to the files it requires.
 * @param {string} relPath The path to the js file.
 * @param {!Array<string>} provides An array of strings with
 *     the names of the objects this file provides.
 * @param {!Array<string>} requires An array of strings with
 *     the names of the objects this file requires.
 */
goog.addDependency = function(relPath, provides, requires) {
  goog.debugLoader_.addDependency(relPath, provides, requires);
};




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


/**
 * Implements a system for the dynamic resolution of dependencies that works in
 * parallel with the BUILD system.
 *
 * Note that all calls to goog.require will be stripped by the compiler.
 *
 * @see goog.provide
 * @param {string} namespace Namespace (as was given in goog.provide,
 *     goog.module, or goog.declareModuleId) in the form
 *     "goog.package.part".
 * @return {?} If called within a goog.module or ES6 module file, the associated
 *     namespace or module otherwise null.
 */
goog.require = function(namespace) {
  // If 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;
    }
  }

  return null;
};

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

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

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


//==============================================================================
// 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.
 */
goog.isDefAndNotNull = function(val) {
  // Note that undefined == null.
  return val != null;
};

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


// 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.

/**
 * 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);
      return;
    }
  }
};

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<string, !goog.Dependency>} */
  this.dependencies_ = {};
  /** @private @const {!Object<string, string>} */
  this.idToPath_ = {};
  /** @private @const {!Object<string, boolean>} */
  this.written_ = {};
  /** @private {!Array<!goog.Dependency>} */
  this.depsToLoad_ = [];
};


/**
 * 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;

    var deps = [];

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

      if (!path) {
        throw 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 (!wasLoading) {
      this.loadDeps_();
    }
  }
};


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

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

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


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


/**
 * 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 {!Array<string>} requires goog symbols or relative paths to Closure
 *     this depends on.
 * @struct @constructor
 */
goog.Dependency = function(path, requires) {
  /** @const */
  this.path = path;
  /** @const */
  this.requires = requires;
};

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


/**
 * 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.
 */
goog.Dependency.prototype.load = function() {
  /** @type {!HTMLDocument} */
  var doc = goog.global.document;
  doc.write('<script src="' + this.path + '" type="text/javascript"><' +
      '/script>');
};


/**
 * @param {string} relPath
 * @param {!Array<string>|undefined} provides
 * @param {!Array<string>} requires
 * @see goog.addDependency
 */
goog.DebugLoader_.prototype.addDependency = function(
    relPath, provides, requires) {
  relPath = relPath.replace(/\\/g, '/');
  var path = goog.normalizePath_(goog.basePath + relPath);
  var dep = new goog.Dependency(path, requires);
  this.dependencies_[path] = dep;
  for (var i = 0; i < provides.length; i++) {
    this.idToPath_[provides[i]] = path;
  }
  this.idToPath_[relPath] = path;
};


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