Ghost/ghost/admin/app/services/lazy-loader.js
Kevin Ansfield e7c3b1b0e3 🐛 Fixed Code Injection input fields not being clickable
no issue
- lazy loaded scripts such as the CodeMirror asset used on the Code Injection screen could throw errors such as `TypeError: Cannot set property 'modeOption' of undefined`
- this was caused by "loading" promise returned from the `lazyLoader` service returning as soon as the network request finished which can be before the loaded script has been parsed and run meaning any processing occurring after the promise returns could be depending on unloaded code
- switched the lazyLoader service's loading mechanism from an ajax fetch to insertion of a `<script>` tag which can have `load` event attached which _will_ return after parsing/loading has completed
2019-02-11 21:08:15 +00:00

85 lines
2.5 KiB
JavaScript

import RSVP from 'rsvp';
import Service, {inject as service} from '@ember/service';
import config from 'ghost-admin/config/environment';
export default Service.extend({
ajax: service(),
ghostPaths: service(),
// This is needed so we can disable it in unit tests
testing: undefined,
scriptPromises: null,
init() {
this._super(...arguments);
this.scriptPromises = {};
if (this.testing === undefined) {
this.testing = config.environment === 'test';
}
},
loadScript(key, url) {
if (this.testing) {
return RSVP.resolve();
}
if (this.scriptPromises[key]) {
return this.scriptPromises[key];
}
let scriptPromise = new RSVP.Promise((resolve, reject) => {
let {adminRoot} = this.ghostPaths;
let script = document.createElement('script');
script.type = 'text/javascript';
script.async = true;
script.src = `${adminRoot}${url}`;
let el = document.getElementsByTagName('script')[0];
el.parentNode.insertBefore(script, el);
script.addEventListener('load', () => {
resolve();
});
script.addEventListener('error', () => {
reject(new Error(`${url} failed to load`));
});
});
this.scriptPromises[key] = scriptPromise;
return scriptPromise;
},
loadStyle(key, url, alternate = false) {
if (this.testing || document.querySelector(`#${key}-styles`)) {
return RSVP.resolve();
}
return new RSVP.Promise((resolve, reject) => {
let link = document.createElement('link');
link.id = `${key}-styles`;
link.rel = alternate ? 'alternate stylesheet' : 'stylesheet';
link.href = `${this.ghostPaths.adminRoot}${url}`;
link.onload = () => {
if (alternate) {
// If stylesheet is alternate and we disable the stylesheet before injecting into the DOM,
// the onload handler never gets called. Thus, we should disable the link after it has finished loading
link.disabled = true;
}
resolve();
};
link.onerror = reject;
if (alternate) {
link.title = key;
}
document.querySelector('head').appendChild(link);
});
}
});