remove jquery and stuff

This commit is contained in:
Zack Rauen 2020-03-29 05:17:12 -04:00
parent dbcf2f9cef
commit ded3f96b07
33 changed files with 1444 additions and 1160 deletions

View File

@ -45,6 +45,7 @@
"Promise": false,
"ace": false,
"Reflect": false,
"Array": false,
"DiscordNative": false,
"self": "off",
"name": "off",

File diff suppressed because one or more lines are too long

2
js/main.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -2,34 +2,24 @@
export const minimumDiscordVersion = "0.0.306";
export const currentDiscordVersion = (window.DiscordNative && window.DiscordNative.remoteApp && window.DiscordNative.remoteApp.getVersion && window.DiscordNative.remoteApp.getVersion()) || "0.0.306";
export const minSupportedVersion = "0.3.0";
export const bbdVersion = "0.3.1";
export const bbdVersion = "0.3.2";
export const bbdChangelog = {
description: "There's some pretty big things in this one, and even bigger things coming. Most of this information is repeated from the `0.3.0` update just in case people hadn't seen the changelog yet.",
description: "More big things.",
changes: [
{
title: "What's New?",
items: [
"**RepoControls**-like controls are now integrated into BD and everyone can enjoy sorting and searching their plugins and themes.",
"BandagedBD has undergone an internal restructure--it's no longer one messy file but *several*. Users shouldn't see any breaking changes (hopefully).",
"**Plugin Developers:** You'll begin (or continue for some) to see deprecation notices for several BD globals. Please take these seriously and update your plugins to use `BdApi`, the globals **_will_** be removed. If you find the API to be missing something that you could do before, please message me in BD2's <#603225817262194699> chat.",
"**Also Plugin Developers:** There is now an option to enable React Developer Tools in the Developer Options in settings. Just make sure to have the extension installed in your local Chrome installation."
"**jQuery** is no longer used internally in BBD. This should speed things up and hopefully close some memory leaks.",
"**VoiceMode** was redone to act more like it used to."
]
},
{
title: "Minor Stuff",
title: "Improvements",
type: "improved",
items: [
"**BD Blue** is now the default accent color for BBD elements in settings. Themes can customize it easily through some new variables: `--bd-blue`, `--bd-blue-hover`, `--bd-blue-active`",
"Some improvements have been made to the plugins and themes pages that should hopefully prevent plugins from causing errors when displaying.",
"You can now review the changelog after you close it by clicking this button in settings: https://i.imgur.com/I3ZdAxG.png"
]
},
{
title: "Minor Stuff",
type: "fixed",
items: [
"**Quick Emote Menu** works again, sorry about that!",
"Fixed an issue with searching plugin/theme lists."
"**Copy Selector** option was revamped to be more consistent and functional.",
"**Emote Menu** has gone through some serious changes to be more efficient and less buggy.",
"Some speed improvements when entering the plugins and themes tabs."
]
}
]
@ -76,7 +66,7 @@ export const settings = {
"BetterTTV Emotes": {id: "bda-es-2", info: "Show BetterTTV Emotes", implemented: true, hidden: false, cat: "emote"},
"Emote Menu": {id: "bda-es-0", info: "Show Twitch/Favourite emotes in emote menu", implemented: true, hidden: false, cat: "emote"},
"Emoji Menu": {id: "bda-es-9", info: "Show Discord emoji menu", implemented: true, hidden: false, cat: "emote"},
"Emote Auto Capitalization": {id: "bda-es-4", info: "Autocapitalize emote commands", implemented: true, hidden: false, cat: "emote"},
"Emote Auto Capitalization": {id: "bda-es-4", info: "Autocapitalize emote commands", implemented: false, hidden: false, cat: "emote"},
"Show Names": {id: "bda-es-6", info: "Show emote names on hover", implemented: true, hidden: false, cat: "emote"},
"Show emote modifiers": {id: "bda-es-8", info: "Enable emote mods (flip, spin, pulse, spin2, spin3, 1spin, 2spin, 3spin, tr, bl, br, shake, shake2, shake3, flap)", implemented: true, hidden: false, cat: "emote"},
"Animate On Hover": {id: "fork-es-2", info: "Only animate the emote modifiers on hover", implemented: true, hidden: false, cat: "emote"}

View File

@ -30,7 +30,6 @@ import DataStore from "./modules/dataStore";
import emoteModule from "./modules/emoteModule";
import ContentManager from "./modules/contentManager";
import ClassNormalizer from "./modules/classNormalizer";
import quickEmoteMenu from "./modules/quickEmoteMenu";
deprecateGlobal("BDV2", BDV2);
deprecateGlobal("pluginModule", pluginModule);
@ -44,7 +43,6 @@ deprecateGlobal("ContentManager", ContentManager);
deprecateGlobal("ClassNormalizer", ClassNormalizer);
window.BdApi = BdApi;
window.quickEmoteMenu = quickEmoteMenu;
import Core from "./modules/core";
export default class CoreWrapper {

View File

@ -6,6 +6,7 @@ import DataStore from "./dataStore";
import pluginModule from "./pluginModule";
import themeModule from "./themeModule";
import settingsPanel from "./settingsPanel";
import DOM from "./domtools";
const BdApi = {
get React() { return BDV2.React; },
@ -34,26 +35,26 @@ BdApi.setWindowPreference = function(key, value) {
//id = id of element
//css = custom css
BdApi.injectCSS = function (id, css) {
$("head").append($("<style>", {id: Utils.escapeID(id), text: css}));
DOM.addStyle(DOM.escapeID(id), css);
};
//Clear css/remove any element
//id = id of element
BdApi.clearCSS = function (id) {
$("#" + Utils.escapeID(id)).remove();
DOM.removeStyle(DOM.escapeID(id));
};
//Inject CSS to document head
//id = id of element
//css = custom css
BdApi.linkJS = function (id, url) {
$("head").append($("<script>", {id: Utils.escapeID(id), src: url, type: "text/javascript"}));
DOM.addScript(DOM.escapeID(id), url);
};
//Clear css/remove any element
//id = id of element
BdApi.unlinkJS = function (id) {
$("#" + Utils.escapeID(id)).remove();
DOM.removeScript(DOM.escapeID(id));
};
//Get another plugin

View File

@ -113,8 +113,6 @@ export default new class ContentManager {
return out;
}
getContentRequire(type) {
const isPlugin = type === "plugin";
const self = this;

View File

@ -1,16 +1,15 @@
import {bdConfig, minSupportedVersion, bbdVersion, settingsCookie, bdpluginErrors, bdthemeErrors, bbdChangelog, defaultCookie, minimumDiscordVersion, currentDiscordVersion} from "../0globals";
import {bdConfig, minSupportedVersion, bbdVersion, settingsCookie, bdpluginErrors, bdthemeErrors, bbdChangelog, defaultCookie, currentDiscordVersion} from "../0globals";
import Utils from "./utils";
import emoteModule from "./emoteModule";
import quickEmoteMenu from "./quickEmoteMenu";
// import publicServersModule from "./publicServers";
// import voiceMode from "./voiceMode";
// import dMode from "./devMode";
import BDV2 from "./v2";
import settingsPanel from "./settingsPanel";
import pluginModule from "./pluginModule";
import themeModule from "./themeModule";
import DataStore from "./dataStore";
import WebpackModules from "./webpackModules";
import DOM from "./domtools";
import BDLogo from "../ui/bdLogo";
import TooltipWrap from "../ui/tooltipWrap";
@ -22,10 +21,10 @@ Core.prototype.setConfig = function(config) {
};
Core.prototype.init = async function() {
// if (currentDiscordVersion < minimumDiscordVersion) {
// Utils.alert("Not Supported", "BetterDiscord v" + bbdVersion + " does not support this old version (" + currentDiscordVersion + ") of Discord. Please update your Discord installation before proceeding.");
// return;
// }
if (!Array.prototype.flat) {
Utils.alert("Not Supported", "BetterDiscord v" + bbdVersion + " does not support this old version (" + currentDiscordVersion + ") of Discord. Please update your Discord installation before proceeding.");
return;
}
if (bdConfig.version < minSupportedVersion) {
Utils.alert("Not Supported", "BetterDiscord v" + bdConfig.version + " (your version)" + " is not supported by the latest js (" + bbdVersion + ").<br><br> Please download the latest version from <a href='https://github.com/rauenzi/BetterDiscordApp/releases/latest' target='_blank'>GitHub</a>");
@ -52,43 +51,33 @@ Core.prototype.init = async function() {
Utils.log("Startup", "Initializing Settings");
this.initSettings();
// emoteModule = new EmoteModule();
// quickEmoteMenu = new QuickEmoteMenu();
Utils.log("Startup", "Initializing EmoteModule");
window.emotePromise = emoteModule.init().then(() => {
emoteModule.initialized = true;
Utils.log("Startup", "Initializing QuickEmoteMenu");
quickEmoteMenu.init();
});
// publicServersModule = new V2_PublicServers();
// voiceMode = new VoiceMode();
// dMode = new devMode();
this.injectExternals();
await this.checkForGuilds();
BDV2.initialize();
Utils.log("Startup", "Updating Settings");
// settingsPanel = new V2_SettingsPanel();
settingsPanel.initializeSettings();
Utils.log("Startup", "Loading Plugins");
// pluginModule = new PluginModule();
pluginModule.loadPlugins();
Utils.log("Startup", "Loading Themes");
// themeModule = new ThemeModule();
themeModule.loadThemes();
$("#customcss").detach().appendTo(document.head);
DOM.addStyle("customcss", atob(DataStore.getBDData("bdcustomcss")));
window.addEventListener("beforeunload", function() {
if (settingsCookie["bda-dc-0"]) document.querySelector(".btn.btn-disconnect").click();
});
emoteModule.autoCapitalize();
Utils.log("Startup", "Removing Loading Icon");
if (document.getElementsByClassName("bd-loaderv2").length) document.getElementsByClassName("bd-loaderv2")[0].remove();
Utils.log("Startup", "Initializing Main Observer");
@ -124,14 +113,13 @@ Core.prototype.checkForGuilds = function() {
else if (timesChecked >= 50) return resolve(bdConfig.deferLoaded = true);
setTimeout(checkForGuilds, 100);
};
$(document).ready(function () {
setTimeout(checkForGuilds, 100);
});
if (document.readyState != "loading") setTimeout(checkForGuilds, 100);
document.addEventListener("DOMContentLoaded", () => {setTimeout(checkForGuilds, 100);});
});
};
Core.prototype.injectExternals = async function() {
await Utils.injectJs("https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.9/ace.js");
await DOM.addScript("ace-script", "https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.9/ace.js");
if (window.require.original) window.require = window.require.original;
};
@ -143,7 +131,6 @@ Core.prototype.initSettings = function () {
}
else {
settingsPanel.loadSettings();
$("<style id=\"customcss\">").text(atob(DataStore.getBDData("bdcustomcss"))).appendTo(document.head);
for (const setting in defaultCookie) {
if (settingsCookie[setting] == undefined) {
settingsCookie[setting] = defaultCookie[setting];

View File

@ -1,89 +1,104 @@
import {settingsCookie} from "../0globals";
import BDV2 from "./v2";
import DOM from "./domtools";
function devMode() {}
export default new class DevMode {
constructor() {
this.debugListener = this.debugListener.bind(this);
this.copySelectorListener = this.copySelectorListener.bind(this);
}
devMode.prototype.enable = function(selectorMode) {
const self = this;
this.disable();
$(document).on("keydown.bdDevmode", function(e) {
if (e.which === 119 || e.which == 118) {//F8
start() {
this.startDebugListener();
if (settingsCookie["fork-dm-1"]) this.startCopySelector();
}
stop() {
this.stopDebugListener();
this.stopCopySelector();
}
startDebugListener() {
this.stopDebugListener();
document.addEventListener("keydown", this.debugListener);
}
stopDebugListener() {
document.removeEventListener("keydown", this.debugListener);
}
startCopySelector() {
this.stopCopySelector();
document.addEventListener("contextmenu", this.copySelectorListener);
}
stopCopySelector() {
document.removeEventListener("contextmenu", this.copySelectorListener);
}
debugListener(e) {
if (e.which === 119 || e.which == 118) {//F8
console.log("%c[%cDevMode%c] %cBreak/Resume", "color: red;", "color: #303030; font-weight:700;", "color:red;", "");
debugger; // eslint-disable-line no-debugger
e.preventDefault();
e.stopImmediatePropagation();
}
});
}
if (!selectorMode) return;
$(document).on("contextmenu.bdDevmode", function(e) {
self.lastSelector = self.getSelector(e.toElement);
function attach() {
let cm = $(".contextMenu-HLZMGh");
if (cm.length <= 0) {
cm = $("<div class=\"contextMenu-HLZMGh bd-context-menu\"></div>");
cm.addClass($(".app, .app-2rEoOp").hasClass("theme-dark") ? "theme-dark" : "theme-light");
cm.appendTo(".app, .app-2rEoOp");
cm.css("top", e.clientY);
cm.css("left", e.clientX);
$(document).on("click.bdDevModeCtx", () => {
cm.remove();
$(document).off(".bdDevModeCtx");
});
$(document).on("contextmenu.bdDevModeCtx", () => {
cm.remove();
$(document).off(".bdDevModeCtx");
});
$(document).on("keyup.bdDevModeCtx", (e) => {
if (e.keyCode === 27) {
cm.remove();
$(document).off(".bdDevModeCtx");
}
});
copySelectorListener(e) {
e.stopPropagation();
const selector = this.getSelector(e.target);
function attach() {
let cm = DOM.query(".contextMenu-HLZMGh");
if (!cm) {
const container = DOM.query("#app-mount");
const cmWrap = DOM.createElement(`<div class="layer-v9HyYc da-layer">`);
cm = DOM.createElement(`<div class="contextMenu-HLZMGh da-contextMenu bd-context-menu"></div>`);
cmWrap.append(cm);
container.append(cmWrap);
cmWrap.style.top = e.clientY + "px";
cmWrap.style.left = e.clientX + "px";
cmWrap.style.zIndex = "1002";
const removeCM = function(e) {
if (e.keyCode && e.keyCode !== 27) return;
cmWrap.remove();
document.removeEventListener("click", removeCM);
document.removeEventListener("contextmenu", removeCM);
document.removeEventListener("keyup", removeCM);
};
document.addEventListener("click", removeCM);
document.addEventListener("contextmenu", removeCM);
document.addEventListener("keyup", removeCM);
}
const cmo = $("<div/>", {
"class": "itemGroup-1tL0uz"
const cmg = DOM.createElement(`<div class="itemGroup-1tL0uz da-itemGroup">`);
const cmi = DOM.createElement(`<div class="item-1Yvehc itemBase-tz5SeC da-item da-itemBase clickable-11uBi- da-clickable">`);
cmi.append(DOM.createElement(`<div class="label-JWQiNe da-label">Copy Selector</div>`));
cmi.addEventListener("click", () => {
BDV2.NativeModule.copy(selector);
cm.style.display = "none";
});
const cmi = $("<div/>", {
"class": "item-1Yvehc",
"click": function() {
BDV2.NativeModule.copy(self.lastSelector);
cm.hide();
}
}).append($("<span/>", {text: "Copy Selector"}));
cmo.append(cmi);
cm.append(cmo);
if (cm.hasClass("undefined")) cm.css("top", "-=" + cmo.outerHeight());
}
cmg.append(cmi);
cm.append(cmg);
}
setImmediate(attach);
setImmediate(attach);
}
e.stopPropagation();
});
};
getSelector(element) {
if (element.id) return `#${element.id}`;
const rules = this.getRules(element);
const latestRule = rules[rules.length - 1];
if (latestRule) return latestRule.selectorText;
else if (element.classList.length) return `.${Array.from(element.classList).join(".")}`;
return `.${Array.from(element.parentElement.classList).join(".")}`;
}
devMode.prototype.getRules = function(element, css = element.ownerDocument.styleSheets) {
//if (window.getMatchedCSSRules) return window.getMatchedCSSRules(element);
const sheets = [...css].filter(s => !s.href || !s.href.includes("BetterDiscordApp"));
const rules = sheets.map(s => [...(s.cssRules || [])]).flat();
const elementRules = rules.filter(r => r && r.selectorText && element.matches(r.selectorText) && r.style.length && r.selectorText.split(", ").length < 8 && !r.selectorText.split(", ").includes("*"));
return elementRules;
};
devMode.prototype.getSelector = function(element) {
if (element.id) return `#${element.id}`;
const rules = this.getRules(element);
const latestRule = rules[rules.length - 1];
if (latestRule) return latestRule.selectorText;
else if (element.classList.length) return `.${Array.from(element.classList).join(".")}`;
return `.${Array.from(element.parentElement.classList).join(".")}`;
};
devMode.prototype.disable = function() {
$(document).off("keydown.bdDevmode");
$(document).off("contextmenu.bdDevmode");
$(document).off("contextmenu.bdDevModeCtx");
};
export default new devMode();
getRules(element, css = element.ownerDocument.styleSheets) {
//if (window.getMatchedCSSRules) return window.getMatchedCSSRules(element);
const sheets = [...css].filter(s => !s.href || !s.href.includes("BetterDiscordApp"));
const rules = sheets.map(s => [...(s.cssRules || [])]).flat();
const elementRules = rules.filter(r => r && r.selectorText && element.matches(r.selectorText) && r.style.length && r.selectorText.split(", ").length < 8 && !r.selectorText.split(", ").includes("*"));
return elementRules;
}
};

753
src/modules/domtools.js Normal file
View File

@ -0,0 +1,753 @@
/**
* Copyright 2018 Zachary 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 in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* From: https://github.com/rauenzi/BDPluginLibrary
*/
/**
* @interface
* @name Offset
* @property {number} top - Top offset of the target element.
* @property {number} right - Right offset of the target element.
* @property {number} bottom - Bottom offset of the target element.
* @property {number} left - Left offset of the target element.
* @property {number} height - Outer height of the target element.
* @property {number} width - Outer width of the target element.
*/
/**
* Function that automatically removes added listener.
* @callback module:DOMTools~CancelListener
*/
export default class DOMTools {
static escapeID(id) {
return id.replace(/^[^a-z]+|[^\w-]+/gi, "-");
}
/**
* Adds a style to the document.
* @param {string} id - identifier to use as the element id
* @param {string} css - css to add to the document
*/
static addStyle(id, css) {
document.head.append(DOMTools.createElement(`<style id="${id}">${css}</style>`));
}
/**
* Removes a style from the document.
* @param {string} id - original identifier used
*/
static removeStyle(id) {
const element = document.getElementById(id);
if (element) element.remove();
}
/**
* Adds/requires a remote script to be loaded
* @param {string} id - identifier to use for this script
* @param {string} url - url from which to load the script
* @returns {Promise} promise that resolves when the script is loaded
*/
static addScript(id, url) {
return new Promise(resolve => {
const script = document.createElement("script");
script.id = id;
script.src = url;
script.type = "text/javascript";
script.onload = resolve;
document.head.append(script);
});
}
/**
* Removes a remote script from the document.
* @param {string} id - original identifier used
*/
static removeScript(id) {
id = this.escapeID(id);
const element = document.getElementById(id);
if (element) element.remove();
}
// https://javascript.info/js-animation
static animate({timing = _ => _, update, duration}) {
const start = performance.now();
requestAnimationFrame(function animate(time) {
// timeFraction goes from 0 to 1
let timeFraction = (time - start) / duration;
if (timeFraction > 1) timeFraction = 1;
// calculate the current animation state
const progress = timing(timeFraction);
update(progress); // draw it
if (timeFraction < 1) {
requestAnimationFrame(animate);
}
});
}
/**
* This is my shit version of not having to use `$` from jQuery. Meaning
* that you can pass a selector and it will automatically run {@link module:DOMTools.query}.
* It also means that you can pass a string of html and it will perform and return `parseHTML`.
* @see module:DOMTools.parseHTML
* @see module:DOMTools.query
* @param {string} selector - Selector to query or HTML to parse
* @returns {(DocumentFragment|NodeList|HTMLElement)} - Either the result of `parseHTML` or `query`
*/
static Q(selector) {
const element = this.parseHTML(selector);
const isHTML = element instanceof NodeList ? Array.from(element).some(n => n.nodeType === 1) : element.nodeType === 1;
if (isHTML) return element;
return this.query(selector);
}
/**
* Essentially a shorthand for `document.querySelector`. If the `baseElement` is not provided
* `document` is used by default.
* @param {string} selector - Selector to query
* @param {Element} [baseElement] - Element to base the query from
* @returns {(Element|null)} - The found element or null if not found
*/
static query(selector, baseElement) {
if (!baseElement) baseElement = document;
return baseElement.querySelector(selector);
}
/**
* Essentially a shorthand for `document.querySelectorAll`. If the `baseElement` is not provided
* `document` is used by default.
* @param {string} selector - Selector to query
* @param {Element} [baseElement] - Element to base the query from
* @returns {Array<Element>} - Array of all found elements
*/
static queryAll(selector, baseElement) {
if (!baseElement) baseElement = document;
return baseElement.querySelectorAll(selector);
}
/**
* Parses a string of HTML and returns the results. If the second parameter is true,
* the parsed HTML will be returned as a document fragment {@see https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment}.
* This is extremely useful if you have a list of elements at the top level, they can then be appended all at once to another node.
*
* If the second parameter is false, then the return value will be the list of parsed
* nodes and there were multiple top level nodes, otherwise the single node is returned.
* @param {string} html - HTML to be parsed
* @param {boolean} [fragment=false] - Whether or not the return should be the raw `DocumentFragment`
* @returns {(DocumentFragment|NodeList|HTMLElement)} - The result of HTML parsing
*/
static parseHTML(html, fragment = false) {
const template = document.createElement("template");
template.innerHTML = html;
const node = template.content.cloneNode(true);
if (fragment) return node;
return node.childNodes.length > 1 ? node.childNodes : node.childNodes[0];
}
/** Alternate name for {@link module:DOMTools.parseHTML} */
static createElement(html, fragment = false) {return this.parseHTML(html, fragment);}
/**
* Takes a string of html and escapes it using the brower's own escaping mechanism.
* @param {String} html - html to be escaped
*/
static escapeHTML(html) {
const textNode = document.createTextNode("");
const spanElement = document.createElement("span");
spanElement.append(textNode);
textNode.nodeValue = html;
return spanElement.innerHTML;
}
/**
* Adds a list of classes from the target element.
* @param {Element} element - Element to edit classes of
* @param {...string} classes - Names of classes to add
* @returns {Element} - `element` to allow for chaining
*/
static addClass(element, ...classes) {
classes = classes.flat().filter(c => c);
for (let c = 0; c < classes.length; c++) classes[c] = classes[c].toString().split(" ");
classes = classes.flat().filter(c => c);
element.classList.add(...classes);
return element;
}
/**
* Removes a list of classes from the target element.
* @param {Element} element - Element to edit classes of
* @param {...string} classes - Names of classes to remove
* @returns {Element} - `element` to allow for chaining
*/
static removeClass(element, ...classes) {
for (let c = 0; c < classes.length; c++) classes[c] = classes[c].toString().split(" ");
classes = classes.flat().filter(c => c);
element.classList.remove(...classes);
return element;
}
/**
* When only one argument is present: Toggle class value;
* i.e., if class exists then remove it and return false, if not, then add it and return true.
* When a second argument is present:
* If the second argument evaluates to true, add specified class value, and if it evaluates to false, remove it.
* @param {Element} element - Element to edit classes of
* @param {string} classname - Name of class to toggle
* @param {boolean} [indicator] - Optional indicator for if the class should be toggled
* @returns {Element} - `element` to allow for chaining
*/
static toggleClass(element, classname, indicator) {
classname = classname.toString().split(" ").filter(c => c);
if (typeof(indicator) !== "undefined") classname.forEach(c => element.classList.toggle(c, indicator));
else classname.forEach(c => element.classList.toggle(c));
return element;
}
/**
* Checks if an element has a specific class
* @param {Element} element - Element to edit classes of
* @param {string} classname - Name of class to check
* @returns {boolean} - `true` if the element has the class, `false` otherwise.
*/
static hasClass(element, classname) {
return classname.toString().split(" ").filter(c => c).every(c => element.classList.contains(c));
}
/**
* Replaces one class with another
* @param {Element} element - Element to edit classes of
* @param {string} oldName - Name of class to replace
* @param {string} newName - New name for the class
* @returns {Element} - `element` to allow for chaining
*/
static replaceClass(element, oldName, newName) {
element.classList.replace(oldName, newName);
return element;
}
/**
* Appends `thisNode` to `thatNode`
* @param {Node} thisNode - Node to be appended to another node
* @param {Node} thatNode - Node for `thisNode` to be appended to
* @returns {Node} - `thisNode` to allow for chaining
*/
static appendTo(thisNode, thatNode) {
if (typeof(thatNode) == "string") thatNode = this.query(thatNode);
if (!thatNode) return null;
thatNode.append(thisNode);
return thisNode;
}
/**
* Prepends `thisNode` to `thatNode`
* @param {Node} thisNode - Node to be prepended to another node
* @param {Node} thatNode - Node for `thisNode` to be prepended to
* @returns {Node} - `thisNode` to allow for chaining
*/
static prependTo(thisNode, thatNode) {
if (typeof(thatNode) == "string") thatNode = this.query(thatNode);
if (!thatNode) return null;
thatNode.prepend(thisNode);
return thisNode;
}
/**
* Insert after a specific element, similar to jQuery's `thisElement.insertAfter(otherElement)`.
* @param {Node} thisNode - The node to insert
* @param {Node} targetNode - Node to insert after in the tree
* @returns {Node} - `thisNode` to allow for chaining
*/
static insertAfter(thisNode, targetNode) {
targetNode.parentNode.insertBefore(thisNode, targetNode.nextSibling);
return thisNode;
}
/**
* Insert after a specific element, similar to jQuery's `thisElement.after(newElement)`.
* @param {Node} thisNode - The node to insert
* @param {Node} newNode - Node to insert after in the tree
* @returns {Node} - `thisNode` to allow for chaining
*/
static after(thisNode, newNode) {
thisNode.parentNode.insertBefore(newNode, thisNode.nextSibling);
return thisNode;
}
/**
* Gets the next sibling element that matches the selector.
* @param {Element} element - Element to get the next sibling of
* @param {string} [selector=""] - Optional selector
* @returns {Element} - The sibling element
*/
static next(element, selector = "") {
return selector ? element.querySelector("+ " + selector) : element.nextElementSibling;
}
/**
* Gets all subsequent siblings.
* @param {Element} element - Element to get next siblings of
* @returns {NodeList} - The list of siblings
*/
static nextAll(element) {
return element.querySelectorAll("~ *");
}
/**
* Gets the subsequent siblings until an element matches the selector.
* @param {Element} element - Element to get the following siblings of
* @param {string} selector - Selector to stop at
* @returns {Array<Element>} - The list of siblings
*/
static nextUntil(element, selector) {
const next = [];
while (element.nextElementSibling && !element.nextElementSibling.matches(selector)) next.push(element = element.nextElementSibling);
return next;
}
/**
* Gets the previous sibling element that matches the selector.
* @param {Element} element - Element to get the previous sibling of
* @param {string} [selector=""] - Optional selector
* @returns {Element} - The sibling element
*/
static previous(element, selector = "") {
const previous = element.previousElementSibling;
if (selector) return previous && previous.matches(selector) ? previous : null;
return previous;
}
/**
* Gets all preceeding siblings.
* @param {Element} element - Element to get preceeding siblings of
* @returns {NodeList} - The list of siblings
*/
static previousAll(element) {
const previous = [];
while (element.previousElementSibling) previous.push(element = element.previousElementSibling);
return previous;
}
/**
* Gets the preceeding siblings until an element matches the selector.
* @param {Element} element - Element to get the preceeding siblings of
* @param {string} selector - Selector to stop at
* @returns {Array<Element>} - The list of siblings
*/
static previousUntil(element, selector) {
const previous = [];
while (element.previousElementSibling && !element.previousElementSibling.matches(selector)) previous.push(element = element.previousElementSibling);
return previous;
}
/**
* Find which index in children a certain node is. Similar to jQuery's `$.index()`
* @param {HTMLElement} node - The node to find its index in parent
* @returns {number} Index of the node
*/
static indexInParent(node) {
const children = node.parentNode.childNodes;
let num = 0;
for (let i = 0; i < children.length; i++) {
if (children[i] == node) return num;
if (children[i].nodeType == 1) num++;
}
return -1;
}
/** Shorthand for {@link module:DOMTools.indexInParent} */
static index(node) {return this.indexInParent(node);}
/**
* Gets the parent of the element if it matches the selector,
* otherwise returns null.
* @param {Element} element - Element to get parent of
* @param {string} [selector=""] - Selector to match parent
* @returns {(Element|null)} - The sibling element or null
*/
static parent(element, selector = "") {
return !selector || element.parentElement.matches(selector) ? element.parentElement : null;
}
/**
* Gets all children of Element that match the selector if provided.
* @param {Element} element - Element to get all children of
* @param {string} selector - Selector to match the children to
* @returns {Array<Element>} - The list of children
*/
static findChild(element, selector) {
return element.querySelector(":scope > " + selector);
}
/**
* Gets all children of Element that match the selector if provided.
* @param {Element} element - Element to get all children of
* @param {string} selector - Selector to match the children to
* @returns {Array<Element>} - The list of children
*/
static findChildren(element, selector) {
return element.querySelectorAll(":scope > " + selector);
}
/**
* Gets all ancestors of Element that match the selector if provided.
* @param {Element} element - Element to get all parents of
* @param {string} [selector=""] - Selector to match the parents to
* @returns {Array<Element>} - The list of parents
*/
static parents(element, selector = "") {
const parents = [];
if (selector) while (element.parentElement && element.parentElement.closest(selector)) parents.push(element = element.parentElement.closest(selector));
else while (element.parentElement) parents.push(element = element.parentElement);
return parents;
}
/**
* Gets the ancestors until an element matches the selector.
* @param {Element} element - Element to get the ancestors of
* @param {string} selector - Selector to stop at
* @returns {Array<Element>} - The list of parents
*/
static parentsUntil(element, selector) {
const parents = [];
while (element.parentElement && !element.parentElement.matches(selector)) parents.push(element = element.parentElement);
return parents;
}
/**
* Gets all siblings of the element that match the selector.
* @param {Element} element - Element to get all siblings of
* @param {string} [selector="*"] - Selector to match the siblings to
* @returns {Array<Element>} - The list of siblings
*/
static siblings(element, selector = "*") {
return Array.from(element.parentElement.children).filter(e => e != element && e.matches(selector));
}
/**
* Sets or gets css styles for a specific element. If `value` is provided
* then it sets the style and returns the element to allow for chaining,
* otherwise returns the style.
* @param {Element} element - Element to set the CSS of
* @param {string} attribute - Attribute to get or set
* @param {string} [value] - Value to set for attribute
* @returns {Element|string} - When setting a value, element is returned for chaining, otherwise the value is returned.
*/
static css(element, attribute, value) {
if (typeof(value) == "undefined") return global.getComputedStyle(element)[attribute];
element.style[attribute] = value;
return element;
}
/**
* Sets or gets the width for a specific element. If `value` is provided
* then it sets the width and returns the element to allow for chaining,
* otherwise returns the width.
* @param {Element} element - Element to set the CSS of
* @param {string} [value] - Width to set
* @returns {Element|string} - When setting a value, element is returned for chaining, otherwise the value is returned.
*/
static width(element, value) {
if (typeof(value) == "undefined") return parseInt(getComputedStyle(element).width);
element.style.width = value;
return element;
}
/**
* Sets or gets the height for a specific element. If `value` is provided
* then it sets the height and returns the element to allow for chaining,
* otherwise returns the height.
* @param {Element} element - Element to set the CSS of
* @param {string} [value] - Height to set
* @returns {Element|string} - When setting a value, element is returned for chaining, otherwise the value is returned.
*/
static height(element, value) {
if (typeof(value) == "undefined") return parseInt(getComputedStyle(element).height);
element.style.height = value;
return element;
}
/**
* Sets the inner text of an element if given a value, otherwise returns it.
* @param {Element} element - Element to set the text of
* @param {string} [text] - Content to set
* @returns {string} - Either the string set by this call or the current text content of the node.
*/
static text(element, text) {
if (typeof(text) == "undefined") return element.textContent;
return element.textContent = text;
}
/**
* Returns the innerWidth of the element.
* @param {Element} element - Element to retrieve inner width of
* @return {number} - The inner width of the element.
*/
static innerWidth(element) {
return element.clientWidth;
}
/**
* Returns the innerHeight of the element.
* @param {Element} element - Element to retrieve inner height of
* @return {number} - The inner height of the element.
*/
static innerHeight(element) {
return element.clientHeight;
}
/**
* Returns the outerWidth of the element.
* @param {Element} element - Element to retrieve outer width of
* @return {number} - The outer width of the element.
*/
static outerWidth(element) {
return element.offsetWidth;
}
/**
* Returns the outerHeight of the element.
* @param {Element} element - Element to retrieve outer height of
* @return {number} - The outer height of the element.
*/
static outerHeight(element) {
return element.offsetHeight;
}
/**
* Gets the offset of the element in the page.
* @param {Element} element - Element to get offset of
* @return {Offset} - The offset of the element
*/
static offset(element) {
return element.getBoundingClientRect();
}
static get listeners() { return this._listeners || (this._listeners = {}); }
/**
* This is similar to jQuery's `on` function and can *hopefully* be used in the same way.
*
* Rather than attempt to explain, I'll show some example usages.
*
* The following will add a click listener (in the `myPlugin` namespace) to `element`.
* `DOMTools.on(element, "click.myPlugin", () => {console.log("clicked!");});`
*
* The following will add a click listener (in the `myPlugin` namespace) to `element` that only fires when the target is a `.block` element.
* `DOMTools.on(element, "click.myPlugin", ".block", () => {console.log("clicked!");});`
*
* The following will add a click listener (without namespace) to `element`.
* `DOMTools.on(element, "click", () => {console.log("clicked!");});`
*
* The following will add a click listener (without namespace) to `element` that only fires once.
* `const cancel = DOMTools.on(element, "click", () => {console.log("fired!"); cancel();});`
*
* @param {Element} element - Element to add listener to
* @param {string} event - Event to listen to with option namespace (e.g. "event.namespace")
* @param {(string|callable)} delegate - Selector to run on element to listen to
* @param {callable} [callback] - Function to fire on event
* @returns {module:DOMTools~CancelListener} - A function that will undo the listener
*/
static on(element, event, delegate, callback) {
const [type, namespace] = event.split(".");
const hasDelegate = delegate && callback;
if (!callback) callback = delegate;
const eventFunc = !hasDelegate ? callback : function(event) {
if (event.target.matches(delegate)) {
callback(event);
}
};
element.addEventListener(type, eventFunc);
const cancel = () => {
element.removeEventListener(type, eventFunc);
};
if (namespace) {
if (!this.listeners[namespace]) this.listeners[namespace] = [];
const newCancel = () => {
cancel();
this.listeners[namespace].splice(this.listeners[namespace].findIndex(l => l.event == type && l.element == element), 1);
};
this.listeners[namespace].push({
event: type,
element: element,
cancel: newCancel
});
return newCancel;
}
return cancel;
}
/**
* Functionality for this method matches {@link module:DOMTools.on} but automatically cancels itself
* and removes the listener upon the first firing of the desired event.
*
* @param {Element} element - Element to add listener to
* @param {string} event - Event to listen to with option namespace (e.g. "event.namespace")
* @param {(string|callable)} delegate - Selector to run on element to listen to
* @param {callable} [callback] - Function to fire on event
* @returns {module:DOMTools~CancelListener} - A function that will undo the listener
*/
static once(element, event, delegate, callback) {
const [type, namespace] = event.split(".");
const hasDelegate = delegate && callback;
if (!callback) callback = delegate;
const eventFunc = !hasDelegate ? function(event) {
callback(event);
element.removeEventListener(type, eventFunc);
} : function(event) {
if (!event.target.matches(delegate)) return;
callback(event);
element.removeEventListener(type, eventFunc);
};
element.addEventListener(type, eventFunc);
const cancel = () => {
element.removeEventListener(type, eventFunc);
};
if (namespace) {
if (!this.listeners[namespace]) this.listeners[namespace] = [];
const newCancel = () => {
cancel();
this.listeners[namespace].splice(this.listeners[namespace].findIndex(l => l.event == type && l.element == element), 1);
};
this.listeners[namespace].push({
event: type,
element: element,
cancel: newCancel
});
return newCancel;
}
return cancel;
}
static __offAll(event, element) {
const [type, namespace] = event.split(".");
let matchFilter = listener => listener.event == type, defaultFilter = _ => _;
if (element) matchFilter = l => l.event == type && l.element == element, defaultFilter = l => l.element == element;
const listeners = this.listeners[namespace] || [];
const list = type ? listeners.filter(matchFilter) : listeners.filter(defaultFilter);
for (let c = 0; c < list.length; c++) list[c].cancel();
}
/**
* This is similar to jQuery's `off` function and can *hopefully* be used in the same way.
*
* Rather than attempt to explain, I'll show some example usages.
*
* The following will remove a click listener called `onClick` (in the `myPlugin` namespace) from `element`.
* `DOMTools.off(element, "click.myPlugin", onClick);`
*
* The following will remove a click listener called `onClick` (in the `myPlugin` namespace) from `element` that only fired when the target is a `.block` element.
* `DOMTools.off(element, "click.myPlugin", ".block", onClick);`
*
* The following will remove a click listener (without namespace) from `element`.
* `DOMTools.off(element, "click", onClick);`
*
* The following will remove all listeners in namespace `myPlugin` from `element`.
* `DOMTools.off(element, ".myPlugin");`
*
* The following will remove all click listeners in namespace `myPlugin` from *all elements*.
* `DOMTools.off("click.myPlugin");`
*
* The following will remove all listeners in namespace `myPlugin` from *all elements*.
* `DOMTools.off(".myPlugin");`
*
* @param {(Element|string)} element - Element to remove listener from
* @param {string} [event] - Event to listen to with option namespace (e.g. "event.namespace")
* @param {(string|callable)} [delegate] - Selector to run on element to listen to
* @param {callable} [callback] - Function to fire on event
* @returns {Element} - The original element to allow for chaining
*/
static off(element, event, delegate, callback) {
if (typeof(element) == "string") return this.__offAll(element);
const [type, namespace] = event.split(".");
if (namespace) return this.__offAll(event, element);
const hasDelegate = delegate && callback;
if (!callback) callback = delegate;
const eventFunc = !hasDelegate ? callback : function(event) {
if (event.target.matches(delegate)) {
callback(event);
}
};
element.removeEventListener(type, eventFunc);
return element;
}
/**
* Adds a listener for when the node is added/removed from the document body.
* The listener is automatically removed upon firing.
* @param {HTMLElement} node - node to wait for
* @param {callable} callback - function to be performed on event
* @param {boolean} onMount - determines if it should fire on Mount or on Unmount
*/
static onMountChange(node, callback, onMount = true) {
const wrappedCallback = () => {
this.observer.unsubscribe(wrappedCallback);
callback();
};
this.observer.subscribe(wrappedCallback, mutation => {
const nodes = Array.from(onMount ? mutation.addedNodes : mutation.removedNodes);
const directMatch = nodes.indexOf(node) > -1;
const parentMatch = nodes.some(parent => parent.contains(node));
return directMatch || parentMatch;
});
return node;
}
/** Shorthand for {@link module:DOMTools.onMountChange} with third parameter `true` */
static onMount(node, callback) { return this.onMountChange(node, callback); }
/** Shorthand for {@link module:DOMTools.onMountChange} with third parameter `false` */
static onUnmount(node, callback) { return this.onMountChange(node, callback, false); }
/** Alias for {@link module:DOMTools.onMount} */
static onAdded(node, callback) { return this.onMount(node, callback); }
/** Alias for {@link module:DOMTools.onUnmount} */
static onRemoved(node, callback) { return this.onUnmount(node, callback, false); }
/**
* Helper function which combines multiple elements into one parent element
* @param {Array<HTMLElement>} elements - array of elements to put into a single parent
*/
static wrap(elements) {
const domWrapper = this.parseHTML(`<div class="dom-wrapper"></div>`);
for (let e = 0; e < elements.length; e++) domWrapper.appendChild(elements[e]);
return domWrapper;
}
/**
* Resolves the node to an HTMLElement. This is mainly used by library modules.
* @param {(jQuery|Element)} node - node to resolve
*/
static resolveElement(node) {
if (!(node instanceof jQuery) && !(node instanceof Element)) return undefined;
return node instanceof jQuery ? node[0] : node;
}
}

View File

@ -122,7 +122,6 @@ EmoteModule.prototype.init = async function () {
};
EmoteModule.prototype.disable = function() {
this.disableAutoCapitalize();
if (this.cancelEmoteRender) return;
this.cancelEmoteRender();
this.cancelEmoteRender = null;
@ -247,42 +246,11 @@ EmoteModule.prototype.downloadEmotes = function(emoteMeta) {
EmoteModule.prototype.getBlacklist = function () {
return new Promise(resolve => {
$.getJSON(`https://rauenzi.github.io/BetterDiscordApp/data/emotefilter.json`, function (data) {
resolve(bemotes.concat(data.blacklist));
require("request").get({url: "https://rauenzi.github.io/BetterDiscordApp/data/emotefilter.json", json: true}, function (err, resp, data) {
if (err) return resolve(bemotes);
resolve(bemotes.splice(0, 0, ...data.blacklist));
});
});
};
EmoteModule.prototype.autoCapitalize = function () {
if (!settingsCookie["bda-es-4"] || this.autoCapitalizeActive) return;
$("body").on("keyup.bdac change.bdac paste.bdac", $(".channelTextArea-rNsIhG textarea:first"), () => {
const text = $(".channelTextArea-rNsIhG textarea:first").val();
if (text == undefined) return;
const lastWord = text.split(" ").pop();
if (lastWord.length > 3) {
if (lastWord == "danSgame") return;
const ret = this.capitalize(lastWord.toLowerCase());
if (ret !== null && ret !== undefined) {
Utils.insertText(Utils.getTextArea()[0], text.replace(lastWord, ret));
}
}
});
this.autoCapitalizeActive = true;
};
EmoteModule.prototype.capitalize = function (value) {
const res = bdEmotes.TwitchGlobal;
for (const p in res) {
if (res.hasOwnProperty(p) && value == (p + "").toLowerCase()) {
return p;
}
}
};
EmoteModule.prototype.disableAutoCapitalize = function() {
this.autoCapitalizeActive = false;
$("body").off(".bdac");
};
export default new EmoteModule();

View File

@ -5,12 +5,15 @@ import BDEvents from "./bdEvents";
import Utils from "./utils";
function PluginModule() {
this.getString = function(value) {
if (!value) return "???";
return typeof value == "string" ? value : value.toString();
};
}
PluginModule.prototype.loadPlugins = function () {
this.loadPluginData();
bdpluginErrors.concat(ContentManager.loadPlugins());
bdpluginErrors.splice(0, 0, ...ContentManager.loadPlugins());
const plugins = Object.keys(bdplugins);
for (let i = 0; i < plugins.length; i++) {
let plugin, name;
@ -19,6 +22,11 @@ PluginModule.prototype.loadPlugins = function () {
plugin = bdplugins[plugins[i]].plugin;
name = plugin.getName();
if (plugin.load && typeof(plugin.load) == "function") plugin.load();
const meta = bdplugins[plugins[i]];
meta.name = this.getString(plugin.getName());
meta.author = this.getString(plugin.getAuthor());
meta.description = this.getString(plugin.getDescription());
meta.version = this.getString(plugin.getVersion());
}
catch (err) {
pluginCookie[name] = false;

View File

@ -2,9 +2,10 @@ import {settingsCookie} from "../0globals";
import BDV2 from "./v2";
import webpackModules from "./webpackModules";
import Utils from "./utils";
import DOM from "./domtools";
import V2C_PublicServers from "../ui/publicServers";
import Layer from "../ui/layer";
import V2C_PublicServers from "../ui/publicservers/publicServers";
import Layer from "../ui/publicservers/layer";
export default new class V2_PublicServers {
@ -13,7 +14,7 @@ export default new class V2_PublicServers {
}
get component() {
return BDV2.react.createElement(Layer, {rootId: "pubslayerroot", id: "pubslayer", children: BDV2.react.createElement(V2C_PublicServers, {rootId: "pubslayerroot"})});
return BDV2.react.createElement(Layer, {rootId: "pubslayerroot", id: "pubslayer"}, BDV2.react.createElement(V2C_PublicServers, {rootId: "pubslayerroot"}));
}
get root() {
@ -26,15 +27,13 @@ export default new class V2_PublicServers {
}
injectRoot() {
if (!$(".layers, .layers-3iHuyZ").length) return false;
$(".layers, .layers-3iHuyZ").append($("<div/>", {
id: "pubslayerroot"
}));
const layers = DOM.query(".layers, .layers-3iHuyZ");
if (!layers) return false;
layers.append(DOM.createElement("<div id='pubslayerroot'>"));
return true;
}
render() {
// BdApi.alert("Broken", "Sorry but the Public Servers modules is currently broken, I recommend disabling this feature for now.");
const root = this.root;
if (!root) {
console.log("FAILED TO LOCATE ROOT: .layers");
@ -44,25 +43,19 @@ export default new class V2_PublicServers {
}
get button() {
const btn = $("<div/>", {
"class": BDV2.guildClasses.listItem,
"id": "bd-pub-li",
"style": settingsCookie["bda-gs-1"] ? "" : "display: none;"
}).append($("<div/>", {
"class": "wrapper-25eVIn " + BDV2.guildClasses.circleButtonMask,
"text": "public",
"id": "bd-pub-button",
"click": () => { this.render(); }
}));
const btn = DOM.createElement(`<div id="bd-pub-li" class="${BDV2.guildClasses.listItem}">`);
if (!settingsCookie["bda-gs-1"]) btn.style.display = "none";
const label = DOM.createElement(`<div id="bd-pub-button" class="${"wrapper-25eVIn " + BDV2.guildClasses.circleButtonMask}">public</div>`);
label.addEventListener("click", () => {this.render();});
btn.append(label);
return btn;
}
_appendButton() {
if ($("#bd-pub-li").length) return;
if (DOM.query("#bd-pub-li")) return;
const wrapper = BDV2.guildClasses.wrapper.split(" ")[0];
const guilds = $(`.${wrapper} .scroller-2FKFPG >:first-child`);
guilds.after(this.button);
const guilds = DOM.query(`.${wrapper} .scroller-2FKFPG >:first-child`);
DOM.after(guilds, this.button);
}
addButton() {
@ -75,6 +68,6 @@ export default new class V2_PublicServers {
removeButton() {
this.guildPatch();
delete this.guildPatch;
$("#bd-pub-li").remove();
DOM.query("#bd-pub-li").remove();
}
};

View File

@ -2,194 +2,154 @@ import {settingsCookie, bdEmotes} from "../0globals";
import DataStore from "./dataStore";
import BDV2 from "./v2";
import Utils from "./utils";
import DOM from "./domtools";
function QuickEmoteMenu() {
this.switchHandler = this.switchHandler.bind(this);
this.favContext = this.favContext.bind(this);
}
const insertEmote = function(e) {
const emote = e.target.getAttribute("title");
const newTextarea = document.querySelector(`.${BDV2.slateEditorClasses.slateTextArea.split(" ")[0]}`);
if (newTextarea) {
const instance = BDV2.getInternalInstance(newTextarea);
const insert = Utils.getNestedProp(instance, "memoizedProps.children.props.editor.insertText");
if (insert) insert(` ${emote} `);
}
else {
const ta = Utils.getTextArea();
Utils.insertText(ta, ta.value.slice(-1) == " " ? ta.value + emote : ta.value + " " + emote);
}
};
const makeEmote = function(name, url, {click = insertEmote, contextmenu} = {}) {
const emote = DOM.createElement(`<div class="emote-container"><img class="emote-icon" alt="${name}" src="${url}" title="${name}"></div>`);
if (click) emote.addEventListener("click", click);
if (contextmenu) emote.addEventListener("contextmenu", contextmenu);
return emote;
};
QuickEmoteMenu.prototype.init = function() {
this.initialized = true;
$(document).on("mousedown", function(e) {
if (e.target.id != "rmenu") $("#rmenu").remove();
});
this.favoriteEmotes = {};
const fe = DataStore.getBDData("bdfavemotes");
if (fe !== "" && fe !== null) {
this.favoriteEmotes = JSON.parse(atob(fe));
}
if (fe !== "" && fe !== null) this.favoriteEmotes = JSON.parse(atob(fe));
let qmeHeader = "";
qmeHeader += "<div id=\"bda-qem\">";
qmeHeader += " <button class=\"active\" id=\"bda-qem-twitch\" onclick='quickEmoteMenu.switchHandler(this); return false;'>Twitch</button>";
qmeHeader += " <button id=\"bda-qem-favourite\" onclick='quickEmoteMenu.switchHandler(this); return false;'>Favourite</button>";
qmeHeader += " <button id=\"bda-qem-emojis\" onclick='quickEmoteMenu.switchHandler(this); return false;'>Emojis</buttond>";
qmeHeader += "</div>";
this.qmeHeader = qmeHeader;
this.qmeHeader = DOM.createElement(`<div id="bda-qem">`);
this.twitchButton = DOM.createElement(`<button class="active" id="bda-qem-twitch">Twitch</button>`);
this.favoriteButton = DOM.createElement(`<button id="bda-qem-favourite">Favorite</button>`);
this.emojiButton = DOM.createElement(`<button id="bda-qem-emojis">Emojis</buttond>`);
this.twitchButton.addEventListener("click", this.switchHandler);
this.favoriteButton.addEventListener("click", this.switchHandler);
this.emojiButton.addEventListener("click", this.switchHandler);
this.qmeHeader.append(this.twitchButton, this.favoriteButton, this.emojiButton);
let teContainer = "";
teContainer += "<div id=\"bda-qem-twitch-container\">";
teContainer += " <div class=\"scroller-wrap scrollerWrap-2lJEkd fade\">";
teContainer += " <div class=\"scroller scroller-2FKFPG\">";
teContainer += " <div class=\"emote-menu-inner\">";
let url = "";
this.teContainer = DOM.createElement(`<div id="bda-qem-twitch-container"><div class="scroller-wrap scrollerWrap-2lJEkd fade"><div class="scroller scroller-2FKFPG"><div class="emote-menu-inner"></div></div></div></div>`);
this.teInner = this.teContainer.querySelector(".emote-menu-inner");
for (const emote in bdEmotes.TwitchGlobal) {
if (bdEmotes.TwitchGlobal.hasOwnProperty(emote)) {
url = bdEmotes.TwitchGlobal[emote];
teContainer += "<div class=\"emote-container\">";
teContainer += " <img class=\"emote-icon\" alt=\"\" src=\"" + url + "\" title=\"" + emote + "\">";
teContainer += " </img>";
teContainer += "</div>";
}
if (!bdEmotes.TwitchGlobal.hasOwnProperty(emote)) continue;
this.teInner.append(makeEmote(emote, bdEmotes.TwitchGlobal[emote]));
}
teContainer += " </div>";
teContainer += " </div>";
teContainer += " </div>";
teContainer += "</div>";
this.teContainer = teContainer;
let faContainer = "";
faContainer += "<div id=\"bda-qem-favourite-container\">";
faContainer += " <div class=\"scroller-wrap scrollerWrap-2lJEkd fade\">";
faContainer += " <div class=\"scroller scroller-2FKFPG\">";
faContainer += " <div class=\"emote-menu-inner\">";
this.faContainer = DOM.createElement(`<div id="bda-qem-favourite-container"><div class="scroller-wrap scrollerWrap-2lJEkd fade"><div class="scroller scroller-2FKFPG"><div class="emote-menu-inner"></div></div></div></div>`);
this.faInner = this.faContainer.querySelector(".emote-menu-inner");
for (const emote in this.favoriteEmotes) {
url = this.favoriteEmotes[emote];
faContainer += "<div class=\"emote-container\">";
faContainer += " <img class=\"emote-icon\" alt=\"\" src=\"" + url + "\" title=\"" + emote + "\" oncontextmenu='quickEmoteMenu.favContext(event, this);'>";
faContainer += " </img>";
faContainer += "</div>";
this.faInner.append(makeEmote(emote, this.favoriteEmotes[emote], {contextmenu: this.favContext}));
}
faContainer += " </div>";
faContainer += " </div>";
faContainer += " </div>";
faContainer += "</div>";
this.faContainer = faContainer;
};
QuickEmoteMenu.prototype.favContext = function(e, em) {
QuickEmoteMenu.prototype.favContext = function(e) {
e.stopPropagation();
const menu = $("<div>", {"id": "removemenu", "data-emoteid": $(em).prop("title"), "text": "Remove", "class": "bd-context-menu context-menu theme-dark"});
menu.css({
top: e.pageY - $("#bda-qem-favourite-container").offset().top,
left: e.pageX - $("#bda-qem-favourite-container").offset().left
});
$(em).parent().append(menu);
const self = this;
menu.on("click", function(e) {
e.preventDefault();
e.stopPropagation();
$(this).remove();
const container = DOM.query("#app-mount");
const cmWrap = DOM.createElement(`<div class="layer-v9HyYc da-layer">`);
const cm = DOM.createElement(`<div class="contextMenu-HLZMGh da-contextMenu bd-context-menu"></div>`);
cmWrap.append(cm);
container.append(cmWrap);
cmWrap.style.top = e.clientY + "px";
cmWrap.style.left = e.clientX + "px";
cmWrap.style.zIndex = "1002";
const removeCM = function(e) {
if (e && e.keyCode && e.keyCode !== 27) return;
cmWrap.remove();
document.removeEventListener("click", removeCM);
document.removeEventListener("contextmenu", removeCM);
document.removeEventListener("keyup", removeCM);
};
document.addEventListener("click", removeCM);
document.addEventListener("contextmenu", removeCM);
document.addEventListener("keyup", removeCM);
delete self.favoriteEmotes[$(this).data("emoteid")];
self.updateFavorites();
return false;
const cmg = DOM.createElement(`<div class="itemGroup-1tL0uz da-itemGroup">`);
const cmi = DOM.createElement(`<div class="item-1Yvehc itemBase-tz5SeC da-item da-itemBase clickable-11uBi- da-clickable">`);
cmi.append(DOM.createElement(`<div class="label-JWQiNe da-label">Remove</div>`));
cmi.addEventListener("click", () => {
delete this.favoriteEmotes[e.target.getAttribute("title")];
e.target.parentElement.remove();
this.saveFavorites();
removeCM();
});
return false;
cmg.append(cmi);
cm.append(cmg);
};
QuickEmoteMenu.prototype.switchHandler = function(e) {
this.switchQem($(e).attr("id"));
this.switchQem(e.target.id);
};
QuickEmoteMenu.prototype.switchQem = function(id) {
const twitch = $("#bda-qem-twitch");
const fav = $("#bda-qem-favourite");
const emojis = $("#bda-qem-emojis");
twitch.removeClass("active");
fav.removeClass("active");
emojis.removeClass("active");
this.twitchButton.classList.remove("active");
this.favoriteButton.classList.remove("active");
this.emojiButton.classList.remove("active");
$(".emojiPicker-3m1S-j").hide();
$("#bda-qem-favourite-container").hide();
$("#bda-qem-twitch-container").hide();
const emojiPicker = DOM.query(".emojiPicker-3m1S-j");
emojiPicker.style.display = "none";
this.faContainer.style.display = "none";
this.teContainer.style.display = "none";
switch (id) {
case "bda-qem-twitch":
twitch.addClass("active");
$("#bda-qem-twitch-container").show();
this.twitchButton.classList.add("active");
this.teContainer.style.display = "";
break;
case "bda-qem-favourite":
fav.addClass("active");
$("#bda-qem-favourite-container").show();
this.favoriteButton.classList.add("active");
this.faContainer.style.display = "";
break;
case "bda-qem-emojis":
emojis.addClass("active");
$(".emojiPicker-3m1S-j").show();
$(".emojiPicker-3m1S-j input").focus();
this.emojiButton.classList.add("active");
emojiPicker.style.display = "";
emojiPicker.querySelector("input").focus();
break;
}
this.lastTab = id;
const emoteIcon = $(".emote-icon");
emoteIcon.off();
emoteIcon.on("click", function () {
const emote = $(this).attr("title");
const newTextarea = document.querySelector(`.${BDV2.slateEditorClasses.slateTextArea.split(" ")[0]}`);
if (newTextarea) {
const instance = BDV2.getInternalInstance(newTextarea);
const insert = Utils.getNestedProp(instance, "memoizedProps.children.props.editor.insertText");
if (insert) insert(` ${emote} `);
}
else {
const ta = Utils.getTextArea();
Utils.insertText(ta[0], ta.val().slice(-1) == " " ? ta.val() + emote : ta.val() + " " + emote);
}
});
};
QuickEmoteMenu.prototype.obsCallback = function (elem) {
if (!this.initialized) return;
const e = $(elem);
if (!settingsCookie["bda-es-9"]) {
e.addClass("bda-qme-hidden");
}
else {
e.removeClass("bda-qme-hidden");
}
if (!settingsCookie["bda-es-9"]) elem.classList.add("bda-qme-hidden");
else elem.classList.remove("bda-qme-hidden");
if (!settingsCookie["bda-es-0"]) return;
e.prepend(this.qmeHeader);
e.append(this.teContainer);
e.append(this.faContainer);
if (this.lastTab == undefined) {
this.lastTab = "bda-qem-emojis";
}
this.switchQem(this.lastTab);
DOM.prependTo(this.qmeHeader, elem);
elem.append(this.teContainer);
elem.append(this.faContainer);
this.switchQem("bda-qem-emojis");
};
QuickEmoteMenu.prototype.favorite = function (name, url) {
if (!this.favoriteEmotes.hasOwnProperty(name)) {
this.favoriteEmotes[name] = url;
}
if (!this.favoriteEmotes.hasOwnProperty(name)) this.favoriteEmotes[name] = url;
this.updateFavorites();
};
QuickEmoteMenu.prototype.updateFavorites = function () {
let faContainer = "";
faContainer += "<div id=\"bda-qem-favourite-container\">";
faContainer += " <div class=\"scroller-wrap scrollerWrap-2lJEkd fade\">";
faContainer += " <div class=\"scroller scroller-2FKFPG\">";
faContainer += " <div class=\"emote-menu-inner\">";
for (const emote in this.favoriteEmotes) {
const url = this.favoriteEmotes[emote];
faContainer += "<div class=\"emote-container\">";
faContainer += " <img class=\"emote-icon\" alt=\"\" src=\"" + url + "\" title=\"" + emote + "\" oncontextmenu=\"quickEmoteMenu.favContext(event, this);\">";
faContainer += " </img>";
faContainer += "</div>";
}
faContainer += " </div>";
faContainer += " </div>";
faContainer += " </div>";
faContainer += "</div>";
this.faContainer = faContainer;
$("#bda-qem-favourite-container").replaceWith(faContainer);
QuickEmoteMenu.prototype.saveFavorites = function () {
DataStore.setBDData("bdfavemotes", btoa(JSON.stringify(this.favoriteEmotes)));
};
QuickEmoteMenu.prototype.updateFavorites = function () {
this.faInner.innerHTML = "";
for (const emote in this.favoriteEmotes) this.faInner.append(makeEmote(emote, this.favoriteEmotes[emote], {contextmenu: this.favContext}));
this.saveFavorites();
};
export default new QuickEmoteMenu();

View File

@ -8,6 +8,7 @@ import BDEvents from "./bdEvents";
import coloredText from "./coloredText";
import tfHour from "./24hour";
import reactDevTools from "./reactDevTools";
import DOM from "./domtools";
import publicServersModule from "./publicServers";
import voiceMode from "./voiceMode";
@ -26,34 +27,32 @@ import CardList from "../ui/addonlist";
export default new class V2_SettingsPanel {
constructor() {
const self = this;
self.sideBarOnClick = self.sideBarOnClick.bind(self);
self.onChange = self.onChange.bind(self);
self.updateSettings = this.updateSettings.bind(self);
self.sidebar = new V2_SettingsPanel_Sidebar(self.sideBarOnClick);
this.sideBarOnClick = this.sideBarOnClick.bind(this);
this.onChange = this.onChange.bind(this);
this.updateSettings = this.updateSettings.bind(this);
this.sidebar = new V2_SettingsPanel_Sidebar(this.sideBarOnClick);
this.buildPluginProps = this.buildPluginProps.bind(this);
this.buildThemeProps = this.buildThemeProps.bind(this);
this.showOriginal = this.showOriginal.bind(this);
}
get root() {
const _root = $("#bd-settingspane-container");
if (!_root.length) {
const _root = DOM.query("#bd-settingspane-container");
if (!_root) {
if (!this.injectRoot()) return null;
return this.root;
}
return _root[0];
return _root;
}
injectRoot() {
if (!$(".layer-3QrUeG .standardSidebarView-3F1I7i, .layer-3QrUeG .ui-standard-sidebar-view").length) return false;
const root = $("<div/>", {
"class": "contentRegion-3nDuYy content-region",
"id": "bd-settingspane-container"
});
$(".layer-3QrUeG .standardSidebarView-3F1I7i, .layer-3QrUeG .ui-standard-sidebar-view").append(root);
const sidebar = DOM.query(".layer-3QrUeG .standardSidebarView-3F1I7i, .layer-3QrUeG .ui-standard-sidebar-view");
if (!sidebar) return false;
const root = DOM.createElement(`<div id="bd-settingspane-container" class="contentRegion-3nDuYy content-region">`);
sidebar.append(root);
Utils.onRemoved(root[0], () => {
BDV2.reactDom.unmountComponentAtNode(root[0]);
Utils.onRemoved(root, () => {
BDV2.reactDom.unmountComponentAtNode(root);
});
return true;
}
@ -80,24 +79,24 @@ export default new class V2_SettingsPanel {
}
sideBarOnClick(id) {
const self = this;
$(".contentRegion-3nDuYy, .content-region").first().hide();
$(self.root).show();
const contentRegion = DOM.query(".contentRegion-3nDuYy, .content-region");
contentRegion.style.display = "none";
this.root.style.display = "";
switch (id) {
case "core":
self.renderCoreSettings();
this.renderCoreSettings();
break;
case "emotes":
self.renderEmoteSettings();
this.renderEmoteSettings();
break;
case "customcss":
self.renderCustomCssEditor();
this.renderCustomCssEditor();
break;
case "plugins":
self.renderPluginPane();
this.renderPluginPane();
break;
case "themes":
self.renderThemePane();
this.renderThemePane();
break;
}
}
@ -111,19 +110,14 @@ export default new class V2_SettingsPanel {
updateSettings(id, enabled) {
settingsCookie[id] = enabled;
// if (id == "bda-gs-b") {
// if (enabled) $("body").addClass("bd-blue");
// else $("body").removeClass("bd-blue");
// }
if (id == "bda-gs-2") {
if (enabled) $("body").addClass("bd-minimal");
else $("body").removeClass("bd-minimal");
if (enabled) DOM.addClass(document.body, "bd-minimal");
else DOM.removeClass(document.body, "bd-minimal");
}
if (id == "bda-gs-3") {
if (enabled) $("body").addClass("bd-minimal-chan");
else $("body").removeClass("bd-minimal-chan");
if (enabled) DOM.addClass(document.body, "bd-minimal-chan");
else DOM.removeClass(document.body, "bd-minimal-chan");
}
if (id == "bda-gs-1") {
@ -132,13 +126,13 @@ export default new class V2_SettingsPanel {
}
if (id == "bda-gs-4") {
if (enabled) voiceMode.enable();
else voiceMode.disable();
if (enabled) voiceMode.start();
else voiceMode.stop();
}
if (id == "bda-gs-5") {
if (enabled) $("#app-mount").addClass("bda-dark");
else $("#app-mount").removeClass("bda-dark");
if (enabled) DOM.addClass(DOM.query("#app-mount"), "bda-dark");
else DOM.removeClass(DOM.query("#app-mount"), "bda-dark");
}
if (enabled && id == "bda-gs-6") tfHour.inject24Hour();
@ -148,11 +142,6 @@ export default new class V2_SettingsPanel {
else coloredText.removeColoredText();
}
if (id == "bda-es-4") {
if (enabled) emoteModule.autoCapitalize();
else emoteModule.disableAutoCapitalize();
}
if (id == "fork-ps-4") {
if (enabled) ClassNormalizer.start();
else ClassNormalizer.stop();
@ -182,12 +171,13 @@ export default new class V2_SettingsPanel {
if (id == "bda-gs-8") {
if (enabled) dMode.enable(settingsCookie["fork-dm-1"]);
else dMode.disable();
if (enabled) dMode.startDebugListener();
else dMode.stopDebugListener();
}
if (id == "fork-dm-1") {
if (settingsCookie["bda-gs-8"]) dMode.enable(enabled);
if (enabled) dMode.startCopySelector();
else dMode.stopCopySelector();
}
if (id === "reactDevTools") {
@ -199,15 +189,13 @@ export default new class V2_SettingsPanel {
}
async initializeSettings() {
// if (settingsCookie["bda-gs-b"]) $("body").addClass("bd-blue");
if (settingsCookie["bda-gs-2"]) $("body").addClass("bd-minimal");
if (settingsCookie["bda-gs-3"]) $("body").addClass("bd-minimal-chan");
if (settingsCookie["bda-gs-2"]) DOM.addClass(document.body, "bd-minimal");
if (settingsCookie["bda-gs-3"]) DOM.addClass(document.body, "bd-minimal-chan");
if (settingsCookie["bda-gs-1"]) publicServersModule.addButton();
if (settingsCookie["bda-gs-4"]) voiceMode.enable();
if (settingsCookie["bda-gs-5"]) $("#app-mount").addClass("bda-dark");
if (settingsCookie["bda-gs-4"]) voiceMode.start();
if (settingsCookie["bda-gs-5"]) DOM.addClass(DOM.query("#app-mount"), "bda-dark");
if (settingsCookie["bda-gs-6"]) tfHour.inject24Hour();
if (settingsCookie["bda-gs-7"]) coloredText.injectColoredText();
if (settingsCookie["bda-es-4"]) emoteModule.autoCapitalize();
if (settingsCookie["fork-ps-4"]) ClassNormalizer.start();
if (settingsCookie["fork-ps-5"]) {
@ -215,7 +203,8 @@ export default new class V2_SettingsPanel {
ContentManager.watchContent("theme");
}
if (settingsCookie["bda-gs-8"]) dMode.enable(settingsCookie["fork-dm-1"]);
if (settingsCookie["bda-gs-8"]) dMode.startDebugListener();
if (settingsCookie["fork-dm-1"]) dMode.startCopySelector();
if (settingsCookie.reactDevTools) reactDevTools.start();
this.saveSettings();
@ -229,14 +218,19 @@ export default new class V2_SettingsPanel {
Object.assign(settingsCookie, DataStore.getSettingGroup("settings"));
}
showOriginal() {
BDV2.reactDom.unmountComponentAtNode(this.root);
this.root.style.display = "none";
DOM.query(".contentRegion-3nDuYy, .content-region").style.display = "";
}
renderSidebar() {
const self = this;
$("[class*='side-'] > [class*='item-']").off("click.v2settingspanel").on("click.v2settingspanel", () => {
BDV2.reactDom.unmountComponentAtNode(self.root);
$(self.root).hide();
$(".contentRegion-3nDuYy, .content-region").first().show();
});
self.sidebar.render();
const tabs = document.querySelectorAll("[class*='side-'] > [class*='item-']");
for (const element of tabs) {
element.removeEventListener("click", this.showOriginal);
element.addEventListener("click", this.showOriginal);
}
this.sidebar.render();
}
get coreComponent() {
@ -322,7 +316,7 @@ export default new class V2_SettingsPanel {
getAddonList(type) {
const isPlugins = type === "plugins";
const list = isPlugins ? Object.values(bdplugins).map(this.buildPluginProps) : Object.values(bdthemes).map(this.buildThemeProps);
const list = isPlugins ? Object.values(bdplugins) : Object.values(bdthemes);
return BDV2.react.createElement(CardList, {type, list});
}

View File

@ -1,6 +1,7 @@
import {bbdChangelog} from "../0globals";
import Utils from "./utils";
import BDV2 from "./v2";
import DOM from "./domtools";
import SideBar from "../ui/sidebar";
import History from "../ui/icons/history";
@ -28,18 +29,19 @@ export default class V2_SettingsPanel_Sidebar {
}
get root() {
const _root = $("#bd-settings-sidebar");
if (!_root.length) {
const _root = DOM.query("#bd-settings-sidebar");
if (!_root) {
if (!this.injectRoot()) return null;
return this.root;
}
return _root[0];
return _root;
}
injectRoot() {
const changeLog = $("[class*='side-'] > [class*='item-']:not([class*=Danger])").last();
if (!changeLog.length) return false;
$("<span/>", {id: "bd-settings-sidebar"}).insertBefore(changeLog.prev());
const tabs = DOM.queryAll("[class*='side-'] > [class*='item-']:not([class*=Danger])");
const changeLog = tabs[tabs.length - 1];
if (!changeLog) return false;
changeLog.parentElement.insertBefore(DOM.createElement(`<div id="bd-settings-sidebar">`), changeLog.previousElementSibling);
return true;
}

View File

@ -3,20 +3,28 @@ import ContentManager from "./contentManager";
import DataStore from "./dataStore";
import BDEvents from "./bdEvents";
import Utils from "./utils";
import DOM from "./domtools";
function ThemeModule() {
this.getString = function(value) {
if (!value) return "???";
return typeof value == "string" ? value : value.toString();
};
}
ThemeModule.prototype.loadThemes = function () {
this.loadThemeData();
bdthemeErrors.concat(ContentManager.loadThemes());
bdthemeErrors.splice(0, 0, ...ContentManager.loadThemes());
const themes = Object.keys(bdthemes);
for (let i = 0; i < themes.length; i++) {
const theme = bdthemes[themes[i]];
theme.name = this.getString(theme.name);
theme.author = this.getString(theme.author);
theme.description = this.getString(theme.description);
theme.version = this.getString(theme.version);
if (!themeCookie[theme.name]) themeCookie[theme.name] = false;
if (themeCookie[theme.name]) $("head").append($("<style>", {id: theme.id, text: unescape(theme.css)}));
if (themeCookie[theme.name]) DOM.addStyle(DOM.escapeID(theme.id), unescape(theme.css));
}
for (const theme in themeCookie) {
if (!bdthemes[theme]) delete themeCookie[theme];
@ -29,7 +37,7 @@ ThemeModule.prototype.enableTheme = function(name, reload = false) {
themeCookie[name] = true;
this.saveThemeData();
const theme = bdthemes[name];
$("head").append($("<style>", {id: theme.id, text: unescape(theme.css)}));
DOM.addStyle(DOM.escapeID(theme.id), unescape(theme.css));
if (settingsCookie["fork-ps-2"] && !reload) Utils.showToast(`${theme.name} v${theme.version} has been applied.`);
};
@ -41,7 +49,7 @@ ThemeModule.prototype.disableTheme = function(name, reload = false) {
themeCookie[name] = false;
this.saveThemeData();
const theme = bdthemes[name];
$(`#${theme.id}`).remove();
DOM.removeStyle(DOM.escapeID(theme.id));
if (settingsCookie["fork-ps-2"] && !reload) Utils.showToast(`${theme.name} v${theme.version} has been disabled.`);
};

View File

@ -1,6 +1,7 @@
import {bbdVersion} from "../0globals";
import WebpackModules from "./webpackModules";
import BDV2 from "./v2";
import DOM from "./domtools";
export default class Utils {
/** Document/window width */
@ -49,7 +50,7 @@ export default class Utils {
}
static getTextArea() {
return $(".channelTextArea-rNsIhG textarea");
return DOM.query(".channelTextArea-rNsIhG textarea");
}
static insertText(textarea, text) {
@ -59,24 +60,6 @@ export default class Utils {
document.execCommand("insertText", false, text);
}
static injectCss(uri) {
$("<link/>", {
type: "text/css",
rel: "stylesheet",
href: uri
}).appendTo($("head"));
}
static injectJs(uri) {
return new Promise(resolve => {
$("<script/>", {
type: "text/javascript",
src: uri,
onload: resolve
}).appendTo($("body"));
});
}
static escapeID(id) {
return id.replace(/^[^a-z]+|[^\w-]+/gi, "-");
}
@ -231,7 +214,7 @@ export default class Utils {
}
static alert(title, content) {
const modal = $(`<div class="bd-modal-wrapper theme-dark">
const modal = DOM.createElement(`<div class="bd-modal-wrapper theme-dark">
<div class="bd-backdrop backdrop-1wrmKB"></div>
<div class="bd-modal modal-1UGdnR">
<div class="bd-modal-inner inner-1JeGVc">
@ -251,21 +234,21 @@ export default class Utils {
</div>
</div>
</div>`);
modal.find(".footer button").on("click", () => {
modal.addClass("closing");
modal.querySelector(".footer button").addEventListener("click", () => {
DOM.addClass(modal, "closing");
setTimeout(() => { modal.remove(); }, 300);
});
modal.find(".bd-backdrop").on("click", () => {
modal.addClass("closing");
modal.querySelector(".bd-backdrop").addEventListener("click", () => {
DOM.addClass(modal, "closing");
setTimeout(() => { modal.remove(); }, 300);
});
modal.appendTo("#app-mount");
DOM.query("#app-mount").append(modal);
}
static showContentErrors({plugins: pluginErrors = [], themes: themeErrors = []}) {
if (!pluginErrors || !themeErrors) return;
if (!pluginErrors.length && !themeErrors.length) return;
const modal = $(`<div class="bd-modal-wrapper theme-dark">
const modal = DOM.createElement(`<div class="bd-modal-wrapper theme-dark">
<div class="bd-backdrop backdrop-1wrmKB"></div>
<div class="bd-modal bd-content-modal modal-1UGdnR">
<div class="bd-modal-inner inner-1JeGVc">
@ -288,7 +271,7 @@ export default class Utils {
</div>
</div>
</div>
<div class="footer footer-2yfCgX">
<div class="footer footer-2yfCgX footer-3rDWdC footer-2gL1pp">
<button type="button">Okay</button>
</div>
</div>
@ -296,19 +279,19 @@ export default class Utils {
</div>`);
function generateTab(errors) {
const container = $(`<div class="errors">`);
const container = DOM.createElement(`<div class="errors">`);
for (const err of errors) {
const error = $(`<div class="error">
const error = DOM.createElement(`<div class="error">
<div class="table-column column-name">${err.name ? err.name : err.file}</div>
<div class="table-column column-message">${err.message}</div>
<div class="table-column column-error"><a class="error-link" href="">${err.error ? err.error.message : ""}</a></div>
</div>`);
container.append(error);
if (err.error) {
error.find("a").on("click", (e) => {
error.querySelectorAll("a").forEach(el => el.addEventListener("click", (e) => {
e.preventDefault();
Utils.err("ContentManager", `Error details for ${err.name ? err.name : err.file}.`, err.error);
});
}));
}
}
return container;
@ -316,24 +299,27 @@ export default class Utils {
const tabs = [generateTab(pluginErrors), generateTab(themeErrors)];
modal.find(".tab-bar-item").on("click", (e) => {
modal.querySelectorAll(".tab-bar-item").forEach(el => el.addEventListener("click", (e) => {
e.preventDefault();
modal.find(".tab-bar-item").removeClass("selected");
$(e.target).addClass("selected");
modal.find(".scroller").empty().append(tabs[$(e.target).index()]);
});
const selected = modal.querySelector(".tab-bar-item.selected");
if (selected) DOM.removeClass(selected, "selected");
DOM.addClass(e.target, "selected");
const scroller = modal.querySelector(".scroller");
scroller.innerHTML = "";
scroller.append(tabs[DOM.index(e.target)]);
}));
modal.find(".footer button").on("click", () => {
modal.addClass("closing");
modal.querySelector(".footer button").addEventListener("click", () => {
DOM.addClass(modal, "closing");
setTimeout(() => { modal.remove(); }, 300);
});
modal.find(".bd-backdrop").on("click", () => {
modal.addClass("closing");
modal.querySelector(".bd-backdrop").on("click", () => {
DOM.addClass(modal, "closing");
setTimeout(() => { modal.remove(); }, 300);
});
modal.appendTo("#app-mount");
if (pluginErrors.length) modal.find(".tab-bar-item")[0].click();
else modal.find(".tab-bar-item")[1].click();
DOM.query("#app-mount").append(modal);
if (pluginErrors.length) modal.querySelector(".tab-bar-item").click();
else modal.querySelectorAll(".tab-bar-item")[1].click();
}
static showChangelogModal(options = {}) {

View File

@ -1,23 +1,25 @@
function VoiceMode() {
import DOM from "./domtools";
}
const style = `
.container-2Rl01u {
display: none!important;
}
VoiceMode.prototype.enable = function () {
$(".scroller.guild-channels ul").first().css("display", "none");
$(".scroller.guild-channels header").first().css("display", "none");
$(".app.flex-vertical, .app-2rEoOp").first().css("overflow", "hidden");
$(".chat-3bRxxu").first().css("visibility", "hidden").css("min-width", "0px");
$(".flex-vertical.channels-wrap").first().css("flex-grow", "100000");
$(".guild-header .btn.btn-hamburger").first().css("visibility", "hidden");
};
.chat-3bRxxu {
display: none!important;
}
VoiceMode.prototype.disable = function () {
$(".scroller.guild-channels ul").first().css("display", "");
$(".scroller.guild-channels header").first().css("display", "");
$(".app.flex-vertical, .app-2rEoOp").first().css("overflow", "");
$(".chat-3bRxxu").first().css("visibility", "").css("min-width", "");
$(".flex-vertical.channels-wrap").first().css("flex-grow", "");
$(".guild-header .btn.btn-hamburger").first().css("visibility", "");
};
.sidebar-2K8pFh {
flex-grow: 1!important;
}
`;
export default new VoiceMode();
export default new class VoiceMode {
start() {
DOM.addStyle("VoiceMode", style);
}
stop() {
DOM.removeStyle("VoiceMode");
}
};

View File

@ -1,6 +1,7 @@
import {settingsCookie} from "../0globals";
import BDV2 from "../modules/v2";
import Utils from "../modules/utils";
import DOM from "../modules/domtools";
import XSvg from "./xSvg";
import ReloadIcon from "./reloadIcon";
@ -18,7 +19,7 @@ export default class V2C_PluginCard extends BDV2.reactComponent {
this.onChange = this.onChange.bind(this);
this.showSettings = this.showSettings.bind(this);
this.setInitialState();
this.hasSettings = typeof this.props.addon.getSettingsPanel === "function";
this.hasSettings = this.props.addon.plugin && typeof this.props.addon.plugin.getSettingsPanel === "function";
this.settingsPanel = "";
this.edit = this.edit.bind(this);
@ -51,20 +52,30 @@ export default class V2C_PluginCard extends BDV2.reactComponent {
}
if (!settingsCookie["fork-ps-3"]) return;
const isHidden = (container, element) => {
const cTop = container.scrollTop;
const cBottom = cTop + container.clientHeight;
const eTop = element.offsetTop;
const eBottom = eTop + element.clientHeight;
return (eTop < cTop || eBottom > cBottom);
};
setImmediate(() => {
const isHidden = (container, element) => {
const cTop = container.scrollTop;
const cBottom = cTop + container.clientHeight;
const eTop = element.offsetTop;
const eBottom = eTop + element.clientHeight;
return (eTop < cTop || eBottom > cBottom);
};
const thisNode = $(BDV2.reactDom.findDOMNode(this));
const container = thisNode.parents(".scroller");
if (!isHidden(container[0], thisNode[0])) return;
container.animate({
scrollTop: thisNode.offset().top - container.offset().top + container.scrollTop() - 30
}, 300);
const thisNode = this.refs.cardNode;
const container = thisNode.closest(".scroller");
if (!isHidden(container, thisNode)) return;
const thisNodeOffset = DOM.offset(thisNode);
const containerOffset = DOM.offset(container);
const original = container.scrollTop;
const endPoint = thisNodeOffset.top - containerOffset.top + container.scrollTop - 30;
DOM.animate({
duration: 300,
update: function(progress) {
if (endPoint > original) container.scrollTop = original + (progress * (endPoint - original));
else container.scrollTop = original - (progress * (original - endPoint));
}
});
});
}
@ -76,10 +87,10 @@ export default class V2C_PluginCard extends BDV2.reactComponent {
get settingsComponent() {
const name = this.getString(this.props.addon.name);
try { this.settingsPanel = this.props.addon.getSettingsPanel(); }
try { this.settingsPanel = this.props.addon.plugin.getSettingsPanel(); }
catch (err) { Utils.err("Plugins", "Unable to get settings panel for " + this.props.addon.name + ".", err); }
return BDV2.react.createElement("div", {className: "bd-card bd-addon-card settings-open ui-switch-item"},
return BDV2.react.createElement("div", {className: "bd-card bd-addon-card settings-open ui-switch-item", ref: "cardNode"},
BDV2.react.createElement("div", {style: {"float": "right", "cursor": "pointer"}, onClick: () => {
this.refs.settingspanel.innerHTML = "";
this.setState({settings: false});
@ -184,41 +195,4 @@ export default class V2C_PluginCard extends BDV2.reactComponent {
this.footer
);
}
}
// get settingsComponent() {
// const addon = this.props.addon;
// const name = this.getString(addon.name);
// try { this.settingsPanel = this.props.getSettingsPanel(); }
// catch (err) { Logger.stacktrace("Addon Settings", "Unable to get settings panel for " + name + ".", err); }
// const props = {id: `${name}-settings`, className: "addon-settings", ref: this.panelRef};
// if (typeof(settingsPanel) == "string") props.dangerouslySetInnerHTML = this.settingsPanel;
// return <div className="bd-addon-card settings-open bd-switch-item">
// <div className="bd-close" onClick={this.closeSettings}><CloseButton /></div>
// <div {...props}>{this.settingsPanel instanceof React.Component ? this.settingsPanel : null}</div>
// </div>;
// }
// componentDidUpdate() {
// if (!this.state.settingsOpen) return;
// if (this.settingsPanel instanceof Node) this.panelRef.current.appendChild(this.settingsPanel);
// // if (!SettingsCookie["fork-ps-3"]) return;
// const isHidden = (container, element) => {
// const cTop = container.scrollTop;
// const cBottom = cTop + container.clientHeight;
// const eTop = element.offsetTop;
// const eBottom = eTop + element.clientHeight;
// return (eTop < cTop || eBottom > cBottom);
// };
// const panel = $(this.panelRef.current);
// const container = panel.parents(".scroller-2FKFPG");
// if (!isHidden(container[0], panel[0])) return;
// container.animate({
// scrollTop: panel.offset().top - container.offset().top + container.scrollTop() - 30
// }, 300);
// }
}

View File

@ -33,7 +33,7 @@ export default class CardList extends BDV2.reactComponent {
}
openFolder() {
require("electron").shell.openItem(this.isPlugins ? ContentManager.pluginsFolder : ContentManager.themesFolder);
require("electron").shell.showItemInFolder(this.isPlugins ? ContentManager.pluginsFolder : ContentManager.themesFolder);
}
edit(name) {
@ -109,18 +109,20 @@ export default class CardList extends BDV2.reactComponent {
return 0;
});
if (!this.state.ascending) sortedAddons.reverse();
const rendered = sortedAddons.map((addon) => {
const rendered = [];
for (let a = 0; a < sortedAddons.length; a++) {
const addon = sortedAddons[a];
if (this.state.query) {
let matches = null;
if (addon.name) matches = addon.name.toLocaleLowerCase().includes(this.state.query);
if (addon.author) matches = matches || addon.author.toLocaleLowerCase().includes(this.state.query);
if (addon.description) matches = matches || addon.description.toLocaleLowerCase().includes(this.state.query);
if (addon.version) matches = matches || addon.version.toLocaleLowerCase().includes(this.state.query);
if (!matches) return null;
if (!matches) continue;
}
const props = this.getProps(addon);
return <ErrorBoundary><AddonCard {...props} reload={!settingsCookie["fork-ps-5"] && this.manager.reload.bind(this.manager)} /></ErrorBoundary>;
});
rendered.push(<ErrorBoundary><AddonCard {...props} reload={!settingsCookie["fork-ps-5"] && this.manager.reload.bind(this.manager)} /></ErrorBoundary>);
}
return rendered;
}
@ -133,7 +135,7 @@ export default class CardList extends BDV2.reactComponent {
this.forceUpdate();
}} />
}</Tooltip>;
const addonCards = this.getAddons().filter(c => c);
const addonCards = this.getAddons();
return <Scroller contentColumn={true} fade={true} dark={true}>
<ContentColumn title={`${this.props.type.toUpperCase()}${addonCards.length}`}>

View File

@ -2,6 +2,7 @@ import {settingsCookie} from "../0globals";
import Settings from "../modules/settingsPanel";
import BDV2 from "../modules/v2";
import DataStore from "../modules/dataStore";
import DOM from "../modules/domtools";
import SettingsTitle from "./settingsTitle";
import Checkbox from "./checkbox";
@ -188,10 +189,8 @@ export default class V2C_CssEditor extends BDV2.reactComponent {
}
updateCss() {
if ($("#customcss").length == 0) {
$("head").append("<style id=\"customcss\"></style>");
}
$("#customcss").text(this.editor.session.getValue()).detach().appendTo(document.head);
DOM.removeStyle("customcss");
DOM.addStyle("customcss", this.editor.session.getValue());
}
saveCss() {
@ -212,19 +211,18 @@ export default class V2C_CssEditor extends BDV2.reactComponent {
}
get detachedRoot() {
const _root = $("#bd-customcss-detach-container");
if (!_root.length) {
const _root = DOM.query("#bd-customcss-detach-container");
if (!_root) {
if (!this.injectDetachedRoot()) return null;
return this.detachedRoot;
}
return _root[0];
return _root;
}
injectDetachedRoot() {
if (!$(".app, .app-2rEoOp").length) return false;
$("<div/>", {
id: "bd-customcss-detach-container"
}).insertAfter($(".app, .app-2rEoOp"));
const app = DOM.query(".app, .app-2rEoOp");
if (!app) return false;
DOM.insertAfter(DOM.createElement(`<div id="bd-customcss-detach-container">`), app);
return true;
}

View File

@ -2,6 +2,7 @@ import {settingsCookie} from "../0globals";
import Settings from "../modules/settingsPanel";
import BDV2 from "../modules/v2";
import DataStore from "../modules/dataStore";
import DOM from "../modules/domtools";
import Checkbox from "./checkbox";
@ -17,7 +18,7 @@ export default class V2C_CssEditorDetached extends BDV2.reactComponent {
}
componentDidMount() {
$("#app-mount").addClass("bd-detached-editor");
DOM.addClass(DOM.query("#app-mount"), "bd-detached-editor");
BDV2.editorDetached = true;
// this.updateLineCount();
this.editor = ace.edit("bd-customcss-editor-detached");
@ -34,7 +35,7 @@ export default class V2C_CssEditorDetached extends BDV2.reactComponent {
}
componentWillUnmount() {
$("#app-mount").removeClass("bd-detached-editor");
DOM.removeClass(DOM.query("#app-mount"), "bd-detached-editor");
BDV2.editorDetached = false;
this.editor.destroy();
}
@ -66,19 +67,18 @@ export default class V2C_CssEditorDetached extends BDV2.reactComponent {
}
get root() {
const _root = $("#bd-customcss-detach-container");
if (!_root.length) {
const _root = DOM.query("#bd-customcss-detach-container");
if (!_root) {
if (!this.injectRoot()) return null;
return this.detachedRoot;
}
return _root[0];
return _root;
}
injectRoot() {
if (!$(".app, .app-2rEoOp").length) return false;
$("<div/>", {
id: "bd-customcss-detach-container"
}).insertAfter($(".app, .app-2rEoOp"));
const app = DOM.query(".app, .app-2rEoOp");
if (!app) return false;
DOM.insertAfter(DOM.createElement(`<div id="bd-customcss-detach-container">`), app);
return true;
}
@ -149,7 +149,7 @@ export default class V2C_CssEditorDetached extends BDV2.reactComponent {
const self = this;
switch (id) {
case "attach":
if ($("#editor-detached").length) self.props.attach();
if (DOM.query("#editor-detached")) self.props.attach();
BDV2.reactDom.unmountComponentAtNode(self.root);
self.root.remove();
break;
@ -163,10 +163,8 @@ export default class V2C_CssEditorDetached extends BDV2.reactComponent {
}
updateCss() {
if ($("#customcss").length == 0) {
$("head").append("<style id=\"customcss\"></style>");
}
$("#customcss").text(this.editor.session.getValue()).detach().appendTo(document.head);
DOM.removeStyle("customcss");
DOM.addStyle("customcss", this.editor.session.getValue());
}
saveCss() {

View File

@ -1,61 +0,0 @@
import BDV2 from "../modules/v2";
export default class V2C_Layer extends BDV2.reactComponent {
constructor(props) {
super(props);
}
componentDidMount() {
$(window).on(`keyup.${this.props.id}`, e => {
if (e.which === 27) {
BDV2.reactDom.unmountComponentAtNode(this.refs.root.parentNode);
}
});
$(`#${this.props.id}`).animate({opacity: 1}, {
step: function(now) {
$(this).css("transform", `scale(${1.1 - 0.1 * now}) 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(now) {
$(this).css("transform", `scale(${1.1 - 0.1 * now}) translateZ(0px)`);
},
duration: 200,
done: () => {$(`#${this.props.rootId}`).remove();}
});
$("[class*=\"layer-\"]").removeClass("publicServersOpen").animate({opacity: 1}, {
step: function(now) {
$(this).css("transform", `scale(${0.07 * now + 0.93}) translateZ(0px)`);
},
duration: 200,
done: () => {$("[class*=\"layer-\"]").css("opacity", "").css("transform", "");}
});
}
componentWillMount() {
$("[class*=\"layer-\"]").addClass("publicServersOpen").animate({opacity: 0}, {
step: function(now) {
$(this).css("transform", `scale(${0.07 * now + 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
);
}
}

View File

@ -1,257 +0,0 @@
import {settingsCookie, pluginCookie, bdplugins} from "../0globals";
import BDV2 from "../modules/v2";
import pluginModule from "../modules/pluginModule";
import Utils from "../modules/utils";
import XSvg from "./xSvg";
import ReloadIcon from "./reloadIcon";
import TooltipWrap from "./tooltipWrap";
import Switch from "./components/switch";
const React = BDV2.React;
export default class V2C_PluginCard extends BDV2.reactComponent {
constructor(props) {
super(props);
const self = this;
self.onChange = self.onChange.bind(self);
self.showSettings = self.showSettings.bind(self);
self.setInitialState();
self.hasSettings = typeof self.props.plugin.getSettingsPanel === "function";
self.settingsPanel = "";
this.reload = this.reload.bind(this);
this.onReload = this.onReload.bind(this);
}
setInitialState() {
this.state = {
checked: pluginCookie[this.props.plugin.getName()],
settings: false,
reloads: 0
};
}
// componentDidMount() {
// BDEvents.on("plugin-reloaded", this.onReload);
// }
// componentWillUnmount() {
// BDEvents.off("plugin-reloaded", this.onReload);
// }
onReload(pluginName) {
if (pluginName !== this.props.plugin.getName()) return;
this.setState({reloads: this.state.reloads + 1});
}
componentDidUpdate() {
if (this.state.settings) {
if (typeof this.settingsPanel === "object") {
this.refs.settingspanel.appendChild(this.settingsPanel);
}
if (!settingsCookie["fork-ps-3"]) return;
const isHidden = (container, element) => {
const cTop = container.scrollTop;
const cBottom = cTop + container.clientHeight;
const eTop = element.offsetTop;
const eBottom = eTop + element.clientHeight;
return (eTop < cTop || eBottom > cBottom);
};
const self = $(BDV2.reactDom.findDOMNode(this));
const container = self.parents(".scroller");
if (!isHidden(container[0], self[0])) return;
container.animate({
scrollTop: self.offset().top - container.offset().top + container.scrollTop() - 30
}, 300);
}
}
reload() {
const plugin = this.props.plugin.getName();
pluginModule.reloadPlugin(plugin);
this.props.plugin = bdplugins[plugin].plugin;
this.onReload(this.props.plugin.getName());
}
getString(value) {
if (!value) return "???";
return typeof value == "string" ? value : value.toString();
}
get settingsComponent() {
const self = this;
const {plugin} = this.props;
const name = this.getString(plugin.getName());
try { self.settingsPanel = plugin.getSettingsPanel(); }
catch (err) { Utils.err("Plugins", "Unable to get settings panel for " + plugin.getName() + ".", err); }
return BDV2.react.createElement("div", {className: "settings-open ui-switch-item bd-addon-card"},
BDV2.react.createElement("div", {style: {"float": "right", "cursor": "pointer"}, onClick: () => {
this.refs.settingspanel.innerHTML = "";
self.setState({settings: false});
}},
BDV2.react.createElement(XSvg, null)
),
typeof self.settingsPanel === "object" && BDV2.react.createElement("div", {id: `plugin-settings-${name}`, className: "plugin-settings", ref: "settingspanel"}),
typeof self.settingsPanel !== "object" && BDV2.react.createElement("div", {id: `plugin-settings-${name}`, className: "plugin-settings", ref: "settingspanel", dangerouslySetInnerHTML: {__html: self.settingsPanel}})
);
}
buildTitle(name, version, author) {
const title = "{{name}} v{{version}} by {{author}}".split(/({{[A-Za-z]+}})/);
const nameIndex = title.findIndex(s => s == "{{name}}");
if (nameIndex) title[nameIndex] = React.createElement("span", {className: "bd-name bda-name"}, name);
const versionIndex = title.findIndex(s => s == "{{version}}");
if (nameIndex) title[versionIndex] = React.createElement("span", {className: "bd-version bda-version"}, version);
const authorIndex = title.findIndex(s => s == "{{author}}");
if (nameIndex) {
const props = {className: "bd-author bda-author"};
if (author.link || author.id) {
props.className += ` ${BDV2.anchorClasses.anchor} ${BDV2.anchorClasses.anchorUnderlineOnHover}`;
props.target = "_blank";
if (author.link) props.href = author.link;
if (author.id) props.onClick = () => {BDV2.LayerStack.popLayer(); BDV2.openDM(author.id);};
}
title[authorIndex] = React.createElement(author.link || author.id ? "a" : "span", props, author.name);
}
return title.flat();
}
makeLink(title, url) {
const props = {className: "bda-link bda-link-website", target: "_blank"};
if (typeof(url) == "string") props.href = url;
if (typeof(url) == "function") props.onClick = (event) => {event.preventDefault(); event.stopPropagation(); url();};
return BDV2.react.createElement("a", props, title);
}
makeButton(title, children, action) {
return <TooltipWrap color="black" position="top" text={title}>
{(props) => {
return <div {...props} className="bd-addon-button" onClick={action}>{children}</div>;
}}
</TooltipWrap>;
}
getLinks(meta) {
const links = [];
if (meta.website) links.push(this.makeLink("Website", meta.website));
if (meta.source) links.push(this.makeLink("Source", meta.source));
if (meta.invite) {
links.push(this.makeLink("Support Server", () => {
const tester = /\.gg\/(.*)$/;
let code = meta.invite;
if (tester.test(code)) code = code.match(tester)[1];
BDV2.LayerStack.popLayer();
BDV2.InviteActions.acceptInviteAndTransitionToInviteChannel(code);
}));
}
if (meta.donate) links.push(this.makeLink("Donate", meta.donate));
if (meta.patreon) links.push(this.makeLink("Patreon", meta.patreon));
return links;
}
getFooter(meta) {
const links = this.getLinks(meta);
return (links.length || this.hasSettings) && BDV2.react.createElement("div", {className: "bda-footer"},
BDV2.react.createElement("span", {className: "bda-links"},
...(links.map((element, index) => index < links.length - 1 ? [element, " | "] : element).flat())
),
this.hasSettings && BDV2.react.createElement("button", {onClick: this.showSettings, className: "bda-settings-button", disabled: !this.state.checked}, "Settings")
);
}
render() {
if (this.state.settings) return this.settingsComponent;
const {plugin} = this.props;
const name = this.getString(plugin.getName());
const author = this.getString(plugin.getAuthor());
const description = this.getString(plugin.getDescription());
const version = this.getString(plugin.getVersion());
const meta = bdplugins[name];
return BDV2.react.createElement("div", {"data-name": name, "data-version": version, "className": "settings-closed ui-switch-item bd-addon-card"},
BDV2.react.createElement("div", {className: "bd-addon-header bda-header"},
BDV2.react.createElement("div", {className: "bd-title bda-header-title"}, this.buildTitle(name, version, {name: author, id: meta.authorId, link: meta.authorLink})),
BDV2.react.createElement("div", {className: "bd-addon-controls bda-controls"},
!settingsCookie["fork-ps-5"] && BDV2.react.createElement(TooltipWrap(ReloadIcon, {color: "black", side: "top", text: "Reload"}), {className: "bd-reload-card", onClick: this.reload}),
React.createElement(Switch, {onChange: this.onChange, checked: this.state.checked})
// 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: "bd-scroller-wrap bda-description-wrap scroller-wrap fade"},
BDV2.react.createElement("div", {className: "bd-addon-description bda-description scroller"}, description)
),
this.getFooter(meta)
);
}
onChange() {
this.setState({checked: !this.state.checked});
pluginModule.togglePlugin(this.props.plugin.getName());
}
showSettings() {
if (!this.hasSettings) return;
this.setState({settings: true});
}
}
// <div className="bd-controls">
// {this.props.editAddon && this.makeButton(Strings.Addons.editAddon, <EditIcon />, this.props.editAddon)}
// {this.props.deleteAddon && this.makeButton(Strings.Addons.deleteAddon, <DeleteIcon />, this.props.deleteAddon)}
// {this.props.showReloadIcon && this.makeButton(Strings.Addons.reload, <ReloadIcon className="bd-reload bd-reload-card" />, this.reload)}
// <Switch checked={this.props.enabled} onChange={this.onChange} />
// </div>
// componentDidUpdate() {
// if (!this.state.settingsOpen) return;
// if (this.settingsPanel instanceof Node) this.panelRef.current.appendChild(this.settingsPanel);
// // if (!SettingsCookie["fork-ps-3"]) return;
// const isHidden = (container, element) => {
// const cTop = container.scrollTop;
// const cBottom = cTop + container.clientHeight;
// const eTop = element.offsetTop;
// const eBottom = eTop + element.clientHeight;
// return (eTop < cTop || eBottom > cBottom);
// };
// const panel = $(this.panelRef.current);
// const container = panel.parents(".scroller-2FKFPG");
// if (!isHidden(container[0], panel[0])) return;
// container.animate({
// scrollTop: panel.offset().top - container.offset().top + container.scrollTop() - 30
// }, 300);
// }
// get settingsComponent() {
// const addon = this.props.addon;
// const name = this.getString(addon.name);
// try { this.settingsPanel = this.props.getSettingsPanel(); }
// catch (err) { Logger.stacktrace("Addon Settings", "Unable to get settings panel for " + name + ".", err); }
// const props = {id: `${name}-settings`, className: "addon-settings", ref: this.panelRef};
// if (typeof(settingsPanel) == "string") props.dangerouslySetInnerHTML = this.settingsPanel;
// return <div className="bd-addon-card settings-open bd-switch-item">
// <div className="bd-close" onClick={this.closeSettings}><CloseButton /></div>
// <div {...props}>{this.settingsPanel instanceof React.Component ? this.settingsPanel : null}</div>
// </div>;
// }

View File

@ -0,0 +1,89 @@
import BDV2 from "../../modules/v2";
import DOM from "../../modules/domtools";
export default class V2C_Layer extends BDV2.reactComponent {
constructor(props) {
super(props);
this.keyupListener = this.keyupListener.bind(this);
}
keyupListener(e) {
if (e.which === 27) {
BDV2.reactDom.unmountComponentAtNode(this.refs.root.parentNode);
}
}
componentDidMount() {
window.addEventListener("keyup", this.keyupListener);
const thisNode = DOM.query(`#${this.props.id}`);
DOM.animate({
duration: 200,
update: function(progress) {
thisNode.style.transform = `scale(${1.1 - 0.1 * progress}) translateZ(0px)`;
thisNode.style.opacity = progress;
if (progress == 1) {
setImmediate(() => {
thisNode.style.transform = "";
thisNode.style.opacity = "";
});
}
}
});
}
componentWillUnmount() {
window.removeEventListener("keyup", this.keyupListener);
const thisNode = DOM.query(`#${this.props.id}`);
DOM.animate({
duration: 200,
update: function(progress) {
thisNode.style.transform = `scale(${1.1 - 0.1 * (1 - progress)}) translateZ(0px)`;
thisNode.style.opacity = 1 - progress;
if (progress == 1) {
setImmediate(() => {
thisNode.remove();
});
}
}
});
const layer = DOM.query(".publicServersOpen");
layer.classList.remove("publicServersOpen");
DOM.animate({
duration: 200,
update: function(progress) {
layer.style.transform = `scale(${0.07 * progress + 0.93}) translateZ(0px)`;
layer.style.opacity = progress;
if (progress == 1) {
setImmediate(() => {
layer.style.transform = "";
layer.style.opacity = "";
});
}
}
});
}
componentWillMount() {
const layer = DOM.query("[class*=\"layer-\"]");
layer.classList.add("publicServersOpen");
DOM.animate({
duration: 200,
update: function(progress) {
layer.style.transform = `scale(${0.07 * (1 - progress) + 0.93}) translateZ(0px)`;
layer.style.opacity = 1 - progress;
}
});
}
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
);
}
}

View File

@ -1,12 +1,13 @@
import BDV2 from "../modules/v2";
import BDV2 from "../../modules/v2";
import Tools from "../tools";
import SettingsTitle from "../settingsTitle";
import TabBarSeparator from "../tabBarSeparator";
import TabBarHeader from "../tabBarHeader";
import TabBarItem from "../tabBarItem";
import Tools from "./tools";
import ServerCard from "./serverCard";
import SidebarView from "./sidebarView";
import SettingsTitle from "./settingsTitle";
import TabBarSeparator from "./tabBarSeparator";
import TabBarHeader from "./tabBarHeader";
import TabBarItem from "./tabBarItem";
export default class V2C_PublicServers extends BDV2.reactComponent {
@ -51,76 +52,68 @@ export default class V2C_PublicServers extends BDV2.reactComponent {
search(query, clear) {
const self = this;
$.ajax({
method: "GET",
url: `${self.endPoint}${query}${query ? "&schema=new" : "?schema=new"}`,
success: data => {
let servers = data.results.reduce((arr, server) => {
server.joined = false;
arr.push(server);
// arr.push(<ServerCard server={server} join={self.join}/>);
return arr;
}, []);
if (!clear) {
servers = self.state.servers.concat(servers);
}
else {
//servers.unshift(self.bdServer);
}
let end = data.size + data.from;
data.next = `?from=${end}`;
if (self.state.term) data.next += `&term=${self.state.term}`;
if (self.state.selectedCategory) data.next += `&category=${self.categoryButtons[self.state.selectedCategory]}`;
if (end >= data.total) {
end = data.total;
data.next = null;
}
let title = `Showing 1-${end} of ${data.total} results in ${self.categoryButtons[self.state.selectedCategory]}`;
if (self.state.term) title += ` for ${self.state.term}`;
self.setState({
loading: false,
title: title,
servers: servers,
next: data.next
});
if (clear) {
//console.log(self);
self.refs.sbv.refs.contentScroller.scrollTop = 0;
}
},
error: () => {
self.setState({
const request = require("request");
request.get({url: `${self.endPoint}${query}${query ? "&schema=new" : "?schema=new"}`, json: true}, function (err, resp, data) {
if (err) {
return self.setState({
loading: false,
title: "Failed to load servers. Check console for details"
});
}
let servers = data.results.reduce((arr, server) => {
server.joined = false;
arr.push(server);
// arr.push(<ServerCard server={server} join={self.join}/>);
return arr;
}, []);
if (!clear) {
servers = self.state.servers.concat(servers);
}
else {
//servers.unshift(self.bdServer);
}
let end = data.size + data.from;
data.next = `?from=${end}`;
if (self.state.term) data.next += `&term=${self.state.term}`;
if (self.state.selectedCategory) data.next += `&category=${self.categoryButtons[self.state.selectedCategory]}`;
if (end >= data.total) {
end = data.total;
data.next = null;
}
let title = `Showing 1-${end} of ${data.total} results in ${self.categoryButtons[self.state.selectedCategory]}`;
if (self.state.term) title += ` for ${self.state.term}`;
self.setState({
loading: false,
title: title,
servers: servers,
next: data.next
});
if (clear) {
//console.log(self);
self.refs.sbv.refs.contentScroller.scrollTop = 0;
}
});
}
join(serverCard) {
async join(serverCard) {
if (serverCard.props.pinned) return this.InviteActions.acceptInvite(serverCard.props.invite_code);
$.ajax({
await fetch(`${this.joinEndPoint}/${serverCard.props.server.identifier}`,{
method: "GET",
url: `${this.joinEndPoint}/${serverCard.props.server.identifier}`,
credentials: "include",
mode: "cors",
headers: {
"Accept": "application/json;",
"Content-Type": "application/json;" ,
"x-discord-token": this.state.connection.user.accessToken
},
crossDomain: true,
xhrFields: {
withCredentials: true
},
success: () => {
serverCard.setState({joined: true});
"Accept": "application/json",
"Content-Type": "application/json"
}
});
serverCard.setState({joined: true});
}
connect() {
@ -187,44 +180,27 @@ export default class V2C_PublicServers extends BDV2.reactComponent {
return "https://join.discordservers.com/connect";
}
checkConnection() {
async checkConnection() {
const self = this;
try {
$.ajax({
const response = await fetch(`https://auth.discordservers.com/info`,{
method: "GET",
url: `https://auth.discordservers.com/info`,
credentials: "include",
mode: "cors",
headers: {
"Accept": "application/json;",
"Content-Type": "application/json;"
},
crossDomain: true,
xhrFields: {
withCredentials: true
},
success: data => {
// Utils.log("PublicServer", "Got data: " + JSON.stringify(data));
self.setState({
selectedCategory: 0,
connection: {
state: 2,
user: data
}
});
self.search("", true);
},
error: () => {
self.setState({
title: "Not connected to discordservers.com!",
loading: true,
selectedCategory: -1,
connection: {
state: 1,
user: null
}
});
"Accept": "application/json",
"Content-Type": "application/json"
}
});
const data = await response.json();
self.setState({
selectedCategory: 0,
connection: {
state: 2,
user: data
}
});
self.search("", true);
}
catch (error) {
self.setState({
@ -240,7 +216,7 @@ export default class V2C_PublicServers extends BDV2.reactComponent {
}
render() {
return BDV2.react.createElement(SidebarView, {ref: "sbv", children: this.component});
return BDV2.react.createElement(SidebarView, {ref: "sbv"}, this.component);
}
get component() {

View File

@ -1,4 +1,4 @@
import BDV2 from "../modules/v2";
import BDV2 from "../../modules/v2";
export default class V2C_ServerCard extends BDV2.reactComponent {
constructor(props) {

View File

@ -1,6 +1,6 @@
import BDV2 from "../modules/v2";
import BDV2 from "../../modules/v2";
import Scroller from "./scroller";
import Scroller from "../scroller";
export default class V2C_SidebarView extends BDV2.reactComponent {
@ -16,7 +16,7 @@ export default class V2C_SidebarView extends BDV2.reactComponent {
BDV2.react.createElement(
"div",
{className: "sidebarRegion-VFTUkN sidebar-region"},
BDV2.react.createElement(Scroller, {key: "sidebarScroller", ref: "sidebarScroller", sidebar: true, fade: sidebar.fade || true, dark: sidebar.dark || true, children: sidebar.component})
BDV2.react.createElement(Scroller, {key: "sidebarScroller", ref: "sidebarScroller", sidebar: true, fade: sidebar.fade || true, dark: sidebar.dark || true}, sidebar.component)
),
BDV2.react.createElement("div", {className: "contentRegion-3nDuYy content-region"},
BDV2.react.createElement("div", {className: "contentTransitionWrap-3hqOEW content-transition-wrap"},

View File

@ -8,18 +8,22 @@ export default class V2C_SideBar extends BDV2.reactComponent {
constructor(props) {
super(props);
const self = this;
const si = $("[class*=side-] > [class*=selected]");
if (si.length) self.scn = si.attr("class");
const ns = $("[class*=side-] > [class*='item-']:not([class*=selected])");
if (ns.length) self.nscn = ns.attr("class");
$("[class*='side-'] > [class*='item-']").on("click", () => {
self.setState({
selected: null
const si = document.querySelector("[class*=side-] > [class*=selected]");
if (si) this.scn = si.className;
const ns = document.querySelector("[class*=side-] > [class*='item-']:not([class*=selected])");
if (ns) this.nscn = ns.className;
const tabs = document.querySelectorAll("[class*='side-'] > [class*='item-']");
for (const element of tabs) {
element.addEventListener("click", () => {
this.setState({
selected: null
});
});
});
self.setInitialState();
self.onClick = self.onClick.bind(self);
}
this.setInitialState();
this.onClick = this.onClick.bind(this);
this.setSelected = this.setSelected.bind(this);
}
setInitialState() {
@ -53,19 +57,21 @@ export default class V2C_SideBar extends BDV2.reactComponent {
);
}
setSelected(e) {
e.target.className = this.scn;
}
onClick(id) {
const self = this;
const si = $("[class*=side] > [class*=selected]");
if (si.length) {
si.off("click.bdsb").on("click.bsb", e => {
$(e.target).attr("class", self.scn);
});
si.attr("class", self.nscn);
const si = document.querySelector("[class*=side] > [class*=selected]");
if (si) {
si.removeEventListener("click", this.setSelected);
si.addEventListener("click", this.setSelected);
si.className = this.nscn;
}
self.setState({selected: null});
self.setState({selected: id});
this.setState({selected: null});
this.setState({selected: id});
if (self.props.onClick) self.props.onClick(id);
if (this.props.onClick) this.props.onClick(id);
}
}

View File

@ -1,119 +0,0 @@
import {settingsCookie, themeCookie, bdthemes} from "../0globals";
import Utils from "../modules/utils";
import BDV2 from "../modules/v2";
import themeModule from "../modules/themeModule";
import ReloadIcon from "./reloadIcon";
import TooltipWrap from "./tooltipWrap";
export default class V2C_ThemeCard extends BDV2.reactComponent {
constructor(props) {
super(props);
this.setInitialState();
this.onChange = this.onChange.bind(this);
this.reload = this.reload.bind(this);
}
setInitialState() {
this.state = {
checked: themeCookie[this.props.theme.name],
reloads: 0
};
}
// componentDidMount() {
// BDEvents.on("theme-reloaded", this.onReload);
// }
// componentWillUnmount() {
// BDEvents.off("theme-reloaded", this.onReload);
// }
onReload(themeName) {
if (themeName !== this.props.theme.name) return;
this.setState({reloads: this.state.reloads + 1});
}
reload() {
const theme = this.props.theme.name;
const error = themeModule.reloadTheme(theme);
if (error) Utils.showToast(`Could not reload ${bdthemes[theme].name}. Check console for details.`, {type: "error"});
else Utils.showToast(`${bdthemes[theme].name} v${bdthemes[theme].version} has been reloaded.`, {type: "success"});
// this.setState(this.state);
this.props.theme = bdthemes[theme];
this.onReload(this.props.theme.name);
}
makeLink(title, url) {
const props = {className: "bda-link bda-link-website", target: "_blank"};
if (typeof(url) == "string") props.href = url;
if (typeof(url) == "function") props.onClick = (event) => {event.preventDefault(); event.stopPropagation(); url();};
return BDV2.react.createElement("a", props, title);
}
render() {
const {theme} = this.props;
const name = theme.name;
const description = theme.description;
const version = theme.version;
const author = theme.author;
const meta = bdthemes[name];
const links = [];
if (meta.website) links.push(this.makeLink("Website", meta.website));
if (meta.source) links.push(this.makeLink("Source", meta.source));
if (meta.invite) {
links.push(this.makeLink("Support Server", () => {
const tester = /\.gg\/(.*)$/;
let code = meta.invite;
if (tester.test(code)) code = code.match(tester)[1];
BDV2.LayerStack.popLayer();
BDV2.InviteActions.acceptInviteAndTransitionToInviteChannel(code);
}));
}
if (meta.donate) links.push(this.makeLink("Donate", meta.donate));
if (meta.patreon) links.push(this.makeLink("Patreon", meta.patreon));
const authorProps = {className: "bda-author"};
if (meta.authorLink || meta.authorId) {
authorProps.className += ` ${BDV2.anchorClasses.anchor} ${BDV2.anchorClasses.anchorUnderlineOnHover}`;
authorProps.target = "_blank";
if (meta.authorLink) authorProps.href = meta.authorLink;
if (meta.authorId) authorProps.onClick = () => {BDV2.LayerStack.popLayer(); BDV2.openDM(meta.authorId);};
}
return BDV2.react.createElement("div", {"data-name": name, "data-version": version, "className": "settings-closed ui-switch-item bd-addon-card"},
BDV2.react.createElement("div", {className: "bda-header"},
BDV2.react.createElement("span", {className: "bda-header-title"},
BDV2.react.createElement("span", {className: "bda-name"}, name),
" v",
BDV2.react.createElement("span", {className: "bda-version"}, version),
" by ",
BDV2.react.createElement(meta.authorLink || meta.authorId ? "a" : "span", authorProps, author)
),
BDV2.react.createElement("div", {className: "bda-controls"},
!settingsCookie["fork-ps-5"] && BDV2.react.createElement(TooltipWrap(ReloadIcon, {color: "black", side: "top", text: "Reload"}), {className: "bd-reload-card", onClick: this.reload}),
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"}, description)
),
(!!links.length) && BDV2.react.createElement("div", {className: "bda-footer"},
BDV2.react.createElement("span", {className: "bda-links"},
...(links.map((element, index) => index < links.length - 1 ? [element, " | "] : element).flat())
)
)
);
}
onChange() {
this.setState({checked: !this.state.checked});
themeModule.toggleTheme(this.props.theme.name);
}
}

View File

@ -31,6 +31,7 @@ export default class V2C_Tools extends BDV2.reactComponent {
if (this.props.onClick) {
this.props.onClick();
}
$(".closeButton-1tv5uR").first().click();
const closeButton = document.querySelector(".closeButton-1tv5uR");
if (closeButton) closeButton.click();
}
}

View File

@ -12,6 +12,7 @@
import Utils from "../modules/utils";
import WebpackModules from "../modules/webpackModules";
import DOM from "../modules/domtools";
const TooltipClasses = WebpackModules.findByProps("tooltip", "tooltipBlack");
const TooltipLayers = WebpackModules.findByProps("layer", "layerContainer");
@ -62,8 +63,8 @@ export default class EmulatedTooltip {
if (!classExists(this.side)) return Utils.err("EmulatedTooltip", `Side ${this.side} does not exist.`);
if (!classExists(this.style)) return Utils.err("EmulatedTooltip", `Style ${this.style} does not exist.`);
this.element = $(`<div class="${TooltipLayers.layer}">`)[0];
this.tooltipElement = $(`<div class="${TooltipClasses.tooltip} ${getClass(this.style)}"><div class="${TooltipClasses.tooltipPointer}"></div>${this.label}</div>`)[0];
this.element = DOM.createElement(`<div class="${TooltipLayers.layer}">`);
this.tooltipElement = DOM.createElement(`<div class="${TooltipClasses.tooltip} ${getClass(this.style)}"><div class="${TooltipClasses.tooltipPointer}"></div>${this.label}</div>`);
this.labelElement = this.tooltipElement.childNodes[1];
this.element.append(this.tooltipElement);