pleroma-fe/static/pleroma-mod-loader.js
2021-08-12 12:22:34 -05:00

325 lines
9.4 KiB
JavaScript

function PleromaModLoader () {
this.config = {
"modDirectory": "/instance/pleroma-mods/",
"mods": []
};
this.loadConfig();
this.loadedMods = {};
this.classes = {};
}
[
function loadMods () {
for (const mod of this.config.mods) {
const modObject = new PleromaMod(mod);
modObject.enabled = true;
if (localStorage.getItem("pleroma_mod_" + mod + "_enabled") === "false") {
modObject.enabled = false;
}
localStorage.setItem("pleroma_mod_" + mod + "_enabled", modObject.enabled);
modObject.include();
this.loadedMods[mod] = modObject;
}
},
function loadConfig () {
window.fetch("/instance/pleroma-mod-config.json").then((response) => {
if (response.ok) {
response.json().then((json) => {
for (const key in json) {
this.config[key] = json[key];
}
this.loadMods();
}).catch((error) => {
console.error("can't parse loader config");
console.error(error);
});
}
}).catch((error) => {
console.warn("can't load mod loader config");
console.warn(error);
});
},
function registerClass (className, object) {
this.classes[className] = object;
},
function waitUntilReady () {
const postPanel = document.getElementsByClassName("post-status-form");
const loginPanel = document.getElementsByClassName("login-form");
if (postPanel.length > 0 || loginPanel.length > 0) {
for (var modName in this.loadedMods) {
const settings = document.querySelector(".settings div[label]:first-child");
if (settings) {
if (!settings.querySelector(".mod-settings")) {
this.appendModSettings(settings);
}
}
const mod = this.loadedMods[modName];
if (mod.enabled && mod.instance) {
if (mod.instance.onReady) {
mod.instance.onReady();
}
}
}
this.createObserver();
} else {
window.setTimeout(() => { this.waitUntilReady(); }, 1000);
}
},
function createCheckbox (label, mod) {
const labelElement = document.createElement("label");
labelElement.classList.add("checkbox");
const input = document.createElement("input");
input.setAttribute("type", "checkbox");
input.checked = mod.enabled;
input.addEventListener("change", (event) => {
if (event.target.checked) {
mod.enable();
} else {
mod.disable();
}
});
labelElement.appendChild(input);
const fakeCheckbox = document.createElement("i");
fakeCheckbox.classList.add("checkbox-indicator");
labelElement.appendChild(fakeCheckbox);
const text = document.createElement("span");
text.classList.add("label");
text.innerText = label;
labelElement.appendChild(text);
return labelElement;
},
function appendModSettings (element) {
const container = document.createElement("div");
container.classList.add("setting-item");
container.classList.add("mod-settings");
const title = document.createElement("h2");
title.innerText = "Mods";
container.appendChild(title);
const optionList = document.createElement("ul");
optionList.classList.add("setting-list");
const modNames = Object.keys(this.loadedMods).sort();
for (const mod of modNames) {
const li = document.createElement("li");
const enable = this.createCheckbox("enable " + mod, this.loadedMods[mod]);
li.appendChild(enable);
optionList.appendChild(li);
}
container.appendChild(optionList);
element.appendChild(container);
},
function createObserver () {
this.containers = {
main: document.querySelector(".main"),
notifications: document.querySelector(".notifications"),
userPanel: document.querySelector(".user-panel"),
settingsModal: document.querySelector(".settings-modal")
};
const observerConfig = { subtree: true, childList: true };
this.observer = new MutationObserver((mutations, observer) => {
if (
mutations.length > 0 &&
mutations[0].addedNodes.length > 0 &&
mutations[0].addedNodes[0].classList &&
mutations[0].addedNodes[0].classList.contains("settings_tab-switcher")
) {
this.appendModSettings(mutations[0].addedNodes[0].querySelector("div[label]:first-child"));
}
for (var modName in this.loadedMods) {
const mod = this.loadedMods[modName];
if (mod.instance && mod.enabled) {
if (mod.instance.onMutation) {
for (const mutation of mutations) {
let filter = null;
if (mod.instance.config.filter) {
filter = new RegExp(mod.instance.config.filter.join("|"));
}
if (!filter || filter.test(mutation.target.className)) {
mod.instance.onMutation(mutation, observer);
}
}
}
}
}
});
this.observer.observe(this.containers.main, observerConfig);
if (this.containers.notifications) {
this.observer.observe(this.containers.notifications, observerConfig);
}
if (this.containers.userPanel) {
this.observer.observe(this.containers.userPanel, observerConfig);
}
if (this.containers.settingsModal) {
this.observer.observe(this.containers.settingsModal, observerConfig);
}
}
].forEach((fn) => { PleromaModLoader.prototype[fn.name] = fn; });
[
function registerMod (mod) {
window.__pleromaModLoader.registerClass(mod.name, mod);
},
function includeModScript (src) {
return PleromaModLoader.includeScript(window.__pleromaModLoader.config.modDirectory + src);
},
function includeModCss (src) {
return PleromaModLoader.includeCss(window.__pleromaModLoader.config.modDirectory + src);
},
function excludeModScript (src) {
return PleromaModLoader.excludeScript(window.__pleromaModLoader.config.modDirectory + src);
},
function excludeModCss (src) {
return PleromaModLoader.excludeCss(window.__pleromaModLoader.config.modDirectory + src);
},
function includeScript (src) {
return new Promise((resolve) => {
const body = document.getElementsByTagName("body")[0];
const script = document.createElement("script");
script.setAttribute("src", src);
script.setAttribute("type", "text/javascript");
script.onload = () => { resolve(); };
body.appendChild(script);
});
},
function getVueScope (element) {
if (!element) {
return null;
}
if (element.__vue__) {
return element.__vue__;
}
if (element.parentNode) {
return PleromaModLoader.getVueScope(element.parentNode);
}
return null;
},
function getRootVueScope () {
return document.querySelector("#app").__vue__;
},
function includeCss (src) {
return new Promise((resolve) => {
const head = document.getElementsByTagName("head")[0];
const link = document.createElement("link");
link.setAttribute("href", src);
link.setAttribute("rel", "stylesheet");
link.setAttribute("type", "text/css");
link.onload = () => { resolve(); };
head.appendChild(link);
});
},
function excludeScript (src) {
return new Promise((resolve) => {
const script = document.querySelector('script[src="' + src + '"]');
if(script) {
script.remove();
}
resolve();
});
},
function excludeCss (src) {
return new Promise((resolve) => {
const link = document.querySelector('link[href="' + src + '"]');
if(link) {
link.remove();
}
resolve();
});
},
function getToken () {
return PleromaModLoader.getRootVueScope().$store._vm.getUserToken();
},
function getModDir () {
return window.__pleromaModLoader.config.modDirectory;
}
].forEach((fn) => { PleromaModLoader[fn.name] = fn; });
function PleromaMod (name) {
this.name = name;
this.instance = null;
this.enabled = localStorage.getItem("pleroma_mod_" + this.name + "_enabled") !== "false";
}
[
function getClassName () {
let className = "PleromaMod";
const nameParts = this.name.split("-");
for (const namePart of nameParts) {
className += namePart.substring(0, 1).toUpperCase();
className += namePart.substring(1);
}
return className;
},
function enable () {
this.enabled = true;
this.modLoaded();
if (this.instance.onReady) {
this.instance.onReady();
}
localStorage.setItem("pleroma_mod_" + this.name + "_enabled", this.enabled);
},
function disable () {
this.enabled = false;
if (this.instance.onDestroy) {
this.instance.onDestroy();
}
console.log(this.name + " unloaded");
this.instance = null;
localStorage.setItem("pleroma_mod_" + this.name + "_enabled", this.enabled);
},
function include () {
console.log("loading " + this.name);
PleromaModLoader.includeScript(
PleromaModLoader.getModDir() + "pleroma-mod-" + this.name + "/mod.js"
).then(() => {
if (this.enabled) {
this.modLoaded();
}
});
},
function modLoaded () {
console.log(this.name + " loaded");
this.instance = new window.__pleromaModLoader.classes[this.getClassName()]();
this.run();
},
function run () {
if (this.instance) {
this.instance.run();
}
}
].forEach((fn) => { PleromaMod.prototype[fn.name] = fn; });
window.__pleromaModLoader = new PleromaModLoader();
window.__pleromaModLoader.waitUntilReady();