const { register } = require('./register');
const { themeManager } = require('./theme-manager');
const {
buildDependencyGetter,
presetMap,
} = require('./dependencies');
const ATTR_VERSION = 'data-hot-version';
class AbortError extends Error {}
const useHandsontable = (version, callback = () => {}, preset = 'hot', buildMode = 'production') => {
const getDependency = buildDependencyGetter(version, buildMode);
const abortSignal = register.getAbortSignal();
const loadDependency = dep => new Promise((resolve, reject) => {
const abortHandler = () => {
reject(new AbortError());
};
if (abortSignal.aborted) {
reject(abortHandler());
}
abortSignal.addEventListener('abort', abortHandler);
const getId = depName => `dependency-reloader_${depName}`;
const [jsUrl, dependentVars = [], cssUrl = undefined, globalVarSharedDependency] = getDependency(dep);
const id = getId(dep);
const _document = document; // eslint-disable-line no-restricted-globals
let script = null;
// As the documentation uses multiple versions of Vue (which reuse the same global variable - `Vue`), every
// time the Vue dependency is loaded, the previously used version should be removed.
if (globalVarSharedDependency) {
script = _document.getElementById(`script-${getId(globalVarSharedDependency)}`);
} else {
script = _document.getElementById(`script-${id}`);
}
// clear outdated version
if (script && (script.getAttribute(ATTR_VERSION) !== version || globalVarSharedDependency)) {
dependentVars.forEach(x => delete x.split('.').reduce((p, c) => p[c] || {}, {}));
script.remove();
script = null;
}
// clear outdated css
const css = _document.getElementById(`css-${id}`);
if (css && css.getAttribute(ATTR_VERSION) !== version) {
css.remove();
}
// import current version
if (!script) {
script = _document.createElement('script');
script.src = jsUrl;
script.id = `script-${id}`;
script.setAttribute(ATTR_VERSION, version);
script.addEventListener('load', () => {
script.loaded = true;
});
_document.head.appendChild(script);
if (Array.isArray(cssUrl)) {
cssUrl.forEach((cssUrlItem, index) => {
_document.head.insertAdjacentHTML(
'beforeend',
// eslint-disable-next-line max-len
``
);
});
} else if (cssUrl) {
_document.head.insertAdjacentHTML(
'beforeend',
``
);
}
}
// execute callback
if (script.loaded) {
setTimeout(() => {
abortSignal.removeEventListener('abort', abortHandler);
register.listen();
themeManager.ensureCorrectHotThemes();
resolve();
});
} else {
script.addEventListener('load', () => {
abortSignal.removeEventListener('abort', abortHandler);
register.listen();
themeManager.ensureCorrectHotThemes();
resolve();
});
}
});
const loadPreset = async() => {
const dependencies = presetMap[preset];
for (let i = 0; i < dependencies.length; i++) {
const dep = dependencies[i];
if (abortSignal.aborted) {
break;
}
// The order of loading is really important. For that purpose await was used.
await loadDependency(dep); // eslint-disable-line no-await-in-loop
}
};
loadPreset()
.then(callback)
.catch((err) => {
if (!(err instanceof AbortError)) {
throw err;
}
});
};
module.exports = { useHandsontable };