//META{"name":"ThemeRepo","website":"https://github.com/mwittrien/BetterDiscordAddons/tree/master/Plugins/ThemeRepo","source":"https://raw.githubusercontent.com/mwittrien/BetterDiscordAddons/master/Plugins/ThemeRepo/ThemeRepo.plugin.js"}*// class ThemeRepo { getName () {return "ThemeRepo";} getVersion () {return "1.8.9";} getAuthor () {return "DevilBro";} getDescription () {return "Allows you to preview all themes from the theme repo and download them on the fly. Repo button is in the theme settings.";} constructor () { this.changelog = { "improved":[["ThemeFixer","Fixed and improved"]] }; this.patchModules = { "V2C_List":"componentDidMount" }; } initConstructor () { this.sortings = { sort: { name: "Name", author: "Author", version: "Version", description: "Description", state: "Update State", fav: "Favorites", new: "New Themes" }, order: { asc: "Ascending", desc: "Descending" } }; this.loading = {is:false, timeout:null, amount:0}; this.cachedThemes = []; this.grabbedThemes = []; this.foundThemes = []; this.loadedThemes = {}; this.generatorThemes = []; this.updateInterval; this.themeRepoButtonMarkup = ``; this.themeRepoIconMarkup = ` `; this.frameMarkup = ``; this.themeEntryMarkup = `
  • v by
  • `; this.themeRepoModalMarkup = `

    Theme Repository

    Themes
    Generator
    Settings
    Sort by:
    ${this.sortings.sort[Object.keys(this.sortings.sort)[0]]}
    Order:
    ${this.sortings.order[Object.keys(this.sortings.order)[0]]}

      Generator Theme:

      You can toggle this menu with the "Ctrl" key to take a better look at the preview.

      Preview in light mode

      Preview with normalized classes

      Include Custom CSS in Preview

      Include ThemeFixer CSS in Preview

      Download ThemeFixer

      Hide updated Themes.

      Hide outdated Themes.

      Hide downloadable Themes.

      Apply Theme after Download (Automatic loading enabled)

      `; this.sortPopoutMarkup = `
      ${Object.keys(this.sortings.sort).map((key, i) => `
      ${this.sortings.sort[key]}
      `).join("")}
      `; this.orderPopoutMarkup = `
      ${Object.keys(this.sortings.order).map((key, i) => `
      ${this.sortings.order[key]}
      `).join("")}
      `; this.defaults = { settings: { notifyOutdated: {value:true, description:"Notifies you when one of your Themes is outdated."}, notifyNewentries: {value:true, description:"Notifies you when there are new entries in the Repo."} } }; this.css = ` ${BDFDB.dotCN.app} > .repo-loadingwrapper { position: absolute; bottom: 0; right: 0; z-index: 1000; animation: repo-loadingwrapper-fade 3s infinite ease; } @keyframes repo-loadingwrapper-fade { from {opacity: 0.1;} 50% {opacity: 0.9;} to {opacity: 0.1;} } iframe.discordPreview { width: 100vw !important; height: 100vh !important; position: absolute !important; z-index: 999 !important; } iframe.discordPreview ~ ${BDFDB.dotCN.appmount} { position: absolute !important; top: 0 !important; } iframe.discordPreview ~ ${BDFDB.dotCNS.appmount + BDFDB.dotCN.titlebar}, iframe.discordPreview ~ ${BDFDB.dotCNS.appmount + BDFDB.dotCN.app} > *:not(.toasts):not(.bd-toasts) { opacity: 0 !important; visibility: hidden !important; } .${this.name}-modal.Repo-modal ${BDFDB.dotCN.modalinner} { min-height: 100%; min-width: 800px; width: 50%; } .${this.name}-modal .themeEntry ${BDFDB.dotCN._repocontrols} > * { margin-right: 5px !important; } .${this.name}-modal .themeEntry ${BDFDB.dotCN._repocontrols} > .previewCheckboxWrapper { margin-right: 0px !important; } .${this.name}-modal .themeEntry svg[fill="currentColor"], .${this.name}-modal .themeEntry ${BDFDB.dotCN.giffavoritebutton} { cursor: pointer; } .${this.name}-modal .themeEntry svg[fill="currentColor"], .${this.name}-modal .themeEntry ${BDFDB.dotCN.giffavoritebutton + BDFDB.notCN.giffavoriteselected} { color: #72767d !important; } ${BDFDB.dotCN.themedark} .${this.name}-modal .themeEntry svg[fill="currentColor"], ${BDFDB.dotCN.themedark} .${this.name}-modal .themeEntry ${BDFDB.dotCN.giffavoritebutton + BDFDB.notCN.giffavoriteselected} { color: #dcddde !important; } .${this.name}-modal .themeEntry.downloadable .trashIcon { opacity: 0 !important; pointer-events: none !important; }`; } getSettingsPanel () { if (!global.BDFDB || typeof BDFDB != "object" || !BDFDB.loaded || !this.started) return; var settings = BDFDB.DataUtils.get(this, "settings"); var settingshtml = `
      ${this.name}
      `; for (let key in settings) { settingshtml += `

      ${this.defaults.settings[key].description}

      `; } settingshtml += `

      Add Theme:

      `; settingshtml += `

      Your additional Theme List:

      `; var ownlist = BDFDB.DataUtils.load(this, "ownlist", "ownlist") || []; if (ownlist) for (let url of ownlist) { settingshtml += `
      ${url}
      `; } settingshtml += `
      `; settingshtml += `

      Force all Themes to be fetched again.

      `; settingshtml += `

      Remove all added Themes from your own list.

      `; settingshtml += `
      `; let settingspanel = BDFDB.DOMUtils.create(settingshtml); BDFDB.initElements(settingspanel, this); BDFDB.ListenerUtils.add(this, settingspanel, "click", ".btn-addtheme", () => {this.addThemeToOwnList(settingspanel);}); BDFDB.ListenerUtils.add(this, settingspanel, "click", "#input-themeurl", e => {if (e.which == 13) this.addThemeToOwnList(settingspanel);}); BDFDB.ListenerUtils.add(this, settingspanel, "click", ".remove-theme", e => {this.removeThemeFromOwnList(e);}); BDFDB.ListenerUtils.add(this, settingspanel, "click", ".remove-all", () => {this.removeAllFromOwnList(settingspanel);}); BDFDB.ListenerUtils.add(this, settingspanel, "click", ".refresh-button", () => { this.loading = {is:false, timeout:null, amount:0}; this.loadThemes(); }); return settingspanel; } //legacy load () {} start () { if (!global.BDFDB) global.BDFDB = {myPlugins:{}}; if (global.BDFDB && global.BDFDB.myPlugins && typeof global.BDFDB.myPlugins == "object") global.BDFDB.myPlugins[this.getName()] = this; var libraryScript = document.querySelector('head script#BDFDBLibraryScript'); if (!libraryScript || (performance.now() - libraryScript.getAttribute("date")) > 600000) { if (libraryScript) libraryScript.remove(); libraryScript = document.createElement("script"); libraryScript.setAttribute("id", "BDFDBLibraryScript"); libraryScript.setAttribute("type", "text/javascript"); libraryScript.setAttribute("src", "https://mwittrien.github.io/BetterDiscordAddons/Plugins/BDFDB.min.js"); libraryScript.setAttribute("date", performance.now()); libraryScript.addEventListener("load", () => {this.initialize();}); document.head.appendChild(libraryScript); } else if (global.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) this.initialize(); this.startTimeout = setTimeout(() => { try {return this.initialize();} catch (err) {console.error(`%c[${this.getName()}]%c`, "color: #3a71c1; font-weight: 700;", "", "Fatal Error: Could not initiate plugin! " + err);} }, 30000); } initialize () { if (global.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) { if (this.started) return; BDFDB.PluginUtils.init(this); this.loadThemes(); this.updateInterval = BDFDB.TimeUtils.interval(() => {this.checkForNewThemes();},1000*60*30); BDFDB.ModuleUtils.forceAllUpdates(this); } else console.error(`%c[${this.getName()}]%c`, "color: #3a71c1; font-weight: 700;", "", "Fatal Error: Could not load BD functions!"); } stop () { if (global.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) { this.stopping = true; BDFDB.TimeUtils.clear(this.updateInterval); BDFDB.TimeUtils.clear(this.loading.timeout); BDFDB.DOMUtils.remove("iframe.discordPreview",".themerepo-notice",".bd-themerepobutton",".themerepo-loadingicon",BDFDB.dotCN.app + " > .repo-loadingwrapper:empty"); BDFDB.PluginUtils.clear(this); } } // begin of own functions onUserSettingsCogContextMenu (instance, menu, returnvalue) { BDFDB.TimeUtils.timeout(() => {for (let child of returnvalue.props.children) if (child && child.props && child.props.label == "BandagedBD" && Array.isArray(child.props.render)) { const repoItem = BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ContextMenuItem, { label: "Theme Repo", action: e => { if (!this.loading.is) BDFDB.ContextMenuUtils.close(menu); this.openThemeRepoModal(); } }); child.props.render.push(repoItem); break; }}); } processV2CList (instance, wrapper, returnvalue) { if (!document.querySelector(".bd-themerepobutton") && window.PluginUpdates && window.PluginUpdates.plugins && instance._reactInternalFiber.key && instance._reactInternalFiber.key.split("-")[0] == "theme") { var folderbutton = document.querySelector(BDFDB.dotCN._repofolderbutton); if (folderbutton) { var repoButton = BDFDB.DOMUtils.create(``); repoButton.addEventListener("click", () => { this.openThemeRepoModal(); }); repoButton.addEventListener("mouseenter", () => { BDFDB.TooltipUtils.create(repoButton, "Open Theme Repo", {type:"top",selector:"themerepo-button-tooltip"}); }); folderbutton.parentElement.insertBefore(repoButton, folderbutton.nextSibling); } } }; addThemeToOwnList (settingspanel) { var themeUrlInput = settingspanel.querySelector("#input-themeurl"); var themeList = settingspanel.querySelector(".theme-list"); if (themeUrlInput && themeList) { var url = themeUrlInput.value; themeUrlInput.value = null; var ownlist = BDFDB.DataUtils.load(this, "ownlist", "ownlist") || []; if (!ownlist.includes(url)) { ownlist.push(url); BDFDB.DataUtils.save(ownlist, this, "ownlist", "ownlist"); let entry = BDFDB.DOMUtils.create(`
      ${url}
      `); BDFDB.ListenerUtils.addToChildren(entry, "click", ".remove-theme", e => {this.removeThemeFromOwnList(e);}); themeList.appendChild(entry); } } } removeThemeFromOwnList (e) { var entry = e.currentTarget.parentElement; var url = entry.querySelector(".entryurl").textContent; entry.remove(); var ownlist = BDFDB.DataUtils.load(this, "ownlist", "ownlist") || []; BDFDB.ArrayUtils.remove(ownlist, url); BDFDB.DataUtils.save(ownlist, this, "ownlist", "ownlist"); } removeAllFromOwnList (settingspanel) { BDFDB.ModalUtils.confirm(this, "Are you sure you want to remove all added Themes from your own list?", () => { BDFDB.DataUtils.save([], this, "ownlist", "ownlist"); BDFDB.DOMUtils.remove(settingspanel.querySelector(BDFDB.dotCN.hovercard)); }); } openThemeRepoModal (options = {}) { if (this.loading.is) { BDFDB.NotificationUtils.toast(`Themes are still being fetched. Try again in some seconds.`, {type:"danger"}); return; } var keyPressed = e => { if (e.which == 17) { var toggle = true; for (let ele of themeRepoModal.querySelectorAll(".varinput, " + BDFDB.dotCN.searchbarinput)) if (ele == document.activeElement) { toggle = false; break; } if (toggle) BDFDB.DOMUtils.toggle(themeRepoModal); } else if (e.which == 27) frame.remove(); }; var messageReceived = e => { if (!document.contains(frame)) { document.removeEventListener("keyup", keyPressed); window.removeEventListener("message", messageReceived); } else if (typeof e.data === "object" && e.data.origin == "DiscordPreview") { switch (e.data.reason) { case "OnLoad": var username = BDFDB.UserUtils.me.username; var id = BDFDB.UserUtils.me.id; var discriminator = BDFDB.UserUtils.me.discriminator; var avatar = BDFDB.UserUtils.getAvatar(); var nativecss = document.querySelector("head link[rel='stylesheet'][integrity]"); nativecss = nativecss && nativecss.href ? nativecss.href : null; var titlebar = document.querySelector(BDFDB.dotCN.titlebar); titlebar = titlebar ? titlebar.outerHTML : null; frame.contentWindow.postMessage({origin:"ThemeRepo",reason:"OnLoad",classes:JSON.stringify(BDFDB.DiscordClasses),classmodules:JSON.stringify(BDFDB.DiscordClassModules),username,id,discriminator,avatar,nativecss,html:document.documentElement.className,titlebar},"*"); frame.contentWindow.postMessage({origin:"ThemeRepo",reason:"DarkLight",checked:darklightinput.checked},"*"); frame.contentWindow.postMessage({origin:"ThemeRepo",reason:"Normalize",checked:normalizeinput.checked},"*"); break; case "KeyUp": keyPressed(e.data); break; } } }; var createSelectChoice = index => { let theme = this.loadedThemes[this.generatorThemes[index-1]] || {name: "-----", author: "-----"}; return `
      ${BDFDB.StringUtils.htmlEscape(theme.name)} by ${BDFDB.StringUtils.htmlEscape(theme.author)}
      `; }; var saveSelectChoice = (selectWrap, type, index) => { let theme = this.loadedThemes[this.generatorThemes[index-1]] || {name: "-----", author: "-----"}; selectWrap.querySelector(BDFDB.dotCN.title).innerHTML = `${BDFDB.StringUtils.htmlEscape(theme.name)} by ${BDFDB.StringUtils.htmlEscape(theme.author)}`; themeRepoModal.querySelectorAll(".previewCheckbox").forEach(checkbox => { checkbox.checked = false; BDFDB.DOMUtils.removeClass(checkbox.parentElement, BDFDB.disCN.switchvaluechecked); BDFDB.DOMUtils.addClass(checkbox.parentElement, BDFDB.disCN.switchvalueunchecked); }); let container = themeRepoModal.querySelector(".variables"); BDFDB.DOMUtils.remove(themeRepoModal.querySelectorAll(".varcontainer")); if (container && theme.fullcss) { frame.contentWindow.postMessage({origin:"ThemeRepo",reason:"NewTheme",checked:true,css:theme.fullcss},"*"); this.createGeneratorVars(frame, container, theme); } }; document.addEventListener("keyup", keyPressed); window.addEventListener("message", messageReceived); var frame = BDFDB.DOMUtils.create(this.frameMarkup); var themeRepoModal = BDFDB.DOMUtils.create(this.themeRepoModalMarkup); var tabbar = themeRepoModal.querySelector(BDFDB.dotCN.tabbar); tabbar.parentElement.insertBefore(BDFDB.createSearchBar("small"), tabbar.nextElementSibling); var hiddenSettings = BDFDB.DataUtils.load(this, "hidden"); var darklightinput = themeRepoModal.querySelector("#input-darklight"); var normalizeinput = themeRepoModal.querySelector("#input-normalize"); var customcssinput = themeRepoModal.querySelector("#input-customcss"); var themefixerinput = themeRepoModal.querySelector("#input-themefixer"); darklightinput.checked = BDFDB.DiscordUtils.getTheme() == BDFDB.disCN.themelight; normalizeinput.checked = window.settingsCookie["fork-ps-4"] == true; customcssinput.checked = false; themefixerinput.checked = false; themeRepoModal.querySelector("#input-hideupdated").checked = hiddenSettings.updated || options.showOnlyOutdated; themeRepoModal.querySelector("#input-hideoutdated").checked = hiddenSettings.outdated && !options.showOnlyOutdated; themeRepoModal.querySelector("#input-hidedownloadable").checked = hiddenSettings.downloadable || options.showOnlyOutdated; if (!BDFDB.BDUtils.isAutoLoadEnabled()) themeRepoModal.querySelector("#RNMoption").remove(); else themeRepoModal.querySelector("#input-rnmstart").checked = BDFDB.DataUtils.load(this, "RNMstart", "RNMstart"); if (options.forcedSort && this.sortings.sort[options.forcedSort]) { var sortinput = themeRepoModal.querySelector(".sort-filter " + BDFDB.dotCN.quickselectvalue); sortinput.innerText = this.sortings.sort[options.forcedSort]; sortinput.setAttribute('option', options.forcedSort); } if (options.forcedOrder && this.sortings.order[options.forcedOrder]) { var orderinput = themeRepoModal.querySelector(".order-filter " + BDFDB.dotCN.quickselectvalue); orderinput.innerText = this.sortings.order[options.forcedOrder]; orderinput.setAttribute('option', options.forcedOrder); } darklightinput.addEventListener("change", e => { frame.contentWindow.postMessage({origin:"ThemeRepo",reason:"DarkLight",checked:darklightinput.checked,light:BDFDB.disCN.themelight,dark:BDFDB.disCN.themedark},"*"); }); normalizeinput.addEventListener("change", e => { frame.contentWindow.postMessage({origin:"ThemeRepo",reason:"Normalize",checked:normalizeinput.checked},"*"); }); customcssinput.addEventListener("change", e => { var customCSS = document.querySelector("style#customcss"); if (customCSS && customCSS.innerText.length > 0) frame.contentWindow.postMessage({origin:"ThemeRepo",reason:"CustomCSS",checked:customcssinput.checked,css:customCSS.innerText},"*"); }); themefixerinput.addEventListener("change", e => { BDFDB.LibraryRequires.request("https://mwittrien.github.io/BetterDiscordAddons/Plugins/ThemeRepo/res/ThemeFixer.css", (error, response, body) => { frame.contentWindow.postMessage({origin:"ThemeRepo",reason:"ThemeFixer",checked:themefixerinput.checked,css:this.createFixerCSS(body)},"*"); }); }); themeRepoModal.querySelector("#download-themefixer").addEventListener("click", e => { BDFDB.LibraryRequires.request("https://mwittrien.github.io/BetterDiscordAddons/Plugins/ThemeRepo/res/ThemeFixer.css", (error, response, body) => { this.createThemeFile("ThemeFixer.theme.css", `//META{"name":"ThemeFixer","description":"ThemeFixerCSS for transparent themes","author":"DevilBro","version":"1.0.2"}*//\n\n` + this.createFixerCSS(body)); }); }); themeRepoModal.querySelector("#download-generated").addEventListener("click", e => { let inputs = themeRepoModal.querySelectorAll(".varinput"); if (!inputs.length) BDFDB.NotificationUtils.toast(`Select a Theme to download a custom generated Theme.`, {type:"error"}); else { let data = this.loadedThemes[this.generatorThemes[themeRepoModal.querySelector(".generator-select " + BDFDB.dotCN.select).getAttribute("value")-1]]; let css = data.fullcss; for (let input of inputs) { let oldvalue = input.getAttribute("placeholder"); let newvalue = input.value; if (newvalue && newvalue.trim() && newvalue != oldvalue) { let varname = input.getAttribute("option"); css = css.replace(new RegExp(`--${BDFDB.StringUtils.regEscape(varname)}(\\s*):(\\s*)${BDFDB.StringUtils.regEscape(oldvalue)}`,"g"),`--${varname}$1:$2${newvalue}`); } } this.createThemeFile(data.name + ".theme.css", css); } }); BDFDB.ListenerUtils.addToChildren(themeRepoModal, "click", BDFDB.dotCNC.modalclose + BDFDB.dotCN.backdrop, () => { frame.remove(); document.removeEventListener("keyup", keyPressed); window.removeEventListener("message", messageReceived); }); BDFDB.ListenerUtils.addToChildren(themeRepoModal, "mouseenter", BDFDB.dotCN.backdrop, e => { if (!document.querySelector(BDFDB.dotCN.colorpicker)) { for (let child of themeRepoModal.childNodes) { child.style.setProperty("transition", "opacity .5s ease-in-out", "important"); child.style.setProperty("opacity", "0", "important"); } } }); BDFDB.ListenerUtils.addToChildren(themeRepoModal, "mouseleave", BDFDB.dotCN.backdrop, e => { if (!document.querySelector(BDFDB.dotCN.colorpicker)) { themeRepoModal.childNodes[0].style.setProperty("opacity", "0.85"); themeRepoModal.childNodes[1].style.setProperty("opacity", "1"); BDFDB.TimeUtils.timeout(() => {for (let child of themeRepoModal.childNodes) child.style.removeProperty("transition");}, 500); } }); BDFDB.ListenerUtils.addToChildren(themeRepoModal, "keyup", BDFDB.dotCN.searchbarinput, () => { BDFDB.TimeUtils.clear(themeRepoModal.searchTimeout); themeRepoModal.searchTimeout = BDFDB.TimeUtils.timeout(() => {this.sortEntries(themeRepoModal);},1000); }); BDFDB.ListenerUtils.addToChildren(themeRepoModal, "click", BDFDB.dotCN.searchbarclear, () => { BDFDB.TimeUtils.clear(themeRepoModal.searchTimeout); themeRepoModal.searchTimeout = BDFDB.TimeUtils.timeout(() => {this.sortEntries(themeRepoModal);},1000); }); BDFDB.ListenerUtils.addToChildren(themeRepoModal, "change", ".hide-checkbox", e => { themeRepoModal.updateHidden = true; BDFDB.DataUtils.save(e.currentTarget.checked, this, "hidden", e.currentTarget.value); }); BDFDB.ListenerUtils.addToChildren(themeRepoModal, "change", "#input-rnmstart", e => { BDFDB.DataUtils.save(e.currentTarget.checked, this, "RNMstart", "RNMstart"); }); BDFDB.ListenerUtils.addToChildren(themeRepoModal, "click", ".sort-filter", e => { BDFDB.createSortPopout(e.currentTarget, this.sortPopoutMarkup, () => {this.sortEntries(themeRepoModal);}); }); BDFDB.ListenerUtils.addToChildren(themeRepoModal, "click", ".order-filter", e => { BDFDB.createSortPopout(e.currentTarget, this.orderPopoutMarkup, () => {this.sortEntries(themeRepoModal);}); }); BDFDB.ListenerUtils.addToChildren(themeRepoModal, "click", BDFDB.dotCN.tabbaritem + "[tab=themes]", e => { if (!BDFDB.DOMUtils.containsClass(e.currentTarget, BDFDB.disCN.settingsitemselected)) { if (themeRepoModal.updateHidden) { delete themeRepoModal.updateHidden; this.sortEntries(themeRepoModal); } } }); let favorites = BDFDB.DataUtils.load(this, "favorites"); let container = themeRepoModal.querySelector(".themes"); themeRepoModal.entries = {}; for (let url in this.loadedThemes) { let theme = this.loadedThemes[url]; let instTheme = BDFDB.BDUtils.getTheme(theme.name); if (instTheme && instTheme.author.toUpperCase() == theme.author.toUpperCase()) theme.state = instTheme.version != theme.version ? 1 : 0; else theme.state = 2; let data = { url: theme.url, requesturl: theme.requesturl, search: (theme.name + " " + theme.version + " " + theme.author + " " + theme.description).toUpperCase(), name: theme.name, version: theme.version, author: theme.author, description: theme.description, fav: favorites[url] ? 0 : 1, new: !this.cachedThemes.includes(url) ? 0 : 1, state: theme.state, css: theme.css }; themeRepoModal.entries[url] = data; this.addEntry(frame, themeRepoModal, container, data); } this.sortEntries(themeRepoModal); var genselect = themeRepoModal.querySelector(".generator-select"); genselect.innerHTML = BDFDB.createSelectMenu(createSelectChoice("-----"), "-----"); genselect.addEventListener("click", e => { BDFDB.openDropdownMenu(e, saveSelectChoice.bind(this), createSelectChoice.bind(this), ["-----"].concat(this.generatorThemes)); }); BDFDB.appendModal(themeRepoModal); document.body.insertBefore(frame, document.body.firstElementChild); themeRepoModal.querySelector(BDFDB.dotCN.searchbarinput).focus(); } createFixerCSS (body) { var oldcss = body.replace(/\n/g, "\\n").replace(/\t/g, "\\t").replace(/\r/g, "\\r").split("REPLACE_CLASS_"); var newcss = oldcss.shift(); for (let str of oldcss) { let reg = /([A-z0-9_]+)(.*)/.exec(str); newcss += BDFDB.dotCN[reg[1]] + reg[2]; } return newcss.replace(/\\n/g, "\n").replace(/\\t/g, "\t").replace(/\\r/g, "\r"); } createGeneratorVars (frame, container, data) { let vars = data.fullcss.split(":root"); if (vars.length < 2) return; vars = vars[1].replace(/\t\(/g, " (").replace(/\r|\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]; vars = vars.slice(2).split(/;--|\*\/--/); var maxwidth = BDFDB.DOMUtils.getRects(container).width - 80; 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 vardescription = varstr.join("").replace(/\*\/|\/\*/g, "").replace(/:/g, ": ").replace(/: \//g, ":/").replace(/--/g, " --").replace(/\( --/g, "(--").trim(); vardescription = vardescription && vardescription.indexOf("*") == 0 ? vardescription.slice(1) : vardescription; let varcontainer = BDFDB.DOMUtils.create(`

      ${varname[0].toUpperCase() + varname.slice(1)}

      ${vardescription ? `
      ${BDFDB.StringUtils.htmlEscape(vardescription)}
      ` : ""}${vars[vars.length-1] == varstr ? '' : `
      `}
      `); let varinput = varcontainer.querySelector(BDFDB.dotCN.input); varinput.value = varvalue; varinput.setAttribute("placeholder", varvalue); let swatch = varcontainer.querySelector(".single-swatch"); let navigator = varcontainer.querySelector(".file-navigator"); let iscolor = BDFDB.ColorUtils.getType(varvalue); let iscomp = !iscolor && /[0-9 ]+,[0-9 ]+,[0-9 ]+/g.test(varvalue); let isurlfile = !iscolor && !iscolor && /url\(.+\)/gi.test(varvalue); let isfile = !iscolor && !iscolor && !isurlfile && /(http(s)?):\/\/[(www\.)?a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/.test(varvalue); if (iscolor || iscomp) { let color = iscomp ? BDFDB.ColorUtils.convert(varvalue.split(","), "RGB") : varvalue; swatch.style.setProperty("background-color", color, "important"); swatch.addEventListener("click", e => { BDFDB.ColorUtils.openPicker(varcontainer, e.currentTarget, color, {gradient:false, comp:iscomp, alpha:iscolor, callback:updatePreview}); }); } else BDFDB.DOMUtils.remove(swatch); if (isurlfile || isfile) { navigator.addEventListener("change", e => { let file = navigator.querySelector("input").files[0]; if (file && file.type.indexOf("image") == 0) varinput.value = `${isurlfile ? "url('" : ""}data:${file.type};base64,${BDFDB.LibraryRequires.fs.readFileSync(file.path).toString("base64")}${isurlfile ? "')" : ""}`; else varinput.value = varvalue; updatePreview(); }); } else BDFDB.DOMUtils.remove(navigator); container.appendChild(varcontainer); } } let inputs = container.querySelectorAll(".varinput"), updateTimeout, updatePreview = () => { BDFDB.TimeUtils.clear(updateTimeout); updateTimeout = BDFDB.TimeUtils.timeout(() => { let css = data.fullcss; for (let input of inputs) { let oldvalue = input.getAttribute("placeholder"); let newvalue = input.value; if (newvalue && newvalue.trim() && newvalue != oldvalue) { let varname = input.getAttribute("option"); css = css.replace(new RegExp(`--${BDFDB.StringUtils.regEscape(varname)}(\\s*):(\\s*)${BDFDB.StringUtils.regEscape(oldvalue)}`,"g"),`--${varname}$1:$2${newvalue}`); } } frame.contentWindow.postMessage({origin:"ThemeRepo",reason:"NewTheme",checked:true,css},"*"); }, 1000); }; BDFDB.ListenerUtils.addToChildren(container, "change input", ".varinput", updatePreview); BDFDB.initElements(container); } addEntry (frame, themeRepoModal, container, data) { if (!frame || !themeRepoModal || !container || !data) return; let entry = BDFDB.DOMUtils.create(this.themeEntryMarkup); setEntryState(data.state); entry.setAttribute("data-name", data.name); entry.setAttribute("data-version", data.version); entry.setAttribute("data-url", data.url); entry.querySelector(BDFDB.dotCN._reponame).innerHTML = data.name; entry.querySelector(BDFDB.dotCN._repoversion).innerHTML = data.version; entry.querySelector(BDFDB.dotCN._repoauthor).innerHTML = data.author; entry.querySelector(BDFDB.dotCN._repodescription).innerHTML = data.description; if (data.new == 0) entry.querySelector(BDFDB.dotCN._repoheadertitle).appendChild(BDFDB.DOMUtils.create(`NEW`)); let favbutton = entry.querySelector(BDFDB.dotCN.giffavoritebutton); BDFDB.DOMUtils.toggleClass(favbutton, BDFDB.disCN.giffavoriteselected, data.fav == 0); favbutton.addEventListener("click", e => { let favorize = data.fav == 1; data.fav = favorize ? 0 : 1; if (favorize) BDFDB.DataUtils.save(true, this, "favorites", data.url); else BDFDB.DataUtils.remove(this, "favorites", data.url); themeRepoModal.entries[data.url] = data; }); let gitbutton = entry.querySelector(".gitIcon"); gitbutton.addEventListener("click", e => { var giturl = null; if (data.requesturl.indexOf("https://raw.githubusercontent.com") == 0) { var temp = data.requesturl.replace("//raw.githubusercontent", "//github").split("/"); temp.splice(5, 0, "blob"); giturl = temp.join("/"); } else if (data.requesturl.indexOf("https://gist.githubusercontent.com/") == 0) { giturl = data.requesturl.replace("//gist.githubusercontent", "//gist.github").split("/raw/")[0]; } if (giturl) window.open(giturl, "_blank"); }); gitbutton.addEventListener("mouseenter", e => { BDFDB.TooltipUtils.create(gitbutton, "Go to Git", {type:"top",selector:"themerepo-giticon-tooltip"}); }); let trashbutton = entry.querySelector(".trashIcon"); trashbutton.addEventListener("click", e => { if (BDFDB.DOMUtils.containsClass(entry, "outdated", "updated", false)) { setEntryState(2); this.deleteThemeFile(data); if (!BDFDB.BDUtils.isAutoLoadEnabled()) this.removeTheme(data); } }); trashbutton.addEventListener("mouseenter", e => { BDFDB.TooltipUtils.create(trashbutton, "Delete Themefile", {type:"top",selector:"themerepo-trashicon-tooltip"}); }); entry.querySelector(".btn-download").addEventListener("click", e => { setEntryState(0); this.downloadTheme(data); if (themeRepoModal.querySelector("#input-rnmstart").checked) BDFDB.TimeUtils.timeout(() => {this.applyTheme(data);},3000); }); entry.querySelector(".previewCheckbox").addEventListener("click", e => { themeRepoModal.querySelector(".generator-select " + BDFDB.dotCN.select).setAttribute('value', "-----"); themeRepoModal.querySelector(".generator-select " + BDFDB.dotCN.title).innerHTML = `----- by -----`; BDFDB.DOMUtils.remove(themeRepoModal.querySelectorAll(".varcontainer")); if (e.currentTarget.checked) themeRepoModal.querySelectorAll(".previewCheckbox").forEach(checkbox => { if (e.currentTarget != checkbox) { checkbox.checked = false; BDFDB.DOMUtils.removeClass(checkbox.parentElement, BDFDB.disCN.switchvaluechecked); BDFDB.DOMUtils.addClass(checkbox.parentElement, BDFDB.disCN.switchvalueunchecked); } }); frame.contentWindow.postMessage({origin:"ThemeRepo",reason:"NewTheme",checked:e.currentTarget.checked,css:data.css},"*"); }); container.appendChild(entry); function setEntryState (state) { data.state = state; BDFDB.DOMUtils.toggleClass(entry, "downloadable", state > 1); BDFDB.DOMUtils.toggleClass(entry, "outdated", state == 1); BDFDB.DOMUtils.toggleClass(entry, "updated", state < 1); let downloadbutton = entry.querySelector(".btn-download"); downloadbutton.innerText = state < 1 ? "Updated" : (state > 1 ? "Download" : "Outdated"); downloadbutton.style.setProperty("background-color", "rgb(" + (state < 1 ? "67,181,129" : (state > 1 ? "114,137,218" : "241,71,71")) + ")", state > 1 ? "" : "important"); themeRepoModal.entries[data.url] = data; }; } sortEntries (themeRepoModal) { if (!themeRepoModal || typeof themeRepoModal.entries != "object") return; let container = themeRepoModal.querySelector(".themes"); if (!container) return; let searchstring = themeRepoModal.querySelector(BDFDB.dotCN.searchbarinput).value.replace(/[<|>]/g, "").toUpperCase(); let entries = themeRepoModal.entries; if (themeRepoModal.querySelector("#input-hideupdated").checked) entries = BDFDB.ObjectUtils.filter(entries, entry => {return entry.state < 1 ? null : entry;}); if (themeRepoModal.querySelector("#input-hideoutdated").checked) entries = BDFDB.ObjectUtils.filter(entries, entry => {return entry.state == 1 ? null : entry;}); if (themeRepoModal.querySelector("#input-hidedownloadable").checked) entries = BDFDB.ObjectUtils.filter(entries, entry => {return entry.state > 1 ? null : entry;}); entries = BDFDB.ObjectUtils.filter(entries, entry => {return entry.search.indexOf(searchstring) > -1 ? entry : null;}); let sortfilter = themeRepoModal.querySelector(".sort-filter " + BDFDB.dotCN.quickselectvalue).getAttribute("option"); entries = BDFDB.ObjectUtils.sort(entries, sortfilter == "new" && !themeRepoModal.querySelector(".newentries-tag") ? "name" : sortfilter); if (themeRepoModal.querySelector(".order-filter " + BDFDB.dotCN.quickselectvalue).getAttribute("option") == "desc") entries = BDFDB.ObjectUtils.reverse(entries); let entrypositions = Object.keys(entries); themeRepoModal.querySelector(".themeAmount").innerText = "ThemeRepo Repository " + entrypositions.length + "/" + Object.keys(this.loadedThemes).length + " Themes"; for (let li of container.children) { let pos = entrypositions.indexOf(li.getAttribute("data-url")); if (pos > -1) { li.querySelectorAll(BDFDB.dotCNC._reponame + BDFDB.dotCNC._repoversion + BDFDB.dotCNC._repoauthor + BDFDB.dotCN._repodescription).forEach(ele => { if (searchstring && searchstring.length > 2 || ele.querySelector(BDFDB.dotCN.highlight)) ele.innerHTML = BDFDB.highlightText(ele.innerText, searchstring); }); li.style.setProperty("order", pos, "important"); } else li.style.removeProperty("order"); BDFDB.DOMUtils.toggle(li, pos > -1); } } loadThemes () { BDFDB.DOMUtils.remove(".themerepo-loadingicon"); let settings = BDFDB.DataUtils.load(this, "settings"); var getThemeInfo, outdated = 0, newentries = 0, i = 0, NFLDreplace = null; var tags = ["name","description","author","version"]; var newentriesdata = BDFDB.DataUtils.load(this, "newentriesdata"), ownlist = BDFDB.DataUtils.load(this, "ownlist", "ownlist") || []; this.cachedThemes = (newentriesdata.urlbase64 ? atob(newentriesdata.urlbase64).split("\n") : []).concat(ownlist); BDFDB.LibraryRequires.request("https://mwittrien.github.io/BetterDiscordAddons/Plugins/ThemeRepo/res/ThemeList.txt", (error, response, body) => { if (!error && body) { body = body.replace(/[\r\t]/g, ""); BDFDB.DataUtils.save(btoa(body), this, "newentriesdata", "urlbase64"); this.loadedThemes = {}; this.grabbedThemes = body.split("\n").filter(n => n); BDFDB.LibraryRequires.request("https://github.com/NFLD99/Better-Discord", (error2, response2, body2) => { if (!error2 && body2) { NFLDreplace = /\/NFLD99\/Better-Discord\/tree\/master\/Themes_[^"]+">([^<]+)/i.exec(body2); NFLDreplace = NFLDreplace && NFLDreplace.length > 1 ? NFLDreplace[1] : null; } this.foundThemes = this.grabbedThemes.concat(ownlist); this.loading = {is:true, timeout:BDFDB.TimeUtils.timeout(() => { BDFDB.TimeUtils.clear(this.loading.timeout); if (this.started) { if (this.loading.is && this.loading.amount < 4) BDFDB.TimeUtils.timeout(() => {this.loadThemes();},10000); this.loading = {is: false, timeout:null, amount:this.loading.amount}; } },1200000), amount:this.loading.amount+1}; var loadingiconwrapper = document.querySelector(BDFDB.dotCN.app + "> .repo-loadingwrapper"); if (!loadingiconwrapper) { loadingiconwrapper = BDFDB.DOMUtils.create(`
      `); document.querySelector(BDFDB.dotCN.app).appendChild(loadingiconwrapper); } var loadingicon = BDFDB.DOMUtils.create(this.themeRepoIconMarkup); BDFDB.DOMUtils.addClass(loadingicon, "themerepo-loadingicon"); loadingicon.addEventListener("mouseenter", () => { BDFDB.TooltipUtils.create(loadingicon, this.getLoadingTooltipText(), {type:"left", delay:500, style:"max-width:unset;", selector:"themerepo-loading-tooltip"}); }); loadingiconwrapper.appendChild(loadingicon); getThemeInfo(() => { if (!this.started) { BDFDB.TimeUtils.clear(this.loading.timeout); return; } BDFDB.DOMUtils.remove(loadingicon, ".themerepo-loadingicon"); if (!loadingiconwrapper.firstChild) BDFDB.DOMUtils.remove(loadingiconwrapper); BDFDB.TimeUtils.clear(this.loading.timeout); this.loading = {is:false, timeout:null, amount:this.loading.amount}; console.log(`%c[${this.name}]%c`, "color: #3a71c1; font-weight: 700;", "", "Finished fetching Themes."); if (document.querySelector(".bd-themerepobutton")) BDFDB.NotificationUtils.toast(`Finished fetching Themes.`, {type:"success"}); if ((settings.notifyOutdated || settings.notifyOutdated == undefined) && outdated > 0) { var oldbarbutton = document.querySelector(".themerepo-outdate-notice " + BDFDB.dotCN.noticedismiss); if (oldbarbutton) oldbarbutton.click(); var bar = BDFDB.NotificationUtils.notice(`${outdated} of your Themes ${outdated == 1 ? "is" : "are"} outdated. Check:`,{type:"danger",btn:"ThemeRepo",selector:"themerepo-notice themerepo-outdate-notice", customicon:this.themeRepoIconMarkup.replace(/#7289da/gi,"#FFF").replace(/#7f8186/gi,"#B9BBBE")}); bar.querySelector(BDFDB.dotCN.noticebutton).addEventListener("click", e => { this.openThemeRepoModal({showOnlyOutdated:true}); bar.querySelector(BDFDB.dotCN.noticedismiss).click(); }); } if ((settings.notifyNewentries || settings.notifyNewentries == undefined) && newentries > 0) { var oldbarbutton = document.querySelector(".themerepo-newentries-notice " + BDFDB.dotCN.noticedismiss); if (oldbarbutton) oldbarbutton.click(); var bar = BDFDB.NotificationUtils.notice(`There ${newentries == 1 ? "is" : "are"} ${newentries} new Theme${newentries == 1 ? "" : "s"} in the Repo. Check:`, {type:"success", btn:"ThemeRepo", selector:"themerepo-notice themerepo-newentries-notice", customicon:this.themeRepoIconMarkup.replace(/#7289da/gi,"#FFF").replace(/#7f8186/gi,"#B9BBBE")}); bar.querySelector(BDFDB.dotCN.noticebutton).addEventListener("click", e => { this.openThemeRepoModal({forcedSort:"new",forcedOrder:"asc"}); bar.querySelector(BDFDB.dotCN.noticedismiss).click(); }); } if (BDFDB.UserUtils.me.id == "278543574059057154") { let wrongUrls = []; for (let url of this.foundThemes) if (url && !this.loadedThemes[url] && !wrongUrls.includes(url)) wrongUrls.push(url); if (wrongUrls.length > 0) { var bar = BDFDB.NotificationUtils.notice(`ThemeRepo: ${wrongUrls.length} Theme${wrongUrls.length > 1 ? "s" : ""} could not be loaded.`, {type:"danger", btn:"List", selector:"themerepo-notice", customicon:this.themeRepoIconMarkup.replace(/#7289da/gi,"#FFF").replace(/#7f8186/gi,"#B9BBBE")}); bar.querySelector(BDFDB.dotCN.noticebutton).addEventListener("click", e => { var toast = BDFDB.NotificationUtils.toast(wrongUrls.join("\n"),{type:"error"}); toast.style.overflow = "hidden"; console.log(wrongUrls.length == 1 ? wrongUrls[0] : wrongUrls); }); } } BDFDB.LibraryRequires.request("https://mwittrien.github.io/BetterDiscordAddons/Plugins/ThemeRepo/res/GeneratorList.txt", (error3, response3, body3) => { if (!error3 && body3) for (let url of body3.replace(/[\r\t]/g, "").split("\n").filter(n => n)) if (this.loadedThemes[url]) this.generatorThemes.push(url); }); }); }); } }); getThemeInfo = (callback) => { if (i >= this.foundThemes.length || !this.started || !this.loading.is) { callback(); return; } let url = this.foundThemes[i]; let requesturl = NFLDreplace && url.includes("NFLD99/Better-Discord/master/Themes") ? url.replace("master/Themes", "master/" + NFLDreplace) : url; BDFDB.LibraryRequires.request(requesturl, (error, response, body) => { if (!response) { if (url && BDFDB.ArrayUtils.getAllIndexes(this.foundThemes, url).length < 2) this.foundThemes.push(url); } else if (body && body.indexOf("404: Not Found") != 0 && response.statusCode == 200) { let theme = {}; let text = body; if ((text.split("*//").length > 1 || text.indexOf("/**") == 0) && text.split("\n").length > 1) { var hasMETAline = text.replace(/\s/g, "").indexOf("//META{"); if (hasMETAline < 20 && hasMETAline > -1) { var searchtext = text.replace(/\s*:\s*/g, ":").replace(/\s*}\s*/g, "}"); for (let tag of tags) { let result = searchtext.split('"' + tag + '":"'); result = result.length > 1 ? result[1].split('",')[0].split('"}')[0] : null; result = result && tag != "version" ? result.charAt(0).toUpperCase() + result.slice(1) : result; theme[tag] = result ? result.trim() : result; } } else { var searchtext = text.replace(/[\r\t| ]*\*\s*/g, "*"); for (let tag of tags) { let result = searchtext.split('@' + tag + ' '); result = result.length > 1 ? result[1].split('\n')[0] : null; result = result && tag != "version" ? result.charAt(0).toUpperCase() + result.slice(1) : result; theme[tag] = result ? result.trim() : result; } } let valid = true; for (let tag of tags) if (theme[tag] === null) valid = false; if (valid) { theme.fullcss = text; theme.css = hasMETAline < 20 && hasMETAline > -1 ? text.split("\n").slice(1).join("\n").replace(/[\r|\n|\t]/g, "") : text.replace(/[\r|\n|\t]/g, ""); theme.url = url; theme.requesturl = requesturl; this.loadedThemes[url] = theme; var instTheme = BDFDB.BDUtils.getTheme(theme.name); if (instTheme && instTheme.author.toUpperCase() == theme.author.toUpperCase() && instTheme.version != theme.version) outdated++; if (!this.cachedThemes.includes(url)) newentries++; } } } i++; var loadingtooltip = document.querySelector(".themerepo-loading-tooltip"); if (loadingtooltip) { BDFDB.DOMUtils.setText(loadingtooltip, this.getLoadingTooltipText()); BDFDB.TooltipUtils.update(loadingtooltip); } getThemeInfo(callback); }); } } getLoadingTooltipText () { return `Loading ThemeRepo - [${Object.keys(this.loadedThemes).length}/${Object.keys(this.grabbedThemes).length}]`; } checkForNewThemes () { BDFDB.LibraryRequires.request("https://mwittrien.github.io/BetterDiscordAddons/Plugins/ThemeRepo/res/ThemeList.txt", (error, response, result) => { if (response && !BDFDB.equals(result.replace(/\t|\r/g, "").split("\n").filter(n => n), this.grabbedThemes)) { this.loading = {is:false, timeout:null, amount:0}; this.loadThemes(); } }); } downloadTheme (data) { BDFDB.LibraryRequires.request(data.requesturl, (error, response, body) => { if (error) BDFDB.NotificationUtils.toast(`Unable to download Theme "${data.name}".`, {type:"danger"}); else this.createThemeFile(data.requesturl.split("/").pop(), body); }); } createThemeFile (filename, content) { BDFDB.LibraryRequires.fs.writeFile(BDFDB.LibraryRequires.path.join(BDFDB.BDUtils.getThemesFolder(), filename), content, (error) => { if (error) BDFDB.NotificationUtils.toast(`Unable to save Theme "${filename}".`, {type:"danger"}); else BDFDB.NotificationUtils.toast(`Successfully saved Theme "${filename}".`, {type:"success"}); }); } applyTheme (data) { if (BDFDB.BDUtils.isThemeEnabled(data.name) == false) { BDFDB.DOMUtils.remove(`style#${data.name}`); document.head.appendChild(BDFDB.DOMUtils.create(``)); window.themeModule.enableTheme(data.name); console.log(`%c[${this.name}]%c`, "color: #3a71c1; font-weight: 700;", "", "Applied Theme " + data.name + "."); } } deleteThemeFile (data) { let filename = data.requesturl.split("/").pop(); BDFDB.LibraryRequires.fs.unlink(BDFDB.LibraryRequires.path.join(BDFDB.BDUtils.getThemesFolder(), filename), (error) => { if (error) BDFDB.NotificationUtils.toast(`Unable to delete Theme "${filename}".`, {type:"danger"}); else BDFDB.NotificationUtils.toast(`Successfully deleted Theme "${filename}".`); }); } removeTheme (data) { if (BDFDB.BDUtils.isThemeEnabled(data.name) == true) { BDFDB.DOMUtils.remove(`style#${data.name}`); window.themeModule.disableTheme(data.name); console.log(`%c[${this.name}]%c`, "color: #3a71c1; font-weight: 700;", "", "Removed Theme " + data.name + "."); } } }