This repository has been archived on 2023-03-18. You can view files and clone it, but cannot push or open issues or pull requests.
osr-discourse-src/app/assets/javascripts/discourse/app/lib/load-script.js

127 lines
2.9 KiB
JavaScript

import getURL, { getURLWithCDN } from "discourse-common/lib/get-url";
import { PUBLIC_JS_VERSIONS } from "discourse/lib/public-js-versions";
import { Promise } from "rsvp";
import { ajax } from "discourse/lib/ajax";
import { run } from "@ember/runloop";
import { isTesting } from "discourse-common/config/environment";
import { registerWaiter } from "@ember/test";
const _loaded = {};
const _loading = {};
function loadWithTag(path, cb) {
const head = document.getElementsByTagName("head")[0];
let finished = false;
let s = document.createElement("script");
s.src = path;
if (isTesting()) {
registerWaiter(() => finished);
}
// Don't leave it hanging if something goes wrong
s.onerror = function () {
finished = true;
};
s.onload = s.onreadystatechange = function (_, abort) {
finished = true;
if (
abort ||
!s.readyState ||
s.readyState === "loaded" ||
s.readyState === "complete"
) {
s = s.onload = s.onreadystatechange = null;
if (!abort) {
run(null, cb);
}
}
};
head.appendChild(s);
}
export function loadCSS(url) {
return loadScript(url, { css: true });
}
export default function loadScript(url, opts = {}) {
if (_loaded[url]) {
return Promise.resolve();
}
url = cacheBuster(url);
// Scripts should always load from CDN
// CSS is type text, to accept it from a CDN we would need to handle CORS
const fullUrl = opts.css ? getURL(url) : getURLWithCDN(url);
document.querySelectorAll("script").forEach((element) => {
const src = element.getAttribute("src");
if (src && src !== fullUrl && !_loading[src]) {
_loaded[src] = true;
}
});
return new Promise(function (resolve) {
// If we already loaded this url
if (_loaded[fullUrl]) {
return resolve();
}
if (_loading[fullUrl]) {
return _loading[fullUrl].then(resolve);
}
let done;
_loading[fullUrl] = new Promise(function (_done) {
done = _done;
});
_loading[fullUrl].then(function () {
delete _loading[fullUrl];
});
const cb = function (data) {
if (opts?.css) {
const style = document.createElement("style");
style.innerText = data;
document.querySelector("head").appendChild(style);
}
done();
resolve();
_loaded[url] = true;
_loaded[fullUrl] = true;
};
if (opts.css) {
ajax({
url: fullUrl,
dataType: "text",
}).then(cb);
} else {
// Always load JavaScript with script tag to avoid Content Security Policy inline violations
loadWithTag(fullUrl, cb);
}
});
}
export function cacheBuster(url) {
if (PUBLIC_JS_VERSIONS) {
let [folder, ...lib] = url.split("/").filter(Boolean);
if (folder === "javascripts") {
lib = lib.join("/").toLowerCase();
const versionedPath = PUBLIC_JS_VERSIONS[lib];
if (versionedPath) {
return `/javascripts/${versionedPath}`;
}
}
}
return url;
}