/** * @name ThemeSettings * @authorId 278543574059057154 * @invite Jx3TjNS * @donate https://www.paypal.me/MircoWittrien * @patreon https://www.patreon.com/MircoWittrien * @website https://github.com/mwittrien/BetterDiscordAddons/tree/master/Plugins/ThemeSettings * @source https://raw.githubusercontent.com/mwittrien/BetterDiscordAddons/master/Plugins/ThemeSettings/ThemeSettings.plugin.js * @updateUrl https://raw.githubusercontent.com/mwittrien/BetterDiscordAddons/master/Plugins/ThemeSettings/ThemeSettings.plugin.js */ module.exports = (_ => { const config = { "info": { "name": "ThemeSettings", "author": "DevilBro", "version": "1.3.0", "description": "Allow you to change Theme Variables within BetterDiscord. Adds a Settings button (similar to Plugins) to customizable Themes in your Themes Page" }, "changeLog": { "added": { "Imports": "You can now enable/disable imports in the settings" } } }; return !window.BDFDB_Global || (!window.BDFDB_Global.loaded && !window.BDFDB_Global.started) ? class { getName() {return config.info.name;} getAuthor() {return config.info.author;} getVersion() {return config.info.version;} getDescription() {return config.info.description;} load() { if (!window.BDFDB_Global || !Array.isArray(window.BDFDB_Global.pluginQueue)) window.BDFDB_Global = Object.assign({}, window.BDFDB_Global, {pluginQueue: []}); if (!window.BDFDB_Global.downloadModal) { window.BDFDB_Global.downloadModal = true; BdApi.showConfirmationModal("Library Missing", `The library plugin needed for ${config.info.name} is missing. Please click "Download Now" to install it.`, { confirmText: "Download Now", cancelText: "Cancel", onCancel: _ => {delete window.BDFDB_Global.downloadModal;}, onConfirm: _ => { delete window.BDFDB_Global.downloadModal; require("request").get("https://mwittrien.github.io/BetterDiscordAddons/Library/0BDFDB.plugin.js", (e, r, b) => { if (!e && b && b.indexOf(`* @name BDFDB`) > -1) require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0BDFDB.plugin.js"), b, _ => {}); else BdApi.alert("Error", "Could not download BDFDB library plugin, try again some time later."); }); } }); } if (!window.BDFDB_Global.pluginQueue.includes(config.info.name)) window.BDFDB_Global.pluginQueue.push(config.info.name); } start() {this.load();} stop() {} getSettingsPanel() { let template = document.createElement("template"); template.innerHTML = `
The library plugin needed for ${config.info.name} is missing.\nPlease click Download Now to install it.
`; template.content.firstElementChild.querySelector("a").addEventListener("click", _ => { require("request").get("https://mwittrien.github.io/BetterDiscordAddons/Library/0BDFDB.plugin.js", (e, r, b) => { if (!e && b && b.indexOf(`* @name BDFDB`) > -1) require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0BDFDB.plugin.js"), b, _ => {}); else BdApi.alert("Error", "Could not download BDFDB library plugin, try again some time later."); }); }); return template.content.firstElementChild; } } : (([Plugin, BDFDB]) => { const isBeta = !(window.BdApi && !Array.isArray(BdApi.settings)); var dir; return class ThemeSettings extends Plugin { onLoad() { dir = BDFDB.BDUtils.getThemesFolder(); } onStart() { let cardObserver = (new MutationObserver(changes => {changes.forEach(change => {if (change.addedNodes) {change.addedNodes.forEach(node => { if (BDFDB.DOMUtils.containsClass(node, BDFDB.disCN._repocard)) this.appendSettingsButton(node); if (node.nodeType != Node.TEXT_NODE) for (let child of node.querySelectorAll(BDFDB.dotCN._repocard)) this.appendSettingsButton(child); });}});})); BDFDB.ObserverUtils.connect(this, document.querySelector(`${BDFDB.dotCN.layer}[aria-label="${BDFDB.DiscordConstants.Layers.USER_SETTINGS}"]`), {name: "cardObserver", instance: cardObserver}, {childList: true, subtree: true}); BDFDB.ObserverUtils.connect(this, BDFDB.dotCN.applayers, {name: "appLayerObserver", instance: (new MutationObserver(changes => {changes.forEach(change => {if (change.addedNodes) {change.addedNodes.forEach(node => { if (node.nodeType != Node.TEXT_NODE && node.getAttribute("aria-label") == BDFDB.DiscordConstants.Layers.USER_SETTINGS) BDFDB.ObserverUtils.connect(this, node, {name: "cardObserver", instance: cardObserver}, {childList: true, subtree: true}); });}});}))}, {childList: true}); for (let child of document.querySelectorAll(BDFDB.dotCN._repocard)) this.appendSettingsButton(child); } onStop() { BDFDB.DOMUtils.remove(".theme-settings-button"); } appendSettingsButton (card) { if (card.querySelector(".theme-settings-button")) return; let addon = BDFDB.ObjectUtils.get(BDFDB.ReactUtils.getInstance(card), "return.stateNode.props.addon"); if (addon && !addon.plugin && !addon.instance && addon.css) { let css = addon.css.replace(/\r/g, ""); let imports = this.getThemeImports(css); let vars = this.getThemeVars(css); if (imports.length || vars.length) { let open = _ => { BDFDB.ModalUtils.open(this, { header: `${addon.name} ${BDFDB.LanguageUtils.LanguageStrings.SETTINGS}`, subheader: "", className: BDFDB.disCN._repomodal, headerClassName: BDFDB.disCN._repomodalheader, contentClassName: BDFDB.disCN._repomodalsettings, footerClassName: BDFDB.disCN._repomodalfooter, size: "MEDIUM", children: this.createThemeInputs(addon, imports, vars), buttons: [{contents: BDFDB.LanguageUtils.LanguageStrings.SAVE, color: "BRAND", click: modal => {this.updateTheme(modal, addon);}}] }); }; if (isBeta) { let controls = card.querySelector("." + BDFDB.disCN._repofooter.split(" ")[0] + " " + BDFDB.dotCN._repocontrols); let settingsButton = document.createElement("button"); settingsButton.className = BDFDB.DOMUtils.formatClassName(BDFDB.disCN._repobutton, BDFDB.disCN._repocontrolsbutton, "theme-settings-button"); settingsButton.appendChild(BDFDB.DOMUtils.create(` `)); controls.insertBefore(settingsButton, controls.firstElementChild); settingsButton.addEventListener("click", open); settingsButton.addEventListener("mouseenter", _ => { BDFDB.TooltipUtils.create(settingsButton, BDFDB.LanguageUtils.LanguageStrings.SETTINGS); }); } else { let footer = card.querySelector("." + BDFDB.disCN._repofooter.split(" ").join(",.")); if (!footer) { footer = document.createElement("div"); footer.className = BDFDB.DOMUtils.formatClassName(BDFDB.disCN._repofooter); let links = document.createElement("span"); links.className = BDFDB.DOMUtils.formatClassName(BDFDB.disCN._repolinks); footer.appendChild(links); card.appendChild(footer); } let settingsButton = document.createElement("button"); settingsButton.className = BDFDB.DOMUtils.formatClassName(BDFDB.disCN._reposettingsbutton, "theme-settings-button"); settingsButton.innerText = "Settings"; footer.appendChild(settingsButton); settingsButton.addEventListener("click", open); } } } } updateTheme (card, addon) { let path = BDFDB.LibraryRequires.path.join(dir, addon.filename); let css = BDFDB.LibraryRequires.fs.readFileSync(path).toString(); if (css) { let amount = 0; for (let input of card.querySelectorAll(BDFDB.dotCN.switchinner)) { let oldValue = input.getAttribute("oldvalue"); let newValue = input.checked; if (newValue.toString() != oldValue.toString()) { let importUrl = input.getAttribute("name"); if (newValue) css = css.replace(new RegExp(`\\n${BDFDB.StringUtils.regEscape("/* @import url(" + importUrl + "); */")}`, "g"), `\n@import url(${importUrl});`); else css = css.replace(new RegExp(`\\n${BDFDB.StringUtils.regEscape("@import url(" + importUrl + ");")}`, "g"), `\n/* @import url(${importUrl}); */`); input.setAttribute("oldvalue", newValue); amount++; } } for (let input of card.querySelectorAll(BDFDB.dotCN.input)) { let oldValue = input.getAttribute("oldvalue"); let newValue = input.value; if (newValue && newValue.trim() && newValue != oldValue) { let varName = input.getAttribute("name"); css = css.replace(new RegExp(`--${BDFDB.StringUtils.regEscape(varName)}(\\s*):(\\s*)${BDFDB.StringUtils.regEscape(oldValue)}`,"g"), `--${varName}$1: $2${newValue}`); input.setAttribute("oldvalue", newValue); input.setAttribute("placeholder", newValue); amount++; } } if (amount > 0) { BDFDB.LibraryRequires.fs.writeFileSync(path, css); BDFDB.NotificationUtils.toast(`Updated ${amount} variable${amount == 1 ? "" : "s"} in ${addon.filename}`, {type: "success"}); } else BDFDB.NotificationUtils.toast(`There are no changed variables to be updated in ${addon.filename}`, {type: "warning"}); } else BDFDB.NotificationUtils.toast(`Could not find themefile: ${addon.filename}`, {type: "error"}); } getThemeImports (css) { return css.split("\n@import url(").splice(1).map(n => [n.split(");")[0], true]).concat(css.split("\n/* @import url(").splice(1).map(n => [n.split("); */")[0], false])); } getThemeVars (css) { let vars = css.split(":root"); if (vars.length > 1) { vars = vars[1].replace(/\t\(/g, " (").replace(/\t| {2,}/g, "").replace(/\/\*\n*((?!\/\*|\*\/).|\n)*\n+((?!\/\*|\*\/).|\n)*\n*\*\//g, "").replace(/\n\/\*.*?\*\//g, "").replace(/\n/g, ""); vars = vars.split("{"); vars.shift(); vars = vars.join("{").replace(/\s*(:|;|--|\*)\s*/g, "$1"); vars = vars.split("}")[0]; return vars.slice(2).split(/;--|\*\/--/); } return []; } createThemeInputs (theme, imports, vars) { let settingsItems = []; if (imports.length) settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsPanelList, { title: "Imports:", dividerBottom: vars.length, children: imports.map(impo => { let name = impo[0].split("/").pop().replace(/"/g, ""); return BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsItem, { type: "Switch", margin: 8, label: name[0].toUpperCase() + name.slice(1), note: impo[0].replace(/"/g, ""), name: impo[0], value: impo[1], oldvalue: impo[1].toString() }); }) })); let varInputs = []; for (let varStr of vars) { varStr = varStr.split(":"); let varName = varStr.shift().trim(); varStr = varStr.join(":").split(/;[^A-z0-9]|\/\*/); let varValue = varStr.shift().trim(); if (varValue) { let childType = "text", childMode = ""; let isColor = BDFDB.ColorUtils.getType(varValue); let isComp = !isColor && /^[0-9 ]+,[0-9 ]+,[0-9 ]+$/g.test(varValue); if (isColor || isComp) { childType = "color"; childMode = isComp && "comp"; } else { let isUrlFile = /url\(.+\)/gi.test(varValue); let isFile = !isUrlFile && /(http(s)?):\/\/[(www\.)?a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/.test(varValue); if (isFile || isUrlFile) { childType = "file"; childMode = isUrlFile && "url"; } } let varDescription = varStr.join("").replace(/\*\/|\/\*/g, "").replace(/:/g, ": ").replace(/: \//g, ":/").replace(/--/g, " --").replace(/\( --/g, "(--").trim(); varInputs.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsItem, { type: "TextInput", margin: 8, childProps: { type: childType, mode: childMode, filter: childType == "file" && "image" }, label: varName[0].toUpperCase() + varName.slice(1), note: varDescription && varDescription.indexOf("*") == 0 ? varDescription.slice(1) : varDescription, basis: "70%", name: varName, value: varValue, oldvalue: varValue, placeholder: varValue })); } }; if (varInputs.length) settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsPanelList, { title: "Variables:", children: varInputs })); return BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsPanel, { addon: theme, children: settingsItems }); } }; })(window.BDFDB_Global.PluginUtils.buildPlugin(config)); })();