Another copy of my dotfiles. Because I don't completely trust GitHub.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

9008 lines
407 KiB

  1. /**
  2. * @name ZeresPluginLibrary
  3. * @version 1.2.29
  4. * @invite TyFxKer
  5. * @authorLink https://twitter.com/ZackRauen
  6. * @donate https://paypal.me/ZackRauen
  7. * @patreon https://patreon.com/Zerebos
  8. * @website https://github.com/rauenzi/BDPluginLibrary
  9. * @source https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js
  10. */
  11. /*@cc_on
  12. @if (@_jscript)
  13. // Offer to self-install for clueless users that try to run this directly.
  14. var shell = WScript.CreateObject("WScript.Shell");
  15. var fs = new ActiveXObject("Scripting.FileSystemObject");
  16. var pathPlugins = shell.ExpandEnvironmentStrings("%APPDATA%\BetterDiscord\plugins");
  17. var pathSelf = WScript.ScriptFullName;
  18. // Put the user at ease by addressing them in the first person
  19. shell.Popup("It looks like you've mistakenly tried to run me directly. \n(Don't do that!)", 0, "I'm a plugin for BetterDiscord", 0x30);
  20. if (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) {
  21. shell.Popup("I'm in the correct folder already.", 0, "I'm already installed", 0x40);
  22. } else if (!fs.FolderExists(pathPlugins)) {
  23. shell.Popup("I can't find the BetterDiscord plugins folder.\nAre you sure it's even installed?", 0, "Can't install myself", 0x10);
  24. } else if (shell.Popup("Should I copy myself to BetterDiscord's plugins folder for you?", 0, "Do you need some help?", 0x34) === 6) {
  25. fs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true);
  26. // Show the user where to put plugins in the future
  27. shell.Exec("explorer " + pathPlugins);
  28. shell.Popup("I'm installed!", 0, "Successfully installed", 0x40);
  29. }
  30. WScript.Quit();
  31. @else@*/
  32. module.exports =
  33. /******/ (function(modules) { // webpackBootstrap
  34. /******/ // The module cache
  35. /******/ var installedModules = {};
  36. /******/
  37. /******/ // The require function
  38. /******/ function __webpack_require__(moduleId) {
  39. /******/
  40. /******/ // Check if module is in cache
  41. /******/ if(installedModules[moduleId]) {
  42. /******/ return installedModules[moduleId].exports;
  43. /******/ }
  44. /******/ // Create a new module (and put it into the cache)
  45. /******/ var module = installedModules[moduleId] = {
  46. /******/ i: moduleId,
  47. /******/ l: false,
  48. /******/ exports: {}
  49. /******/ };
  50. /******/
  51. /******/ // Execute the module function
  52. /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  53. /******/
  54. /******/ // Flag the module as loaded
  55. /******/ module.l = true;
  56. /******/
  57. /******/ // Return the exports of the module
  58. /******/ return module.exports;
  59. /******/ }
  60. /******/
  61. /******/
  62. /******/ // expose the modules object (__webpack_modules__)
  63. /******/ __webpack_require__.m = modules;
  64. /******/
  65. /******/ // expose the module cache
  66. /******/ __webpack_require__.c = installedModules;
  67. /******/
  68. /******/ // define getter function for harmony exports
  69. /******/ __webpack_require__.d = function(exports, name, getter) {
  70. /******/ if(!__webpack_require__.o(exports, name)) {
  71. /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
  72. /******/ }
  73. /******/ };
  74. /******/
  75. /******/ // define __esModule on exports
  76. /******/ __webpack_require__.r = function(exports) {
  77. /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
  78. /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
  79. /******/ }
  80. /******/ Object.defineProperty(exports, '__esModule', { value: true });
  81. /******/ };
  82. /******/
  83. /******/ // create a fake namespace object
  84. /******/ // mode & 1: value is a module id, require it
  85. /******/ // mode & 2: merge all properties of value into the ns
  86. /******/ // mode & 4: return value when already ns object
  87. /******/ // mode & 8|1: behave like require
  88. /******/ __webpack_require__.t = function(value, mode) {
  89. /******/ if(mode & 1) value = __webpack_require__(value);
  90. /******/ if(mode & 8) return value;
  91. /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
  92. /******/ var ns = Object.create(null);
  93. /******/ __webpack_require__.r(ns);
  94. /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
  95. /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
  96. /******/ return ns;
  97. /******/ };
  98. /******/
  99. /******/ // getDefaultExport function for compatibility with non-harmony modules
  100. /******/ __webpack_require__.n = function(module) {
  101. /******/ var getter = module && module.__esModule ?
  102. /******/ function getDefault() { return module['default']; } :
  103. /******/ function getModuleExports() { return module; };
  104. /******/ __webpack_require__.d(getter, 'a', getter);
  105. /******/ return getter;
  106. /******/ };
  107. /******/
  108. /******/ // Object.prototype.hasOwnProperty.call
  109. /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
  110. /******/
  111. /******/ // __webpack_public_path__
  112. /******/ __webpack_require__.p = "";
  113. /******/
  114. /******/
  115. /******/ // Load entry module and return exports
  116. /******/ return __webpack_require__(__webpack_require__.s = "./src/index.js");
  117. /******/ })
  118. /************************************************************************/
  119. /******/ ({
  120. /***/ "./src/config.js":
  121. /*!***********************!*\
  122. !*** ./src/config.js ***!
  123. \***********************/
  124. /*! no static exports found */
  125. /***/ (function(module, exports) {
  126. module.exports = {
  127. info: {
  128. name: "ZeresPluginLibrary",
  129. authors: [{
  130. name: "Zerebos",
  131. discord_id: "249746236008169473",
  132. github_username: "rauenzi",
  133. twitter_username: "ZackRauen"
  134. }],
  135. version: "1.2.29",
  136. description: "Gives other plugins utility functions and the ability to emulate v2.",
  137. github: "https://github.com/rauenzi/BDPluginLibrary",
  138. github_raw: "https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js"
  139. },
  140. changelog: [
  141. {
  142. title: "Internal Changes",
  143. type: "fixed",
  144. items: [
  145. "Changes how elements and jQuery are resolved internally that could cause crashes when jQuery doesn't exist.",
  146. ]
  147. },
  148. ],
  149. main: "plugin.js"
  150. };
  151. /***/ }),
  152. /***/ "./src/index.js":
  153. /*!**********************!*\
  154. !*** ./src/index.js ***!
  155. \**********************/
  156. /*! exports provided: default */
  157. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  158. "use strict";
  159. __webpack_require__.r(__webpack_exports__);
  160. /* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
  161. /* harmony import */ var ui__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ui */ "./src/ui/ui.js");
  162. const Library = {};
  163. Library.DiscordContextMenu = ui__WEBPACK_IMPORTED_MODULE_1__["DiscordContextMenu"];
  164. Library.DCM = ui__WEBPACK_IMPORTED_MODULE_1__["DiscordContextMenu"];
  165. Library.ContextMenu = ui__WEBPACK_IMPORTED_MODULE_1__["ContextMenu"];
  166. Library.Tooltip = ui__WEBPACK_IMPORTED_MODULE_1__["Tooltip"];
  167. Library.EmulatedTooltip = ui__WEBPACK_IMPORTED_MODULE_1__["Tooltip"]; // @deprecated 12/3/2020 the original Tooltip module was replaced with the EmulatedTooltip.
  168. Library.Toasts = ui__WEBPACK_IMPORTED_MODULE_1__["Toasts"];
  169. Library.Settings = ui__WEBPACK_IMPORTED_MODULE_1__["Settings"];
  170. Library.Popouts = ui__WEBPACK_IMPORTED_MODULE_1__["Popouts"];
  171. Library.Modals = ui__WEBPACK_IMPORTED_MODULE_1__["Modals"];
  172. for (const mod in modules__WEBPACK_IMPORTED_MODULE_0__) Library[mod] = modules__WEBPACK_IMPORTED_MODULE_0__[mod];
  173. Library.Components = {ErrorBoundary: ui__WEBPACK_IMPORTED_MODULE_1__["ErrorBoundary"]};
  174. const config = __webpack_require__(/*! ./src/config.js */ "./src/config.js");
  175. const baseModule = __webpack_require__(/*! ./src/plugin.js */ "./src/plugin.js");
  176. const pluginFunction = baseModule.default ? baseModule.default : baseModule;
  177. const getBoundLibrary = () => {
  178. const name = config.info.name;
  179. const BoundAPI = {
  180. Logger: {
  181. stacktrace: (message, error) => Library.Logger.stacktrace(name, message, error),
  182. log: (...message) => Library.Logger.log(name, ...message),
  183. error: (...message) => Library.Logger.err(name, ...message),
  184. err: (...message) => Library.Logger.err(name, ...message),
  185. warn: (...message) => Library.Logger.warn(name, ...message),
  186. info: (...message) => Library.Logger.info(name, ...message),
  187. debug: (...message) => Library.Logger.debug(name, ...message)
  188. },
  189. Patcher: {
  190. getPatchesByCaller: () => {return Library.Patcher.getPatchesByCaller(name);},
  191. unpatchAll: () => {return Library.Patcher.unpatchAll(name);},
  192. before: (moduleToPatch, functionName, callback, options = {}) => {return Library.Patcher.before(name, moduleToPatch, functionName, callback, options);},
  193. instead: (moduleToPatch, functionName, callback, options = {}) => {return Library.Patcher.instead(name, moduleToPatch, functionName, callback, options);},
  194. after: (moduleToPatch, functionName, callback, options = {}) => {return Library.Patcher.after(name, moduleToPatch, functionName, callback, options);}
  195. }
  196. };
  197. const BoundLib = Object.assign({}, Library);
  198. BoundLib.Logger = BoundAPI.Logger;
  199. BoundLib.Patcher = BoundAPI.Patcher;
  200. return BoundLib;
  201. };
  202. /* harmony default export */ __webpack_exports__["default"] = (pluginFunction(Library.Structs.Plugin(config), false ? undefined : Library)); // eslint-disable-line new-cap
  203. /***/ }),
  204. /***/ "./src/modules/colorconverter.js":
  205. /*!***************************************!*\
  206. !*** ./src/modules/colorconverter.js ***!
  207. \***************************************/
  208. /*! exports provided: default */
  209. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  210. "use strict";
  211. __webpack_require__.r(__webpack_exports__);
  212. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return ColorConverter; });
  213. /* harmony import */ var _webpackmodules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./webpackmodules */ "./src/modules/webpackmodules.js");
  214. /**
  215. * Helpful utilities for dealing with colors.
  216. * @module ColorConverter
  217. * @version 0.0.2
  218. */
  219. const DiscordColorUtils = _webpackmodules__WEBPACK_IMPORTED_MODULE_0__["default"].getByProps("getDarkness", "isValidHex");
  220. class ColorConverter {
  221. static getDarkness(color) {
  222. return DiscordColorUtils.getDarkness(color);
  223. }
  224. static hex2int(color) {return DiscordColorUtils.hex2int(color);}
  225. static hex2rgb(color) {return DiscordColorUtils.hex2rgb(color);}
  226. static int2hex(color) {return DiscordColorUtils.int2hex(color);}
  227. static int2rgba(color, alpha) {return DiscordColorUtils.int2rgba(color, alpha);}
  228. static isValidHex(color) {return DiscordColorUtils.isValidHex(color);}
  229. /**
  230. * Will get the red green and blue values of any color string.
  231. * @param {string} color - the color to obtain the red, green and blue values of. Can be in any of these formats: #fff, #ffffff, rgb, rgba
  232. * @returns {array} - array containing the red, green, and blue values
  233. */
  234. static getRGB(color) {
  235. let result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color);
  236. if (result) return [parseInt(result[1]), parseInt(result[2]), parseInt(result[3])];
  237. result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*\)/.exec(color);
  238. if (result) return [parseFloat(result[1]) * 2.55, parseFloat(result[2]) * 2.55, parseFloat(result[3]) * 2.55];
  239. result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color);
  240. if (result) return [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)];
  241. result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color);
  242. if (result) return [parseInt(result[1] + result[1], 16), parseInt(result[2] + result[2], 16), parseInt(result[3] + result[3], 16)];
  243. }
  244. /**
  245. * Will get the darken the color by a certain percent
  246. * @param {string} color - Can be in any of these formats: #fff, #ffffff, rgb, rgba
  247. * @param {number} percent - percent to darken the color by (0-100)
  248. * @returns {string} - new color in rgb format
  249. */
  250. static darkenColor(color, percent) {
  251. const rgb = this.getRGB(color);
  252. for (let i = 0; i < rgb.length; i++) rgb[i] = Math.round(Math.max(0, rgb[i] - rgb[i] * (percent / 100)));
  253. return "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")";
  254. }
  255. /**
  256. * Will get the lighten the color by a certain percent
  257. * @param {string} color - Can be in any of these formats: #fff, #ffffff, rgb, rgba
  258. * @param {number} percent - percent to lighten the color by (0-100)
  259. * @returns {string} - new color in rgb format
  260. */
  261. static lightenColor(color, percent) {
  262. const rgb = this.getRGB(color);
  263. for (let i = 0; i < rgb.length; i++) rgb[i] = Math.round(Math.min(255, rgb[i] + rgb[i] * (percent / 100)));
  264. return "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")";
  265. }
  266. /**
  267. * Converts a color to rgba format string
  268. * @param {string} color - Can be in any of these formats: #fff, #ffffff, rgb, rgba
  269. * @param {number} alpha - alpha level for the new color
  270. * @returns {string} - new color in rgb format
  271. */
  272. static rgbToAlpha(color, alpha) {
  273. const rgb = this.getRGB(color);
  274. return "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "," + alpha + ")";
  275. }
  276. }
  277. /***/ }),
  278. /***/ "./src/modules/discordapi.js":
  279. /*!***********************************!*\
  280. !*** ./src/modules/discordapi.js ***!
  281. \***********************************/
  282. /*! exports provided: default */
  283. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  284. "use strict";
  285. __webpack_require__.r(__webpack_exports__);
  286. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return DiscordAPI; });
  287. /* harmony import */ var structs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! structs */ "./src/structs/structs.js");
  288. /* harmony import */ var _discordmodules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./discordmodules */ "./src/modules/discordmodules.js");
  289. /**
  290. * BetterDiscord Discord API
  291. * Copyright (c) 2018-present JsSucks
  292. * All rights reserved.
  293. *
  294. * This source code is licensed under the MIT license found at
  295. * https://github.com/JsSucks/BetterDiscordApp/blob/master/LICENSE
  296. */
  297. /**
  298. * A large list of known and useful webpack modules internal to Discord.
  299. * Click the filename below to see the whole list.
  300. * @deprecated 7/27/2020
  301. * @module DiscordAPI
  302. * @version 0.0.1
  303. */
  304. class DiscordAPI {
  305. static get InsufficientPermissions() {return structs__WEBPACK_IMPORTED_MODULE_0__["InsufficientPermissions"];}
  306. static get List() {return structs__WEBPACK_IMPORTED_MODULE_0__["List"];}
  307. static get User() {return structs__WEBPACK_IMPORTED_MODULE_0__["User"];}
  308. static get Channel() {return structs__WEBPACK_IMPORTED_MODULE_0__["Channel"];}
  309. static get Guild() {return structs__WEBPACK_IMPORTED_MODULE_0__["Guild"];}
  310. static get Message() {return structs__WEBPACK_IMPORTED_MODULE_0__["Message"];}
  311. static get UserSettings() {return structs__WEBPACK_IMPORTED_MODULE_0__["UserSettings"];}
  312. /**
  313. * A list of loaded guilds.
  314. */
  315. static get guilds() {
  316. const guilds = _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].GuildStore.getGuilds();
  317. return structs__WEBPACK_IMPORTED_MODULE_0__["List"].from(Object.values(guilds), g => structs__WEBPACK_IMPORTED_MODULE_0__["Guild"].from(g));
  318. }
  319. /**
  320. * A list of loaded channels.
  321. */
  322. static get channels() {
  323. const channels = _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].ChannelStore.getChannels();
  324. return structs__WEBPACK_IMPORTED_MODULE_0__["List"].from(Object.values(channels), c => structs__WEBPACK_IMPORTED_MODULE_0__["Channel"].from(c));
  325. }
  326. /**
  327. * A list of loaded users.
  328. */
  329. static get users() {
  330. const users = _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].UserStore.getUsers();
  331. return structs__WEBPACK_IMPORTED_MODULE_0__["List"].from(Object.values(users), u => structs__WEBPACK_IMPORTED_MODULE_0__["User"].from(u));
  332. }
  333. /**
  334. * An object mapping guild IDs to their member counts.
  335. */
  336. static get memberCounts() {
  337. return _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].MemberCountStore.getMemberCounts();
  338. }
  339. /**
  340. * A list of guilds in the order they appear in the server list.
  341. */
  342. static get sortedGuilds() {
  343. const guilds = _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].SortedGuildStore.getSortedGuilds();
  344. return structs__WEBPACK_IMPORTED_MODULE_0__["List"].from(guilds, g => structs__WEBPACK_IMPORTED_MODULE_0__["Guild"].from(g));
  345. }
  346. /**
  347. * An array of guild IDs in the order they appear in the server list.
  348. */
  349. static get guildPositions() {
  350. return _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].SortedGuildStore.guildPositions;
  351. }
  352. /**
  353. * The currently selected guild.
  354. */
  355. static get currentGuild() {
  356. const guild = _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].GuildStore.getGuild(_discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].SelectedGuildStore.getGuildId());
  357. return guild ? structs__WEBPACK_IMPORTED_MODULE_0__["Guild"].from(guild) : null;
  358. }
  359. /**
  360. * The currently selected channel.
  361. */
  362. static get currentChannel() {
  363. const channel = _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].ChannelStore.getChannel(_discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].SelectedChannelStore.getChannelId());
  364. return channel ? structs__WEBPACK_IMPORTED_MODULE_0__["Channel"].from(channel) : null;
  365. }
  366. /**
  367. * The current user.
  368. */
  369. static get currentUser() {
  370. const user = _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].UserStore.getCurrentUser();
  371. return user ? structs__WEBPACK_IMPORTED_MODULE_0__["User"].from(user) : null;
  372. }
  373. /**
  374. * A list of the current user's friends.
  375. */
  376. static get friends() {
  377. const friends = _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].RelationshipStore.getFriendIDs();
  378. return structs__WEBPACK_IMPORTED_MODULE_0__["List"].from(friends, id => structs__WEBPACK_IMPORTED_MODULE_0__["User"].fromId(id));
  379. }
  380. }
  381. /***/ }),
  382. /***/ "./src/modules/discordclasses.js":
  383. /*!***************************************!*\
  384. !*** ./src/modules/discordclasses.js ***!
  385. \***************************************/
  386. /*! exports provided: default */
  387. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  388. "use strict";
  389. __webpack_require__.r(__webpack_exports__);
  390. /* harmony import */ var _discordclassmodules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./discordclassmodules */ "./src/modules/discordclassmodules.js");
  391. /* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js");
  392. const getRaw = function(prop) {
  393. if (!this.hasOwnProperty(prop)) return "";
  394. return this[prop];
  395. };
  396. const getClass = function(prop) {
  397. if (!this.hasOwnProperty(prop)) return "";
  398. return this[prop].split(" ")[0];
  399. };
  400. /**
  401. * Proxy for all the class packages, allows us to safely attempt
  402. * to retrieve nested things without error. Also wraps the class in
  403. * {@link module:DOMTools.ClassName} which adds features but can still
  404. * be used in native function.
  405. *
  406. * For a list of all available class namespaces check out {@link module:DiscordClassModules}.
  407. *
  408. * @see module:DiscordClassModules
  409. * @module DiscordClasses
  410. * @version 0.1.0
  411. */
  412. const DiscordModules = new Proxy(_discordclassmodules__WEBPACK_IMPORTED_MODULE_0__["default"], {
  413. get: function(list, item) {
  414. if (item == "getRaw" || item == "getClass") return (module, prop) => DiscordModules[module][item]([prop]);
  415. if (list[item] === undefined) return new Proxy({}, {get: function() {return "";}});
  416. return new Proxy(list[item], {
  417. get: function(obj, prop) {
  418. if (prop == "getRaw") return getRaw.bind(obj);
  419. if (prop == "getClass") return getClass.bind(obj);
  420. if (!obj.hasOwnProperty(prop)) return "";
  421. return new _domtools__WEBPACK_IMPORTED_MODULE_1__["default"].ClassName(obj[prop]);
  422. }
  423. });
  424. }
  425. });
  426. /* harmony default export */ __webpack_exports__["default"] = (DiscordModules);
  427. /***/ }),
  428. /***/ "./src/modules/discordclassmodules.js":
  429. /*!********************************************!*\
  430. !*** ./src/modules/discordclassmodules.js ***!
  431. \********************************************/
  432. /*! exports provided: default */
  433. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  434. "use strict";
  435. __webpack_require__.r(__webpack_exports__);
  436. /* harmony import */ var _utilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utilities */ "./src/modules/utilities.js");
  437. /* harmony import */ var _webpackmodules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./webpackmodules */ "./src/modules/webpackmodules.js");
  438. /**
  439. * A large list of known and labelled classes in discord.
  440. * Click the source link down below to view more info. Otherwise, if you
  441. * have the library installed or have a plugin using this library,
  442. * do `Object.keys(ZLibrary.DiscordClassModules)` in console for a list of modules.
  443. *
  444. * You can use this directly, however the preferred way of doing this is to use {@link module:DiscordClasses} or {@link module:DiscordSelectors}
  445. *
  446. * @see module:DiscordClasses
  447. * @see module:DiscordSelectors
  448. * @module DiscordClassModules
  449. * @version 0.0.2
  450. */
  451. /* harmony default export */ __webpack_exports__["default"] = (_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].memoizeObject({
  452. get ContextMenu() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("menu", "item");},
  453. get Scrollers() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("scrollerWrap", "scrollerThemed", "scrollerTrack");},
  454. get AccountDetails() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("container", "avatar", "hasBuildOverride");},
  455. get Typing() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("typing", "text");},
  456. get UserPopout() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("userPopout");},
  457. get PopoutRoles() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("roleCircle");},
  458. get UserModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("profileBadge");},
  459. get Textarea() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("channelTextArea", "textArea");},
  460. get Popouts() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("popouts", "popout");},
  461. get Titles() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("defaultMarginh5");},
  462. get Notices() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("notice", "colorInfo");},
  463. get Backdrop() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("backdrop");},
  464. get Modals() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m.modal && m.inner && !m.header);},
  465. get AuditLog() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("userHook");},
  466. get ChannelList() {return Object.assign({}, _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("containerDefault"), _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("name", "unread"), _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("sidebar", "hasNotice"));},
  467. get MemberList() {return Object.assign({}, _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("member", "memberInner"), _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("members", "membersWrap"));},
  468. get TitleWrap() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("titleWrapper");},
  469. get Titlebar() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("titleBar");},
  470. get Embeds() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("embed", "embedAuthor");},
  471. get Layers() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("layers", "layer");},
  472. get TooltipLayers() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("layerContainer", "layer");},
  473. get Margins() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => !m.title && m.marginBottom40 && m.marginTop40);},
  474. get Dividers() {return Object.assign({}, _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("dividerDefault"), _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => Object.keys(m).length == 1 && m.divider));},
  475. get Changelog() {return Object.assign({}, _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("container", "added"), _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("content", "modal", "size"));},
  476. get BasicInputs() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("inputDefault");},
  477. get Messages() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("message", "containerCozy");},
  478. get Guilds() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("guildsWrapper");},
  479. get EmojiPicker() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("emojiPicker", "emojiItem");},
  480. get Reactions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("reaction", "reactionInner");},
  481. get Checkbox() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("checkbox", "checkboxInner");},
  482. get Tooltips() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("tooltip", "tooltipBlack");}
  483. }));
  484. /***/ }),
  485. /***/ "./src/modules/discordmodules.js":
  486. /*!***************************************!*\
  487. !*** ./src/modules/discordmodules.js ***!
  488. \***************************************/
  489. /*! exports provided: default */
  490. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  491. "use strict";
  492. __webpack_require__.r(__webpack_exports__);
  493. /* harmony import */ var _utilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utilities */ "./src/modules/utilities.js");
  494. /* harmony import */ var _webpackmodules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./webpackmodules */ "./src/modules/webpackmodules.js");
  495. /**
  496. * A large list of known and useful webpack modules internal to Discord.
  497. * Click the source link down below to view more info. Otherwise, if you
  498. * have the library installed or have a plugin using this library,
  499. * do `Object.keys(ZLibrary.DiscordModules)` in console for a list of modules.
  500. * @module DiscordModules
  501. * @version 0.0.3
  502. */
  503. /* harmony default export */ __webpack_exports__["default"] = (_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].memoizeObject({
  504. get React() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("createElement", "cloneElement");},
  505. get ReactDOM() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("render", "findDOMNode");},
  506. get Events() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByPrototypes("setMaxListeners", "emit");},
  507. /* Guild Info, Stores, and Utilities */
  508. get GuildStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getGuild");},
  509. get SortedGuildStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getSortedGuilds");},
  510. get SelectedGuildStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getLastSelectedGuildId");},
  511. get GuildSync() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getSyncedGuilds");},
  512. get GuildInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getAcronym");},
  513. get GuildChannelsStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getChannels", "getDefaultChannel");},
  514. get GuildMemberStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getMember");},
  515. get MemberCountStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getMemberCounts");},
  516. get GuildEmojiStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getEmojis");},
  517. get GuildActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("requestMembers");}, // apparently it's back
  518. get GuildPermissions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getGuildPermissions");},
  519. /* Channel Store & Actions */
  520. get ChannelStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getChannel", "getDMFromUserId");},
  521. get SelectedChannelStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getLastSelectedChannelId");},
  522. get ChannelActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("selectChannel");},
  523. get PrivateChannelActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("openPrivateChannel");},
  524. // Absorbed into ChannelActions
  525. // get ChannelSelector() {return WebpackModules.getByProps("selectGuild", "selectChannel");},
  526. /* Current User Info, State and Settings */
  527. get UserInfoStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getSessionId");},
  528. get UserSettingsStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("guildPositions");},
  529. // Not really needed by plugins
  530. // get AccountManager() {return WebpackModules.getByProps("register", "login");},
  531. get UserSettingsUpdater() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("updateRemoteSettings");},
  532. get OnlineWatcher() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("isOnline");},
  533. get CurrentUserIdle() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("isIdle");},
  534. get RelationshipStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("isBlocked", "getFriendIDs");},
  535. get RelationshipManager() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("addRelationship");},
  536. get MentionStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getMentions");},
  537. /* User Stores and Utils */
  538. get UserStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getCurrentUser");},
  539. get UserStatusStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getStatus", "getState");},
  540. get UserTypingStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("isTyping");},
  541. get UserActivityStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getActivity");},
  542. get UserNameResolver() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getName");},
  543. get UserNoteStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getNote");},
  544. get UserNoteActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("updateNote");},
  545. /* Emoji Store and Utils */
  546. get EmojiInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("isEmojiDisabled");},
  547. get EmojiUtils() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getGuildEmoji");},
  548. get EmojiStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getByCategory", "EMOJI_NAME_RE");},
  549. /* Invite Store and Utils */
  550. get InviteStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getInvites");},
  551. get InviteResolver() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("resolveInvite");},
  552. get InviteActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("acceptInvite");},
  553. /* Discord Objects & Utils */
  554. get DiscordConstants() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Permissions", "ActivityTypes", "StatusTypes");},
  555. get DiscordPermissions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Permissions", "ActivityTypes", "StatusTypes").Permissions;},
  556. get Permissions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("computePermissions");},
  557. get ColorConverter() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("hex2int");},
  558. get ColorShader() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("darken");},
  559. get TinyColor() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByPrototypes("toRgb");},
  560. get ClassResolver() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getClass");},
  561. get ButtonData() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("ButtonSizes");},
  562. // They removed this
  563. // get IconNames() {return WebpackModules.getByProps("IconNames");},
  564. get NavigationUtils() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("transitionTo", "replaceWith", "getHistory");},
  565. /* Discord Messages */
  566. get MessageStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getMessages");},
  567. get MessageActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("jumpToMessage", "_sendMessage");},
  568. get MessageQueue() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("enqueue");},
  569. get MessageParser() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => Object.keys(m).length && Object.keys(m).every(k => k === "parse" || k === "unparse"));},
  570. /* In-Game Overlay */
  571. // Plugins don't need these
  572. // get OverlayUserPopoutSettings() {return WebpackModules.getByProps("openUserPopout");},
  573. // get OverlayUserPopoutInfo() {return WebpackModules.getByProps("getOpenedUserPopout");},
  574. /* Experiments */
  575. get ExperimentStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getExperimentOverrides");},
  576. get ExperimentsManager() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("isDeveloper");},
  577. get CurrentExperiment() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getExperimentId");},
  578. /* Streams */
  579. get StreamStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getAllActiveStreams", "getStreamForUser");},
  580. get StreamPreviewStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getIsPreviewLoading", "getPreviewURL");},
  581. /* Images, Avatars and Utils */
  582. get ImageResolver() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getUserAvatarURL", "getGuildIconURL");},
  583. get ImageUtils() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getSizedImageSrc");},
  584. get AvatarDefaults() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getUserAvatarURL", "DEFAULT_AVATARS");},
  585. /* Drag & Drop */
  586. // No longer a part of their DND arch
  587. // get DNDActions() {return WebpackModules.getByProps("beginDrag");},
  588. get DNDSources() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("addTarget");},
  589. get DNDObjects() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("DragSource");},
  590. /* Electron & Other Internals with Utils*/
  591. get ElectronModule() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("setBadge");},
  592. get Dispatcher() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("dirtyDispatch");},
  593. get PathUtils() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("hasBasename");},
  594. get NotificationModule() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("showNotification");},
  595. get RouterModule() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Router");},
  596. get APIModule() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getAPIBaseURL");},
  597. get AnalyticEvents() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("AnalyticEventConfigs");},
  598. get KeyGenerator() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByRegex(/"binary"/);},
  599. get Buffers() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Buffer", "kMaxLength");},
  600. get DeviceStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getDevices");},
  601. get SoftwareInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("os");},
  602. // Absorbed into Sentry
  603. // get CurrentContext() {return WebpackModules.getByProps("setTagsContext");},
  604. /* Media Stuff (Audio/Video) */
  605. get MediaDeviceInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Codecs", "MediaEngineContextTypes");},
  606. get MediaInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getOutputVolume");},
  607. get MediaEngineInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("determineMediaEngine");},
  608. get VoiceInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getEchoCancellation");},
  609. // DNE with restructure
  610. // get VideoStream() {return WebpackModules.getByProps("getVideoStream");},
  611. get SoundModule() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("playSound");},
  612. /* Window, DOM, HTML */
  613. get WindowInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("isFocused", "windowSize");},
  614. // Was never needed anyway
  615. // get TagInfo() {return WebpackModules.getByProps("VALID_TAG_NAMES");},
  616. get DOMInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("canUseDOM");},
  617. /* Locale/Location and Time */
  618. get LocaleManager() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("setLocale");},
  619. get Moment() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("parseZone");},
  620. get LocationManager() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("createLocation");},
  621. get Timestamps() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("fromTimestamp");},
  622. /* Strings and Utils */
  623. get Strings() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Messages").Messages;},
  624. get StringFormats() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("a", "z");},
  625. get StringUtils() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("toASCII");},
  626. /* URLs and Utils */
  627. get URLParser() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Url", "parse");},
  628. get ExtraURLs() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getArticleURL");},
  629. /* Text Processing */
  630. get hljs() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("highlight", "highlightBlock");},
  631. get SimpleMarkdown() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("parseBlock", "parseInline", "defaultOutput");},
  632. /* DOM/React Components */
  633. /* ==================== */
  634. get LayerManager() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("popLayer", "pushLayer");},
  635. // Restructured away
  636. // get Tooltips() {return WebpackModules.find(m => m.hide && m.show && !m.search && !m.submit && !m.search && !m.activateRagingDemon && !m.dismiss);},
  637. get UserSettingsWindow() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "updateAccount");},
  638. get ChannelSettingsWindow() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "updateChannel");},
  639. get GuildSettingsWindow() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "updateGuild");},
  640. /* Modals */
  641. get ModalActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("openModal", "updateModal");},
  642. get ModalStack() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("push", "update", "pop", "popWithKey");},
  643. get UserProfileModals() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("fetchMutualFriends", "setSection");},
  644. get AlertModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByPrototypes("handleCancel", "handleSubmit");},
  645. get ConfirmationModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].findByDisplayName("ConfirmModal");},
  646. // Grab with react components or open with UserProfileModals
  647. // get UserProfileModal() {
  648. // return WebpackModules.find(m => {
  649. // try {return m.modalConfig && m.prototype.render().type.displayName == "FluxContainer(Component)";}
  650. // catch (err) {return false;}
  651. // });
  652. // },
  653. get ChangeNicknameModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "changeNickname");},
  654. get CreateChannelModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "createChannel");},
  655. get PruneMembersModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "prune");},
  656. get NotificationSettingsModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "updateNotificationSettings");},
  657. get PrivacySettingsModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m.open && m.open.toString().includes("PRIVACY_SETTINGS_MODAL"));},
  658. // No longer available
  659. // get CreateInviteModal() {return WebpackModules.getByProps("open", "createInvite");},
  660. get Changelog() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule((m => m.defaultProps && m.defaultProps.selectable == false));},
  661. // Grab with react components
  662. // get Avatar() {
  663. // return WebpackModules.find(m => {
  664. // if (m.displayName != "FluxContainer(t)") return false;
  665. // try {
  666. // const temp = new m();
  667. // return temp.state && temp.state.hasOwnProperty("isFocused");
  668. // }
  669. // catch (err) {return false;}
  670. // });
  671. // },
  672. /* Popouts */
  673. get PopoutStack() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "close", "closeAll");},
  674. get PopoutOpener() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("openPopout");},
  675. // Grab with react components
  676. // get EmojiPicker() {return WebpackModules.getByDisplayName("FluxContainer(EmojiPicker)");},
  677. get UserPopout() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("ConnectedUserPopout");},
  678. /* Context Menus */
  679. get ContextMenuActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("openContextMenu");},
  680. get ContextMenuItemsGroup() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByRegex(/itemGroup/);},
  681. get ContextMenuItem() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByRegex(/\.label\b.*\.hint\b.*\.action\b/);},
  682. /* Misc */
  683. get ExternalLink() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByRegex(/trusted/);},
  684. get TextElement() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("Text");},
  685. get FlexChild() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Child");},
  686. get Titles() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Tags", "default");},
  687. /* Settings */
  688. get SettingsWrapper() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("FormItem");},
  689. get SettingsNote() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("FormText");},
  690. get SettingsDivider() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => !m.defaultProps && m.prototype && m.prototype.render && m.prototype.render.toString().includes("default.divider"));},
  691. get ColorPicker() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("ColorPicker");}, // Loaded by Discord on demand
  692. get Dropdown() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m.prototype && !m.prototype.handleClick && m.prototype.render && m.prototype.render.toString().includes("default.select"));},
  693. get Keybind() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByPrototypes("handleComboChange");},
  694. get RadioGroup() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("RadioGroup");},
  695. get Slider() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByPrototypes("renderMark");},
  696. get SwitchRow() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("SwitchItem");},
  697. get Textbox() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m.defaultProps && m.defaultProps.type == "text");},
  698. }));
  699. /***/ }),
  700. /***/ "./src/modules/discordselectors.js":
  701. /*!*****************************************!*\
  702. !*** ./src/modules/discordselectors.js ***!
  703. \*****************************************/
  704. /*! exports provided: default */
  705. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  706. "use strict";
  707. __webpack_require__.r(__webpack_exports__);
  708. /* harmony import */ var _discordclassmodules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./discordclassmodules */ "./src/modules/discordclassmodules.js");
  709. /* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js");
  710. const getSelectorAll = function(prop) {
  711. if (!this.hasOwnProperty(prop)) return "";
  712. return `.${this[prop].split(" ").join(".")}`;
  713. };
  714. const getSelector = function(prop) {
  715. if (!this.hasOwnProperty(prop)) return "";
  716. return `.${this[prop].split(" ")[0]}`;
  717. };
  718. /**
  719. * Gives us a way to retrieve the internal classes as selectors without
  720. * needing to concatenate strings or use string templates. Wraps the
  721. * selector in {@link module:DOMTools.Selector} which adds features but can
  722. * still be used in native function.
  723. *
  724. * For a list of all available class namespaces check out {@link module:DiscordClassModules}.
  725. *
  726. * @see module:DiscordClassModules
  727. * @module DiscordSelectors
  728. * @version 0.1.0
  729. */
  730. const DiscordSelectors = new Proxy(_discordclassmodules__WEBPACK_IMPORTED_MODULE_0__["default"], {
  731. get: function(list, item) {
  732. if (item == "getSelectorAll" || item == "getSelector") return (module, prop) => DiscordSelectors[module][item]([prop]);
  733. if (list[item] === undefined) return new Proxy({}, {get: function() {return "";}});
  734. return new Proxy(list[item], {
  735. get: function(obj, prop) {
  736. if (prop == "getSelectorAll") return getSelectorAll.bind(obj);
  737. if (prop == "getSelector") return getSelector.bind(obj);
  738. if (!obj.hasOwnProperty(prop)) return "";
  739. return new _domtools__WEBPACK_IMPORTED_MODULE_1__["default"].Selector(obj[prop]);
  740. }
  741. });
  742. }
  743. });
  744. /* harmony default export */ __webpack_exports__["default"] = (DiscordSelectors);
  745. /***/ }),
  746. /***/ "./src/modules/domtools.js":
  747. /*!*********************************!*\
  748. !*** ./src/modules/domtools.js ***!
  749. \*********************************/
  750. /*! exports provided: default */
  751. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  752. "use strict";
  753. __webpack_require__.r(__webpack_exports__);
  754. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return DOMTools; });
  755. /* harmony import */ var _utilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utilities */ "./src/modules/utilities.js");
  756. /* harmony import */ var _logger__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./logger */ "./src/modules/logger.js");
  757. /* harmony import */ var structs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! structs */ "./src/structs/structs.js");
  758. /**
  759. * Helpful utilities for dealing with DOM operations.
  760. *
  761. * This module also extends `HTMLElement` to add a set of utility functions,
  762. * the same as the ones available in the module itself, but with the `element`
  763. * parameter bound to `this`.
  764. * @module DOMTools
  765. * @version 0.0.5
  766. */
  767. /**
  768. * @interface
  769. * @name Offset
  770. * @property {number} top - Top offset of the target element.
  771. * @property {number} right - Right offset of the target element.
  772. * @property {number} bottom - Bottom offset of the target element.
  773. * @property {number} left - Left offset of the target element.
  774. * @property {number} height - Outer height of the target element.
  775. * @property {number} width - Outer width of the target element.
  776. */
  777. /**
  778. * Function that automatically removes added listener.
  779. * @callback module:DOMTools~CancelListener
  780. */
  781. class DOMTools {
  782. static get Selector() {return structs__WEBPACK_IMPORTED_MODULE_2__["Selector"];}
  783. static get ClassName() {return structs__WEBPACK_IMPORTED_MODULE_2__["ClassName"];}
  784. static get DOMObserver() {return structs__WEBPACK_IMPORTED_MODULE_2__["DOMObserver"];}
  785. /**
  786. * Default DOMObserver for global usage.
  787. *
  788. * @see DOMObserver
  789. */
  790. static get observer() {
  791. return this._observer || (this._observer = new structs__WEBPACK_IMPORTED_MODULE_2__["DOMObserver"]());
  792. }
  793. /**
  794. * This is my shit version of not having to use `$` from jQuery. Meaning
  795. * that you can pass a selector and it will automatically run {@link module:DOMTools.query}.
  796. * It also means that you can pass a string of html and it will perform and return `parseHTML`.
  797. * @see module:DOMTools.parseHTML
  798. * @see module:DOMTools.query
  799. * @param {string} selector - Selector to query or HTML to parse
  800. * @returns {(DocumentFragment|NodeList|HTMLElement)} - Either the result of `parseHTML` or `query`
  801. */
  802. static Q(selector) {
  803. const element = this.parseHTML(selector);
  804. const isHTML = element instanceof NodeList ? Array.from(element).some(n => n.nodeType === 1) : element.nodeType === 1;
  805. if (isHTML) return element;
  806. return this.query(selector);
  807. }
  808. /**
  809. * Essentially a shorthand for `document.querySelector`. If the `baseElement` is not provided
  810. * `document` is used by default.
  811. * @param {string} selector - Selector to query
  812. * @param {Element} [baseElement] - Element to base the query from
  813. * @returns {(Element|null)} - The found element or null if not found
  814. */
  815. static query(selector, baseElement) {
  816. if (!baseElement) baseElement = document;
  817. return baseElement.querySelector(selector);
  818. }
  819. /**
  820. * Essentially a shorthand for `document.querySelectorAll`. If the `baseElement` is not provided
  821. * `document` is used by default.
  822. * @param {string} selector - Selector to query
  823. * @param {Element} [baseElement] - Element to base the query from
  824. * @returns {Array<Element>} - Array of all found elements
  825. */
  826. static queryAll(selector, baseElement) {
  827. if (!baseElement) baseElement = document;
  828. return baseElement.querySelectorAll(selector);
  829. }
  830. /**
  831. * Parses a string of HTML and returns the results. If the second parameter is true,
  832. * the parsed HTML will be returned as a document fragment {@see https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment}.
  833. * 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.
  834. *
  835. * If the second parameter is false, then the return value will be the list of parsed
  836. * nodes and there were multiple top level nodes, otherwise the single node is returned.
  837. * @param {string} html - HTML to be parsed
  838. * @param {boolean} [fragment=false] - Whether or not the return should be the raw `DocumentFragment`
  839. * @returns {(DocumentFragment|NodeList|HTMLElement)} - The result of HTML parsing
  840. */
  841. static parseHTML(html, fragment = false) {
  842. const template = document.createElement("template");
  843. template.innerHTML = html;
  844. const node = template.content.cloneNode(true);
  845. if (fragment) return node;
  846. return node.childNodes.length > 1 ? node.childNodes : node.childNodes[0];
  847. }
  848. /** Alternate name for {@link module:DOMTools.parseHTML} */
  849. static createElement(html, fragment = false) {return this.parseHTML(html, fragment);}
  850. /**
  851. * Takes a string of html and escapes it using the brower's own escaping mechanism.
  852. * @param {String} html - html to be escaped
  853. */
  854. static escapeHTML(html) {
  855. const textNode = document.createTextNode("");
  856. const spanElement = document.createElement("span");
  857. spanElement.append(textNode);
  858. textNode.nodeValue = html;
  859. return spanElement.innerHTML;
  860. }
  861. /**
  862. * Takes a string and escapes it for use as a DOM id.
  863. * @param {String} id - string to be escaped
  864. */
  865. static escapeID(id) {
  866. return id.replace(/^[^a-z]+|[^\w-]+/gi, "-");
  867. }
  868. /**
  869. * Adds a list of classes from the target element.
  870. * @param {Element} element - Element to edit classes of
  871. * @param {...string} classes - Names of classes to add
  872. * @returns {Element} - `element` to allow for chaining
  873. */
  874. static addClass(element, ...classes) {
  875. classes = classes.flat().filter(c => c);
  876. for (let c = 0; c < classes.length; c++) classes[c] = classes[c].toString().split(" ");
  877. classes = classes.flat().filter(c => c);
  878. element.classList.add(...classes);
  879. return element;
  880. }
  881. /**
  882. * Removes a list of classes from the target element.
  883. * @param {Element} element - Element to edit classes of
  884. * @param {...string} classes - Names of classes to remove
  885. * @returns {Element} - `element` to allow for chaining
  886. */
  887. static removeClass(element, ...classes) {
  888. for (let c = 0; c < classes.length; c++) classes[c] = classes[c].toString().split(" ");
  889. classes = classes.flat().filter(c => c);
  890. element.classList.remove(...classes);
  891. return element;
  892. }
  893. /**
  894. * When only one argument is present: Toggle class value;
  895. * i.e., if class exists then remove it and return false, if not, then add it and return true.
  896. * When a second argument is present:
  897. * If the second argument evaluates to true, add specified class value, and if it evaluates to false, remove it.
  898. * @param {Element} element - Element to edit classes of
  899. * @param {string} classname - Name of class to toggle
  900. * @param {boolean} [indicator] - Optional indicator for if the class should be toggled
  901. * @returns {Element} - `element` to allow for chaining
  902. */
  903. static toggleClass(element, classname, indicator) {
  904. classname = classname.toString().split(" ").filter(c => c);
  905. if (typeof(indicator) !== "undefined") classname.forEach(c => element.classList.toggle(c, indicator));
  906. else classname.forEach(c => element.classList.toggle(c));
  907. return element;
  908. }
  909. /**
  910. * Checks if an element has a specific class
  911. * @param {Element} element - Element to edit classes of
  912. * @param {string} classname - Name of class to check
  913. * @returns {boolean} - `true` if the element has the class, `false` otherwise.
  914. */
  915. static hasClass(element, classname) {
  916. return classname.toString().split(" ").filter(c => c).every(c => element.classList.contains(c));
  917. }
  918. /**
  919. * Replaces one class with another
  920. * @param {Element} element - Element to edit classes of
  921. * @param {string} oldName - Name of class to replace
  922. * @param {string} newName - New name for the class
  923. * @returns {Element} - `element` to allow for chaining
  924. */
  925. static replaceClass(element, oldName, newName) {
  926. element.classList.replace(oldName, newName);
  927. return element;
  928. }
  929. /**
  930. * Appends `thisNode` to `thatNode`
  931. * @param {Node} thisNode - Node to be appended to another node
  932. * @param {Node} thatNode - Node for `thisNode` to be appended to
  933. * @returns {Node} - `thisNode` to allow for chaining
  934. */
  935. static appendTo(thisNode, thatNode) {
  936. if (typeof(thatNode) == "string") thatNode = this.query(thatNode);
  937. if (!thatNode) return null;
  938. thatNode.append(thisNode);
  939. return thisNode;
  940. }
  941. /**
  942. * Prepends `thisNode` to `thatNode`
  943. * @param {Node} thisNode - Node to be prepended to another node
  944. * @param {Node} thatNode - Node for `thisNode` to be prepended to
  945. * @returns {Node} - `thisNode` to allow for chaining
  946. */
  947. static prependTo(thisNode, thatNode) {
  948. if (typeof(thatNode) == "string") thatNode = this.query(thatNode);
  949. if (!thatNode) return null;
  950. thatNode.prepend(thisNode);
  951. return thisNode;
  952. }
  953. /**
  954. * Insert after a specific element, similar to jQuery's `thisElement.insertAfter(otherElement)`.
  955. * @param {Node} thisNode - The node to insert
  956. * @param {Node} targetNode - Node to insert after in the tree
  957. * @returns {Node} - `thisNode` to allow for chaining
  958. */
  959. static insertAfter(thisNode, targetNode) {
  960. targetNode.parentNode.insertBefore(thisNode, targetNode.nextSibling);
  961. return thisNode;
  962. }
  963. /**
  964. * Insert after a specific element, similar to jQuery's `thisElement.after(newElement)`.
  965. * @param {Node} thisNode - The node to insert
  966. * @param {Node} newNode - Node to insert after in the tree
  967. * @returns {Node} - `thisNode` to allow for chaining
  968. */
  969. static after(thisNode, newNode) {
  970. thisNode.parentNode.insertBefore(newNode, thisNode.nextSibling);
  971. return thisNode;
  972. }
  973. /**
  974. * Gets the next sibling element that matches the selector.
  975. * @param {Element} element - Element to get the next sibling of
  976. * @param {string} [selector=""] - Optional selector
  977. * @returns {Element} - The sibling element
  978. */
  979. static next(element, selector = "") {
  980. return selector ? element.querySelector("+ " + selector) : element.nextElementSibling;
  981. }
  982. /**
  983. * Gets all subsequent siblings.
  984. * @param {Element} element - Element to get next siblings of
  985. * @returns {NodeList} - The list of siblings
  986. */
  987. static nextAll(element) {
  988. return element.querySelectorAll("~ *");
  989. }
  990. /**
  991. * Gets the subsequent siblings until an element matches the selector.
  992. * @param {Element} element - Element to get the following siblings of
  993. * @param {string} selector - Selector to stop at
  994. * @returns {Array<Element>} - The list of siblings
  995. */
  996. static nextUntil(element, selector) {
  997. const next = [];
  998. while (element.nextElementSibling && !element.nextElementSibling.matches(selector)) next.push(element = element.nextElementSibling);
  999. return next;
  1000. }
  1001. /**
  1002. * Gets the previous sibling element that matches the selector.
  1003. * @param {Element} element - Element to get the previous sibling of
  1004. * @param {string} [selector=""] - Optional selector
  1005. * @returns {Element} - The sibling element
  1006. */
  1007. static previous(element, selector = "") {
  1008. const previous = element.previousElementSibling;
  1009. if (selector) return previous && previous.matches(selector) ? previous : null;
  1010. return previous;
  1011. }
  1012. /**
  1013. * Gets all preceeding siblings.
  1014. * @param {Element} element - Element to get preceeding siblings of
  1015. * @returns {NodeList} - The list of siblings
  1016. */
  1017. static previousAll(element) {
  1018. const previous = [];
  1019. while (element.previousElementSibling) previous.push(element = element.previousElementSibling);
  1020. return previous;
  1021. }
  1022. /**
  1023. * Gets the preceeding siblings until an element matches the selector.
  1024. * @param {Element} element - Element to get the preceeding siblings of
  1025. * @param {string} selector - Selector to stop at
  1026. * @returns {Array<Element>} - The list of siblings
  1027. */
  1028. static previousUntil(element, selector) {
  1029. const previous = [];
  1030. while (element.previousElementSibling && !element.previousElementSibling.matches(selector)) previous.push(element = element.previousElementSibling);
  1031. return previous;
  1032. }
  1033. /**
  1034. * Find which index in children a certain node is. Similar to jQuery's `$.index()`
  1035. * @param {HTMLElement} node - The node to find its index in parent
  1036. * @returns {number} Index of the node
  1037. */
  1038. static indexInParent(node) {
  1039. const children = node.parentNode.childNodes;
  1040. let num = 0;
  1041. for (let i = 0; i < children.length; i++) {
  1042. if (children[i] == node) return num;
  1043. if (children[i].nodeType == 1) num++;
  1044. }
  1045. return -1;
  1046. }
  1047. /** Shorthand for {@link module:DOMTools.indexInParent} */
  1048. static index(node) {return this.indexInParent(node);}
  1049. /**
  1050. * Gets the parent of the element if it matches the selector,
  1051. * otherwise returns null.
  1052. * @param {Element} element - Element to get parent of
  1053. * @param {string} [selector=""] - Selector to match parent
  1054. * @returns {(Element|null)} - The sibling element or null
  1055. */
  1056. static parent(element, selector = "") {
  1057. return !selector || element.parentElement.matches(selector) ? element.parentElement : null;
  1058. }
  1059. /**
  1060. * Gets all children of Element that match the selector if provided.
  1061. * @param {Element} element - Element to get all children of
  1062. * @param {string} selector - Selector to match the children to
  1063. * @returns {Array<Element>} - The list of children
  1064. */
  1065. static findChild(element, selector) {
  1066. return element.querySelector(":scope > " + selector);
  1067. }
  1068. /**
  1069. * Gets all children of Element that match the selector if provided.
  1070. * @param {Element} element - Element to get all children of
  1071. * @param {string} selector - Selector to match the children to
  1072. * @returns {Array<Element>} - The list of children
  1073. */
  1074. static findChildren(element, selector) {
  1075. return element.querySelectorAll(":scope > " + selector);
  1076. }
  1077. /**
  1078. * Gets all ancestors of Element that match the selector if provided.
  1079. * @param {Element} element - Element to get all parents of
  1080. * @param {string} [selector=""] - Selector to match the parents to
  1081. * @returns {Array<Element>} - The list of parents
  1082. */
  1083. static parents(element, selector = "") {
  1084. const parents = [];
  1085. if (selector) while (element.parentElement && element.parentElement.closest(selector)) parents.push(element = element.parentElement.closest(selector));
  1086. else while (element.parentElement) parents.push(element = element.parentElement);
  1087. return parents;
  1088. }
  1089. /**
  1090. * Gets the ancestors until an element matches the selector.
  1091. * @param {Element} element - Element to get the ancestors of
  1092. * @param {string} selector - Selector to stop at
  1093. * @returns {Array<Element>} - The list of parents
  1094. */
  1095. static parentsUntil(element, selector) {
  1096. const parents = [];
  1097. while (element.parentElement && !element.parentElement.matches(selector)) parents.push(element = element.parentElement);
  1098. return parents;
  1099. }
  1100. /**
  1101. * Gets all siblings of the element that match the selector.
  1102. * @param {Element} element - Element to get all siblings of
  1103. * @param {string} [selector="*"] - Selector to match the siblings to
  1104. * @returns {Array<Element>} - The list of siblings
  1105. */
  1106. static siblings(element, selector = "*") {
  1107. return Array.from(element.parentElement.children).filter(e => e != element && e.matches(selector));
  1108. }
  1109. /**
  1110. * Sets or gets css styles for a specific element. If `value` is provided
  1111. * then it sets the style and returns the element to allow for chaining,
  1112. * otherwise returns the style.
  1113. * @param {Element} element - Element to set the CSS of
  1114. * @param {string} attribute - Attribute to get or set
  1115. * @param {string} [value] - Value to set for attribute
  1116. * @returns {Element|string} - When setting a value, element is returned for chaining, otherwise the value is returned.
  1117. */
  1118. static css(element, attribute, value) {
  1119. if (typeof(value) == "undefined") return global.getComputedStyle(element)[attribute];
  1120. element.style[attribute] = value;
  1121. return element;
  1122. }
  1123. /**
  1124. * Sets or gets the width for a specific element. If `value` is provided
  1125. * then it sets the width and returns the element to allow for chaining,
  1126. * otherwise returns the width.
  1127. * @param {Element} element - Element to set the CSS of
  1128. * @param {string} [value] - Width to set
  1129. * @returns {Element|string} - When setting a value, element is returned for chaining, otherwise the value is returned.
  1130. */
  1131. static width(element, value) {
  1132. if (typeof(value) == "undefined") return parseInt(getComputedStyle(element).width);
  1133. element.style.width = value;
  1134. return element;
  1135. }
  1136. /**
  1137. * Sets or gets the height for a specific element. If `value` is provided
  1138. * then it sets the height and returns the element to allow for chaining,
  1139. * otherwise returns the height.
  1140. * @param {Element} element - Element to set the CSS of
  1141. * @param {string} [value] - Height to set
  1142. * @returns {Element|string} - When setting a value, element is returned for chaining, otherwise the value is returned.
  1143. */
  1144. static height(element, value) {
  1145. if (typeof(value) == "undefined") return parseInt(getComputedStyle(element).height);
  1146. element.style.height = value;
  1147. return element;
  1148. }
  1149. /**
  1150. * Sets the inner text of an element if given a value, otherwise returns it.
  1151. * @param {Element} element - Element to set the text of
  1152. * @param {string} [text] - Content to set
  1153. * @returns {string} - Either the string set by this call or the current text content of the node.
  1154. */
  1155. static text(element, text) {
  1156. if (typeof(text) == "undefined") return element.textContent;
  1157. return element.textContent = text;
  1158. }
  1159. /**
  1160. * Returns the innerWidth of the element.
  1161. * @param {Element} element - Element to retrieve inner width of
  1162. * @return {number} - The inner width of the element.
  1163. */
  1164. static innerWidth(element) {
  1165. return element.clientWidth;
  1166. }
  1167. /**
  1168. * Returns the innerHeight of the element.
  1169. * @param {Element} element - Element to retrieve inner height of
  1170. * @return {number} - The inner height of the element.
  1171. */
  1172. static innerHeight(element) {
  1173. return element.clientHeight;
  1174. }
  1175. /**
  1176. * Returns the outerWidth of the element.
  1177. * @param {Element} element - Element to retrieve outer width of
  1178. * @return {number} - The outer width of the element.
  1179. */
  1180. static outerWidth(element) {
  1181. return element.offsetWidth;
  1182. }
  1183. /**
  1184. * Returns the outerHeight of the element.
  1185. * @param {Element} element - Element to retrieve outer height of
  1186. * @return {number} - The outer height of the element.
  1187. */
  1188. static outerHeight(element) {
  1189. return element.offsetHeight;
  1190. }
  1191. /**
  1192. * Gets the offset of the element in the page.
  1193. * @param {Element} element - Element to get offset of
  1194. * @return {Offset} - The offset of the element
  1195. */
  1196. static offset(element) {
  1197. return element.getBoundingClientRect();
  1198. }
  1199. static get listeners() {return this._listeners || (this._listeners = {});}
  1200. /**
  1201. * This is similar to jQuery's `on` function and can *hopefully* be used in the same way.
  1202. *
  1203. * Rather than attempt to explain, I'll show some example usages.
  1204. *
  1205. * The following will add a click listener (in the `myPlugin` namespace) to `element`.
  1206. * `DOMTools.on(element, "click.myPlugin", () => {console.log("clicked!");});`
  1207. *
  1208. * The following will add a click listener (in the `myPlugin` namespace) to `element` that only fires when the target is a `.block` element.
  1209. * `DOMTools.on(element, "click.myPlugin", ".block", () => {console.log("clicked!");});`
  1210. *
  1211. * The following will add a click listener (without namespace) to `element`.
  1212. * `DOMTools.on(element, "click", () => {console.log("clicked!");});`
  1213. *
  1214. * The following will add a click listener (without namespace) to `element` that only fires once.
  1215. * `const cancel = DOMTools.on(element, "click", () => {console.log("fired!"); cancel();});`
  1216. *
  1217. * @param {Element} element - Element to add listener to
  1218. * @param {string} event - Event to listen to with option namespace (e.g. "event.namespace")
  1219. * @param {(string|callable)} delegate - Selector to run on element to listen to
  1220. * @param {callable} [callback] - Function to fire on event
  1221. * @returns {module:DOMTools~CancelListener} - A function that will undo the listener
  1222. */
  1223. static on(element, event, delegate, callback) {
  1224. const [type, namespace] = event.split(".");
  1225. const hasDelegate = delegate && callback;
  1226. if (!callback) callback = delegate;
  1227. const eventFunc = !hasDelegate ? callback : function(ev) {
  1228. if (ev.target.matches(delegate)) {
  1229. callback(ev);
  1230. }
  1231. };
  1232. element.addEventListener(type, eventFunc);
  1233. const cancel = () => {
  1234. element.removeEventListener(type, eventFunc);
  1235. };
  1236. if (namespace) {
  1237. if (!this.listeners[namespace]) this.listeners[namespace] = [];
  1238. const newCancel = () => {
  1239. cancel();
  1240. this.listeners[namespace].splice(this.listeners[namespace].findIndex(l => l.event == type && l.element == element), 1);
  1241. };
  1242. this.listeners[namespace].push({
  1243. event: type,
  1244. element: element,
  1245. cancel: newCancel
  1246. });
  1247. return newCancel;
  1248. }
  1249. return cancel;
  1250. }
  1251. /**
  1252. * Functionality for this method matches {@link module:DOMTools.on} but automatically cancels itself
  1253. * and removes the listener upon the first firing of the desired event.
  1254. *
  1255. * @param {Element} element - Element to add listener to
  1256. * @param {string} event - Event to listen to with option namespace (e.g. "event.namespace")
  1257. * @param {(string|callable)} delegate - Selector to run on element to listen to
  1258. * @param {callable} [callback] - Function to fire on event
  1259. * @returns {module:DOMTools~CancelListener} - A function that will undo the listener
  1260. */
  1261. static once(element, event, delegate, callback) {
  1262. const [type, namespace] = event.split(".");
  1263. const hasDelegate = delegate && callback;
  1264. if (!callback) callback = delegate;
  1265. const eventFunc = !hasDelegate ? function(ev) {
  1266. callback(ev);
  1267. element.removeEventListener(type, eventFunc);
  1268. } : function(ev) {
  1269. if (!ev.target.matches(delegate)) return;
  1270. callback(ev);
  1271. element.removeEventListener(type, eventFunc);
  1272. };
  1273. element.addEventListener(type, eventFunc);
  1274. const cancel = () => {
  1275. element.removeEventListener(type, eventFunc);
  1276. };
  1277. if (namespace) {
  1278. if (!this.listeners[namespace]) this.listeners[namespace] = [];
  1279. const newCancel = () => {
  1280. cancel();
  1281. this.listeners[namespace].splice(this.listeners[namespace].findIndex(l => l.event == type && l.element == element), 1);
  1282. };
  1283. this.listeners[namespace].push({
  1284. event: type,
  1285. element: element,
  1286. cancel: newCancel
  1287. });
  1288. return newCancel;
  1289. }
  1290. return cancel;
  1291. }
  1292. static __offAll(event, element) {
  1293. const [type, namespace] = event.split(".");
  1294. let matchFilter = listener => listener.event == type, defaultFilter = _ => _;
  1295. if (element) {
  1296. matchFilter = l => l.event == type && l.element == element;
  1297. defaultFilter = l => l.element == element;
  1298. }
  1299. const listeners = this.listeners[namespace] || [];
  1300. const list = type ? listeners.filter(matchFilter) : listeners.filter(defaultFilter);
  1301. for (let c = 0; c < list.length; c++) list[c].cancel();
  1302. }
  1303. /**
  1304. * This is similar to jQuery's `off` function and can *hopefully* be used in the same way.
  1305. *
  1306. * Rather than attempt to explain, I'll show some example usages.
  1307. *
  1308. * The following will remove a click listener called `onClick` (in the `myPlugin` namespace) from `element`.
  1309. * `DOMTools.off(element, "click.myPlugin", onClick);`
  1310. *
  1311. * 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.
  1312. * `DOMTools.off(element, "click.myPlugin", ".block", onClick);`
  1313. *
  1314. * The following will remove a click listener (without namespace) from `element`.
  1315. * `DOMTools.off(element, "click", onClick);`
  1316. *
  1317. * The following will remove all listeners in namespace `myPlugin` from `element`.
  1318. * `DOMTools.off(element, ".myPlugin");`
  1319. *
  1320. * The following will remove all click listeners in namespace `myPlugin` from *all elements*.
  1321. * `DOMTools.off("click.myPlugin");`
  1322. *
  1323. * The following will remove all listeners in namespace `myPlugin` from *all elements*.
  1324. * `DOMTools.off(".myPlugin");`
  1325. *
  1326. * @param {(Element|string)} element - Element to remove listener from
  1327. * @param {string} [event] - Event to listen to with option namespace (e.g. "event.namespace")
  1328. * @param {(string|callable)} [delegate] - Selector to run on element to listen to
  1329. * @param {callable} [callback] - Function to fire on event
  1330. * @returns {Element} - The original element to allow for chaining
  1331. */
  1332. static off(element, event, delegate, callback) {
  1333. if (typeof(element) == "string") return this.__offAll(element);
  1334. const [type, namespace] = event.split(".");
  1335. if (namespace) return this.__offAll(event, element);
  1336. const hasDelegate = delegate && callback;
  1337. if (!callback) callback = delegate;
  1338. const eventFunc = !hasDelegate ? callback : function(ev) {
  1339. if (ev.target.matches(delegate)) {
  1340. callback(ev);
  1341. }
  1342. };
  1343. element.removeEventListener(type, eventFunc);
  1344. return element;
  1345. }
  1346. /**
  1347. * Adds a listener for when the node is added/removed from the document body.
  1348. * The listener is automatically removed upon firing.
  1349. * @param {HTMLElement} node - node to wait for
  1350. * @param {callable} callback - function to be performed on event
  1351. * @param {boolean} onMount - determines if it should fire on Mount or on Unmount
  1352. */
  1353. static onMountChange(node, callback, onMount = true) {
  1354. const wrappedCallback = () => {
  1355. this.observer.unsubscribe(wrappedCallback);
  1356. callback();
  1357. };
  1358. this.observer.subscribe(wrappedCallback, mutation => {
  1359. const nodes = Array.from(onMount ? mutation.addedNodes : mutation.removedNodes);
  1360. const directMatch = nodes.indexOf(node) > -1;
  1361. const parentMatch = nodes.some(parent => parent.contains(node));
  1362. return directMatch || parentMatch;
  1363. });
  1364. return node;
  1365. }
  1366. /** Shorthand for {@link module:DOMTools.onMountChange} with third parameter `true` */
  1367. static onMount(node, callback) {return this.onMountChange(node, callback);}
  1368. /** Shorthand for {@link module:DOMTools.onMountChange} with third parameter `false` */
  1369. static onUnmount(node, callback) {return this.onMountChange(node, callback, false);}
  1370. /** Alias for {@link module:DOMTools.onMount} */
  1371. static onAdded(node, callback) {return this.onMount(node, callback);}
  1372. /** Alias for {@link module:DOMTools.onUnmount} */
  1373. static onRemoved(node, callback) {return this.onUnmount(node, callback, false);}
  1374. /**
  1375. * Helper function which combines multiple elements into one parent element
  1376. * @param {Array<HTMLElement>} elements - array of elements to put into a single parent
  1377. */
  1378. static wrap(elements) {
  1379. const domWrapper = this.parseHTML(`<div class="dom-wrapper"></div>`);
  1380. for (let e = 0; e < elements.length; e++) domWrapper.appendChild(elements[e]);
  1381. return domWrapper;
  1382. }
  1383. /**
  1384. * Resolves the node to an HTMLElement. This is mainly used by library modules.
  1385. * @param {(jQuery|Element)} node - node to resolve
  1386. */
  1387. static resolveElement(node) {
  1388. try {
  1389. if (!(node instanceof window.jQuery) && !(node instanceof Element)) return undefined;
  1390. return node instanceof window.jQuery ? node[0] : node;
  1391. }
  1392. catch {
  1393. return node;
  1394. }
  1395. }
  1396. }
  1397. const addToPrototype = function(MainObject, prop, func) {
  1398. _utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, prop, function() {
  1399. _logger__WEBPACK_IMPORTED_MODULE_1__["default"].warn("DOMTools", "These custom functions on HTMLElement will be removed.");
  1400. return Reflect.apply(func, this, arguments);
  1401. });
  1402. };
  1403. addToPrototype(HTMLElement, "addClass", function(...classes) {return DOMTools.addClass(this, ...classes);});
  1404. addToPrototype(HTMLElement, "removeClass", function(...classes) {return DOMTools.removeClass(this, ...classes);});
  1405. addToPrototype(HTMLElement, "toggleClass", function(className, indicator) {return DOMTools.toggleClass(this, className, indicator);});
  1406. addToPrototype(HTMLElement, "replaceClass", function(oldClass, newClass) {return DOMTools.replaceClass(this, oldClass, newClass);});
  1407. addToPrototype(HTMLElement, "hasClass", function(className) {return DOMTools.hasClass(this, className);});
  1408. addToPrototype(HTMLElement, "insertAfter", function(referenceNode) {return DOMTools.insertAfter(this, referenceNode);});
  1409. addToPrototype(HTMLElement, "after", function(newNode) {return DOMTools.after(this, newNode);});
  1410. addToPrototype(HTMLElement, "next", function(selector = "") {return DOMTools.next(this, selector);});
  1411. addToPrototype(HTMLElement, "nextAll", function() {return DOMTools.nextAll(this);});
  1412. addToPrototype(HTMLElement, "nextUntil", function(selector) {return DOMTools.nextUntil(this, selector);});
  1413. addToPrototype(HTMLElement, "previous", function(selector = "") {return DOMTools.previous(this, selector);});
  1414. addToPrototype(HTMLElement, "previousAll", function() {return DOMTools.previousAll(this);});
  1415. addToPrototype(HTMLElement, "previousUntil", function(selector) {return DOMTools.previousUntil(this, selector);});
  1416. addToPrototype(HTMLElement, "index", function() {return DOMTools.index(this);});
  1417. addToPrototype(HTMLElement, "findChild", function(selector) {return DOMTools.findChild(this, selector);});
  1418. addToPrototype(HTMLElement, "findChildren", function(selector) {return DOMTools.findChildren(this, selector);});
  1419. addToPrototype(HTMLElement, "parent", function(selector) {return DOMTools.parent(this, selector);});
  1420. addToPrototype(HTMLElement, "parents", function(selector = "") {return DOMTools.parents(this, selector);});
  1421. addToPrototype(HTMLElement, "parentsUntil", function(selector) {return DOMTools.parentsUntil(this, selector);});
  1422. addToPrototype(HTMLElement, "siblings", function(selector = "*") {return DOMTools.siblings(this, selector);});
  1423. addToPrototype(HTMLElement, "css", function(attribute, value) {return DOMTools.css(this, attribute, value);});
  1424. addToPrototype(HTMLElement, "width", function(value) {return DOMTools.width(this, value);});
  1425. addToPrototype(HTMLElement, "height", function(value) {return DOMTools.height(this, value);});
  1426. addToPrototype(HTMLElement, "innerWidth", function() {return DOMTools.innerWidth(this);});
  1427. addToPrototype(HTMLElement, "innerHeight", function() {return DOMTools.innerHeight(this);});
  1428. addToPrototype(HTMLElement, "outerWidth", function() {return DOMTools.outerWidth(this);});
  1429. addToPrototype(HTMLElement, "outerHeight", function() {return DOMTools.outerHeight(this);});
  1430. addToPrototype(HTMLElement, "offset", function() {return DOMTools.offset(this);});
  1431. addToPrototype(HTMLElement, "text", function(value) {return DOMTools.text(this, value);});
  1432. addToPrototype(HTMLElement, "on", function(event, delegate, callback) {return DOMTools.on(this, event, delegate, callback);});
  1433. addToPrototype(HTMLElement, "once", function(event, delegate, callback) {return DOMTools.once(this, event, delegate, callback);});
  1434. addToPrototype(HTMLElement, "off", function(event, delegate, callback) {return DOMTools.off(this, event, delegate, callback);});
  1435. addToPrototype(HTMLElement, "find", function(selector) {return DOMTools.query(selector, this);});
  1436. addToPrototype(HTMLElement, "findAll", function(selector) {return DOMTools.queryAll(selector, this);});
  1437. addToPrototype(HTMLElement, "appendTo", function(otherNode) {return DOMTools.appendTo(this, otherNode);});
  1438. addToPrototype(HTMLElement, "onAdded", function(callback) {return DOMTools.onAdded(this, callback);});
  1439. addToPrototype(HTMLElement, "onRemoved", function(callback) {return DOMTools.onRemoved(this, callback);});
  1440. /***/ }),
  1441. /***/ "./src/modules/logger.js":
  1442. /*!*******************************!*\
  1443. !*** ./src/modules/logger.js ***!
  1444. \*******************************/
  1445. /*! exports provided: LogTypes, default */
  1446. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  1447. "use strict";
  1448. __webpack_require__.r(__webpack_exports__);
  1449. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "LogTypes", function() { return LogTypes; });
  1450. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return Logger; });
  1451. /**
  1452. * Simple logger for the lib and plugins.
  1453. *
  1454. * @module Logger
  1455. * @version 0.1.0
  1456. */
  1457. /* eslint-disable no-console */
  1458. /**
  1459. * List of logging types.
  1460. */
  1461. const LogTypes = {
  1462. /** Alias for error */
  1463. err: "error",
  1464. error: "error",
  1465. /** Alias for debug */
  1466. dbg: "debug",
  1467. debug: "debug",
  1468. log: "log",
  1469. warn: "warn",
  1470. info: "info"
  1471. };
  1472. class Logger {
  1473. /**
  1474. * Logs an error using a collapsed error group with stacktrace.
  1475. *
  1476. * @param {string} module - Name of the calling module.
  1477. * @param {string} message - Message or error to have logged.
  1478. * @param {Error} error - Error object to log with the message.
  1479. */
  1480. static stacktrace(module, message, error) {
  1481. console.error(`%c[${module}]%c ${message}\n\n%c`, "color: #3a71c1; font-weight: 700;", "color: red; font-weight: 700;", "color: red;", error);
  1482. }
  1483. /**
  1484. * Logs using error formatting. For logging an actual error object consider {@link module:Logger.stacktrace}
  1485. *
  1486. * @param {string} module - Name of the calling module.
  1487. * @param {string} message - Messages to have logged.
  1488. */
  1489. static err(module, ...message) {Logger._log(module, message, "error");}
  1490. /**
  1491. * Logs a warning message.
  1492. *
  1493. * @param {string} module - Name of the calling module.
  1494. * @param {...any} message - Messages to have logged.
  1495. */
  1496. static warn(module, ...message) {Logger._log(module, message, "warn");}
  1497. /**
  1498. * Logs an informational message.
  1499. *
  1500. * @param {string} module - Name of the calling module.
  1501. * @param {...any} message - Messages to have logged.
  1502. */
  1503. static info(module, ...message) {Logger._log(module, message, "info");}
  1504. /**
  1505. * Logs used for debugging purposes.
  1506. *
  1507. * @param {string} module - Name of the calling module.
  1508. * @param {...any} message - Messages to have logged.
  1509. */
  1510. static debug(module, ...message) {Logger._log(module, message, "debug");}
  1511. /**
  1512. * Logs used for basic loggin.
  1513. *
  1514. * @param {string} module - Name of the calling module.
  1515. * @param {...any} message - Messages to have logged.
  1516. */
  1517. static log(module, ...message) {Logger._log(module, message);}
  1518. /**
  1519. * Logs strings using different console levels and a module label.
  1520. *
  1521. * @param {string} module - Name of the calling module.
  1522. * @param {any|Array<any>} message - Messages to have logged.
  1523. * @param {module:Logger.LogTypes} type - Type of log to use in console.
  1524. */
  1525. static _log(module, message, type = "log") {
  1526. type = Logger.parseType(type);
  1527. if (!Array.isArray(message)) message = [message];
  1528. console[type](`%c[${module}]%c`, "color: #3a71c1; font-weight: 700;", "", ...message);
  1529. }
  1530. static parseType(type) {
  1531. return LogTypes.hasOwnProperty(type) ? LogTypes[type] : "log";
  1532. }
  1533. }
  1534. /***/ }),
  1535. /***/ "./src/modules/modules.js":
  1536. /*!********************************!*\
  1537. !*** ./src/modules/modules.js ***!
  1538. \********************************/
  1539. /*! exports provided: Utilities, WebpackModules, Filters, DiscordModules, ColorConverter, DOMTools, DiscordClasses, DiscordSelectors, ReactTools, ReactComponents, DiscordAPI, Logger, Patcher, PluginUpdater, PluginUtilities, DiscordClassModules, Structs */
  1540. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  1541. "use strict";
  1542. __webpack_require__.r(__webpack_exports__);
  1543. /* harmony import */ var _utilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utilities */ "./src/modules/utilities.js");
  1544. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Utilities", function() { return _utilities__WEBPACK_IMPORTED_MODULE_0__["default"]; });
  1545. /* harmony import */ var _webpackmodules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./webpackmodules */ "./src/modules/webpackmodules.js");
  1546. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "WebpackModules", function() { return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"]; });
  1547. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Filters", function() { return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["Filters"]; });
  1548. /* harmony import */ var _discordmodules__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./discordmodules */ "./src/modules/discordmodules.js");
  1549. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "DiscordModules", function() { return _discordmodules__WEBPACK_IMPORTED_MODULE_2__["default"]; });
  1550. /* harmony import */ var _colorconverter__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./colorconverter */ "./src/modules/colorconverter.js");
  1551. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ColorConverter", function() { return _colorconverter__WEBPACK_IMPORTED_MODULE_3__["default"]; });
  1552. /* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js");
  1553. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "DOMTools", function() { return _domtools__WEBPACK_IMPORTED_MODULE_4__["default"]; });
  1554. /* harmony import */ var _discordclasses__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./discordclasses */ "./src/modules/discordclasses.js");
  1555. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "DiscordClasses", function() { return _discordclasses__WEBPACK_IMPORTED_MODULE_5__["default"]; });
  1556. /* harmony import */ var _discordselectors__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./discordselectors */ "./src/modules/discordselectors.js");
  1557. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "DiscordSelectors", function() { return _discordselectors__WEBPACK_IMPORTED_MODULE_6__["default"]; });
  1558. /* harmony import */ var _reacttools__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./reacttools */ "./src/modules/reacttools.js");
  1559. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ReactTools", function() { return _reacttools__WEBPACK_IMPORTED_MODULE_7__["default"]; });
  1560. /* harmony import */ var _reactcomponents__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./reactcomponents */ "./src/modules/reactcomponents.js");
  1561. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ReactComponents", function() { return _reactcomponents__WEBPACK_IMPORTED_MODULE_8__["default"]; });
  1562. /* harmony import */ var _discordapi__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./discordapi */ "./src/modules/discordapi.js");
  1563. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "DiscordAPI", function() { return _discordapi__WEBPACK_IMPORTED_MODULE_9__["default"]; });
  1564. /* harmony import */ var _logger__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./logger */ "./src/modules/logger.js");
  1565. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Logger", function() { return _logger__WEBPACK_IMPORTED_MODULE_10__["default"]; });
  1566. /* harmony import */ var _patcher__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./patcher */ "./src/modules/patcher.js");
  1567. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Patcher", function() { return _patcher__WEBPACK_IMPORTED_MODULE_11__["default"]; });
  1568. /* harmony import */ var _pluginupdater__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./pluginupdater */ "./src/modules/pluginupdater.js");
  1569. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "PluginUpdater", function() { return _pluginupdater__WEBPACK_IMPORTED_MODULE_12__["default"]; });
  1570. /* harmony import */ var _pluginutilities__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./pluginutilities */ "./src/modules/pluginutilities.js");
  1571. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "PluginUtilities", function() { return _pluginutilities__WEBPACK_IMPORTED_MODULE_13__["default"]; });
  1572. /* harmony import */ var _discordclassmodules__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./discordclassmodules */ "./src/modules/discordclassmodules.js");
  1573. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "DiscordClassModules", function() { return _discordclassmodules__WEBPACK_IMPORTED_MODULE_14__["default"]; });
  1574. /* harmony import */ var structs__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! structs */ "./src/structs/structs.js");
  1575. /* harmony reexport (module object) */ __webpack_require__.d(__webpack_exports__, "Structs", function() { return structs__WEBPACK_IMPORTED_MODULE_15__; });
  1576. // export {default as DiscordComponents} from "./discordcomponents";
  1577. /***/ }),
  1578. /***/ "./src/modules/patcher.js":
  1579. /*!********************************!*\
  1580. !*** ./src/modules/patcher.js ***!
  1581. \********************************/
  1582. /*! exports provided: default */
  1583. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  1584. "use strict";
  1585. __webpack_require__.r(__webpack_exports__);
  1586. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return Patcher; });
  1587. /* harmony import */ var _logger__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./logger */ "./src/modules/logger.js");
  1588. /* harmony import */ var _discordmodules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./discordmodules */ "./src/modules/discordmodules.js");
  1589. /* harmony import */ var _webpackmodules__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./webpackmodules */ "./src/modules/webpackmodules.js");
  1590. /**
  1591. * Patcher that can patch other functions allowing you to run code before, after or
  1592. * instead of the original function. Can also alter arguments and return values.
  1593. *
  1594. * This is a modified version of what we have been working on in BDv2. {@link https://github.com/JsSucks/BetterDiscordApp/blob/master/client/src/modules/patcher.js}
  1595. *
  1596. * @module Patcher
  1597. * @version 0.0.2
  1598. */
  1599. class Patcher {
  1600. // Use window._patches instead of local variables in case something tries to whack the lib
  1601. static get patches() {return window._patches || (window._patches = []);}
  1602. /**
  1603. * Returns all the patches done by a specific caller
  1604. * @param {string} name - Name of the patch caller
  1605. * @method
  1606. */
  1607. static getPatchesByCaller(name) {
  1608. if (!name) return [];
  1609. const patches = [];
  1610. for (const patch of this.patches) {
  1611. for (const childPatch of patch.children) {
  1612. if (childPatch.caller === name) patches.push(childPatch);
  1613. }
  1614. }
  1615. return patches;
  1616. }
  1617. /**
  1618. * Unpatches all patches passed, or when a string is passed unpatches all
  1619. * patches done by that specific caller.
  1620. * @param {Array|string} patches - Either an array of patches to unpatch or a caller name
  1621. */
  1622. static unpatchAll(patches) {
  1623. if (typeof patches === "string") patches = this.getPatchesByCaller(patches);
  1624. for (const patch of patches) {
  1625. patch.unpatch();
  1626. }
  1627. }
  1628. static resolveModule(module) {
  1629. if (!module || typeof(module) === "function" || (typeof(module) === "object" && !Array.isArray(module))) return module;
  1630. if (typeof module === "string") return _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"][module];
  1631. if (Array.isArray(module)) return _webpackmodules__WEBPACK_IMPORTED_MODULE_2__["default"].findByUniqueProperties(module);
  1632. return null;
  1633. }
  1634. static makeOverride(patch) {
  1635. return function () {
  1636. let returnValue;
  1637. if (!patch.children || !patch.children.length) return patch.originalFunction.apply(this, arguments);
  1638. for (const superPatch of patch.children.filter(c => c.type === "before")) {
  1639. try {
  1640. superPatch.callback(this, arguments);
  1641. }
  1642. catch (err) {
  1643. _logger__WEBPACK_IMPORTED_MODULE_0__["default"].err("Patcher", `Could not fire before callback of ${patch.functionName} for ${superPatch.caller}`, err);
  1644. }
  1645. }
  1646. const insteads = patch.children.filter(c => c.type === "instead");
  1647. if (!insteads.length) {returnValue = patch.originalFunction.apply(this, arguments);}
  1648. else {
  1649. for (const insteadPatch of insteads) {
  1650. try {
  1651. const tempReturn = insteadPatch.callback(this, arguments, patch.originalFunction.bind(this));
  1652. if (typeof(tempReturn) !== "undefined") returnValue = tempReturn;
  1653. }
  1654. catch (err) {
  1655. _logger__WEBPACK_IMPORTED_MODULE_0__["default"].err("Patcher", `Could not fire instead callback of ${patch.functionName} for ${insteadPatch.caller}`, err);
  1656. }
  1657. }
  1658. }
  1659. for (const slavePatch of patch.children.filter(c => c.type === "after")) {
  1660. try {
  1661. const tempReturn = slavePatch.callback(this, arguments, returnValue);
  1662. if (typeof(tempReturn) !== "undefined") returnValue = tempReturn;
  1663. }
  1664. catch (err) {
  1665. _logger__WEBPACK_IMPORTED_MODULE_0__["default"].err("Patcher", `Could not fire after callback of ${patch.functionName} for ${slavePatch.caller}`, err);
  1666. }
  1667. }
  1668. return returnValue;
  1669. };
  1670. }
  1671. static rePatch(patch) {
  1672. patch.proxyFunction = patch.module[patch.functionName] = this.makeOverride(patch);
  1673. }
  1674. static makePatch(module, functionName, name) {
  1675. const patch = {
  1676. name,
  1677. module,
  1678. functionName,
  1679. originalFunction: module[functionName],
  1680. proxyFunction: null,
  1681. revert: () => { // Calling revert will destroy any patches added to the same module after this
  1682. patch.module[patch.functionName] = patch.originalFunction;
  1683. patch.proxyFunction = null;
  1684. patch.children = [];
  1685. },
  1686. counter: 0,
  1687. children: []
  1688. };
  1689. patch.proxyFunction = module[functionName] = this.makeOverride(patch);
  1690. Object.assign(module[functionName], patch.originalFunction);
  1691. module[functionName].__originalFunction = patch.originalFunction;
  1692. module[functionName].toString = () => patch.originalFunction.toString();
  1693. this.patches.push(patch);
  1694. return patch;
  1695. }
  1696. /**
  1697. * Function with no arguments and no return value that may be called to revert changes made by {@link module:Patcher}, restoring (unpatching) original method.
  1698. * @callback module:Patcher~unpatch
  1699. */
  1700. /**
  1701. * A callback that modifies method logic. This callback is called on each call of the original method and is provided all data about original call. Any of the data can be modified if necessary, but do so wisely.
  1702. *
  1703. * The third argument for the callback will be `undefined` for `before` patches. `originalFunction` for `instead` patches and `returnValue` for `after` patches.
  1704. *
  1705. * @callback module:Patcher~patchCallback
  1706. * @param {object} thisObject - `this` in the context of the original function.
  1707. * @param {arguments} arguments - The original arguments of the original function.
  1708. * @param {(function|*)} extraValue - For `instead` patches, this is the original function from the module. For `after` patches, this is the return value of the function.
  1709. * @return {*} Makes sense only when using an `instead` or `after` patch. If something other than `undefined` is returned, the returned value replaces the value of `returnValue`. If used for `before` the return value is ignored.
  1710. */
  1711. /**
  1712. * This method patches onto another function, allowing your code to run beforehand.
  1713. * Using this, you are also able to modify the incoming arguments before the original method is run.
  1714. *
  1715. * @param {string} caller - Name of the caller of the patch function. Using this you can undo all patches with the same name using {@link module:Patcher.unpatchAll}. Use `""` if you don't care.
  1716. * @param {object} moduleToPatch - Object with the function to be patched. Can also patch an object's prototype.
  1717. * @param {string} functionName - Name of the method to be patched
  1718. * @param {module:Patcher~patchCallback} callback - Function to run before the original method
  1719. * @param {object} options - Object used to pass additional options.
  1720. * @param {string} [options.displayName] You can provide meaningful name for class/object provided in `what` param for logging purposes. By default, this function will try to determine name automatically.
  1721. * @param {boolean} [options.forcePatch=true] Set to `true` to patch even if the function doesnt exist. (Adds noop function in place).
  1722. * @return {module:Patcher~unpatch} Function with no arguments and no return value that should be called to cancel (unpatch) this patch. You should save and run it when your plugin is stopped.
  1723. */
  1724. static before(caller, moduleToPatch, functionName, callback, options = {}) {return this.pushChildPatch(caller, moduleToPatch, functionName, callback, Object.assign(options, {type: "before"}));}
  1725. /**
  1726. * This method patches onto another function, allowing your code to run after.
  1727. * Using this, you are also able to modify the return value, using the return of your code instead.
  1728. *
  1729. * @param {string} caller - Name of the caller of the patch function. Using this you can undo all patches with the same name using {@link module:Patcher.unpatchAll}. Use `""` if you don't care.
  1730. * @param {object} moduleToPatch - Object with the function to be patched. Can also patch an object's prototype.
  1731. * @param {string} functionName - Name of the method to be patched
  1732. * @param {module:Patcher~patchCallback} callback - Function to run instead of the original method
  1733. * @param {object} options - Object used to pass additional options.
  1734. * @param {string} [options.displayName] You can provide meaningful name for class/object provided in `what` param for logging purposes. By default, this function will try to determine name automatically.
  1735. * @param {boolean} [options.forcePatch=true] Set to `true` to patch even if the function doesnt exist. (Adds noop function in place).
  1736. * @return {module:Patcher~unpatch} Function with no arguments and no return value that should be called to cancel (unpatch) this patch. You should save and run it when your plugin is stopped.
  1737. */
  1738. static after(caller, moduleToPatch, functionName, callback, options = {}) {return this.pushChildPatch(caller, moduleToPatch, functionName, callback, Object.assign(options, {type: "after"}));}
  1739. /**
  1740. * This method patches onto another function, allowing your code to run instead.
  1741. * Using this, you are also able to modify the return value, using the return of your code instead.
  1742. *
  1743. * @param {string} caller - Name of the caller of the patch function. Using this you can undo all patches with the same name using {@link module:Patcher.unpatchAll}. Use `""` if you don't care.
  1744. * @param {object} moduleToPatch - Object with the function to be patched. Can also patch an object's prototype.
  1745. * @param {string} functionName - Name of the method to be patched
  1746. * @param {module:Patcher~patchCallback} callback - Function to run after the original method
  1747. * @param {object} options - Object used to pass additional options.
  1748. * @param {string} [options.displayName] You can provide meaningful name for class/object provided in `what` param for logging purposes. By default, this function will try to determine name automatically.
  1749. * @param {boolean} [options.forcePatch=true] Set to `true` to patch even if the function doesnt exist. (Adds noop function in place).
  1750. * @return {module:Patcher~unpatch} Function with no arguments and no return value that should be called to cancel (unpatch) this patch. You should save and run it when your plugin is stopped.
  1751. */
  1752. static instead(caller, moduleToPatch, functionName, callback, options = {}) {return this.pushChildPatch(caller, moduleToPatch, functionName, callback, Object.assign(options, {type: "instead"}));}
  1753. /**
  1754. * This method patches onto another function, allowing your code to run before, instead or after the original function.
  1755. * Using this you are able to modify the incoming arguments before the original function is run as well as the return
  1756. * value before the original function actually returns.
  1757. *
  1758. * @param {string} caller - Name of the caller of the patch function. Using this you can undo all patches with the same name using {@link module:Patcher.unpatchAll}. Use `""` if you don't care.
  1759. * @param {object} moduleToPatch - Object with the function to be patched. Can also patch an object's prototype.
  1760. * @param {string} functionName - Name of the method to be patched
  1761. * @param {module:Patcher~patchCallback} callback - Function to run after the original method
  1762. * @param {object} options - Object used to pass additional options.
  1763. * @param {string} [options.type=after] - Determines whether to run the function `before`, `instead`, or `after` the original.
  1764. * @param {string} [options.displayName] You can provide meaningful name for class/object provided in `what` param for logging purposes. By default, this function will try to determine name automatically.
  1765. * @param {boolean} [options.forcePatch=true] Set to `true` to patch even if the function doesnt exist. (Adds noop function in place).
  1766. * @return {module:Patcher~unpatch} Function with no arguments and no return value that should be called to cancel (unpatch) this patch. You should save and run it when your plugin is stopped.
  1767. */
  1768. static pushChildPatch(caller, moduleToPatch, functionName, callback, options = {}) {
  1769. const {type = "after", forcePatch = true} = options;
  1770. const module = this.resolveModule(moduleToPatch);
  1771. if (!module) return null;
  1772. if (!module[functionName] && forcePatch) module[functionName] = function() {};
  1773. if (!(module[functionName] instanceof Function)) return null;
  1774. if (typeof moduleToPatch === "string") options.displayName = moduleToPatch;
  1775. const displayName = options.displayName || module.displayName || module.name || module.constructor.displayName || module.constructor.name;
  1776. const patchId = `${displayName}.${functionName}`;
  1777. const patch = this.patches.find(p => p.module == module && p.functionName == functionName) || this.makePatch(module, functionName, patchId);
  1778. if (!patch.proxyFunction) this.rePatch(patch);
  1779. const child = {
  1780. caller,
  1781. type,
  1782. id: patch.counter,
  1783. callback,
  1784. unpatch: () => {
  1785. patch.children.splice(patch.children.findIndex(cpatch => cpatch.id === child.id && cpatch.type === type), 1);
  1786. if (patch.children.length <= 0) {
  1787. const patchNum = this.patches.findIndex(p => p.module == module && p.functionName == functionName);
  1788. if (patchNum < 0) return;
  1789. this.patches[patchNum].revert();
  1790. this.patches.splice(patchNum, 1);
  1791. }
  1792. }
  1793. };
  1794. patch.children.push(child);
  1795. patch.counter++;
  1796. return child.unpatch;
  1797. }
  1798. }
  1799. /***/ }),
  1800. /***/ "./src/modules/pluginupdater.js":
  1801. /*!**************************************!*\
  1802. !*** ./src/modules/pluginupdater.js ***!
  1803. \**************************************/
  1804. /*! exports provided: default */
  1805. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  1806. "use strict";
  1807. __webpack_require__.r(__webpack_exports__);
  1808. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return PluginUpdater; });
  1809. /* harmony import */ var _pluginutilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./pluginutilities */ "./src/modules/pluginutilities.js");
  1810. /* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js");
  1811. /* harmony import */ var _logger__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./logger */ "./src/modules/logger.js");
  1812. /* harmony import */ var _discordclasses__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./discordclasses */ "./src/modules/discordclasses.js");
  1813. /* harmony import */ var ui__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ui */ "./src/ui/ui.js");
  1814. /* harmony import */ var _styles_updates_css__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../styles/updates.css */ "./src/styles/updates.css");
  1815. /**
  1816. * Functions that check for and update existing plugins.
  1817. * @module PluginUpdater
  1818. * @version 0.1.2
  1819. */
  1820. /**
  1821. * Function that gets the remote version from the file contents.
  1822. * @param {string} fileContent - the content of the remote file
  1823. * @returns {string} - remote version
  1824. * @callback module:PluginUpdater~versioner
  1825. */
  1826. /**
  1827. * Comparator that takes the current version and the remote version,
  1828. * then compares them returning `true` if there is an update and `false` otherwise.
  1829. * @param {string} currentVersion - the current version of the plugin
  1830. * @param {string} remoteVersion - the remote version of the plugin
  1831. * @returns {boolean} - whether the plugin has an update or not
  1832. * @callback module:PluginUpdater~comparator
  1833. */
  1834. class PluginUpdater {
  1835. static get CSS() {return _styles_updates_css__WEBPACK_IMPORTED_MODULE_5__["default"];}
  1836. /**
  1837. * Checks for updates for the specified plugin at the specified link. The final
  1838. * parameter should link to the raw text of the plugin and will compare semantic
  1839. * versions.
  1840. * @param {string} pluginName - name of the plugin
  1841. * @param {string} currentVersion - current version (semantic versioning only)
  1842. * @param {string} updateURL - url to check for update
  1843. * @param {module:PluginUpdater~versioner} [versioner] - versioner that finds the remote version. If not provided uses {@link module:PluginUpdater.defaultVersioner}.
  1844. * @param {module:PluginUpdater~comparator} [comparator] - comparator that determines if there is an update. If not provided uses {@link module:PluginUpdater.defaultComparator}.
  1845. */
  1846. static checkForUpdate(pluginName, currentVersion, updateURL, versioner, comparator) {
  1847. let updateLink = "https://raw.githubusercontent.com/rauenzi/BetterDiscordAddons/master/Plugins/" + pluginName + "/" + pluginName + ".plugin.js";
  1848. if (updateURL) updateLink = updateURL;
  1849. if (typeof(versioner) != "function") versioner = this.defaultVersioner;
  1850. if (typeof(comparator) != "function") comparator = this.defaultComparator;
  1851. if (typeof window.PluginUpdates === "undefined") {
  1852. window.PluginUpdates = {
  1853. plugins: {},
  1854. checkAll: async function() {
  1855. for (const key in this.plugins) {
  1856. const plugin = this.plugins[key];
  1857. if (!plugin.versioner) plugin.versioner = PluginUpdater.defaultVersioner;
  1858. if (!plugin.comparator) plugin.comparator = PluginUpdater.defaultComparator;
  1859. await PluginUpdater.processUpdateCheck(plugin.name, plugin.raw);
  1860. }
  1861. },
  1862. interval: setInterval(() => {
  1863. window.PluginUpdates.checkAll();
  1864. }, 7200000)
  1865. };
  1866. this.patchPluginList();
  1867. }
  1868. window.PluginUpdates.plugins[updateLink] = {name: pluginName, raw: updateLink, version: currentVersion, versioner: versioner, comparator: comparator};
  1869. PluginUpdater.processUpdateCheck(pluginName, updateLink);
  1870. }
  1871. /**
  1872. * Will check for updates and automatically show or remove the update notice
  1873. * bar based on the internal result. Better not to call this directly and to
  1874. * instead use {@link module:PluginUpdater.checkForUpdate}.
  1875. * @param {string} pluginName - name of the plugin to check
  1876. * @param {string} updateLink - link to the raw text version of the plugin
  1877. */
  1878. static async processUpdateCheck(pluginName, updateLink) {
  1879. return new Promise(resolve => {
  1880. const request = require("request");
  1881. request(updateLink, (error, response, result) => {
  1882. if (error || response.statusCode !== 200) return resolve();
  1883. const remoteVersion = window.PluginUpdates.plugins[updateLink].versioner(result);
  1884. const hasUpdate = window.PluginUpdates.plugins[updateLink].comparator(window.PluginUpdates.plugins[updateLink].version, remoteVersion);
  1885. if (hasUpdate) resolve(this.showUpdateNotice(pluginName, updateLink));
  1886. else resolve(this.removeUpdateNotice(pluginName));
  1887. });
  1888. });
  1889. }
  1890. /**
  1891. * The default versioner used as {@link module:PluginUpdater~versioner} for {@link module:PluginUpdater.checkForUpdate}.
  1892. * This works on basic semantic versioning e.g. "1.0.0". You do not need to provide this as a versioner if your plugin adheres
  1893. * to this style as this will be used as default.
  1894. * @param {string} currentVersion
  1895. * @param {string} content
  1896. */
  1897. static defaultVersioner(content) {
  1898. const remoteVersion = content.match(/['"][0-9]+\.[0-9]+\.[0-9]+['"]/i);
  1899. if (!remoteVersion) return "0.0.0";
  1900. return remoteVersion.toString().replace(/['"]/g, "");
  1901. }
  1902. /**
  1903. * The default comparator used as {@link module:PluginUpdater~comparator} for {@link module:PluginUpdater.checkForUpdate}.
  1904. * This works on basic semantic versioning e.g. "1.0.0". You do not need to provide this as a comparator if your plugin adheres
  1905. * to this style as this will be used as default.
  1906. * @param {string} currentVersion
  1907. * @param {string} content
  1908. */
  1909. static defaultComparator(currentVersion, remoteVersion) {
  1910. currentVersion = currentVersion.split(".").map((e) => {return parseInt(e);});
  1911. remoteVersion = remoteVersion.split(".").map((e) => {return parseInt(e);});
  1912. if (remoteVersion[0] > currentVersion[0]) return true;
  1913. else if (remoteVersion[0] == currentVersion[0] && remoteVersion[1] > currentVersion[1]) return true;
  1914. else if (remoteVersion[0] == currentVersion[0] && remoteVersion[1] == currentVersion[1] && remoteVersion[2] > currentVersion[2]) return true;
  1915. return false;
  1916. }
  1917. static patchPluginList() {
  1918. _domtools__WEBPACK_IMPORTED_MODULE_1__["default"].observer.subscribeToQuerySelector(mutation => {
  1919. if (!mutation.addedNodes || !mutation.addedNodes.length) return;
  1920. const button = document.getElementsByClassName("bd-pfbtn")[0];
  1921. if (!button || !button.textContent.toLowerCase().includes("plugin") || button.nextElementSibling.classList.contains("bd-updatebtn")) return;
  1922. button.after(PluginUpdater.createUpdateButton());
  1923. }, "#bd-settingspane-container");
  1924. }
  1925. /**
  1926. * Creates the update button found in the plugins page of BetterDiscord
  1927. * settings. Returned button will already have listeners to create the tooltip.
  1928. * @returns {HTMLElement} check for update button
  1929. */
  1930. static createUpdateButton() {
  1931. const updateButton = _domtools__WEBPACK_IMPORTED_MODULE_1__["default"].parseHTML(`<button class="bd-pfbtn bd-updatebtn" style="left: 220px;">Check for Updates</button>`);
  1932. updateButton.onclick = function () {
  1933. ui__WEBPACK_IMPORTED_MODULE_4__["Toasts"].info("Plugin update check in progress.");
  1934. window.PluginUpdates.checkAll().then(() => {ui__WEBPACK_IMPORTED_MODULE_4__["Toasts"].success("Plugin update check complete.");});
  1935. };
  1936. const tooltip = new ui__WEBPACK_IMPORTED_MODULE_4__["Tooltip"](updateButton, "Checks for updates of plugins that support this feature. Right-click for a list.");
  1937. updateButton.oncontextmenu = function () {
  1938. if (!window.PluginUpdates || !window.PluginUpdates.plugins) return;
  1939. tooltip.label = Object.values(window.PluginUpdates.plugins).map(p => p.name).join(", ");
  1940. tooltip.side = "bottom";
  1941. tooltip.show();
  1942. updateButton.onmouseout = function() {
  1943. tooltip.label = "Checks for updates of plugins that support this feature. Right-click for a list.";
  1944. tooltip.side = "top";
  1945. };
  1946. };
  1947. return updateButton;
  1948. }
  1949. /**
  1950. * Will download the latest version and replace the the old plugin version.
  1951. * Will also update the button in the update bar depending on if the user
  1952. * is using RestartNoMore plugin by square {@link https://github.com/Inve1951/BetterDiscordStuff/blob/master/plugins/restartNoMore.plugin.js}
  1953. * @param {string} pluginName - name of the plugin to download
  1954. * @param {string} updateLink - link to the raw text version of the plugin
  1955. */
  1956. static downloadPlugin(pluginName, updateLink) {
  1957. const request = require("request");
  1958. const fileSystem = require("fs");
  1959. const path = require("path");
  1960. request(updateLink, async (error, response, body) => {
  1961. if (error) return _logger__WEBPACK_IMPORTED_MODULE_2__["default"].warn("PluginUpdates", "Unable to get update for " + pluginName);
  1962. const remoteVersion = window.PluginUpdates.plugins[updateLink].versioner(body);
  1963. let filename = updateLink.split("/");
  1964. filename = filename[filename.length - 1];
  1965. const file = path.join(_pluginutilities__WEBPACK_IMPORTED_MODULE_0__["default"].getPluginsFolder(), filename);
  1966. await new Promise(r => fileSystem.writeFile(file, body, r));
  1967. ui__WEBPACK_IMPORTED_MODULE_4__["Toasts"].success(`${pluginName} ${window.PluginUpdates.plugins[updateLink].version} has been replaced by ${pluginName} ${remoteVersion}`);
  1968. this.removeUpdateNotice(pluginName);
  1969. if (BdApi.isSettingEnabled("fork-ps-5")) return;
  1970. if (!window.PluginUpdates.downloaded) {
  1971. window.PluginUpdates.downloaded = [];
  1972. const button = _domtools__WEBPACK_IMPORTED_MODULE_1__["default"].parseHTML(`<button class="btn btn-reload ${_discordclasses__WEBPACK_IMPORTED_MODULE_3__["default"].Notices.buttonMinor} ${_discordclasses__WEBPACK_IMPORTED_MODULE_3__["default"].Notices.button}">Reload</button>`);
  1973. const tooltip = new ui__WEBPACK_IMPORTED_MODULE_4__["Tooltip"](button, window.PluginUpdates.downloaded.join(", "), {side: "top"});
  1974. button.addEventListener("click", (e) => {
  1975. e.preventDefault();
  1976. window.location.reload(false);
  1977. });
  1978. button.addEventListener("mouseenter", () => {
  1979. tooltip.label = window.PluginUpdates.downloaded.join(", ");
  1980. });
  1981. document.getElementById("pluginNotice").append(button);
  1982. }
  1983. window.PluginUpdates.plugins[updateLink].version = remoteVersion;
  1984. window.PluginUpdates.downloaded.push(pluginName);
  1985. });
  1986. }
  1987. /**
  1988. * Will show the update notice top bar seen in Discord. Better not to call
  1989. * this directly and to instead use {@link module:PluginUpdater.checkForUpdate}.
  1990. * @param {string} pluginName - name of the plugin
  1991. * @param {string} updateLink - link to the raw text version of the plugin
  1992. */
  1993. static showUpdateNotice(pluginName, updateLink) {
  1994. if (!document.getElementById("pluginNotice")) {
  1995. const noticeElement = _domtools__WEBPACK_IMPORTED_MODULE_1__["default"].parseHTML(`<div class="${_discordclasses__WEBPACK_IMPORTED_MODULE_3__["default"].Notices.notice} ${_discordclasses__WEBPACK_IMPORTED_MODULE_3__["default"].Notices.colorInfo}" id="pluginNotice">
  1996. <div class="${_discordclasses__WEBPACK_IMPORTED_MODULE_3__["default"].Notices.closeButton}" id="pluginNoticeDismiss"></div>
  1997. <span class="notice-message">The following plugins have updates:</span>&nbsp;&nbsp;<strong id="outdatedPlugins"></strong>
  1998. </div>`);
  1999. _domtools__WEBPACK_IMPORTED_MODULE_1__["default"].query("[class*='app-'] > [class*='app-']").prepend(noticeElement);
  2000. noticeElement.querySelector("#pluginNoticeDismiss").addEventListener("click", async () => {
  2001. noticeElement.classList.add("closing");
  2002. await new Promise(resolve => setTimeout(resolve, 400));
  2003. noticeElement.remove();
  2004. });
  2005. }
  2006. const pluginNoticeID = pluginName + "-notice";
  2007. if (document.getElementById(pluginNoticeID)) return;
  2008. const pluginNoticeElement = _domtools__WEBPACK_IMPORTED_MODULE_1__["default"].parseHTML(`<span id="${pluginNoticeID}">${pluginName}</span>`);
  2009. pluginNoticeElement.addEventListener("click", () => {
  2010. this.downloadPlugin(pluginName, updateLink);
  2011. });
  2012. if (document.getElementById("outdatedPlugins").querySelectorAll("span").length) document.getElementById("outdatedPlugins").append(_domtools__WEBPACK_IMPORTED_MODULE_1__["default"].createElement("<span class='separator'>, </span>"));
  2013. document.getElementById("outdatedPlugins").append(pluginNoticeElement);
  2014. const tooltip = new ui__WEBPACK_IMPORTED_MODULE_4__["Tooltip"](pluginNoticeElement, "Click To Update!", {side: "bottom"});
  2015. // If this is the first one added, show the tooltip immediately.
  2016. if (document.getElementById("outdatedPlugins").querySelectorAll("span").length === 1) tooltip.show();
  2017. }
  2018. /**
  2019. * Will remove the plugin from the update notice top bar seen in Discord.
  2020. * Better not to call this directly and to instead use {@link module:PluginUpdater.checkForUpdate}.
  2021. * @param {string} pluginName - name of the plugin
  2022. */
  2023. static removeUpdateNotice(pluginName) {
  2024. if (!document.getElementById("outdatedPlugins")) return;
  2025. const notice = document.getElementById(pluginName + "-notice");
  2026. if (notice) {
  2027. if (notice.nextElementSibling && notice.nextElementSibling.matches(".separator")) notice.nextElementSibling.remove();
  2028. else if (notice.previousElementSibling && notice.previousElementSibling.matches(".separator")) notice.previousElementSibling.remove();
  2029. notice.remove();
  2030. }
  2031. if (!document.getElementById("outdatedPlugins").querySelectorAll("span").length) {
  2032. if (document.querySelector("#pluginNotice .btn-reload")) document.querySelector("#pluginNotice .notice-message").textContent = "To finish updating you need to reload.";
  2033. else document.getElementById("pluginNoticeDismiss").click();
  2034. }
  2035. }
  2036. }
  2037. /***/ }),
  2038. /***/ "./src/modules/pluginutilities.js":
  2039. /*!****************************************!*\
  2040. !*** ./src/modules/pluginutilities.js ***!
  2041. \****************************************/
  2042. /*! exports provided: default */
  2043. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  2044. "use strict";
  2045. __webpack_require__.r(__webpack_exports__);
  2046. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return PluginUtilities; });
  2047. /* harmony import */ var _logger__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./logger */ "./src/modules/logger.js");
  2048. /* harmony import */ var _utilities__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./utilities */ "./src/modules/utilities.js");
  2049. /* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js");
  2050. /* harmony import */ var _ui_discordcontextmenu__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../ui/discordcontextmenu */ "./src/ui/discordcontextmenu.js");
  2051. /**
  2052. * A series of useful functions for BetterDiscord plugins.
  2053. * @module PluginUtilities
  2054. * @version 0.2.5
  2055. */
  2056. class PluginUtilities {
  2057. /**
  2058. * Loads data through BetterDiscord's API.
  2059. * @param {string} name - name for the file (usually plugin name)
  2060. * @param {string} key - which key the data is saved under
  2061. * @param {object} defaultData - default data to populate the object with
  2062. * @returns {object} the combined saved and default data
  2063. */
  2064. static loadData(name, key, defaultData) {
  2065. const defaults = _utilities__WEBPACK_IMPORTED_MODULE_1__["default"].deepclone(defaultData);
  2066. try {return _utilities__WEBPACK_IMPORTED_MODULE_1__["default"].extend(defaults ? defaults : {}, BdApi.getData(name, key));}
  2067. catch (err) {_logger__WEBPACK_IMPORTED_MODULE_0__["default"].err(name, "Unable to load data: ", err);}
  2068. return defaults;
  2069. }
  2070. /**
  2071. * Saves data through BetterDiscord's API.
  2072. * @param {string} name - name for the file (usually plugin name)
  2073. * @param {string} key - which key the data should be saved under
  2074. * @param {object} data - data to save
  2075. */
  2076. static saveData(name, key, data) {
  2077. try {BdApi.setData(name, key, data);}
  2078. catch (err) {_logger__WEBPACK_IMPORTED_MODULE_0__["default"].err(name, "Unable to save data: ", err);}
  2079. }
  2080. /**
  2081. * Loads settings through BetterDiscord's API.
  2082. * @param {string} name - name for the file (usually plugin name)
  2083. * @param {object} defaultData - default data to populate the object with
  2084. * @returns {object} the combined saved and default settings
  2085. */
  2086. static loadSettings(name, defaultSettings) {
  2087. return this.loadData(name, "settings", defaultSettings);
  2088. }
  2089. /**
  2090. * Saves settings through BetterDiscord's API.
  2091. * @param {string} name - name for the file (usually plugin name)
  2092. * @param {object} data - settings to save
  2093. */
  2094. static saveSettings(name, data) {
  2095. this.saveData(name, "settings", data);
  2096. }
  2097. /**
  2098. * Get the full path to the BetterDiscord folder.
  2099. * @returns {string} full path to the BetterDiscord folder
  2100. */
  2101. static getBDFolder(subtarget = "") {
  2102. const process = require("process");
  2103. const path = require("path");
  2104. if (process.env.injDir) return path.resolve(process.env.injDir, subtarget);
  2105. switch (process.platform) {
  2106. case "win32":
  2107. return path.resolve(process.env.appdata, "BetterDiscord/", subtarget);
  2108. case "darwin":
  2109. return path.resolve(process.env.HOME, "Library/Preferences/", "BetterDiscord/", subtarget);
  2110. default:
  2111. return path.resolve(process.env.XDG_CONFIG_HOME ? process.env.XDG_CONFIG_HOME : process.env.HOME + "/.config", "BetterDiscord/", subtarget);
  2112. }
  2113. }
  2114. /**
  2115. * Get the full path to the plugins folder.
  2116. * @returns {string} full path to the plugins folder
  2117. */
  2118. static getPluginsFolder() {
  2119. return this.getBDFolder("plugins/");
  2120. }
  2121. /**
  2122. * Get the full path to the themes folder.
  2123. * @returns {string} full path to the themes folder
  2124. */
  2125. static getThemesFolder() {
  2126. return this.getBDFolder("themes/");
  2127. }
  2128. /**
  2129. * Adds a callback to a set of listeners for onSwitch.
  2130. * @param {callable} callback - basic callback to happen on channel switch
  2131. */
  2132. static addOnSwitchListener(callback) {
  2133. require("electron").remote.getCurrentWebContents().on("did-navigate-in-page", callback);
  2134. }
  2135. /**
  2136. * Removes the listener added by {@link InternalUtilities.addOnSwitchListener}.
  2137. * @param {callable} callback - callback to remove from the listener list
  2138. */
  2139. static removeOnSwitchListener(callback) {
  2140. require("electron").remote.getCurrentWebContents().removeListener("did-navigate-in-page", callback);
  2141. }
  2142. /**
  2143. * Adds a style to the document.
  2144. * @param {string} id - identifier to use as the element id
  2145. * @param {string} css - css to add to the document
  2146. */
  2147. static addStyle(id, css) {
  2148. document.head.append(_domtools__WEBPACK_IMPORTED_MODULE_2__["default"].createElement(`<style id="${id}">${css}</style>`));
  2149. }
  2150. /**
  2151. * Removes a style from the document.
  2152. * @param {string} id - original identifier used
  2153. */
  2154. static removeStyle(id) {
  2155. const element = document.getElementById(id);
  2156. if (element) element.remove();
  2157. }
  2158. /**
  2159. * Adds/requires a remote script to be loaded
  2160. * @param {string} id - identifier to use for this script
  2161. * @param {string} url - url from which to load the script
  2162. * @returns {Promise} promise that resolves when the script is loaded
  2163. */
  2164. static addScript(id, url) {
  2165. return new Promise(resolve => {
  2166. const script = document.createElement("script");
  2167. script.id = id;
  2168. script.src = url;
  2169. script.type = "text/javascript";
  2170. script.onload = resolve;
  2171. document.head.append(script);
  2172. });
  2173. }
  2174. /**
  2175. * Removes a remote script from the document.
  2176. * @param {string} id - original identifier used
  2177. */
  2178. static removeScript(id) {
  2179. const element = document.getElementById(id);
  2180. if (element) element.remove();
  2181. }
  2182. static async getContextMenu(type) {
  2183. return _ui_discordcontextmenu__WEBPACK_IMPORTED_MODULE_3__["default"].getDiscordMenu(type);
  2184. }
  2185. static forceUpdateContextMenus() {
  2186. return _ui_discordcontextmenu__WEBPACK_IMPORTED_MODULE_3__["default"].forceUpdateMenus();
  2187. }
  2188. }
  2189. /***/ }),
  2190. /***/ "./src/modules/reactcomponents.js":
  2191. /*!****************************************!*\
  2192. !*** ./src/modules/reactcomponents.js ***!
  2193. \****************************************/
  2194. /*! exports provided: ReactHelpers, default */
  2195. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  2196. "use strict";
  2197. __webpack_require__.r(__webpack_exports__);
  2198. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ReactHelpers", function() { return Helpers; });
  2199. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return ReactComponents; });
  2200. /* harmony import */ var _patcher__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./patcher */ "./src/modules/patcher.js");
  2201. /* harmony import */ var _reflection__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./reflection */ "./src/modules/reflection.js");
  2202. /* harmony import */ var _discordmodules__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./discordmodules */ "./src/modules/discordmodules.js");
  2203. /* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js");
  2204. /* harmony import */ var _reacttools__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./reacttools */ "./src/modules/reacttools.js");
  2205. /**
  2206. * BetterDiscord React Component Manipulations
  2207. * Original concept and some code by samogot - https://github.com/samogot / https://github.com/samogot/betterdiscord-plugins/tree/master/v2/1Lib%20Discord%20Internals
  2208. *
  2209. * Copyright (c) 2015-present JsSucks - https://github.com/JsSucks
  2210. * All rights reserved.
  2211. * https://github.com/JsSucks - https://betterdiscord.net
  2212. *
  2213. * This source code is licensed under the MIT license found in the
  2214. * LICENSE file in the root directory of this source tree.
  2215. */
  2216. class Helpers {
  2217. static get plannedActions() {
  2218. return this._plannedActions || (this._plannedActions = new Map());
  2219. }
  2220. static recursiveArray(parent, key, count = 1) {
  2221. let index = 0;
  2222. function* innerCall(parent, key) {
  2223. const item = parent[key];
  2224. if (Array.isArray(item)) {
  2225. for (const subKey of item.keys()) {
  2226. yield* innerCall(item, subKey);
  2227. }
  2228. return;
  2229. }
  2230. yield {item, parent, key, index: index++, count};
  2231. }
  2232. return innerCall(parent, key);
  2233. }
  2234. static recursiveArrayCount(parent, key) {
  2235. let count = 0;
  2236. // eslint-disable-next-line no-empty-pattern
  2237. for (let {} of this.recursiveArray(parent, key)) ++count;
  2238. return this.recursiveArray(parent, key, count);
  2239. }
  2240. static get recursiveChildren() {
  2241. return function* (parent, key, index = 0, count = 1) {
  2242. const item = parent[key];
  2243. yield {item, parent, key, index, count};
  2244. if (item && item.props && item.props.children) {
  2245. for (const {parent, key, index, count} of this.recursiveArrayCount(item.props, "children")) {
  2246. yield* this.recursiveChildren(parent, key, index, count);
  2247. }
  2248. }
  2249. };
  2250. }
  2251. static returnFirst(iterator, process) {
  2252. for (const child of iterator) {
  2253. const retVal = process(child);
  2254. if (retVal !== undefined) return retVal;
  2255. }
  2256. }
  2257. static getFirstChild(rootParent, rootKey, selector) {
  2258. const getDirectChild = (item, selector) => {
  2259. if (item && item.props && item.props.children) {
  2260. return this.returnFirst(this.recursiveArrayCount(item.props, "children"), checkFilter.bind(null, selector));
  2261. }
  2262. };
  2263. const checkFilter = (selector, {item, parent, key, count, index}) => {
  2264. let match = true;
  2265. if (selector.type) match = item && selector.type === item.type;
  2266. if (match && selector.tag) match = item && typeof item.type === "string" && selector.tag === item.type;
  2267. if (match && selector.className) {
  2268. match = item && item.props && typeof item.props.className === "string";
  2269. if (match) {
  2270. const classes = item.props.className.split(" ");
  2271. if (selector.className === true) match = !!classes[0];
  2272. else if (typeof selector.className === "string") match = classes.includes(selector.className);
  2273. else if (selector.className instanceof RegExp) match = !!classes.find(cls => selector.className.test(cls));
  2274. else match = false;
  2275. }
  2276. }
  2277. if (match && selector.text) {
  2278. if (selector.text === true) match = typeof item === "string";
  2279. else if (typeof selector.text === "string") match = item === selector.text;
  2280. else if (selector.text instanceof RegExp) match = typeof item === "string" && selector.text.test(item);
  2281. else match = false;
  2282. }
  2283. if (match && selector.nthChild) match = index === (selector.nthChild < 0 ? count + selector.nthChild : selector.nthChild);
  2284. if (match && selector.hasChild) match = getDirectChild(item, selector.hasChild);
  2285. if (match && selector.hasSuccessor) match = item && !!this.getFirstChild(parent, key, selector.hasSuccessor).item;
  2286. if (match && selector.eq) {
  2287. --selector.eq;
  2288. return;
  2289. }
  2290. if (match) {
  2291. if (selector.child) return getDirectChild(item, selector.child);
  2292. else if (selector.successor) return this.getFirstChild(parent, key, selector.successor);
  2293. return {item, parent, key};
  2294. }
  2295. };
  2296. return this.returnFirst(this.recursiveChildren(rootParent, rootKey), checkFilter.bind(null, selector)) || {};
  2297. }
  2298. static parseSelector(selector) {
  2299. if (selector.startsWith(".")) return {className: selector.substr(1)};
  2300. if (selector.startsWith("#")) return {id: selector.substr(1)};
  2301. return {};
  2302. }
  2303. static findByProp(obj, what, value) {
  2304. if (obj.hasOwnProperty(what) && obj[what] === value) return obj;
  2305. if (obj.props && !obj.children) return this.findByProp(obj.props, what, value);
  2306. if (!obj.children) return null;
  2307. if (!Array.isArray(obj.children)) return this.findByProp(obj.children, what, value);
  2308. for (const child of obj.children) {
  2309. if (!child) continue;
  2310. const findInChild = this.findByProp(child, what, value);
  2311. if (findInChild) return findInChild;
  2312. }
  2313. return null;
  2314. }
  2315. static findProp(obj, what) {
  2316. if (obj.hasOwnProperty(what)) return obj[what];
  2317. if (obj.props && !obj.children) return this.findProp(obj.props, what);
  2318. if (!obj.children) return null;
  2319. if (!Array.isArray(obj.children)) return this.findProp(obj.children, what);
  2320. for (const child of obj.children) {
  2321. if (!child) continue;
  2322. const findInChild = this.findProp(child, what);
  2323. if (findInChild) return findInChild;
  2324. }
  2325. return null;
  2326. }
  2327. static get React() {
  2328. return _discordmodules__WEBPACK_IMPORTED_MODULE_2__["default"].React;
  2329. }
  2330. static get ReactDOM() {
  2331. return _discordmodules__WEBPACK_IMPORTED_MODULE_2__["default"].ReactDOM;
  2332. }
  2333. }
  2334. class ReactComponent {
  2335. constructor(id, component, selector, filter) {
  2336. this.id = id;
  2337. this.component = component;
  2338. // this.important = important;
  2339. this.selector = selector;
  2340. this.filter = filter;
  2341. }
  2342. forceUpdateAll() {
  2343. if (!this.selector) return;
  2344. for (const e of document.querySelectorAll(this.selector)) {
  2345. Object(_reflection__WEBPACK_IMPORTED_MODULE_1__["default"])(e).forceUpdate(this); // eslint-disable-line new-cap
  2346. }
  2347. }
  2348. }
  2349. /**
  2350. * Methods for obtaining and interacting with react components.
  2351. * @module ReactComponents
  2352. * @version 0.0.1
  2353. */
  2354. class ReactComponents {
  2355. static get components() {return this._components || (this._components = new Map());}
  2356. static get unknownComponents() {return this._unknownComponents || (this._unknownComponents = new Set());}
  2357. static get listeners() {return this._listeners || (this._listeners = new Map());}
  2358. static get nameSetters() {return this._nameSetters || (this._nameSetters = new Set());}
  2359. static get ReactComponent() {return ReactComponent;}
  2360. static get Helpers() {return Helpers;}
  2361. static get AutoPatcher() {return ReactAutoPatcher;}
  2362. static push(component, selector, filter) {
  2363. if (typeof(component) !== "function") return null;
  2364. const {displayName} = component;
  2365. if (!displayName) return this.processUnknown(component);
  2366. const have = this.components.get(displayName);
  2367. if (have) {
  2368. if (!have.selector) have.selector = selector;
  2369. if (!have.filter) have.filter = filter;
  2370. return component;
  2371. }
  2372. const c = new ReactComponent(displayName, component, selector, filter);
  2373. this.components.set(c.id, c);
  2374. // if (!have) this.components.push(c);
  2375. const listener = this.listeners.get(displayName);
  2376. if (listener) {
  2377. for (const l of listener.children) l(c);
  2378. this.listeners.delete(listener);
  2379. }
  2380. // for (const listen of this.listeners) {
  2381. // if (!listen.filter) continue;
  2382. // }
  2383. return c;
  2384. }
  2385. /**
  2386. * Finds a component from the components array or by waiting for it to be mounted.
  2387. * @param {String} name The component's name
  2388. * @param {Object} selector A selector to look for
  2389. * @return {Promise<ReactComponent>}
  2390. */
  2391. static async getComponentByName(name, selector) {
  2392. return this.getComponent(name, selector, m => m.displayName == name);
  2393. }
  2394. /**
  2395. * Finds a component from the components array or by waiting for it to be mounted.
  2396. * @param {String} name The component's name
  2397. * @param {Object} selector A selector to look for
  2398. * @param {Function} filter A function to filter components if a single element is rendered by multiple components
  2399. * @return {Promise<ReactComponent>}
  2400. */
  2401. static async getComponent(name, selector, filter) {
  2402. const have = this.components.get(name);
  2403. if (have) {
  2404. if (!have.selector) have.selector = selector;
  2405. if (!have.filter) have.filter = filter;
  2406. return have;
  2407. }
  2408. if (selector) {
  2409. const callback = () => {
  2410. if (this.components.get(name)) {
  2411. // Logger.info("ReactComponents", `Important component ${name} already found`);
  2412. _domtools__WEBPACK_IMPORTED_MODULE_3__["default"].observer.unsubscribe(observerSubscription);
  2413. return;
  2414. }
  2415. const elements = document.querySelectorAll(selector);
  2416. if (!elements.length) return;
  2417. let component, reflect;
  2418. for (const element of elements) {
  2419. reflect = Object(_reflection__WEBPACK_IMPORTED_MODULE_1__["default"])(element); // eslint-disable-line new-cap
  2420. component = filter ? reflect.components.find(filter) : reflect.component;
  2421. if (component) break;
  2422. }
  2423. if (!component && filter) return;// Logger.log("ReactComponents", ["Found elements matching the query selector but no components passed the filter"]);
  2424. _domtools__WEBPACK_IMPORTED_MODULE_3__["default"].observer.unsubscribe(observerSubscription);
  2425. if (!component) return;// Logger.err("ReactComponents", [`FAILED TO GET IMPORTANT COMPONENT ${name} WITH REFLECTION FROM`, elements]);
  2426. if (!component.displayName) component.displayName = name;
  2427. // if (component.displayName && component.displayName != name) {
  2428. // let existing = this.listeners.find(l => l.id === component.displayName);
  2429. // let current = this.listeners.find(l => l.id === name);
  2430. // if (!existing) {current.id = component.displayName;}
  2431. // else {
  2432. // existing.listeners.push(current.listeners);
  2433. // Utilities.removeFromArray(this.listeners, current);
  2434. // }
  2435. // }
  2436. // Logger.info("ReactComponents", [`Found important component ${name} with reflection`, reflect]);
  2437. this.push(component, selector, filter);
  2438. };
  2439. const observerSubscription = _domtools__WEBPACK_IMPORTED_MODULE_3__["default"].observer.subscribeToQuerySelector(callback, selector, null, true);
  2440. setTimeout(callback, 0);
  2441. }
  2442. let listener = this.listeners.get(name);
  2443. if (!listener) {
  2444. listener = {
  2445. id: name,
  2446. children: [],
  2447. filter
  2448. };
  2449. this.listeners.set(name, listener);
  2450. }
  2451. return new Promise(resolve => {
  2452. listener.children.push(resolve);
  2453. });
  2454. }
  2455. static setName(name, filter) {
  2456. const have = this.components.get(name);
  2457. if (have) return have;
  2458. for (const component of this.unknownComponents.entries()) {
  2459. if (!filter(component)) continue;
  2460. component.displayName = name;
  2461. this.unknownComponents.delete(component);
  2462. return this.push(component);
  2463. }
  2464. return this.nameSetters.add({name, filter});
  2465. }
  2466. static processUnknown(component) {
  2467. const have = this.unknownComponents.has(component);
  2468. for (const setter of this.nameSetters.entries()) {
  2469. if (setter.filter.filter(component)) {
  2470. component.displayName = setter.name;
  2471. this.nameSetters.delete(setter);
  2472. return this.push(component);
  2473. }
  2474. }
  2475. if (have) return have;
  2476. this.unknownComponents.add(component);
  2477. return component;
  2478. }
  2479. static *recursiveComponents(internalInstance = _reacttools__WEBPACK_IMPORTED_MODULE_4__["default"].rootInstance) {
  2480. if (internalInstance.stateNode) yield internalInstance.stateNode;
  2481. if (internalInstance.sibling) yield* this.recursiveComponents(internalInstance.sibling);
  2482. if (internalInstance.child) yield* this.recursiveComponents(internalInstance.child);
  2483. }
  2484. }
  2485. class ReactAutoPatcher {
  2486. /**
  2487. * Wait for React to be loaded and patch it's createElement to store all unknown components.
  2488. * Also patches some known components.
  2489. */
  2490. static async autoPatch() {
  2491. this.autoUnpatch();
  2492. _patcher__WEBPACK_IMPORTED_MODULE_0__["default"].before("ReactComponents", _discordmodules__WEBPACK_IMPORTED_MODULE_2__["default"].React, "createElement", (react, [component]) => ReactComponents.push(component));
  2493. _patcher__WEBPACK_IMPORTED_MODULE_0__["default"].instead("ReactComponents", _discordmodules__WEBPACK_IMPORTED_MODULE_2__["default"].React.Component.prototype, "UNSAFE_componentWillMount", (component) => ReactComponents.push(component));
  2494. _patcher__WEBPACK_IMPORTED_MODULE_0__["default"].instead("ReactComponents", _discordmodules__WEBPACK_IMPORTED_MODULE_2__["default"].React.Component.prototype, "componentWillMount", (component) => ReactComponents.push(component));
  2495. // this.patchComponents();
  2496. }
  2497. static async autoUnpatch() {
  2498. _patcher__WEBPACK_IMPORTED_MODULE_0__["default"].unpatchAll("ReactComponents");
  2499. }
  2500. /**
  2501. * Finds and processes all currently available react components.
  2502. */
  2503. static processAll() {
  2504. for (const component of ReactComponents.recursiveComponents()) {
  2505. ReactComponents.push(component.constructor);
  2506. }
  2507. }
  2508. }
  2509. /***/ }),
  2510. /***/ "./src/modules/reacttools.js":
  2511. /*!***********************************!*\
  2512. !*** ./src/modules/reacttools.js ***!
  2513. \***********************************/
  2514. /*! exports provided: default */
  2515. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  2516. "use strict";
  2517. __webpack_require__.r(__webpack_exports__);
  2518. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return ReactTools; });
  2519. /* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js");
  2520. /* harmony import */ var _discordmodules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./discordmodules */ "./src/modules/discordmodules.js");
  2521. /* harmony import */ var _utilities__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./utilities */ "./src/modules/utilities.js");
  2522. /* harmony import */ var _reflection__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./reflection */ "./src/modules/reflection.js");
  2523. /**
  2524. * Helpful utilities for dealing with getting react information from DOM objects.
  2525. * @module ReactTools
  2526. * @version 0.0.5
  2527. */
  2528. class ReactTools {
  2529. /**
  2530. * Performs reflection on a specific node.
  2531. * @param {(HTMLElement|jQuery|Selector)} node - node or selector to reflect on.
  2532. */
  2533. static Reflect(node) {
  2534. return Object(_reflection__WEBPACK_IMPORTED_MODULE_3__["default"])(node);
  2535. }
  2536. static get rootInstance() {return document.getElementById("app-mount")._reactRootContainer._internalRoot.current;}
  2537. /**
  2538. * Grabs the react internal instance of a specific node.
  2539. * @param {(HTMLElement|jQuery)} node - node to obtain react instance of
  2540. * @return {object} the internal react instance
  2541. */
  2542. static getReactInstance(node) {
  2543. const domNode = _domtools__WEBPACK_IMPORTED_MODULE_0__["default"].resolveElement(node);
  2544. if (!(domNode instanceof Element)) return undefined;
  2545. return domNode[Object.keys(domNode).find((key) => key.startsWith("__reactInternalInstance") || key.startsWith("__reactFiber"))];
  2546. }
  2547. /**
  2548. * Grabs a value from the react internal instance. Allows you to grab
  2549. * long depth values safely without accessing no longer valid properties.
  2550. * @param {(HTMLElement|jQuery)} node - node to obtain react instance of
  2551. * @param {string} path - path to the requested value
  2552. * @return {(*|undefined)} the value requested or undefined if not found.
  2553. */
  2554. static getReactProperty(node, path) {
  2555. return _utilities__WEBPACK_IMPORTED_MODULE_2__["default"].getNestedProp(this.getReactInstance(node), path);
  2556. }
  2557. /**
  2558. * Grabs a value from the react internal instance. Allows you to grab
  2559. * long depth values safely without accessing no longer valid properties.
  2560. * @param {(HTMLElement|jQuery)} node - node to obtain react instance of
  2561. * @param {object} options - options for the search
  2562. * @param {array} [options.include] - list of items to include from the search
  2563. * @param {array} [options.exclude=["Popout", "Tooltip", "Scroller", "BackgroundFlash"]] - list of items to exclude from the search
  2564. * @param {callable} [options.filter=_=>_] - filter to check the current instance with (should return a boolean)
  2565. * @return {(*|null)} the owner instance or undefined if not found.
  2566. */
  2567. static getOwnerInstance(node, {include, exclude = ["Popout", "Tooltip", "Scroller", "BackgroundFlash"], filter = _ => _} = {}) {
  2568. if (node === undefined) return undefined;
  2569. const excluding = include === undefined;
  2570. const nameFilter = excluding ? exclude : include;
  2571. function getDisplayName(owner) {
  2572. const type = owner.type;
  2573. if (!type) return null;
  2574. return type.displayName || type.name || null;
  2575. }
  2576. function classFilter(owner) {
  2577. const name = getDisplayName(owner);
  2578. return (name !== null && !!(nameFilter.includes(name) ^ excluding));
  2579. }
  2580. let curr = this.getReactInstance(node);
  2581. for (curr = curr && curr.return; !_utilities__WEBPACK_IMPORTED_MODULE_2__["default"].isNil(curr); curr = curr.return) {
  2582. if (_utilities__WEBPACK_IMPORTED_MODULE_2__["default"].isNil(curr)) continue;
  2583. const owner = curr.stateNode;
  2584. if (!_utilities__WEBPACK_IMPORTED_MODULE_2__["default"].isNil(owner) && !(owner instanceof HTMLElement) && classFilter(curr) && filter(owner)) return owner;
  2585. }
  2586. return null;
  2587. }
  2588. /**
  2589. * Creates and renders a react element that wraps dom elements.
  2590. * @param {(HTMLElement|Array<HTMLElement>)} element - element or array of elements to wrap into a react element
  2591. * @returns {object} - rendered react element
  2592. */
  2593. static createWrappedElement(element) {
  2594. if (Array.isArray(element)) element = _domtools__WEBPACK_IMPORTED_MODULE_0__["default"].wrap(element);
  2595. return _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].React.createElement(this.wrapElement(element));
  2596. }
  2597. /**
  2598. * Creates an unrendered react component that wraps dom elements.
  2599. * @param {(HTMLElement|Array<HTMLElement>)} element - element or array of elements to wrap into a react component
  2600. * @returns {object} - unrendered react component
  2601. */
  2602. static wrapElement(element) {
  2603. if (Array.isArray(element)) element = _domtools__WEBPACK_IMPORTED_MODULE_0__["default"].wrap(element);
  2604. return class ReactWrapper extends _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].React.Component {
  2605. constructor(props) {
  2606. super(props);
  2607. this.element = element;
  2608. }
  2609. componentDidMount() {this.refs.element.appendChild(this.element);}
  2610. render() {return _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].React.createElement("div", {className: "react-wrapper", ref: "element"});}
  2611. };
  2612. }
  2613. }
  2614. /***/ }),
  2615. /***/ "./src/modules/reflection.js":
  2616. /*!***********************************!*\
  2617. !*** ./src/modules/reflection.js ***!
  2618. \***********************************/
  2619. /*! exports provided: default */
  2620. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  2621. "use strict";
  2622. __webpack_require__.r(__webpack_exports__);
  2623. /* harmony import */ var _logger__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./logger */ "./src/modules/logger.js");
  2624. /* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js");
  2625. /* harmony import */ var _webpackmodules__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./webpackmodules */ "./src/modules/webpackmodules.js");
  2626. /* harmony import */ var _reactcomponents__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./reactcomponents */ "./src/modules/reactcomponents.js");
  2627. /**
  2628. * BetterDiscord Reflection Module
  2629. * Copyright (c) 2015-present JsSucks - https://github.com/JsSucks
  2630. * All rights reserved.
  2631. * https://betterdiscord.net
  2632. *
  2633. * This source code is licensed under the MIT license found in the
  2634. * LICENSE file in the root directory of this source tree.
  2635. */
  2636. class Reflection {
  2637. static reactInternalInstance(node) {
  2638. if (!node) return null;
  2639. if (!Object.keys(node) || !Object.keys(node).length) return null;
  2640. const riiKey = Object.keys(node).find(k => k.startsWith("__reactInternalInstance") || k.startsWith("__reactFiber"));
  2641. return riiKey ? node[riiKey] : null;
  2642. }
  2643. static findProp(node, prop) {
  2644. const ii = this.reactInternalInstance(node);
  2645. if (!ii) return null;
  2646. const fir = this.findInReturn(ii, prop);
  2647. if (fir) return fir;
  2648. const fim = this.findInChildProps(ii, prop);
  2649. if (fim) return fim;
  2650. return null;
  2651. }
  2652. static findInReturn(internalInstance, prop) {
  2653. const r = internalInstance.return;
  2654. if (!r) return null;
  2655. let find = this.findMemoizedProp(r, prop);
  2656. if (find) return find;
  2657. find = this.findMemoizedState(r, prop);
  2658. if (find) return find;
  2659. return this.findInReturn(r, prop);
  2660. }
  2661. static findMemoizedProp(obj, prop) {
  2662. if (!obj.hasOwnProperty("memoizedProps")) return null;
  2663. obj = obj.memoizedProps;
  2664. return this.findPropIn(obj, prop);
  2665. }
  2666. static findMemoizedState(obj, prop) {
  2667. if (!obj.hasOwnProperty("memoizedState")) return null;
  2668. obj = obj.memoizedState;
  2669. return this.findPropIn(obj, prop);
  2670. }
  2671. static findInChildProps(obj, prop) {
  2672. try {
  2673. const f = obj.children || obj.memoizedProps.children;
  2674. if (!f.props) return null;
  2675. if (!f.props.hasOwnProperty(prop)) return null;
  2676. return f.props[prop];
  2677. }
  2678. catch (err) {
  2679. return null;
  2680. }
  2681. }
  2682. static findPropIn(obj, prop) {
  2683. if (obj && !Array.isArray(obj) && obj instanceof Object && obj.hasOwnProperty(prop)) return obj[prop];
  2684. if (obj && Array.isArray(obj)) {
  2685. const found = obj.find(mp => {
  2686. if (mp.props && mp.props.hasOwnProperty(prop)) return true;
  2687. });
  2688. if (found) return found;
  2689. }
  2690. return null;
  2691. }
  2692. static propIterator(obj, propNames) {
  2693. if (obj === null || obj === undefined) return null;
  2694. const curPropName = propNames.shift(1);
  2695. if (!obj.hasOwnProperty(curPropName)) return null;
  2696. const curProp = obj[curPropName];
  2697. if (propNames.length === 0) {
  2698. return curProp;
  2699. }
  2700. return this.propIterator(curProp, propNames);
  2701. }
  2702. static getState(node) {
  2703. const stateNode = this.getStateNode(node);
  2704. if (stateNode) return stateNode.state;
  2705. }
  2706. static getStateNode(node) {
  2707. return this.getStateNodes(node)[0];
  2708. }
  2709. static getStateNodes(node) {
  2710. const instance = this.reactInternalInstance(node);
  2711. const stateNodes = [];
  2712. let lastInstance = instance;
  2713. while (lastInstance && lastInstance.return) {
  2714. if (lastInstance.return.stateNode instanceof HTMLElement) break;
  2715. if (lastInstance.return.stateNode) stateNodes.push(lastInstance.return.stateNode);
  2716. lastInstance = lastInstance.return;
  2717. }
  2718. return stateNodes;
  2719. }
  2720. static getComponentStateNode(node, component) {
  2721. if (component instanceof _reactcomponents__WEBPACK_IMPORTED_MODULE_3__["default"].ReactComponent) component = component.component;
  2722. for (const stateNode of this.getStateNodes(node)) {
  2723. if (stateNode instanceof component) return stateNode;
  2724. }
  2725. }
  2726. static findStateNode(node, filter, first = true) {
  2727. return this.getStateNodes(node)[first ? "find" : "filter"](filter);
  2728. }
  2729. static getComponent(node) {
  2730. return this.getComponents(node)[0];
  2731. }
  2732. static getComponents(node) {
  2733. const instance = this.reactInternalInstance(node);
  2734. const components = [];
  2735. let lastInstance = instance;
  2736. while (lastInstance && lastInstance.return) {
  2737. if (typeof lastInstance.return.type === "string") break;
  2738. if (lastInstance.return.type) components.push(lastInstance.return.type);
  2739. lastInstance = lastInstance.return;
  2740. }
  2741. return components;
  2742. }
  2743. static findComponent(node, filter, first = true) {
  2744. return this.getComponents(node)[first ? "find" : "filter"](filter);
  2745. }
  2746. }
  2747. const propsProxyHandler = {
  2748. get(node, prop) {
  2749. return Reflection.findProp(node, prop);
  2750. }
  2751. };
  2752. /* harmony default export */ __webpack_exports__["default"] = (function(node) {
  2753. return new class ReflectionInstance {
  2754. constructor(ele) {
  2755. if (typeof ele === "string") ele = document.querySelector(ele);
  2756. this.node = _domtools__WEBPACK_IMPORTED_MODULE_1__["default"].resolveElement(ele);
  2757. }
  2758. get el() {return this.node;}
  2759. get element() {return this.node;}
  2760. get reactInternalInstance() {
  2761. return Reflection.reactInternalInstance(this.node);
  2762. }
  2763. get props() {
  2764. return new Proxy(this.node, propsProxyHandler);
  2765. }
  2766. get state() {
  2767. return Reflection.getState(this.node);
  2768. }
  2769. get stateNode() {
  2770. return Reflection.getStateNode(this.node);
  2771. }
  2772. get stateNodes() {
  2773. return Reflection.getStateNodes(this.node);
  2774. }
  2775. getComponentStateNode(component) {
  2776. return Reflection.getComponentStateNode(this.node, component);
  2777. }
  2778. findStateNode(filter) {
  2779. if (typeof filter === "function") return Reflection.findStateNode(this.node, filter);
  2780. if (filter) return Reflection.getComponentStateNode(this.node, filter);
  2781. return Reflection.getStateNode(this.node);
  2782. }
  2783. get component() {
  2784. return Reflection.getComponent(this.node);
  2785. }
  2786. get components() {
  2787. return Reflection.getComponents(this.node);
  2788. }
  2789. getComponentByProps(props, selector) {
  2790. return Reflection.findComponent(this.node, _webpackmodules__WEBPACK_IMPORTED_MODULE_2__["Filters"].byProperties(props, selector));
  2791. }
  2792. getComponentByPrototypes(props, selector) {
  2793. return Reflection.findComponent(this.node, _webpackmodules__WEBPACK_IMPORTED_MODULE_2__["Filters"].byPrototypeFields(props, selector));
  2794. }
  2795. getComponentByRegex(regex, selector) {
  2796. return Reflection.findComponent(this.node, _webpackmodules__WEBPACK_IMPORTED_MODULE_2__["Filters"].byCode(regex, selector));
  2797. }
  2798. getComponentByDisplayName(name) {
  2799. return Reflection.findComponent(this.node, _webpackmodules__WEBPACK_IMPORTED_MODULE_2__["Filters"].byDisplayName(name));
  2800. }
  2801. forceUpdate(filter) {
  2802. try {
  2803. const stateNode = this.findStateNode(filter);
  2804. if (!stateNode || !stateNode.forceUpdate) return;
  2805. stateNode.forceUpdate();
  2806. }
  2807. catch (err) {
  2808. _logger__WEBPACK_IMPORTED_MODULE_0__["default"].err("Reflection", err);
  2809. }
  2810. }
  2811. prop(propName) {
  2812. const split = propName.split(".");
  2813. const first = Reflection.findProp(this.node, split[0]);
  2814. if (split.length === 1) return first;
  2815. return Reflection.propIterator(first, split.slice(1));
  2816. }
  2817. }(node);
  2818. });
  2819. /***/ }),
  2820. /***/ "./src/modules/utilities.js":
  2821. /*!**********************************!*\
  2822. !*** ./src/modules/utilities.js ***!
  2823. \**********************************/
  2824. /*! exports provided: default */
  2825. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  2826. "use strict";
  2827. __webpack_require__.r(__webpack_exports__);
  2828. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return Utilities; });
  2829. /* harmony import */ var _logger__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./logger */ "./src/modules/logger.js");
  2830. /**
  2831. * Random set of utilities that didn't fit elsewhere.
  2832. * @module Utilities
  2833. * @version 0.0.2
  2834. */
  2835. class Utilities {
  2836. /**
  2837. * Stably sorts arrays since `.sort()` has issues.
  2838. * @param {Array} list - array to sort
  2839. * @param {function} comparator - comparator to sort by
  2840. */
  2841. static stableSort(list, comparator) {
  2842. const entries = Array(list.length);
  2843. // wrap values with initial indices
  2844. for (let index = 0; index < list.length; index++) {
  2845. entries[index] = [index, list[index]];
  2846. }
  2847. // sort with fallback based on initial indices
  2848. entries.sort(function (a, b) {
  2849. const comparison = Number(this(a[1], b[1]));
  2850. return comparison || a[0] - b[0];
  2851. }.bind(comparator));
  2852. // re-map original array to stable sorted values
  2853. for (let index = 0; index < list.length; index++) {
  2854. list[index] = entries[index][1];
  2855. }
  2856. }
  2857. /**
  2858. * Generates an automatically memoizing version of an object.
  2859. * @param {Object} object - object to memoize
  2860. * @returns {Proxy} the proxy to the object that memoizes properties
  2861. */
  2862. static memoizeObject(object) {
  2863. const proxy = new Proxy(object, {
  2864. get: function(obj, mod) {
  2865. if (!obj.hasOwnProperty(mod)) return undefined;
  2866. if (Object.getOwnPropertyDescriptor(obj, mod).get) {
  2867. const value = obj[mod];
  2868. delete obj[mod];
  2869. obj[mod] = value;
  2870. }
  2871. return obj[mod];
  2872. },
  2873. set: function(obj, mod, value) {
  2874. if (obj.hasOwnProperty(mod)) return _logger__WEBPACK_IMPORTED_MODULE_0__["default"].err("MemoizedObject", "Trying to overwrite existing property");
  2875. obj[mod] = value;
  2876. return obj[mod];
  2877. }
  2878. });
  2879. Object.defineProperty(proxy, "hasOwnProperty", {value: function(prop) {
  2880. return this[prop] !== undefined;
  2881. }});
  2882. return proxy;
  2883. }
  2884. /**
  2885. * Wraps the method in a `try..catch` block.
  2886. * @param {callable} method - method to wrap
  2887. * @param {string} description - description of method
  2888. * @returns {callable} wrapped version of method
  2889. */
  2890. static suppressErrors(method, description) {
  2891. return (...params) => {
  2892. try {return method(...params);}
  2893. catch (e) {_logger__WEBPACK_IMPORTED_MODULE_0__["default"].err("Suppression", "Error occurred in " + description, e);}
  2894. };
  2895. }
  2896. /**
  2897. * This only exists because Samo relied on lodash being there... fuck lodash.
  2898. * @param {*} anything - whatever you want
  2899. */
  2900. static isNil(anything) {
  2901. return anything == null;
  2902. }
  2903. /**
  2904. * Format template strings with placeholders (`${placeholder}`) into full strings.
  2905. * Quick example: `PluginUtilities.formatString("Hello, ${user}", {user: "Zerebos"})`
  2906. * would return "Hello, Zerebos".
  2907. * @param {string} string - string to format
  2908. * @param {object} values - object literal of placeholders to replacements
  2909. * @returns {string} the properly formatted string
  2910. */
  2911. static formatTString(string, values) {
  2912. for (const val in values) {
  2913. let replacement = values[val];
  2914. if (Array.isArray(replacement)) replacement = JSON.stringify(replacement);
  2915. if (typeof(replacement) === "object" && replacement !== null) replacement = replacement.toString();
  2916. string = string.replace(new RegExp(`\\$\\{${val}\\}`, "g"), replacement);
  2917. }
  2918. return string;
  2919. }
  2920. /**
  2921. * Format strings with placeholders (`{{placeholder}}`) into full strings.
  2922. * Quick example: `PluginUtilities.formatString("Hello, {{user}}", {user: "Zerebos"})`
  2923. * would return "Hello, Zerebos".
  2924. * @param {string} string - string to format
  2925. * @param {object} values - object literal of placeholders to replacements
  2926. * @returns {string} the properly formatted string
  2927. */
  2928. static formatString(string, values) {
  2929. for (const val in values) {
  2930. let replacement = values[val];
  2931. if (Array.isArray(replacement)) replacement = JSON.stringify(replacement);
  2932. if (typeof(replacement) === "object" && replacement !== null) replacement = replacement.toString();
  2933. string = string.replace(new RegExp(`{{${val}}}`, "g"), replacement);
  2934. }
  2935. return string;
  2936. }
  2937. /**
  2938. * Finds a value, subobject, or array from a tree that matches a specific filter. Great for patching render functions.
  2939. * @param {object} tree React tree to look through. Can be a rendered object or an internal instance.
  2940. * @param {callable} searchFilter Filter function to check subobjects against.
  2941. */
  2942. static findInReactTree(tree, searchFilter) {
  2943. return this.findInTree(tree, searchFilter, {walkable: ["props", "children", "child", "sibling"]});
  2944. }
  2945. /**
  2946. * Finds a value, subobject, or array from a tree that matches a specific filter.
  2947. * @param {object} tree Tree that should be walked
  2948. * @param {callable} searchFilter Filter to check against each object and subobject
  2949. * @param {object} options Additional options to customize the search
  2950. * @param {Array<string>|null} [options.walkable=null] Array of strings to use as keys that are allowed to be walked on. Null value indicates all keys are walkable
  2951. * @param {Array<string>} [options.ignore=[]] Array of strings to use as keys to exclude from the search, most helpful when `walkable = null`.
  2952. */
  2953. static findInTree(tree, searchFilter, {walkable = null, ignore = []} = {}) {
  2954. if (typeof searchFilter === "string") {
  2955. if (tree.hasOwnProperty(searchFilter)) return tree[searchFilter];
  2956. }
  2957. else if (searchFilter(tree)) {
  2958. return tree;
  2959. }
  2960. if (typeof tree !== "object" || tree == null) return undefined;
  2961. let tempReturn;
  2962. if (Array.isArray(tree)) {
  2963. for (const value of tree) {
  2964. tempReturn = this.findInTree(value, searchFilter, {walkable, ignore});
  2965. if (typeof tempReturn != "undefined") return tempReturn;
  2966. }
  2967. }
  2968. else {
  2969. const toWalk = walkable == null ? Object.keys(tree) : walkable;
  2970. for (const key of toWalk) {
  2971. if (!tree.hasOwnProperty(key) || ignore.includes(key)) continue;
  2972. tempReturn = this.findInTree(tree[key], searchFilter, {walkable, ignore});
  2973. if (typeof tempReturn != "undefined") return tempReturn;
  2974. }
  2975. }
  2976. return tempReturn;
  2977. }
  2978. /**
  2979. * Gets a nested property (if it exists) safely. Path should be something like `prop.prop2.prop3`.
  2980. * Numbers can be used for arrays as well like `prop.prop2.array.0.id`.
  2981. * @param {Object} obj - object to get nested property of
  2982. * @param {string} path - representation of the property to obtain
  2983. */
  2984. static getNestedProp(obj, path) {
  2985. return path.split(".").reduce(function(ob, prop) {
  2986. return ob && ob[prop];
  2987. }, obj);
  2988. }
  2989. /**
  2990. * Builds a classname string from any number of arguments. This includes arrays and objects.
  2991. * When given an array all values from the array are added to the list.
  2992. * When given an object they keys are added as the classnames if the value is truthy.
  2993. * Copyright (c) 2018 Jed Watson https://github.com/JedWatson/classnames MIT License
  2994. * @param {...Any} argument - anything that should be used to add classnames.
  2995. */
  2996. static className() {
  2997. const classes = [];
  2998. const hasOwn = {}.hasOwnProperty;
  2999. for (let i = 0; i < arguments.length; i++) {
  3000. const arg = arguments[i];
  3001. if (!arg) continue;
  3002. const argType = typeof arg;
  3003. if (argType === "string" || argType === "number") {
  3004. classes.push(arg);
  3005. }
  3006. else if (Array.isArray(arg) && arg.length) {
  3007. const inner = this.classNames.apply(null, arg);
  3008. if (inner) {
  3009. classes.push(inner);
  3010. }
  3011. }
  3012. else if (argType === "object") {
  3013. for (const key in arg) {
  3014. if (hasOwn.call(arg, key) && arg[key]) {
  3015. classes.push(key);
  3016. }
  3017. }
  3018. }
  3019. }
  3020. return classes.join(" ");
  3021. }
  3022. /**
  3023. * Safely adds to the prototype of an existing object by checking if the
  3024. * property exists on the prototype.
  3025. * @param {object} object - Object whose prototype to extend
  3026. * @param {string} prop - Name of the prototype property to add
  3027. * @param {callable} func - Function to run
  3028. */
  3029. static addToPrototype(object, prop, func) {
  3030. if (!object.prototype) return;
  3031. if (object.prototype[prop]) return;
  3032. return object.prototype[prop] = func;
  3033. }
  3034. /**
  3035. * Deep extends an object with a set of other objects. Objects later in the list
  3036. * of `extenders` have priority, that is to say if one sets a key to be a primitive,
  3037. * it will be overwritten with the next one with the same key. If it is an object,
  3038. * and the keys match, the object is extended. This happens recursively.
  3039. * @param {object} extendee - Object to be extended
  3040. * @param {...object} extenders - Objects to extend with
  3041. * @returns {object} - A reference to `extendee`
  3042. */
  3043. static extend(extendee, ...extenders) {
  3044. for (let i = 0; i < extenders.length; i++) {
  3045. for (const key in extenders[i]) {
  3046. if (extenders[i].hasOwnProperty(key)) {
  3047. if (Array.isArray(extendee[key]) && Array.isArray(extenders[i][key])) this.extend(extendee[key], extenders[i][key]);
  3048. else if (typeof extendee[key] === "object" && typeof extenders[i][key] === "object") this.extend(extendee[key], extenders[i][key]);
  3049. else if (Array.isArray(extenders[i][key])) extendee[key] = [], this.extend(extendee[key], extenders[i][key]); // eslint-disable-line no-sequences
  3050. else if (typeof extenders[i][key] === "object") extendee[key] = {}, this.extend(extendee[key], extenders[i][key]); // eslint-disable-line no-sequences
  3051. else extendee[key] = extenders[i][key];
  3052. }
  3053. }
  3054. }
  3055. return extendee;
  3056. }
  3057. /* Code below comes from our work on BDv2:
  3058. * https://github.com/JsSucks/BetterDiscordApp/blob/master/common/modules/utils.js
  3059. */
  3060. /**
  3061. * Clones an object and all it's properties.
  3062. * @param {Any} value The value to clone
  3063. * @return {Any} The cloned value
  3064. */
  3065. static deepclone(value) {
  3066. if (typeof value === "object") {
  3067. if (Array.isArray(value)) return value.map(i => this.deepclone(i));
  3068. const clone = Object.assign({}, value);
  3069. for (const key in clone) {
  3070. clone[key] = this.deepclone(clone[key]);
  3071. }
  3072. return clone;
  3073. }
  3074. return value;
  3075. }
  3076. /**
  3077. * Freezes an object and all it's properties.
  3078. * @param {Any} object The object to freeze
  3079. * @param {Function} exclude A function to filter object that shouldn't be frozen
  3080. */
  3081. static deepfreeze(object, exclude) {
  3082. if (exclude && exclude(object)) return;
  3083. if (typeof object === "object" && object !== null) {
  3084. const properties = Object.getOwnPropertyNames(object);
  3085. for (const property of properties) {
  3086. this.deepfreeze(object[property], exclude);
  3087. }
  3088. Object.freeze(object);
  3089. }
  3090. return object;
  3091. }
  3092. /**
  3093. * Removes an item from an array. This differs from Array.prototype.filter as it mutates the original array instead of creating a new one.
  3094. * @param {Array} array The array to filter
  3095. * @param {Any} item The item to remove from the array
  3096. * @return {Array}
  3097. */
  3098. static removeFromArray(array, item, filter) {
  3099. let index;
  3100. while ((index = filter ? array.findIndex(item) : array.indexOf(item)) > -1) array.splice(index, 1);
  3101. return array;
  3102. }
  3103. /**
  3104. * Returns a function, that, as long as it continues to be invoked, will not
  3105. * be triggered. The function will be called after it stops being called for
  3106. * N milliseconds.
  3107. *
  3108. * Adapted from the version by David Walsh (https://davidwalsh.name/javascript-debounce-function)
  3109. *
  3110. * @param {function} executor
  3111. * @param {number} delay
  3112. */
  3113. static debounce(executor, delay) {
  3114. let timeout;
  3115. return function(...args) {
  3116. const callback = () => {
  3117. timeout = null;
  3118. Reflect.apply(executor, null, args);
  3119. };
  3120. clearTimeout(timeout);
  3121. timeout = setTimeout(callback, delay);
  3122. };
  3123. }
  3124. /**
  3125. * Checks if a file exists and is a file.
  3126. * @param {String} path The file's path
  3127. * @return {Promise}
  3128. * @deprecated 12/3/2020 Just use fs...
  3129. */
  3130. static async fileExists(path) {
  3131. const fs = require("fs");
  3132. return new Promise((resolve, reject) => {
  3133. fs.stat(path, (err, stats) => {
  3134. if (err) {
  3135. return reject({
  3136. message: `No such file or directory: ${err.path}`,
  3137. err
  3138. });
  3139. }
  3140. if (!stats.isFile()) {
  3141. return reject({
  3142. message: `Not a file: ${path}`,
  3143. stats
  3144. });
  3145. }
  3146. resolve();
  3147. });
  3148. });
  3149. }
  3150. /**
  3151. * Returns the contents of a file.
  3152. * @param {String} path The file's path
  3153. * @return {Promise}
  3154. * @deprecated 12/3/2020 Just use fs...
  3155. */
  3156. static async readFile(path) {
  3157. await this.fileExists(path);
  3158. const fs = require("fs");
  3159. return new Promise((resolve, reject) => {
  3160. fs.readFile(path, "utf-8", (err, data) => {
  3161. if (err) {
  3162. return reject({
  3163. message: `Could not read file: ${path}`,
  3164. err
  3165. });
  3166. }
  3167. resolve(data);
  3168. });
  3169. });
  3170. }
  3171. }
  3172. /***/ }),
  3173. /***/ "./src/modules/webpackmodules.js":
  3174. /*!***************************************!*\
  3175. !*** ./src/modules/webpackmodules.js ***!
  3176. \***************************************/
  3177. /*! exports provided: Filters, default */
  3178. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  3179. "use strict";
  3180. __webpack_require__.r(__webpack_exports__);
  3181. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Filters", function() { return Filters; });
  3182. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return WebpackModules; });
  3183. /* harmony import */ var _discordmodules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./discordmodules */ "./src/modules/discordmodules.js");
  3184. /**
  3185. * Random set of utilities that didn't fit elsewhere.
  3186. * @module WebpackModules
  3187. * @version 0.0.2
  3188. */
  3189. /**
  3190. * Checks if a given module matches a set of parameters.
  3191. * @callback module:WebpackModules.Filters~filter
  3192. * @param {*} module - module to check
  3193. * @returns {boolean} - True if the module matches the filter, false otherwise
  3194. */
  3195. /**
  3196. * Filters for use with {@link module:WebpackModules} but may prove useful elsewhere.
  3197. */
  3198. class Filters {
  3199. /**
  3200. * Generates a {@link module:WebpackModules.Filters~filter} that filters by a set of properties.
  3201. * @param {Array<string>} props - Array of property names
  3202. * @param {module:WebpackModules.Filters~filter} filter - Additional filter
  3203. * @returns {module:WebpackModules.Filters~filter} - A filter that checks for a set of properties
  3204. */
  3205. static byProperties(props, filter = m => m) {
  3206. return module => {
  3207. const component = filter(module);
  3208. if (!component) return false;
  3209. for (let p = 0; p < props.length; p++) {
  3210. if (module[props[p]] === undefined) return false;
  3211. }
  3212. return true;
  3213. };
  3214. }
  3215. /**
  3216. * Generates a {@link module:WebpackModules.Filters~filter} that filters by a set of properties on the object's prototype.
  3217. * @param {Array<string>} fields - Array of property names
  3218. * @param {module:WebpackModules.Filters~filter} filter - Additional filter
  3219. * @returns {module:WebpackModules.Filters~filter} - A filter that checks for a set of properties on the object's prototype
  3220. */
  3221. static byPrototypeFields(fields, filter = m => m) {
  3222. return module => {
  3223. const component = filter(module);
  3224. if (!component) return false;
  3225. if (!component.prototype) return false;
  3226. for (let f = 0; f < fields.length; f++) {
  3227. if (module.prototype[fields[f]] === undefined) return false;
  3228. }
  3229. return true;
  3230. };
  3231. }
  3232. /**
  3233. * Generates a {@link module:WebpackModules.Filters~filter} that filters by a regex.
  3234. * @param {RegExp} search - A RegExp to check on the module
  3235. * @param {module:WebpackModules.Filters~filter} filter - Additional filter
  3236. * @returns {module:WebpackModules.Filters~filter} - A filter that checks for a set of properties
  3237. */
  3238. static byCode(search, filter = m => m) {
  3239. return module => {
  3240. const method = filter(module);
  3241. if (!method) return false;
  3242. let methodString = "";
  3243. try {methodString = method.toString([]);}
  3244. catch (err) {methodString = method.toString();}
  3245. return methodString.search(search) !== -1;
  3246. };
  3247. }
  3248. /**
  3249. * Generates a {@link module:WebpackModules.Filters~filter} that filters by strings.
  3250. * @param {...String} search - A RegExp to check on the module
  3251. * @returns {module:WebpackModules.Filters~filter} - A filter that checks for a set of strings
  3252. */
  3253. static byString(...strings) {
  3254. return module => {
  3255. let moduleString = "";
  3256. try {moduleString = module.toString([]);}
  3257. catch (err) {moduleString = module.toString();}
  3258. for (const s of strings) {
  3259. if (!moduleString.includes(s)) return false;
  3260. }
  3261. return true;
  3262. };
  3263. }
  3264. /**
  3265. * Generates a {@link module:WebpackModules.Filters~filter} that filters by a set of properties.
  3266. * @param {string} name - Name the module should have
  3267. * @param {module:WebpackModules.Filters~filter} filter - Additional filter
  3268. * @returns {module:WebpackModules.Filters~filter} - A filter that checks for a set of properties
  3269. */
  3270. static byDisplayName(name) {
  3271. return module => {
  3272. return module && module.displayName === name;
  3273. };
  3274. }
  3275. /**
  3276. * Generates a combined {@link module:WebpackModules.Filters~filter} from a list of filters.
  3277. * @param {...module:WebpackModules.Filters~filter} filters - A list of filters
  3278. * @returns {module:WebpackModules.Filters~filter} - Combinatory filter of all arguments
  3279. */
  3280. static combine(...filters) {
  3281. return module => {
  3282. return filters.every(filter => filter(module));
  3283. };
  3284. }
  3285. }
  3286. class WebpackModules {
  3287. static find(filter, first = true) {return this.getModule(filter, first);}
  3288. static findAll(filter) {return this.getModule(filter, false);}
  3289. static findByUniqueProperties(props, first = true) {return first ? this.getByProps(...props) : this.getAllByProps(...props);}
  3290. static findByDisplayName(name) {return this.getByDisplayName(name);}
  3291. /**
  3292. * Finds a module using a filter function.
  3293. * @param {Function} filter A function to use to filter modules
  3294. * @param {Boolean} first Whether to return only the first matching module
  3295. * @return {Any}
  3296. */
  3297. static getModule(filter, first = true) {
  3298. const wrappedFilter = (m) => {
  3299. try {return filter(m);}
  3300. catch (err) {return false;}
  3301. };
  3302. const modules = this.getAllModules();
  3303. const rm = [];
  3304. for (const index in modules) {
  3305. if (!modules.hasOwnProperty(index)) continue;
  3306. const module = modules[index];
  3307. const {exports} = module;
  3308. let foundModule = null;
  3309. if (!exports) continue;
  3310. if (exports.__esModule && exports.default && wrappedFilter(exports.default)) foundModule = exports.default;
  3311. if (wrappedFilter(exports)) foundModule = exports;
  3312. if (!foundModule) continue;
  3313. if (first) return foundModule;
  3314. rm.push(foundModule);
  3315. }
  3316. return first || rm.length == 0 ? undefined : rm;
  3317. }
  3318. /**
  3319. * Gets the index in the webpack require cache of a specific
  3320. * module using a filter.
  3321. * @param {Function} filter A function to use to filter modules
  3322. * @return {Number|null}
  3323. */
  3324. static getIndex(filter) {
  3325. const wrappedFilter = (m) => {
  3326. try {return filter(m);}
  3327. catch (err) {return false;}
  3328. };
  3329. const modules = this.getAllModules();
  3330. for (const index in modules) {
  3331. if (!modules.hasOwnProperty(index)) continue;
  3332. const module = modules[index];
  3333. const exports = module.exports;
  3334. let foundModule = null;
  3335. if (!exports) continue;
  3336. if (exports.__esModule && exports.default && wrappedFilter(exports.default)) foundModule = exports.default;
  3337. if (wrappedFilter(exports)) foundModule = exports;
  3338. if (!foundModule) continue;
  3339. return index;
  3340. }
  3341. return null;
  3342. }
  3343. /**
  3344. * Gets the index in the webpack require cache of a specific
  3345. * module that was already found.
  3346. * @param {Any} module An already acquired module
  3347. * @return {Number|null}
  3348. */
  3349. static getIndexByModule(module) {
  3350. return this.getIndex(m => m == module);
  3351. }
  3352. /**
  3353. * Finds all modules matching a filter function.
  3354. * @param {Function} filter A function to use to filter modules
  3355. */
  3356. static getModules(filter) {return this.getModule(filter, false);}
  3357. /**
  3358. * Finds a module by its name.
  3359. * @param {String} name The name of the module
  3360. * @param {Function} fallback A function to use to filter modules if not finding a known module
  3361. * @return {Any}
  3362. */
  3363. static getModuleByName(name, fallback) {
  3364. if (_discordmodules__WEBPACK_IMPORTED_MODULE_0__["default"].hasOwnProperty(name)) return _discordmodules__WEBPACK_IMPORTED_MODULE_0__["default"][name];
  3365. if (!fallback) return undefined;
  3366. const module = this.getModule(fallback, true);
  3367. return module ? _discordmodules__WEBPACK_IMPORTED_MODULE_0__["default"][name] = module : undefined;
  3368. }
  3369. /**
  3370. * Finds a module by its display name.
  3371. * @param {String} name The display name of the module
  3372. * @return {Any}
  3373. */
  3374. static getByDisplayName(name) {
  3375. return this.getModule(Filters.byDisplayName(name), true);
  3376. }
  3377. /**
  3378. * Finds a module using its code.
  3379. * @param {RegEx} regex A regular expression to use to filter modules
  3380. * @param {Boolean} first Whether to return the only the first matching module
  3381. * @return {Any}
  3382. */
  3383. static getByRegex(regex, first = true) {
  3384. return this.getModule(Filters.byCode(regex), first);
  3385. }
  3386. /**
  3387. * Finds a single module using properties on its prototype.
  3388. * @param {...string} prototypes Properties to use to filter modules
  3389. * @return {Any}
  3390. */
  3391. static getByPrototypes(...prototypes) {
  3392. return this.getModule(Filters.byPrototypeFields(prototypes), true);
  3393. }
  3394. /**
  3395. * Finds all modules with a set of properties of its prototype.
  3396. * @param {...string} prototypes Properties to use to filter modules
  3397. * @return {Any}
  3398. */
  3399. static getAllByPrototypes(...prototypes) {
  3400. return this.getModule(Filters.byPrototypeFields(prototypes), false);
  3401. }
  3402. /**
  3403. * Finds a single module using its own properties.
  3404. * @param {...string} props Properties to use to filter modules
  3405. * @return {Any}
  3406. */
  3407. static getByProps(...props) {
  3408. return this.getModule(Filters.byProperties(props), true);
  3409. }
  3410. /**
  3411. * Finds all modules with a set of properties.
  3412. * @param {...string} props Properties to use to filter modules
  3413. * @return {Any}
  3414. */
  3415. static getAllByProps(...props) {
  3416. return this.getModule(Filters.byProperties(props), false);
  3417. }
  3418. /**
  3419. * Finds a single module using a set of strings.
  3420. * @param {...String} props Strings to use to filter modules
  3421. * @return {Any}
  3422. */
  3423. static getByString(...strings) {
  3424. return this.getModule(Filters.byString(...strings), true);
  3425. }
  3426. /**
  3427. * Finds all modules with a set of strings.
  3428. * @param {...String} strings Strings to use to filter modules
  3429. * @return {Any}
  3430. */
  3431. static getAllByString(...strings) {
  3432. return this.getModule(Filters.byString(...strings), false);
  3433. }
  3434. /**
  3435. * Gets a specific module by index of the webpack require cache.
  3436. * Best used in combination with getIndex in order to patch a
  3437. * specific function.
  3438. *
  3439. * Note: this gives the **raw** module, meaning the actual module
  3440. * is in returnValue.exports. This is done in order to be able
  3441. * to patch modules which export a single function directly.
  3442. * @param {Number} index Index into the webpack require cache
  3443. * @return {Any}
  3444. */
  3445. static getByIndex(index) {
  3446. return WebpackModules.require.c[index].exports;
  3447. }
  3448. /**
  3449. * Discord's __webpack_require__ function.
  3450. */
  3451. static get require() {
  3452. if (this._require) return this._require;
  3453. const id = "zl-webpackmodules";
  3454. const __webpack_require__ = window.webpackJsonp.push([[], {
  3455. [id]: (module, exports, req) => module.exports = req
  3456. }, [[id]]]);
  3457. delete __webpack_require__.m[id];
  3458. delete __webpack_require__.c[id];
  3459. return this._require = __webpack_require__;
  3460. }
  3461. /**
  3462. * Returns all loaded modules.
  3463. * @return {Array}
  3464. */
  3465. static getAllModules() {
  3466. return this.require.c;
  3467. }
  3468. }
  3469. /***/ }),
  3470. /***/ "./src/plugin.js":
  3471. /*!***********************!*\
  3472. !*** ./src/plugin.js ***!
  3473. \***********************/
  3474. /*! exports provided: default */
  3475. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  3476. "use strict";
  3477. __webpack_require__.r(__webpack_exports__);
  3478. /* harmony default export */ __webpack_exports__["default"] = ((BasePlugin, Library) => {
  3479. const {PluginUpdater, Patcher, Logger, Settings, Toasts, PluginUtilities, ReactComponents, DCM} = Library;
  3480. const PluginLibrary = class PluginLibrary extends BasePlugin {
  3481. get Library() {return Library;}
  3482. load() {
  3483. super.load();
  3484. const wasLibLoaded = !!document.getElementById("ZLibraryCSS");
  3485. const isBBDLoading = document.getElementsByClassName("bd-loaderv2").length;
  3486. PluginUtilities.removeStyle("ZLibraryCSS");
  3487. PluginUtilities.addStyle("ZLibraryCSS", Settings.CSS + Toasts.CSS + PluginUpdater.CSS);
  3488. ReactComponents.AutoPatcher.processAll();
  3489. ReactComponents.AutoPatcher.autoPatch();
  3490. DCM.patchComponents();
  3491. /**
  3492. * Checking if this is the library first being loaded during init
  3493. * This means that subsequent loads will cause dependents to reload
  3494. * This also means first load when installing for the first time
  3495. * will automatically reload the dependent plugins. This is needed
  3496. * for those plugins that prompt to download and install the lib.
  3497. */
  3498. if (!wasLibLoaded && isBBDLoading) return; // If the this is the lib's first load AND this is BD's initialization
  3499. /**
  3500. * Now we can go ahead and reload any dependent plugins by checking
  3501. * for any with instance._config. Both plugins using buildPlugin()
  3502. * and plugin skeletons that prompt for download should have this
  3503. * instance property.
  3504. */
  3505. // development vs master
  3506. const id = BdApi.version ? ["settings", "general", "showToasts"] : ["fork-ps-2"];
  3507. const wasEnabled = BdApi.isSettingEnabled(...id);
  3508. if (wasEnabled) BdApi.disableSetting(...id);
  3509. this._reloadPlugins();
  3510. if (wasEnabled) BdApi.enableSetting(...id);
  3511. }
  3512. _reloadPlugins() {
  3513. const list = BdApi.Plugins.getAll().reduce((acc, val) => {
  3514. if (!val._config) return acc;
  3515. const name = val.getName();
  3516. if (name === "ZeresPluginLibrary") return acc;
  3517. acc.push(name);
  3518. return acc;
  3519. }, []);
  3520. for (let p = 0; p < list.length; p++) BdApi.Plugins.reload(list[p]);
  3521. }
  3522. static buildPlugin(config) {
  3523. const name = config.info.name;
  3524. const BoundAPI = {
  3525. Logger: {
  3526. stacktrace: (message, error) => Logger.stacktrace(name, message, error),
  3527. log: (...message) => Logger.log(name, ...message),
  3528. error: (...message) => Logger.err(name, ...message),
  3529. err: (...message) => Logger.err(name, ...message),
  3530. warn: (...message) => Logger.warn(name, ...message),
  3531. info: (...message) => Logger.info(name, ...message),
  3532. debug: (...message) => Logger.debug(name, ...message)
  3533. },
  3534. Patcher: {
  3535. getPatchesByCaller: () => {return Patcher.getPatchesByCaller(name);},
  3536. unpatchAll: () => {return Patcher.unpatchAll(name);},
  3537. before: (moduleToPatch, functionName, callback, options = {}) => {return Patcher.before(name, moduleToPatch, functionName, callback, options);},
  3538. instead: (moduleToPatch, functionName, callback, options = {}) => {return Patcher.instead(name, moduleToPatch, functionName, callback, options);},
  3539. after: (moduleToPatch, functionName, callback, options = {}) => {return Patcher.after(name, moduleToPatch, functionName, callback, options);}
  3540. }
  3541. };
  3542. const BoundLib = Object.assign({}, Library);
  3543. BoundLib.Logger = BoundAPI.Logger;
  3544. BoundLib.Patcher = BoundAPI.Patcher;
  3545. return [Library.Structs.Plugin(config), BoundLib]; // eslint-disable-line new-cap
  3546. }
  3547. };
  3548. Object.assign(PluginLibrary, Library);
  3549. Library.buildPlugin = PluginLibrary.buildPlugin;
  3550. window.ZLibrary = Library;
  3551. window.ZLibraryPromise = new Promise(r => setImmediate(r));
  3552. window.ZeresPluginLibrary = PluginLibrary;
  3553. return PluginLibrary;
  3554. });
  3555. /***/ }),
  3556. /***/ "./src/structs/discord/channel.js":
  3557. /*!****************************************!*\
  3558. !*** ./src/structs/discord/channel.js ***!
  3559. \****************************************/
  3560. /*! exports provided: Channel, PermissionOverwrite, RolePermissionOverwrite, MemberPermissionOverwrite, GuildChannel, GuildTextChannel, GuildVoiceChannel, ChannelCategory, PrivateChannel, DirectMessageChannel, GroupChannel */
  3561. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  3562. "use strict";
  3563. __webpack_require__.r(__webpack_exports__);
  3564. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Channel", function() { return Channel; });
  3565. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PermissionOverwrite", function() { return PermissionOverwrite; });
  3566. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RolePermissionOverwrite", function() { return RolePermissionOverwrite; });
  3567. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MemberPermissionOverwrite", function() { return MemberPermissionOverwrite; });
  3568. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GuildChannel", function() { return GuildChannel; });
  3569. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GuildTextChannel", function() { return GuildTextChannel; });
  3570. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GuildVoiceChannel", function() { return GuildVoiceChannel; });
  3571. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ChannelCategory", function() { return ChannelCategory; });
  3572. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PrivateChannel", function() { return PrivateChannel; });
  3573. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DirectMessageChannel", function() { return DirectMessageChannel; });
  3574. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GroupChannel", function() { return GroupChannel; });
  3575. /* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
  3576. /* harmony import */ var structs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! structs */ "./src/structs/structs.js");
  3577. /* harmony import */ var _guild__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./guild */ "./src/structs/discord/guild.js");
  3578. /* harmony import */ var _message__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./message */ "./src/structs/discord/message.js");
  3579. /* harmony import */ var _user__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./user */ "./src/structs/discord/user.js");
  3580. /**
  3581. * BetterDiscord Channel Struct
  3582. * Copyright (c) 2018-present JsSucks
  3583. * All rights reserved.
  3584. *
  3585. * This source code is licensed under the MIT license found at
  3586. * https://github.com/JsSucks/BetterDiscordApp/blob/master/LICENSE
  3587. */
  3588. const BotMessager = modules__WEBPACK_IMPORTED_MODULE_0__["WebpackModules"].getByProps("createBotMessage");
  3589. const cache = new WeakMap();
  3590. /**
  3591. * @memberof module:DiscordAPI
  3592. */
  3593. class Channel {
  3594. constructor(data) {
  3595. if (cache.has(data)) return cache.get(data);
  3596. cache.set(data, this);
  3597. this.discordObject = data;
  3598. }
  3599. static from(channel) {
  3600. switch (channel.type) {
  3601. default: return new Channel(channel);
  3602. case 0: return new GuildTextChannel(channel);
  3603. case 1: return new DirectMessageChannel(channel);
  3604. case 2: return new GuildVoiceChannel(channel);
  3605. case 3: return new GroupChannel(channel);
  3606. case 4: return new ChannelCategory(channel);
  3607. }
  3608. }
  3609. static fromId(id) {
  3610. const channel = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].ChannelStore.getChannel(id);
  3611. if (channel) return Channel.from(channel);
  3612. }
  3613. static get GuildChannel() {return GuildChannel;}
  3614. static get GuildTextChannel() {return GuildTextChannel;}
  3615. static get GuildVoiceChannel() {return GuildVoiceChannel;}
  3616. static get ChannelCategory() {return ChannelCategory;}
  3617. static get PrivateChannel() {return PrivateChannel;}
  3618. static get DirectMessageChannel() {return DirectMessageChannel;}
  3619. static get GroupChannel() {return GroupChannel;}
  3620. get id() {return this.discordObject.id;}
  3621. get applicationId() {return this.discordObject.application_id;}
  3622. get type() {return this.discordObject.type;}
  3623. get name() {return this.discordObject.name;}
  3624. /**
  3625. * Send a message in this channel.
  3626. * @param {String|object} content The new message's content
  3627. * @param {Boolean} parse Whether to parse the message or send it as it is
  3628. * @return {Promise<Message>}
  3629. */
  3630. async sendMessage(content, parse = false) {
  3631. if (this.assertPermissions) this.assertPermissions("SEND_MESSAGES", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.VIEW_CHANNEL | modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.SEND_MESSAGES);
  3632. this.select();
  3633. if (parse) content = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageParser.parse(this.discordObject, content);
  3634. else if (typeof content == "string") content = {content, validNonShortcutEmojis: Array(0)};
  3635. const response = await modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageActions._sendMessage(this.id, content, {});
  3636. return _message__WEBPACK_IMPORTED_MODULE_3__["Message"].from(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageStore.getMessage(this.id, response.body.id));
  3637. }
  3638. /**
  3639. * Send a bot message in this channel that only the current user can see.
  3640. * @param {String} content The new message's content
  3641. * @return {Message}
  3642. */
  3643. sendBotMessage(content) {
  3644. this.select();
  3645. if (!BotMessager) return modules__WEBPACK_IMPORTED_MODULE_0__["Logger"].err("DiscordAPI", "Unable to create bot message");
  3646. const message = BotMessager.createBotMessage(this.id, content);
  3647. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageActions.receiveMessage(this.id, message);
  3648. return _message__WEBPACK_IMPORTED_MODULE_3__["Message"].from(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageStore.getMessage(this.id, message.id));
  3649. }
  3650. /**
  3651. * A list of messages in this channel.
  3652. */
  3653. get messages() {
  3654. const messages = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageStore.getMessages(this.id).toArray();
  3655. return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(messages, m => _message__WEBPACK_IMPORTED_MODULE_3__["Message"].from(m));
  3656. }
  3657. /**
  3658. * Jumps to the latest message in this channel.
  3659. */
  3660. jumpToPresent() {
  3661. if (this.assertPermissions) this.assertPermissions("VIEW_CHANNEL", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.VIEW_CHANNEL);
  3662. if (this.hasMoreAfter) modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageActions.jumpToPresent(this.id, modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordConstants.MAX_MESSAGES_PER_CHANNEL);
  3663. else this.messages[this.messages.length - 1].jumpTo(false);
  3664. }
  3665. get hasMoreAfter() {
  3666. return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageStore.getMessages(this.id).hasMoreAfter;
  3667. }
  3668. /**
  3669. * Sends an invite in this channel.
  3670. * @param {String} code The invite code
  3671. * @return {Promise<Message>}
  3672. */
  3673. async sendInvite(code) {
  3674. if (this.assertPermissions) this.assertPermissions("SEND_MESSAGES", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.VIEW_CHANNEL | modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.SEND_MESSAGES);
  3675. const response = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageActions.sendInvite(this.id, code);
  3676. return _message__WEBPACK_IMPORTED_MODULE_3__["Message"].from(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageStore.getMessage(this.id, response.body.id));
  3677. }
  3678. /**
  3679. * Opens this channel in the UI.
  3680. */
  3681. select() {
  3682. if (this.assertPermissions) this.assertPermissions("VIEW_CHANNEL", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.VIEW_CHANNEL);
  3683. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].NavigationUtils.transitionToGuild(this.guildId ? this.guildId : modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordConstants.ME, this.id);
  3684. }
  3685. /**
  3686. * Whether this channel is currently selected.
  3687. */
  3688. get isSelected() {
  3689. return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentChannel === this;
  3690. }
  3691. /**
  3692. * Updates this channel.
  3693. * @return {Promise}
  3694. */
  3695. async updateChannel(body) {
  3696. if (this.assertPermissions) this.assertPermissions("MANAGE_CHANNELS", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.MANAGE_CHANNELS);
  3697. await modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].APIModule.patch({
  3698. url: `${modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordConstants.Endpoints.CHANNELS}/${this.id}`,
  3699. body
  3700. });
  3701. this.discordObject = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].ChannelStore.getChannel(this.id);
  3702. cache.set(this.discordObject, this);
  3703. }
  3704. }
  3705. class PermissionOverwrite {
  3706. constructor(data, channel_id) {
  3707. this.discordObject = data;
  3708. this.channelId = channel_id;
  3709. }
  3710. static from(data, channel_id) {
  3711. switch (data.type) {
  3712. default: return new PermissionOverwrite(data, channel_id);
  3713. case "role": return new RolePermissionOverwrite(data, channel_id);
  3714. case "member": return new MemberPermissionOverwrite(data, channel_id);
  3715. }
  3716. }
  3717. static get RolePermissionOverwrite() {return RolePermissionOverwrite;}
  3718. static get MemberPermissionOverwrite() {return MemberPermissionOverwrite;}
  3719. get type() {return this.discordObject.type;}
  3720. get allow() {return this.discordObject.allow;}
  3721. get deny() {return this.discordObject.deny;}
  3722. get channel() {
  3723. return Channel.fromId(this.channelId);
  3724. }
  3725. get guild() {
  3726. if (this.channel) return this.channel.guild;
  3727. return null;
  3728. }
  3729. }
  3730. class RolePermissionOverwrite extends PermissionOverwrite {
  3731. get roleId() {return this.discordObject.id;}
  3732. get role() {
  3733. if (this.guild) return this.guild.roles.find(r => r.id === this.roleId);
  3734. return null;
  3735. }
  3736. }
  3737. class MemberPermissionOverwrite extends PermissionOverwrite {
  3738. get memberId() {return this.discordObject.id;}
  3739. get member() {
  3740. return _user__WEBPACK_IMPORTED_MODULE_4__["GuildMember"].fromId(this.memberId);
  3741. }
  3742. }
  3743. class GuildChannel extends Channel {
  3744. static get PermissionOverwrite() {return PermissionOverwrite;}
  3745. get guildId() {return this.discordObject.guild_id;}
  3746. get parentId() {return this.discordObject.parent_id;} // Channel category
  3747. get position() {return this.discordObject.position;}
  3748. get nicks() {return this.discordObject.nicks;}
  3749. checkPermissions(perms) {
  3750. return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].Permissions.can({data: BigInt(perms)}, modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentUser, this.discordObject);
  3751. }
  3752. assertPermissions(name, perms) {
  3753. if (!this.checkPermissions(perms)) throw new structs__WEBPACK_IMPORTED_MODULE_1__["InsufficientPermissions"](name);
  3754. }
  3755. get category() {
  3756. return Channel.fromId(this.parentId);
  3757. }
  3758. /**
  3759. * The current user's permissions on this channel.
  3760. */
  3761. get permissions() {
  3762. return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildPermissions.getChannelPermissions(this.id);
  3763. }
  3764. get permissionOverwrites() {
  3765. return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(Object.values(this.discordObject.permissionOverwrites), p => PermissionOverwrite.from(p, this.id));
  3766. }
  3767. get guild() {
  3768. return _guild__WEBPACK_IMPORTED_MODULE_2__["Guild"].fromId(this.guildId);
  3769. }
  3770. /**
  3771. * Whether this channel is the guild's default channel.
  3772. */
  3773. get isDefaultChannel() {
  3774. return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildChannelsStore.getDefaultChannel(this.guildId).id === this.id;
  3775. }
  3776. /**
  3777. * Opens this channel's settings window.
  3778. * @param {String} section The section to open (see DiscordConstants.ChannelSettingsSections)
  3779. */
  3780. openSettings(section = "OVERVIEW") {
  3781. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].ChannelSettingsWindow.setSection(section);
  3782. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].ChannelSettingsWindow.open(this.id);
  3783. }
  3784. /**
  3785. * Updates this channel's name.
  3786. * @param {String} name The channel's new name
  3787. * @return {Promise}
  3788. */
  3789. updateName(name) {
  3790. return this.updateChannel({name});
  3791. }
  3792. /**
  3793. * Changes the channel's position.
  3794. * @param {Number} position The channel's new position
  3795. * @return {Promise}
  3796. */
  3797. changeSortLocation(position = 0) {
  3798. if (position instanceof GuildChannel) position = position.position;
  3799. return this.updateChannel({position});
  3800. }
  3801. /**
  3802. * Updates this channel's permission overwrites.
  3803. * @param {Array} permission_overwrites An array of permission overwrites
  3804. * @return {Promise}
  3805. */
  3806. updatePermissionOverwrites(permission_overwrites) {
  3807. return this.updateChannel({permission_overwrites});
  3808. }
  3809. /**
  3810. * Updates this channel's category.
  3811. * @param {ChannelCategory} category The new channel category
  3812. * @return {Promise}
  3813. */
  3814. updateCategory(category) {
  3815. return this.updateChannel({parent_id: category.id || category});
  3816. }
  3817. }
  3818. // Type 0 - GUILD_TEXT
  3819. class GuildTextChannel extends GuildChannel {
  3820. get type() {return "GUILD_TEXT";}
  3821. get topic() {return this.discordObject.topic;}
  3822. get nsfw() {return this.discordObject.nsfw;}
  3823. /**
  3824. * Updates this channel's topic.
  3825. * @param {String} topic The new channel topic
  3826. * @return {Promise}
  3827. */
  3828. updateTopic(topic) {
  3829. return this.updateChannel({topic});
  3830. }
  3831. /**
  3832. * Updates this channel's NSFW flag.
  3833. * @param {Boolean} nsfw Whether the channel should be marked as NSFW
  3834. * @return {Promise}
  3835. */
  3836. setNsfw(nsfw = true) {
  3837. return this.updateChannel({nsfw});
  3838. }
  3839. setNotNsfw() {
  3840. return this.setNswf(false);
  3841. }
  3842. }
  3843. // Type 2 - GUILD_VOICE
  3844. class GuildVoiceChannel extends GuildChannel {
  3845. get type() {return "GUILD_VOICE";}
  3846. get userLimit() {return this.discordObject.userLimit;}
  3847. get bitrate() {return this.discordObject.bitrate;}
  3848. sendMessage() {throw new Error("Cannot send messages in a voice channel.");}
  3849. get messages() {return new structs__WEBPACK_IMPORTED_MODULE_1__["List"]();}
  3850. jumpToPresent() {throw new Error("Cannot select a voice channel.");}
  3851. get hasMoreAfter() {return false;}
  3852. sendInvite() {throw new Error("Cannot invite someone to a voice channel.");}
  3853. select() {throw new Error("Cannot select a voice channel.");}
  3854. /**
  3855. * Updates this channel's bitrate.
  3856. * @param {Number} bitrate The new bitrate
  3857. * @return {Promise}
  3858. */
  3859. updateBitrate(bitrate) {
  3860. return this.updateChannel({bitrate});
  3861. }
  3862. /**
  3863. * Updates this channel's user limit.
  3864. * @param {Number} user_limit The new user limit
  3865. * @return {Promise}
  3866. */
  3867. updateUserLimit(user_limit) {
  3868. return this.updateChannel({user_limit});
  3869. }
  3870. }
  3871. // Type 4 - GUILD_CATEGORY
  3872. class ChannelCategory extends GuildChannel {
  3873. get type() {return "GUILD_CATEGORY";}
  3874. get parentId() {return undefined;}
  3875. get category() {return undefined;}
  3876. sendMessage() {throw new Error("Cannot send messages in a channel category.");}
  3877. get messages() {return new structs__WEBPACK_IMPORTED_MODULE_1__["List"]();}
  3878. jumpToPresent() {throw new Error("Cannot select a channel category.");}
  3879. get hasMoreAfter() {return false;}
  3880. sendInvite() {throw new Error("Cannot invite someone to a channel category.");}
  3881. select() {throw new Error("Cannot select a channel category.");}
  3882. updateCategory() {throw new Error("Cannot set a channel category on another channel category.");}
  3883. /**
  3884. * A list of channels in this category.
  3885. */
  3886. get channels() {
  3887. return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(this.guild.channels, c => c.parentId === this.id);
  3888. }
  3889. /**
  3890. * Opens the create channel modal for this guild.
  3891. * @param {Number} type The type of channel to create - either 0 (text), 2 (voice) or 4 (category)
  3892. * @param {GuildChannel} clone A channel to clone permissions of
  3893. */
  3894. openCreateChannelModal(type, category, clone) {
  3895. this.guild.openCreateChannelModal(type, this.id, this, clone);
  3896. }
  3897. /**
  3898. * Creates a channel in this category.
  3899. * @param {Number} type The type of channel to create - either 0 (text) or 2 (voice)
  3900. * @param {String} name A name for the new channel
  3901. * @param {Array} permission_overwrites An array of PermissionOverwrite-like objects - leave to use the permissions of the category
  3902. * @return {Promise<GuildChannel>}
  3903. */
  3904. createChannel(type, name, permission_overwrites) {
  3905. return this.guild.createChannel(type, name, this, permission_overwrites);
  3906. }
  3907. }
  3908. class PrivateChannel extends Channel {
  3909. get userLimit() {return this.discordObject.userLimit;}
  3910. get bitrate() {return this.discordObject.bitrate;}
  3911. }
  3912. // Type 1 - DM
  3913. class DirectMessageChannel extends PrivateChannel {
  3914. get type() {return "DM";}
  3915. get recipientId() {return this.discordObject.recipients[0];}
  3916. /**
  3917. * The other user of this direct message channel.
  3918. */
  3919. get recipient() {
  3920. return _user__WEBPACK_IMPORTED_MODULE_4__["User"].fromId(this.recipientId);
  3921. }
  3922. }
  3923. // Type 3 - GROUP_DM
  3924. class GroupChannel extends PrivateChannel {
  3925. get ownerId() {return this.discordObject.ownerId;}
  3926. get type() {return "GROUP_DM";}
  3927. get name() {return this.discordObject.name;}
  3928. get icon() {return this.discordObject.icon;}
  3929. /**
  3930. * A list of the other members of this group direct message channel.
  3931. */
  3932. get members() {
  3933. return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(this.discordObject.recipients, id => _user__WEBPACK_IMPORTED_MODULE_4__["User"].fromId(id));
  3934. }
  3935. /**
  3936. * The owner of this group direct message channel. This is usually the person who created it.
  3937. */
  3938. get owner() {
  3939. return _user__WEBPACK_IMPORTED_MODULE_4__["User"].fromId(this.ownerId);
  3940. }
  3941. /**
  3942. * Updates this channel's name.
  3943. * @param {String} name The channel's new name
  3944. * @return {Promise}
  3945. */
  3946. updateName(name) {
  3947. return this.updateChannel({name});
  3948. }
  3949. }
  3950. // export {Channel, GuildChannel, ChannelCategory, GuildTextChannel, GuildVoiceChannel, PrivateChannel, DirectMessageChannel, GroupChannel};
  3951. // export {PermissionOverwrite, RolePermissionOverwrite, MemberPermissionOverwrite};
  3952. /***/ }),
  3953. /***/ "./src/structs/discord/guild.js":
  3954. /*!**************************************!*\
  3955. !*** ./src/structs/discord/guild.js ***!
  3956. \**************************************/
  3957. /*! exports provided: Role, Emoji, Guild */
  3958. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  3959. "use strict";
  3960. __webpack_require__.r(__webpack_exports__);
  3961. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Role", function() { return Role; });
  3962. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Emoji", function() { return Emoji; });
  3963. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Guild", function() { return Guild; });
  3964. /* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
  3965. /* harmony import */ var structs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! structs */ "./src/structs/structs.js");
  3966. /* harmony import */ var _channel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./channel */ "./src/structs/discord/channel.js");
  3967. /* harmony import */ var _user__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./user */ "./src/structs/discord/user.js");
  3968. /**
  3969. * BetterDiscord Guild Struct
  3970. * Copyright (c) 2018-present JsSucks
  3971. * All rights reserved.
  3972. *
  3973. * This source code is licensed under the MIT license found at
  3974. * https://github.com/JsSucks/BetterDiscordApp/blob/master/LICENSE
  3975. */
  3976. const roles = new WeakMap();
  3977. class Role {
  3978. constructor(data, guild_id) {
  3979. if (roles.has(data)) return roles.get(data);
  3980. roles.set(data, this);
  3981. this.discordObject = data;
  3982. this.guildId = guild_id;
  3983. }
  3984. get id() {return this.discordObject.id;}
  3985. get name() {return this.discordObject.name;}
  3986. get position() {return this.discordObject.position;}
  3987. get originalPosition() {return this.discordObject.originalPosition;}
  3988. get permissions() {return this.discordObject.permissions;}
  3989. get managed() {return this.discordObject.managed;}
  3990. get mentionable() {return this.discordObject.mentionable;}
  3991. get hoist() {return this.discordObject.hoist;}
  3992. get colour() {return this.discordObject.color;}
  3993. get colourString() {return this.discordObject.colorString;}
  3994. get guild() {
  3995. return Guild.fromId(this.guildId);
  3996. }
  3997. get members() {
  3998. return this.guild.members.filter(m => m.roles.includes(this));
  3999. }
  4000. }
  4001. const emojis = new WeakMap();
  4002. class Emoji {
  4003. constructor(data) {
  4004. if (emojis.has(data)) return emojis.get(data);
  4005. emojis.set(data, this);
  4006. this.discordObject = data;
  4007. }
  4008. get id() {return this.discordObject.id;}
  4009. get guildId() {return this.discordObject.guild_id;}
  4010. get name() {return this.discordObject.name;}
  4011. get managed() {return this.discordObject.managed;}
  4012. get animated() {return this.discordObject.animated;}
  4013. get allNamesString() {return this.discordObject.allNamesString;}
  4014. get requireColons() {return this.discordObject.require_colons;}
  4015. get url() {return this.discordObject.url;}
  4016. get roles() {return this.discordObject.roles;}
  4017. get guild() {
  4018. return Guild.fromId(this.guildId);
  4019. }
  4020. }
  4021. const guilds = new WeakMap();
  4022. /**
  4023. * @memberof module:DiscordAPI
  4024. */
  4025. class Guild {
  4026. constructor(data) {
  4027. if (guilds.has(data)) return guilds.get(data);
  4028. guilds.set(data, this);
  4029. this.discordObject = data;
  4030. }
  4031. static from(data) {
  4032. return new Guild(data);
  4033. }
  4034. static fromId(id) {
  4035. const guild = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildStore.getGuild(id);
  4036. if (guild) return Guild.from(guild);
  4037. }
  4038. static get Role() {return Role;}
  4039. static get Emoji() {return Emoji;}
  4040. get id() {return this.discordObject.id;}
  4041. get ownerId() {return this.discordObject.ownerId;}
  4042. get applicationId() {return this.discordObject.application_id;}
  4043. get systemChannelId() {return this.discordObject.systemChannelId;}
  4044. get name() {return this.discordObject.name;}
  4045. get acronym() {return this.discordObject.acronym;}
  4046. get icon() {return this.discordObject.icon;}
  4047. get joinedAt() {return this.discordObject.joinedAt;}
  4048. get verificationLevel() {return this.discordObject.verificationLevel;}
  4049. get mfaLevel() {return this.discordObject.mfaLevel;}
  4050. get large() {return this.discordObject.large;}
  4051. get lazy() {return this.discordObject.lazy;}
  4052. get voiceRegion() {return this.discordObject.region;}
  4053. get afkChannelId() {return this.discordObject.afkChannelId;}
  4054. get afkTimeout() {return this.discordObject.afkTimeout;}
  4055. get explicitContentFilter() {return this.discordObject.explicitContentFilter;}
  4056. get defaultMessageNotifications() {return this.discordObject.defaultMessageNotifications;}
  4057. get splash() {return this.discordObject.splash;}
  4058. get features() {return this.discordObject.features;}
  4059. get owner() {
  4060. return this.members.find(m => m.userId === this.ownerId);
  4061. }
  4062. get roles() {
  4063. return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(Object.values(this.discordObject.roles), r => new Role(r, this.id))
  4064. .sort((r1, r2) => r1.position === r2.position ? 0 : r1.position > r2.position ? 1 : -1);
  4065. }
  4066. get channels() {
  4067. const channels = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildChannelsStore.getChannels(this.id);
  4068. const returnChannels = new structs__WEBPACK_IMPORTED_MODULE_1__["List"]();
  4069. for (const category in channels) {
  4070. if (channels.hasOwnProperty(category)) {
  4071. if (!Array.isArray(channels[category])) continue;
  4072. const channelList = channels[category];
  4073. for (const channel of channelList) {
  4074. // For some reason Discord adds a new category with the ID "null" and name "Uncategorized"
  4075. if (channel.channel.id === "null") continue;
  4076. returnChannels.push(_channel__WEBPACK_IMPORTED_MODULE_2__["Channel"].from(channel.channel));
  4077. }
  4078. }
  4079. }
  4080. return returnChannels;
  4081. }
  4082. /**
  4083. * Channels that don't have a parent. (Channel categories and any text/voice channel not in one.)
  4084. */
  4085. get mainChannels() {
  4086. return this.channels.filter(c => !c.parentId);
  4087. }
  4088. /**
  4089. * The guild's default channel. (Usually the first in the list.)
  4090. */
  4091. get defaultChannel() {
  4092. return _channel__WEBPACK_IMPORTED_MODULE_2__["Channel"].from(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildChannelsStore.getDefaultChannel(this.id));
  4093. }
  4094. /**
  4095. * The guild's AFK channel.
  4096. */
  4097. get afkChannel() {
  4098. if (this.afkChannelId) return _channel__WEBPACK_IMPORTED_MODULE_2__["Channel"].fromId(this.afkChannelId);
  4099. return null;
  4100. }
  4101. /**
  4102. * The channel system messages are sent to.
  4103. */
  4104. get systemChannel() {
  4105. if (this.systemChannelId) return _channel__WEBPACK_IMPORTED_MODULE_2__["Channel"].fromId(this.systemChannelId);
  4106. return null;
  4107. }
  4108. /**
  4109. * A list of GuildMember objects.
  4110. */
  4111. get members() {
  4112. const members = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildMemberStore.getMembers(this.id);
  4113. return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(members, m => new _user__WEBPACK_IMPORTED_MODULE_3__["GuildMember"](m, this.id));
  4114. }
  4115. /**
  4116. * The current user as a GuildMember of this guild.
  4117. */
  4118. get currentUser() {
  4119. return this.members.find(m => m.user === modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentUser);
  4120. }
  4121. /**
  4122. * The total number of members in the guild.
  4123. */
  4124. get memberCount() {
  4125. return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MemberCountStore.getMemberCount(this.id);
  4126. }
  4127. /**
  4128. * An array of the guild's custom emojis.
  4129. */
  4130. get emojis() {
  4131. return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].EmojiUtils.getGuildEmoji(this.id), e => new Emoji(e));
  4132. }
  4133. checkPermissions(perms) {
  4134. return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].Permissions.can(perms, modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentUser, this.discordObject);
  4135. }
  4136. assertPermissions(name, perms) {
  4137. if (!this.checkPermissions(perms)) throw new structs__WEBPACK_IMPORTED_MODULE_1__["InsufficientPermissions"](name);
  4138. }
  4139. /**
  4140. * The current user's permissions on this guild.
  4141. */
  4142. get permissions() {
  4143. return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildPermissions.getGuildPermissions(this.id);
  4144. }
  4145. getMember(id) {
  4146. const member = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildMemberStore.getMember(this.id, id);
  4147. if (member) return new _user__WEBPACK_IMPORTED_MODULE_3__["GuildMember"](member, this.id);
  4148. }
  4149. isMember(id) {
  4150. return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildMemberStore.isMember(this.id, id);
  4151. }
  4152. /**
  4153. * Whether the user has not restricted direct messages from members of this guild.
  4154. */
  4155. get allowPrivateMessages() {
  4156. return !modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].UserSettings.restrictedGuildIds.includes(this.id);
  4157. }
  4158. /**
  4159. * Marks all messages in the guild as read.
  4160. */
  4161. markAsRead() {
  4162. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildActions.markGuildAsRead(this.id);
  4163. }
  4164. /**
  4165. * Selects the guild in the UI.
  4166. */
  4167. select() {
  4168. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildActions.selectGuild(this.id);
  4169. }
  4170. /**
  4171. * Whether this guild is currently selected.
  4172. */
  4173. get isSelected() {
  4174. return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentGuild === this;
  4175. }
  4176. /**
  4177. * Opens this guild's settings window.
  4178. * @param {String} section The section to open (see DiscordConstants.GuildSettingsSections)
  4179. */
  4180. openSettings(section = "OVERVIEW") {
  4181. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildSettingsWindow.setSection(section);
  4182. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildSettingsWindow.open(this.id);
  4183. }
  4184. /**
  4185. * Kicks members who don't have any roles and haven't been seen in the number of days passed.
  4186. * @param {Number} days
  4187. */
  4188. pruneMembers(days) {
  4189. this.assertPermissions("KICK_MEMBERS", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.KICK_MEMBERS);
  4190. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].PruneMembersModal.prune(this.id, days);
  4191. }
  4192. openPruneMumbersModal() {
  4193. this.assertPermissions("KICK_MEMBERS", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.KICK_MEMBERS);
  4194. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].PruneMembersModal.open(this.id);
  4195. }
  4196. /**
  4197. * Opens the create channel modal for this guild.
  4198. * @param {Number} type The type of channel to create - either 0 (text), 2 (voice) or 4 (category)
  4199. * @param {ChannelCategory} category The category to create the channel in
  4200. * @param {GuildChannel} clone A channel to clone permissions, topic, bitrate and user limit of
  4201. */
  4202. openCreateChannelModal(type, category, clone) {
  4203. this.assertPermissions("MANAGE_CHANNELS", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.MANAGE_CHANNELS);
  4204. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].CreateChannelModal.open(type, this.id, category ? category.id : undefined, clone ? clone.id : undefined);
  4205. }
  4206. /**
  4207. * Creates a channel in this guild.
  4208. * @param {Number} type The type of channel to create - either 0 (text), 2 (voice) or 4 (category)
  4209. * @param {String} name A name for the new channel
  4210. * @param {ChannelCategory} category The category to create the channel in
  4211. * @param {Array} permission_overwrites An array of PermissionOverwrite-like objects - leave to use the permissions of the category
  4212. * @return {Promise<GuildChannel>}
  4213. */
  4214. async createChannel(type, name, category, permission_overwrites) {
  4215. this.assertPermissions("MANAGE_CHANNELS", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.MANAGE_CHANNELS);
  4216. const response = await modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].APIModule.post({
  4217. url: modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordConstants.Endpoints.GUILD_CHANNELS(this.id), // eslint-disable-line new-cap
  4218. body: {
  4219. type,
  4220. name,
  4221. parent_id: category ? category.id : undefined,
  4222. permission_overwrites: permission_overwrites ? permission_overwrites.map(p => ({
  4223. type: p.type,
  4224. id: (p.type === "user" ? p.userId : p.roleId) || p.id,
  4225. allow: p.allow,
  4226. deny: p.deny
  4227. })) : undefined
  4228. }
  4229. });
  4230. return _channel__WEBPACK_IMPORTED_MODULE_2__["Channel"].fromId(response.body.id);
  4231. }
  4232. openNotificationSettingsModal() {
  4233. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].NotificationSettingsModal.open(this.id);
  4234. }
  4235. openPrivacySettingsModal() {
  4236. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].PrivacySettingsModal.open(this.id);
  4237. }
  4238. nsfwAgree() {
  4239. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildActions.nsfwAgree(this.id);
  4240. }
  4241. nsfwDisagree() {
  4242. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildActions.nsfwDisagree(this.id);
  4243. }
  4244. /**
  4245. * Changes the guild's position in the list.
  4246. * @param {Number} index The new position
  4247. */
  4248. changeSortLocation(index) {
  4249. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildActions.move(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].guildPositions.indexOf(this.id), index);
  4250. }
  4251. /**
  4252. * Updates this guild.
  4253. * @return {Promise}
  4254. */
  4255. async updateGuild(body) {
  4256. this.assertPermissions("MANAGE_GUILD", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.MANAGE_GUILD);
  4257. await modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildSettingsWindow.saveGuild(this.id, body);
  4258. this.discordObject = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildStore.getGuild(this.id);
  4259. guilds.set(this.discordObject, this);
  4260. }
  4261. /**
  4262. * Updates this guild's name.
  4263. * @param {String} name The new name
  4264. * @return {Promise}
  4265. */
  4266. updateName(name) {
  4267. return this.updateGuild({name});
  4268. }
  4269. /**
  4270. * Updates this guild's voice region.
  4271. * @param {String} region The ID of the new voice region (obtainable via the API - see https://discordapp.com/developers/docs/resources/voice#list-voice-regions)
  4272. * @return {Promise}
  4273. */
  4274. updateVoiceRegion(region) {
  4275. return this.updateGuild({region});
  4276. }
  4277. /**
  4278. * Updates this guild's verification level.
  4279. * @param {Number} verificationLevel The new verification level (see https://discordapp.com/developers/docs/resources/guild#guild-object-verification-level)
  4280. * @return {Promise}
  4281. */
  4282. updateVerificationLevel(verification_level) {
  4283. return this.updateGuild({verification_level});
  4284. }
  4285. /**
  4286. * Updates this guild's default message notification level.
  4287. * @param {Number} defaultMessageNotifications The new default notification level (0: all messages, 1: only mentions)
  4288. * @return {Promise}
  4289. */
  4290. updateDefaultMessageNotifications(default_message_notifications) {
  4291. return this.updateGuild({default_message_notifications});
  4292. }
  4293. /**
  4294. * Updates this guild's explicit content filter level.
  4295. * @param {Number} explicitContentFilter The new explicit content filter level (0: disabled, 1: members without roles, 2: everyone)
  4296. * @return {Promise}
  4297. */
  4298. updateExplicitContentFilter(explicit_content_filter) {
  4299. return this.updateGuild({explicit_content_filter});
  4300. }
  4301. /**
  4302. * Updates this guild's AFK channel.
  4303. * @param {GuildVoiceChannel} afkChannel The new AFK channel
  4304. * @return {Promise}
  4305. */
  4306. updateAfkChannel(afk_channel) {
  4307. return this.updateGuild({afk_channel_id: afk_channel.id || afk_channel});
  4308. }
  4309. /**
  4310. * Updates this guild's AFK timeout.
  4311. * @param {Number} afkTimeout The new AFK timeout
  4312. * @return {Promise}
  4313. */
  4314. updateAfkTimeout(afk_timeout) {
  4315. return this.updateGuild({afk_timeout});
  4316. }
  4317. /**
  4318. * Updates this guild's icon.
  4319. * @param {Buffer|String} icon A buffer/base 64 encoded 128x128 JPEG image
  4320. * @return {Promise}
  4321. */
  4322. updateIcon(icon) {
  4323. return this.updateGuild({icon: typeof icon === "string" ? icon : icon.toString("base64")});
  4324. }
  4325. /**
  4326. * Updates this guild's icon using a local file.
  4327. * TODO
  4328. * @param {String} icon_path The path to the new icon
  4329. * @return {Promise}
  4330. */
  4331. async updateIconFromFile(icon_path) {
  4332. const buffer = await modules__WEBPACK_IMPORTED_MODULE_0__["Utilities"].readFileBuffer(icon_path);
  4333. return this.updateIcon(buffer);
  4334. }
  4335. /**
  4336. * Updates this guild's owner. (Should plugins really ever need to do this?)
  4337. * @param {User|GuildMember} owner The user/guild member to transfer ownership to
  4338. * @return {Promise}
  4339. */
  4340. updateOwner(owner) {
  4341. return this.updateGuild({owner_id: owner.user ? owner.user.id : owner.id || owner});
  4342. }
  4343. /**
  4344. * Updates this guild's splash image.
  4345. * (I don't know what this is actually used for. The API documentation says it's VIP-only.)
  4346. * @param {Buffer|String} icon A buffer/base 64 encoded 128x128 JPEG image
  4347. * @return {Promise}
  4348. */
  4349. updateSplash(splash) {
  4350. return this.updateGuild({splash: typeof splash === "string" ? splash : splash.toString("base64")});
  4351. }
  4352. /**
  4353. * Updates this guild's splash image using a local file.
  4354. * TODO
  4355. * @param {String} splash_path The path to the new splash
  4356. * @return {Promise}
  4357. */
  4358. async updateSplashFromFile(splash_path) {
  4359. const buffer = await modules__WEBPACK_IMPORTED_MODULE_0__["Utilities"].readFileBuffer(splash_path);
  4360. return this.updateSplash(buffer);
  4361. }
  4362. /**
  4363. * Updates this guild's system channel.
  4364. * @param {GuildTextChannel} systemChannel The new system channel
  4365. * @return {Promise}
  4366. */
  4367. updateSystemChannel(system_channel) {
  4368. return this.updateGuild({system_channel_id: system_channel.id || system_channel});
  4369. }
  4370. }
  4371. /***/ }),
  4372. /***/ "./src/structs/discord/message.js":
  4373. /*!****************************************!*\
  4374. !*** ./src/structs/discord/message.js ***!
  4375. \****************************************/
  4376. /*! exports provided: Reaction, Embed, Message, DefaultMessage, RecipientAddMessage, RecipientRemoveMessage, CallMessage, GroupChannelNameChangeMessage, GroupChannelIconChangeMessage, MessagePinnedMessage, GuildMemberJoinMessage */
  4377. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  4378. "use strict";
  4379. __webpack_require__.r(__webpack_exports__);
  4380. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Reaction", function() { return Reaction; });
  4381. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Embed", function() { return Embed; });
  4382. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Message", function() { return Message; });
  4383. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DefaultMessage", function() { return DefaultMessage; });
  4384. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RecipientAddMessage", function() { return RecipientAddMessage; });
  4385. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RecipientRemoveMessage", function() { return RecipientRemoveMessage; });
  4386. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CallMessage", function() { return CallMessage; });
  4387. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GroupChannelNameChangeMessage", function() { return GroupChannelNameChangeMessage; });
  4388. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GroupChannelIconChangeMessage", function() { return GroupChannelIconChangeMessage; });
  4389. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MessagePinnedMessage", function() { return MessagePinnedMessage; });
  4390. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GuildMemberJoinMessage", function() { return GuildMemberJoinMessage; });
  4391. /* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
  4392. /* harmony import */ var structs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! structs */ "./src/structs/structs.js");
  4393. /* harmony import */ var _channel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./channel */ "./src/structs/discord/channel.js");
  4394. /* harmony import */ var _user__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./user */ "./src/structs/discord/user.js");
  4395. /**
  4396. * BetterDiscord Message Struct
  4397. * Copyright (c) 2018-present JsSucks
  4398. * All rights reserved.
  4399. *
  4400. * This source code is licensed under the MIT license found at
  4401. * https://github.com/JsSucks/BetterDiscordApp/blob/master/LICENSE
  4402. */
  4403. const reactions = new WeakMap();
  4404. class Reaction {
  4405. constructor(data, message_id, channel_id) {
  4406. if (reactions.has(data)) return reactions.get(data);
  4407. reactions.set(data, this);
  4408. this.discordObject = data;
  4409. this.messageId = message_id;
  4410. this.channelId = channel_id;
  4411. }
  4412. get emoji() {
  4413. const id = this.discordObject.emoji.id;
  4414. if (!id || !this.guild) return this.discordObject.emoji;
  4415. return this.guild.emojis.find(e => e.id === id);
  4416. }
  4417. get count() {return this.discordObject.count;}
  4418. get me() {return this.discordObject.me;}
  4419. get channel() {
  4420. return _channel__WEBPACK_IMPORTED_MODULE_2__["Channel"].fromId(this.channel_id);
  4421. }
  4422. get message() {
  4423. if (this.channel) return this.channel.messages.find(m => m.id === this.messageId);
  4424. return null;
  4425. }
  4426. get guild() {
  4427. if (this.channel) return this.channel.guild;
  4428. return null;
  4429. }
  4430. }
  4431. const embeds = new WeakMap();
  4432. class Embed {
  4433. constructor(data, message_id, channel_id) {
  4434. if (embeds.has(data)) return embeds.get(data);
  4435. embeds.set(data, this);
  4436. this.discordObject = data;
  4437. this.messageId = message_id;
  4438. this.channelId = channel_id;
  4439. }
  4440. get title() {return this.discordObject.title;}
  4441. get type() {return this.discordObject.type;}
  4442. get description() {return this.discordObject.description;}
  4443. get url() {return this.discordObject.url;}
  4444. get timestamp() {return this.discordObject.timestamp;}
  4445. get colour() {return this.discordObject.color;}
  4446. get footer() {return this.discordObject.footer;}
  4447. get image() {return this.discordObject.image;}
  4448. get thumbnail() {return this.discordObject.thumbnail;}
  4449. get video() {return this.discordObject.video;}
  4450. get provider() {return this.discordObject.provider;}
  4451. get author() {return this.discordObject.author;}
  4452. get fields() {return this.discordObject.fields;}
  4453. get channel() {
  4454. return _channel__WEBPACK_IMPORTED_MODULE_2__["Channel"].fromId(this.channelId);
  4455. }
  4456. get message() {
  4457. if (this.channel) return this.channel.messages.find(m => m.id === this.messageId);
  4458. return null;
  4459. }
  4460. get guild() {
  4461. if (this.channel) return this.channel.guild;
  4462. return null;
  4463. }
  4464. }
  4465. const messages = new WeakMap();
  4466. /**
  4467. * @memberof module:DiscordAPI
  4468. */
  4469. class Message {
  4470. constructor(data) {
  4471. if (messages.has(data)) return messages.get(data);
  4472. messages.set(data, this);
  4473. this.discordObject = data;
  4474. }
  4475. static from(data) {
  4476. switch (data.type) {
  4477. default: return new Message(data);
  4478. case 0: return new DefaultMessage(data);
  4479. case 1: return new RecipientAddMessage(data);
  4480. case 2: return new RecipientRemoveMessage(data);
  4481. case 3: return new CallMessage(data);
  4482. case 4: return new GroupChannelNameChangeMessage(data);
  4483. case 5: return new GroupChannelIconChangeMessage(data);
  4484. case 6: return new MessagePinnedMessage(data);
  4485. case 7: return new GuildMemberJoinMessage(data);
  4486. }
  4487. }
  4488. static get DefaultMessage() {return DefaultMessage;}
  4489. static get RecipientAddMessage() {return RecipientAddMessage;}
  4490. static get RecipientRemoveMessage() {return RecipientRemoveMessage;}
  4491. static get CallMessage() {return CallMessage;}
  4492. static get GroupChannelNameChangeMessage() {return GroupChannelNameChangeMessage;}
  4493. static get GroupChannelIconChangeMessage() {return GroupChannelIconChangeMessage;}
  4494. static get MessagePinnedMessage() {return MessagePinnedMessage;}
  4495. static get GuildMemberJoinMessage() {return GuildMemberJoinMessage;}
  4496. static get Reaction() {return Reaction;}
  4497. static get Embed() {return Embed;}
  4498. get id() {return this.discordObject.id;}
  4499. get channelId() {return this.discordObject.channel_id;}
  4500. get nonce() {return this.discordObject.nonce;}
  4501. get type() {return this.discordObject.type;}
  4502. get timestamp() {return this.discordObject.timestamp;}
  4503. get state() {return this.discordObject.state;}
  4504. get nick() {return this.discordObject.nick;}
  4505. get colourString() {return this.discordObject.colorString;}
  4506. get author() {
  4507. if (this.discordObject.author && !this.webhookId) return _user__WEBPACK_IMPORTED_MODULE_3__["User"].from(this.discordObject.author);
  4508. return null;
  4509. }
  4510. get channel() {
  4511. return _channel__WEBPACK_IMPORTED_MODULE_2__["Channel"].fromId(this.channelId);
  4512. }
  4513. get guild() {
  4514. if (this.channel) return this.channel.guild;
  4515. return null;
  4516. }
  4517. /**
  4518. * Deletes the message.
  4519. * @return {Promise}
  4520. */
  4521. delete() {
  4522. if (!this.isDeletable) throw new Error(`Message type ${this.type} is not deletable.`);
  4523. if (this.author !== modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentUser) {
  4524. if (this.channel.assertPermissions) this.channel.assertPermissions("MANAGE_MESSAGES", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.MANAGE_MESSAGES);
  4525. else if (!this.channel.owner === modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentUser) throw new structs__WEBPACK_IMPORTED_MODULE_1__["InsufficientPermissions"]("MANAGE_MESSAGES");
  4526. }
  4527. return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].APIModule.delete(`${modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordConstants.Endpoints.MESSAGES(this.channelId)}/${this.id}`); // eslint-disable-line new-cap
  4528. }
  4529. get isDeletable() {
  4530. return this.type === "DEFAULT" || this.type === "CHANNEL_PINNED_MESSAGE" || this.type === "GUILD_MEMBER_JOIN";
  4531. }
  4532. /**
  4533. * Jumps to the message.
  4534. */
  4535. jumpTo(flash = true) {
  4536. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageActions.jumpToMessage(this.channelId, this.id, flash);
  4537. }
  4538. }
  4539. class DefaultMessage extends Message {
  4540. get webhookId() {return this.discordObject.webhookId;}
  4541. get type() {return "DEFAULT";}
  4542. get content() {return this.discordObject.content;}
  4543. get contentParsed() {return this.discordObject.contentParsed;}
  4544. get inviteCodes() {return this.discordObject.invites;}
  4545. get attachments() {return this.discordObject.attachments;}
  4546. get mentionIds() {return this.discordObject.mentions;}
  4547. get mentionRoleIds() {return this.discordObject.mentionRoles;}
  4548. get mentionEveryone() {return this.discordObject.mentionEveryone;}
  4549. get editedTimestamp() {return this.discordObject.editedTimestamp;}
  4550. get tts() {return this.discordObject.tts;}
  4551. get mentioned() {return this.discordObject.mentioned;}
  4552. get bot() {return this.discordObject.bot;}
  4553. get blocked() {return this.discordObject.blocked;}
  4554. get pinned() {return this.discordObject.pinned;}
  4555. get activity() {return this.discordObject.activity;}
  4556. get application() {return this.discordObject.application;}
  4557. get webhook() {
  4558. if (this.webhookId) return this.discordObject.author;
  4559. return null;
  4560. }
  4561. get mentions() {
  4562. return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(this.mentionIds, id => _user__WEBPACK_IMPORTED_MODULE_3__["User"].fromId(id));
  4563. }
  4564. get mention_roles() {
  4565. return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(this.mentionRoleIds, id => this.guild.roles.find(r => r.id === id));
  4566. }
  4567. get embeds() {
  4568. return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(this.discordObject.embeds, r => new Embed(r, this.id, this.channelId));
  4569. }
  4570. get reactions() {
  4571. return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(this.discordObject.reactions, r => new Reaction(r, this.id, this.channelId));
  4572. }
  4573. get edited() {
  4574. return !!this.editedTimestamp;
  4575. }
  4576. /**
  4577. * Programmatically update the message's content.
  4578. * @param {String} content The message's new content
  4579. * @param {Boolean} parse Whether to parse the message or update it as it is
  4580. * @return {Promise}
  4581. */
  4582. async edit(content, parse = false) {
  4583. if (this.author !== modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentUser) throw new Error("Cannot edit messages sent by other users.");
  4584. if (parse) content = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageParser.parse(this.discordObject, content);
  4585. else content = {content};
  4586. const response = await modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].APIModule.patch({
  4587. url: `${modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordConstants.Endpoints.MESSAGES(this.channelId)}/${this.id}`, // eslint-disable-line new-cap
  4588. body: content
  4589. });
  4590. this.discordObject = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageStore.getMessage(this.id, response.body.id);
  4591. messages.set(this.discordObject, this);
  4592. }
  4593. /**
  4594. * Start the edit mode of the UI.
  4595. * @param {String} content A string to show in the message text area - if empty the message's current content will be used
  4596. */
  4597. startEdit(content) {
  4598. if (this.author !== modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentUser) throw new Error("Cannot edit messages sent by other users.");
  4599. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageActions.startEditMessage(this.channelId, this.id, content || this.content);
  4600. }
  4601. /**
  4602. * Exit the edit mode of the UI.
  4603. */
  4604. endEdit() {
  4605. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageActions.endEditMessage();
  4606. }
  4607. }
  4608. class RecipientAddMessage extends Message {
  4609. get type() {return "RECIPIENT_ADD";}
  4610. get addedUserId() {return this.discordObject.mentions[0];}
  4611. get addedUser() {
  4612. return _user__WEBPACK_IMPORTED_MODULE_3__["User"].fromId(this.addedUserId);
  4613. }
  4614. }
  4615. class RecipientRemoveMessage extends Message {
  4616. get type() {return "RECIPIENT_REMOVE";}
  4617. get removedUserId() {return this.discordObject.mentions[0];}
  4618. get removedUser() {
  4619. return _user__WEBPACK_IMPORTED_MODULE_3__["User"].fromId(this.removedUserId);
  4620. }
  4621. get userLeft() {
  4622. return this.author === this.removedUser;
  4623. }
  4624. }
  4625. class CallMessage extends Message {
  4626. get type() {return "CALL";}
  4627. get mentionIds() {return this.discordObject.mentions;}
  4628. get call() {return this.discordObject.call;}
  4629. get endedTimestamp() {return this.call.endedTimestamp;}
  4630. get mentions() {
  4631. return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(this.mentionIds, id => _user__WEBPACK_IMPORTED_MODULE_3__["User"].fromId(id));
  4632. }
  4633. get participants() {
  4634. return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(this.call.participants, id => _user__WEBPACK_IMPORTED_MODULE_3__["User"].fromId(id));
  4635. }
  4636. }
  4637. class GroupChannelNameChangeMessage extends Message {
  4638. get type() {return "CHANNEL_NAME_CHANGE";}
  4639. get newName() {return this.discordObject.content;}
  4640. }
  4641. class GroupChannelIconChangeMessage extends Message {
  4642. get type() {return "CHANNEL_ICON_CHANGE";}
  4643. }
  4644. class MessagePinnedMessage extends Message {
  4645. get type() {return "CHANNEL_PINNED_MESSAGE";}
  4646. }
  4647. class GuildMemberJoinMessage extends Message {
  4648. get type() {return "GUILD_MEMBER_JOIN";}
  4649. }
  4650. /***/ }),
  4651. /***/ "./src/structs/discord/user.js":
  4652. /*!*************************************!*\
  4653. !*** ./src/structs/discord/user.js ***!
  4654. \*************************************/
  4655. /*! exports provided: User, GuildMember */
  4656. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  4657. "use strict";
  4658. __webpack_require__.r(__webpack_exports__);
  4659. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "User", function() { return User; });
  4660. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GuildMember", function() { return GuildMember; });
  4661. /* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
  4662. /* harmony import */ var structs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! structs */ "./src/structs/structs.js");
  4663. /* harmony import */ var _guild__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./guild */ "./src/structs/discord/guild.js");
  4664. /* harmony import */ var _channel__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./channel */ "./src/structs/discord/channel.js");
  4665. /**
  4666. * BetterDiscord User Struct
  4667. * Copyright (c) 2018-present JsSucks
  4668. * All rights reserved.
  4669. *
  4670. * This source code is licensed under the MIT license found at
  4671. * https://github.com/JsSucks/BetterDiscordApp/blob/master/LICENSE
  4672. */
  4673. const users = new WeakMap();
  4674. /**
  4675. * @memberof module:DiscordAPI
  4676. */
  4677. class User {
  4678. constructor(data) {
  4679. if (users.has(data)) return users.get(data);
  4680. users.set(data, this);
  4681. this.discordObject = data;
  4682. }
  4683. static from(data) {
  4684. return new User(data);
  4685. }
  4686. static fromId(id) {
  4687. const user = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserStore.getUser(id);
  4688. if (user) return User.from(user);
  4689. }
  4690. get id() {return this.discordObject.id;}
  4691. get username() {return this.discordObject.username;}
  4692. get usernameLowerCase() {return this.discordObject.usernameLowerCase;}
  4693. get discriminator() {return this.discordObject.discriminator;}
  4694. get avatar() {return this.discordObject.avatar;}
  4695. get email() {return undefined;}
  4696. get phone() {return undefined;}
  4697. get flags() {return this.discordObject.flags;}
  4698. get isBot() {return this.discordObject.bot;}
  4699. get premium() {return this.discordObject.premium;}
  4700. get verified() {return this.discordObject.verified;}
  4701. get mfaEnabled() {return this.discordObject.mfaEnabled;}
  4702. get mobile() {return this.discordObject.mobile;}
  4703. get tag() {return this.discordObject.tag;}
  4704. get avatarUrl() {return this.discordObject.avatarURL;}
  4705. get createdAt() {return this.discordObject.createdAt;}
  4706. get isClamied() {return this.discordObject.isClaimed();}
  4707. get isLocalBot() {return this.discordObject.isLocalBot();}
  4708. get isPhoneVerified() {return this.discordObject.isPhoneVerified();}
  4709. get guilds() {
  4710. return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].guilds.filter(g => g.members.find(m => m.user === this));
  4711. }
  4712. get status() {
  4713. return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserStatusStore.getStatus(this.id);
  4714. }
  4715. get activity() {
  4716. // type can be either 0 (normal/rich presence game), 1 (streaming) or 2 (listening to Spotify)
  4717. // (3 appears as watching but is undocumented)
  4718. return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserStatusStore.getActivity(this.id);
  4719. }
  4720. get note() {
  4721. const note = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserNoteStore.getNote(this.id);
  4722. if (note) return note;
  4723. return null;
  4724. }
  4725. /**
  4726. * Updates the note for this user.
  4727. * @param {String} note The new note
  4728. * @return {Promise}
  4729. */
  4730. updateNote(note) {
  4731. return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].APIModule.put({
  4732. url: `${modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordConstants.Endpoints.NOTES}/${this.id}`,
  4733. body: {note}
  4734. });
  4735. }
  4736. get privateChannel() {
  4737. return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].channels.find(c => c.type === "DM" && c.recipientId === this.id);
  4738. }
  4739. async ensurePrivateChannel() {
  4740. if (modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentUser === this) throw new Error("Cannot create a direct message channel to the current user.");
  4741. return _channel__WEBPACK_IMPORTED_MODULE_3__["Channel"].fromId(await modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].PrivateChannelActions.ensurePrivateChannel(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentUser.id, this.id));
  4742. }
  4743. async sendMessage(content, parse = true) {
  4744. const channel = await this.ensurePrivateChannel();
  4745. return channel.sendMessage(content, parse);
  4746. }
  4747. get isFriend() {
  4748. return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].RelationshipStore.isFriend(this.id);
  4749. }
  4750. get isBlocked() {
  4751. return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].RelationshipStore.isBlocked(this.id);
  4752. }
  4753. addFriend() {
  4754. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].RelationshipManager.addRelationship(this.id, {location: "Context Menu"});
  4755. }
  4756. removeFriend() {
  4757. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].RelationshipManager.removeRelationship(this.id, {location: "Context Menu"});
  4758. }
  4759. block() {
  4760. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].RelationshipManager.addRelationship(this.id, {location: "Context Menu"}, modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordConstants.RelationshipTypes.BLOCKED);
  4761. }
  4762. unblock() {
  4763. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].RelationshipManager.removeRelationship(this.id, {location: "Context Menu"});
  4764. }
  4765. /**
  4766. * Opens the profile modal for this user.
  4767. * @param {String} section The section to open (see DiscordConstants.UserProfileSections)
  4768. */
  4769. openUserProfileModal(section = "USER_INFO") {
  4770. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserProfileModal.open(this.id);
  4771. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserProfileModal.setSection(section);
  4772. }
  4773. }
  4774. const guild_members = new WeakMap();
  4775. class GuildMember {
  4776. constructor(data, guild_id) {
  4777. if (guild_members.has(data)) return guild_members.get(data);
  4778. guild_members.set(data, this);
  4779. this.discordObject = data;
  4780. this.guildId = guild_id;
  4781. }
  4782. get userId() {return this.discordObject.userId;}
  4783. get nickname() {return this.discordObject.nick;}
  4784. get colourString() {return this.discordObject.colorString;}
  4785. get hoistRoleId() {return this.discordObject.hoistRoleId;}
  4786. get roleIds() {return this.discordObject.roles;}
  4787. get user() {
  4788. return User.fromId(this.userId);
  4789. }
  4790. get name() {
  4791. return this.nickname || this.user.username;
  4792. }
  4793. get guild() {
  4794. return _guild__WEBPACK_IMPORTED_MODULE_2__["Guild"].fromId(this.guildId);
  4795. }
  4796. get roles() {
  4797. return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(this.roleIds, id => this.guild.roles.find(r => r.id === id))
  4798. .sort((r1, r2) => r1.position === r2.position ? 0 : r1.position > r2.position ? 1 : -1);
  4799. }
  4800. get hoistRole() {
  4801. return this.guild.roles.find(r => r.id === this.hoistRoleId);
  4802. }
  4803. checkPermissions(perms) {
  4804. return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].Permissions.can(perms, modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentUser.discordObject, this.guild.discordObject);
  4805. }
  4806. assertPermissions(name, perms) {
  4807. if (!this.checkPermissions(perms)) throw new structs__WEBPACK_IMPORTED_MODULE_1__["InsufficientPermissions"](name);
  4808. }
  4809. /**
  4810. * Opens the modal to change this user's nickname.
  4811. */
  4812. openChangeNicknameModal() {
  4813. if (modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentUser === this.user) this.assertPermissions("CHANGE_NICKNAME", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.CHANGE_NICKNAME);
  4814. else this.assertPermissions("MANAGE_NICKNAMES", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.MANAGE_NICKNAMES);
  4815. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].ChangeNicknameModal.open(this.guildId, this.userId);
  4816. }
  4817. /**
  4818. * Changes the user's nickname on this guild.
  4819. * @param {String} nickname The user's new nickname
  4820. * @return {Promise}
  4821. */
  4822. changeNickname(nick) {
  4823. if (modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentUser === this.user) this.assertPermissions("CHANGE_NICKNAME", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.CHANGE_NICKNAME);
  4824. else this.assertPermissions("MANAGE_NICKNAMES", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.MANAGE_NICKNAMES);
  4825. return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].APIModule.patch({
  4826. url: `${modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordConstants.Endpoints.GUILD_MEMBERS(this.guild_id)}/${modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentUser === this.user ? "@me/nick" : this.userId}`, // eslint-disable-line new-cap
  4827. body: {nick}
  4828. });
  4829. }
  4830. /**
  4831. * Kicks this user from the guild.
  4832. * @param {String} reason A reason to attach to the audit log entry
  4833. * @return {Promise}
  4834. */
  4835. kick(reason = "") {
  4836. this.assertPermissions("KICK_MEMBERS", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.KICK_MEMBERS);
  4837. return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildActions.kickUser(this.guildId, this.userId, reason);
  4838. }
  4839. /**
  4840. * Bans this user from the guild.
  4841. * @param {Number} daysToDelete The number of days of the user's recent message history to delete
  4842. * @param {String} reason A reason to attach to the audit log entry
  4843. * @return {Promise}
  4844. */
  4845. ban(daysToDelete = 1, reason = "") {
  4846. this.assertPermissions("BAN_MEMBERS", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.BAN_MEMBERS);
  4847. return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildActions.banUser(this.guildId, this.userId, daysToDelete, reason);
  4848. }
  4849. /**
  4850. * Removes the ban for this user.
  4851. * @return {Promise}
  4852. */
  4853. unban() {
  4854. this.assertPermissions("BAN_MEMBERS", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.BAN_MEMBERS);
  4855. return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildActions.unbanUser(this.guildId, this.userId);
  4856. }
  4857. /**
  4858. * Moves this user to another voice channel.
  4859. * @param {GuildVoiceChannel} channel The channel to move this user to
  4860. */
  4861. move(channel) {
  4862. this.assertPermissions("MOVE_MEMBERS", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.MOVE_MEMBERS);
  4863. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildActions.setChannel(this.guildId, this.userId, channel.id);
  4864. }
  4865. /**
  4866. * Mutes this user for everyone in the guild.
  4867. */
  4868. mute(active = true) {
  4869. this.assertPermissions("MUTE_MEMBERS", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.MUTE_MEMBERS);
  4870. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildActions.setServerMute(this.guildId, this.userId, active);
  4871. }
  4872. /**
  4873. * Unmutes this user.
  4874. */
  4875. unmute() {
  4876. this.mute(false);
  4877. }
  4878. /**
  4879. * Deafens this user.
  4880. */
  4881. deafen(active = true) {
  4882. this.assertPermissions("DEAFEN_MEMBERS", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.DEAFEN_MEMBERS);
  4883. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildActions.setServerDeaf(this.guildId, this.userId, active);
  4884. }
  4885. /**
  4886. * Undeafens this user.
  4887. */
  4888. undeafen() {
  4889. this.deafen(false);
  4890. }
  4891. /**
  4892. * Gives this user a role.
  4893. * @param {Role} role The role to add
  4894. * @return {Promise}
  4895. */
  4896. addRole(...roles) {
  4897. const newRoles = this.roleIds.concat([]);
  4898. let changed = false;
  4899. for (const role of roles) {
  4900. if (newRoles.includes(role.id || role)) continue;
  4901. newRoles.push(role.id || role);
  4902. changed = true;
  4903. }
  4904. if (!changed) return;
  4905. return this.updateRoles(newRoles);
  4906. }
  4907. /**
  4908. * Removes a role from this user.
  4909. * @param {Role} role The role to remove
  4910. * @return {Promise}
  4911. */
  4912. removeRole(...roles) {
  4913. const newRoles = this.roleIds.concat([]);
  4914. let changed = false;
  4915. for (const role of roles) {
  4916. if (!newRoles.includes(role.id || role)) continue;
  4917. modules__WEBPACK_IMPORTED_MODULE_0__["Utilities"].removeFromArray(newRoles, role.id || role);
  4918. changed = true;
  4919. }
  4920. if (!changed) return;
  4921. return this.updateRoles(newRoles);
  4922. }
  4923. /**
  4924. * Updates this user's roles.
  4925. * @param {Array} roles An array of Role objects or role IDs
  4926. * @return {Promise}
  4927. */
  4928. updateRoles(roles) {
  4929. roles = roles.map(r => r.id || r);
  4930. return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].APIModule.patch({
  4931. url: `${modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordConstants.Endpoints.GUILD_MEMBERS(this.guildId)}/${this.userId}`, // eslint-disable-line new-cap
  4932. body: {roles}
  4933. });
  4934. }
  4935. }
  4936. /***/ }),
  4937. /***/ "./src/structs/discord/usersettings.js":
  4938. /*!*********************************************!*\
  4939. !*** ./src/structs/discord/usersettings.js ***!
  4940. \*********************************************/
  4941. /*! exports provided: UserSettings */
  4942. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  4943. "use strict";
  4944. __webpack_require__.r(__webpack_exports__);
  4945. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "UserSettings", function() { return UserSettings; });
  4946. /* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
  4947. /* harmony import */ var structs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! structs */ "./src/structs/structs.js");
  4948. /* harmony import */ var _guild__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./guild */ "./src/structs/discord/guild.js");
  4949. /**
  4950. * BetterDiscord Channel Struct
  4951. * Copyright (c) 2018-present JsSucks
  4952. * All rights reserved.
  4953. *
  4954. * This source code is licensed under the MIT license found at
  4955. * https://github.com/JsSucks/BetterDiscordApp/blob/master/LICENSE
  4956. */
  4957. /**
  4958. * @memberof module:DiscordAPI
  4959. */
  4960. class UserSettings {
  4961. /**
  4962. * Opens Discord's settings UI.
  4963. */
  4964. static open(section = "ACCOUNT") {
  4965. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsWindow.setSection(section);
  4966. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsWindow.open();
  4967. }
  4968. /**
  4969. * The user's current status. Either "online", "idle", "dnd" or "invisible".
  4970. */
  4971. static get status() {return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.status;}
  4972. /**
  4973. * The user's selected explicit content filter level.
  4974. * 0 == off, 1 == everyone except friends, 2 == everyone
  4975. * Configurable in the privacy and safety panel.
  4976. */
  4977. static get explicitContentFilter() {return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.explicitContentFilter;}
  4978. /**
  4979. * Whether to disallow direct messages from server members by default.
  4980. */
  4981. static get defaultGuildsRestricted() {return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.defaultGuildsRestricted;}
  4982. /**
  4983. * An array of guilds to disallow direct messages from their members.
  4984. * This is bypassed if the member is has another mutual guild with this disabled, or the member is friends with the current user.
  4985. * Configurable in each server's privacy settings.
  4986. */
  4987. static get restrictedGuildIds() {return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.restrictedGuilds;}
  4988. static get restrictedGuilds() {
  4989. return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(this.restrictedGuildIds, id => _guild__WEBPACK_IMPORTED_MODULE_2__["Guild"].fromId(id) || id);
  4990. }
  4991. /**
  4992. * An array of flags specifying who should be allowed to add the current user as a friend.
  4993. * If everyone is checked, this will only have one item, "all". Otherwise it has either "mutual_friends", "mutual_guilds", both or neither.
  4994. * Configurable in the privacy and safety panel.
  4995. */
  4996. static get friendSourceFlags() {return Object.keys(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.friendSourceFlags);}
  4997. static get friendSourceEveryone() {return this.friend_source_flags.include("all");}
  4998. static get friendSourceMutual_friends() {return this.friend_source_flags.include("all") || this.friend_source_flags.include("mutual_friends");}
  4999. static get friendSourceMutual_guilds() {return this.friend_source_flags.include("all") || this.friend_source_flags.include("mutual_guilds");}
  5000. static get friendSourceAnyone() {return this.friend_source_flags.length > 0;}
  5001. /**
  5002. * Whether to automatically add accounts from other platforms running on the user's computer.
  5003. * Configurable in the connections panel.
  5004. */
  5005. static get detectPlatformAccounts() {return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.detectPlatformAccounts;}
  5006. /**
  5007. * The number of seconds Discord will wait for activity before sending mobile push notifications.
  5008. * Configurable in the notifications panel.
  5009. */
  5010. static get afkTimeout() {return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.afkTimeout;}
  5011. /**
  5012. * Whether to display the currently running game as a status message.
  5013. * Configurable in the games panel.
  5014. */
  5015. static get showCurrentGame() {return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.showCurrentGame;}
  5016. /**
  5017. * Whether to show images uploaded directly to Discord.
  5018. * Configurable in the text and images panel.
  5019. */
  5020. static get inlineAttachmentMedia() {return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.inlineAttachmentMedia;}
  5021. /**
  5022. * Whether to show images linked in Discord.
  5023. * Configurable in the text and images panel.
  5024. */
  5025. static get inlineEmbedMedia() {return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.inlineEmbedMedia;}
  5026. /**
  5027. * Whether to automatically play GIFs when the Discord window is active without having to hover the mouse over the image.
  5028. * Configurable in the text and images panel.
  5029. */
  5030. static get autoplayGifs() {return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.gifAutoPlay;}
  5031. /**
  5032. * Whether to show content from HTTP[s] links as embeds.
  5033. * Configurable in the text and images panel.
  5034. */
  5035. static get showEmbeds() {return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.renderEmbeds;}
  5036. /**
  5037. * Whether to show a message's reactions.
  5038. * Configurable in the text and images panel.
  5039. */
  5040. static get showReactions() {return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.renderReactions;}
  5041. /**
  5042. * Whether to play animated emoji.
  5043. * Configurable in the text and images panel.
  5044. */
  5045. static get animateEmoji() {return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.animateEmoji;}
  5046. /**
  5047. * Whether to convert ASCII emoticons to emoji.
  5048. * Configurable in the text and images panel.
  5049. */
  5050. static get convertEmoticons() {return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.convertEmoticons;}
  5051. /**
  5052. * Whether to allow playing text-to-speech messages.
  5053. * Configurable in the text and images panel.
  5054. */
  5055. static get allowTts() {return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.enableTTSCommand;}
  5056. /**
  5057. * The user's selected theme. Either "dark" or "light".
  5058. * Configurable in the appearance panel.
  5059. */
  5060. static get theme() {return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.theme;}
  5061. /**
  5062. * Whether the user has enabled compact mode.
  5063. * `true` if compact mode is enabled, `false` if cozy mode is enabled.
  5064. * Configurable in the appearance panel.
  5065. */
  5066. static get displayCompact() {return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.messageDisplayCompact;}
  5067. /**
  5068. * Whether the user has enabled developer mode.
  5069. * Currently only adds a "Copy ID" option to the context menu on users, guilds and channels.
  5070. * Configurable in the appearance panel.
  5071. */
  5072. static get developerMode() {return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.developerMode;}
  5073. /**
  5074. * The user's selected language code.
  5075. * Configurable in the language panel.
  5076. */
  5077. static get locale() {return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.locale;}
  5078. /**
  5079. * The user's timezone offset in hours.
  5080. * This is not configurable.
  5081. */
  5082. static get timezoneOffset() {return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.timezoneOffset;}
  5083. }
  5084. /***/ }),
  5085. /***/ "./src/structs/dom/classname.js":
  5086. /*!**************************************!*\
  5087. !*** ./src/structs/dom/classname.js ***!
  5088. \**************************************/
  5089. /*! exports provided: default */
  5090. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  5091. "use strict";
  5092. __webpack_require__.r(__webpack_exports__);
  5093. /* harmony import */ var _selector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./selector */ "./src/structs/dom/selector.js");
  5094. /**
  5095. * Representation of a Class Name
  5096. * @memberof module:DOMTools
  5097. **/
  5098. class ClassName {
  5099. /**
  5100. *
  5101. * @param {string} name - name of the class to represent
  5102. */
  5103. constructor(name) {
  5104. this.value = name;
  5105. }
  5106. /**
  5107. * Concatenates new class names to the current one using spaces.
  5108. * @param {string} classNames - list of class names to add to this class name
  5109. * @returns {ClassName} returns self to allow chaining
  5110. */
  5111. add(...classNames) {
  5112. for (let i = 0; i < classNames.length; i++) this.value += " " + classNames[i];
  5113. return this;
  5114. }
  5115. /**
  5116. * Returns the raw class name, this is how native function get the value.
  5117. * @returns {string} raw class name.
  5118. */
  5119. toString() {
  5120. return this.value;
  5121. }
  5122. /**
  5123. * Returns the raw class name, this is how native function get the value.
  5124. * @returns {string} raw class name.
  5125. */
  5126. valueOf() {
  5127. return this.value;
  5128. }
  5129. /**
  5130. * Returns the classname represented as {@link module:DOMTools.Selector}.
  5131. * @returns {Selector} selector representation of this class name.
  5132. */
  5133. get selector() {
  5134. return new _selector__WEBPACK_IMPORTED_MODULE_0__["default"](this.value);
  5135. }
  5136. get single() {
  5137. return this.value.split(" ")[0];
  5138. }
  5139. get first() {
  5140. return this.value.split(" ")[0];
  5141. }
  5142. }
  5143. /* harmony default export */ __webpack_exports__["default"] = (ClassName);
  5144. /***/ }),
  5145. /***/ "./src/structs/dom/observer.js":
  5146. /*!*************************************!*\
  5147. !*** ./src/structs/dom/observer.js ***!
  5148. \*************************************/
  5149. /*! exports provided: default */
  5150. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  5151. "use strict";
  5152. __webpack_require__.r(__webpack_exports__);
  5153. /* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
  5154. /**
  5155. * BetterDiscord Client DOM Module
  5156. * Copyright (c) 2015-present JsSucks - https://github.com/JsSucks
  5157. * All rights reserved.
  5158. * https://betterdiscord.net
  5159. *
  5160. * This source code is licensed under the MIT license found in the
  5161. * LICENSE file in the root directory of this source tree.
  5162. */
  5163. /* eslint-disable operator-linebreak */
  5164. /**
  5165. * Representation of a MutationObserver but with helpful utilities.
  5166. * @memberof module:DOMTools
  5167. **/
  5168. class DOMObserver {
  5169. constructor(root, options) {
  5170. this.observe = this.observe.bind(this);
  5171. this.subscribe = this.subscribe.bind(this);
  5172. this.observerCallback = this.observerCallback.bind(this);
  5173. this.active = false;
  5174. this.root = root || document.getElementById("app-mount");
  5175. this.options = options || {attributes: true, childList: true, subtree: true};
  5176. this.observer = new MutationObserver(this.observerCallback);
  5177. this.observe();
  5178. }
  5179. observerCallback(mutations) {
  5180. for (const sub of Array.from(this.subscriptions)) {
  5181. try {
  5182. const filteredMutations = sub.filter ? mutations.filter(sub.filter) : mutations;
  5183. if (sub.group) {
  5184. if (!filteredMutations.length) continue;
  5185. sub.callback.call(sub.bind || sub, filteredMutations);
  5186. }
  5187. else {
  5188. for (const mutation of filteredMutations) sub.callback.call(sub.bind || sub, mutation);
  5189. }
  5190. }
  5191. catch (err) {
  5192. modules__WEBPACK_IMPORTED_MODULE_0__["Logger"].stacktrace("DOMObserver", "Error in observer callback", err);
  5193. }
  5194. }
  5195. }
  5196. /**
  5197. * Starts observing the element. This will be called when attaching a callback.
  5198. * You don't need to call this manually.
  5199. */
  5200. observe() {
  5201. if (this.active) return;
  5202. this.observer.observe(this.root, this.options);
  5203. this.active = true;
  5204. }
  5205. /**
  5206. * Disconnects this observer. This stops callbacks being called, but does not unbind them.
  5207. * You probably want to use observer.unsubscribeAll instead.
  5208. */
  5209. disconnect() {
  5210. if (!this.active) return;
  5211. this.observer.disconnect();
  5212. this.active = false;
  5213. }
  5214. reconnect() {
  5215. if (this.active) {
  5216. this.disconnect();
  5217. this.observe();
  5218. }
  5219. }
  5220. get root() {return this._root;}
  5221. set root(root) {this._root = root; this.reconnect();}
  5222. get options() {return this._options;}
  5223. set options(options) {this._options = options; this.reconnect();}
  5224. get subscriptions() {
  5225. return this._subscriptions || (this._subscriptions = []);
  5226. }
  5227. /**
  5228. * Subscribes to mutations.
  5229. * @param {Function} callback A function to call when on a mutation
  5230. * @param {Function} filter A function to call to filter mutations
  5231. * @param {Any} bind Something to bind the callback to
  5232. * @param {Boolean} group Whether to call the callback with an array of mutations instead of a single mutation
  5233. * @return {Object}
  5234. */
  5235. subscribe(callback, filter, bind, group) {
  5236. const subscription = {callback, filter, bind, group};
  5237. this.subscriptions.push(subscription);
  5238. this.observe();
  5239. return subscription;
  5240. }
  5241. /**
  5242. * Removes a subscription and disconnect if there are none left.
  5243. * @param {Object} subscription A subscription object returned by observer.subscribe
  5244. */
  5245. unsubscribe(subscription) {
  5246. if (!this.subscriptions.includes(subscription)) subscription = this.subscriptions.find(s => s.callback === subscription);
  5247. modules__WEBPACK_IMPORTED_MODULE_0__["Utilities"].removeFromArray(this.subscriptions, subscription);
  5248. if (!this.subscriptions.length) this.disconnect();
  5249. }
  5250. unsubscribeAll() {
  5251. this.subscriptions.splice(0, this.subscriptions.length);
  5252. this.disconnect();
  5253. }
  5254. /**
  5255. * Subscribes to mutations that affect an element matching a selector.
  5256. * @param {Function} callback A function to call when on a mutation
  5257. * @param {Function} filter A function to call to filter mutations
  5258. * @param {Any} bind Something to bind the callback to
  5259. * @param {Boolean} group Whether to call the callback with an array of mutations instead of a single mutation
  5260. * @return {Object}
  5261. */
  5262. subscribeToQuerySelector(callback, selector, bind, group) {
  5263. return this.subscribe(callback, mutation => {
  5264. return mutation.target.matches(selector) // If the target matches the selector
  5265. || Array.from(mutation.addedNodes).concat(Array.from(mutation.removedNodes)) // Or if either an added or removed node
  5266. .find(n => n instanceof Element && (n.matches(selector) || n.querySelector(selector))); // match or contain an element matching the selector
  5267. }, bind, group);
  5268. }
  5269. }
  5270. /* harmony default export */ __webpack_exports__["default"] = (DOMObserver);
  5271. /***/ }),
  5272. /***/ "./src/structs/dom/selector.js":
  5273. /*!*************************************!*\
  5274. !*** ./src/structs/dom/selector.js ***!
  5275. \*************************************/
  5276. /*! exports provided: default */
  5277. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  5278. "use strict";
  5279. __webpack_require__.r(__webpack_exports__);
  5280. /**
  5281. * Representation of a Selector
  5282. * @memberof module:DOMTools
  5283. **/
  5284. class Selector {
  5285. /**
  5286. *
  5287. * @param {string} classname - class to create selector for
  5288. */
  5289. constructor(className) {
  5290. this.value = " ." + className.split(" ").join(".");
  5291. }
  5292. /**
  5293. * Returns the raw selector, this is how native function get the value.
  5294. * @returns {string} raw selector.
  5295. */
  5296. toString() {
  5297. return this.value;
  5298. }
  5299. /**
  5300. * Returns the raw selector, this is how native function get the value.
  5301. * @returns {string} raw selector.
  5302. */
  5303. valueOf() {
  5304. return this.value;
  5305. }
  5306. selector(symbol, other) {
  5307. this.value = `${this.toString()} ${symbol} ${other.toString()}`;
  5308. return this;
  5309. }
  5310. /**
  5311. * Adds another selector as a direct child `>` to this one.
  5312. * @param {string|DOMTools.Selector} other - Selector to add as child
  5313. * @returns {DOMTools.Selector} returns self to allow chaining
  5314. */
  5315. child(other) {
  5316. return this.selector(">", other);
  5317. }
  5318. /**
  5319. * Adds another selector as a adjacent sibling `+` to this one.
  5320. * @param {string|DOMTools.Selector} other - Selector to add as adjacent sibling
  5321. * @returns {DOMTools.Selector} returns self to allow chaining
  5322. */
  5323. adjacent(other) {
  5324. return this.selector("+", other);
  5325. }
  5326. /**
  5327. * Adds another selector as a general sibling `~` to this one.
  5328. * @param {string|DOMTools.Selector} other - Selector to add as sibling
  5329. * @returns {DOMTools.Selector} returns self to allow chaining
  5330. */
  5331. sibling(other) {
  5332. return this.selector("~", other);
  5333. }
  5334. /**
  5335. * Adds another selector as a descendent `(space)` to this one.
  5336. * @param {string|DOMTools.Selector} other - Selector to add as descendent
  5337. * @returns {DOMTools.Selector} returns self to allow chaining
  5338. */
  5339. descend(other) {
  5340. return this.selector(" ", other);
  5341. }
  5342. /**
  5343. * Adds another selector to this one via `,`.
  5344. * @param {string|DOMTools.Selector} other - Selector to add
  5345. * @returns {DOMTools.Selector} returns self to allow chaining
  5346. */
  5347. and(other) {
  5348. return this.selector(",", other);
  5349. }
  5350. }
  5351. /* harmony default export */ __webpack_exports__["default"] = (Selector);
  5352. /***/ }),
  5353. /***/ "./src/structs/errors/permissionserror.js":
  5354. /*!************************************************!*\
  5355. !*** ./src/structs/errors/permissionserror.js ***!
  5356. \************************************************/
  5357. /*! exports provided: default, InsufficientPermissions */
  5358. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  5359. "use strict";
  5360. __webpack_require__.r(__webpack_exports__);
  5361. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "InsufficientPermissions", function() { return InsufficientPermissions; });
  5362. class PermissionsError extends Error {
  5363. constructor(message) {
  5364. super(message);
  5365. this.name = "PermissionsError";
  5366. }
  5367. }
  5368. /**
  5369. * @memberof module:DiscordAPI
  5370. */
  5371. class InsufficientPermissions extends PermissionsError {
  5372. constructor(message) {
  5373. super(`Missing Permission — ${message}`);
  5374. this.name = "InsufficientPermissions";
  5375. }
  5376. }
  5377. /* harmony default export */ __webpack_exports__["default"] = (PermissionsError);
  5378. /***/ }),
  5379. /***/ "./src/structs/list.js":
  5380. /*!*****************************!*\
  5381. !*** ./src/structs/list.js ***!
  5382. \*****************************/
  5383. /*! exports provided: default */
  5384. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  5385. "use strict";
  5386. __webpack_require__.r(__webpack_exports__);
  5387. /**
  5388. * @memberof module:DiscordAPI
  5389. */
  5390. /**
  5391. * Extension of Array that adds simple utilities.
  5392. */
  5393. class List extends Array {
  5394. /**
  5395. * Allows multiple filters at once
  5396. * @param {...callable} filters - set a filters to filter the list by
  5397. */
  5398. get(...filters) {
  5399. return this.find(item => {
  5400. for (const filter of filters) {
  5401. for (const key in filter) {
  5402. if (filter.hasOwnProperty(key)) {
  5403. if (item[key] !== filter[key]) return false;
  5404. }
  5405. }
  5406. }
  5407. return true;
  5408. });
  5409. }
  5410. }
  5411. /* harmony default export */ __webpack_exports__["default"] = (List);
  5412. /***/ }),
  5413. /***/ "./src/structs/listenable.js":
  5414. /*!***********************************!*\
  5415. !*** ./src/structs/listenable.js ***!
  5416. \***********************************/
  5417. /*! exports provided: default */
  5418. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  5419. "use strict";
  5420. __webpack_require__.r(__webpack_exports__);
  5421. /**
  5422. * Acts as an interface for anything that should be listenable.
  5423. */
  5424. class Listenable {
  5425. constructor() {
  5426. this.listeners = [];
  5427. }
  5428. /**
  5429. * Adds a listener to the current object.
  5430. * @param {callable} callback - callback for when the event occurs
  5431. * @returns {callable} - a way to cancel the listener without needing to call `removeListener`
  5432. */
  5433. addListener(callback) {
  5434. if (typeof(callback) !== "function") return;
  5435. this.listeners.push(callback);
  5436. return () => {
  5437. this.listeners.splice(this.listeners.indexOf(callback), 1);
  5438. };
  5439. }
  5440. /**
  5441. * Removes a listener from the current object.
  5442. * @param {callable} callback - callback that was originally registered
  5443. */
  5444. removeListener(callback) {
  5445. if (typeof(callback) !== "function") return;
  5446. this.listeners.splice(this.listeners.indexOf(callback), 1);
  5447. }
  5448. /**
  5449. * Alerts the listeners that an event occurred. Data passed is optional
  5450. * @param {*} [...data] - Any data desired to be passed to listeners
  5451. */
  5452. alertListeners(...data) {
  5453. for (let l = 0; l < this.listeners.length; l++) this.listeners[l](...data);
  5454. }
  5455. }
  5456. /* harmony default export */ __webpack_exports__["default"] = (Listenable);
  5457. /***/ }),
  5458. /***/ "./src/structs/plugin.js":
  5459. /*!*******************************!*\
  5460. !*** ./src/structs/plugin.js ***!
  5461. \*******************************/
  5462. /*! exports provided: default */
  5463. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  5464. "use strict";
  5465. __webpack_require__.r(__webpack_exports__);
  5466. /* harmony import */ var _modules_pluginupdater__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../modules/pluginupdater */ "./src/modules/pluginupdater.js");
  5467. /* harmony import */ var _modules_logger__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../modules/logger */ "./src/modules/logger.js");
  5468. /* harmony import */ var _modules_reacttools__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../modules/reacttools */ "./src/modules/reacttools.js");
  5469. /* harmony import */ var _ui_modals__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../ui/modals */ "./src/ui/modals.js");
  5470. /* harmony import */ var _modules_pluginutilities__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../modules/pluginutilities */ "./src/modules/pluginutilities.js");
  5471. /* harmony import */ var _modules_utilities__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../modules/utilities */ "./src/modules/utilities.js");
  5472. /* harmony import */ var _modules_discordmodules__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../modules/discordmodules */ "./src/modules/discordmodules.js");
  5473. /* harmony import */ var _ui_settings__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../ui/settings */ "./src/ui/settings/index.js");
  5474. /* harmony default export */ __webpack_exports__["default"] = (function(config) {
  5475. return class Plugin {
  5476. constructor() {
  5477. this._config = config;
  5478. this._enabled = false;
  5479. if (typeof(config.defaultConfig) != "undefined") {
  5480. this.defaultSettings = {};
  5481. for (let s = 0; s < config.defaultConfig.length; s++) {
  5482. const current = config.defaultConfig[s];
  5483. if (current.type != "category") {this.defaultSettings[current.id] = current.value;}
  5484. else {
  5485. this.defaultSettings[current.id] = {};
  5486. for (let s = 0; s < current.settings.length; s++) {
  5487. const subCurrent = current.settings[s];
  5488. this.defaultSettings[current.id][subCurrent.id] = subCurrent.value;
  5489. }
  5490. }
  5491. }
  5492. this._hasConfig = true;
  5493. this.settings = _modules_utilities__WEBPACK_IMPORTED_MODULE_5__["default"].deepclone(this.defaultSettings);
  5494. }
  5495. }
  5496. getName() {return this._config.info.name.replace(" ", "");}
  5497. getDescription() {return this._config.info.description;}
  5498. getVersion() {return this._config.info.version;}
  5499. getAuthor() {return this._config.info.authors.map(a => a.name).join(", ");}
  5500. load() {
  5501. const currentVersionInfo = _modules_pluginutilities__WEBPACK_IMPORTED_MODULE_4__["default"].loadData(this.getName(), "currentVersionInfo", {version: this.getVersion(), hasShownChangelog: false});
  5502. if (currentVersionInfo.version != this.getVersion() || !currentVersionInfo.hasShownChangelog) {
  5503. this.showChangelog();
  5504. _modules_pluginutilities__WEBPACK_IMPORTED_MODULE_4__["default"].saveData(this.getName(), "currentVersionInfo", {version: this.getVersion(), hasShownChangelog: true});
  5505. }
  5506. _modules_pluginupdater__WEBPACK_IMPORTED_MODULE_0__["default"].checkForUpdate(this.getName(), this.getVersion(), this._config.info.github_raw);
  5507. }
  5508. async start() {
  5509. _modules_logger__WEBPACK_IMPORTED_MODULE_1__["default"].info(this.getName(), `version ${this.getVersion()} has started.`);
  5510. if (this.defaultSettings) this.settings = this.loadSettings();
  5511. this._enabled = true;
  5512. if (typeof(this.onStart) == "function") this.onStart();
  5513. }
  5514. stop() {
  5515. _modules_logger__WEBPACK_IMPORTED_MODULE_1__["default"].info(this.getName(), `version ${this.getVersion()} has stopped.`);
  5516. this._enabled = false;
  5517. if (typeof(this.onStop) == "function") this.onStop();
  5518. }
  5519. get isEnabled() {return this._enabled;}
  5520. get strings() {
  5521. if (!this._config.strings) return {};
  5522. const locale = _modules_discordmodules__WEBPACK_IMPORTED_MODULE_6__["default"].UserSettingsStore.locale.split("-")[0];
  5523. if (this._config.strings.hasOwnProperty(locale)) return this._config.strings[locale];
  5524. if (this._config.strings.hasOwnProperty("en")) return this._config.strings.en;
  5525. return this._config.strings;
  5526. }
  5527. set strings(strings) {
  5528. this._config.strings = strings;
  5529. }
  5530. showSettingsModal() {
  5531. if (typeof(this.getSettingsPanel) != "function") return;
  5532. _ui_modals__WEBPACK_IMPORTED_MODULE_3__["default"].showModal(this.getName() + " Settings", _modules_reacttools__WEBPACK_IMPORTED_MODULE_2__["default"].createWrappedElement(this.getSettingsPanel()), {
  5533. cancelText: "",
  5534. confirmText: "Done",
  5535. size: _ui_modals__WEBPACK_IMPORTED_MODULE_3__["default"].ModalSizes.MEDIUM
  5536. });
  5537. }
  5538. showChangelog(footer) {
  5539. if (typeof(this._config.changelog) == "undefined") return;
  5540. _ui_modals__WEBPACK_IMPORTED_MODULE_3__["default"].showChangelogModal(this.getName() + " Changelog", this.getVersion(), this._config.changelog, footer);
  5541. }
  5542. saveSettings(settings) {
  5543. _modules_pluginutilities__WEBPACK_IMPORTED_MODULE_4__["default"].saveSettings(this.getName(), this.settings ? this.settings : settings);
  5544. }
  5545. loadSettings(defaultSettings) {
  5546. // loadSettings -> loadData -> defaultSettings gets deep cloned
  5547. return _modules_pluginutilities__WEBPACK_IMPORTED_MODULE_4__["default"].loadSettings(this.getName(), this.defaultSettings ? this.defaultSettings : defaultSettings);
  5548. }
  5549. buildSetting(data) {
  5550. const {name, note, type, value, onChange, id} = data;
  5551. let setting = null;
  5552. if (type == "color") setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_7__["ColorPicker"](name, note, value, onChange, {disabled: data.disabled, presetColors: data.presetColors});
  5553. else if (type == "dropdown") setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_7__["Dropdown"](name, note, value, data.options, onChange);
  5554. else if (type == "file") setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_7__["FilePicker"](name, note, onChange);
  5555. else if (type == "keybind") setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_7__["Keybind"](name, note, value, onChange);
  5556. else if (type == "radio") setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_7__["RadioGroup"](name, note, value, data.options, onChange, {disabled: data.disabled});
  5557. else if (type == "slider") setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_7__["Slider"](name, note, data.min, data.max, value, onChange, data);
  5558. else if (type == "switch") setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_7__["Switch"](name, note, value, onChange, {disabled: data.disabled});
  5559. else if (type == "textbox") setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_7__["Textbox"](name, note, value, onChange, {placeholder: data.placeholder || ""});
  5560. if (id) setting.id = id;
  5561. return setting;
  5562. }
  5563. buildSettingsPanel() {
  5564. const config = this._config.defaultConfig;
  5565. const buildGroup = (group) => {
  5566. const {name, id, collapsible, shown, settings} = group;
  5567. // this.settings[id] = {};
  5568. const list = [];
  5569. for (let s = 0; s < settings.length; s++) {
  5570. const current = Object.assign({}, settings[s]);
  5571. current.value = this.settings[id][current.id];
  5572. current.onChange = (value) => {
  5573. this.settings[id][current.id] = value;
  5574. };
  5575. if (Object.keys(this.strings).length && this.strings.settings && this.strings.settings[id] && this.strings.settings[id][current.id]) {
  5576. const {name, note} = this.strings.settings[id][current.id];
  5577. current.name = name;
  5578. current.note = note;
  5579. }
  5580. list.push(this.buildSetting(current));
  5581. }
  5582. const settingGroup = new _ui_settings__WEBPACK_IMPORTED_MODULE_7__["SettingGroup"](name, {shown, collapsible}).append(...list);
  5583. settingGroup.id = id;
  5584. return settingGroup;
  5585. };
  5586. const list = [];
  5587. for (let s = 0; s < config.length; s++) {
  5588. const current = Object.assign({}, config[s]);
  5589. if (current.type != "category") {
  5590. current.value = this.settings[current.id];
  5591. current.onChange = (value) => {
  5592. this.settings[current.id] = value;
  5593. };
  5594. if (Object.keys(this.strings).length && this.strings.settings && this.strings.settings[current.id]) {
  5595. const {name, note} = this.strings.settings[current.id];
  5596. current.name = name;
  5597. current.note = note;
  5598. }
  5599. list.push(this.buildSetting(current));
  5600. }
  5601. else {
  5602. list.push(buildGroup(current));
  5603. }
  5604. }
  5605. return new _ui_settings__WEBPACK_IMPORTED_MODULE_7__["SettingPanel"](this.saveSettings.bind(this), ...list);
  5606. }
  5607. };
  5608. });
  5609. /***/ }),
  5610. /***/ "./src/structs/screen.js":
  5611. /*!*******************************!*\
  5612. !*** ./src/structs/screen.js ***!
  5613. \*******************************/
  5614. /*! exports provided: default */
  5615. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  5616. "use strict";
  5617. __webpack_require__.r(__webpack_exports__);
  5618. /**
  5619. * Representation of the screen such as width and height.
  5620. */
  5621. class Screen {
  5622. /** Document/window width */
  5623. static get width() {return Math.max(document.documentElement.clientWidth, window.innerWidth || 0);}
  5624. /** Document/window height */
  5625. static get height() {return Math.max(document.documentElement.clientHeight, window.innerHeight || 0);}
  5626. }
  5627. /* harmony default export */ __webpack_exports__["default"] = (Screen);
  5628. /***/ }),
  5629. /***/ "./src/structs/structs.js":
  5630. /*!********************************!*\
  5631. !*** ./src/structs/structs.js ***!
  5632. \********************************/
  5633. /*! exports provided: List, Screen, Selector, ClassName, DOMObserver, InsufficientPermissions, User, GuildMember, Role, Emoji, Guild, Channel, PermissionOverwrite, RolePermissionOverwrite, MemberPermissionOverwrite, GuildChannel, GuildTextChannel, GuildVoiceChannel, ChannelCategory, PrivateChannel, DirectMessageChannel, GroupChannel, Reaction, Embed, Message, DefaultMessage, RecipientAddMessage, RecipientRemoveMessage, CallMessage, GroupChannelNameChangeMessage, GroupChannelIconChangeMessage, MessagePinnedMessage, GuildMemberJoinMessage, UserSettings, Plugin, Listenable */
  5634. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  5635. "use strict";
  5636. __webpack_require__.r(__webpack_exports__);
  5637. /* harmony import */ var _list__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./list */ "./src/structs/list.js");
  5638. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "List", function() { return _list__WEBPACK_IMPORTED_MODULE_0__["default"]; });
  5639. /* harmony import */ var _screen__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./screen */ "./src/structs/screen.js");
  5640. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Screen", function() { return _screen__WEBPACK_IMPORTED_MODULE_1__["default"]; });
  5641. /* harmony import */ var _dom_selector__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./dom/selector */ "./src/structs/dom/selector.js");
  5642. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Selector", function() { return _dom_selector__WEBPACK_IMPORTED_MODULE_2__["default"]; });
  5643. /* harmony import */ var _dom_classname__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./dom/classname */ "./src/structs/dom/classname.js");
  5644. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ClassName", function() { return _dom_classname__WEBPACK_IMPORTED_MODULE_3__["default"]; });
  5645. /* harmony import */ var _dom_observer__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./dom/observer */ "./src/structs/dom/observer.js");
  5646. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "DOMObserver", function() { return _dom_observer__WEBPACK_IMPORTED_MODULE_4__["default"]; });
  5647. /* harmony import */ var _errors_permissionserror__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./errors/permissionserror */ "./src/structs/errors/permissionserror.js");
  5648. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "InsufficientPermissions", function() { return _errors_permissionserror__WEBPACK_IMPORTED_MODULE_5__["InsufficientPermissions"]; });
  5649. /* harmony import */ var _discord_user__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./discord/user */ "./src/structs/discord/user.js");
  5650. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "User", function() { return _discord_user__WEBPACK_IMPORTED_MODULE_6__["User"]; });
  5651. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GuildMember", function() { return _discord_user__WEBPACK_IMPORTED_MODULE_6__["GuildMember"]; });
  5652. /* harmony import */ var _discord_guild__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./discord/guild */ "./src/structs/discord/guild.js");
  5653. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Role", function() { return _discord_guild__WEBPACK_IMPORTED_MODULE_7__["Role"]; });
  5654. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Emoji", function() { return _discord_guild__WEBPACK_IMPORTED_MODULE_7__["Emoji"]; });
  5655. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Guild", function() { return _discord_guild__WEBPACK_IMPORTED_MODULE_7__["Guild"]; });
  5656. /* harmony import */ var _discord_channel__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./discord/channel */ "./src/structs/discord/channel.js");
  5657. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Channel", function() { return _discord_channel__WEBPACK_IMPORTED_MODULE_8__["Channel"]; });
  5658. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "PermissionOverwrite", function() { return _discord_channel__WEBPACK_IMPORTED_MODULE_8__["PermissionOverwrite"]; });
  5659. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "RolePermissionOverwrite", function() { return _discord_channel__WEBPACK_IMPORTED_MODULE_8__["RolePermissionOverwrite"]; });
  5660. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "MemberPermissionOverwrite", function() { return _discord_channel__WEBPACK_IMPORTED_MODULE_8__["MemberPermissionOverwrite"]; });
  5661. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GuildChannel", function() { return _discord_channel__WEBPACK_IMPORTED_MODULE_8__["GuildChannel"]; });
  5662. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GuildTextChannel", function() { return _discord_channel__WEBPACK_IMPORTED_MODULE_8__["GuildTextChannel"]; });
  5663. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GuildVoiceChannel", function() { return _discord_channel__WEBPACK_IMPORTED_MODULE_8__["GuildVoiceChannel"]; });
  5664. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ChannelCategory", function() { return _discord_channel__WEBPACK_IMPORTED_MODULE_8__["ChannelCategory"]; });
  5665. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "PrivateChannel", function() { return _discord_channel__WEBPACK_IMPORTED_MODULE_8__["PrivateChannel"]; });
  5666. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "DirectMessageChannel", function() { return _discord_channel__WEBPACK_IMPORTED_MODULE_8__["DirectMessageChannel"]; });
  5667. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GroupChannel", function() { return _discord_channel__WEBPACK_IMPORTED_MODULE_8__["GroupChannel"]; });
  5668. /* harmony import */ var _discord_message__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./discord/message */ "./src/structs/discord/message.js");
  5669. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Reaction", function() { return _discord_message__WEBPACK_IMPORTED_MODULE_9__["Reaction"]; });
  5670. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Embed", function() { return _discord_message__WEBPACK_IMPORTED_MODULE_9__["Embed"]; });
  5671. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Message", function() { return _discord_message__WEBPACK_IMPORTED_MODULE_9__["Message"]; });
  5672. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "DefaultMessage", function() { return _discord_message__WEBPACK_IMPORTED_MODULE_9__["DefaultMessage"]; });
  5673. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "RecipientAddMessage", function() { return _discord_message__WEBPACK_IMPORTED_MODULE_9__["RecipientAddMessage"]; });
  5674. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "RecipientRemoveMessage", function() { return _discord_message__WEBPACK_IMPORTED_MODULE_9__["RecipientRemoveMessage"]; });
  5675. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "CallMessage", function() { return _discord_message__WEBPACK_IMPORTED_MODULE_9__["CallMessage"]; });
  5676. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GroupChannelNameChangeMessage", function() { return _discord_message__WEBPACK_IMPORTED_MODULE_9__["GroupChannelNameChangeMessage"]; });
  5677. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GroupChannelIconChangeMessage", function() { return _discord_message__WEBPACK_IMPORTED_MODULE_9__["GroupChannelIconChangeMessage"]; });
  5678. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "MessagePinnedMessage", function() { return _discord_message__WEBPACK_IMPORTED_MODULE_9__["MessagePinnedMessage"]; });
  5679. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GuildMemberJoinMessage", function() { return _discord_message__WEBPACK_IMPORTED_MODULE_9__["GuildMemberJoinMessage"]; });
  5680. /* harmony import */ var _discord_usersettings__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./discord/usersettings */ "./src/structs/discord/usersettings.js");
  5681. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "UserSettings", function() { return _discord_usersettings__WEBPACK_IMPORTED_MODULE_10__["UserSettings"]; });
  5682. /* harmony import */ var _plugin__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./plugin */ "./src/structs/plugin.js");
  5683. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Plugin", function() { return _plugin__WEBPACK_IMPORTED_MODULE_11__["default"]; });
  5684. /* harmony import */ var _listenable__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./listenable */ "./src/structs/listenable.js");
  5685. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Listenable", function() { return _listenable__WEBPACK_IMPORTED_MODULE_12__["default"]; });
  5686. /***/ }),
  5687. /***/ "./src/styles/settings.css":
  5688. /*!*********************************!*\
  5689. !*** ./src/styles/settings.css ***!
  5690. \*********************************/
  5691. /*! exports provided: default */
  5692. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  5693. "use strict";
  5694. __webpack_require__.r(__webpack_exports__);
  5695. /* harmony default export */ __webpack_exports__["default"] = (".plugin-input-group {\r\n margin-top: 5px;\r\n}\r\n\r\n.plugin-input-group .button-collapse {\r\n background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxOS4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iQ2FscXVlXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB2aWV3Qm94PSItOTUwIDUzMiAxOCAxOCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAtOTUwIDUzMiAxOCAxODsiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4NCgkuc3Qwe2ZpbGw6bm9uZTt9DQoJLnN0MXtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjEuNTtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9DQo8L3N0eWxlPg0KPHBhdGggY2xhc3M9InN0MCIgZD0iTS05MzIsNTMydjE4aC0xOHYtMThILTkzMnoiLz4NCjxwb2x5bGluZSBjbGFzcz0ic3QxIiBwb2ludHM9Ii05MzYuNiw1MzguOCAtOTQxLDU0My4yIC05NDUuNCw1MzguOCAiLz4NCjwvc3ZnPg0K);\r\n height: 16px;\r\n width: 16px;\r\n display: inline-block;\r\n vertical-align: bottom;\r\n transition: transform .3s ease;\r\n transform: rotate(0);\r\n}\r\n\r\n.plugin-input-group .button-collapse.collapsed {\r\n transition: transform .3s ease;\r\n transform: rotate(-90deg);\r\n}\r\n\r\n.plugin-input-group h2 {\r\n font-size: 14px;\r\n}\r\n\r\n.plugin-input-group .plugin-input-group h2 {\r\n margin-left: 16px;\r\n}\r\n\r\n.plugin-inputs {\r\n height: auto;\r\n overflow: hidden;\r\n transition: height 300ms cubic-bezier(0.47, 0, 0.745, 0.715);\r\n}\r\n\r\n.plugin-inputs.collapsed {\r\n height: 0px;\r\n}\r\n\r\n.file-input {\r\n\r\n}\r\n\r\n.file-input::-webkit-file-upload-button {\r\n color: white;\r\n background: #7289DA;\r\n outline: 0;\r\n border: 0;\r\n padding: 10px;\r\n vertical-align: top;\r\n margin-top: -10px;\r\n margin-left: -10px;\r\n border-radius: 3px 0 0 3px;\r\n font-size: 14px;\r\n font-weight: 500;\r\n font-family: Whitney,Helvetica Neue,Helvetica,Arial,sans-serif;\r\n cursor: pointer;\r\n}\r\n\r\n.color-input {\r\n background: none;\r\n padding: 0;\r\n border: none;\r\n}\r\n\r\n.color-input:hover {\r\n opacity: 0.8;\r\n}\r\n");
  5696. /***/ }),
  5697. /***/ "./src/styles/toasts.css":
  5698. /*!*******************************!*\
  5699. !*** ./src/styles/toasts.css ***!
  5700. \*******************************/
  5701. /*! exports provided: default */
  5702. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  5703. "use strict";
  5704. __webpack_require__.r(__webpack_exports__);
  5705. /* harmony default export */ __webpack_exports__["default"] = (".toasts {\r\n position: fixed;\r\n display: flex;\r\n top: 0;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: flex-end;\r\n pointer-events: none;\r\n z-index: 4000;\r\n}\r\n\r\n@keyframes toast-up {\r\n from {\r\n transform: translateY(0);\r\n opacity: 0;\r\n }\r\n}\r\n\r\n.toast {\r\n animation: toast-up 300ms ease;\r\n transform: translateY(-10px);\r\n background: #36393F;\r\n padding: 10px;\r\n border-radius: 5px;\r\n box-shadow: 0 0 0 1px rgba(32,34,37,.6), 0 2px 10px 0 rgba(0,0,0,.2);\r\n font-weight: 500;\r\n color: #fff;\r\n user-select: text;\r\n font-size: 14px;\r\n opacity: 1;\r\n margin-top: 10px;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n}\r\n\r\n@keyframes toast-down {\r\n to {\r\n transform: translateY(0px);\r\n opacity: 0;\r\n }\r\n}\r\n\r\n.toast.closing {\r\n animation: toast-down 200ms ease;\r\n animation-fill-mode: forwards;\r\n opacity: 1;\r\n transform: translateY(-10px);\r\n}\r\n\r\n.toast.toast-info {\r\n background-color: #4a90e2;\r\n}\r\n\r\n.toast.toast-success {\r\n background-color: #43b581;\r\n}\r\n\r\n.toast.toast-danger,\r\n.toast.toast-error {\r\n background-color: #f04747;\r\n}\r\n\r\n.toast.toast-warning,\r\n.toast.toast-warn {\r\n background-color: #FFA600;\r\n}\r\n\r\n.toast-icon {\r\n margin-right: 5px;\r\n fill: white;\r\n border-radius: 50%;\r\n overflow: hidden;\r\n height: 20px;\r\n width: 20px;\r\n}\r\n\r\n.toast-text {\r\n line-height: 20px;\r\n}");
  5706. /***/ }),
  5707. /***/ "./src/styles/updates.css":
  5708. /*!********************************!*\
  5709. !*** ./src/styles/updates.css ***!
  5710. \********************************/
  5711. /*! exports provided: default */
  5712. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  5713. "use strict";
  5714. __webpack_require__.r(__webpack_exports__);
  5715. /* harmony default export */ __webpack_exports__["default"] = ("#pluginNotice {\r\n -webkit-app-region: drag;\r\n border-radius: 0;\r\n overflow: hidden;\r\n height: 36px;\r\n animation: open-updates 400ms ease;\r\n}\r\n\r\n@keyframes open-updates {\r\n from { height: 0; }\r\n}\r\n\r\n#pluginNotice.closing {\r\n transition: height 400ms ease;\r\n height: 0;\r\n}\r\n\r\n#outdatedPlugins {\r\n font-weight: 700;\r\n}\r\n\r\n#outdatedPlugins>span {\r\n -webkit-app-region: no-drag;\r\n color: #fff;\r\n cursor: pointer;\r\n}\r\n\r\n#outdatedPlugins>span:hover {\r\n text-decoration: underline;\r\n}");
  5716. /***/ }),
  5717. /***/ "./src/ui/contextmenu.js":
  5718. /*!*******************************!*\
  5719. !*** ./src/ui/contextmenu.js ***!
  5720. \*******************************/
  5721. /*! exports provided: updateDiscordMenu, Menu, ItemGroup, MenuItem, TextItem, ImageItem, SubMenuItem, ToggleItem */
  5722. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  5723. "use strict";
  5724. __webpack_require__.r(__webpack_exports__);
  5725. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "updateDiscordMenu", function() { return updateDiscordMenu; });
  5726. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Menu", function() { return Menu; });
  5727. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ItemGroup", function() { return ItemGroup; });
  5728. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MenuItem", function() { return MenuItem; });
  5729. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TextItem", function() { return TextItem; });
  5730. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ImageItem", function() { return ImageItem; });
  5731. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SubMenuItem", function() { return SubMenuItem; });
  5732. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ToggleItem", function() { return ToggleItem; });
  5733. /* harmony import */ var _modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../modules/discordclasses */ "./src/modules/discordclasses.js");
  5734. /* harmony import */ var _modules_discordselectors__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../modules/discordselectors */ "./src/modules/discordselectors.js");
  5735. /* harmony import */ var _modules_reacttools__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../modules/reacttools */ "./src/modules/reacttools.js");
  5736. /* harmony import */ var _modules_discordmodules__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../modules/discordmodules */ "./src/modules/discordmodules.js");
  5737. /* harmony import */ var _modules_domtools__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../modules/domtools */ "./src/modules/domtools.js");
  5738. /* harmony import */ var _structs_screen__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../structs/screen */ "./src/structs/screen.js");
  5739. /**
  5740. * Self-made context menus that emulate Discord's own context menus.
  5741. * @module ContextMenu
  5742. * @version 0.1.0
  5743. * @deprecated 12/3/2020 in favor of DiscordContextMenu
  5744. */
  5745. /**
  5746. * Updates the location of a Discord menu, especially useful when adding items to the menu via DOM.
  5747. * @param {HTMLElement|jQuery} menu - The original discord menu
  5748. */
  5749. function updateDiscordMenu(menu) {
  5750. const menuNode = _modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].resolveElement(menu);
  5751. if (!(menuNode instanceof Element)) return;
  5752. const updateHeight = _modules_reacttools__WEBPACK_IMPORTED_MODULE_2__["default"].getReactProperty(menuNode, "return.return.return.stateNode.updatePosition");
  5753. if (updateHeight) updateHeight();
  5754. }
  5755. /** Main menu class for creating custom context menus. */
  5756. class Menu {
  5757. /**
  5758. *
  5759. * @param {boolean} [scroll=false] - should this menu be a scrolling menu (usually only used for submenus)
  5760. */
  5761. constructor(submenu = false, scroll = false) {
  5762. this.theme = _modules_discordmodules__WEBPACK_IMPORTED_MODULE_3__["default"].UserSettingsStore.theme == "dark" ? "theme-dark" : "theme-light";
  5763. this.isSubmenu = submenu;
  5764. this.element = _modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement(`<div class="${_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.contextMenu} plugin-context-menu ${this.theme}"></div>`);
  5765. this.scroll = scroll;
  5766. if (!scroll) return;
  5767. this.scroller = _modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement(`<div class="${_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].Scrollers.scroller} ${_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.scroller}"></div>`);
  5768. this.scrollerWrap = _modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement(`<div class="${_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].Scrollers.scrollerWrap} ${_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].Scrollers.scrollerThemed} ${_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].Scrollers.themeGhostHairline}"></div>`);
  5769. this.scrollerWrap.append(this.scroller);
  5770. this.element.append(this.scrollerWrap);
  5771. }
  5772. /**
  5773. * Adds an item group to the menu. The group should already be populated.
  5774. * @param {module:ContextMenu.ItemGroup} contextGroup - group to add to the menu
  5775. * @returns {module:ContextMenu.Menu} returns self for chaining
  5776. */
  5777. addGroup(contextGroup) {
  5778. if (this.scroll) this.scroller.append(contextGroup.getElement());
  5779. else this.element.append(contextGroup.getElement());
  5780. return this;
  5781. }
  5782. /**
  5783. * Adds items to the context menu directly. It is recommended to add to a group and use
  5784. * {@link module:ContextMenu.Menu.addGroup} instead to behave as natively as possible.
  5785. * @param {module:ContextMenu.MenuItem} contextItems - list of items to add to the context menu
  5786. * @returns {module:ContextMenu.Menu} returns self for chaining
  5787. */
  5788. addItems(...contextItems) {
  5789. for (let i = 0; i < contextItems.length; i++) {
  5790. if (this.scroll) this.scroller.append(contextItems[i].getElement());
  5791. else this.element.append(contextItems[i].getElement());
  5792. }
  5793. return this;
  5794. }
  5795. /**
  5796. * Shows the menu at a specific x and y position. This generally comes from the
  5797. * pointer position on a right click event.
  5798. * @param {number} x - x coordinate for the menu to show at
  5799. * @param {number} y - y coordinate for the menu to show at
  5800. */
  5801. show(x, y) {
  5802. const mouseX = x;
  5803. const mouseY = y;
  5804. const parents = this.element.closest(this.parentSelector);
  5805. const depth = parents.length;
  5806. // if (depth == 0) {
  5807. const layer = _modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement(`<div class="${_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].TooltipLayers.layer}"></div>`);
  5808. let elementToAdd = this.element;
  5809. if (this.isSubmenu) {
  5810. const submenu = _modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement(`<div class="${_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.subMenuContext}"></div>`);
  5811. submenu.append(this.element);
  5812. elementToAdd = submenu;
  5813. }
  5814. layer.append(elementToAdd);
  5815. layer.appendTo(_modules_discordselectors__WEBPACK_IMPORTED_MODULE_1__["default"].Popouts.popouts.sibling(_modules_discordselectors__WEBPACK_IMPORTED_MODULE_1__["default"].TooltipLayers.layerContainer).toString());
  5816. // }
  5817. this.element.css("top", mouseY + "px").css("left", mouseX + "px");
  5818. if (mouseY + this.element.outerHeight() >= _structs_screen__WEBPACK_IMPORTED_MODULE_5__["default"].height) {
  5819. this.element.addClass("invertY").addClass(_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.invertY);
  5820. this.element.css("top", `${mouseY - this.element.outerHeight()}px`);
  5821. if (depth > 0) this.element.css("top", `${(mouseY + this.element.parent().outerHeight()) - this.element.outerHeight()}px`);
  5822. }
  5823. if (this.element.offset().left + this.element.outerWidth() >= _structs_screen__WEBPACK_IMPORTED_MODULE_5__["default"].width) {
  5824. this.element.addClass("invertX");
  5825. this.element.css("left", `${mouseX - this.element.outerWidth()}px`);
  5826. }
  5827. if (this.element.offset().left + 2 * this.element.outerWidth() >= _structs_screen__WEBPACK_IMPORTED_MODULE_5__["default"].width) {
  5828. this.element.addClass(_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.invertChildX);
  5829. }
  5830. if (depth !== 0) return;
  5831. _modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].on(document, "mousedown.zctx", (e) => {if (!this.element.contains(e.target) && !this.element.isSameNode(e.target)) this.removeMenu();});
  5832. _modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].on(document, "click.zctx", (e) => {if (this.element.contains(e.target)) this.removeMenu();});
  5833. _modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].on(document, "keyup.zctx", (e) => {if (e.keyCode === 27) this.removeMenu();});
  5834. }
  5835. /** Allows you to remove the menu. */
  5836. removeMenu() {
  5837. this.element.closest(_modules_discordselectors__WEBPACK_IMPORTED_MODULE_1__["default"].TooltipLayers.layer.toString())[0].remove();
  5838. const childs = this.element.findAll(this.parentSelector);
  5839. if (childs) childs.forEach(c => c.remove());
  5840. _modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].off(document, ".zctx");
  5841. }
  5842. /**
  5843. * Used to attach a menu to a menu item. This is how to create a submenu.
  5844. * If using {@link module:ContextMenu.SubMenuItem} then you do not need
  5845. * to call this function as it is done automatically. If you want to attach
  5846. * a submenu to an existing Discord context menu, then you should use this
  5847. * method.
  5848. * @param {(HTMLElement|jQuery)} menuItem - item to attach to
  5849. */
  5850. attachTo(menuItem) {
  5851. this.menuItem = menuItem;
  5852. menuItem.addEventListener("mouseenter", () => {
  5853. // this.element.appendTo(DiscordSelectors.Popouts.popouts.sibling(DiscordSelectors.TooltipLayers.layerContainer).toString());
  5854. // const left = this.element.parents(this.parentSelector)[0].css("left");
  5855. // console.log(parseInt(menuItem.offset().left), parseInt(menuItem.offset().top));
  5856. this.show(parseInt(menuItem.offset().right), parseInt(menuItem.offset().top));
  5857. });
  5858. menuItem.addEventListener("mouseleave", () => {this.element.closest(_modules_discordselectors__WEBPACK_IMPORTED_MODULE_1__["default"].TooltipLayers.layer.toString())[0].remove();});
  5859. }
  5860. get parentSelector() {return this.element.closest(".plugin-context-menu").length > this.element.closest(_modules_discordselectors__WEBPACK_IMPORTED_MODULE_1__["default"].ContextMenu.contextMenu).length ? ".plugin-context-menu" : _modules_discordselectors__WEBPACK_IMPORTED_MODULE_1__["default"].ContextMenu.contextMenu;}
  5861. }
  5862. /** Class that represents a group of menu items. */
  5863. class ItemGroup {
  5864. /** Creates an item group. */
  5865. constructor() {
  5866. this.element = _modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement(`<div class="${_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.itemGroup}"></div>`);
  5867. }
  5868. /**
  5869. * This is the method of adding menu items to a menu group.
  5870. * @param {module:ContextMenu.MenuItem} contextItems - list of context menu items to add to this group
  5871. * @returns {module:ContextMenu.ItemGroup} returns self for chaining
  5872. */
  5873. addItems(...contextItems) {
  5874. for (let i = 0; i < contextItems.length; i++) {
  5875. this.element.append(contextItems[i].getElement());
  5876. }
  5877. return this;
  5878. }
  5879. /** @returns {HTMLElement} returns the DOM node for the group */
  5880. getElement() {return this.element;}
  5881. }
  5882. /**
  5883. * Fires when the attached menu item it clicked.
  5884. * @param {MouseEvent} event - the mouse event from clicking the item
  5885. * @callback module:ContextMenu~clickEvent
  5886. */
  5887. /**
  5888. * Fires when the checkbox item changes state.
  5889. * @param {boolean} isChecked - if the checkbox is now checked
  5890. * @callback module:ContextMenu~onChange
  5891. */
  5892. /** Base class for all other menu items. */
  5893. class MenuItem {
  5894. /**
  5895. * @param {string} label - label to show on the menu item
  5896. * @param {object} options - additional options for the item
  5897. * @param {boolean} [options.danger=false] - should the item show as danger
  5898. * @param {module:ContextMenu~clickEvent} [options.callback] - callback for when it is clicked
  5899. */
  5900. constructor(label, options = {}) {
  5901. const {danger = false, callback} = options;
  5902. this.element = _modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement(`<div class="${_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.item}"></div>`);
  5903. this.label = label;
  5904. if (danger) this.element.addClass(_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.danger);
  5905. this.element.on("click", (event) => {
  5906. if (!Array.from(this.element.children).some(c => c.isSameNode(event.target)) && !this.element.isSameNode(event.target)) return;
  5907. if (typeof(callback) == "function") callback(event);
  5908. else event.stopPropagation();
  5909. });
  5910. }
  5911. getElement() {return this.element;}
  5912. }
  5913. /**
  5914. * Creates a text menu item that can have a hint.
  5915. * @extends module:ContextMenu.MenuItem
  5916. */
  5917. class TextItem extends MenuItem {
  5918. /**
  5919. * @param {string} label - label to show on the menu item
  5920. * @param {object} options - additional options for the item
  5921. * @param {string} [options.hint=""] - hint to show on the item (usually used for key combos)
  5922. * @param {boolean} [options.danger=false] - should the item show as danger
  5923. * @param {module:ContextMenu~clickEvent} [options.callback] - callback for when it is clicked
  5924. */
  5925. constructor(label, options = {}) {
  5926. super(label, options);
  5927. const {hint = ""} = options;
  5928. this.element.append(_modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement(`<span>${label}</span>`));
  5929. this.element.append(_modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement(`<div class="${_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.hint}">${hint}</div>`));
  5930. }
  5931. }
  5932. /**
  5933. * Creates an image menu item that can have an image.
  5934. * @extends module:ContextMenu.MenuItem
  5935. */
  5936. class ImageItem extends MenuItem {
  5937. /**
  5938. * @param {string} label - label to show on the menu item
  5939. * @param {string} imageSrc - link to the image to embed
  5940. * @param {object} options - additional options for the item
  5941. * @param {string} [options.hint=""] - hint to show on the item (usually used for key combos)
  5942. * @param {boolean} [options.danger=false] - should the item show as danger
  5943. * @param {module:ContextMenu~clickEvent} [options.callback] - callback for when it is clicked
  5944. */
  5945. constructor(label, imageSrc, options = {}) {
  5946. super(label, options);
  5947. this.element.addClass(_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.itemImage);
  5948. this.element.append(_modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement(`<div class="${_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.label}">${label}</div>`));
  5949. this.element.append(_modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement(`<img src="${imageSrc}">`));
  5950. }
  5951. }
  5952. /**
  5953. * Creates a menu item with an attached submenu.
  5954. * @extends module:ContextMenu.MenuItem
  5955. */
  5956. class SubMenuItem extends MenuItem {
  5957. /**
  5958. * @param {string} label - label to show on the menu item
  5959. * @param {module:ContextMenu.Menu} subMenu - context menu that should be attached to this item
  5960. * @param {object} options - additional options for the item
  5961. * @param {string} [options.hint=""] - hint to show on the item (usually used for key combos)
  5962. * @param {boolean} [options.danger=false] - should the item show as danger
  5963. * @param {module:ContextMenu~clickEvent} [options.callback] - callback for when it is clicked
  5964. */
  5965. constructor(label, subMenu, options = {}) {
  5966. // if (!(subMenu instanceof ContextSubMenu)) throw "subMenu must be of ContextSubMenu type.";
  5967. super(label, options);
  5968. this.element.addClass(_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.itemSubMenu);
  5969. this.element.append(_modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement(`<div class="${_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.label}">${label}</div>`));
  5970. this.element.append(_modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement(`<svg class="caret-UIZBlm da-caret" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M16.59 8.59004L12 13.17L7.41 8.59004L6 10L12 16L18 10L16.59 8.59004Z"></path></svg>`));
  5971. this.subMenu = subMenu;
  5972. this.subMenu.attachTo(this.getElement());
  5973. }
  5974. }
  5975. /**
  5976. * Creates a menu item with a checkbox.
  5977. * @extends module:ContextMenu.MenuItem
  5978. */
  5979. class ToggleItem extends MenuItem {
  5980. /**
  5981. * @param {string} label - label to show on the menu item
  5982. * @param {boolean} checked - should the item start out checked
  5983. * @param {object} options - additional options for the item
  5984. * @param {string} [options.hint=""] - hint to show on the item (usually used for key combos)
  5985. * @param {boolean} [options.danger=false] - should the item show as danger
  5986. * @param {module:ContextMenu~onChange} [options.callback] - callback for when the checkbox changes
  5987. */
  5988. constructor(label, checked, options = {}) {
  5989. const {callback: onChange} = options;
  5990. if (options.callback) delete options.callback;
  5991. super(label, options);
  5992. this.element.addClass(_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.itemToggle);
  5993. this.element.append(_modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement(`<div class="${_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.label}">${label}</div>`));
  5994. this.checkbox = _modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement(`<div class="checkbox ${_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].Checkbox.checkbox} ${_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.checkbox}" role="button"></div>`);
  5995. this.checkbox.append(_modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement(`<div class="checkbox-inner ${_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].Checkbox.checkboxInner}"></div>`));
  5996. this.checkbox.append(_modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement("<span>"));
  5997. this.input = _modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement(`<input type="checkbox" class="${_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].Checkbox.checkboxElement}">`);
  5998. this.input.checked = checked;
  5999. this.checkbox.find(".checkbox-inner").append(this.input);
  6000. this.checkbox.find(".checkbox-inner").append(_modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement("<span>"));
  6001. this.element.append(this.checkbox);
  6002. this.element.on("click", (e) => {
  6003. if (!Array.from(this.element.children).some(c => c.isSameNode(e.target)) && !this.element.isSameNode(e.target)) return;
  6004. e.stopPropagation();
  6005. this.input.checked = !this.input.checked;
  6006. if (typeof(onChange) == "function") onChange(this.input.checked);
  6007. });
  6008. }
  6009. }
  6010. /***/ }),
  6011. /***/ "./src/ui/discordcontextmenu.js":
  6012. /*!**************************************!*\
  6013. !*** ./src/ui/discordcontextmenu.js ***!
  6014. \**************************************/
  6015. /*! exports provided: default */
  6016. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  6017. "use strict";
  6018. __webpack_require__.r(__webpack_exports__);
  6019. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return DiscordContextMenu; });
  6020. /* harmony import */ var _modules_discordmodules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../modules/discordmodules */ "./src/modules/discordmodules.js");
  6021. /* harmony import */ var _modules_webpackmodules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../modules/webpackmodules */ "./src/modules/webpackmodules.js");
  6022. /* harmony import */ var _modules_reacttools__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../modules/reacttools */ "./src/modules/reacttools.js");
  6023. /* harmony import */ var _modules_patcher__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../modules/patcher */ "./src/modules/patcher.js");
  6024. /* harmony import */ var _modules_utilities__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../modules/utilities */ "./src/modules/utilities.js");
  6025. /* harmony import */ var _modules_discordclasses__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../modules/discordclasses */ "./src/modules/discordclasses.js");
  6026. /* harmony import */ var _modules_domtools__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../modules/domtools */ "./src/modules/domtools.js");
  6027. /* harmony import */ var _modules_logger__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../modules/logger */ "./src/modules/logger.js");
  6028. // d = e.label,
  6029. // f = e.icon,
  6030. // h = e.imageUrl,
  6031. // v = e.hint,
  6032. // m = e.subtext,
  6033. // g = e.hasSubmenu,
  6034. // y = e.disabled,
  6035. // E = e.isFocused,
  6036. // S = e.menuItemProps,
  6037. // T = e.action,
  6038. // b = e.onClose,
  6039. const React = _modules_discordmodules__WEBPACK_IMPORTED_MODULE_0__["default"].React;
  6040. const ContextMenuActions = _modules_discordmodules__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenuActions;
  6041. const ce = React.createElement;
  6042. const ContextMenu = _modules_webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("MenuRadioItem", "MenuItem");
  6043. /**
  6044. * Fires when the item is clicked.
  6045. * @param {MouseEvent} event - The event generated on click
  6046. * @callback module:DiscordContextMenu~MenuItemOnClick
  6047. */
  6048. /**
  6049. * @interface
  6050. * @name module:DiscordContextMenu~MenuItem
  6051. * @description
  6052. * This is the generic context menu item component. It is very extensible and will adapt
  6053. * it's type depending on the props.
  6054. *
  6055. * Note: The item ID should be unique to this item across the entire menu. If no `id` is
  6056. * provided, the system will use the `label`. Plugins should ensure there are no `label`
  6057. * conflicts if they do not wish to provide `id`. `label` conflicts (when not using
  6058. * unique `id`s) can cause multiple items to be hovered at once.
  6059. *
  6060. * @param {object} props - props to pass to the react renderer
  6061. * @param {string} props.label - label to show on the menu item
  6062. * @param {string} [props.id] - specific id used for this item
  6063. * @param {string} [props.hint] - hint to show on the right hand side (usually keyboard combo)
  6064. * @param {string} [props.subtext] - description to show underneath
  6065. * @param {string} [props.image] - link to image to show on the side
  6066. * @param {function} [props.icon] - react component to render on the side
  6067. * @param {function} [props.render] - render function for custom rendering the menu item
  6068. * @param {module:DiscordContextMenu~MenuItemOnClick} [props.action] - function to perform on click
  6069. * @param {module:DiscordContextMenu~MenuItemOnClick} [props.onClick] - function to perform on click (alias of `action`)
  6070. * @param {function} [props.onClose] - function to run when this is closed
  6071. * @param {boolean} [props.danger=false] - should the item show as danger (red)
  6072. * @param {boolean} [props.disabled=false] - should the item be disabled/unclickable
  6073. *
  6074. * @param {object} [props.style] - allows you to add custom styles
  6075. * @param {boolean} [props.closeOnClick] - allows you to prevent closing on click
  6076. */
  6077. /**
  6078. * @interface
  6079. * @name module:DiscordContextMenu~MenuToggleItem
  6080. * @extends module:DiscordContextMenu~MenuItem
  6081. * @description
  6082. * This item is used for creating checkboxes in menus. Properties shown here are additional
  6083. * to those of the main MenuItem {@link module:DiscordContextMenu~MenuItem}
  6084. *
  6085. *
  6086. * @param {boolean} [props.checked=false] - should the checkbox be checked
  6087. * @param {boolean} [props.active=false] - alias of `checked`
  6088. */
  6089. /**
  6090. * @interface
  6091. * @name module:DiscordContextMenu~MenuRadioItem
  6092. * @extends module:DiscordContextMenu~MenuItem
  6093. * @description
  6094. * This item is used for creating radio selections in menus. Properties shown here are additional
  6095. * to those of the main MenuItem {@link module:DiscordContextMenu~MenuItem}
  6096. *
  6097. * Note: for the `forceUpdate` option... Without this enabled, you will manually need to
  6098. * manage the state for the functional component. If you do not the toggle will appear
  6099. * to not update. @see {@link https://reactjs.org/docs/hooks-reference.html#usestate}
  6100. *
  6101. * @param {boolean} [props.checked=false] - should the checkbox be checked
  6102. * @param {boolean} [props.active=false] - alias of `checked`
  6103. * @param {boolean} [props.forceUpdate=true] - should the menu be force-updated after click
  6104. */
  6105. /**
  6106. * @interface
  6107. * @name module:DiscordContextMenu~SubMenuItem
  6108. * @extends module:DiscordContextMenu~MenuItem
  6109. * @description
  6110. * This item is used for creating nested submenus. Properties shown here are additional
  6111. * to those of the main MenuItem {@link module:DiscordContextMenu~MenuItem}
  6112. *
  6113. * @param {Array<object>} [props.render] - array of items to render in the submenu
  6114. * @param {Array<object>} [props.items] - alias of `render`
  6115. * @param {Array<object>} [props.children] - Already rendered elements
  6116. */
  6117. /**
  6118. * @interface
  6119. * @name module:DiscordContextMenu~MenuControlItem
  6120. * @extends module:DiscordContextMenu~MenuItem
  6121. * @description
  6122. * This item is used for adding custom controls like sliders to the context menu.
  6123. * Properties shown here are additional to those of the main MenuItem {@link module:DiscordContextMenu~MenuItem}
  6124. *
  6125. * @param {function} [props.control] - control function that renders the component
  6126. */
  6127. /**
  6128. * A utility for building and rendering Discord's own menus.
  6129. * @module DiscordContextMenu
  6130. * @version 0.0.1
  6131. */
  6132. class DiscordContextMenu {
  6133. /**
  6134. * Builds a single menu item. The only prop shown here is the type, the rest should
  6135. * match the actual component being built. View those to see what options exist
  6136. * for each, they often have less in common than you might think. See {@link module:DiscordContextMenu.MenuItem}
  6137. * for the majority of props commonly available. Check the documentation for the
  6138. * rest of the components.
  6139. *
  6140. * @param {object} props - props used to build the item
  6141. * @param {string} [props.type="text"] - type of the item, options: text, submenu, toggle, radio, custom, separator
  6142. * @returns {object} the created component
  6143. *
  6144. * @see {@link module:DiscordContextMenu~MenuItem}
  6145. * @see {@link module:DiscordContextMenu~MenuToggleItem}
  6146. * @see {@link module:DiscordContextMenu~MenuRadioItem}
  6147. * @see {@link module:DiscordContextMenu~SubMenuItem}
  6148. * @see {@link module:DiscordContextMenu~MenuControlItem}
  6149. *
  6150. * @example
  6151. * // Creates a single menu item that prints "MENU ITEM" on click
  6152. * DiscordContextMenu.buildMenuItem({
  6153. * label: "Menu Item",
  6154. * action: () => {console.log("MENU ITEM");}
  6155. * });
  6156. *
  6157. * @example
  6158. * // Creates a single toggle item that starts unchecked
  6159. * // and print the new value on every toggle
  6160. * DiscordContextMenu.buildMenuItem({
  6161. * type: "toggle",
  6162. * label: "Item Toggle",
  6163. * checked: false,
  6164. * action: (newValue) => {console.log(newValue);}
  6165. * });
  6166. */
  6167. static buildMenuItem(props) {
  6168. const {type} = props;
  6169. if (type === "separator") return ce(ContextMenu.MenuSeparator);
  6170. let Component = ContextMenu.MenuItem;
  6171. if (type === "submenu") {
  6172. if (!props.children) props.children = this.buildMenuChildren(props.render || props.items);
  6173. }
  6174. else if (type === "toggle" || type === "radio") {
  6175. Component = type === "toggle" ? ContextMenu.MenuCheckboxItem : ContextMenu.MenuRadioItem;
  6176. if (props.active) props.checked = props.active;
  6177. }
  6178. else if (type === "control") {
  6179. Component = ContextMenu.MenuControlItem;
  6180. }
  6181. if (!props.id) props.id = `${_modules_domtools__WEBPACK_IMPORTED_MODULE_6__["default"].escapeID(props.label)}`;
  6182. if (props.danger) props.color = "colorDanger";
  6183. if (props.onClick && !props.action) props.action = props.onClick;
  6184. props.extended = true;
  6185. return ce(Component, props);
  6186. }
  6187. /**
  6188. * Creates the all the items **and groups** of a context menu recursively.
  6189. * There is no hard limit to the number of groups within groups or number
  6190. * of items in a menu.
  6191. * @param {Array<object>} setup - array of item props used to build items. See {@link module:DiscordContextMenu.buildMenuItem}
  6192. * @returns {Array<object>} array of the created component
  6193. *
  6194. * @example
  6195. * // Creates a single item group item with a toggle item
  6196. * DiscordContextMenu.buildMenuChildren([{
  6197. * type: "group",
  6198. * items: [{
  6199. * type: "toggle",
  6200. * label: "Item Toggle",
  6201. * active: false,
  6202. * action: (newValue) => {console.log(newValue);}
  6203. * }]
  6204. * }]);
  6205. *
  6206. * @example
  6207. * // Creates two item groups with a single toggle item each
  6208. * DiscordContextMenu.buildMenuChildren([{
  6209. * type: "group",
  6210. * items: [{
  6211. * type: "toggle",
  6212. * label: "Item Toggle",
  6213. * active: false,
  6214. * action: (newValue) => {
  6215. * console.log(newValue);
  6216. * }
  6217. * }]
  6218. * }, {
  6219. * type: "group",
  6220. * items: [{
  6221. * type: "toggle",
  6222. * label: "Item Toggle",
  6223. * active: false,
  6224. * action: (newValue) => {
  6225. * console.log(newValue);
  6226. * }
  6227. * }]
  6228. * }]);
  6229. */
  6230. static buildMenuChildren(setup) {
  6231. const mapper = s => {
  6232. if (s.type === "group") return buildGroup(s);
  6233. return this.buildMenuItem(s);
  6234. };
  6235. const buildGroup = function(group) {
  6236. const items = group.items.map(mapper).filter(i => i);
  6237. return ce(ContextMenu.MenuGroup, null, items);
  6238. };
  6239. return setup.map(mapper).filter(i => i);
  6240. }
  6241. /**
  6242. * Creates the menu *component* including the wrapping `ContextMenu`.
  6243. * Calls {@link module:DiscordContextMenu.buildMenuChildren} under the covers.
  6244. * Used to call in combination with {@link module:DiscordContextMenu.openContextMenu}.
  6245. * @param {Array<object>} setup - array of item props used to build items. See {@link module:DiscordContextMenu.buildMenuChildren}
  6246. * @returns {function} the unique context menu component
  6247. */
  6248. static buildMenu(setup) {
  6249. return (props) => {return ce(ContextMenu.default, props, this.buildMenuChildren(setup));};
  6250. }
  6251. /**
  6252. *
  6253. * @param {MouseEvent} event - The context menu event. This can be emulated, requires target, and all X, Y locations.
  6254. * @param {function} menuComponent - Component to render. This can be any react component or output of {@link module:DiscordContextMenu.buildMenu}
  6255. * @param {object} config - configuration/props for the context menu
  6256. * @param {string} [config.position="right"] - default position for the menu, options: "left", "right"
  6257. * @param {string} [config.align="top"] - default alignment for the menu, options: "bottom", "top"
  6258. * @param {function} [config.onClose] - function to run when the menu is closed
  6259. * @param {boolean} [config.noBlurEvent=false] - No clue
  6260. */
  6261. static openContextMenu(event, menuComponent, config) {
  6262. return ContextMenuActions.openContextMenu(event, function(e) {
  6263. return ce(menuComponent, Object.assign({}, e, {onClose: ContextMenuActions.closeContextMenu}));
  6264. }, config);
  6265. }
  6266. /**
  6267. * Attempts to find and return a specific context menu type's module. Useful
  6268. * when patching the render of these menus.
  6269. * @param {string} type - name of the context menu type
  6270. * @returns {Promise<object>} the webpack module the menu was found in
  6271. */
  6272. static async getDiscordMenu() {
  6273. _modules_logger__WEBPACK_IMPORTED_MODULE_7__["default"].warn("DiscordContextMenu", "This function no longer applies, please update your plugin.");
  6274. // return new Promise(resolve => {
  6275. // const cancel = Patcher.after("ZeresLibrary.DiscordContextMenu", ContextMenuActions, "openContextMenu", (_, [, component]) => {
  6276. // const rendered = component();
  6277. // const menuType = rendered.props && rendered.props.type || (rendered.type && rendered.type.displayName);
  6278. // if (!menuType || typeof(menuType) != "string" || !menuType.includes(type)) return;
  6279. // cancel();
  6280. // return resolve(WebpackModules.getModule(m => m.default == rendered.type));
  6281. // });
  6282. // });
  6283. }
  6284. /**
  6285. * Calls `forceUpdate()` on all context menus it can find. Useful for
  6286. * after patching a menu.
  6287. */
  6288. static forceUpdateMenus() {
  6289. const menus = document.querySelectorAll(`.${_modules_discordclasses__WEBPACK_IMPORTED_MODULE_5__["default"].ContextMenu.menu.first}`);
  6290. for (const menu of menus) {
  6291. const stateNode = _modules_utilities__WEBPACK_IMPORTED_MODULE_4__["default"].findInTree(_modules_reacttools__WEBPACK_IMPORTED_MODULE_2__["default"].getReactInstance(menu), m=>m && m.forceUpdate && m.updatePosition, {walkable: ["return", "stateNode"]});
  6292. if (!stateNode) continue;
  6293. stateNode.forceUpdate();
  6294. stateNode.updatePosition();
  6295. }
  6296. }
  6297. static patchComponents() {
  6298. _modules_patcher__WEBPACK_IMPORTED_MODULE_3__["default"].unpatchAll("DCM");
  6299. this.patchMenuItem();
  6300. this.patchToggleItem();
  6301. }
  6302. static patchMenuItem() {
  6303. const MenuItem = _modules_webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m.default && m.default.displayName == "MenuItem");
  6304. if (!MenuItem || !MenuItem.default) return;
  6305. _modules_patcher__WEBPACK_IMPORTED_MODULE_3__["default"].after("DCM", MenuItem, "default", (_, args, ret) => {
  6306. if (!args || !args[0] || !args[0].extended) return;
  6307. const [props] = args;
  6308. if (props.style) ret.props.style = props.style;
  6309. if (props.closeOnClick !== false || !props.action) return;
  6310. ret.props.onClick = function(e) {
  6311. e.preventDefault();
  6312. e.stopPropagation();
  6313. return props.action(...arguments);
  6314. };
  6315. });
  6316. }
  6317. static patchToggleItem() {
  6318. const MenuToggleItem = _modules_webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m.default && m.default.displayName == "MenuCheckboxItem");
  6319. if (!MenuToggleItem || !MenuToggleItem.default) return;
  6320. _modules_patcher__WEBPACK_IMPORTED_MODULE_3__["default"].before("DCM", MenuToggleItem, "default", (_, args) => {
  6321. if (!args || !args[0] || !args[0].extended) return;
  6322. const [props] = args;
  6323. const [active, doToggle] = React.useState(props.checked || false);
  6324. props.checked = active;
  6325. const originalAction = props.action;
  6326. props.action = function(ev) {
  6327. originalAction(ev);
  6328. doToggle(!active);
  6329. };
  6330. });
  6331. }
  6332. }
  6333. /***/ }),
  6334. /***/ "./src/ui/errorboundary.js":
  6335. /*!*********************************!*\
  6336. !*** ./src/ui/errorboundary.js ***!
  6337. \*********************************/
  6338. /*! exports provided: default, WrapBoundary */
  6339. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  6340. "use strict";
  6341. __webpack_require__.r(__webpack_exports__);
  6342. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return ErrorBoundary; });
  6343. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "WrapBoundary", function() { return WrapBoundary; });
  6344. /* harmony import */ var _modules_discordmodules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../modules/discordmodules */ "./src/modules/discordmodules.js");
  6345. const React = _modules_discordmodules__WEBPACK_IMPORTED_MODULE_0__["default"].React;
  6346. const ce = React.createElement;
  6347. class ErrorBoundary extends React.Component {
  6348. constructor(props) {
  6349. super(props);
  6350. this.state = {hasError: false};
  6351. }
  6352. componentDidCatch() {
  6353. this.setState({hasError: true});
  6354. }
  6355. render() {
  6356. if (this.state.hasError) return this.props.errorChildren ? this.props.errorChildren : ce("div", {className: "error"}, "Component Error");
  6357. return this.props.children;
  6358. }
  6359. }
  6360. function WrapBoundary(Original) {
  6361. return class ErrorBoundaryWrapper extends React.Component {
  6362. render() {
  6363. return ce(ErrorBoundary, null, ce(Original, this.props));
  6364. }
  6365. };
  6366. }
  6367. /***/ }),
  6368. /***/ "./src/ui/icons.js":
  6369. /*!*************************!*\
  6370. !*** ./src/ui/icons.js ***!
  6371. \*************************/
  6372. /*! exports provided: IconError, IconInfo, IconSuccess, IconWarning */
  6373. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  6374. "use strict";
  6375. __webpack_require__.r(__webpack_exports__);
  6376. /* harmony import */ var _icons_error__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./icons/error */ "./src/ui/icons/error.js");
  6377. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "IconError", function() { return _icons_error__WEBPACK_IMPORTED_MODULE_0__["default"]; });
  6378. /* harmony import */ var _icons_info__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./icons/info */ "./src/ui/icons/info.js");
  6379. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "IconInfo", function() { return _icons_info__WEBPACK_IMPORTED_MODULE_1__["default"]; });
  6380. /* harmony import */ var _icons_success__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./icons/success */ "./src/ui/icons/success.js");
  6381. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "IconSuccess", function() { return _icons_success__WEBPACK_IMPORTED_MODULE_2__["default"]; });
  6382. /* harmony import */ var _icons_warning__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./icons/warning */ "./src/ui/icons/warning.js");
  6383. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "IconWarning", function() { return _icons_warning__WEBPACK_IMPORTED_MODULE_3__["default"]; });
  6384. /***/ }),
  6385. /***/ "./src/ui/icons/error.js":
  6386. /*!*******************************!*\
  6387. !*** ./src/ui/icons/error.js ***!
  6388. \*******************************/
  6389. /*! exports provided: default */
  6390. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  6391. "use strict";
  6392. __webpack_require__.r(__webpack_exports__);
  6393. /**
  6394. * Error Icon
  6395. * @param {number} size - Size of the icon.
  6396. */
  6397. /* harmony default export */ __webpack_exports__["default"] = (function(size) {
  6398. return `<svg width="${size || 24}" height="${size || 24}" viewBox="0 0 24 24">
  6399. <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z" />
  6400. </svg>`;
  6401. });
  6402. /***/ }),
  6403. /***/ "./src/ui/icons/info.js":
  6404. /*!******************************!*\
  6405. !*** ./src/ui/icons/info.js ***!
  6406. \******************************/
  6407. /*! exports provided: default */
  6408. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  6409. "use strict";
  6410. __webpack_require__.r(__webpack_exports__);
  6411. /**
  6412. * Info Icon
  6413. * @param {number} size - Size of the icon.
  6414. */
  6415. /* harmony default export */ __webpack_exports__["default"] = (function(size) {
  6416. return `<svg width="${size || 24}" height="${size || 24}" viewBox="0 0 24 24">
  6417. <path d="M0 0h24v24H0z" fill="none"/>
  6418. <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/>
  6419. </svg>`;
  6420. });
  6421. /***/ }),
  6422. /***/ "./src/ui/icons/success.js":
  6423. /*!*********************************!*\
  6424. !*** ./src/ui/icons/success.js ***!
  6425. \*********************************/
  6426. /*! exports provided: default */
  6427. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  6428. "use strict";
  6429. __webpack_require__.r(__webpack_exports__);
  6430. /**
  6431. * Success Icon
  6432. * @param {number} size - Size of the icon.
  6433. */
  6434. /* harmony default export */ __webpack_exports__["default"] = (function(size) {
  6435. return `<svg width="${size || 24}" height="${size || 24}" viewBox="0 0 24 24">
  6436. <path d="M0 0h24v24H0z" fill="none"/>
  6437. <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
  6438. </svg>`;
  6439. });
  6440. /***/ }),
  6441. /***/ "./src/ui/icons/warning.js":
  6442. /*!*********************************!*\
  6443. !*** ./src/ui/icons/warning.js ***!
  6444. \*********************************/
  6445. /*! exports provided: default */
  6446. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  6447. "use strict";
  6448. __webpack_require__.r(__webpack_exports__);
  6449. /**
  6450. * Warning Icon
  6451. * @param {number} size - Size of the icon.
  6452. */
  6453. /* harmony default export */ __webpack_exports__["default"] = (function(size) {
  6454. return `<svg width="${size || 24}" height="${size || 24}" viewBox="0 0 24 24">
  6455. <path d="M0 0h24v24H0z" fill="none"/>
  6456. <path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/>
  6457. </svg>`;
  6458. });
  6459. /***/ }),
  6460. /***/ "./src/ui/modals.js":
  6461. /*!**************************!*\
  6462. !*** ./src/ui/modals.js ***!
  6463. \**************************/
  6464. /*! exports provided: default */
  6465. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  6466. "use strict";
  6467. __webpack_require__.r(__webpack_exports__);
  6468. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return Modals; });
  6469. /* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
  6470. /**
  6471. * Allows an easy way to create and show modals.
  6472. * @module Modals
  6473. * @version 0.0.1
  6474. */
  6475. const React = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].React;
  6476. const ce = React.createElement;
  6477. const Markdown = modules__WEBPACK_IMPORTED_MODULE_0__["WebpackModules"].getByDisplayName("Markdown");
  6478. class Modals {
  6479. /** Sizes of modals. */
  6480. static get ModalSizes() {return {};}
  6481. /**
  6482. * Shows the user profile modal for a given user.
  6483. * @param {string} userId - id of the user to show profile for
  6484. */
  6485. static showUserProfile(userId) {
  6486. return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserProfileModal.open(userId);
  6487. }
  6488. /**
  6489. * Acts as a wrapper for {@link module:Modals.showModal} where the `children` is a text element.
  6490. * @param {string} title - title of the modal
  6491. * @param {string} content - text to show inside the modal. Can be markdown.
  6492. * @param {object} [options] - see {@link module:Modals.showModal}
  6493. * @see module:Modals.showModal
  6494. */
  6495. static showConfirmationModal(title, content, options = {}) {
  6496. this.showModal(title, ce(Markdown, null, content), options);
  6497. }
  6498. /**
  6499. * Shows a very simple alert modal that has title, content and an okay button.
  6500. * @param {string} title - title of the modal
  6501. * @param {string} body - text to show inside the modal
  6502. */
  6503. static showAlertModal(title, body) {
  6504. this.showConfirmationModal(title, body, {cancelText: null});
  6505. }
  6506. /**
  6507. * Shows a generic but very customizable modal.
  6508. * @param {string} title - title of the modal
  6509. * @param {(ReactElement|Array<ReactElement>)} children - a single or array of rendered react elements to act as children
  6510. * @param {object} [options] - options to modify the modal
  6511. * @param {boolean} [options.danger=false] - whether the main button should be red or not
  6512. * @param {string} [options.confirmText=Okay] - text for the confirmation/submit button
  6513. * @param {string} [options.cancelText=Cancel] - text for the cancel button
  6514. * @param {callable} [options.onConfirm=NOOP] - callback to occur when clicking the submit button
  6515. * @param {callable} [options.onCancel=NOOP] - callback to occur when clicking the cancel button
  6516. */
  6517. static showModal(title, children, options = {}) {
  6518. const {danger = false, confirmText = "Okay", cancelText = "Cancel", onConfirm = () => {}, onCancel = () => {}} = options;
  6519. return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].ModalActions.openModal(props => {
  6520. return React.createElement(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].ConfirmationModal, Object.assign({
  6521. header: title,
  6522. red: danger,
  6523. confirmText: confirmText,
  6524. cancelText: cancelText,
  6525. onConfirm: onConfirm,
  6526. onCancel: onCancel
  6527. }, props), children);
  6528. });
  6529. }
  6530. /**
  6531. * @interface
  6532. * @name module:Modals~Changelog
  6533. * @property {string} title - title of the changelog section
  6534. * @property {string} [type=added] - type information of the section. Options: added, improved, fixed, progress.
  6535. * @property {Array<string>} items - itemized list of items to show in that section. Can use markdown.
  6536. */
  6537. /**
  6538. * Shows a changelog modal based on changelog data.
  6539. * @param {string} title - title of the modal
  6540. * @param {string} version - subtitle (usually version or date) of the modal
  6541. * @param {module:Modals~Changelog} changelog - changelog to show inside the modal
  6542. * @param {string} footer - either an html element or text to show in the footer of the modal. Can use markdown.
  6543. */
  6544. static showChangelogModal(title, version, changelog, footer) {
  6545. const TextElement = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].TextElement;
  6546. const changelogItems = [];
  6547. for (let c = 0; c < changelog.length; c++) {
  6548. const entry = changelog[c];
  6549. const type = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordClasses"].Changelog[entry.type] ? modules__WEBPACK_IMPORTED_MODULE_0__["DiscordClasses"].Changelog[entry.type] : modules__WEBPACK_IMPORTED_MODULE_0__["DiscordClasses"].Changelog.added;
  6550. const margin = c == 0 ? modules__WEBPACK_IMPORTED_MODULE_0__["DiscordClasses"].Changelog.marginTop : "";
  6551. changelogItems.push(ce("h1", {className: `${type} ${margin}`,}, entry.title));
  6552. const list = ce("ul", null, entry.items.map(i => ce("li", null, ce(Markdown, null, i))));
  6553. changelogItems.push(list);
  6554. }
  6555. const renderHeader = function() {
  6556. return ce(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].FlexChild.Child, {grow: 1, shrink: 1},
  6557. ce(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].Titles.default, {tag: modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].Titles.Tags.H4}, title),
  6558. ce(TextElement,
  6559. {size: TextElement.Sizes.SMALL, color: TextElement.Colors.PRIMARY, className: modules__WEBPACK_IMPORTED_MODULE_0__["DiscordClasses"].Changelog.date.toString()},
  6560. "Version " + version
  6561. )
  6562. );
  6563. };
  6564. const renderFooter = footer ? function() {
  6565. return ce(Markdown, null, footer);
  6566. } : null;
  6567. modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].ModalStack.push(function(props) {
  6568. return ce(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].Changelog, Object.assign({
  6569. className: modules__WEBPACK_IMPORTED_MODULE_0__["DiscordClasses"].Changelog.container.toString(),
  6570. selectable: true,
  6571. onScroll: _ => _,
  6572. onClose: _ => _,
  6573. renderHeader: renderHeader,
  6574. renderFooter: renderFooter,
  6575. children: changelogItems
  6576. }, props));
  6577. });
  6578. }
  6579. }
  6580. /***/ }),
  6581. /***/ "./src/ui/popouts.js":
  6582. /*!***************************!*\
  6583. !*** ./src/ui/popouts.js ***!
  6584. \***************************/
  6585. /*! exports provided: default */
  6586. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  6587. "use strict";
  6588. __webpack_require__.r(__webpack_exports__);
  6589. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return Popouts; });
  6590. /* harmony import */ var structs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! structs */ "./src/structs/structs.js");
  6591. /* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
  6592. /**
  6593. * Allows an easy way to create and show popouts.
  6594. * @module Popouts
  6595. * @version 0.0.1
  6596. */
  6597. class Popouts {
  6598. /**
  6599. * Shows the user popout for a user relative to a target element
  6600. * @param {HTMLElement} target - Element to show the popout in relation to
  6601. * @param {object} user - Discord User object for the user to show
  6602. * @param {object} [options] - Options to modify the request
  6603. * @param {string} [options.guild="currentGuildId"] - Id of the guild (uses current if not specified)
  6604. * @param {string} [options.channel="currentChannelId"] - Id of the channel (uses current if not specified)
  6605. * @param {string} [options.position="right"] - Positioning relative to element
  6606. */
  6607. static showUserPopout(target, user, options = {}) {
  6608. const {guild = modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].SelectedGuildStore.getGuildId(), channel = modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].SelectedChannelStore.getChannelId()} = options;
  6609. let {position = "right"} = options;
  6610. target = modules__WEBPACK_IMPORTED_MODULE_1__["DOMTools"].resolveElement(target);
  6611. if (target.getBoundingClientRect().right + 250 >= structs__WEBPACK_IMPORTED_MODULE_0__["Screen"].width) position = "left";
  6612. modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].PopoutOpener.openPopout(target, {
  6613. position: position,
  6614. offsetX: 0,
  6615. offsetY: 0,
  6616. animationType: "default",
  6617. preventInvert: false,
  6618. zIndexBoost: 0,
  6619. closeOnScroll: false,
  6620. shadow: false,
  6621. backdrop: false,
  6622. toggleClose: true,
  6623. render: (props) => {
  6624. return modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].React.createElement(modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].UserPopout, Object.assign({}, props, {
  6625. userId: user.id,
  6626. guildId: guild,
  6627. channelId: channel
  6628. }));
  6629. }
  6630. }, "ZeresLibrary");
  6631. }
  6632. }
  6633. /***/ }),
  6634. /***/ "./src/ui/settings/index.js":
  6635. /*!**********************************!*\
  6636. !*** ./src/ui/settings/index.js ***!
  6637. \**********************************/
  6638. /*! exports provided: CSS, ReactSetting, SettingField, SettingGroup, SettingPanel, Textbox, ColorPicker, FilePicker, Slider, Switch, Dropdown, Keybind, RadioGroup */
  6639. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  6640. "use strict";
  6641. __webpack_require__.r(__webpack_exports__);
  6642. /* harmony import */ var _styles_settings_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../styles/settings.css */ "./src/styles/settings.css");
  6643. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "CSS", function() { return _styles_settings_css__WEBPACK_IMPORTED_MODULE_0__["default"]; });
  6644. /* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./settingfield */ "./src/ui/settings/settingfield.js");
  6645. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ReactSetting", function() { return _settingfield__WEBPACK_IMPORTED_MODULE_1__["ReactSetting"]; });
  6646. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "SettingField", function() { return _settingfield__WEBPACK_IMPORTED_MODULE_1__["default"]; });
  6647. /* harmony import */ var _settinggroup__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./settinggroup */ "./src/ui/settings/settinggroup.js");
  6648. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "SettingGroup", function() { return _settinggroup__WEBPACK_IMPORTED_MODULE_2__["default"]; });
  6649. /* harmony import */ var _settingpanel__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./settingpanel */ "./src/ui/settings/settingpanel.js");
  6650. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "SettingPanel", function() { return _settingpanel__WEBPACK_IMPORTED_MODULE_3__["default"]; });
  6651. /* harmony import */ var _types_textbox__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./types/textbox */ "./src/ui/settings/types/textbox.js");
  6652. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Textbox", function() { return _types_textbox__WEBPACK_IMPORTED_MODULE_4__["default"]; });
  6653. /* harmony import */ var _types_color__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./types/color */ "./src/ui/settings/types/color.js");
  6654. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ColorPicker", function() { return _types_color__WEBPACK_IMPORTED_MODULE_5__["default"]; });
  6655. /* harmony import */ var _types_file__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./types/file */ "./src/ui/settings/types/file.js");
  6656. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "FilePicker", function() { return _types_file__WEBPACK_IMPORTED_MODULE_6__["default"]; });
  6657. /* harmony import */ var _types_slider__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./types/slider */ "./src/ui/settings/types/slider.js");
  6658. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Slider", function() { return _types_slider__WEBPACK_IMPORTED_MODULE_7__["default"]; });
  6659. /* harmony import */ var _types_switch__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./types/switch */ "./src/ui/settings/types/switch.js");
  6660. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Switch", function() { return _types_switch__WEBPACK_IMPORTED_MODULE_8__["default"]; });
  6661. /* harmony import */ var _types_dropdown__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./types/dropdown */ "./src/ui/settings/types/dropdown.js");
  6662. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Dropdown", function() { return _types_dropdown__WEBPACK_IMPORTED_MODULE_9__["default"]; });
  6663. /* harmony import */ var _types_keybind__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./types/keybind */ "./src/ui/settings/types/keybind.js");
  6664. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Keybind", function() { return _types_keybind__WEBPACK_IMPORTED_MODULE_10__["default"]; });
  6665. /* harmony import */ var _types_radiogroup__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./types/radiogroup */ "./src/ui/settings/types/radiogroup.js");
  6666. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "RadioGroup", function() { return _types_radiogroup__WEBPACK_IMPORTED_MODULE_11__["default"]; });
  6667. /**
  6668. * An object that makes generating settings panel 10x easier.
  6669. * @module Settings
  6670. * @version 1.1.2
  6671. */
  6672. /***/ }),
  6673. /***/ "./src/ui/settings/settingfield.js":
  6674. /*!*****************************************!*\
  6675. !*** ./src/ui/settings/settingfield.js ***!
  6676. \*****************************************/
  6677. /*! exports provided: default, ReactSetting */
  6678. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  6679. "use strict";
  6680. __webpack_require__.r(__webpack_exports__);
  6681. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ReactSetting", function() { return ReactSetting; });
  6682. /* harmony import */ var _structs_listenable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../structs/listenable */ "./src/structs/listenable.js");
  6683. /* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
  6684. const AccessibilityProvider = modules__WEBPACK_IMPORTED_MODULE_1__["WebpackModules"].getByProps("AccessibilityPreferencesContext").AccessibilityPreferencesContext.Provider;
  6685. const LayerProvider = modules__WEBPACK_IMPORTED_MODULE_1__["WebpackModules"].getByProps("AppReferencePositionLayer").AppLayerProvider().props.layerContext.Provider; // eslint-disable-line new-cap
  6686. /**
  6687. * Setting field to extend to create new settings
  6688. * @memberof module:Settings
  6689. * @version 1.0.1
  6690. */
  6691. class SettingField extends _structs_listenable__WEBPACK_IMPORTED_MODULE_0__["default"] {
  6692. /**
  6693. * @param {string} name - name label of the setting
  6694. * @param {string} note - help/note to show underneath or above the setting
  6695. * @param {callable} onChange - callback to perform on setting change
  6696. * @param {(ReactComponent|HTMLElement)} settingtype - actual setting to render
  6697. * @param {object} [props] - object of props to give to the setting and the settingtype
  6698. * @param {boolean} [props.noteOnTop=false] - determines if the note should be shown above the element or not.
  6699. */
  6700. constructor(name, note, onChange, settingtype, props = {}) {
  6701. super();
  6702. this.name = name;
  6703. this.note = note;
  6704. if (typeof(onChange) == "function") this.addListener(onChange);
  6705. this.inputWrapper = modules__WEBPACK_IMPORTED_MODULE_1__["DOMTools"].parseHTML(`<div class="plugin-input-container"></div>`);
  6706. this.type = typeof(settingtype) == "function" ? settingtype : modules__WEBPACK_IMPORTED_MODULE_1__["ReactTools"].wrapElement(settingtype);
  6707. this.props = props;
  6708. modules__WEBPACK_IMPORTED_MODULE_1__["DOMTools"].onAdded(this.getElement(), () => {this.onAdded();});
  6709. modules__WEBPACK_IMPORTED_MODULE_1__["DOMTools"].onRemoved(this.getElement(), () => {this.onRemoved();});
  6710. }
  6711. /** @returns {HTMLElement} - root element for setting */
  6712. getElement() {return this.inputWrapper;}
  6713. /** Fires onchange to listeners */
  6714. onChange() {
  6715. this.alertListeners(...arguments);
  6716. }
  6717. /** Fired when root node added to DOM */
  6718. onAdded() {
  6719. const reactElement = modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].ReactDOM.render(modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].React.createElement(ReactSetting, Object.assign({
  6720. title: this.name,
  6721. type: this.type,
  6722. note: this.note,
  6723. }, this.props)), this.getElement());
  6724. if (this.props.onChange) reactElement.props.onChange = this.props.onChange(reactElement);
  6725. reactElement.forceUpdate();
  6726. }
  6727. /** Fired when root node removed from DOM */
  6728. onRemoved() {
  6729. modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].ReactDOM.unmountComponentAtNode(this.getElement());
  6730. }
  6731. }
  6732. /* harmony default export */ __webpack_exports__["default"] = (SettingField);
  6733. class ReactSetting extends modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].React.Component {
  6734. get noteElement() {
  6735. const className = this.props.noteOnTop ? modules__WEBPACK_IMPORTED_MODULE_1__["DiscordClasses"].Margins.marginBottom8 : modules__WEBPACK_IMPORTED_MODULE_1__["DiscordClasses"].Margins.marginTop8;
  6736. return modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].React.createElement(modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].SettingsNote, {children: this.props.note, type: "description", className: className.toString()});
  6737. }
  6738. get dividerElement() {return modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].React.createElement("div", {className: modules__WEBPACK_IMPORTED_MODULE_1__["DiscordClasses"].Dividers.divider.add(modules__WEBPACK_IMPORTED_MODULE_1__["DiscordClasses"].Dividers.dividerDefault).toString()});}
  6739. render() {
  6740. const ce = modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].React.createElement;
  6741. const SettingElement = ce(this.props.type, this.props);
  6742. const Context = ce(AccessibilityProvider, {value: {reducedMotion: {enabled: false, rawValue: "no-preference"}}}, ce(LayerProvider, {value: [document.querySelector("#app-mount > .layerContainer-yqaFcK")]}, SettingElement));
  6743. if (this.props.inline) {
  6744. const Flex = modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].FlexChild;
  6745. const titleDefault = modules__WEBPACK_IMPORTED_MODULE_1__["WebpackModules"].getByProps("titleDefault") ? modules__WEBPACK_IMPORTED_MODULE_1__["WebpackModules"].getByProps("titleDefault").title : "titleDefault-a8-ZSr title-31JmR4 da-titleDefault da-title";
  6746. return ce(Flex, {direction: Flex.Direction.VERTICAL},
  6747. ce(Flex, {align: Flex.Align.START},
  6748. ce(Flex.Child, {wrap: !0},
  6749. ce("div", {className: titleDefault}, this.props.title)
  6750. ),
  6751. ce(Flex.Child, {grow: 0, shrink: 0}, Context)
  6752. ),
  6753. this.noteElement,
  6754. this.dividerElement
  6755. );
  6756. }
  6757. return ce(modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].SettingsWrapper, {
  6758. className: modules__WEBPACK_IMPORTED_MODULE_1__["DiscordClasses"].Margins.marginBottom20.toString(),
  6759. title: this.props.title,
  6760. children: [
  6761. this.props.noteOnTop ? this.noteElement : Context,
  6762. this.props.noteOnTop ? Context : this.noteElement,
  6763. this.dividerElement
  6764. ]
  6765. });
  6766. }
  6767. }
  6768. /***/ }),
  6769. /***/ "./src/ui/settings/settinggroup.js":
  6770. /*!*****************************************!*\
  6771. !*** ./src/ui/settings/settinggroup.js ***!
  6772. \*****************************************/
  6773. /*! exports provided: default */
  6774. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  6775. "use strict";
  6776. __webpack_require__.r(__webpack_exports__);
  6777. /* harmony import */ var _structs_listenable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../structs/listenable */ "./src/structs/listenable.js");
  6778. /* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
  6779. /* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./settingfield */ "./src/ui/settings/settingfield.js");
  6780. /**
  6781. * Grouping of controls for easier management in settings panels.
  6782. * @memberof module:Settings
  6783. * @version 1.0.2
  6784. */
  6785. class SettingGroup extends _structs_listenable__WEBPACK_IMPORTED_MODULE_0__["default"] {
  6786. /**
  6787. * @param {string} groupName - title for the group of settings
  6788. * @param {object} [options] - additional options for the group
  6789. * @param {callback} [options.callback] - callback called on settings changed
  6790. * @param {boolean} [options.collapsible=true] - determines if the group should be collapsible
  6791. * @param {boolean} [options.shown=false] - determines if the group should be expanded by default
  6792. */
  6793. constructor(groupName, options = {}) {
  6794. super();
  6795. const {collapsible = true, shown = false, callback = () => {}} = options;
  6796. this.addListener(callback);
  6797. this.onChange = this.onChange.bind(this);
  6798. const collapsed = shown || !collapsible ? "" : "collapsed";
  6799. const group = modules__WEBPACK_IMPORTED_MODULE_1__["DOMTools"].parseHTML(`<div class="plugin-input-group">
  6800. <h2 class="${modules__WEBPACK_IMPORTED_MODULE_1__["DiscordClasses"].Titles.h5} ${modules__WEBPACK_IMPORTED_MODULE_1__["DiscordClasses"].Titles.defaultMarginh5} ${modules__WEBPACK_IMPORTED_MODULE_1__["DiscordClasses"].Titles.defaultColor}">
  6801. <span class="button-collapse ${collapsed}"></span> ${groupName}
  6802. </h2>
  6803. <div class="plugin-inputs collapsible ${collapsed}"></div>
  6804. </div>`);
  6805. const label = group.querySelector("h2");
  6806. const controls = group.querySelector(".plugin-inputs");
  6807. this.group = group;
  6808. this.label = label;
  6809. this.controls = controls;
  6810. if (!collapsible) return;
  6811. label.addEventListener("click", async () => {
  6812. const button = label.querySelector(".button-collapse");
  6813. const wasCollapsed = button.classList.contains("collapsed");
  6814. group.parentElement.querySelectorAll(":scope > .plugin-input-group > .collapsible:not(.collapsed)").forEach((element) => {
  6815. element.style.setProperty("height", element.scrollHeight + "px");
  6816. element.classList.add("collapsed");
  6817. setImmediate(() => {element.style.setProperty("height", "");});
  6818. });
  6819. group.parentElement.querySelectorAll(":scope > .plugin-input-group > h2 > .button-collapse").forEach(e => e.classList.add("collapsed"));
  6820. if (!wasCollapsed) return;
  6821. controls.style.setProperty("height", controls.scrollHeight + "px");
  6822. controls.classList.remove("collapsed");
  6823. button.classList.remove("collapsed");
  6824. await new Promise(resolve => setTimeout(resolve, 300));
  6825. controls.style.setProperty("height", "");
  6826. });
  6827. }
  6828. /** @returns {HTMLElement} - root node for the group. */
  6829. getElement() {return this.group;}
  6830. /**
  6831. * Adds multiple nodes to this group.
  6832. * @param {(...HTMLElement|...jQuery|...module:Settings.SettingField|...module:Settings.SettingGroup)} nodes - list of nodes to add to the group container
  6833. * @returns {module:Settings.SettingGroup} - returns self for chaining
  6834. */
  6835. append(...nodes) {
  6836. for (let i = 0; i < nodes.length; i++) {
  6837. if (modules__WEBPACK_IMPORTED_MODULE_1__["DOMTools"].resolveElement(nodes[i]) instanceof Element) this.controls.append(nodes[i]);
  6838. else if (nodes[i] instanceof _settingfield__WEBPACK_IMPORTED_MODULE_2__["default"] || nodes[i] instanceof SettingGroup) this.controls.append(nodes[i].getElement());
  6839. if (nodes[i] instanceof _settingfield__WEBPACK_IMPORTED_MODULE_2__["default"]) {
  6840. nodes[i].addListener(((node) => (value) => {
  6841. this.onChange(node.id || node.name, value);
  6842. })(nodes[i]));
  6843. }
  6844. else if (nodes[i] instanceof SettingGroup) {
  6845. nodes[i].addListener(((node) => (settingId, value) => {
  6846. this.onChange(node.id || node.name, settingId, value);
  6847. })(nodes[i]));
  6848. }
  6849. }
  6850. return this;
  6851. }
  6852. /**
  6853. * Appends this node to another
  6854. * @param {HTMLElement} node - node to attach the group to.
  6855. * @returns {module:Settings.SettingGroup} - returns self for chaining
  6856. */
  6857. appendTo(node) {
  6858. node.append(this.group);
  6859. return this;
  6860. }
  6861. /** Fires onchange to listeners */
  6862. onChange() {
  6863. this.alertListeners(...arguments);
  6864. }
  6865. }
  6866. /* harmony default export */ __webpack_exports__["default"] = (SettingGroup);
  6867. /***/ }),
  6868. /***/ "./src/ui/settings/settingpanel.js":
  6869. /*!*****************************************!*\
  6870. !*** ./src/ui/settings/settingpanel.js ***!
  6871. \*****************************************/
  6872. /*! exports provided: default */
  6873. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  6874. "use strict";
  6875. __webpack_require__.r(__webpack_exports__);
  6876. /* harmony import */ var _structs_listenable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../structs/listenable */ "./src/structs/listenable.js");
  6877. /* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
  6878. /* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./settingfield */ "./src/ui/settings/settingfield.js");
  6879. /* harmony import */ var _settinggroup__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./settinggroup */ "./src/ui/settings/settinggroup.js");
  6880. /**
  6881. * Grouping of controls for easier management in settings panels.
  6882. * @memberof module:Settings
  6883. * @version 1.0.2
  6884. */
  6885. class SettingPanel extends _structs_listenable__WEBPACK_IMPORTED_MODULE_0__["default"] {
  6886. /**
  6887. * Creates a new settings panel
  6888. * @param {callable} onChange - callback to fire when settings change
  6889. * @param {(...HTMLElement|...jQuery|...module:Settings.SettingField|...module:Settings.SettingGroup)} nodes - list of nodes to add to the panel container
  6890. */
  6891. constructor(onChange, ...nodes) {
  6892. super();
  6893. this.element = modules__WEBPACK_IMPORTED_MODULE_1__["DOMTools"].parseHTML(`<div class="plugin-form-container"></div>`);
  6894. if (typeof(onChange) == "function") this.addListener(onChange);
  6895. this.onChange = this.onChange.bind(this);
  6896. this.append(...nodes);
  6897. }
  6898. /**
  6899. * Creates a new settings panel
  6900. * @param {callable} onChange - callback to fire when settings change
  6901. * @param {(...HTMLElement|...jQuery|...module:Settings.SettingField|...module:Settings.SettingGroup)} nodes - list of nodes to add to the panel container
  6902. * @returns {HTMLElement} - root node for the panel.
  6903. */
  6904. static build(onChange, ...nodes) {
  6905. return (new SettingPanel(onChange, ...nodes)).getElement();
  6906. }
  6907. /** @returns {HTMLElement} - root node for the panel. */
  6908. getElement() {return this.element;}
  6909. /**
  6910. * Adds multiple nodes to this panel.
  6911. * @param {(...HTMLElement|...jQuery|...SettingField|...SettingGroup)} nodes - list of nodes to add to the panel container
  6912. * @returns {module:Settings.SettingPanel} - returns self for chaining
  6913. */
  6914. append(...nodes) {
  6915. for (let i = 0; i < nodes.length; i++) {
  6916. if (modules__WEBPACK_IMPORTED_MODULE_1__["DOMTools"].resolveElement(nodes[i]) instanceof Element) this.element.append(nodes[i]);
  6917. else if (nodes[i] instanceof _settingfield__WEBPACK_IMPORTED_MODULE_2__["default"] || nodes[i] instanceof _settinggroup__WEBPACK_IMPORTED_MODULE_3__["default"]) this.element.append(nodes[i].getElement());
  6918. if (nodes[i] instanceof _settingfield__WEBPACK_IMPORTED_MODULE_2__["default"]) {
  6919. nodes[i].addListener(((node) => (value) => {
  6920. this.onChange(node.id || node.name, value);
  6921. })(nodes[i]));
  6922. }
  6923. else if (nodes[i] instanceof _settinggroup__WEBPACK_IMPORTED_MODULE_3__["default"]) {
  6924. nodes[i].addListener(((node) => (settingId, value) => {
  6925. this.onChange(node.id || node.name, settingId, value);
  6926. })(nodes[i]));
  6927. }
  6928. }
  6929. return this;
  6930. }
  6931. /** Fires onchange to listeners */
  6932. onChange() {
  6933. this.alertListeners(...arguments);
  6934. }
  6935. }
  6936. /* harmony default export */ __webpack_exports__["default"] = (SettingPanel);
  6937. /***/ }),
  6938. /***/ "./src/ui/settings/types/color.js":
  6939. /*!****************************************!*\
  6940. !*** ./src/ui/settings/types/color.js ***!
  6941. \****************************************/
  6942. /*! exports provided: default */
  6943. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  6944. "use strict";
  6945. __webpack_require__.r(__webpack_exports__);
  6946. /* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js");
  6947. /* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
  6948. const presetColors = [1752220, 3066993, 3447003, 10181046, 15277667, 15844367, 15105570, 15158332, 9807270, 6323595, 1146986, 2067276, 2123412, 7419530, 11342935, 12745742, 11027200, 10038562, 9936031, 5533306];
  6949. /**
  6950. * Creates a color picker using Discord's built in color picker
  6951. * as a base. Input and output using hex strings.
  6952. * @memberof module:Settings
  6953. * @version 0.1.0
  6954. * @extends module:Settings.SettingField
  6955. */
  6956. class ColorPicker extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] {
  6957. /**
  6958. * @param {string} name - name label of the setting
  6959. * @param {string} note - help/note to show underneath or above the setting
  6960. * @param {string} value - current hex color
  6961. * @param {callable} onChange - callback to perform on setting change, callback receives hex string
  6962. * @param {object} [options] - object of options to give to the setting
  6963. * @param {boolean} [options.disabled=false] - should the setting be disabled
  6964. * @param {Array<number>} [options.colors=presetColors] - preset list of colors
  6965. */
  6966. constructor(name, note, value, onChange, options = {}) {
  6967. if (modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].ColorPicker) {
  6968. super(name, note, onChange, modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].ColorPicker, {
  6969. disabled: !!options.disabled,
  6970. onChange: reactElement => color => {
  6971. reactElement.props.value = color;
  6972. reactElement.forceUpdate();
  6973. this.onChange(modules__WEBPACK_IMPORTED_MODULE_1__["ColorConverter"].int2hex(color));
  6974. },
  6975. colors: Array.isArray(options.colors) ? options.colors : presetColors,
  6976. defaultColor: typeof(value) == "number" ? value : modules__WEBPACK_IMPORTED_MODULE_1__["ColorConverter"].hex2int(value),
  6977. value: 0
  6978. });
  6979. }
  6980. else {
  6981. const classes = ["color-input"];
  6982. if (options.disabled) classes.push(modules__WEBPACK_IMPORTED_MODULE_1__["DiscordClasses"].BasicInputs.disabled);
  6983. const ReactColorPicker = modules__WEBPACK_IMPORTED_MODULE_1__["DOMTools"].parseHTML(`<input type="color" class="${classes.join(" ")}">`);
  6984. if (options.disabled) ReactColorPicker.setAttribute("disabled", "");
  6985. if (value) ReactColorPicker.setAttribute("value", value);
  6986. ReactColorPicker.addEventListener("change", (event) => {
  6987. this.onChange(event.target.value);
  6988. });
  6989. super(name, note, onChange, ReactColorPicker, {inline: true});
  6990. }
  6991. }
  6992. /** Default colors for ColorPicker */
  6993. static get presetColors() {return presetColors;}
  6994. }
  6995. /* harmony default export */ __webpack_exports__["default"] = (ColorPicker);
  6996. /***/ }),
  6997. /***/ "./src/ui/settings/types/dropdown.js":
  6998. /*!*******************************************!*\
  6999. !*** ./src/ui/settings/types/dropdown.js ***!
  7000. \*******************************************/
  7001. /*! exports provided: default */
  7002. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  7003. "use strict";
  7004. __webpack_require__.r(__webpack_exports__);
  7005. /* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js");
  7006. /* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
  7007. /**
  7008. * @interface
  7009. * @name module:Settings~DropdownItem
  7010. * @property {string} label - label to show in the dropdown
  7011. * @property {*} value - actual value represented by label (this is passed via onChange)
  7012. */
  7013. /**
  7014. * Creates a dropdown using discord's built in dropdown.
  7015. * @memberof module:Settings
  7016. * @version 0.0.1
  7017. * @extends module:Settings.SettingField
  7018. */
  7019. class Dropdown extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] {
  7020. /**
  7021. * @param {string} name - name label of the setting
  7022. * @param {string} note - help/note to show underneath or above the setting
  7023. * @param {*} defaultValue - currently selected value
  7024. * @param {Array<module:Settings~DropdownItem>} values - array of all options available
  7025. * @param {callable} onChange - callback to perform on setting change, callback item value
  7026. * @param {object} [options] - object of options to give to the setting
  7027. * @param {boolean} [options.clearable=false] - should be able to empty the field value
  7028. * @param {boolean} [options.searchable=false] - should user be able to search the dropdown
  7029. * @param {boolean} [options.disabled=false] - should the setting be disabled
  7030. */
  7031. constructor(name, note, defaultValue, values, onChange, options = {}) {
  7032. const {clearable = false, searchable = false, disabled = false} = options;
  7033. super(name, note, onChange, modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].Dropdown, {
  7034. clearable: clearable,
  7035. searchable: searchable,
  7036. disabled: disabled,
  7037. options: values,
  7038. onChange: dropdown => opt => {
  7039. dropdown.props.value = opt && opt.value;
  7040. dropdown.forceUpdate();
  7041. this.onChange(opt && opt.value);
  7042. },
  7043. value: defaultValue
  7044. });
  7045. }
  7046. }
  7047. /* harmony default export */ __webpack_exports__["default"] = (Dropdown);
  7048. /***/ }),
  7049. /***/ "./src/ui/settings/types/file.js":
  7050. /*!***************************************!*\
  7051. !*** ./src/ui/settings/types/file.js ***!
  7052. \***************************************/
  7053. /*! exports provided: default */
  7054. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  7055. "use strict";
  7056. __webpack_require__.r(__webpack_exports__);
  7057. /* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js");
  7058. /* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
  7059. /**
  7060. * Creates a file picker using chromium's default.
  7061. * @memberof module:Settings
  7062. * @version 0.0.1
  7063. * @extends module:Settings.SettingField
  7064. */
  7065. class FilePicker extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] {
  7066. /**
  7067. * @param {string} name - name label of the setting
  7068. * @param {string} note - help/note to show underneath or above the setting
  7069. * @param {callable} onChange - callback to perform on setting change, callback receives File object
  7070. * @param {object} [options] - object of options to give to the setting
  7071. * @param {boolean} [options.disabled=false] - should the setting be disabled
  7072. * @param {Array<string>|string} [options.accept] - what file types should be accepted
  7073. * @param {boolean} [options.multiple=false] - should multiple files be accepted
  7074. */
  7075. constructor(name, note, onChange, options = {}) {
  7076. const classes = modules__WEBPACK_IMPORTED_MODULE_1__["DiscordClasses"].BasicInputs.inputDefault.add("file-input");
  7077. if (options.disabled) classes.add(modules__WEBPACK_IMPORTED_MODULE_1__["DiscordClasses"].BasicInputs.disabled);
  7078. const ReactFilePicker = modules__WEBPACK_IMPORTED_MODULE_1__["DOMTools"].parseHTML(`<input type="file" class="${classes}">`);
  7079. if (options.disabled) ReactFilePicker.setAttribute("disabled", "");
  7080. if (options.multiple) ReactFilePicker.setAttribute("multiple", "");
  7081. if (options.accept) ReactFilePicker.setAttribute("accept", Array.isArray(options.accept) ? options.accept.join(",") : options.accept);
  7082. ReactFilePicker.addEventListener("change", (event) => {
  7083. this.onChange(event.target.files[0]);
  7084. });
  7085. super(name, note, onChange, ReactFilePicker);
  7086. }
  7087. }
  7088. /* harmony default export */ __webpack_exports__["default"] = (FilePicker);
  7089. /***/ }),
  7090. /***/ "./src/ui/settings/types/keybind.js":
  7091. /*!******************************************!*\
  7092. !*** ./src/ui/settings/types/keybind.js ***!
  7093. \******************************************/
  7094. /*! exports provided: default */
  7095. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  7096. "use strict";
  7097. __webpack_require__.r(__webpack_exports__);
  7098. /* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js");
  7099. /* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
  7100. /**
  7101. * Creates a keybind setting using discord's built in keybind recorder.
  7102. * @memberof module:Settings
  7103. * @version 0.0.1
  7104. * @extends module:Settings.SettingField
  7105. */
  7106. class Keybind extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] {
  7107. /**
  7108. * @param {string} name - name label of the setting
  7109. * @param {string} note - help/note to show underneath or above the setting
  7110. * @param {Array<number>} value - array of keycodes
  7111. * @param {callable} onChange - callback to perform on setting change, callback receives array of keycodes
  7112. * @param {object} [options] - object of options to give to the setting
  7113. * @param {boolean} [options.disabled=false] - should the setting be disabled
  7114. */
  7115. constructor(label, help, value, onChange, options = {}) {
  7116. const {disabled = false} = options;
  7117. super(label, help, onChange, modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].Keybind, {
  7118. disabled: disabled,
  7119. defaultValue: value.map(a => [0, a]),
  7120. onChange: element => val => {
  7121. if (!Array.isArray(val)) return;
  7122. element.props.value = val;
  7123. this.onChange(value.map(a => a[1]));
  7124. }
  7125. });
  7126. }
  7127. }
  7128. /* harmony default export */ __webpack_exports__["default"] = (Keybind);
  7129. /***/ }),
  7130. /***/ "./src/ui/settings/types/radiogroup.js":
  7131. /*!*********************************************!*\
  7132. !*** ./src/ui/settings/types/radiogroup.js ***!
  7133. \*********************************************/
  7134. /*! exports provided: default */
  7135. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  7136. "use strict";
  7137. __webpack_require__.r(__webpack_exports__);
  7138. /* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js");
  7139. /* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
  7140. /**
  7141. * @interface
  7142. * @name module:Settings~RadioItem
  7143. * @property {string} name - label to show in the dropdown
  7144. * @property {*} value - actual value represented by label (this is passed via onChange)
  7145. * @property {string} desc - description/help text to show below name
  7146. * @property {string} color - hex string to color the item
  7147. */
  7148. /**
  7149. * Creates a radio group using discord's built in radios.
  7150. * @memberof module:Settings
  7151. * @version 0.0.1
  7152. * @extends module:Settings.SettingField
  7153. */
  7154. class RadioGroup extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] {
  7155. /**
  7156. * @param {string} name - name label of the setting
  7157. * @param {string} note - help/note to show underneath or above the setting
  7158. * @param {*} defaultValue - currently selected value
  7159. * @param {Array<module:Settings~RadioItem>} values - array of all options available
  7160. * @param {callable} onChange - callback to perform on setting change, callback item value
  7161. * @param {object} [options] - object of options to give to the setting
  7162. * @param {boolean} [options.disabled=false] - should the setting be disabled
  7163. */
  7164. constructor(name, note, defaultValue, values, onChange, options = {}) {
  7165. super(name, note, onChange, modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].RadioGroup, {
  7166. noteOnTop: true,
  7167. disabled: !!options.disabled,
  7168. options: values,
  7169. onChange: reactElement => option => {
  7170. reactElement.props.value = option.value;
  7171. reactElement.forceUpdate();
  7172. this.onChange(option.value);
  7173. },
  7174. value: defaultValue
  7175. });
  7176. }
  7177. }
  7178. /* harmony default export */ __webpack_exports__["default"] = (RadioGroup);
  7179. /***/ }),
  7180. /***/ "./src/ui/settings/types/slider.js":
  7181. /*!*****************************************!*\
  7182. !*** ./src/ui/settings/types/slider.js ***!
  7183. \*****************************************/
  7184. /*! exports provided: default */
  7185. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  7186. "use strict";
  7187. __webpack_require__.r(__webpack_exports__);
  7188. /* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js");
  7189. /* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
  7190. /**
  7191. * Used to render the grabber tooltip.
  7192. * @param {Number} value - The value to render
  7193. * @returns {string} the text to show in the tooltip
  7194. * @callback module:Settings~SliderRenderValue
  7195. */
  7196. /**
  7197. * Creates a slider/range using discord's built in slider.
  7198. * @memberof module:Settings
  7199. * @version 0.1.0
  7200. * @extends module:Settings.SettingField
  7201. */
  7202. class Slider extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] {
  7203. /**
  7204. *
  7205. * @param {string} name - name label of the setting
  7206. * @param {string} note - help/note to show underneath or above the setting
  7207. * @param {number} min - minimum value allowed
  7208. * @param {number} max - maximum value allowed
  7209. * @param {number} value - currently selected value
  7210. * @param {callable} onChange - callback to fire when setting is changed, callback receives number
  7211. * @param {object} [options] - object of options to give to the setting
  7212. * @param {boolean} [options.disabled=false] - should the setting be disabled
  7213. * @param {object} [options.fillStyles] - object of css styles to add to active slider
  7214. * @param {Array<number>} [options.markers] - array of vertical markers to show on the slider
  7215. * @param {boolean} [options.stickToMarkers] - should the slider be forced to use markers
  7216. * @param {boolean} [options.equidistant] - should the markers be scaled to be equidistant
  7217. * @param {module:Settings~SliderRenderValue} [options.onValueRender] - function to call to render the value in the tooltip
  7218. * @param {module:Settings~SliderRenderValue} [options.renderValue] - alias of `onValueRender`
  7219. * @param {string} [options.units] - can be used in place of `onValueRender` will use this string and render Math.round(value) + units
  7220. */
  7221. constructor(name, note, min, max, value, onChange, options = {}) {
  7222. const props = {
  7223. onChange: _ => _,
  7224. initialValue: value,
  7225. disabled: !!options.disabled,
  7226. minValue: min,
  7227. maxValue: max,
  7228. handleSize: 10
  7229. };
  7230. if (options.fillStyles) props.fillStyles = options.fillStyles;
  7231. if (options.markers) props.markers = options.markers;
  7232. if (options.stickToMarkers) props.stickToMarkers = options.stickToMarkers;
  7233. if (typeof(options.equidistant) != "undefined") props.equidistant = options.equidistant;
  7234. if (options.units) props.onValueRender = (val) => `${Math.round(val)}${options.units}`;
  7235. if (options.onValueRender || options.renderValue) props.onValueRender = options.onValueRender || options.renderValue;
  7236. super(name, note, onChange, modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].Slider, Object.assign(props, {onValueChange: v => this.onChange(v)}));
  7237. }
  7238. }
  7239. /* harmony default export */ __webpack_exports__["default"] = (Slider);
  7240. /***/ }),
  7241. /***/ "./src/ui/settings/types/switch.js":
  7242. /*!*****************************************!*\
  7243. !*** ./src/ui/settings/types/switch.js ***!
  7244. \*****************************************/
  7245. /*! exports provided: default */
  7246. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  7247. "use strict";
  7248. __webpack_require__.r(__webpack_exports__);
  7249. /* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js");
  7250. /* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
  7251. class SwitchWrapper extends modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].React.Component {
  7252. constructor(props) {
  7253. super(props);
  7254. this.state = {enabled: this.props.value};
  7255. }
  7256. render() {
  7257. return modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].React.createElement(modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].SwitchRow, Object.assign({}, this.props, {
  7258. value: this.state.enabled,
  7259. onChange: e => {
  7260. this.props.onChange(e);
  7261. this.setState({enabled: e});
  7262. }
  7263. }));
  7264. }
  7265. }
  7266. /**
  7267. * Creates a switch using discord's built in switch.
  7268. * @memberof module:Settings
  7269. * @version 0.1.0
  7270. * @extends module:Settings.SettingField
  7271. */
  7272. class Switch extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] {
  7273. /**
  7274. * @param {string} name - name label of the setting
  7275. * @param {string} note - help/note to show underneath or above the setting
  7276. * @param {boolean} isChecked - should switch be checked
  7277. * @param {callable} onChange - callback to perform on setting change, callback receives boolean
  7278. * @param {object} [options] - object of options to give to the setting
  7279. * @param {boolean} [options.disabled=false] - should the setting be disabled
  7280. */
  7281. constructor(name, note, isChecked, onChange, options = {}) {
  7282. super(name, note, onChange);
  7283. this.disabled = !!options.disabled;
  7284. this.value = !!isChecked;
  7285. }
  7286. onAdded() {
  7287. modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].ReactDOM.render(modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].React.createElement(SwitchWrapper, {
  7288. children: this.name,
  7289. note: this.note,
  7290. disabled: this.disabled,
  7291. hideBorder: false,
  7292. value: this.value,
  7293. onChange: (e) => {this.onChange(e);}
  7294. }), this.getElement());
  7295. }
  7296. }
  7297. /* harmony default export */ __webpack_exports__["default"] = (Switch);
  7298. /***/ }),
  7299. /***/ "./src/ui/settings/types/textbox.js":
  7300. /*!******************************************!*\
  7301. !*** ./src/ui/settings/types/textbox.js ***!
  7302. \******************************************/
  7303. /*! exports provided: default */
  7304. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  7305. "use strict";
  7306. __webpack_require__.r(__webpack_exports__);
  7307. /* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js");
  7308. /* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
  7309. // TODO: Documentation
  7310. /**
  7311. * Creates a textbox using discord's built in textbox.
  7312. * @memberof module:Settings
  7313. * @version 0.1.0
  7314. * @extends module:Settings.SettingField
  7315. */
  7316. class Textbox extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] {
  7317. /**
  7318. * @param {string} name - name label of the setting
  7319. * @param {string} note - help/note to show underneath or above the setting
  7320. * @param {string} value - current text in box
  7321. * @param {callable} onChange - callback to perform on setting change, callback receives text
  7322. * @param {object} [options] - object of options to give to the setting
  7323. * @param {string} [options.placeholder=""] - placeholder for when textbox is empty
  7324. * @param {boolean} [options.disabled=false] - should the setting be disabled
  7325. */
  7326. constructor(name, note, value, onChange, options = {}) {
  7327. const {placeholder = "", disabled = false} = options;
  7328. super(name, note, onChange, modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].Textbox, {
  7329. onChange: textbox => val => {
  7330. textbox.props.value = val;
  7331. textbox.forceUpdate();
  7332. this.onChange(val);
  7333. },
  7334. value: value,
  7335. disabled: disabled,
  7336. placeholder: placeholder || ""
  7337. });
  7338. }
  7339. }
  7340. /* harmony default export */ __webpack_exports__["default"] = (Textbox);
  7341. /***/ }),
  7342. /***/ "./src/ui/toasts.js":
  7343. /*!**************************!*\
  7344. !*** ./src/ui/toasts.js ***!
  7345. \**************************/
  7346. /*! exports provided: default */
  7347. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  7348. "use strict";
  7349. __webpack_require__.r(__webpack_exports__);
  7350. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return Toast; });
  7351. /* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
  7352. /* harmony import */ var ui__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ui */ "./src/ui/ui.js");
  7353. /* harmony import */ var _styles_toasts_css__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../styles/toasts.css */ "./src/styles/toasts.css");
  7354. /**
  7355. * Toast maker similar to Android.
  7356. *
  7357. * @module Toasts
  7358. * @version 0.0.1
  7359. */
  7360. class Toast {
  7361. static get CSS() {return _styles_toasts_css__WEBPACK_IMPORTED_MODULE_2__["default"];}
  7362. /** Shorthand for `type = "success"` for {@link module:Toasts.show} */
  7363. static async success(content, options = {}) {return this.show(content, Object.assign(options, {type: "success"}));}
  7364. /** Shorthand for `type = "info"` for {@link module:Toasts.show} */
  7365. static async info(content, options = {}) {return this.show(content, Object.assign(options, {type: "info"}));}
  7366. /** Shorthand for `type = "warning"` for {@link module:Toasts.show} */
  7367. static async warning(content, options = {}) {return this.show(content, Object.assign(options, {type: "warning"}));}
  7368. /** Shorthand for `type = "error"` for {@link module:Toasts.show} */
  7369. static async error(content, options = {}) {return this.show(content, Object.assign(options, {type: "error"}));}
  7370. /** Shorthand for `type = "default"` for {@link module:Toasts.show} */
  7371. static async default(content, options = {}) {return this.show(content, Object.assign(options, {type: "default"}));}
  7372. /**
  7373. * Shows a simple toast, similar to Android, centered over
  7374. * the textarea if it exists, and center screen otherwise.
  7375. * Vertically it shows towards the bottom like in Android.
  7376. * @param {string} content - The string to show in the toast.
  7377. * @param {object} options - additional options for the toast
  7378. * @param {string} [options.type] - Changes the type of the toast stylistically and semantically. {@link module:Toasts.ToastTypes}
  7379. * @param {string} [options.icon] - URL to an optional icon
  7380. * @param {number} [options.timeout=3000] - Adjusts the time (in ms) the toast should be shown for before disappearing automatically
  7381. * @returns {Promise} - Promise that resolves when the toast is removed from the DOM
  7382. */
  7383. static async show(content, options = {}) {
  7384. const {type = "", icon = "", timeout = 3000} = options;
  7385. this.ensureContainer();
  7386. const toast = modules__WEBPACK_IMPORTED_MODULE_0__["DOMTools"].parseHTML(this.buildToast(content, this.parseType(type), icon));
  7387. document.querySelector(".toasts").appendChild(toast);
  7388. await new Promise(resolve => setTimeout(resolve, timeout));
  7389. toast.classList.add("closing");
  7390. await new Promise(resolve => setTimeout(resolve, 300));
  7391. toast.remove();
  7392. if (!document.querySelectorAll(".toasts .toast").length) document.querySelector(".toasts").remove();
  7393. }
  7394. static buildToast(message, type, icon) {
  7395. const hasIcon = type || icon;
  7396. const className = `toast ${hasIcon ? "toast-has-icon" : ""} ${type && type != "default" ? `toast-${type}` : ""}`;
  7397. if (!icon && type) icon = type;
  7398. return modules__WEBPACK_IMPORTED_MODULE_0__["Utilities"].formatString(`<div class="{{className}}">{{icon}}<div class="toast-text">{{message}}</div></div>`, {
  7399. className: className,
  7400. icon: hasIcon ? this.getIcon(icon) : "",
  7401. message: message
  7402. });
  7403. }
  7404. static getIcon(icon) {
  7405. let iconInner = `<img src="${icon}" width="20" height="20" />`;
  7406. switch (icon) {
  7407. case "success": iconInner = ui__WEBPACK_IMPORTED_MODULE_1__["Icons"].IconSuccess(20); break; // eslint-disable-line new-cap
  7408. case "warning": iconInner = ui__WEBPACK_IMPORTED_MODULE_1__["Icons"].IconWarning(20); break; // eslint-disable-line new-cap
  7409. case "info": iconInner = ui__WEBPACK_IMPORTED_MODULE_1__["Icons"].IconInfo(20); break; // eslint-disable-line new-cap
  7410. case "error": iconInner = ui__WEBPACK_IMPORTED_MODULE_1__["Icons"].IconError(20); // eslint-disable-line new-cap
  7411. }
  7412. return modules__WEBPACK_IMPORTED_MODULE_0__["Utilities"].formatString(`<div class="toast-icon">{{icon}}</div>`, {icon: iconInner});
  7413. }
  7414. static ensureContainer() {
  7415. if (document.querySelector(".toasts")) return;
  7416. const channelClass = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordSelectors"].ChannelList.sidebar;
  7417. const container = channelClass ? document.querySelector(channelClass.adjacent("div")) : null;
  7418. const memberlist = container ? container.querySelector(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordSelectors"].MemberList.membersWrap) : null;
  7419. const form = container ? container.querySelector("form") : null;
  7420. const left = container ? container.getBoundingClientRect().left : 310;
  7421. const right = memberlist ? memberlist.getBoundingClientRect().left : 0;
  7422. const width = right ? right - container.getBoundingClientRect().left : container.offsetWidth;
  7423. const bottom = form ? form.offsetHeight : 80;
  7424. const toastWrapper = document.createElement("div");
  7425. toastWrapper.classList.add("toasts");
  7426. toastWrapper.style.setProperty("left", left + "px");
  7427. toastWrapper.style.setProperty("width", width + "px");
  7428. toastWrapper.style.setProperty("bottom", bottom + "px");
  7429. document.querySelector("#app-mount").appendChild(toastWrapper);
  7430. }
  7431. static parseType(type) {
  7432. return this.ToastTypes.hasOwnProperty(type) ? this.ToastTypes[type] : "";
  7433. }
  7434. /**
  7435. * Enumeration of accepted types.
  7436. */
  7437. static get ToastTypes() {
  7438. return {
  7439. "default": "",
  7440. "error": "error",
  7441. "success": "success",
  7442. "warning": "warning",
  7443. "info": "info"
  7444. };
  7445. }
  7446. }
  7447. /***/ }),
  7448. /***/ "./src/ui/tooltip.js":
  7449. /*!***************************!*\
  7450. !*** ./src/ui/tooltip.js ***!
  7451. \***************************/
  7452. /*! exports provided: default */
  7453. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  7454. "use strict";
  7455. __webpack_require__.r(__webpack_exports__);
  7456. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return Tooltip; });
  7457. /* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
  7458. /* harmony import */ var _structs_screen__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../structs/screen */ "./src/structs/screen.js");
  7459. /**
  7460. * Tooltip that automatically show and hide themselves on mouseenter and mouseleave events.
  7461. * Will also remove themselves if the node to watch is removed from DOM through
  7462. * a MutationObserver.
  7463. *
  7464. * Note this is not using Discord's internals but normal DOM manipulation and emulates
  7465. * Discord's own tooltips as closely as possible.
  7466. *
  7467. * @module Tooltip
  7468. * @version 1.0.0
  7469. */
  7470. const getClass = function(sideOrColor) {
  7471. const upperCase = sideOrColor[0].toUpperCase() + sideOrColor.slice(1);
  7472. const tooltipClass = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordClasses"].Tooltips[`tooltip${upperCase}`];
  7473. if (tooltipClass) return tooltipClass.value;
  7474. return null;
  7475. };
  7476. const classExists = function(sideOrColor) {
  7477. return !!getClass(sideOrColor);
  7478. };
  7479. const toPx = function(value) {
  7480. return `${value}px`;
  7481. };
  7482. /* <div class="layer-v9HyYc da-layer" style="left: 234.5px; bottom: 51px;">
  7483. <div class="tooltip-2QfLtc da-tooltip tooltipTop-XDDSxx tooltipBlack-PPG47z">
  7484. <div class="tooltipPointer-3ZfirK da-tooltipPointer"></div>
  7485. User Settings
  7486. </div>
  7487. </div> */
  7488. class Tooltip {
  7489. /**
  7490. *
  7491. * @constructor
  7492. * @param {(HTMLElement|jQuery)} node - DOM node to monitor and show the tooltip on
  7493. * @param {string} tip - string to show in the tooltip
  7494. * @param {object} options - additional options for the tooltip
  7495. * @param {string} [options.style=black] - correlates to the discord styling/colors (black, brand, green, grey, red, yellow)
  7496. * @param {string} [options.side=top] - can be any of top, right, bottom, left
  7497. * @param {boolean} [options.preventFlip=false] - prevents moving the tooltip to the opposite side if it is too big or goes offscreen
  7498. * @param {boolean} [options.isTimestamp=false] - adds the timestampTooltip class (disables text wrapping)
  7499. * @param {boolean} [options.disablePointerEvents=false] - disables pointer events
  7500. * @param {boolean} [options.disabled=false] - whether the tooltip should be disabled from showing on hover
  7501. */
  7502. constructor(node, text, options = {}) {
  7503. const {style = "black", side = "top", preventFlip = false, isTimestamp = false, disablePointerEvents = false, disabled = false} = options;
  7504. this.node = modules__WEBPACK_IMPORTED_MODULE_0__["DOMTools"].resolveElement(node);
  7505. this.label = text;
  7506. this.style = style.toLowerCase();
  7507. this.side = side.toLowerCase();
  7508. this.preventFlip = preventFlip;
  7509. this.isTimestamp = isTimestamp;
  7510. this.disablePointerEvents = disablePointerEvents;
  7511. this.disabled = disabled;
  7512. this.active = false;
  7513. if (!classExists(this.side)) return modules__WEBPACK_IMPORTED_MODULE_0__["Logger"].err("Tooltip", `Side ${this.side} does not exist.`);
  7514. if (!classExists(this.style)) return modules__WEBPACK_IMPORTED_MODULE_0__["Logger"].err("Tooltip", `Style ${this.style} does not exist.`);
  7515. this.element = modules__WEBPACK_IMPORTED_MODULE_0__["DOMTools"].createElement(`<div class="${modules__WEBPACK_IMPORTED_MODULE_0__["DiscordClasses"].TooltipLayers.layer}">`);
  7516. this.tooltipElement = modules__WEBPACK_IMPORTED_MODULE_0__["DOMTools"].createElement(`<div class="${modules__WEBPACK_IMPORTED_MODULE_0__["DiscordClasses"].Tooltips.tooltip} ${getClass(this.style)}"><div class="${modules__WEBPACK_IMPORTED_MODULE_0__["DiscordClasses"].Tooltips.tooltipPointer}"></div><div class="${modules__WEBPACK_IMPORTED_MODULE_0__["DiscordClasses"].Tooltips.tooltipContent}">${this.label}</div></div>`);
  7517. this.labelElement = this.tooltipElement.childNodes[1];
  7518. this.element.append(this.tooltipElement);
  7519. if (this.disablePointerEvents) {
  7520. this.element.classList.add(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordClasses"].TooltipLayers.disabledPointerEvents);
  7521. this.tooltipElement.classList.add(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordClasses"].Tooltips.tooltipDisablePointerEvents);
  7522. }
  7523. if (this.isTimestamp) this.tooltipElement.classList.add(modules__WEBPACK_IMPORTED_MODULE_0__["WebpackModules"].getByProps("timestampTooltip").timestampTooltip);
  7524. this.node.addEventListener("mouseenter", () => {
  7525. if (this.disabled) return;
  7526. this.show();
  7527. });
  7528. this.node.addEventListener("mouseleave", () => {
  7529. this.hide();
  7530. });
  7531. }
  7532. /** Alias for the constructor */
  7533. static create(node, text, options = {}) {return new Tooltip(node, text, options);}
  7534. /** Container where the tooltip will be appended. */
  7535. get container() {return document.querySelector(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordSelectors"].Popouts.popouts.sibling(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordSelectors"].TooltipLayers.layerContainer));}
  7536. /** Boolean representing if the tooltip will fit on screen above the element */
  7537. get canShowAbove() {return this.node.getBoundingClientRect().top - this.element.offsetHeight >= 0;}
  7538. /** Boolean representing if the tooltip will fit on screen below the element */
  7539. get canShowBelow() {return this.node.getBoundingClientRect().top + this.node.offsetHeight + this.element.offsetHeight <= _structs_screen__WEBPACK_IMPORTED_MODULE_1__["default"].height;}
  7540. /** Boolean representing if the tooltip will fit on screen to the left of the element */
  7541. get canShowLeft() {return this.node.getBoundingClientRect().left - this.element.offsetWidth >= 0;}
  7542. /** Boolean representing if the tooltip will fit on screen to the right of the element */
  7543. get canShowRight() {return this.node.getBoundingClientRect().left + this.node.offsetWidth + this.element.offsetWidth <= _structs_screen__WEBPACK_IMPORTED_MODULE_1__["default"].width;}
  7544. /** Hides the tooltip. Automatically called on mouseleave. */
  7545. hide() {
  7546. /** Don't rehide if already inactive */
  7547. if (!this.active) return;
  7548. this.active = false;
  7549. this.element.remove();
  7550. this.tooltipElement.className = this._className;
  7551. }
  7552. /** Shows the tooltip. Automatically called on mouseenter. Will attempt to flip if position was wrong. */
  7553. show() {
  7554. /** Don't reshow if already active */
  7555. if (this.active) return;
  7556. this.active = true;
  7557. this.tooltipElement.className = `${modules__WEBPACK_IMPORTED_MODULE_0__["DiscordClasses"].Tooltips.tooltip} ${getClass(this.style)}`;
  7558. if (this.disablePointerEvents) this.tooltipElement.classList.add(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordClasses"].Tooltips.tooltipDisablePointerEvents);
  7559. if (this.isTimestamp) this.tooltipElement.classList.add(modules__WEBPACK_IMPORTED_MODULE_0__["WebpackModules"].getByProps("timestampTooltip").timestampTooltip);
  7560. this.labelElement.textContent = this.label;
  7561. this.container.append(this.element);
  7562. if (this.side == "top") {
  7563. if (this.canShowAbove || (!this.canShowAbove && this.preventFlip)) this.showAbove();
  7564. else this.showBelow();
  7565. }
  7566. if (this.side == "bottom") {
  7567. if (this.canShowBelow || (!this.canShowBelow && this.preventFlip)) this.showBelow();
  7568. else this.showAbove();
  7569. }
  7570. if (this.side == "left") {
  7571. if (this.canShowLeft || (!this.canShowLeft && this.preventFlip)) this.showLeft();
  7572. else this.showRight();
  7573. }
  7574. if (this.side == "right") {
  7575. if (this.canShowRight || (!this.canShowRight && this.preventFlip)) this.showRight();
  7576. else this.showLeft();
  7577. }
  7578. /** Do not create a new observer each time if one already exists! */
  7579. if (this.observer) return;
  7580. /** Use an observer in show otherwise you'll cause unclosable tooltips */
  7581. this.observer = new MutationObserver((mutations) => {
  7582. mutations.forEach((mutation) => {
  7583. const nodes = Array.from(mutation.removedNodes);
  7584. const directMatch = nodes.indexOf(this.node) > -1;
  7585. const parentMatch = nodes.some(parent => parent.contains(this.node));
  7586. if (directMatch || parentMatch) {
  7587. this.hide();
  7588. this.observer.disconnect();
  7589. }
  7590. });
  7591. });
  7592. this.observer.observe(document.body, {subtree: true, childList: true});
  7593. }
  7594. /** Force showing the tooltip above the node. */
  7595. showAbove() {
  7596. this.tooltipElement.classList.add(getClass("top"));
  7597. this.element.style.setProperty("top", toPx(this.node.getBoundingClientRect().top - this.element.offsetHeight - 10));
  7598. this.centerHorizontally();
  7599. }
  7600. /** Force showing the tooltip below the node. */
  7601. showBelow() {
  7602. this.tooltipElement.classList.add(getClass("bottom"));
  7603. this.element.style.setProperty("top", toPx(this.node.getBoundingClientRect().top + this.node.offsetHeight + 10));
  7604. this.centerHorizontally();
  7605. }
  7606. /** Force showing the tooltip to the left of the node. */
  7607. showLeft() {
  7608. this.tooltipElement.classList.add(getClass("left"));
  7609. this.element.style.setProperty("left", toPx(this.node.getBoundingClientRect().left - this.element.offsetWidth - 10));
  7610. this.centerVertically();
  7611. }
  7612. /** Force showing the tooltip to the right of the node. */
  7613. showRight() {
  7614. this.tooltipElement.classList.add(getClass("right"));
  7615. this.element.style.setProperty("left", toPx(this.node.getBoundingClientRect().left + this.node.offsetWidth + 10));
  7616. this.centerVertically();
  7617. }
  7618. centerHorizontally() {
  7619. const nodecenter = this.node.getBoundingClientRect().left + (this.node.offsetWidth / 2);
  7620. this.element.style.setProperty("left", toPx(nodecenter - (this.element.offsetWidth / 2)));
  7621. }
  7622. centerVertically() {
  7623. const nodecenter = this.node.getBoundingClientRect().top + (this.node.offsetHeight / 2);
  7624. this.element.style.setProperty("top", toPx(nodecenter - (this.element.offsetHeight / 2)));
  7625. }
  7626. }
  7627. /***/ }),
  7628. /***/ "./src/ui/ui.js":
  7629. /*!**********************!*\
  7630. !*** ./src/ui/ui.js ***!
  7631. \**********************/
  7632. /*! exports provided: Tooltip, Toasts, Popouts, Modals, DiscordContextMenu, ErrorBoundary, Settings, ContextMenu, Icons */
  7633. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  7634. "use strict";
  7635. __webpack_require__.r(__webpack_exports__);
  7636. /* harmony import */ var _settings__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./settings */ "./src/ui/settings/index.js");
  7637. /* harmony reexport (module object) */ __webpack_require__.d(__webpack_exports__, "Settings", function() { return _settings__WEBPACK_IMPORTED_MODULE_0__; });
  7638. /* harmony import */ var _contextmenu__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./contextmenu */ "./src/ui/contextmenu.js");
  7639. /* harmony reexport (module object) */ __webpack_require__.d(__webpack_exports__, "ContextMenu", function() { return _contextmenu__WEBPACK_IMPORTED_MODULE_1__; });
  7640. /* harmony import */ var _icons__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./icons */ "./src/ui/icons.js");
  7641. /* harmony reexport (module object) */ __webpack_require__.d(__webpack_exports__, "Icons", function() { return _icons__WEBPACK_IMPORTED_MODULE_2__; });
  7642. /* harmony import */ var _tooltip__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./tooltip */ "./src/ui/tooltip.js");
  7643. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Tooltip", function() { return _tooltip__WEBPACK_IMPORTED_MODULE_3__["default"]; });
  7644. /* harmony import */ var _toasts__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./toasts */ "./src/ui/toasts.js");
  7645. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Toasts", function() { return _toasts__WEBPACK_IMPORTED_MODULE_4__["default"]; });
  7646. /* harmony import */ var _popouts__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./popouts */ "./src/ui/popouts.js");
  7647. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Popouts", function() { return _popouts__WEBPACK_IMPORTED_MODULE_5__["default"]; });
  7648. /* harmony import */ var _modals__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./modals */ "./src/ui/modals.js");
  7649. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Modals", function() { return _modals__WEBPACK_IMPORTED_MODULE_6__["default"]; });
  7650. /* harmony import */ var _discordcontextmenu__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./discordcontextmenu */ "./src/ui/discordcontextmenu.js");
  7651. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "DiscordContextMenu", function() { return _discordcontextmenu__WEBPACK_IMPORTED_MODULE_7__["default"]; });
  7652. /* harmony import */ var _errorboundary__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./errorboundary */ "./src/ui/errorboundary.js");
  7653. /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ErrorBoundary", function() { return _errorboundary__WEBPACK_IMPORTED_MODULE_8__["default"]; });
  7654. /***/ })
  7655. /******/ })["default"];
  7656. /*@end@*/