var addScript = function addScript(script) {
  var _a;
  // Get the first script element, we're just going to use it
  // as a reference for where to insert ours. Do NOT try to do
  // this just once at the top and then re-use the same script
  // as a reference later. Some weird loaders *remove* script
  // elements after the browser has executed their contents,
  // so the same reference might not have a parentNode later.
  var firstScript = document.getElementsByTagName('script')[0];
  // Append the script to the DOM, triggering execution.
  (_a = firstScript === null || firstScript === void 0 ? void 0 : firstScript.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(script, firstScript);
};
/**
 * 以 Callback 的方式加载 js 文件
 * @param {String}          src  js文件路径
 * @param {Function|Object} callback  加载回调
 * @param {String}          charset  指定js的字符集
 * @param {Object}          context Callback context
 * @example
 * ```ts
 * loader('https://example.com/lib.js', (err) => {
 *   if (err) return console.error(err);
 *   console.log('loaded');
 * });
 *
 * // 传递 setup 回调以修改 script 标签
 * loader('https://example.com/lib.js', {
 *   callback: (err) => {},
 *   setup: (script) => script.setAttribute('data-foo', '1'),
 * });
 * ```
 */
var loader = function loader(src, callback) {
  var charset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'utf-8';
  var context = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
  var setup;
  if (callback && typeof callback !== 'function') {
    context = callback.context || context;
    setup = callback.setup;
    callback = callback.callback;
  }
  var done = false;
  var err;
  var privateCleanup; // _must_ be set below.
  /**
     * Final handler for error or completion.
     *
     * **Note**: Will only be called _once_.
     *
     * @returns {void}
     */
  var privateFinish = function privateFinish() {
    // Only call once.
    if (done) {
      return;
    }
    done = true;
    // Internal cleanup.
    privateCleanup === null || privateCleanup === void 0 ? void 0 : privateCleanup();
    // Callback.
    if (callback) {
      callback.call(context, err);
    }
  };
  /**
     * Error handler
     *
     * @returns {void}
     */
  var privateError = function privateError() {
    err = new Error(src || 'EMPTY');
    privateFinish();
  };
  var curScript = document.querySelector("script[src=\"".concat(src, "\"]"));
  if (curScript) {
    var tc = setInterval(function () {
      if (curScript.isready) {
        // 判断js加载完成
        privateFinish();
        clearInterval(tc);
      }
    }, 20);
  } else {
    var script = document.createElement('script');
    script.isready = false;
    if (script.readyState && !('async' in script)) {
      var isReady = {
        loaded: true,
        complete: true
      };
      var inserted = false;
      // Clear out listeners, state.
      privateCleanup = function privateCleanup() {
        script.onreadystatechange = null;
        script.onerror = null;
      };
      // Attach the handler before setting src, otherwise we might
      // miss events (consider that IE could fire them synchronously
      // upon setting src, for example).
      script.onreadystatechange = function () {
        var firstState = script.readyState;
        // Protect against any errors from state change randomness.
        if (err) {
          return;
        }
        if (!inserted && isReady[firstState]) {
          inserted = true;
          // Append to DOM.
          addScript(script);
        }
        if (firstState === 'loaded') {
          // The act of accessing the property should change the script's
          // `readyState`.
          //
          // And, oh yeah, this hack is so hacky-ish we need the following
          // eslint-disable-next-line @typescript-eslint/no-unused-expressions
          script.children;
          if (script.readyState === 'loading') {
            // State transitions indicate we've hit the load error.
            //
            // **Note**: We are not intending to _return_ a value, just have
            // a shorter short-circuit code path here.
            return privateError();
          }
        }
        // It's possible for readyState to be "complete" immediately
        // after we insert (and execute) the script in the branch
        // above. So check readyState again here and react without
        // waiting for another onreadystatechange.
        if (script.readyState === 'complete') {
          script.isready = true;
          privateFinish();
        }
      };
      // Onerror handler _may_ work here.
      script.onerror = privateError;
      // call the setup callback to mutate the script tag
      if (setup) {
        setup.call(context, script);
      }
      // This triggers a request for the script, but its contents won't
      // be executed until we append it to the DOM.
      script.src = src;
      // In some cases, the readyState is already "loaded" immediately
      // after setting src. It's a lie! Don't append to the DOM until
      // the onreadystatechange event says so.
    } else {
      // This section is for modern browsers, including IE10+.
      // Clear out listeners.
      privateCleanup = function privateCleanup() {
        script.onload = null;
        script.onerror = null;
      };
      script.onerror = privateError;
      script.onload = function () {
        script.isready = true;
        privateFinish();
      };
      script.async = true;
      script.charset = charset || 'utf-8';
      // call the setup callback to mutate the script tag
      if (setup) {
        setup.call(context, script);
      }
      script.src = src;
      // Append to DOM.
      addScript(script);
    }
  }
};

export { loader };
