diff --git a/LICENSE b/LICENSE index 58c3eea6..c19c6dc4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2015-present Jiiks | Jiiks.net +Copyright (c) 2015-present Jiiks | 2017-present Zack Rauen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/js/main.js b/js/main.js index 119efc68..83cf5d8f 100644 --- a/js/main.js +++ b/js/main.js @@ -66,6 +66,7 @@ document.body.appendChild(v2Loader); })(); +/* global DiscordNative:false */ window.bdStorage = {}; window.bdStorage.get = function(i) { @@ -84,43 +85,47 @@ window.bdPluginStorage.set = function(pn, i, v) { var bdSettings = {}; var bdSettingsStorage = {}; +var releaseChannel = DiscordNative.globals.releaseChannel; bdSettingsStorage.initialize = function() { let fs = require("fs"); - let data = {}; + let data = {stable: {}, canary: {}, ptb: {}}; if (fs.existsSync(bdConfig.dataPath + "/bdsettings.json")) { try { data = JSON.parse(fs.readFileSync(bdConfig.dataPath + "/bdsettings.json")); + + // Convery to new style. To be removed a month from 10/14/2018 + if (data.hasOwnProperty("settings")) data = {[releaseChannel]: data}; } catch (err) { - data = {}; + data = {stable: {}, canary: {}, ptb: {}}; } } if (data) bdSettings = data; - else bdSettings = {}; + else bdSettings = {stable: {}, canary: {}, ptb: {}}; }; bdSettingsStorage.get = function(key) { - if (bdSettings[key]) return bdSettings[key]; + if (bdSettings[releaseChannel][key]) return bdSettings[releaseChannel][key]; return null; }; bdSettingsStorage.set = function(key, data) { let fs = require("fs"); - bdSettings[key] = data; + bdSettings[releaseChannel][key] = data; try { fs.writeFileSync(bdConfig.dataPath + "/bdsettings.json", JSON.stringify(bdSettings, null, 4)); return true; } catch (err) { - utils.err(err); + Utils.err(err); return false; } }; -var settingsPanel, emoteModule, utils, quickEmoteMenu, voiceMode, pluginModule, themeModule, dMode, publicServersModule; +var settingsPanel, emoteModule, quickEmoteMenu, voiceMode, pluginModule, themeModule, dMode, publicServersModule; var jsVersion = 1.792; var supportedVersion = "0.2.81"; -var bbdVersion = "0.1.0"; +var bbdVersion = "0.1.1"; var mainCore; @@ -221,16 +226,19 @@ Core.prototype.init = async function() { return; } - utils = new Utils(); - await utils.getHash(); - utils.log("Initializing Settings"); + Utils.log("Initializing Settings"); this.initSettings(); classNormalizer = new ClassNormalizer(); emoteModule = new EmoteModule(); - utils.log("Initializing EmoteModule"); - window.emotePromise = emoteModule.init().then(() => {emoteModule.initialized = true;}); - publicServersModule = new V2_PublicServers(); quickEmoteMenu = new QuickEmoteMenu(); + Utils.log("Initializing EmoteModule"); + window.emotePromise = emoteModule.init().then(() => { + emoteModule.initialized = true; + Utils.log("Initializing QuickEmoteMenu"); + quickEmoteMenu.init(); + }); + publicServersModule = new V2_PublicServers(); + voiceMode = new VoiceMode(); dMode = new devMode(); @@ -243,7 +251,7 @@ Core.prototype.init = async function() { self.injectExternals(); - utils.log("Updating Settings"); + Utils.log("Updating Settings"); settingsPanel = new V2_SettingsPanel(); settingsPanel.updateSettings(); @@ -251,18 +259,15 @@ Core.prototype.init = async function() { if (!bdpluginErrors) bdpluginErrors = []; if (!bdthemeErrors) bdthemeErrors = []; - utils.log("Loading Plugins"); + Utils.log("Loading Plugins"); pluginModule = new PluginModule(); pluginModule.loadPlugins(); - utils.log("Loading Themes"); + Utils.log("Loading Themes"); themeModule = new ThemeModule(); themeModule.loadThemes(); $("#customcss").detach().appendTo(document.head); - - utils.log("Initializing QuickEmoteMenu"); - quickEmoteMenu.init(); window.addEventListener("beforeunload", function(){ if (settingsCookie["bda-dc-0"]) document.querySelector(".btn.btn-disconnect").click(); @@ -279,14 +284,14 @@ Core.prototype.init = async function() { self.saveSettings(); } - utils.log("Removing Loading Icon"); + Utils.log("Removing Loading Icon"); document.getElementsByClassName("bd-loaderv2")[0].remove(); - utils.log("Initializing Main Observer"); + Utils.log("Initializing Main Observer"); self.initObserver(); // Show loading errors if (settingsCookie["fork-ps-1"]) { - utils.log("Collecting Startup Errors"); + Utils.log("Collecting Startup Errors"); self.showStartupErrors(); } } @@ -302,20 +307,11 @@ Core.prototype.init = async function() { }; Core.prototype.injectExternals = function() { - utils.injectJs("https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.9/ace.js"); + Utils.injectJs("https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.9/ace.js"); }; Core.prototype.initSettings = function () { - // $.removeCookie('the_cookie', { path: '/' }); bdSettingsStorage.initialize(); - - if ($.cookie("better-discord")) { - settingsCookie = JSON.parse($.cookie("better-discord")); - this.saveSettings(); - $.removeCookie("better-discord", {path: "/"}); - return; - } - if (!bdSettingsStorage.get("settings")) { settingsCookie = defaultCookie; this.saveSettings(); @@ -377,9 +373,14 @@ Core.prototype.initObserver = function () { Core.prototype.inject24Hour = function() { if (this.cancel24Hour) return; - this.cancel24Hour = Utils.monkeyPatch(BDV2.TimeFormatter, "calendarFormat", {before: ({methodArguments}) => { + const twelveHour = new RegExp(`([0-9]{1,2}):([0-9]{1,2})\\s(AM|PM)`); + + this.cancel24Hour = Utils.monkeyPatch(BDV2.TimeFormatter, "calendarFormat", {after: (data) => { if (!settingsCookie["bda-gs-6"]) return; - methodArguments[0]._locale._abbr = "en-GB"; + const matched = data.returnValue.match(twelveHour); + if (!matched || matched.length !== 4) return; + if (matched[3] === "AM") return data.returnValue = data.returnValue.replace(matched[0], `${matched[1] === "12" ? "00" : matched[1].padStart(2, "0")}:${matched[2]}`); + return data.returnValue = data.returnValue.replace(matched[0], `${parseInt(matched[1]) + 12}:${matched[2]}`); }}); }; @@ -480,7 +481,7 @@ Core.prototype.showStartupErrors = function() { if (err.error) { error.find("a").on("click", (e) => { e.preventDefault(); - utils.err(`Error details for ${err.name ? err.name : err.file}.`, err.error); + Utils.err(`Error details for ${err.name ? err.name : err.file}.`, err.error); }); } } @@ -592,7 +593,7 @@ EmoteModule.prototype.init = async function () { let emoteInfo = { TwitchGlobal: { url: "https://twitchemotes.com/api_cache/v3/global.json", - backup: "https://" + bdConfig.updater.CDN + "/" + bdConfig.repo + "/BetterDiscordApp/" + bdConfig.hash + "/data/emotedata_twitch_global.json", + backup: "https://cdn.staticaly.com/gh/rauenzi/BetterDiscordApp/master/data/emotedata_twitch_global.json", variable: "TwitchGlobal", oldVariable: "emotesTwitch", getEmoteURL: (e) => `https://static-cdn.jtvnw.net/emoticons/v1/${e.id}/1.0`, @@ -600,7 +601,7 @@ EmoteModule.prototype.init = async function () { }, TwitchSubscriber: { url: "https://twitchemotes.com/api_cache/v3/subscriber.json", - backup: "https://" + bdConfig.updater.CDN + "/" + bdConfig.repo + "/BetterDiscordApp/" + bdConfig.hash + "/data/emotedata_twitch_subscriber.json", + backup: "https://cdn.staticaly.com/gh/rauenzi/BetterDiscordApp/master/data/emotedata_twitch_subscriber.json", variable: "TwitchSubscriber", oldVariable: "subEmotesTwitch", parser: (data) => { @@ -621,7 +622,7 @@ EmoteModule.prototype.init = async function () { getOldData: (url) => url.match(/\/([0-9]+)\//)[1] }, FrankerFaceZ: { - url: "https://" + bdConfig.updater.CDN + "/" + bdConfig.repo + "/BetterDiscordApp/" + bdConfig.hash + "/data/emotedata_ffz.json", + url: "https://cdn.staticaly.com/gh/rauenzi/BetterDiscordApp/master/data/emotedata_ffz.json", variable: "FrankerFaceZ", oldVariable: "emotesFfz", getEmoteURL: (e) => `https://cdn.frankerfacez.com/emoticon/${e}/1`, @@ -643,7 +644,7 @@ EmoteModule.prototype.init = async function () { getOldData: (url) => url }, BTTV2: { - url: "https://" + bdConfig.updater.CDN + "/" + bdConfig.repo + "/BetterDiscordApp/" + bdConfig.hash + "/data/emotedata_bttv.json", + url: "https://cdn.staticaly.com/gh/rauenzi/BetterDiscordApp/master/data/emotedata_bttv.json", variable: "BTTV2", oldVariable: "emotesBTTV2", getEmoteURL: (e) => `https://cdn.betterttv.net/emote/${e}/1x`, @@ -666,10 +667,10 @@ EmoteModule.prototype.init = async function () { for (let n = 0; n < nodes.length; n++) { const node = nodes[n]; if (typeof(node) !== "string") continue; - const words = node.split(/([^\s]+)([\s]|$)/g); + const words = node.split(/([^\s]+)([\s]|$)/g); for (let c = 0, clen = this.categories.length; c < clen; c++) { for (let w = 0, wlen = words.length; w < wlen; w++) { - let emote = words[w]; + let emote = words[w]; let emoteSplit = emote.split(":"); let emoteName = emoteSplit[0]; let emoteModifier = emoteSplit[1] ? emoteSplit[1] : ""; @@ -694,15 +695,14 @@ EmoteModule.prototype.init = async function () { } if (!window.bdEmotes[current][emoteName] || !settingsCookie[window.bdEmoteSettingIDs[current]]) continue; - const results = nodes[n].match(new RegExp(`([\\s]|^)${utils.escape(emoteModifier ? emoteName + ":" + emoteModifier : emoteName)}([\\s]|$)`)); - if (!results) continue; + const results = nodes[n].match(new RegExp(`([\\s]|^)${Utils.escape(emoteModifier ? emoteName + ":" + emoteModifier : emoteName)}([\\s]|$)`)); + if (!results) continue; const pre = nodes[n].substring(0, results.index + results[1].length); const post = nodes[n].substring(results.index + results[0].length - results[2].length); nodes[n] = pre; const emoteComponent = BDV2.react.createElement(BDEmote, {name: emoteName, url: window.bdEmotes[current][emoteName], modifier: emoteModifier}); nodes.splice(n + 1, 0, post); nodes.splice(n + 1, 0, emoteComponent); - n = n + 2; } } } @@ -728,8 +728,8 @@ EmoteModule.prototype.clearEmoteData = async function() { let emoteFile = "emote_data.json"; let file = bdConfig.dataPath + emoteFile; let exists = _fs.existsSync(file); - if (exists) _fs.unlinkSync(file); + window.bdStorage.set("emoteCacheDate", (new Date()).toJSON()); window.bdEmotes = { TwitchGlobal: {}, @@ -748,19 +748,30 @@ EmoteModule.prototype.goBack = async function(emoteInfo) { } }; +EmoteModule.prototype.isCacheValid = function() { + const cacheDate = new Date(window.bdStorage.get("emoteCacheDate") || null); + const currentDate = new Date(); + const daysBetween = Math.round(Math.abs((currentDate.getTime() - cacheDate.getTime()) / (24 * 60 * 60 * 1000))); + if (daysBetween > bdConfig.cache.days) { + window.bdStorage.set("emoteCacheDate", currentDate.toJSON()); + return false; + } + return true; +}; + EmoteModule.prototype.loadEmoteData = async function(emoteInfo) { - let _fs = require("fs"); - let emoteFile = "emote_data.json"; - let file = bdConfig.dataPath + emoteFile; - let exists = _fs.existsSync(file); + const _fs = require("fs"); + const emoteFile = "emote_data.json"; + const file = bdConfig.dataPath + emoteFile; + const exists = _fs.existsSync(file); - if (exists && !bdConfig.cache.expired) { + if (exists && this.isCacheValid()) { if (settingsCookie["fork-ps-2"]) mainCore.showToast("Loading emotes from cache.", {type: "info"}); - utils.log("[Emotes] Loading emotes from local cache."); + Utils.log("[Emotes] Loading emotes from local cache."); - let data = await new Promise(resolve => { + const data = await new Promise(resolve => { _fs.readFile(file, "utf8", (err, data) => { - utils.log("[Emotes] Emotes loaded from cache."); + Utils.log("[Emotes] Emotes loaded from cache."); if (err) data = {}; resolve(data); }); @@ -769,7 +780,7 @@ EmoteModule.prototype.loadEmoteData = async function(emoteInfo) { let isValid = Utils.testJSON(data); if (isValid) window.bdEmotes = JSON.parse(data); - for (let e in emoteInfo) { + for (const e in emoteInfo) { isValid = Object.keys(window.bdEmotes[emoteInfo[e].variable]).length > 0; } @@ -778,7 +789,7 @@ EmoteModule.prototype.loadEmoteData = async function(emoteInfo) { return; } - utils.log("[Emotes] Cache was corrupt, downloading..."); + Utils.log("[Emotes] Cache was corrupt, downloading..."); _fs.unlinkSync(file); } @@ -794,7 +805,7 @@ EmoteModule.prototype.loadEmoteData = async function(emoteInfo) { if (settingsCookie["fork-ps-2"]) mainCore.showToast("All emotes successfully downloaded.", {type: "success"}); try { _fs.writeFileSync(file, JSON.stringify(window.bdEmotes), "utf8"); } - catch (err) { utils.err("[Emotes] Could not save emote data.", err); } + catch (err) { Utils.err("[Emotes] Could not save emote data.", err); } quickEmoteMenu.init(); }; @@ -806,12 +817,12 @@ EmoteModule.prototype.downloadEmotes = function(emoteMeta) { timeout: emoteMeta.timeout ? emoteMeta.timeout : 5000 }; - utils.log("[Emotes] Downloading: " + emoteMeta.variable); + Utils.log("[Emotes] Downloading: " + emoteMeta.variable); return new Promise((resolve, reject) => { request(options, (error, response, body) => { if (error) { - utils.err("[Emotes] Could not download " + emoteMeta.variable, error); + Utils.err("[Emotes] Could not download " + emoteMeta.variable, error); if (emoteMeta.backup) { emoteMeta.url = emoteMeta.backup; emoteMeta.backup = null; @@ -826,7 +837,7 @@ EmoteModule.prototype.downloadEmotes = function(emoteMeta) { parsedData = JSON.parse(body); } catch (err) { - utils.err("[Emotes] Could not download " + emoteMeta.variable, error); + Utils.err("[Emotes] Could not download " + emoteMeta.variable, error); if (emoteMeta.backup) { emoteMeta.url = emoteMeta.backup; emoteMeta.backup = null; @@ -841,14 +852,14 @@ EmoteModule.prototype.downloadEmotes = function(emoteMeta) { parsedData[emote] = emoteMeta.getEmoteURL(parsedData[emote]); } resolve(parsedData); - utils.log("[Emotes] Downloaded: " + emoteMeta.variable); + Utils.log("[Emotes] Downloaded: " + emoteMeta.variable); }); }); }; EmoteModule.prototype.getBlacklist = function () { return new Promise(resolve => { - $.getJSON("https://cdn.rawgit.com/rauenzi/betterDiscordApp/" + _hash + "/data/emotefilter.json", function (data) { + $.getJSON("https://cdn.staticaly.com/gh/rauenzi/BetterDiscordApp/master/data/emotefilter.json", function (data) { resolve(bemotes = data.blacklist); }); }); @@ -868,7 +879,7 @@ EmoteModule.prototype.autoCapitalize = function () { if (lastWord == "danSgame") return; var ret = this.capitalize(lastWord.toLowerCase()); if (ret !== null && ret !== undefined) { - utils.insertText(utils.getTextArea()[0], text.replace(lastWord, ret)); + Utils.insertText(Utils.getTextArea()[0], text.replace(lastWord, ret)); } } }); @@ -1011,8 +1022,8 @@ QuickEmoteMenu.prototype.switchQem = function(id) { emoteIcon.off(); emoteIcon.on("click", function () { var emote = $(this).attr("title"); - var ta = utils.getTextArea(); - utils.insertText(ta[0], ta.val().slice(-1) == " " ? ta.val() + emote : ta.val() + " " + emote); + var ta = Utils.getTextArea(); + Utils.insertText(ta[0], ta.val().slice(-1) == " " ? ta.val() + emote : ta.val() + " " + emote); }); }; @@ -1078,202 +1089,175 @@ QuickEmoteMenu.prototype.updateFavorites = function () { * Date: 26/08/2015 - 15:54 * https://github.com/Jiiks/BetterDiscordApp */ - -var _hash; - -function Utils() { - -} - -Utils.prototype.getTextArea = function () { - return $(".channelTextArea-1LDbYG textarea"); -}; - -Utils.prototype.insertText = function (textarea, text) { - textarea.focus(); - textarea.selectionStart = 0; - textarea.selectionEnd = textarea.value.length; - document.execCommand("insertText", false, text); -}; - -Utils.prototype.jqDefer = function (fnc) { - if (window.jQuery) { - fnc(); - } - else { - setTimeout(function () { - this.jqDefer(fnc); - }, 100); - } -}; - -Utils.prototype.getHash = function () { - return new Promise((resolve) => { - $.getJSON("https://api.github.com/repos/rauenzi/BetterDiscordApp/commits/master").done(function (data) { - _hash = data.sha; - bdConfig.hash = _hash; - resolve(_hash); - }).fail(() => { - _hash = _bdhash || "48844445d65c6fb5a019eff14d7dcffcc1744071"; - resolve(_hash); - }); - }); -}; - -Utils.prototype.loadHtml = function (html, callback) { - var container = $("
", { - "class": "bd-container" - }).appendTo("body"); - - //TODO Inject these in next core update - html = "//cdn.rawgit.com/Jiiks/BetterDiscordApp/" + _hash + "/html/" + html + ".html"; - - container.load(html, callback()); -}; - -Utils.prototype.injectJs = function (uri) { - $("", { - type: "text/javascript", - src: uri - }).appendTo($("body")); -}; - -Utils.prototype.injectCss = function (uri) { - $("", { - type: "text/css", - rel: "stylesheet", - href: uri - }).appendTo($("head")); -}; - -Utils.prototype.escapeID = function(id) { - return id.replace(/^[^a-z]+|[^\w-]+/gi, ""); -}; - -Utils.prototype.log = function (message) { - console.log("%c[BetterDiscord] %c" + message + "", "color: #3a71c1; font-weight: 700;", ""); -}; - -Utils.prototype.err = function (message, error) { - console.log("%c[BetterDiscord] %c" + message + "", "color: red; font-weight: 700;", ""); - if (error) { - console.groupCollapsed("%cError: " + error.message, "color: red;"); - console.error(error.stack); - console.groupEnd(); - } -}; - -Utils.prototype.escape = function(s) { - return s.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&"); -}; - -Utils.prototype.insertElement = function(node, regex, element) { - - var child = node.firstChild; - - while (child) { - if (child.nodeType != Node.TEXT_NODE) {child = child.nextSibling; continue;} - var bk = 0; - child.data.replace(regex, function(all) { - var args = [].slice.call(arguments); - var offset = args[args.length - 2]; - var newTextNode = args[1] ? child.splitText(offset + args[1].length + bk) : child.splitText(offset + bk); - bk -= child.data.length + all.length; - - newTextNode.data = newTextNode.data.substr(all.trim().length); - child.parentNode.insertBefore(element, newTextNode); - child = newTextNode; - }); - - regex.lastIndex = 0; - - - child = child.nextSibling; +var Utils = class Utils { + static getTextArea() { + return $(".channelTextArea-1LDbYG textarea"); } - return node; -}; - -Utils.prototype.getTextNodes = function(node) { - var textNodes = []; - if (!node) return textNodes; - for (var i = 0; i < node.childNodes.length; i++) { - var curNode = node.childNodes[i]; - if (curNode.nodeType === Node.TEXT_NODE) { - textNodes.push(curNode); - } + static insertText(textarea, text) { + textarea.focus(); + textarea.selectionStart = 0; + textarea.selectionEnd = textarea.value.length; + document.execCommand("insertText", false, text); } - return textNodes; -}; -Utils.testJSON = function(data) { - try { - JSON.parse(data); - return true; - } - catch (err) { - return false; - } -}; - -Utils.suppressErrors = (method, desiption) => (...params) => { - try { return method(...params); } - catch (e) { console.error("Error occurred in " + desiption, e); } -}; - -Utils.monkeyPatch = (what, methodName, options) => { - const {before, after, instead, once = false, silent = false, force = false} = options; - const displayName = options.displayName || what.displayName || what.name || what.constructor.displayName || what.constructor.name; - if (!silent) console.log("patch", methodName, "of", displayName); // eslint-disable-line no-console - if (!what[methodName]) { - if (force) what[methodName] = function() {}; - else return console.error(methodName, "does not exist for", displayName); // eslint-disable-line no-console - } - const origMethod = what[methodName]; - const cancel = () => { - if (!silent) console.log("unpatch", methodName, "of", displayName); // eslint-disable-line no-console - what[methodName] = origMethod; - }; - what[methodName] = function() { - const data = { - thisObject: this, - methodArguments: arguments, - cancelPatch: cancel, - originalMethod: origMethod, - callOriginalMethod: () => data.returnValue = data.originalMethod.apply(data.thisObject, data.methodArguments) - }; - if (instead) { - const tempRet = Utils.suppressErrors(instead, "`instead` callback of " + what[methodName].displayName)(data); - if (tempRet !== undefined) data.returnValue = tempRet; + static jqDefer(fnc) { + if (window.jQuery) { + fnc(); } else { - if (before) Utils.suppressErrors(before, "`before` callback of " + what[methodName].displayName)(data); - data.callOriginalMethod(); - if (after) Utils.suppressErrors(after, "`after` callback of " + what[methodName].displayName)(data); + setTimeout(function () { + this.jqDefer(fnc); + }, 100); } - if (once) cancel(); - return data.returnValue; - }; - what[methodName].__monkeyPatched = true; - what[methodName].displayName = "patched " + (what[methodName].displayName || methodName); - return cancel; -}; + } -Utils.onRemoved = function(node, callback) { - const observer = new MutationObserver((mutations) => { - for (let m = 0; m < mutations.length; m++) { - const mutation = mutations[m]; - const nodes = Array.from(mutation.removedNodes); - const directMatch = nodes.indexOf(node) > -1; - const parentMatch = nodes.some(parent => parent.contains(node)); - if (directMatch || parentMatch) { - observer.disconnect(); - callback(); + static injectCss(uri) { + $("", { + type: "text/css", + rel: "stylesheet", + href: uri + }).appendTo($("head")); + } + + static injectJs(uri) { + $("", { + type: "text/javascript", + src: uri + }).appendTo($("body")); + } + + static escapeID(id) { + return id.replace(/^[^a-z]+|[^\w-]+/gi, ""); + } + + static log(message) { + console.log("%c[BetterDiscord] %c" + message + "", "color: #3a71c1; font-weight: 700;", ""); + } + + static err(message, error) { + console.log("%c[BetterDiscord] %c" + message + "", "color: red; font-weight: 700;", ""); + if (error) { + console.groupCollapsed("%cError: " + error.message, "color: red;"); + console.error(error.stack); + console.groupEnd(); + } + } + + static escape(s) { + return s.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&"); + } + + static insertElement(node, regex, element) { + + var child = node.firstChild; + + while (child) { + if (child.nodeType != Node.TEXT_NODE) {child = child.nextSibling; continue;} + var bk = 0; + child.data.replace(regex, function(all) { + var args = [].slice.call(arguments); + var offset = args[args.length - 2]; + var newTextNode = args[1] ? child.splitText(offset + args[1].length + bk) : child.splitText(offset + bk); + bk -= child.data.length + all.length; + + newTextNode.data = newTextNode.data.substr(all.trim().length); + child.parentNode.insertBefore(element, newTextNode); + child = newTextNode; + }); + + regex.lastIndex = 0; + + + child = child.nextSibling; + } + + return node; + } + + static getTextNodes(node) { + var textNodes = []; + if (!node) return textNodes; + for (var i = 0; i < node.childNodes.length; i++) { + var curNode = node.childNodes[i]; + if (curNode.nodeType === Node.TEXT_NODE) { + textNodes.push(curNode); } } - }); + return textNodes; + } - observer.observe(document.body, {subtree: true, childList: true}); + static testJSON(data) { + try { + JSON.parse(data); + return true; + } + catch (err) { + return false; + } + } + + static suppressErrors(method, desiption) { + return (...params) => { + try { return method(...params); } + catch (e) { console.error("Error occurred in " + desiption, e); } + }; + } + + static monkeyPatch(what, methodName, options) { + const {before, after, instead, once = false, silent = false, force = false} = options; + const displayName = options.displayName || what.displayName || what.name || what.constructor.displayName || what.constructor.name; + if (!silent) console.log("patch", methodName, "of", displayName); // eslint-disable-line no-console + if (!what[methodName]) { + if (force) what[methodName] = function() {}; + else return console.error(methodName, "does not exist for", displayName); // eslint-disable-line no-console + } + const origMethod = what[methodName]; + const cancel = () => { + if (!silent) console.log("unpatch", methodName, "of", displayName); // eslint-disable-line no-console + what[methodName] = origMethod; + }; + what[methodName] = function() { + const data = { + thisObject: this, + methodArguments: arguments, + cancelPatch: cancel, + originalMethod: origMethod, + callOriginalMethod: () => data.returnValue = data.originalMethod.apply(data.thisObject, data.methodArguments) + }; + if (instead) { + const tempRet = Utils.suppressErrors(instead, "`instead` callback of " + what[methodName].displayName)(data); + if (tempRet !== undefined) data.returnValue = tempRet; + } + else { + if (before) Utils.suppressErrors(before, "`before` callback of " + what[methodName].displayName)(data); + data.callOriginalMethod(); + if (after) Utils.suppressErrors(after, "`after` callback of " + what[methodName].displayName)(data); + } + if (once) cancel(); + return data.returnValue; + }; + what[methodName].__monkeyPatched = true; + what[methodName].displayName = "patched " + (what[methodName].displayName || methodName); + return cancel; + } + + static onRemoved(node, callback) { + const observer = new MutationObserver((mutations) => { + for (let m = 0; m < mutations.length; m++) { + const mutation = mutations[m]; + const nodes = Array.from(mutation.removedNodes); + const directMatch = nodes.indexOf(node) > -1; + const parentMatch = nodes.some(parent => parent.contains(node)); + if (directMatch || parentMatch) { + observer.disconnect(); + callback(); + } + } + }); + + observer.observe(document.body, {subtree: true, childList: true}); + } }; @@ -1333,7 +1317,7 @@ PluginModule.prototype.loadPlugins = function () { } catch (err) { pluginCookie[name] = false; - utils.err("Plugin " + name + " could not be loaded.", err); + Utils.err("Plugin " + name + " could not be loaded.", err); bdpluginErrors.push({name: name, file: bdplugins[plugins[i]].filename, reason: "load() could not be fired.", error: {message: err.message, stack: err.stack}}); continue; } @@ -1347,7 +1331,7 @@ PluginModule.prototype.loadPlugins = function () { } catch (err) { pluginCookie[name] = false; - utils.err("Plugin " + name + " could not be started.", err); + Utils.err("Plugin " + name + " could not be started.", err); bdpluginErrors.push({name: name, file: bdplugins[plugins[i]].filename, reason: "start() could not be fired.", error: {message: err.message, stack: err.stack}}); } } @@ -1368,7 +1352,7 @@ PluginModule.prototype.startPlugin = function (plugin) { catch (err) { pluginCookie[plugin] = false; this.savePluginData(); - utils.err("Plugin " + name + " could not be started.", err); + Utils.err("Plugin " + name + " could not be started.", err); } }; @@ -1378,7 +1362,7 @@ PluginModule.prototype.stopPlugin = function (plugin) { if (settingsCookie["fork-ps-2"]) mainCore.showToast(`${bdplugins[plugin].plugin.getName()} v${bdplugins[plugin].plugin.getVersion()} has stopped.`); } catch (err) { - utils.err("Plugin " + name + " could not be stopped.", err); + Utils.err("Plugin " + name + " could not be stopped.", err); } }; @@ -1400,13 +1384,6 @@ PluginModule.prototype.togglePlugin = function (plugin) { }; PluginModule.prototype.loadPluginData = function () { - if ($.cookie("bd-plugins")) { - pluginCookie = JSON.parse($.cookie("bd-plugins")); - this.savePluginData(); - $.removeCookie("bd-plugins", {path: "/"}); - return; - } - let saved = bdSettingsStorage.get("plugins"); if (saved) { pluginCookie = saved; @@ -1424,7 +1401,7 @@ PluginModule.prototype.newMessage = function () { if (!pluginCookie[plugin.getName()]) continue; if (typeof plugin.onMessage === "function") { try { plugin.onMessage(); } - catch (err) { utils.err("Unable to fire onMessage for " + plugin.getName() + ".", err); } + catch (err) { Utils.err("Unable to fire onMessage for " + plugin.getName() + ".", err); } } } }; @@ -1436,7 +1413,7 @@ PluginModule.prototype.channelSwitch = function () { if (!pluginCookie[plugin.getName()]) continue; if (typeof plugin.onSwitch === "function") { try { plugin.onSwitch(); } - catch (err) { utils.err("Unable to fire onSwitch for " + plugin.getName() + ".", err); } + catch (err) { Utils.err("Unable to fire onSwitch for " + plugin.getName() + ".", err); } } } }; @@ -1448,7 +1425,7 @@ PluginModule.prototype.rawObserver = function(e) { if (!pluginCookie[plugin.getName()]) continue; if (typeof plugin.observer === "function") { try { plugin.observer(e); } - catch (err) { utils.err("Unable to fire observer for " + plugin.getName() + ".", err); } + catch (err) { Utils.err("Unable to fire observer for " + plugin.getName() + ".", err); } } } }; @@ -1475,7 +1452,7 @@ ThemeModule.prototype.loadThemes = function () { for (var i = 0; i < themes.length; i++) { var name = bdthemes[themes[i]].name; if (!themeCookie[name]) themeCookie[name] = false; - if (themeCookie[name]) $("head").append($("`); + $("head").append(``); if (settingsCookie["fork-ps-2"]) mainCore.showToast(`${bdthemes[theme].name} v${bdthemes[theme].version} has been applied.`); }; ThemeModule.prototype.disableTheme = function (theme) { themeCookie[theme] = false; this.saveThemeData(); - $(`#${utils.escapeID(bdthemes[theme].name)}`).remove(); + $(`#${Utils.escapeID(bdthemes[theme].name)}`).remove(); if (settingsCookie["fork-ps-2"]) mainCore.showToast(`${bdthemes[theme].name} v${bdthemes[theme].version} has been removed.`); }; @@ -1503,13 +1480,6 @@ ThemeModule.prototype.toggleTheme = function (theme) { }; ThemeModule.prototype.loadThemeData = function () { - if ($.cookie("bd-themes")) { - themeCookie = JSON.parse($.cookie("bd-themes")); - this.saveThemeData(); - $.removeCookie("bd-themes", {path: "/"}); - return; - } - let saved = bdSettingsStorage.get("themes"); if (saved) { themeCookie = saved; @@ -1540,26 +1510,26 @@ var BdApi = { //id = id of element //css = custom css BdApi.injectCSS = function (id, css) { - $("head").append($("`),settingsCookie["fork-ps-2"]&&mainCore.showToast(`${bdthemes[t].name} v${bdthemes[t].version} has been applied.`)},ThemeModule.prototype.disableTheme=function(t){themeCookie[t]=!1,this.saveThemeData(),$(`#${utils.escapeID(bdthemes[t].name)}`).remove(),settingsCookie["fork-ps-2"]&&mainCore.showToast(`${bdthemes[t].name} v${bdthemes[t].version} has been removed.`)},ThemeModule.prototype.toggleTheme=function(t){themeCookie[t]?this.disableTheme(t):this.enableTheme(t)},ThemeModule.prototype.loadThemeData=function(){if($.cookie("bd-themes"))return themeCookie=JSON.parse($.cookie("bd-themes")),this.saveThemeData(),void $.removeCookie("bd-themes",{path:"/"});let t=bdSettingsStorage.get("themes");t&&(themeCookie=t)},ThemeModule.prototype.saveThemeData=function(){bdSettingsStorage.set("themes",themeCookie)};var BdApi={get React(){return BDV2.react},get ReactDOM(){return BDV2.reactDom},injectCSS:function(t,a){$("head").append($(""),$("#customcss").html(this.editor.session.getValue()).detach().appendTo(document.head)}saveCss(){window.bdStorage.set("bdcustomcss",btoa(this.editor.session.getValue()))}}class V2C_CssEditor extends BDV2.reactComponent{constructor(t){super(t);let a=this;a.props.lines=0,a.setInitialState(),a.attach=a.attach.bind(a),a.detachedEditor=BDV2.react.createElement(V2C_CssEditorDetached,{attach:a.attach}),a.onClick=a.onClick.bind(a),a.updateCss=a.updateCss.bind(a),a.saveCss=a.saveCss.bind(a),a.detach=a.detach.bind(a)}setInitialState(){this.state={detached:this.props.detached||BDV2.editorDetached}}componentDidMount(){this.editor=ace.edit("bd-customcss-editor"),this.editor.setTheme("ace/theme/monokai"),this.editor.session.setMode("ace/mode/css"),this.editor.setShowPrintMargin(!1),this.editor.setFontSize(14),this.editor.on("change",()=>{settingsCookie["bda-css-0"]&&(this.saveCss(),this.updateCss())})}componentWillUnmount(){this.editor.destroy()}componentDidUpdate(t,a){let o=this;a.detached&&!o.state.detached&&BDV2.reactDom.unmountComponentAtNode(o.detachedRoot)}codeMirror(){}get options(){return{lineNumbers:!0,mode:"css",indentUnit:4,theme:"material",scrollbarStyle:"simple"}}get css(){let t=window.bdStorage.get("bdcustomcss"),a="";return t&&""!==t&&(a=atob(t)),a}updateLineCount(){let t=this.refs.editor.value.split("\n").length;t==this.props.lines||(this.refs.lines.textContent=Array.from(Array(t),(a,o)=>o+1).join(".\n")+".",this.props.lines=t)}render(){let t=this,{detached:a}=t.state;return BDV2.react.createElement("div",{className:"content-column default",style:{padding:"60px 40px 0px"}},a&&BDV2.react.createElement("div",{id:"editor-detached"},BDV2.react.createElement(V2Components.SettingsTitle,{text:"Custom CSS Editor"}),BDV2.react.createElement("h3",null,"Editor Detached"),BDV2.react.createElement("button",{className:"btn btn-primary",onClick:()=>{t.attach()}},"Attach")),!a&&BDV2.react.createElement("div",null,BDV2.react.createElement(V2Components.SettingsTitle,{text:"Custom CSS Editor"}),BDV2.react.createElement("div",{className:"editor-wrapper"},BDV2.react.createElement("div",{id:"bd-customcss-editor",className:"editor",ref:"editor"},t.css)),BDV2.react.createElement("div",{id:"bd-customcss-attach-controls"},BDV2.react.createElement("ul",{className:"checkbox-group"},BDV2.react.createElement(V2Components.Checkbox,{id:"live-update",text:"Live Update",onChange:this.onChange,checked:settingsCookie["bda-css-0"]})),BDV2.react.createElement("div",{id:"bd-customcss-detach-controls-button"},BDV2.react.createElement("button",{style:{borderRadius:"3px 0 0 3px",borderRight:"1px solid #3f4146"},className:"btn btn-primary",onClick:()=>{t.onClick("update")}},"Update"),BDV2.react.createElement("button",{style:{borderRadius:"0",borderLeft:"1px solid #2d2d2d",borderRight:"1px solid #2d2d2d"},className:"btn btn-primary",onClick:()=>{t.onClick("save")}},"Save"),BDV2.react.createElement("button",{style:{borderRadius:"0 3px 3px 0",borderLeft:"1px solid #3f4146"},className:"btn btn-primary",onClick:()=>{t.onClick("detach")}},"Detach"),BDV2.react.createElement("span",{style:{fontSize:"10px",marginLeft:"5px"}},"Unsaved changes are lost on detach")))))}onClick(t){let a=this;"update"===t?a.updateCss():"save"===t?a.saveCss():"detach"===t?a.detach():void 0}onChange(t,a){"live-update"===t?(settingsCookie["bda-css-0"]=a,mainCore.saveSettings()):void 0}updateCss(){0==$("#customcss").length&&$("head").append(""),$("#customcss").html(this.editor.session.getValue()).detach().appendTo(document.head)}saveCss(){window.bdStorage.set("bdcustomcss",btoa(this.editor.session.getValue()))}detach(){let t=this;t.setState({detached:!0});let a=t.detachedRoot;return a?void BDV2.reactDom.render(t.detachedEditor,a):void console.log("FAILED TO INJECT ROOT: .app")}get detachedRoot(){let t=$("#bd-customcss-detach-container");return t.length?t[0]:this.injectDetachedRoot()?this.detachedRoot:null}injectDetachedRoot(){return!!$(".app").length&&($("",{id:"bd-customcss-detach-container"}).insertAfter($(".app")),!0)}attach(){let t=this;t.setState({detached:!1})}}class V2C_List extends BDV2.reactComponent{constructor(t){super(t)}render(){return BDV2.react.createElement("ul",{className:this.props.className},this.props.children)}}class V2C_ContentColumn extends BDV2.reactComponent{constructor(t){super(t)}render(){return BDV2.react.createElement("div",{className:"content-column default"},BDV2.react.createElement("h2",{className:"ui-form-title h2 margin-reset margin-bottom-20"},this.props.title),this.props.children)}}class V2C_PluginCard extends BDV2.reactComponent{constructor(t){super(t);let a=this;a.onChange=a.onChange.bind(a),a.showSettings=a.showSettings.bind(a),a.setInitialState(),a.hasSettings="function"==typeof a.props.plugin.getSettingsPanel,a.settingsPanel=""}setInitialState(){this.state={checked:pluginCookie[this.props.plugin.getName()],settings:!1}}componentDidUpdate(){if(this.state.settings){if("object"==typeof this.settingsPanel&&this.refs.settingspanel.appendChild(this.settingsPanel),!settingsCookie["fork-ps-3"])return;var t=(d,l)=>{let u=d.scrollTop,h=u+d.clientHeight,g=l.offsetTop,b=g+l.clientHeight;return gh};let a=$(BDV2.reactDom.findDOMNode(this)),o=a.parents(".scroller");if(!t(o[0],a[0]))return;o.animate({scrollTop:a.offset().top-o.offset().top+o.scrollTop()-30},300)}}render(){let t=this,{plugin:a}=this.props,o=a.getName(),d=a.getAuthor(),l=a.getDescription(),u=a.getVersion(),h=bdplugins[o].website,g=bdplugins[o].source;if(this.state.settings){try{t.settingsPanel=a.getSettingsPanel()}catch(b){utils.err("Unable to get settings panel for "+a.getName()+".",b)}return BDV2.react.createElement("li",{className:"settings-open ui-switch-item"},BDV2.react.createElement("div",{style:{float:"right",cursor:"pointer"},onClick:()=>{this.refs.settingspanel.innerHTML="",t.setState({settings:!1})}},BDV2.react.createElement(V2Components.XSvg,null)),"object"==typeof t.settingsPanel&&BDV2.react.createElement("div",{id:`plugin-settings-${o}`,className:"plugin-settings",ref:"settingspanel"}),"object"!=typeof t.settingsPanel&&BDV2.react.createElement("div",{id:`plugin-settings-${o}`,className:"plugin-settings",ref:"settingspanel",dangerouslySetInnerHTML:{__html:t.settingsPanel}}))}return BDV2.react.createElement("li",{"data-name":o,"data-version":u,className:"settings-closed ui-switch-item"},BDV2.react.createElement("div",{className:"bda-header"},BDV2.react.createElement("span",{className:"bda-header-title"},BDV2.react.createElement("span",{className:"bda-name"},o)," v",BDV2.react.createElement("span",{className:"bda-version"},u)," by ",BDV2.react.createElement("span",{className:"bda-author"},d)),BDV2.react.createElement("label",{className:"ui-switch-wrapper ui-flex-child",style:{flex:"0 0 auto"}},BDV2.react.createElement("input",{checked:this.state.checked,onChange:this.onChange,className:"ui-switch-checkbox",type:"checkbox"}),BDV2.react.createElement("div",{className:this.state.checked?"ui-switch checked":"ui-switch"}))),BDV2.react.createElement("div",{className:"bda-description-wrap scroller-wrap fade"},BDV2.react.createElement("div",{className:"bda-description scroller"},l)),(h||g||this.hasSettings)&&BDV2.react.createElement("div",{className:"bda-footer"},BDV2.react.createElement("span",{className:"bda-links"},h&&BDV2.react.createElement("a",{className:"bda-link bda-link-website",href:h,target:"_blank"},"Website"),h&&g&&" | ",g&&BDV2.react.createElement("a",{className:"bda-link bda-link-source",href:g,target:"_blank"},"Source")),this.hasSettings&&BDV2.react.createElement("button",{onClick:this.showSettings,className:"bda-settings-button",disabled:!this.state.checked},"Settings")))}onChange(){this.setState({checked:!this.state.checked}),pluginModule.togglePlugin(this.props.plugin.getName())}showSettings(){this.hasSettings&&this.setState({settings:!0})}}class V2C_ThemeCard extends BDV2.reactComponent{constructor(t){super(t),this.setInitialState(),this.onChange=this.onChange.bind(this)}setInitialState(){this.state={checked:themeCookie[this.props.theme.name]}}render(){let{theme:t}=this.props,a=t.name,o=t.description,d=t.version,l=t.author,u=bdthemes[a].website,h=bdthemes[a].source;return BDV2.react.createElement("li",{"data-name":a,"data-version":d,className:"settings-closed ui-switch-item"},BDV2.react.createElement("div",{className:"bda-header"},BDV2.react.createElement("span",{className:"bda-header-title"},BDV2.react.createElement("span",{className:"bda-name"},a)," v",BDV2.react.createElement("span",{className:"bda-version"},d)," by ",BDV2.react.createElement("span",{className:"bda-author"},l)),BDV2.react.createElement("label",{className:"ui-switch-wrapper ui-flex-child",style:{flex:"0 0 auto"}},BDV2.react.createElement("input",{checked:this.state.checked,onChange:this.onChange,className:"ui-switch-checkbox",type:"checkbox"}),BDV2.react.createElement("div",{className:this.state.checked?"ui-switch checked":"ui-switch"}))),BDV2.react.createElement("div",{className:"bda-description-wrap scroller-wrap fade"},BDV2.react.createElement("div",{className:"bda-description scroller"},o)),(u||h)&&BDV2.react.createElement("div",{className:"bda-footer"},BDV2.react.createElement("span",{className:"bda-links"},u&&BDV2.react.createElement("a",{className:"bda-link",href:u,target:"_blank"},"Website"),u&&h&&" | ",h&&BDV2.react.createElement("a",{className:"bda-link",href:h,target:"_blank"},"Source"))))}onChange(){this.setState({checked:!this.state.checked}),themeModule.toggleTheme(this.props.theme.name)}}class V2Cs_TabBar{static get Item(){return V2C_TabBarItem}static get Header(){return V2C_TabBarHeader}static get Separator(){return V2C_TabBarSeparator}}class V2Components{static get SettingsPanel(){return V2C_SettingsPanel}static get Switch(){return V2C_Switch}static get Scroller(){return V2C_Scroller}static get TabBar(){return V2Cs_TabBar}static get SideBar(){return V2C_SideBar}static get Tools(){return V2C_Tools}static get SettingsTitle(){return V2C_SettingsTitle}static get CssEditor(){return V2C_CssEditor}static get Checkbox(){return V2C_Checkbox}static get List(){return V2C_List}static get PluginCard(){return V2C_PluginCard}static get ThemeCard(){return V2C_ThemeCard}static get ContentColumn(){return V2C_ContentColumn}static get XSvg(){return V2C_XSvg}static get Layer(){return V2C_Layer}static get SidebarView(){return V2C_SidebarView}static get ServerCard(){return V2C_ServerCard}}class V2_SettingsPanel_Sidebar{constructor(t){this.onClick=t}get items(){return[{text:"Core",id:"core"},{text:"Zere's Fork",id:"fork"},{text:"Emotes",id:"emotes"},{text:"Custom CSS",id:"customcss"},{text:"Plugins",id:"plugins"},{text:"Themes",id:"themes"}]}get component(){return BDV2.react.createElement("span",null,BDV2.react.createElement(V2Components.SideBar,{onClick:this.onClick,headerText:"Bandaged BD",items:this.items}),BDV2.react.createElement("div",{style:{fontSize:"12px",fontWeight:"600",color:"#72767d",padding:"2px 10px"}},`BD v${bdVersion} by `,BDV2.react.createElement("a",{href:"https://github.com/Jiiks/",target:"_blank"},"Jiiks")),BDV2.react.createElement("div",{style:{fontSize:"12px",fontWeight:"600",color:"#72767d",padding:"2px 10px"}},`BBD v${bbdVersion} by `,BDV2.react.createElement("a",{href:"https://github.com/rauenzi/",target:"_blank"},"Zerebos")))}get root(){let t=$("#bd-settings-sidebar");return t.length?t[0]:this.injectRoot()?this.root:null}injectRoot(){let t=$("[class*='side-'] > [class*='item-']:not([class*=Danger])").last();return!!t.length&&($("",{id:"bd-settings-sidebar"}).insertBefore(t.prev()),!0)}render(){let t=this.root;return t?void(BDV2.reactDom.render(this.component,t),Utils.onRemoved(t,()=>{BDV2.reactDom.unmountComponentAtNode(t)})):void console.log("FAILED TO LOCATE ROOT: [class*='side-'] > [class*='item-']:not([class*=Danger])")}}class V2_SettingsPanel{constructor(){let t=this;t.sideBarOnClick=t.sideBarOnClick.bind(t),t.onChange=t.onChange.bind(t),t.updateSettings=this.updateSettings.bind(t),t.sidebar=new V2_SettingsPanel_Sidebar(t.sideBarOnClick)}get root(){let t=$("#bd-settingspane-container");return t.length?t[0]:this.injectRoot()?this.root:null}injectRoot(){if(!$(".layer-3QrUeG .ui-standard-sidebar-view, .layer-3QrUeG .ui-standard-sidebar-view").length)return!1;const t=$("",{"class":"content-region",id:"bd-settingspane-container"});return $(".layer-3QrUeG .ui-standard-sidebar-view, .layer-3QrUeG .ui-standard-sidebar-view").append(t),Utils.onRemoved(t[0],()=>{BDV2.reactDom.unmountComponentAtNode(t[0])}),!0}get coreSettings(){return this.getSettings("core")}get forkSettings(){return this.getSettings("fork")}get emoteSettings(){return this.getSettings("emote")}getSettings(t){return Object.keys(settings).reduce((a,o)=>{let d=settings[o];return d.cat===t&&d.implemented&&!d.hidden&&(d.text=o,a.push(d)),a},[])}sideBarOnClick(t){let a=this;$(".content-region").first().hide(),$(a.root).show();"core"===t?a.renderCoreSettings():"fork"===t?a.renderForkSettings():"emotes"===t?a.renderEmoteSettings():"customcss"===t?a.renderCustomCssEditor():"plugins"===t?a.renderPluginPane():"themes"===t?a.renderThemePane():void 0}onClick(){}onChange(t,a){settingsCookie[t]=a,this.updateSettings()}updateSettings(){let t=settingsCookie;t["bda-es-0"]?$("#twitchcord-button-container").show():$("#twitchcord-button-container").hide(),t["bda-gs-b"]?$("body").addClass("bd-blue"):$("body").removeClass("bd-blue"),t["bda-gs-2"]?$("body").addClass("bd-minimal"):$("body").removeClass("bd-minimal"),t["bda-gs-3"]?$("body").addClass("bd-minimal-chan"):$("body").removeClass("bd-minimal-chan"),t["bda-gs-1"]?$("#bd-pub-li").show():$("#bd-pub-li").hide(),t["bda-gs-4"]?voiceMode.enable():voiceMode.disable(),t["bda-gs-5"]?$("#app-mount").addClass("bda-dark"):$("#app-mount").removeClass("bda-dark"),t["bda-gs-6"]&&mainCore.inject24Hour(),t["bda-gs-7"]?mainCore.injectColoredText():mainCore.removeColoredText(),t["fork-ps-4"]?classNormalizer.start():classNormalizer.stop(),t["bda-gs-8"]?dMode.enable(t["fork-dm-1"]):dMode.disable(),mainCore.saveSettings()}renderSidebar(){let t=this;$("[class*='side-'] > [class*='item-']").off("click.v2settingspanel").on("click.v2settingspanel",()=>{BDV2.reactDom.unmountComponentAtNode(t.root),$(t.root).hide(),$(".content-region").first().show()}),t.sidebar.render()}get coreComponent(){return BDV2.react.createElement(V2Components.Scroller,{contentColumn:!0,fade:!0,dark:!0,children:[BDV2.react.createElement(V2Components.SettingsPanel,{key:"cspanel",title:"Core Settings",onChange:this.onChange,settings:this.coreSettings}),BDV2.react.createElement(V2Components.Tools,{key:"tools"})]})}get forkComponent(){return BDV2.react.createElement(V2Components.Scroller,{contentColumn:!0,fade:!0,dark:!0,children:[BDV2.react.createElement(V2Components.SettingsPanel,{key:"fspanel",title:"Zere's Fork Settings",onChange:this.onChange,settings:this.forkSettings,button:{title:"Clear Emote Cache",onClick:()=>{emoteModule.clearEmoteData(),emoteModule.init(),quickEmoteMenu.init()}}}),BDV2.react.createElement(V2Components.Tools,{key:"tools"})]})}get emoteComponent(){return BDV2.react.createElement(V2Components.Scroller,{contentColumn:!0,fade:!0,dark:!0,children:[BDV2.react.createElement(V2Components.SettingsPanel,{key:"espanel",title:"Emote Settings",onChange:this.onChange,settings:this.emoteSettings}),BDV2.react.createElement(V2Components.Tools,{key:"tools"})]})}get customCssComponent(){return BDV2.react.createElement(V2Components.Scroller,{contentColumn:!0,fade:!0,dark:!0,children:[BDV2.react.createElement(V2Components.CssEditor,{key:"csseditor"}),BDV2.react.createElement(V2Components.Tools,{key:"tools"})]})}get pluginsComponent(){let t=Object.keys(bdplugins).reduce((l,u)=>{return l.push(BDV2.react.createElement(V2Components.PluginCard,{key:u,plugin:bdplugins[u].plugin})),l},[]),a=BDV2.react.createElement(V2Components.List,{key:"plugin-list",className:"bda-slist",children:t}),o=BDV2.react.createElement("button",{key:"folder-button",className:"bd-pfbtn",onClick:()=>{betterDiscordIPC.send("asynchronous-message",{arg:"opendir",path:"plugindir"})}},"Open Plugin Folder"),d=BDV2.react.createElement(V2Components.ContentColumn,{key:"pcolumn",title:"Plugins",children:[o,a]});return BDV2.react.createElement(V2Components.Scroller,{contentColumn:!0,fade:!0,dark:!0,children:[d,BDV2.react.createElement(V2Components.Tools,{key:"tools"})]})}get themesComponent(){let t=Object.keys(bdthemes).reduce((l,u)=>{return l.push(BDV2.react.createElement(V2Components.ThemeCard,{key:u,theme:bdthemes[u]})),l},[]),a=BDV2.react.createElement(V2Components.List,{key:"theme-list",className:"bda-slist",children:t}),o=BDV2.react.createElement("button",{key:"folder-button",className:"bd-pfbtn",onClick:()=>{betterDiscordIPC.send("asynchronous-message",{arg:"opendir",path:"themedir"})}},"Open Theme Folder"),d=BDV2.react.createElement(V2Components.ContentColumn,{key:"tcolumn",title:"Themes",children:[o,a]});return BDV2.react.createElement(V2Components.Scroller,{contentColumn:!0,fade:!0,dark:!0,children:[d,BDV2.react.createElement(V2Components.Tools,{key:"tools"})]})}renderCoreSettings(){let t=this.root;return t?void BDV2.reactDom.render(this.coreComponent,t):void console.log("FAILED TO LOCATE ROOT: .layer .ui-standard-sidebar-view")}renderForkSettings(){let t=this.root;return t?void BDV2.reactDom.render(this.forkComponent,t):void console.log("FAILED TO LOCATE ROOT: .layer .ui-standard-sidebar-view")}renderEmoteSettings(){let t=this.root;return t?void BDV2.reactDom.render(this.emoteComponent,t):void console.log("FAILED TO LOCATE ROOT: .layer .ui-standard-sidebar-view")}renderCustomCssEditor(){let t=this.root;return t?void BDV2.reactDom.render(this.customCssComponent,t):void console.log("FAILED TO LOCATE ROOT: .layer .ui-standard-sidebar-view")}renderPluginPane(){let t=this.root;return t?void BDV2.reactDom.render(this.pluginsComponent,t):void console.log("FAILED TO LOCATE ROOT: .layer .ui-standard-sidebar-view")}renderThemePane(){let t=this.root;return t?void BDV2.reactDom.render(this.themesComponent,t):void console.log("FAILED TO LOCATE ROOT: .layer .ui-standard-sidebar-view")}}class V2C_Layer extends BDV2.reactComponent{constructor(t){super(t)}componentDidMount(){$(window).on(`keyup.${this.props.id}`,t=>{27===t.which&&BDV2.reactDom.unmountComponentAtNode(this.refs.root.parentNode)}),$(`#${this.props.id}`).animate({opacity:1},{step:function(t){$(this).css("transform",`scale(${1.1-0.1*t}) translateZ(0px)`)},duration:200,done:()=>{$(`#${this.props.id}`).css("opacity","").css("transform","")}})}componentWillUnmount(){$(window).off(`keyup.${this.props.id}`),$(`#${this.props.id}`).animate({opacity:0},{step:function(t){$(this).css("transform",`scale(${1.1-0.1*t}) translateZ(0px)`)},duration:200,done:()=>{$(`#${this.props.rootId}`).remove()}}),$("[class*=\"layer-\"]").removeClass("publicServersOpen").animate({opacity:1},{step:function(t){$(this).css("transform",`scale(${0.07*t+0.93}) translateZ(0px)`)},duration:200,done:()=>{$("[class*=\"layer-\"]").css("opacity","").css("transform","")}})}componentWillMount(){$("[class*=\"layer-\"]").addClass("publicServersOpen").animate({opacity:0},{step:function(t){$(this).css("transform",`scale(${0.07*t+0.93}) translateZ(0px)`)},duration:200})}render(){return BDV2.react.createElement("div",{className:"layer bd-layer layer-3QrUeG",id:this.props.id,ref:"root",style:{opacity:0,transform:"scale(1.1) translateZ(0px)"}},this.props.children)}}class V2C_SidebarView extends BDV2.reactComponent{constructor(t){super(t)}render(){let{sidebar:t,content:a,tools:o}=this.props.children;return BDV2.react.createElement("div",{className:"ui-standard-sidebar-view"},BDV2.react.createElement("div",{className:"sidebar-region"},BDV2.react.createElement(V2Components.Scroller,{key:"sidebarScroller",ref:"sidebarScroller",sidebar:!0,fade:t.fade||!0,dark:t.dark||!0,children:t.component})),BDV2.react.createElement("div",{className:"content-region"},BDV2.react.createElement("div",{className:"content-transition-wrap"},BDV2.react.createElement("div",{className:"scrollerWrap-2lJEkd content-region-scroller-wrap scrollerThemed-2oenus themeGhost-28MSn0 scrollerTrack-1ZIpsv"},BDV2.react.createElement("div",{className:"scroller-2FKFPG content-region-scroller scroller",ref:"contentScroller"},BDV2.react.createElement("div",{className:"content-column default"},a.component),o.component)))))}}class V2_PublicServers{constructor(){}get component(){return BDV2.react.createElement(V2Components.Layer,{rootId:"pubslayerroot",id:"pubslayer",children:BDV2.react.createElement(V2C_PublicServers,{rootId:"pubslayerroot"})})}get root(){let t=document.getElementById("pubslayerroot");return t?t:this.injectRoot()?this.root:null}injectRoot(){return!!$(".layers, .layers-3iHuyZ").length&&($(".layers, .layers-3iHuyZ").append($("",{id:"pubslayerroot"})),!0)}render(){let t=this.root;return t?void BDV2.reactDom.render(this.component,t):void console.log("FAILED TO LOCATE ROOT: .layers")}get button(){let t=$("",{"class":BDV2.guildClasses.guild,id:"bd-pub-li",css:{height:"20px",display:settingsCookie["bda-gs-1"]?"":"none"}}).append($("",{"class":BDV2.guildClasses.guildInner,css:{height:"20px","border-radius":"4px"}}).append($("",{}).append($("",{text:"public",id:"bd-pub-button",css:{"line-height":"20px","font-size":"12px"},click:()=>{this.render()}}))));return t}initialize(){let t=$(`.${BDV2.guildClasses.guilds}>:first-child`);t.after(this.button)}}class V2C_ServerCard extends BDV2.reactComponent{constructor(t){super(t),this.props.server.iconUrl||(this.props.server.iconUrl=this.props.fallback),this.state={imageError:!1,joined:this.props.guildList.includes(this.props.server.identifier)}}render(){let{server:t}=this.props;return BDV2.react.createElement("div",{className:`card-3Qj_Yx cardPrimary-1Hv-to marginBottom8-AtZOdT bd-server-card${t.pinned?" bd-server-card-pinned":""}`},BDV2.react.createElement("img",{ref:"img",className:"bd-server-image",src:t.iconUrl,onError:this.handleError.bind(this)}),BDV2.react.createElement("div",{className:"flexChild-faoVW3 bd-server-content"},BDV2.react.createElement("div",{className:"flex-1xMQg5 flex-1O1GKY horizontal-1ae9ci horizontal-2EEEnY directionRow-3v3tfG noWrap-3jynv6 bd-server-header"},BDV2.react.createElement("h5",{className:"h5-18_1nd defaultColor-1_ajX0 margin-reset bd-server-name"},t.name),BDV2.react.createElement("h5",{className:"h5-18_1nd defaultColor-1_ajX0 margin-reset bd-server-member-count"},t.members," Members")),BDV2.react.createElement("div",{className:"flex-1xMQg5 flex-1O1GKY horizontal-1ae9ci horizontal-2EEEnY directionRow-3v3tfG noWrap-3jynv6"},BDV2.react.createElement("div",{className:"scrollerWrap-2lJEkd scrollerThemed-2oenus themeGhostHairline-DBD-2d scrollerFade-1Ijw5y bd-server-description-container"},BDV2.react.createElement("div",{className:"scroller-2FKFPG scroller bd-server-description"},t.description))),BDV2.react.createElement("div",{className:"flex-1xMQg5 flex-1O1GKY horizontal-1ae9ci horizontal-2EEEnY directionRow-3v3tfG noWrap-3jynv6 bd-server-footer"},BDV2.react.createElement("div",{className:"flexChild-faoVW3 bd-server-tags",style:{flex:"1 1 auto"}},t.categories.join(", ")),this.state.joined&&BDV2.react.createElement("button",{type:"button",className:"button-38aScr lookFilled-1Gx00P colorBrand-3pXr91 sizeMin-1mJd1x grow-q77ONN colorGreen-29iAKY",style:{minHeight:"12px",marginTop:"4px",backgroundColor:"#3ac15c"}},BDV2.react.createElement("div",{className:"ui-button-contents"},"Joined")),t.error&&BDV2.react.createElement("button",{type:"button",className:"button-38aScr lookFilled-1Gx00P colorBrand-3pXr91 sizeMin-1mJd1x grow-q77ONN disabled-9aF2ug",style:{minHeight:"12px",marginTop:"4px",backgroundColor:"#c13a3a"}},BDV2.react.createElement("div",{className:"ui-button-contents"},"Error")),!t.error&&!this.state.joined&&BDV2.react.createElement("button",{type:"button",className:"button-38aScr lookFilled-1Gx00P colorBrand-3pXr91 sizeMin-1mJd1x grow-q77ONN",style:{minHeight:"12px",marginTop:"4px"},onClick:()=>{this.join()}},BDV2.react.createElement("div",{className:"ui-button-contents"},"Join")))))}handleError(){this.props.server.iconUrl=this.props.fallback,this.setState({imageError:!0})}join(){this.props.join(this)}}class V2C_PublicServers extends BDV2.reactComponent{constructor(t){super(t),this.setInitialState(),this.close=this.close.bind(this),this.changeCategory=this.changeCategory.bind(this),this.search=this.search.bind(this),this.searchKeyDown=this.searchKeyDown.bind(this),this.checkConnection=this.checkConnection.bind(this),this.join=this.join.bind(this),this.connect=this.connect.bind(this),this.GuildStore=BDV2.WebpackModules.findByUniqueProperties(["getGuilds"]),this.AvatarDefaults=BDV2.WebpackModules.findByUniqueProperties(["getUserAvatarURL","DEFAULT_AVATARS"]),this.InviteActions=BDV2.WebpackModules.findByUniqueProperties(["acceptInvite"]),this.SortedGuildStore=BDV2.WebpackModules.findByUniqueProperties(["getSortedGuilds"])}componentDidMount(){this.checkConnection()}setInitialState(){this.state={selectedCategory:-1,title:"Loading...",loading:!0,servers:[],next:null,connection:{state:0,user:null}}}close(){BDV2.reactDom.unmountComponentAtNode(document.getElementById(this.props.rootId))}search(t,a){let o=this;$.ajax({method:"GET",url:`${o.endPoint}${t}${t?"&schema=new":"?schema=new"}`,success:d=>{let l=d.results.reduce((g,b)=>{return b.joined=!1,g.push(b),g},[]);a||(l=o.state.servers.concat(l));let u=d.size+d.from;d.next=`?from=${u}`,o.state.term&&(d.next+=`&term=${o.state.term}`),o.state.selectedCategory&&(d.next+=`&category=${o.categoryButtons[o.state.selectedCategory]}`),u>=d.total&&(u=d.total,d.next=null);let h=`Showing 1-${u} of ${d.total} results in ${o.categoryButtons[o.state.selectedCategory]}`;o.state.term&&(h+=` for ${o.state.term}`),o.setState({loading:!1,title:h,servers:l,next:d.next}),a&&(o.refs.sbv.refs.contentScroller.scrollTop=0)},error:()=>{o.setState({loading:!1,title:"Failed to load servers. Check console for details"})}})}join(t){return t.props.pinned?this.InviteActions.acceptInvite(t.props.invite_code):void $.ajax({method:"GET",url:`${this.joinEndPoint}/${t.props.server.identifier}`,headers:{Accept:"application/json;","Content-Type":"application/json;","x-discord-id":this.state.connection.user.id,"x-discord-token":this.state.connection.user.accessToken},crossDomain:!0,xhrFields:{withCredentials:!0},success:()=>{t.setState({joined:!0})}})}connect(){let t=this,a=t.windowOptions;a.x=Math.round(window.screenX+window.innerWidth/2-a.width/2),a.y=Math.round(window.screenY+window.innerHeight/2-a.height/2),t.joinWindow=new(window.require("electron").remote.BrowserWindow)(a);let o=window.location.hostname.split(".")[0],d=t.connectEndPoint+("canary"===o||"ptb"===o?`/${o}`:"")+"?betterDiscord";t.joinWindow.webContents.on("did-navigate",(l,u)=>{"https://join.discordservers.com/session"!=u||(t.joinWindow.close(),t.checkConnection())}),t.joinWindow.loadURL(d)}get windowOptions(){return{width:500,height:550,backgroundColor:"#282b30",show:!0,resizable:!1,maximizable:!1,minimizable:!1,alwaysOnTop:!0,frame:!1,center:!1}}get bdServer(){let a=this.SortedGuildStore.guildPositions,o=this.AvatarDefaults.DEFAULT_AVATARS;return BDV2.react.createElement(V2Components.ServerCard,{server:{name:"BetterDiscord",online:"7500+",members:"20000+",categories:["community","programming","support"],description:"Official BetterDiscord server for support etc",identifier:"86004744966914048",iconUrl:"https://cdn.discordapp.com/icons/86004744966914048/292e7f6bfff2b71dfd13e508a859aedd.webp",nativejoin:!0,invite_code:"0Tmfo5ZbORCRqbAd",pinned:!0},pinned:!0,join:this.join,guildList:a,fallback:o[Math.floor(5*Math.random())]})}get endPoint(){return"https://search.discordservers.com"}get joinEndPoint(){return"https://join.discordservers.com"}get connectEndPoint(){return"https://join.discordservers.com/connect"}checkConnection(){let t=this;try{$.ajax({method:"GET",url:`${t.joinEndPoint}/session`,headers:{Accept:"application/json;","Content-Type":"application/json;"},crossDomain:!0,xhrFields:{withCredentials:!0},success:a=>{t.setState({selectedCategory:0,connection:{state:2,user:a}}),t.search("",!0)},error:a=>{if(403===a.status||404===a.status)return void t.setState({title:"Not connected to discordservers.com!",loading:!0,selectedCategory:-1,connection:{state:1,user:null}})}})}catch(a){t.setState({title:"Not connected to discordservers.com!",loading:!0,selectedCategory:-1,connection:{state:1,user:null}})}}render(){return BDV2.react.createElement(V2Components.SidebarView,{ref:"sbv",children:this.component})}get component(){return{sidebar:{component:this.sidebar},content:{component:this.content},tools:{component:BDV2.react.createElement(V2Components.Tools,{key:"pt",ref:"tools",onClick:this.close})}}}get sidebar(){return BDV2.react.createElement("div",{className:"sidebar",key:"ps"},BDV2.react.createElement("div",{className:"ui-tab-bar SIDE"},BDV2.react.createElement("div",{className:"ui-tab-bar-header",style:{fontSize:"16px"}},"Public Servers"),BDV2.react.createElement(V2Components.TabBar.Separator,null),this.searchInput,BDV2.react.createElement(V2Components.TabBar.Separator,null),BDV2.react.createElement(V2Components.TabBar.Header,{text:"Categories"}),this.categoryButtons.map((t,a)=>{return BDV2.react.createElement(V2Components.TabBar.Item,{id:a,onClick:this.changeCategory,key:a,text:t,selected:this.state.selectedCategory===a})}),BDV2.react.createElement(V2Components.TabBar.Separator,null),this.footer,this.connection))}get searchInput(){return BDV2.react.createElement("div",{className:"ui-form-item"},BDV2.react.createElement("div",{className:"ui-text-input flex-vertical",style:{width:"172px",marginLeft:"10px"}},BDV2.react.createElement("input",{ref:"searchinput",onKeyDown:this.searchKeyDown,onChange:()=>{},type:"text",className:"input default",placeholder:"Search...",maxLength:"50"})))}searchKeyDown(t){let a=this;if(!(a.state.loading||13!==t.which)){a.setState({loading:!0,title:"Loading...",term:t.target.value});let o=`?term=${t.target.value}`;0!==a.state.selectedCategory&&(o+=`&category=${a.categoryButtons[a.state.selectedCategory]}`),a.search(o,!0)}}get categoryButtons(){return["All","FPS Games","MMO Games","Strategy Games","Sports Games","Puzzle Games","Retro Games","Party Games","Tabletop Games","Sandbox Games","Simulation Games","Community","Language","Programming","Other"]}changeCategory(t){let a=this;if(!a.state.loading)return a.refs.searchinput.value="",a.setState({loading:!0,selectedCategory:t,title:"Loading...",term:null}),0===t?void a.search("",!0):void a.search(`?category=${a.categoryButtons[t]}`,!0)}get content(){let t=this,a=this.SortedGuildStore.guildPositions,o=this.AvatarDefaults.DEFAULT_AVATARS;return 1===t.state.connection.state?t.notConnected:[BDV2.react.createElement("div",{ref:"content",key:"pc",className:"content-column default"},BDV2.react.createElement(V2Components.SettingsTitle,{text:t.state.title}),t.bdServer,t.state.servers.map(d=>{return BDV2.react.createElement(V2Components.ServerCard,{key:d.identifier,server:d,join:t.join,guildList:a,fallback:o[Math.floor(5*Math.random())]})}),t.state.next&&BDV2.react.createElement("button",{type:"button",onClick:()=>{t.state.loading||(t.setState({loading:!0}),t.search(t.state.next,!1))},className:"ui-button filled brand small grow",style:{width:"100%",marginTop:"10px",marginBottom:"10px"}},BDV2.react.createElement("div",{className:"ui-button-contents"},t.state.loading?"Loading":"Load More")),0