28b5f7de52
Over in: * https://github.com/whatwg/html/issues/3840 * https://bugzilla.mozilla.org/show_bug.cgi?id=1281135 I'm trying to come up with a model for `<link rel="stylesheet" disabled>` in which Blink / WebKit and Firefox can agree on. See that HTML spec issue for all the inconsistencies of WebKit / Blink, and the following post for more context: * https://groups.google.com/d/msg/mozilla.dev.platform/BdgNaChHnpY/mhXzCBwSCgAJ --- Unfortunately, my change to Firefox breaks the Ghost Admin panel night-mode switch (you can see it in Firefox Nightly). This is because with my change, removing the `disabled` attribute from an stylesheet behaves the same regardless of whether the `disabled` attribute is added dynamically or not. That means that adding the `disabled` attribute dynamically "unloads" the stylesheet completely (just like when the attribute is there before inserting the link in the document, or from the parser). Thus removing the attribute will load the stylesheet again and fire a load event. This is problematic for the code as-is, because it means that each time that the load event fires when the disabled attribute is removed on an alternate, then it's added again. :) Prevent that from happening by removing the load event listener ASAP. What this code wants is to only resolve the promise once after all. Given this is so far the only regression from my change that has been reported (over at https://bugzilla.mozilla.org/show_bug.cgi?id=1546707), I think fixing the Ghost-Admin panel is worth it. If this pattern is somehow common, then we'll probably revert that patch and go back to the sad current state of affairs regarding interop :(
86 lines
2.5 KiB
JavaScript
86 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 = () => {
|
|
link.onload = null;
|
|
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);
|
|
});
|
|
}
|
|
});
|