t-comm
Version:
专业、稳定、纯粹的工具库
177 lines (173 loc) • 5.97 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
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);
}
}
};
exports.loader = loader;