· 6 years ago · Oct 29, 2019, 09:31 PM
1var ZLibrary =
2/******/ (function(modules) { // webpackBootstrap
3/******/ // The module cache
4/******/ var installedModules = {};
5/******/
6/******/ // The require function
7/******/ function __webpack_require__(moduleId) {
8/******/
9/******/ // Check if module is in cache
10/******/ if(installedModules[moduleId]) {
11/******/ return installedModules[moduleId].exports;
12/******/ }
13/******/ // Create a new module (and put it into the cache)
14/******/ var module = installedModules[moduleId] = {
15/******/ i: moduleId,
16/******/ l: false,
17/******/ exports: {}
18/******/ };
19/******/
20/******/ // Execute the module function
21/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
22/******/
23/******/ // Flag the module as loaded
24/******/ module.l = true;
25/******/
26/******/ // Return the exports of the module
27/******/ return module.exports;
28/******/ }
29/******/
30/******/
31/******/ // expose the modules object (__webpack_modules__)
32/******/ __webpack_require__.m = modules;
33/******/
34/******/ // expose the module cache
35/******/ __webpack_require__.c = installedModules;
36/******/
37/******/ // define getter function for harmony exports
38/******/ __webpack_require__.d = function(exports, name, getter) {
39/******/ if(!__webpack_require__.o(exports, name)) {
40/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
41/******/ }
42/******/ };
43/******/
44/******/ // define __esModule on exports
45/******/ __webpack_require__.r = function(exports) {
46/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
47/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
48/******/ }
49/******/ Object.defineProperty(exports, '__esModule', { value: true });
50/******/ };
51/******/
52/******/ // create a fake namespace object
53/******/ // mode & 1: value is a module id, require it
54/******/ // mode & 2: merge all properties of value into the ns
55/******/ // mode & 4: return value when already ns object
56/******/ // mode & 8|1: behave like require
57/******/ __webpack_require__.t = function(value, mode) {
58/******/ if(mode & 1) value = __webpack_require__(value);
59/******/ if(mode & 8) return value;
60/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
61/******/ var ns = Object.create(null);
62/******/ __webpack_require__.r(ns);
63/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
64/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
65/******/ return ns;
66/******/ };
67/******/
68/******/ // getDefaultExport function for compatibility with non-harmony modules
69/******/ __webpack_require__.n = function(module) {
70/******/ var getter = module && module.__esModule ?
71/******/ function getDefault() { return module['default']; } :
72/******/ function getModuleExports() { return module; };
73/******/ __webpack_require__.d(getter, 'a', getter);
74/******/ return getter;
75/******/ };
76/******/
77/******/ // Object.prototype.hasOwnProperty.call
78/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
79/******/
80/******/ // __webpack_public_path__
81/******/ __webpack_require__.p = "";
82/******/
83/******/
84/******/ // Load entry module and return exports
85/******/ return __webpack_require__(__webpack_require__.s = "./src/remote.js");
86/******/ })
87/************************************************************************/
88/******/ ({
89
90/***/ "./src/modules/colorconverter.js":
91/*!***************************************!*\
92 !*** ./src/modules/colorconverter.js ***!
93 \***************************************/
94/*! exports provided: default */
95/***/ (function(module, __webpack_exports__, __webpack_require__) {
96
97"use strict";
98__webpack_require__.r(__webpack_exports__);
99/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return ColorConverter; });
100/* harmony import */ var _webpackmodules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./webpackmodules */ "./src/modules/webpackmodules.js");
101/**
102 * Helpful utilities for dealing with colors.
103 * @module ColorConverter
104 * @version 0.0.2
105 */
106
107
108
109const DiscordColorUtils = _webpackmodules__WEBPACK_IMPORTED_MODULE_0__["default"].getByProps("getDarkness", "isValidHex");
110
111class ColorConverter {
112
113 static getDarkness(color) {
114 return DiscordColorUtils.getDarkness(color);
115 }
116
117 static hex2int(color) {return DiscordColorUtils.hex2int(color);}
118
119 static hex2rgb(color) {return DiscordColorUtils.hex2rgb(color);}
120
121 static int2hex(color) {return DiscordColorUtils.int2hex(color);}
122
123 static int2rgba(color, alpha) {return DiscordColorUtils.int2rgba(color, alpha);}
124
125 static isValidHex(color) {return DiscordColorUtils.isValidHex(color);}
126
127 /**
128 * Will get the red green and blue values of any color string.
129 * @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
130 * @returns {array} - array containing the red, green, and blue values
131 */
132 static getRGB(color) {
133 let result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color);
134 if (result) return [parseInt(result[1]), parseInt(result[2]), parseInt(result[3])];
135
136 result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*\)/.exec(color);
137 if (result) return [parseFloat(result[1]) * 2.55, parseFloat(result[2]) * 2.55, parseFloat(result[3]) * 2.55];
138
139 result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color);
140 if (result) return [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)];
141
142 result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color);
143 if (result) return [parseInt(result[1] + result[1], 16), parseInt(result[2] + result[2], 16), parseInt(result[3] + result[3], 16)];
144 }
145
146 /**
147 * Will get the darken the color by a certain percent
148 * @param {string} color - Can be in any of these formats: #fff, #ffffff, rgb, rgba
149 * @param {number} percent - percent to darken the color by (0-100)
150 * @returns {string} - new color in rgb format
151 */
152 static darkenColor(color, percent) {
153 const rgb = this.getRGB(color);
154 for (let i = 0; i < rgb.length; i++) rgb[i] = Math.round(Math.max(0, rgb[i] - rgb[i] * (percent / 100)));
155 return "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")";
156 }
157
158 /**
159 * Will get the lighten the color by a certain percent
160 * @param {string} color - Can be in any of these formats: #fff, #ffffff, rgb, rgba
161 * @param {number} percent - percent to lighten the color by (0-100)
162 * @returns {string} - new color in rgb format
163 */
164 static lightenColor(color, percent) {
165 const rgb = this.getRGB(color);
166 for (let i = 0; i < rgb.length; i++) rgb[i] = Math.round(Math.min(255, rgb[i] + rgb[i] * (percent / 100)));
167 return "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")";
168 }
169
170 /**
171 * Converts a color to rgba format string
172 * @param {string} color - Can be in any of these formats: #fff, #ffffff, rgb, rgba
173 * @param {number} alpha - alpha level for the new color
174 * @returns {string} - new color in rgb format
175 */
176 static rgbToAlpha(color, alpha) {
177 const rgb = this.getRGB(color);
178 return "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "," + alpha + ")";
179 }
180
181}
182
183/***/ }),
184
185/***/ "./src/modules/discordapi.js":
186/*!***********************************!*\
187 !*** ./src/modules/discordapi.js ***!
188 \***********************************/
189/*! exports provided: default */
190/***/ (function(module, __webpack_exports__, __webpack_require__) {
191
192"use strict";
193__webpack_require__.r(__webpack_exports__);
194/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return DiscordAPI; });
195/* harmony import */ var structs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! structs */ "./src/structs/structs.js");
196/* harmony import */ var _discordmodules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./discordmodules */ "./src/modules/discordmodules.js");
197/**
198 * BetterDiscord Discord API
199 * Copyright (c) 2018-present JsSucks
200 * All rights reserved.
201 *
202 * This source code is licensed under the MIT license found at
203 * https://github.com/JsSucks/BetterDiscordApp/blob/master/LICENSE
204*/
205
206/**
207 * A large list of known and useful webpack modules internal to Discord.
208 * Click the filename below to see the whole list.
209 * @module DiscordAPI
210 * @version 0.0.1
211 */
212
213
214
215class DiscordAPI {
216
217 static get InsufficientPermissions() {return structs__WEBPACK_IMPORTED_MODULE_0__["InsufficientPermissions"];}
218 static get List() {return structs__WEBPACK_IMPORTED_MODULE_0__["List"];}
219 static get User() {return structs__WEBPACK_IMPORTED_MODULE_0__["User"];}
220 static get Channel() {return structs__WEBPACK_IMPORTED_MODULE_0__["Channel"];}
221 static get Guild() {return structs__WEBPACK_IMPORTED_MODULE_0__["Guild"];}
222 static get Message() {return structs__WEBPACK_IMPORTED_MODULE_0__["Message"];}
223 static get UserSettings() {return structs__WEBPACK_IMPORTED_MODULE_0__["UserSettings"];}
224
225 /**
226 * A list of loaded guilds.
227 */
228 static get guilds() {
229 const guilds = _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].GuildStore.getGuilds();
230 return structs__WEBPACK_IMPORTED_MODULE_0__["List"].from(Object.values(guilds), g => structs__WEBPACK_IMPORTED_MODULE_0__["Guild"].from(g));
231 }
232
233 /**
234 * A list of loaded channels.
235 */
236 static get channels() {
237 const channels = _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].ChannelStore.getChannels();
238 return structs__WEBPACK_IMPORTED_MODULE_0__["List"].from(Object.values(channels), c => structs__WEBPACK_IMPORTED_MODULE_0__["Channel"].from(c));
239 }
240
241 /**
242 * A list of loaded users.
243 */
244 static get users() {
245 const users = _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].UserStore.getUsers();
246 return structs__WEBPACK_IMPORTED_MODULE_0__["List"].from(Object.values(users), u => structs__WEBPACK_IMPORTED_MODULE_0__["User"].from(u));
247 }
248
249 /**
250 * An object mapping guild IDs to their member counts.
251 */
252 static get memberCounts() {
253 return _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].MemberCountStore.getMemberCounts();
254 }
255
256 /**
257 * A list of guilds in the order they appear in the server list.
258 */
259 static get sortedGuilds() {
260 const guilds = _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].SortedGuildStore.getSortedGuilds();
261 return structs__WEBPACK_IMPORTED_MODULE_0__["List"].from(guilds, g => structs__WEBPACK_IMPORTED_MODULE_0__["Guild"].from(g));
262 }
263
264 /**
265 * An array of guild IDs in the order they appear in the server list.
266 */
267 static get guildPositions() {
268 return _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].SortedGuildStore.guildPositions;
269 }
270
271 /**
272 * The currently selected guild.
273 */
274 static get currentGuild() {
275 const guild = _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].GuildStore.getGuild(_discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].SelectedGuildStore.getGuildId());
276 return guild ? structs__WEBPACK_IMPORTED_MODULE_0__["Guild"].from(guild) : null;
277 }
278
279 /**
280 * The currently selected channel.
281 */
282 static get currentChannel() {
283 const channel = _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].ChannelStore.getChannel(_discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].SelectedChannelStore.getChannelId());
284 return channel ? structs__WEBPACK_IMPORTED_MODULE_0__["Channel"].from(channel) : null;
285 }
286
287 /**
288 * The current user.
289 */
290 static get currentUser() {
291 const user = _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].UserStore.getCurrentUser();
292 return user ? structs__WEBPACK_IMPORTED_MODULE_0__["User"].from(user) : null;
293 }
294
295 /**
296 * A list of the current user's friends.
297 */
298 static get friends() {
299 const friends = _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].RelationshipStore.getFriendIDs();
300 return structs__WEBPACK_IMPORTED_MODULE_0__["List"].from(friends, id => structs__WEBPACK_IMPORTED_MODULE_0__["User"].fromId(id));
301 }
302}
303
304/***/ }),
305
306/***/ "./src/modules/discordclasses.js":
307/*!***************************************!*\
308 !*** ./src/modules/discordclasses.js ***!
309 \***************************************/
310/*! exports provided: default */
311/***/ (function(module, __webpack_exports__, __webpack_require__) {
312
313"use strict";
314__webpack_require__.r(__webpack_exports__);
315/* harmony import */ var _discordclassmodules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./discordclassmodules */ "./src/modules/discordclassmodules.js");
316/* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js");
317
318
319
320const getRaw = function(prop) {
321 if (!this.hasOwnProperty(prop)) return "";
322 return this[prop];
323};
324
325const getClass = function(prop) {
326 if (!this.hasOwnProperty(prop)) return "";
327 return this[prop].split(" ")[0];
328};
329
330/**
331 * Proxy for all the class packages, allows us to safely attempt
332 * to retrieve nested things without error. Also wraps the class in
333 * {@link module:DOMTools.ClassName} which adds features but can still
334 * be used in native function.
335 *
336 * For a list of all available class namespaces check out {@link module:DiscordClassModules}.
337 *
338 * @see module:DiscordClassModules
339 * @module DiscordClasses
340 * @version 0.1.0
341 */
342const DiscordModules = new Proxy(_discordclassmodules__WEBPACK_IMPORTED_MODULE_0__["default"], {
343 get: function(list, item) {
344 if (item == "getRaw" || item == "getClass") return (module, prop) => DiscordModules[module][item]([prop]);
345 if (list[item] === undefined) return new Proxy({}, {get: function() {return "";}});
346 return new Proxy(list[item], {
347 get: function(obj, prop) {
348 if (prop == "getRaw") return getRaw.bind(obj);
349 if (prop == "getClass") return getClass.bind(obj);
350 if (!obj.hasOwnProperty(prop)) return "";
351 return new _domtools__WEBPACK_IMPORTED_MODULE_1__["default"].ClassName(obj[prop]);
352 }
353 });
354 }
355});
356/* harmony default export */ __webpack_exports__["default"] = (DiscordModules);
357
358/***/ }),
359
360/***/ "./src/modules/discordclassmodules.js":
361/*!********************************************!*\
362 !*** ./src/modules/discordclassmodules.js ***!
363 \********************************************/
364/*! exports provided: default */
365/***/ (function(module, __webpack_exports__, __webpack_require__) {
366
367"use strict";
368__webpack_require__.r(__webpack_exports__);
369/* harmony import */ var _utilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utilities */ "./src/modules/utilities.js");
370/* harmony import */ var _webpackmodules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./webpackmodules */ "./src/modules/webpackmodules.js");
371
372
373
374/**
375 * A large list of known and labelled classes in discord.
376 * Click the source link down below to view more info. Otherwise, if you
377 * have the library installed or have a plugin using this library,
378 * do `Object.keys(ZLibrary.DiscordClassModules)` in console for a list of modules.
379 *
380 * You can use this directly, however the preferred way of doing this is to use {@link module:DiscordClasses} or {@link module:DiscordSelectors}
381 *
382 * @see module:DiscordClasses
383 * @see module:DiscordSelectors
384 * @module DiscordClassModules
385 * @version 0.0.2
386 */
387/* harmony default export */ __webpack_exports__["default"] = (_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].memoizeObject({
388 get ContextMenu() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("itemToggle");},
389 get Scrollers() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("scrollerWrap", "scrollerThemed", "scrollerTrack");},
390 get AccountDetails() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("container", "avatar", "hasBuildOverride");},
391 get Typing() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("typing", "text");},
392 get UserPopout() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("userPopout");},
393 get PopoutRoles() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("roleCircle");},
394 get UserModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("profileBadge");},
395 get Textarea() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("channelTextArea", "textArea");},
396 get Popouts() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("popouts");},
397 get Titles() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("defaultMarginh5");},
398 get Notices() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("noticeInfo");},
399 get Backdrop() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("backdrop");},
400 get Modals() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m.modal && m.inner && !m.header);},
401 get AuditLog() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("userHook");},
402 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"));},
403 get MemberList() {return Object.assign({}, _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("member", "memberInner"), _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("members", "membersWrap"));},
404 get TitleWrap() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("titleWrapper");},
405 get Titlebar() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("titleBar");},
406 get Embeds() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("embed", "embedAuthor");},
407 get Layers() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("layers", "layer");},
408 get TooltipLayers() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("layerContainer", "layer");},
409 get Margins() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => !m.title && m.marginBottom40 && m.marginTop40);},
410 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));},
411 get Changelog() {return Object.assign({}, _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("container", "added"), _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("content", "modal", "size"));},
412 get BasicInputs() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("inputDefault");},
413 get Messages() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("message", "containerCozy");},
414 get Guilds() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("guildsWrapper");},
415 get EmojiPicker() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("emojiPicker", "emojiItem");},
416 get Reactions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("reaction", "reactionInner");},
417 get Checkbox() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("checkbox", "checkboxInner");},
418 get Tooltips() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("tooltip", "tooltipBlack");}
419}));
420
421
422
423/***/ }),
424
425/***/ "./src/modules/discordmodules.js":
426/*!***************************************!*\
427 !*** ./src/modules/discordmodules.js ***!
428 \***************************************/
429/*! exports provided: default */
430/***/ (function(module, __webpack_exports__, __webpack_require__) {
431
432"use strict";
433__webpack_require__.r(__webpack_exports__);
434/* harmony import */ var _utilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utilities */ "./src/modules/utilities.js");
435/* harmony import */ var _webpackmodules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./webpackmodules */ "./src/modules/webpackmodules.js");
436/**
437 * A large list of known and useful webpack modules internal to Discord.
438 * Click the source link down below to view more info. Otherwise, if you
439 * have the library installed or have a plugin using this library,
440 * do `Object.keys(ZLibrary.DiscordModules)` in console for a list of modules.
441 * @module DiscordModules
442 * @version 0.0.3
443 */
444
445
446
447/* harmony default export */ __webpack_exports__["default"] = (_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].memoizeObject({
448 get React() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("createElement", "cloneElement");},
449 get ReactDOM() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("render", "findDOMNode");},
450 get Events() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByPrototypes("setMaxListeners", "emit");},
451
452 /* Guild Info, Stores, and Utilities */
453 get GuildStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getGuild");},
454 get SortedGuildStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getSortedGuilds");},
455 get SelectedGuildStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getLastSelectedGuildId");},
456 get GuildSync() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getSyncedGuilds");},
457 get GuildInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getAcronym");},
458 get GuildChannelsStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getChannels", "getDefaultChannel");},
459 get GuildMemberStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getMember");},
460 get MemberCountStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getMemberCounts");},
461 get GuildEmojiStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getEmojis");},
462 get GuildActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("markGuildAsRead");},
463 get GuildPermissions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getGuildPermissions");},
464
465 /* Channel Store & Actions */
466 get ChannelStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getChannels", "getDMFromUserId");},
467 get SelectedChannelStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getLastSelectedChannelId");},
468 get ChannelActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("selectChannel");},
469 get PrivateChannelActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("openPrivateChannel");},
470 get ChannelSelector() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("selectGuild", "selectChannel");},
471
472 /* Current User Info, State and Settings */
473 get UserInfoStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getToken");},
474 get UserSettingsStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("guildPositions");},
475 get AccountManager() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("register", "login");},
476 get UserSettingsUpdater() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("updateRemoteSettings");},
477 get OnlineWatcher() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("isOnline");},
478 get CurrentUserIdle() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getIdleTime");},
479 get RelationshipStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("isBlocked", "getFriendIDs");},
480 get RelationshipManager() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("addRelationship");},
481 get MentionStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getMentions");},
482
483 /* User Stores and Utils */
484 get UserStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getCurrentUser");},
485 get UserStatusStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getStatus", "getState");},
486 get UserTypingStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("isTyping");},
487 get UserActivityStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getActivity");},
488 get UserNameResolver() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getName");},
489 get UserNoteStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getNote");},
490 get UserNoteActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("updateNote");},
491
492 /* Emoji Store and Utils */
493 get EmojiInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("isEmojiDisabled");},
494 get EmojiUtils() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getGuildEmoji");},
495 get EmojiStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getByCategory", "EMOJI_NAME_RE");},
496
497 /* Invite Store and Utils */
498 get InviteStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getInvites");},
499 get InviteResolver() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("findInvite");},
500 get InviteActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("acceptInvite");},
501
502 /* Discord Objects & Utils */
503 get DiscordConstants() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Permissions", "ActivityTypes", "StatusTypes");},
504 get DiscordPermissions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Permissions", "ActivityTypes", "StatusTypes").Permissions;},
505 get Permissions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getHighestRole");},
506 get ColorConverter() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("hex2int");},
507 get ColorShader() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("darken");},
508 get TinyColor() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByPrototypes("toRgb");},
509 get ClassResolver() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getClass");},
510 get ButtonData() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("ButtonSizes");},
511 get IconNames() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("IconNames");},
512 get NavigationUtils() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("transitionTo", "replaceWith", "getHistory");},
513
514 /* Discord Messages */
515 get MessageStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getMessages");},
516 get MessageActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("jumpToMessage", "_sendMessage");},
517 get MessageQueue() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("enqueue");},
518 get MessageParser() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("createMessage", "parse", "unparse");},
519
520 /* In-Game Overlay */
521 get OverlayUserPopoutSettings() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("openUserPopout");},
522 get OverlayUserPopoutInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getOpenedUserPopout");},
523
524 /* Experiments */
525 get ExperimentStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getExperimentOverrides");},
526 get ExperimentsManager() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("isDeveloper");},
527 get CurrentExperiment() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getExperimentId");},
528
529 /* Images, Avatars and Utils */
530 get ImageResolver() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getUserAvatarURL", "getGuildIconURL");},
531 get ImageUtils() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getSizedImageSrc");},
532 get AvatarDefaults() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getUserAvatarURL", "DEFAULT_AVATARS");},
533
534 /* Drag & Drop */
535 get DNDActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("beginDrag");},
536 get DNDSources() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("addTarget");},
537 get DNDObjects() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("DragSource");},
538
539 /* Electron & Other Internals with Utils*/
540 get ElectronModule() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("setBadge");},
541 get Dispatcher() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("dirtyDispatch");},
542 get PathUtils() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("hasBasename");},
543 get NotificationModule() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("showNotification");},
544 get RouterModule() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Router");},
545 get APIModule() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getAPIBaseURL");},
546 get AnalyticEvents() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("AnalyticEventConfigs");},
547 get KeyGenerator() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByRegex(/"binary"/);},
548 get Buffers() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Buffer", "kMaxLength");},
549 get DeviceStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getDevices");},
550 get SoftwareInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("os");},
551 get CurrentContext() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("setTagsContext");},
552
553 /* Media Stuff (Audio/Video) */
554 get MediaDeviceInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Codecs", "SUPPORTED_BROWSERS");},
555 get MediaInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getOutputVolume");},
556 get MediaEngineInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("MediaEngineFeatures");},
557 get VoiceInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("EchoCancellation");},
558 get VideoStream() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getVideoStream");},
559 get SoundModule() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("playSound");},
560
561 /* Window, DOM, HTML */
562 get WindowInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("isFocused", "windowSize");},
563 get TagInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("VALID_TAG_NAMES");},
564 get DOMInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("canUseDOM");},
565
566 /* Locale/Location and Time */
567 get LocaleManager() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("setLocale");},
568 get Moment() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("parseZone");},
569 get LocationManager() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("createLocation");},
570 get Timestamps() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("fromTimestamp");},
571
572 /* Strings and Utils */
573 get Strings() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Messages").Messages;},
574 get StringFormats() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("a", "z");},
575 get StringUtils() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("toASCII");},
576
577 /* URLs and Utils */
578 get URLParser() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Url", "parse");},
579 get ExtraURLs() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getArticleURL");},
580
581 /* Text Processing */
582 get hljs() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("highlight", "highlightBlock");},
583 get SimpleMarkdown() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("parseBlock", "parseInline", "defaultOutput");},
584
585 /* DOM/React Components */
586 /* ==================== */
587 get LayerManager() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("popLayer", "pushLayer");},
588 get Tooltips() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].find(m => m.hide && m.show && !m.search && !m.submit && !m.search && !m.activateRagingDemon && !m.dismiss);},
589 get UserSettingsWindow() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "updateAccount");},
590 get ChannelSettingsWindow() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "updateChannel");},
591 get GuildSettingsWindow() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "updateGuild");},
592
593 /* Modals */
594 get ModalStack() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("push", "update", "pop", "popWithKey");},
595 get UserProfileModals() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("fetchMutualFriends", "setSection");},
596 get AlertModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByPrototypes("handleCancel", "handleSubmit", "handleMinorConfirm");},
597 get ConfirmationModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m.defaultProps && m.key && m.key() == "confirm-modal");},
598 get UserProfileModal() {
599 return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].find(m => {
600 try {return m.modalConfig && m.prototype.render().type.displayName == "FluxContainer(Component)";}
601 catch (err) {return false;}
602 });
603 },
604 get ChangeNicknameModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "changeNickname");},
605 get CreateChannelModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "createChannel");},
606 get PruneMembersModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "prune");},
607 get NotificationSettingsModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "updateNotificationSettings");},
608 get PrivacySettingsModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByRegex(/PRIVACY_SETTINGS_MODAL_OPEN/, m => m.open);},
609 get CreateInviteModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "createInvite");},
610 get Changelog() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule((m => m.defaultProps && m.defaultProps.selectable == false));},
611 get Avatar() {
612 return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].find(m => {
613 if (m.displayName != "FluxContainer(t)") return false;
614 try {
615 const temp = new m();
616 return temp.state && temp.state.hasOwnProperty("isFocused");
617 }
618 catch (err) {return false;}
619 });
620 },
621
622 /* Popouts */
623 get PopoutStack() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "close", "closeAll");},
624 get PopoutOpener() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("openPopout");},
625 get EmojiPicker() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("FluxContainer(EmojiPicker)");},
626 get UserPopout() {
627 return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("FluxContainer(ForwardRef(SubscribeGuildMembersContainer(UserPopout)))");
628 },
629
630 /* Context Menus */
631 get ContextMenuActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("openContextMenu");},
632 get ContextMenuItemsGroup() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByRegex(/itemGroup/);},
633 get ContextMenuItem() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByRegex(/\.label\b.*\.hint\b.*\.action\b/);},
634
635 /* Misc */
636 get ExternalLink() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByRegex(/trusted/);},
637 get TextElement() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Sizes", "Weights");},
638 get FlexChild() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Child");},
639 get Titles() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Tags", "default");},
640
641 /* Settings */
642 get SettingsWrapper() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("FormItem");},
643 get SettingsNote() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("FormText");},
644 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"));},
645
646 get ColorPicker() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByPrototypes("renderCustomColorPopout");},
647 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"));},
648 get Keybind() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByPrototypes("handleComboChange");},
649 get RadioGroup() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m.defaultProps && m.defaultProps.options && m.defaultProps.size);},
650 get Slider() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByPrototypes("renderMark");},
651 get SwitchRow() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m.defaultProps && m.defaultProps.hideBorder == false);},
652 get Textbox() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m.defaultProps && m.defaultProps.type == "text");},
653}));
654
655/***/ }),
656
657/***/ "./src/modules/discordselectors.js":
658/*!*****************************************!*\
659 !*** ./src/modules/discordselectors.js ***!
660 \*****************************************/
661/*! exports provided: default */
662/***/ (function(module, __webpack_exports__, __webpack_require__) {
663
664"use strict";
665__webpack_require__.r(__webpack_exports__);
666/* harmony import */ var _discordclassmodules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./discordclassmodules */ "./src/modules/discordclassmodules.js");
667/* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js");
668
669
670
671const getSelectorAll = function(prop) {
672 if (!this.hasOwnProperty(prop)) return "";
673 return `.${this[prop].split(" ").join(".")}`;
674};
675
676const getSelector = function(prop) {
677 if (!this.hasOwnProperty(prop)) return "";
678 return `.${this[prop].split(" ")[0]}`;
679};
680
681/**
682 * Gives us a way to retrieve the internal classes as selectors without
683 * needing to concatenate strings or use string templates. Wraps the
684 * selector in {@link module:DOMTools.Selector} which adds features but can
685 * still be used in native function.
686 *
687 * For a list of all available class namespaces check out {@link module:DiscordClassModules}.
688 *
689 * @see module:DiscordClassModules
690 * @module DiscordSelectors
691 * @version 0.1.0
692 */
693const DiscordSelectors = new Proxy(_discordclassmodules__WEBPACK_IMPORTED_MODULE_0__["default"], {
694 get: function(list, item) {
695 if (item == "getSelectorAll" || item == "getSelector") return (module, prop) => DiscordSelectors[module][item]([prop]);
696 if (list[item] === undefined) return new Proxy({}, {get: function() {return "";}});
697 return new Proxy(list[item], {
698 get: function(obj, prop) {
699 if (prop == "getSelectorAll") return getSelectorAll.bind(obj);
700 if (prop == "getSelector") return getSelector.bind(obj);
701 if (!obj.hasOwnProperty(prop)) return "";
702 return new _domtools__WEBPACK_IMPORTED_MODULE_1__["default"].Selector(obj[prop]);
703 }
704 });
705 }
706});
707
708/* harmony default export */ __webpack_exports__["default"] = (DiscordSelectors);
709
710/***/ }),
711
712/***/ "./src/modules/domtools.js":
713/*!*********************************!*\
714 !*** ./src/modules/domtools.js ***!
715 \*********************************/
716/*! exports provided: default */
717/***/ (function(module, __webpack_exports__, __webpack_require__) {
718
719"use strict";
720__webpack_require__.r(__webpack_exports__);
721/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return DOMTools; });
722/* harmony import */ var _utilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utilities */ "./src/modules/utilities.js");
723/* harmony import */ var structs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! structs */ "./src/structs/structs.js");
724/**
725 * Helpful utilities for dealing with DOM operations.
726 *
727 * This module also extends `HTMLElement` to add a set of utility functions,
728 * the same as the ones available in the module itself, but with the `element`
729 * parameter bound to `this`.
730 * @module DOMTools
731 * @version 0.0.5
732 */
733
734
735
736
737/**
738 * @interface
739 * @name Offset
740 * @property {number} top - Top offset of the target element.
741 * @property {number} right - Right offset of the target element.
742 * @property {number} bottom - Bottom offset of the target element.
743 * @property {number} left - Left offset of the target element.
744 * @property {number} height - Outer height of the target element.
745 * @property {number} width - Outer width of the target element.
746 */
747
748 /**
749 * Function that automatically removes added listener.
750 * @callback module:DOMTools~CancelListener
751 */
752
753class DOMTools {
754
755 static get Selector() {return structs__WEBPACK_IMPORTED_MODULE_1__["Selector"];}
756 static get ClassName() {return structs__WEBPACK_IMPORTED_MODULE_1__["ClassName"];}
757 static get DOMObserver() {return structs__WEBPACK_IMPORTED_MODULE_1__["DOMObserver"];}
758
759 /**
760 * Default DOMObserver for global usage.
761 *
762 * @see DOMObserver
763 */
764 static get observer() {
765 return this._observer || (this._observer = new structs__WEBPACK_IMPORTED_MODULE_1__["DOMObserver"]());
766 }
767
768 /**
769 * This is my shit version of not having to use `$` from jQuery. Meaning
770 * that you can pass a selector and it will automatically run {@link module:DOMTools.query}.
771 * It also means that you can pass a string of html and it will perform and return `parseHTML`.
772 * @see module:DOMTools.parseHTML
773 * @see module:DOMTools.query
774 * @param {string} selector - Selector to query or HTML to parse
775 * @returns {(DocumentFragment|NodeList|HTMLElement)} - Either the result of `parseHTML` or `query`
776 */
777 static Q(selector) {
778 const element = this.parseHTML(selector);
779 const isHTML = element instanceof NodeList ? Array.from(element).some(n => n.nodeType === 1) : element.nodeType === 1;
780 if (isHTML) return element;
781 return this.query(selector);
782 }
783
784 /**
785 * Essentially a shorthand for `document.querySelector`. If the `baseElement` is not provided
786 * `document` is used by default.
787 * @param {string} selector - Selector to query
788 * @param {Element} [baseElement] - Element to base the query from
789 * @returns {(Element|null)} - The found element or null if not found
790 */
791 static query(selector, baseElement) {
792 if (!baseElement) baseElement = document;
793 return baseElement.querySelector(selector);
794 }
795
796 /**
797 * Essentially a shorthand for `document.querySelectorAll`. If the `baseElement` is not provided
798 * `document` is used by default.
799 * @param {string} selector - Selector to query
800 * @param {Element} [baseElement] - Element to base the query from
801 * @returns {Array<Element>} - Array of all found elements
802 */
803 static queryAll(selector, baseElement) {
804 if (!baseElement) baseElement = document;
805 return baseElement.querySelectorAll(selector);
806 }
807
808 /**
809 * Parses a string of HTML and returns the results. If the second parameter is true,
810 * the parsed HTML will be returned as a document fragment {@see https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment}.
811 * 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.
812 *
813 * If the second parameter is false, then the return value will be the list of parsed
814 * nodes and there were multiple top level nodes, otherwise the single node is returned.
815 * @param {string} html - HTML to be parsed
816 * @param {boolean} [fragment=false] - Whether or not the return should be the raw `DocumentFragment`
817 * @returns {(DocumentFragment|NodeList|HTMLElement)} - The result of HTML parsing
818 */
819 static parseHTML(html, fragment = false) {
820 const template = document.createElement("template");
821 template.innerHTML = html;
822 const node = template.content.cloneNode(true);
823 if (fragment) return node;
824 return node.childNodes.length > 1 ? node.childNodes : node.childNodes[0];
825 }
826
827 /** Alternate name for {@link module:DOMTools.parseHTML} */
828 static createElement(html, fragment = false) {return this.parseHTML(html, fragment);}
829
830 /**
831 * Takes a string of html and escapes it using the brower's own escaping mechanism.
832 * @param {String} html - html to be escaped
833 */
834 static escapeHTML(html) {
835 const textNode = document.createTextNode("");
836 const spanElement = document.createElement("span");
837 spanElement.append(textNode);
838 textNode.nodeValue = html;
839 return spanElement.innerHTML;
840 }
841
842 /**
843 * Adds a list of classes from the target element.
844 * @param {Element} element - Element to edit classes of
845 * @param {...string} classes - Names of classes to add
846 * @returns {Element} - `element` to allow for chaining
847 */
848 static addClass(element, ...classes) {
849 classes = classes.flat().filter(c => c);
850 for (let c = 0; c < classes.length; c++) classes[c] = classes[c].toString().split(" ");
851 classes = classes.flat().filter(c => c);
852 element.classList.add(...classes);
853 return element;
854 }
855
856 /**
857 * Removes a list of classes from the target element.
858 * @param {Element} element - Element to edit classes of
859 * @param {...string} classes - Names of classes to remove
860 * @returns {Element} - `element` to allow for chaining
861 */
862 static removeClass(element, ...classes) {
863 for (let c = 0; c < classes.length; c++) classes[c] = classes[c].toString().split(" ");
864 classes = classes.flat().filter(c => c);
865 element.classList.remove(...classes);
866 return element;
867 }
868
869 /**
870 * When only one argument is present: Toggle class value;
871 * i.e., if class exists then remove it and return false, if not, then add it and return true.
872 * When a second argument is present:
873 * If the second argument evaluates to true, add specified class value, and if it evaluates to false, remove it.
874 * @param {Element} element - Element to edit classes of
875 * @param {string} classname - Name of class to toggle
876 * @param {boolean} [indicator] - Optional indicator for if the class should be toggled
877 * @returns {Element} - `element` to allow for chaining
878 */
879 static toggleClass(element, classname, indicator) {
880 classname = classname.toString().split(" ").filter(c => c);
881 if (typeof(indicator) !== "undefined") classname.forEach(c => element.classList.toggle(c, indicator));
882 else classname.forEach(c => element.classList.toggle(c));
883 return element;
884 }
885
886 /**
887 * Checks if an element has a specific class
888 * @param {Element} element - Element to edit classes of
889 * @param {string} classname - Name of class to check
890 * @returns {boolean} - `true` if the element has the class, `false` otherwise.
891 */
892 static hasClass(element, classname) {
893 return classname.toString().split(" ").filter(c => c).every(c => element.classList.contains(c));
894 }
895
896 /**
897 * Replaces one class with another
898 * @param {Element} element - Element to edit classes of
899 * @param {string} oldName - Name of class to replace
900 * @param {string} newName - New name for the class
901 * @returns {Element} - `element` to allow for chaining
902 */
903 static replaceClass(element, oldName, newName) {
904 element.classList.replace(oldName, newName);
905 return element;
906 }
907
908 /**
909 * Appends `thisNode` to `thatNode`
910 * @param {Node} thisNode - Node to be appended to another node
911 * @param {Node} thatNode - Node for `thisNode` to be appended to
912 * @returns {Node} - `thisNode` to allow for chaining
913 */
914 static appendTo(thisNode, thatNode) {
915 if (typeof(thatNode) == "string") thatNode = this.query(thatNode);
916 if (!thatNode) return null;
917 thatNode.append(thisNode);
918 return thisNode;
919 }
920
921 /**
922 * Prepends `thisNode` to `thatNode`
923 * @param {Node} thisNode - Node to be prepended to another node
924 * @param {Node} thatNode - Node for `thisNode` to be prepended to
925 * @returns {Node} - `thisNode` to allow for chaining
926 */
927 static prependTo(thisNode, thatNode) {
928 if (typeof(thatNode) == "string") thatNode = this.query(thatNode);
929 if (!thatNode) return null;
930 thatNode.prepend(thisNode);
931 return thisNode;
932 }
933
934 /**
935 * Insert after a specific element, similar to jQuery's `thisElement.insertAfter(otherElement)`.
936 * @param {Node} thisNode - The node to insert
937 * @param {Node} targetNode - Node to insert after in the tree
938 * @returns {Node} - `thisNode` to allow for chaining
939 */
940 static insertAfter(thisNode, targetNode) {
941 targetNode.parentNode.insertBefore(thisNode, targetNode.nextSibling);
942 return thisNode;
943 }
944
945 /**
946 * Insert after a specific element, similar to jQuery's `thisElement.after(newElement)`.
947 * @param {Node} thisNode - The node to insert
948 * @param {Node} newNode - Node to insert after in the tree
949 * @returns {Node} - `thisNode` to allow for chaining
950 */
951 static after(thisNode, newNode) {
952 thisNode.parentNode.insertBefore(newNode, thisNode.nextSibling);
953 return thisNode;
954 }
955
956 /**
957 * Gets the next sibling element that matches the selector.
958 * @param {Element} element - Element to get the next sibling of
959 * @param {string} [selector=""] - Optional selector
960 * @returns {Element} - The sibling element
961 */
962 static next(element, selector = "") {
963 return selector ? element.querySelector("+ " + selector) : element.nextElementSibling;
964 }
965
966 /**
967 * Gets all subsequent siblings.
968 * @param {Element} element - Element to get next siblings of
969 * @returns {NodeList} - The list of siblings
970 */
971 static nextAll(element) {
972 return element.querySelectorAll("~ *");
973 }
974
975 /**
976 * Gets the subsequent siblings until an element matches the selector.
977 * @param {Element} element - Element to get the following siblings of
978 * @param {string} selector - Selector to stop at
979 * @returns {Array<Element>} - The list of siblings
980 */
981 static nextUntil(element, selector) {
982 const next = [];
983 while (element.nextElementSibling && !element.nextElementSibling.matches(selector)) next.push(element = element.nextElementSibling);
984 return next;
985 }
986
987 /**
988 * Gets the previous sibling element that matches the selector.
989 * @param {Element} element - Element to get the previous sibling of
990 * @param {string} [selector=""] - Optional selector
991 * @returns {Element} - The sibling element
992 */
993 static previous(element, selector = "") {
994 const previous = element.previousElementSibling;
995 if (selector) return previous && previous.matches(selector) ? previous : null;
996 return previous;
997 }
998
999 /**
1000 * Gets all preceeding siblings.
1001 * @param {Element} element - Element to get preceeding siblings of
1002 * @returns {NodeList} - The list of siblings
1003 */
1004 static previousAll(element) {
1005 const previous = [];
1006 while (element.previousElementSibling) previous.push(element = element.previousElementSibling);
1007 return previous;
1008 }
1009
1010 /**
1011 * Gets the preceeding siblings until an element matches the selector.
1012 * @param {Element} element - Element to get the preceeding siblings of
1013 * @param {string} selector - Selector to stop at
1014 * @returns {Array<Element>} - The list of siblings
1015 */
1016 static previousUntil(element, selector) {
1017 const previous = [];
1018 while (element.previousElementSibling && !element.previousElementSibling.matches(selector)) previous.push(element = element.previousElementSibling);
1019 return previous;
1020 }
1021
1022 /**
1023 * Find which index in children a certain node is. Similar to jQuery's `$.index()`
1024 * @param {HTMLElement} node - The node to find its index in parent
1025 * @returns {number} Index of the node
1026 */
1027 static indexInParent(node) {
1028 const children = node.parentNode.childNodes;
1029 let num = 0;
1030 for (let i = 0; i < children.length; i++) {
1031 if (children[i] == node) return num;
1032 if (children[i].nodeType == 1) num++;
1033 }
1034 return -1;
1035 }
1036
1037 /** Shorthand for {@link module:DOMTools.indexInParent} */
1038 static index(node) {return this.indexInParent(node);}
1039
1040 /**
1041 * Gets the parent of the element if it matches the selector,
1042 * otherwise returns null.
1043 * @param {Element} element - Element to get parent of
1044 * @param {string} [selector=""] - Selector to match parent
1045 * @returns {(Element|null)} - The sibling element or null
1046 */
1047 static parent(element, selector = "") {
1048 return !selector || element.parentElement.matches(selector) ? element.parentElement : null;
1049 }
1050
1051 /**
1052 * Gets all children of Element that match the selector if provided.
1053 * @param {Element} element - Element to get all children of
1054 * @param {string} selector - Selector to match the children to
1055 * @returns {Array<Element>} - The list of children
1056 */
1057 static findChild(element, selector) {
1058 return element.querySelector(":scope > " + selector);
1059 }
1060
1061 /**
1062 * Gets all children of Element that match the selector if provided.
1063 * @param {Element} element - Element to get all children of
1064 * @param {string} selector - Selector to match the children to
1065 * @returns {Array<Element>} - The list of children
1066 */
1067 static findChildren(element, selector) {
1068 return element.querySelectorAll(":scope > " + selector);
1069 }
1070
1071 /**
1072 * Gets all ancestors of Element that match the selector if provided.
1073 * @param {Element} element - Element to get all parents of
1074 * @param {string} [selector=""] - Selector to match the parents to
1075 * @returns {Array<Element>} - The list of parents
1076 */
1077 static parents(element, selector = "") {
1078 const parents = [];
1079 if (selector) while (element.parentElement && element.parentElement.closest(selector)) parents.push(element = element.parentElement.closest(selector));
1080 else while (element.parentElement) parents.push(element = element.parentElement);
1081 return parents;
1082 }
1083
1084 /**
1085 * Gets the ancestors until an element matches the selector.
1086 * @param {Element} element - Element to get the ancestors of
1087 * @param {string} selector - Selector to stop at
1088 * @returns {Array<Element>} - The list of parents
1089 */
1090 static parentsUntil(element, selector) {
1091 const parents = [];
1092 while (element.parentElement && !element.parentElement.matches(selector)) parents.push(element = element.parentElement);
1093 return parents;
1094 }
1095
1096 /**
1097 * Gets all siblings of the element that match the selector.
1098 * @param {Element} element - Element to get all siblings of
1099 * @param {string} [selector="*"] - Selector to match the siblings to
1100 * @returns {Array<Element>} - The list of siblings
1101 */
1102 static siblings(element, selector = "*") {
1103 return Array.from(element.parentElement.children).filter(e => e != element && e.matches(selector));
1104 }
1105
1106 /**
1107 * Sets or gets css styles for a specific element. If `value` is provided
1108 * then it sets the style and returns the element to allow for chaining,
1109 * otherwise returns the style.
1110 * @param {Element} element - Element to set the CSS of
1111 * @param {string} attribute - Attribute to get or set
1112 * @param {string} [value] - Value to set for attribute
1113 * @returns {Element|string} - When setting a value, element is returned for chaining, otherwise the value is returned.
1114 */
1115 static css(element, attribute, value) {
1116 if (typeof(value) == "undefined") return global.getComputedStyle(element)[attribute];
1117 element.style[attribute] = value;
1118 return element;
1119 }
1120
1121 /**
1122 * Sets or gets the width for a specific element. If `value` is provided
1123 * then it sets the width and returns the element to allow for chaining,
1124 * otherwise returns the width.
1125 * @param {Element} element - Element to set the CSS of
1126 * @param {string} [value] - Width to set
1127 * @returns {Element|string} - When setting a value, element is returned for chaining, otherwise the value is returned.
1128 */
1129 static width(element, value) {
1130 if (typeof(value) == "undefined") return parseInt(getComputedStyle(element).width);
1131 element.style.width = value;
1132 return element;
1133 }
1134
1135 /**
1136 * Sets or gets the height for a specific element. If `value` is provided
1137 * then it sets the height and returns the element to allow for chaining,
1138 * otherwise returns the height.
1139 * @param {Element} element - Element to set the CSS of
1140 * @param {string} [value] - Height to set
1141 * @returns {Element|string} - When setting a value, element is returned for chaining, otherwise the value is returned.
1142 */
1143 static height(element, value) {
1144 if (typeof(value) == "undefined") return parseInt(getComputedStyle(element).height);
1145 element.style.height = value;
1146 return element;
1147 }
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 /**
1161 * Returns the innerWidth of the element.
1162 * @param {Element} element - Element to retrieve inner width of
1163 * @return {number} - The inner width of the element.
1164 */
1165 static innerWidth(element) {
1166 return element.clientWidth;
1167 }
1168
1169 /**
1170 * Returns the innerHeight of the element.
1171 * @param {Element} element - Element to retrieve inner height of
1172 * @return {number} - The inner height of the element.
1173 */
1174 static innerHeight(element) {
1175 return element.clientHeight;
1176 }
1177
1178 /**
1179 * Returns the outerWidth of the element.
1180 * @param {Element} element - Element to retrieve outer width of
1181 * @return {number} - The outer width of the element.
1182 */
1183 static outerWidth(element) {
1184 return element.offsetWidth;
1185 }
1186
1187 /**
1188 * Returns the outerHeight of the element.
1189 * @param {Element} element - Element to retrieve outer height of
1190 * @return {number} - The outer height of the element.
1191 */
1192 static outerHeight(element) {
1193 return element.offsetHeight;
1194 }
1195
1196 /**
1197 * Gets the offset of the element in the page.
1198 * @param {Element} element - Element to get offset of
1199 * @return {Offset} - The offset of the element
1200 */
1201 static offset(element) {
1202 return element.getBoundingClientRect();
1203 }
1204
1205 static get listeners() { return this._listeners || (this._listeners = {}); }
1206
1207 /**
1208 * This is similar to jQuery's `on` function and can *hopefully* be used in the same way.
1209 *
1210 * Rather than attempt to explain, I'll show some example usages.
1211 *
1212 * The following will add a click listener (in the `myPlugin` namespace) to `element`.
1213 * `DOMTools.on(element, "click.myPlugin", () => {console.log("clicked!");});`
1214 *
1215 * The following will add a click listener (in the `myPlugin` namespace) to `element` that only fires when the target is a `.block` element.
1216 * `DOMTools.on(element, "click.myPlugin", ".block", () => {console.log("clicked!");});`
1217 *
1218 * The following will add a click listener (without namespace) to `element`.
1219 * `DOMTools.on(element, "click", () => {console.log("clicked!");});`
1220 *
1221 * The following will add a click listener (without namespace) to `element` that only fires once.
1222 * `const cancel = DOMTools.on(element, "click", () => {console.log("fired!"); cancel();});`
1223 *
1224 * @param {Element} element - Element to add listener to
1225 * @param {string} event - Event to listen to with option namespace (e.g. "event.namespace")
1226 * @param {(string|callable)} delegate - Selector to run on element to listen to
1227 * @param {callable} [callback] - Function to fire on event
1228 * @returns {module:DOMTools~CancelListener} - A function that will undo the listener
1229 */
1230 static on(element, event, delegate, callback) {
1231 const [type, namespace] = event.split(".");
1232 const hasDelegate = delegate && callback;
1233 if (!callback) callback = delegate;
1234 const eventFunc = !hasDelegate ? callback : function(event) {
1235 if (event.target.matches(delegate)) {
1236 callback(event);
1237 }
1238 };
1239
1240 element.addEventListener(type, eventFunc);
1241 const cancel = () => {
1242 element.removeEventListener(type, eventFunc);
1243 };
1244 if (namespace) {
1245 if (!this.listeners[namespace]) this.listeners[namespace] = [];
1246 const newCancel = () => {
1247 cancel();
1248 this.listeners[namespace].splice(this.listeners[namespace].findIndex(l => l.event == type && l.element == element), 1);
1249 };
1250 this.listeners[namespace].push({
1251 event: type,
1252 element: element,
1253 cancel: newCancel
1254 });
1255 return newCancel;
1256 }
1257 return cancel;
1258 }
1259
1260 /**
1261 * Functionality for this method matches {@link module:DOMTools.on} but automatically cancels itself
1262 * and removes the listener upon the first firing of the desired event.
1263 *
1264 * @param {Element} element - Element to add listener to
1265 * @param {string} event - Event to listen to with option namespace (e.g. "event.namespace")
1266 * @param {(string|callable)} delegate - Selector to run on element to listen to
1267 * @param {callable} [callback] - Function to fire on event
1268 * @returns {module:DOMTools~CancelListener} - A function that will undo the listener
1269 */
1270 static once(element, event, delegate, callback) {
1271 const [type, namespace] = event.split(".");
1272 const hasDelegate = delegate && callback;
1273 if (!callback) callback = delegate;
1274 const eventFunc = !hasDelegate ? function(event) {
1275 callback(event);
1276 element.removeEventListener(type, eventFunc);
1277 } : function(event) {
1278 if (!event.target.matches(delegate)) return;
1279 callback(event);
1280 element.removeEventListener(type, eventFunc);
1281 };
1282
1283 element.addEventListener(type, eventFunc);
1284 const cancel = () => {
1285 element.removeEventListener(type, eventFunc);
1286 };
1287 if (namespace) {
1288 if (!this.listeners[namespace]) this.listeners[namespace] = [];
1289 const newCancel = () => {
1290 cancel();
1291 this.listeners[namespace].splice(this.listeners[namespace].findIndex(l => l.event == type && l.element == element), 1);
1292 };
1293 this.listeners[namespace].push({
1294 event: type,
1295 element: element,
1296 cancel: newCancel
1297 });
1298 return newCancel;
1299 }
1300 return cancel;
1301 }
1302
1303 static __offAll(event, element) {
1304 const [type, namespace] = event.split(".");
1305 let matchFilter = listener => listener.event == type, defaultFilter = _ => _;
1306 if (element) matchFilter = l => l.event == type && l.element == element, defaultFilter = l => l.element == element;
1307 const listeners = this.listeners[namespace] || [];
1308 const list = type ? listeners.filter(matchFilter) : listeners.filter(defaultFilter);
1309 for (let c = 0; c < list.length; c++) list[c].cancel();
1310 }
1311
1312 /**
1313 * This is similar to jQuery's `off` function and can *hopefully* be used in the same way.
1314 *
1315 * Rather than attempt to explain, I'll show some example usages.
1316 *
1317 * The following will remove a click listener called `onClick` (in the `myPlugin` namespace) from `element`.
1318 * `DOMTools.off(element, "click.myPlugin", onClick);`
1319 *
1320 * 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.
1321 * `DOMTools.off(element, "click.myPlugin", ".block", onClick);`
1322 *
1323 * The following will remove a click listener (without namespace) from `element`.
1324 * `DOMTools.off(element, "click", onClick);`
1325 *
1326 * The following will remove all listeners in namespace `myPlugin` from `element`.
1327 * `DOMTools.off(element, ".myPlugin");`
1328 *
1329 * The following will remove all click listeners in namespace `myPlugin` from *all elements*.
1330 * `DOMTools.off("click.myPlugin");`
1331 *
1332 * The following will remove all listeners in namespace `myPlugin` from *all elements*.
1333 * `DOMTools.off(".myPlugin");`
1334 *
1335 * @param {(Element|string)} element - Element to remove listener from
1336 * @param {string} [event] - Event to listen to with option namespace (e.g. "event.namespace")
1337 * @param {(string|callable)} [delegate] - Selector to run on element to listen to
1338 * @param {callable} [callback] - Function to fire on event
1339 * @returns {Element} - The original element to allow for chaining
1340 */
1341 static off(element, event, delegate, callback) {
1342 if (typeof(element) == "string") return this.__offAll(element);
1343 const [type, namespace] = event.split(".");
1344 if (namespace) return this.__offAll(event, element);
1345
1346 const hasDelegate = delegate && callback;
1347 if (!callback) callback = delegate;
1348 const eventFunc = !hasDelegate ? callback : function(event) {
1349 if (event.target.matches(delegate)) {
1350 callback(event);
1351 }
1352 };
1353
1354 element.removeEventListener(type, eventFunc);
1355 return element;
1356 }
1357
1358 /**
1359 * Adds a listener for when the node is added/removed from the document body.
1360 * The listener is automatically removed upon firing.
1361 * @param {HTMLElement} node - node to wait for
1362 * @param {callable} callback - function to be performed on event
1363 * @param {boolean} onMount - determines if it should fire on Mount or on Unmount
1364 */
1365 static onMountChange(node, callback, onMount = true) {
1366 const wrappedCallback = () => {
1367 this.observer.unsubscribe(wrappedCallback);
1368 callback();
1369 };
1370 this.observer.subscribe(wrappedCallback, mutation => {
1371 const nodes = Array.from(onMount ? mutation.addedNodes : mutation.removedNodes);
1372 const directMatch = nodes.indexOf(node) > -1;
1373 const parentMatch = nodes.some(parent => parent.contains(node));
1374 return directMatch || parentMatch;
1375 });
1376 return node;
1377 }
1378
1379 /** Shorthand for {@link module:DOMTools.onMountChange} with third parameter `true` */
1380 static onMount(node, callback) { return this.onMountChange(node, callback); }
1381
1382 /** Shorthand for {@link module:DOMTools.onMountChange} with third parameter `false` */
1383 static onUnmount(node, callback) { return this.onMountChange(node, callback, false); }
1384
1385 /** Alias for {@link module:DOMTools.onMount} */
1386 static onAdded(node, callback) { return this.onMount(node, callback); }
1387
1388 /** Alias for {@link module:DOMTools.onUnmount} */
1389 static onRemoved(node, callback) { return this.onUnmount(node, callback, false); }
1390
1391 /**
1392 * Helper function which combines multiple elements into one parent element
1393 * @param {Array<HTMLElement>} elements - array of elements to put into a single parent
1394 */
1395 static wrap(elements) {
1396 const domWrapper = this.parseHTML(`<div class="dom-wrapper"></div>`);
1397 for (let e = 0; e < elements.length; e++) domWrapper.appendChild(elements[e]);
1398 return domWrapper;
1399 }
1400
1401 /**
1402 * Resolves the node to an HTMLElement. This is mainly used by library modules.
1403 * @param {(jQuery|Element)} node - node to resolve
1404 */
1405 static resolveElement(node) {
1406 if (!(node instanceof jQuery) && !(node instanceof Element)) return undefined;
1407 return node instanceof jQuery ? node[0] : node;
1408 }
1409}
1410
1411_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "addClass", function(...classes) {return DOMTools.addClass(this, ...classes);});
1412_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "removeClass", function(...classes) {return DOMTools.removeClass(this, ...classes);});
1413_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "toggleClass", function(className, indicator) {return DOMTools.toggleClass(this, className, indicator);});
1414_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "replaceClass", function(oldClass, newClass) {return DOMTools.replaceClass(this, oldClass, newClass);});
1415_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "hasClass", function(className) {return DOMTools.hasClass(this, className);});
1416_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "insertAfter", function(referenceNode) {return DOMTools.insertAfter(this, referenceNode);});
1417_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "after", function(newNode) {return DOMTools.after(this, newNode);});
1418_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "next", function(selector = "") {return DOMTools.next(this, selector);});
1419_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "nextAll", function() {return DOMTools.nextAll(this);});
1420_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "nextUntil", function(selector) {return DOMTools.nextUntil(this, selector);});
1421_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "previous", function(selector = "") {return DOMTools.previous(this, selector);});
1422_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "previousAll", function() {return DOMTools.previousAll(this);});
1423_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "previousUntil", function(selector) {return DOMTools.previousUntil(this, selector);});
1424_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "index", function() {return DOMTools.index(this);});
1425_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "findChild", function(selector) {return DOMTools.findChild(this, selector);});
1426_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "findChildren", function(selector) {return DOMTools.findChildren(this, selector);});
1427_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "parent", function(selector) {return DOMTools.parent(this, selector);});
1428_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "parents", function(selector = "") {return DOMTools.parents(this, selector);});
1429_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "parentsUntil", function(selector) {return DOMTools.parentsUntil(this, selector);});
1430_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "siblings", function(selector = "*") {return DOMTools.siblings(this, selector);});
1431_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "css", function(attribute, value) {return DOMTools.css(this, attribute, value);});
1432_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "width", function(value) {return DOMTools.width(this, value);});
1433_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "height", function(value) {return DOMTools.height(this, value);});
1434_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "innerWidth", function() {return DOMTools.innerWidth(this);});
1435_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "innerHeight", function() {return DOMTools.innerHeight(this);});
1436_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "outerWidth", function() {return DOMTools.outerWidth(this);});
1437_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "outerHeight", function() {return DOMTools.outerHeight(this);});
1438_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "offset", function() {return DOMTools.offset(this);});
1439_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "text", function(value) {return DOMTools.text(this, value);});
1440_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "on", function(event, delegate, callback) {return DOMTools.on(this, event, delegate, callback);});
1441_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "once", function(event, delegate, callback) {return DOMTools.once(this, event, delegate, callback);});
1442_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "off", function(event, delegate, callback) {return DOMTools.off(this, event, delegate, callback);});
1443_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "find", function(selector) {return DOMTools.query(selector, this);});
1444_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "findAll", function(selector) {return DOMTools.queryAll(selector, this);});
1445_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "appendTo", function(otherNode) {return DOMTools.appendTo(this, otherNode);});
1446_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "onAdded", function(callback) {return DOMTools.onAdded(this, callback);});
1447_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].addToPrototype(HTMLElement, "onRemoved", function(callback) {return DOMTools.onRemoved(this, callback);});
1448
1449/***/ }),
1450
1451/***/ "./src/modules/logger.js":
1452/*!*******************************!*\
1453 !*** ./src/modules/logger.js ***!
1454 \*******************************/
1455/*! exports provided: LogTypes, default */
1456/***/ (function(module, __webpack_exports__, __webpack_require__) {
1457
1458"use strict";
1459__webpack_require__.r(__webpack_exports__);
1460/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "LogTypes", function() { return LogTypes; });
1461/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return Logger; });
1462/**
1463 * Simple logger for the lib and plugins.
1464 *
1465 * @module Logger
1466 * @version 0.1.0
1467 */
1468
1469/* eslint-disable no-console */
1470
1471/**
1472 * List of logging types.
1473 */
1474const LogTypes = {
1475 /** Alias for error */
1476 err: "error",
1477 error: "error",
1478 /** Alias for debug */
1479 dbg: "debug",
1480 debug: "debug",
1481 log: "log",
1482 warn: "warn",
1483 info: "info"
1484};
1485
1486class Logger {
1487
1488 /**
1489 * Logs an error using a collapsed error group with stacktrace.
1490 *
1491 * @param {string} module - Name of the calling module.
1492 * @param {string} message - Message or error to have logged.
1493 * @param {Error} error - Error object to log with the message.
1494 */
1495 static stacktrace(module, message, error) {
1496 console.error(`%c[${module}]%c ${message}\n\n%c`, "color: #3a71c1; font-weight: 700;", "color: red; font-weight: 700;", "color: red;", error);
1497 }
1498
1499 /**
1500 * Logs using error formatting. For logging an actual error object consider {@link module:Logger.stacktrace}
1501 *
1502 * @param {string} module - Name of the calling module.
1503 * @param {string} message - Messages to have logged.
1504 */
1505 static err(module, ...message) { Logger._log(module, message, "error"); }
1506
1507 /**
1508 * Logs a warning message.
1509 *
1510 * @param {string} module - Name of the calling module.
1511 * @param {...any} message - Messages to have logged.
1512 */
1513 static warn(module, ...message) { Logger._log(module, message, "warn"); }
1514
1515 /**
1516 * Logs an informational message.
1517 *
1518 * @param {string} module - Name of the calling module.
1519 * @param {...any} message - Messages to have logged.
1520 */
1521 static info(module, ...message) { Logger._log(module, message, "info"); }
1522
1523 /**
1524 * Logs used for debugging purposes.
1525 *
1526 * @param {string} module - Name of the calling module.
1527 * @param {...any} message - Messages to have logged.
1528 */
1529 static debug(module, ...message) { Logger._log(module, message, "debug"); }
1530
1531 /**
1532 * Logs used for basic loggin.
1533 *
1534 * @param {string} module - Name of the calling module.
1535 * @param {...any} message - Messages to have logged.
1536 */
1537 static log(module, ...message) { Logger._log(module, message); }
1538
1539 /**
1540 * Logs strings using different console levels and a module label.
1541 *
1542 * @param {string} module - Name of the calling module.
1543 * @param {any|Array<any>} message - Messages to have logged.
1544 * @param {module:Logger.LogTypes} type - Type of log to use in console.
1545 */
1546 static _log(module, message, type = "log") {
1547 type = Logger.parseType(type);
1548 if (!Array.isArray(message)) message = [message];
1549 console[type](`%c[${module}]%c`, "color: #3a71c1; font-weight: 700;", "", ...message);
1550 }
1551
1552 static parseType(type) {
1553 return LogTypes.hasOwnProperty(type) ? LogTypes[type] : "log";
1554 }
1555
1556}
1557
1558/***/ }),
1559
1560/***/ "./src/modules/modules.js":
1561/*!********************************!*\
1562 !*** ./src/modules/modules.js ***!
1563 \********************************/
1564/*! exports provided: Utilities, WebpackModules, Filters, DiscordModules, ColorConverter, DOMTools, DiscordClasses, DiscordSelectors, ReactTools, ReactComponents, DiscordAPI, Logger, Patcher, PluginUpdater, PluginUtilities, DiscordClassModules, Structs */
1565/***/ (function(module, __webpack_exports__, __webpack_require__) {
1566
1567"use strict";
1568__webpack_require__.r(__webpack_exports__);
1569/* harmony import */ var _utilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utilities */ "./src/modules/utilities.js");
1570/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Utilities", function() { return _utilities__WEBPACK_IMPORTED_MODULE_0__["default"]; });
1571
1572/* harmony import */ var _webpackmodules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./webpackmodules */ "./src/modules/webpackmodules.js");
1573/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "WebpackModules", function() { return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"]; });
1574
1575/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Filters", function() { return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["Filters"]; });
1576
1577/* harmony import */ var _discordmodules__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./discordmodules */ "./src/modules/discordmodules.js");
1578/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "DiscordModules", function() { return _discordmodules__WEBPACK_IMPORTED_MODULE_2__["default"]; });
1579
1580/* harmony import */ var _colorconverter__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./colorconverter */ "./src/modules/colorconverter.js");
1581/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ColorConverter", function() { return _colorconverter__WEBPACK_IMPORTED_MODULE_3__["default"]; });
1582
1583/* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js");
1584/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "DOMTools", function() { return _domtools__WEBPACK_IMPORTED_MODULE_4__["default"]; });
1585
1586/* harmony import */ var _discordclasses__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./discordclasses */ "./src/modules/discordclasses.js");
1587/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "DiscordClasses", function() { return _discordclasses__WEBPACK_IMPORTED_MODULE_5__["default"]; });
1588
1589/* harmony import */ var _discordselectors__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./discordselectors */ "./src/modules/discordselectors.js");
1590/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "DiscordSelectors", function() { return _discordselectors__WEBPACK_IMPORTED_MODULE_6__["default"]; });
1591
1592/* harmony import */ var _reacttools__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./reacttools */ "./src/modules/reacttools.js");
1593/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ReactTools", function() { return _reacttools__WEBPACK_IMPORTED_MODULE_7__["default"]; });
1594
1595/* harmony import */ var _reactcomponents__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./reactcomponents */ "./src/modules/reactcomponents.js");
1596/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ReactComponents", function() { return _reactcomponents__WEBPACK_IMPORTED_MODULE_8__["default"]; });
1597
1598/* harmony import */ var _discordapi__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./discordapi */ "./src/modules/discordapi.js");
1599/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "DiscordAPI", function() { return _discordapi__WEBPACK_IMPORTED_MODULE_9__["default"]; });
1600
1601/* harmony import */ var _logger__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./logger */ "./src/modules/logger.js");
1602/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Logger", function() { return _logger__WEBPACK_IMPORTED_MODULE_10__["default"]; });
1603
1604/* harmony import */ var _patcher__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./patcher */ "./src/modules/patcher.js");
1605/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Patcher", function() { return _patcher__WEBPACK_IMPORTED_MODULE_11__["default"]; });
1606
1607/* harmony import */ var _pluginupdater__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./pluginupdater */ "./src/modules/pluginupdater.js");
1608/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "PluginUpdater", function() { return _pluginupdater__WEBPACK_IMPORTED_MODULE_12__["default"]; });
1609
1610/* harmony import */ var _pluginutilities__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./pluginutilities */ "./src/modules/pluginutilities.js");
1611/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "PluginUtilities", function() { return _pluginutilities__WEBPACK_IMPORTED_MODULE_13__["default"]; });
1612
1613/* harmony import */ var _discordclassmodules__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./discordclassmodules */ "./src/modules/discordclassmodules.js");
1614/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "DiscordClassModules", function() { return _discordclassmodules__WEBPACK_IMPORTED_MODULE_14__["default"]; });
1615
1616/* harmony import */ var structs__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! structs */ "./src/structs/structs.js");
1617/* harmony reexport (module object) */ __webpack_require__.d(__webpack_exports__, "Structs", function() { return structs__WEBPACK_IMPORTED_MODULE_15__; });
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642/***/ }),
1643
1644/***/ "./src/modules/patcher.js":
1645/*!********************************!*\
1646 !*** ./src/modules/patcher.js ***!
1647 \********************************/
1648/*! exports provided: default */
1649/***/ (function(module, __webpack_exports__, __webpack_require__) {
1650
1651"use strict";
1652__webpack_require__.r(__webpack_exports__);
1653/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return Patcher; });
1654/* harmony import */ var _logger__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./logger */ "./src/modules/logger.js");
1655/* harmony import */ var _discordmodules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./discordmodules */ "./src/modules/discordmodules.js");
1656/* harmony import */ var _webpackmodules__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./webpackmodules */ "./src/modules/webpackmodules.js");
1657/**
1658 * Patcher that can patch other functions allowing you to run code before, after or
1659 * instead of the original function. Can also alter arguments and return values.
1660 *
1661 * 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}
1662 *
1663 * @module Patcher
1664 * @version 0.0.2
1665 */
1666
1667
1668
1669
1670
1671class Patcher {
1672
1673 static get patches() { return this._patches || (this._patches = []); }
1674
1675 /**
1676 * Returns all the patches done by a specific caller
1677 * @param {string} name - Name of the patch caller
1678 * @method
1679 */
1680 static getPatchesByCaller(name) {
1681 if (!name) return [];
1682 const patches = [];
1683 for (const patch of this.patches) {
1684 for (const childPatch of patch.children) {
1685 if (childPatch.caller === name) patches.push(childPatch);
1686 }
1687 }
1688 return patches;
1689 }
1690
1691 /**
1692 * Unpatches all patches passed, or when a string is passed unpatches all
1693 * patches done by that specific caller.
1694 * @param {Array|string} patches - Either an array of patches to unpatch or a caller name
1695 */
1696 static unpatchAll(patches) {
1697 if (typeof patches === "string") patches = this.getPatchesByCaller(patches);
1698
1699 for (const patch of patches) {
1700 patch.unpatch();
1701 }
1702 }
1703
1704 static resolveModule(module) {
1705 if (module instanceof Function || (module instanceof Object && !(module instanceof Array))) return module;
1706 if (typeof module === "string") return _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"][module];
1707 if (module instanceof Array) return _webpackmodules__WEBPACK_IMPORTED_MODULE_2__["default"].findByUniqueProperties(module);
1708 return null;
1709 }
1710
1711 static makeOverride(patch) {
1712 return function () {
1713 let returnValue = undefined;
1714 if (!patch.children || !patch.children.length) return patch.originalFunction.apply(this, arguments);
1715 for (const superPatch of patch.children.filter(c => c.type === "before")) {
1716 try {
1717 superPatch.callback(this, arguments);
1718 }
1719 catch (err) {
1720 _logger__WEBPACK_IMPORTED_MODULE_0__["default"].err("Patcher", `Could not fire before callback of ${patch.functionName} for ${superPatch.caller}`, err);
1721 }
1722 }
1723
1724 const insteads = patch.children.filter(c => c.type === "instead");
1725 if (!insteads.length) {returnValue = patch.originalFunction.apply(this, arguments);}
1726 else {
1727 for (const insteadPatch of insteads) {
1728 try {
1729 const tempReturn = insteadPatch.callback(this, arguments, patch.originalFunction.bind(this));
1730 if (typeof(tempReturn) !== "undefined") returnValue = tempReturn;
1731 }
1732 catch (err) {
1733 _logger__WEBPACK_IMPORTED_MODULE_0__["default"].err("Patcher", `Could not fire instead callback of ${patch.functionName} for ${insteadPatch.caller}`, err);
1734 }
1735 }
1736 }
1737
1738 for (const slavePatch of patch.children.filter(c => c.type === "after")) {
1739 try {
1740 const tempReturn = slavePatch.callback(this, arguments, returnValue);
1741 if (typeof(tempReturn) !== "undefined") returnValue = tempReturn;
1742 }
1743 catch (err) {
1744 _logger__WEBPACK_IMPORTED_MODULE_0__["default"].err("Patcher", `Could not fire after callback of ${patch.functionName} for ${slavePatch.caller}`, err);
1745 }
1746 }
1747 return returnValue;
1748 };
1749 }
1750
1751 static rePatch(patch) {
1752 patch.proxyFunction = patch.module[patch.functionName] = this.makeOverride(patch);
1753 }
1754
1755 static makePatch(module, functionName, name) {
1756 const patch = {
1757 name,
1758 module,
1759 functionName,
1760 originalFunction: module[functionName],
1761 proxyFunction: null,
1762 revert: () => { // Calling revert will destroy any patches added to the same module after this
1763 patch.module[patch.functionName] = patch.originalFunction;
1764 patch.proxyFunction = null;
1765 patch.children = [];
1766 },
1767 counter: 0,
1768 children: []
1769 };
1770 patch.proxyFunction = module[functionName] = this.makeOverride(patch);
1771 return this.patches.push(patch), patch;
1772 }
1773
1774 /**
1775 * Function with no arguments and no return value that may be called to revert changes made by {@link module:Patcher}, restoring (unpatching) original method.
1776 * @callback module:Patcher~unpatch
1777 */
1778
1779 /**
1780 * 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.
1781 *
1782 * The third argument for the callback will be `undefined` for `before` patches. `originalFunction` for `instead` patches and `returnValue` for `after` patches.
1783 *
1784 * @callback module:Patcher~patchCallback
1785 * @param {object} thisObject - `this` in the context of the original function.
1786 * @param {arguments} arguments - The original arguments of the original function.
1787 * @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.
1788 * @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.
1789 */
1790
1791 /**
1792 * This method patches onto another function, allowing your code to run beforehand.
1793 * Using this, you are also able to modify the incoming arguments before the original method is run.
1794 *
1795 * @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.
1796 * @param {object} moduleToPatch - Object with the function to be patched. Can also patch an object's prototype.
1797 * @param {string} functionName - Name of the method to be patched
1798 * @param {module:Patcher~patchCallback} callback - Function to run before the original method
1799 * @param {object} options - Object used to pass additional options.
1800 * @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.
1801 * @param {boolean} [options.forcePatch=true] Set to `true` to patch even if the function doesnt exist. (Adds noop function in place).
1802 * @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.
1803 */
1804 static before(caller, moduleToPatch, functionName, callback, options = {}) { return this.pushChildPatch(caller, moduleToPatch, functionName, callback, Object.assign(options, {type: "before"})); }
1805
1806 /**
1807 * This method patches onto another function, allowing your code to run after.
1808 * Using this, you are also able to modify the return value, using the return of your code instead.
1809 *
1810 * @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.
1811 * @param {object} moduleToPatch - Object with the function to be patched. Can also patch an object's prototype.
1812 * @param {string} functionName - Name of the method to be patched
1813 * @param {module:Patcher~patchCallback} callback - Function to run instead of the original method
1814 * @param {object} options - Object used to pass additional options.
1815 * @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.
1816 * @param {boolean} [options.forcePatch=true] Set to `true` to patch even if the function doesnt exist. (Adds noop function in place).
1817 * @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.
1818 */
1819 static after(caller, moduleToPatch, functionName, callback, options = {}) { return this.pushChildPatch(caller, moduleToPatch, functionName, callback, Object.assign(options, {type: "after"})); }
1820
1821 /**
1822 * This method patches onto another function, allowing your code to run instead.
1823 * Using this, you are also able to modify the return value, using the return of your code instead.
1824 *
1825 * @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.
1826 * @param {object} moduleToPatch - Object with the function to be patched. Can also patch an object's prototype.
1827 * @param {string} functionName - Name of the method to be patched
1828 * @param {module:Patcher~patchCallback} callback - Function to run after the original method
1829 * @param {object} options - Object used to pass additional options.
1830 * @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.
1831 * @param {boolean} [options.forcePatch=true] Set to `true` to patch even if the function doesnt exist. (Adds noop function in place).
1832 * @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.
1833 */
1834 static instead(caller, moduleToPatch, functionName, callback, options = {}) { return this.pushChildPatch(caller, moduleToPatch, functionName, callback, Object.assign(options, {type: "instead"})); }
1835
1836 /**
1837 * This method patches onto another function, allowing your code to run before, instead or after the original function.
1838 * Using this you are able to modify the incoming arguments before the original function is run as well as the return
1839 * value before the original function actually returns.
1840 *
1841 * @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.
1842 * @param {object} moduleToPatch - Object with the function to be patched. Can also patch an object's prototype.
1843 * @param {string} functionName - Name of the method to be patched
1844 * @param {module:Patcher~patchCallback} callback - Function to run after the original method
1845 * @param {object} options - Object used to pass additional options.
1846 * @param {string} [options.type=after] - Determines whether to run the function `before`, `instead`, or `after` the original.
1847 * @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.
1848 * @param {boolean} [options.forcePatch=true] Set to `true` to patch even if the function doesnt exist. (Adds noop function in place).
1849 * @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.
1850 */
1851 static pushChildPatch(caller, moduleToPatch, functionName, callback, options = {}) {
1852 const {type = "after", forcePatch = true} = options;
1853 const module = this.resolveModule(moduleToPatch);
1854 if (!module) return null;
1855 if (!module[functionName] && forcePatch) module[functionName] = function() {};
1856 if (!(module[functionName] instanceof Function)) return null;
1857
1858 if (typeof moduleToPatch === "string") options.displayName = moduleToPatch;
1859 const displayName = options.displayName || module.displayName || module.name || module.constructor.displayName || module.constructor.name;
1860
1861 const patchId = `${displayName}.${functionName}`;
1862 const patch = this.patches.find(p => p.module == module && p.functionName == functionName) || this.makePatch(module, functionName, patchId);
1863 if (!patch.proxyFunction) this.rePatch(patch);
1864 const child = {
1865 caller,
1866 type,
1867 id: patch.counter,
1868 callback,
1869 unpatch: () => {
1870 patch.children.splice(patch.children.findIndex(cpatch => cpatch.id === child.id && cpatch.type === type), 1);
1871 if (patch.children.length <= 0) {
1872 let patchNum = this.patches.findIndex(p => p.module == module && p.functionName == functionName);
1873 this.patches[patchNum].revert();
1874 this.patches.splice(patchNum, 1);
1875 }
1876 }
1877 };
1878 patch.children.push(child);
1879 patch.counter++;
1880 return child.unpatch;
1881 }
1882
1883}
1884
1885/***/ }),
1886
1887/***/ "./src/modules/pluginupdater.js":
1888/*!**************************************!*\
1889 !*** ./src/modules/pluginupdater.js ***!
1890 \**************************************/
1891/*! exports provided: default */
1892/***/ (function(module, __webpack_exports__, __webpack_require__) {
1893
1894"use strict";
1895__webpack_require__.r(__webpack_exports__);
1896/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return PluginUpdater; });
1897/* harmony import */ var _pluginutilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./pluginutilities */ "./src/modules/pluginutilities.js");
1898/* harmony import */ var _patcher__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./patcher */ "./src/modules/patcher.js");
1899/* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js");
1900/* harmony import */ var _logger__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./logger */ "./src/modules/logger.js");
1901/* harmony import */ var _discordclasses__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./discordclasses */ "./src/modules/discordclasses.js");
1902/* harmony import */ var _discordmodules__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./discordmodules */ "./src/modules/discordmodules.js");
1903/* harmony import */ var ui__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ui */ "./src/ui/ui.js");
1904/**
1905 * Functions that check for and update existing plugins.
1906 * @module PluginUpdater
1907 * @version 0.1.2
1908 */
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918/**
1919 * Function that gets the remote version from the file contents.
1920 * @param {string} fileContent - the content of the remote file
1921 * @returns {string} - remote version
1922 * @callback module:PluginUpdater~versioner
1923 */
1924
1925/**
1926 * Comparator that takes the current version and the remote version,
1927 * then compares them returning `true` if there is an update and `false` otherwise.
1928 * @param {string} currentVersion - the current version of the plugin
1929 * @param {string} remoteVersion - the remote version of the plugin
1930 * @returns {boolean} - whether the plugin has an update or not
1931 * @callback module:PluginUpdater~comparator
1932 */
1933
1934class PluginUpdater {
1935
1936 static get CSS() { return __webpack_require__(/*! ../styles/updates.css */ "./src/styles/updates.css"); }
1937
1938 /**
1939 * Checks for updates for the specified plugin at the specified link. The final
1940 * parameter should link to the raw text of the plugin and will compare semantic
1941 * versions.
1942 * @param {string} pluginName - name of the plugin
1943 * @param {string} currentVersion - current version (semantic versioning only)
1944 * @param {string} updateURL - url to check for update
1945 * @param {module:PluginUpdater~versioner} [versioner] - versioner that finds the remote version. If not provided uses {@link module:PluginUpdater.defaultVersioner}.
1946 * @param {module:PluginUpdater~comparator} [comparator] - comparator that determines if there is an update. If not provided uses {@link module:PluginUpdater.defaultComparator}.
1947 */
1948 static checkForUpdate(pluginName, currentVersion, updateURL, versioner, comparator) {
1949 let updateLink = "https://raw.githubusercontent.com/rauenzi/BetterDiscordAddons/master/Plugins/" + pluginName + "/" + pluginName + ".plugin.js";
1950 if (updateURL) updateLink = updateURL;
1951 if (typeof(versioner) != "function") versioner = this.defaultVersioner;
1952 if (typeof(comparator) != "function") comparator = this.defaultComparator;
1953
1954 if (typeof window.PluginUpdates === "undefined") {
1955 window.PluginUpdates = {
1956 plugins: {},
1957 checkAll: function() {
1958 for (const key in this.plugins) {
1959 const plugin = this.plugins[key];
1960 if (!plugin.versioner) plugin.versioner = PluginUpdater.defaultVersioner;
1961 if (!plugin.comparator) plugin.comparator = PluginUpdater.defaultComparator;
1962 PluginUpdater.processUpdateCheck(plugin.name, plugin.raw);
1963 }
1964 },
1965 interval: setInterval(() => {
1966 window.PluginUpdates.checkAll();
1967 }, 7200000)
1968 };
1969 this.patchPluginList();
1970 }
1971
1972 window.PluginUpdates.plugins[updateLink] = {name: pluginName, raw: updateLink, version: currentVersion, versioner: versioner, comparator: comparator};
1973 PluginUpdater.processUpdateCheck(pluginName, updateLink);
1974 }
1975
1976 /**
1977 * Will check for updates and automatically show or remove the update notice
1978 * bar based on the internal result. Better not to call this directly and to
1979 * instead use {@link module:PluginUpdater.checkForUpdate}.
1980 * @param {string} pluginName - name of the plugin to check
1981 * @param {string} updateLink - link to the raw text version of the plugin
1982 */
1983 static processUpdateCheck(pluginName, updateLink) {
1984 const request = __webpack_require__(/*! request */ "request");
1985 request(updateLink, (error, response, result) => {
1986 if (error) return;
1987 const remoteVersion = window.PluginUpdates.plugins[updateLink].versioner(result);
1988 const hasUpdate = window.PluginUpdates.plugins[updateLink].comparator(window.PluginUpdates.plugins[updateLink].version, remoteVersion);
1989 if (hasUpdate) this.showUpdateNotice(pluginName, updateLink);
1990 else this.removeUpdateNotice(pluginName);
1991 });
1992 }
1993
1994 /**
1995 * The default versioner used as {@link module:PluginUpdater~versioner} for {@link module:PluginUpdater.checkForUpdate}.
1996 * 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
1997 * to this style as this will be used as default.
1998 * @param {string} currentVersion
1999 * @param {string} content
2000 */
2001 static defaultVersioner(content) {
2002 const remoteVersion = content.match(/['"][0-9]+\.[0-9]+\.[0-9]+['"]/i);
2003 if (!remoteVersion) return "0.0.0";
2004 return remoteVersion.toString().replace(/['"]/g, "");
2005 }
2006
2007 /**
2008 * The default comparator used as {@link module:PluginUpdater~comparator} for {@link module:PluginUpdater.checkForUpdate}.
2009 * 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
2010 * to this style as this will be used as default.
2011 * @param {string} currentVersion
2012 * @param {string} content
2013 */
2014 static defaultComparator(currentVersion, remoteVersion) {
2015 currentVersion = currentVersion.split(".").map((e) => {return parseInt(e);});
2016 remoteVersion = remoteVersion.split(".").map((e) => {return parseInt(e);});
2017
2018 if (remoteVersion[0] > currentVersion[0]) return true;
2019 else if (remoteVersion[0] == currentVersion[0] && remoteVersion[1] > currentVersion[1]) return true;
2020 else if (remoteVersion[0] == currentVersion[0] && remoteVersion[1] == currentVersion[1] && remoteVersion[2] > currentVersion[2]) return true;
2021 return false;
2022 }
2023
2024 static patchPluginList() {
2025 try {
2026 V2C_ContentColumn.prototype;
2027 }
2028 catch (e) {return;}
2029 _patcher__WEBPACK_IMPORTED_MODULE_1__["default"].after("ZeresLibrary", V2C_ContentColumn.prototype, "componentDidMount", (self) => {
2030 if (self._reactInternalFiber.key != "pcolumn") return;
2031 const column = _discordmodules__WEBPACK_IMPORTED_MODULE_5__["default"].ReactDOM.findDOMNode(self);
2032 if (!column) return;
2033 const button = column.getElementsByClassName("bd-pfbtn")[0];
2034 if (!button || button.nextElementSibling.classList.contains("bd-updatebtn")) return;
2035 button.after(PluginUpdater.createUpdateButton());
2036 });
2037 const button = document.getElementsByClassName("bd-pfbtn")[0];
2038 if (!button || !button.textContent.toLowerCase().includes("plugin") || button.nextElementSibling.classList.contains("bd-updatebtn")) return;
2039 button.after(PluginUpdater.createUpdateButton());
2040 }
2041
2042 /**
2043 * Creates the update button found in the plugins page of BetterDiscord
2044 * settings. Returned button will already have listeners to create the tooltip.
2045 * @returns {HTMLElement} check for update button
2046 */
2047 static createUpdateButton() {
2048 const updateButton = _domtools__WEBPACK_IMPORTED_MODULE_2__["default"].parseHTML(`<button class="bd-pfbtn bd-updatebtn" style="left: 220px;">Check for Updates</button>`);
2049 updateButton.onclick = function () {
2050 window.PluginUpdates.checkAll();
2051 };
2052 const tooltip = new ui__WEBPACK_IMPORTED_MODULE_6__["EmulatedTooltip"](updateButton, "Checks for updates of plugins that support this feature. Right-click for a list.");
2053 updateButton.oncontextmenu = function () {
2054 if (!window.PluginUpdates || !window.PluginUpdates.plugins) return;
2055 tooltip.label = Object.values(window.PluginUpdates.plugins).map(p => p.name).join(", ");
2056 tooltip.side = "bottom";
2057 tooltip.show();
2058 updateButton.onmouseout = function() {
2059 tooltip.label = "Checks for updates of plugins that support this feature. Right-click for a list.";
2060 tooltip.side = "top";
2061 };
2062 };
2063 return updateButton;
2064 }
2065
2066 /**
2067 * Will download the latest version and replace the the old plugin version.
2068 * Will also update the button in the update bar depending on if the user
2069 * is using RestartNoMore plugin by square {@link https://github.com/Inve1951/BetterDiscordStuff/blob/master/plugins/restartNoMore.plugin.js}
2070 * @param {string} pluginName - name of the plugin to download
2071 * @param {string} updateLink - link to the raw text version of the plugin
2072 */
2073 static downloadPlugin(pluginName, updateLink) {
2074 const request = __webpack_require__(/*! request */ "request");
2075 const fileSystem = __webpack_require__(/*! fs */ "fs");
2076 const path = __webpack_require__(/*! path */ "path");
2077 request(updateLink, async (error, response, body) => {
2078 if (error) return _logger__WEBPACK_IMPORTED_MODULE_3__["default"].warn("PluginUpdates", "Unable to get update for " + pluginName);
2079 const remoteVersion = window.PluginUpdates.plugins[updateLink].versioner(body);
2080 let filename = updateLink.split("/");
2081 filename = filename[filename.length - 1];
2082 const file = path.join(_pluginutilities__WEBPACK_IMPORTED_MODULE_0__["default"].getPluginsFolder(), filename);
2083 await new Promise(r => fileSystem.writeFile(file, body, r));
2084 ui__WEBPACK_IMPORTED_MODULE_6__["Toasts"].success(`${pluginName} ${window.PluginUpdates.plugins[updateLink].version} has been replaced by ${pluginName} ${remoteVersion}`);
2085 this.removeUpdateNotice(pluginName);
2086
2087 const oldRNM = window.bdplugins["Restart-No-More"] && window.pluginCookie["Restart-No-More"];
2088 const newRNM = window.bdplugins["Restart No More"] && window.pluginCookie["Restart No More"];
2089 const BBDLoader = window.settingsCookie["fork-ps-5"];
2090 if (oldRNM || newRNM || BBDLoader) return;
2091 if (!window.PluginUpdates.downloaded) {
2092 window.PluginUpdates.downloaded = [];
2093 const button = _domtools__WEBPACK_IMPORTED_MODULE_2__["default"].parseHTML(`<button class="btn btn-reload ${_discordclasses__WEBPACK_IMPORTED_MODULE_4__["default"].Notices.btn} ${_discordclasses__WEBPACK_IMPORTED_MODULE_4__["default"].Notices.button}">Reload</button>`);
2094 const tooltip = new ui__WEBPACK_IMPORTED_MODULE_6__["EmulatedTooltip"](button, window.PluginUpdates.downloaded.join(", "), {side: "top"});
2095 button.addEventListener("click", (e) => {
2096 e.preventDefault();
2097 window.location.reload(false);
2098 });
2099 button.addEventListener("mouseenter", () => {
2100 tooltip.label = window.PluginUpdates.downloaded.join(", ");
2101 });
2102 document.getElementById("pluginNotice").append(button);
2103 }
2104 window.PluginUpdates.plugins[updateLink].version = remoteVersion;
2105 window.PluginUpdates.downloaded.push(pluginName);
2106 });
2107 }
2108
2109 /**
2110 * Will show the update notice top bar seen in Discord. Better not to call
2111 * this directly and to instead use {@link module:PluginUpdater.checkForUpdate}.
2112 * @param {string} pluginName - name of the plugin
2113 * @param {string} updateLink - link to the raw text version of the plugin
2114 */
2115 static showUpdateNotice(pluginName, updateLink) {
2116 if (!document.getElementById("pluginNotice")) {
2117 const noticeElement = _domtools__WEBPACK_IMPORTED_MODULE_2__["default"].parseHTML(`<div class="${_discordclasses__WEBPACK_IMPORTED_MODULE_4__["default"].Notices.notice} ${_discordclasses__WEBPACK_IMPORTED_MODULE_4__["default"].Notices.noticeInfo}" id="pluginNotice">
2118 <div class="${_discordclasses__WEBPACK_IMPORTED_MODULE_4__["default"].Notices.dismiss}" id="pluginNoticeDismiss"></div>
2119 <span class="notice-message">The following plugins have updates:</span> <strong id="outdatedPlugins"></strong>
2120 </div>`);
2121 _domtools__WEBPACK_IMPORTED_MODULE_2__["default"].query("[class*='app-'] > [class*='app-']").prepend(noticeElement);
2122 noticeElement.querySelector("#pluginNoticeDismiss").addEventListener("click", async () => {
2123 noticeElement.classList.add("closing");
2124 await new Promise(resolve => setTimeout(resolve, 400));
2125 noticeElement.remove();
2126 });
2127 }
2128 const pluginNoticeID = pluginName + "-notice";
2129 if (document.getElementById(pluginNoticeID)) return;
2130 const pluginNoticeElement = _domtools__WEBPACK_IMPORTED_MODULE_2__["default"].parseHTML(`<span id="${pluginNoticeID}">${pluginName}</span>`);
2131 pluginNoticeElement.addEventListener("click", () => {
2132 this.downloadPlugin(pluginName, updateLink);
2133 });
2134 if (document.getElementById("outdatedPlugins").querySelectorAll("span").length) document.getElementById("outdatedPlugins").append(_domtools__WEBPACK_IMPORTED_MODULE_2__["default"].createElement("<span class='separator'>, </span>"));
2135 document.getElementById("outdatedPlugins").append(pluginNoticeElement);
2136 }
2137
2138 /**
2139 * Will remove the plugin from the update notice top bar seen in Discord.
2140 * Better not to call this directly and to instead use {@link module:PluginUpdater.checkForUpdate}.
2141 * @param {string} pluginName - name of the plugin
2142 */
2143 static removeUpdateNotice(pluginName) {
2144 if (!document.getElementById("outdatedPlugins")) return;
2145 const notice = document.getElementById(pluginName + "-notice");
2146 if (notice) {
2147 if (notice.nextElementSibling && notice.nextElementSibling.matches(".separator")) notice.nextElementSibling.remove();
2148 else if (notice.previousElementSibling && notice.previousElementSibling.matches(".separator")) notice.previousElementSibling.remove();
2149 notice.remove();
2150 }
2151
2152 if (!document.getElementById("outdatedPlugins").querySelectorAll("span").length) {
2153 if (document.querySelector("#pluginNotice .btn-reload")) document.querySelector("#pluginNotice .notice-message").textContent = "To finish updating you need to reload.";
2154 else document.getElementById("pluginNoticeDismiss").click();
2155 }
2156 }
2157}
2158
2159/***/ }),
2160
2161/***/ "./src/modules/pluginutilities.js":
2162/*!****************************************!*\
2163 !*** ./src/modules/pluginutilities.js ***!
2164 \****************************************/
2165/*! exports provided: default */
2166/***/ (function(module, __webpack_exports__, __webpack_require__) {
2167
2168"use strict";
2169__webpack_require__.r(__webpack_exports__);
2170/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return PluginUtilities; });
2171/* harmony import */ var _logger__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./logger */ "./src/modules/logger.js");
2172/* harmony import */ var _utilities__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./utilities */ "./src/modules/utilities.js");
2173/* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js");
2174
2175
2176
2177
2178/**
2179 * A series of useful functions for BetterDiscord plugins.
2180 * @module PluginUtilities
2181 * @version 0.2.5
2182 */
2183
2184
2185 class PluginUtilities {
2186
2187 /**
2188 * Loads data through BetterDiscord's API.
2189 * @param {string} name - name for the file (usually plugin name)
2190 * @param {string} key - which key the data is saved under
2191 * @param {object} defaultData - default data to populate the object with
2192 * @returns {object} the combined saved and default data
2193 */
2194 static loadData(name, key, defaultData) {
2195 try { return _utilities__WEBPACK_IMPORTED_MODULE_1__["default"].extend(defaultData ? defaultData : {}, BdApi.getData(name, key)); }
2196 catch (err) { _logger__WEBPACK_IMPORTED_MODULE_0__["default"].err(name, "Unable to load data: ", err); }
2197 }
2198
2199 /**
2200 * Saves data through BetterDiscord's API.
2201 * @param {string} name - name for the file (usually plugin name)
2202 * @param {string} key - which key the data should be saved under
2203 * @param {object} data - data to save
2204 */
2205 static saveData(name, key, data) {
2206 try { BdApi.setData(name, key, data); }
2207 catch (err) { _logger__WEBPACK_IMPORTED_MODULE_0__["default"].err(name, "Unable to save data: ", err); }
2208 }
2209
2210 /**
2211 * Loads settings through BetterDiscord's API.
2212 * @param {string} name - name for the file (usually plugin name)
2213 * @param {object} defaultData - default data to populate the object with
2214 * @returns {object} the combined saved and default settings
2215 */
2216 static loadSettings(name, defaultSettings) {
2217 return this.loadData(name, "settings", defaultSettings);
2218 }
2219
2220 /**
2221 * Saves settings through BetterDiscord's API.
2222 * @param {string} name - name for the file (usually plugin name)
2223 * @param {object} data - settings to save
2224 */
2225 static saveSettings(name, data) {
2226 this.saveData(name, "settings", data);
2227 }
2228
2229 /**
2230 * Get the full path to the BetterDiscord folder.
2231 * @returns {string} full path to the BetterDiscord folder
2232 */
2233 static getBDFolder(subtarget = "") {
2234 const process = __webpack_require__(/*! process */ "process");
2235 const path = __webpack_require__(/*! path */ "path");
2236 if (process.env.injDir) return path.resolve(process.env.injDir, subtarget);
2237 switch (process.platform) {
2238 case "win32":
2239 return path.resolve(process.env.appdata, "BetterDiscord/", subtarget);
2240 case "darwin":
2241 return path.resolve(process.env.HOME, "Library/Preferences/", "BetterDiscord/", subtarget);
2242 default:
2243 return path.resolve(process.env.XDG_CONFIG_HOME ? process.env.XDG_CONFIG_HOME : process.env.HOME + "/.config", "BetterDiscord/", subtarget);
2244 }
2245 }
2246
2247 /**
2248 * Get the full path to the plugins folder.
2249 * @returns {string} full path to the plugins folder
2250 */
2251 static getPluginsFolder() {
2252 return this.getBDFolder("plugins/");
2253 }
2254
2255 /**
2256 * Get the full path to the themes folder.
2257 * @returns {string} full path to the themes folder
2258 */
2259 static getThemesFolder() {
2260 return this.getBDFolder("themes/");
2261 }
2262
2263 /**
2264 * Adds a callback to a set of listeners for onSwitch.
2265 * @param {callable} callback - basic callback to happen on channel switch
2266 */
2267 static addOnSwitchListener(callback) {
2268 __webpack_require__(/*! electron */ "electron").remote.getCurrentWebContents().on("did-navigate-in-page", callback);
2269 }
2270
2271 /**
2272 * Removes the listener added by {@link InternalUtilities.addOnSwitchListener}.
2273 * @param {callable} callback - callback to remove from the listener list
2274 */
2275 static removeOnSwitchListener(callback) {
2276 __webpack_require__(/*! electron */ "electron").remote.getCurrentWebContents().removeListener("did-navigate-in-page", callback);
2277 }
2278
2279 /**
2280 * Adds a style to the document.
2281 * @param {string} id - identifier to use as the element id
2282 * @param {string} css - css to add to the document
2283 */
2284 static addStyle(id, css) {
2285 document.head.append(_domtools__WEBPACK_IMPORTED_MODULE_2__["default"].createElement(`<style id="${id}">${css}</style>`));
2286 }
2287
2288 /**
2289 * Removes a style from the document.
2290 * @param {string} id - original identifier used
2291 */
2292 static removeStyle(id) {
2293 const element = document.getElementById(id);
2294 if (element) element.remove();
2295 }
2296
2297 /**
2298 * Adds/requires a remote script to be loaded
2299 * @param {string} id - identifier to use for this script
2300 * @param {string} url - url from which to load the script
2301 * @returns {Promise} promise that resolves when the script is loaded
2302 */
2303 static addScript(id, url) {
2304 return new Promise(resolve => {
2305 const script = document.createElement("script");
2306 script.id = id;
2307 script.src = url;
2308 script.type = "text/javascript";
2309 script.onload = resolve;
2310 document.head.append(script);
2311 });
2312 }
2313
2314 /**
2315 * Removes a remote script from the document.
2316 * @param {string} id - original identifier used
2317 */
2318 static removeScript(id) {
2319 const element = document.getElementById(id);
2320 if (element) element.remove();
2321 }
2322}
2323
2324
2325
2326
2327/***/ }),
2328
2329/***/ "./src/modules/reactcomponents.js":
2330/*!****************************************!*\
2331 !*** ./src/modules/reactcomponents.js ***!
2332 \****************************************/
2333/*! exports provided: ReactHelpers, default */
2334/***/ (function(module, __webpack_exports__, __webpack_require__) {
2335
2336"use strict";
2337__webpack_require__.r(__webpack_exports__);
2338/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ReactHelpers", function() { return Helpers; });
2339/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return ReactComponents; });
2340/* harmony import */ var _utilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utilities */ "./src/modules/utilities.js");
2341/* harmony import */ var _patcher__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./patcher */ "./src/modules/patcher.js");
2342/* harmony import */ var _reflection__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./reflection */ "./src/modules/reflection.js");
2343/* harmony import */ var _discordmodules__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./discordmodules */ "./src/modules/discordmodules.js");
2344/* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js");
2345/* harmony import */ var _reacttools__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./reacttools */ "./src/modules/reacttools.js");
2346/**
2347 * BetterDiscord React Component Manipulations
2348 * Original concept and some code by samogot - https://github.com/samogot / https://github.com/samogot/betterdiscord-plugins/tree/master/v2/1Lib%20Discord%20Internals
2349 *
2350 * Copyright (c) 2015-present JsSucks - https://github.com/JsSucks
2351 * All rights reserved.
2352 * https://github.com/JsSucks - https://betterdiscord.net
2353 *
2354 * This source code is licensed under the MIT license found in the
2355 * LICENSE file in the root directory of this source tree.
2356*/
2357
2358// import Logger from "./logger";
2359
2360
2361
2362
2363
2364
2365
2366class Helpers {
2367 static get plannedActions() {
2368 return this._plannedActions || (this._plannedActions = new Map());
2369 }
2370
2371 static recursiveArray(parent, key, count = 1) {
2372 let index = 0;
2373 function* innerCall(parent, key) {
2374 const item = parent[key];
2375 if (item instanceof Array) {
2376 for (const subKey of item.keys()) {
2377 yield* innerCall(item, subKey);
2378 }
2379 return;
2380 }
2381 yield {item, parent, key, index: index++, count};
2382 }
2383
2384 return innerCall(parent, key);
2385 }
2386
2387 static recursiveArrayCount(parent, key) {
2388 let count = 0;
2389 // eslint-disable-next-line no-empty-pattern
2390 for (let {} of this.recursiveArray(parent, key)) ++count;
2391 return this.recursiveArray(parent, key, count);
2392 }
2393
2394 static get recursiveChildren() {
2395 return function* (parent, key, index = 0, count = 1) {
2396 const item = parent[key];
2397 yield {item, parent, key, index, count};
2398 if (item && item.props && item.props.children) {
2399 for (const {parent, key, index, count} of this.recursiveArrayCount(item.props, "children")) {
2400 yield* this.recursiveChildren(parent, key, index, count);
2401 }
2402 }
2403 };
2404 }
2405
2406 static returnFirst(iterator, process) {
2407 for (const child of iterator) {
2408 const retVal = process(child);
2409 if (retVal !== undefined) return retVal;
2410 }
2411 }
2412
2413 static getFirstChild(rootParent, rootKey, selector) {
2414 const getDirectChild = (item, selector) => {
2415 if (item && item.props && item.props.children) {
2416 return this.returnFirst(this.recursiveArrayCount(item.props, "children"), checkFilter.bind(null, selector));
2417 }
2418 };
2419 const checkFilter = (selector, {item, parent, key, count, index}) => {
2420 let match = true;
2421 if (selector.type) match = item && selector.type === item.type;
2422 if (match && selector.tag) match = item && typeof item.type === "string" && selector.tag === item.type;
2423 if (match && selector.className) {
2424 match = item && item.props && typeof item.props.className === "string";
2425 if (match) {
2426 const classes = item.props.className.split(" ");
2427 if (selector.className === true) match = !!classes[0];
2428 else if (typeof selector.className === "string") match = classes.includes(selector.className);
2429 else if (selector.className instanceof RegExp) match = !!classes.find(cls => selector.className.test(cls));
2430 else match = false;
2431 }
2432 }
2433 if (match && selector.text) {
2434 if (selector.text === true) match = typeof item === "string";
2435 else if (typeof selector.text === "string") match = item === selector.text;
2436 else if (selector.text instanceof RegExp) match = typeof item === "string" && selector.text.test(item);
2437 else match = false;
2438 }
2439 if (match && selector.nthChild) match = index === (selector.nthChild < 0 ? count + selector.nthChild : selector.nthChild);
2440 if (match && selector.hasChild) match = getDirectChild(item, selector.hasChild);
2441 if (match && selector.hasSuccessor) match = item && !!this.getFirstChild(parent, key, selector.hasSuccessor).item;
2442 if (match && selector.eq) {
2443 --selector.eq;
2444 return;
2445 }
2446 if (match) {
2447 if (selector.child) return getDirectChild(item, selector.child);
2448 else if (selector.successor) return this.getFirstChild(parent, key, selector.successor);
2449 return {item, parent, key};
2450 }
2451 };
2452 return this.returnFirst(this.recursiveChildren(rootParent, rootKey), checkFilter.bind(null, selector)) || {};
2453 }
2454
2455 static parseSelector(selector) {
2456 if (selector.startsWith(".")) return {className: selector.substr(1)};
2457 if (selector.startsWith("#")) return {id: selector.substr(1)};
2458 return {};
2459 }
2460
2461 static findByProp(obj, what, value) {
2462 if (obj.hasOwnProperty(what) && obj[what] === value) return obj;
2463 if (obj.props && !obj.children) return this.findByProp(obj.props, what, value);
2464 if (!obj.children) return null;
2465 if (!(obj.children instanceof Array)) return this.findByProp(obj.children, what, value);
2466 for (const child of obj.children) {
2467 if (!child) continue;
2468 const findInChild = this.findByProp(child, what, value);
2469 if (findInChild) return findInChild;
2470 }
2471 return null;
2472 }
2473
2474 static findProp(obj, what) {
2475 if (obj.hasOwnProperty(what)) return obj[what];
2476 if (obj.props && !obj.children) return this.findProp(obj.props, what);
2477 if (!obj.children) return null;
2478 if (!(obj.children instanceof Array)) return this.findProp(obj.children, what);
2479 for (const child of obj.children) {
2480 if (!child) continue;
2481 const findInChild = this.findProp(child, what);
2482 if (findInChild) return findInChild;
2483 }
2484 return null;
2485 }
2486
2487 static get React() {
2488 return _discordmodules__WEBPACK_IMPORTED_MODULE_3__["default"].React;
2489 }
2490
2491 static get ReactDOM() {
2492 return _discordmodules__WEBPACK_IMPORTED_MODULE_3__["default"].ReactDOM;
2493 }
2494}
2495
2496
2497
2498class ReactComponent {
2499 constructor(id, component, selector, filter) {
2500 this.id = id;
2501 this.component = component;
2502 // this.important = important;
2503 this.selector = selector;
2504 this.filter = filter;
2505 }
2506
2507 forceUpdateAll() {
2508 if (!this.selector) return;
2509 for (const e of document.querySelectorAll(this.selector)) {
2510 Object(_reflection__WEBPACK_IMPORTED_MODULE_2__["default"])(e).forceUpdate(this);
2511 }
2512 }
2513}
2514
2515/**
2516 * Methods for obtaining and interacting with react components.
2517 * @module ReactComponents
2518 * @version 0.0.1
2519 */
2520class ReactComponents {
2521 static get components() {return this._components || (this._components = []);}
2522 static get unknownComponents() {return this._unknownComponents || (this._unknownComponents = []);}
2523 static get listeners() {return this._listeners || (this._listeners = []);}
2524 static get nameSetters() {return this._nameSetters || (this._nameSetters = []);}
2525
2526 static get ReactComponent() {return ReactComponent;}
2527 static get Helpers() {return Helpers;}
2528 static get AutoPatcher() {return ReactAutoPatcher;}
2529
2530 static push(component, selector, filter) {
2531 if (!(component instanceof Function)) return null;
2532 const {displayName} = component;
2533 if (!displayName) return this.processUnknown(component);
2534
2535 const have = this.components.find(comp => comp.id === displayName);
2536 if (have) {
2537 if (!have.selector) have.selector = selector;
2538 if (!have.filter) have.filter = filter;
2539 return component;
2540 }
2541
2542 const c = new ReactComponent(displayName, component, selector, filter);
2543 this.components.push(c);
2544 // if (!have) this.components.push(c);
2545
2546 const listener = this.listeners.find(listener => listener.id === displayName);
2547 if (listener) {
2548 for (const l of listener.listeners) l(c);
2549 _utilities__WEBPACK_IMPORTED_MODULE_0__["default"].removeFromArray(this.listeners, listener);
2550 }
2551
2552 // for (const listen of this.listeners) {
2553 // if (!listen.filter) continue;
2554 // }
2555
2556 return c;
2557 }
2558
2559 /**
2560 * Finds a component from the components array or by waiting for it to be mounted.
2561 * @param {String} name The component's name
2562 * @param {Object} selector A selector to look for
2563 * @return {Promise<ReactComponent>}
2564 */
2565 static async getComponentByName(name, selector) {
2566 return this.getComponent(name, selector, m => m.displayName == name);
2567 }
2568
2569 /**
2570 * Finds a component from the components array or by waiting for it to be mounted.
2571 * @param {String} name The component's name
2572 * @param {Object} selector A selector to look for
2573 * @param {Function} filter A function to filter components if a single element is rendered by multiple components
2574 * @return {Promise<ReactComponent>}
2575 */
2576 static async getComponent(name, selector, filter) {
2577 const have = this.components.find(c => c.id === name);
2578 if (have) return have;
2579
2580 if (selector) {
2581 const callback = () => {
2582 if (this.components.find(c => c.id === name)) {
2583 // Logger.info("ReactComponents", `Important component ${name} already found`);
2584 _domtools__WEBPACK_IMPORTED_MODULE_4__["default"].observer.unsubscribe(observerSubscription);
2585 return;
2586 }
2587
2588 const elements = document.querySelectorAll(selector);
2589 if (!elements.length) return;
2590
2591 let component, reflect;
2592 for (const element of elements) {
2593 reflect = Object(_reflection__WEBPACK_IMPORTED_MODULE_2__["default"])(element);
2594 component = filter ? reflect.components.find(filter) : reflect.component;
2595 if (component) break;
2596 }
2597
2598 if (!component && filter) return;// Logger.log("ReactComponents", ["Found elements matching the query selector but no components passed the filter"]);
2599
2600 _domtools__WEBPACK_IMPORTED_MODULE_4__["default"].observer.unsubscribe(observerSubscription);
2601
2602 if (!component) return;// Logger.err("ReactComponents", [`FAILED TO GET IMPORTANT COMPONENT ${name} WITH REFLECTION FROM`, elements]);
2603
2604 if (!component.displayName) component.displayName = name;
2605 // if (component.displayName && component.displayName != name) {
2606 // let existing = this.listeners.find(l => l.id === component.displayName);
2607 // let current = this.listeners.find(l => l.id === name);
2608 // if (!existing) {current.id = component.displayName;}
2609 // else {
2610 // existing.listeners.push(current.listeners);
2611 // Utilities.removeFromArray(this.listeners, current);
2612 // }
2613 // }
2614 //Logger.info("ReactComponents", [`Found important component ${name} with reflection`, reflect]);
2615
2616 this.push(component, selector, filter);
2617 };
2618
2619 const observerSubscription = _domtools__WEBPACK_IMPORTED_MODULE_4__["default"].observer.subscribeToQuerySelector(callback, selector, null, true);
2620 setTimeout(callback, 0);
2621 }
2622
2623 let listener = this.listeners.find(l => l.id === name);
2624 if (!listener) {
2625 this.listeners.push(listener = {
2626 id: name,
2627 listeners: [],
2628 filter
2629 });
2630 }
2631
2632
2633 return new Promise(resolve => {
2634 listener.listeners.push(resolve);
2635 });
2636 }
2637
2638 static setName(name, filter) {
2639 const have = this.components.find(c => c.id === name);
2640 if (have) return have;
2641
2642 for (const [rci, rc] of this.unknownComponents.entries()) {
2643 if (filter(rc.component)) {
2644 rc.component.displayName = name;
2645 this.unknownComponents.splice(rci, 1);
2646 return this.push(rc.component);
2647 }
2648 }
2649 return this.nameSetters.push({name, filter});
2650 }
2651
2652 static processUnknown(component) {
2653 const have = this.unknownComponents.find(c => c.component === component);
2654 for (const [fi, filter] of this.nameSetters.entries()) {
2655 if (filter.filter.filter(component)) {
2656 // Logger.log("ReactComponents", "Filter match!");
2657 component.displayName = filter.name;
2658 this.nameSetters.splice(fi, 1);
2659 return this.push(component);
2660 }
2661 }
2662 if (have) return have;
2663 this.unknownComponents.push(component);
2664 return component;
2665 }
2666
2667 static *recursiveComponents(internalInstance = _reacttools__WEBPACK_IMPORTED_MODULE_5__["default"].rootInstance) {
2668 if (internalInstance.stateNode) yield internalInstance.stateNode;
2669 if (internalInstance.sibling) yield *this.recursiveComponents(internalInstance.sibling);
2670 if (internalInstance.child) yield *this.recursiveComponents(internalInstance.child);
2671 }
2672}
2673
2674class ReactAutoPatcher {
2675 /**
2676 * Wait for React to be loaded and patch it's createElement to store all unknown components.
2677 * Also patches some known components.
2678 */
2679 static async autoPatch() {
2680 this.unpatchCreateElement = _patcher__WEBPACK_IMPORTED_MODULE_1__["default"].before("ReactComponents", _discordmodules__WEBPACK_IMPORTED_MODULE_3__["default"].React, "createElement", (react, [component]) => ReactComponents.push(component));
2681 this.unpatchCreateElement = _patcher__WEBPACK_IMPORTED_MODULE_1__["default"].instead("ReactComponents", _discordmodules__WEBPACK_IMPORTED_MODULE_3__["default"].React.Component.prototype, "UNSAFE_componentWillMount", (component) => ReactComponents.push(component));
2682 this.unpatchCreateElement = _patcher__WEBPACK_IMPORTED_MODULE_1__["default"].instead("ReactComponents", _discordmodules__WEBPACK_IMPORTED_MODULE_3__["default"].React.Component.prototype, "componentWillMount", (component) => ReactComponents.push(component));
2683 // this.patchComponents();
2684 }
2685
2686 /**
2687 * Finds and processes all currently available react components.
2688 */
2689 static processAll() {
2690 for (const component of ReactComponents.recursiveComponents()) {
2691 ReactComponents.push(component.constructor);
2692 }
2693 }
2694}
2695
2696
2697/***/ }),
2698
2699/***/ "./src/modules/reacttools.js":
2700/*!***********************************!*\
2701 !*** ./src/modules/reacttools.js ***!
2702 \***********************************/
2703/*! exports provided: default */
2704/***/ (function(module, __webpack_exports__, __webpack_require__) {
2705
2706"use strict";
2707__webpack_require__.r(__webpack_exports__);
2708/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return ReactTools; });
2709/* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js");
2710/* harmony import */ var _discordmodules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./discordmodules */ "./src/modules/discordmodules.js");
2711/* harmony import */ var _utilities__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./utilities */ "./src/modules/utilities.js");
2712/* harmony import */ var _reflection__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./reflection */ "./src/modules/reflection.js");
2713/**
2714 * Helpful utilities for dealing with getting react information from DOM objects.
2715 * @module ReactTools
2716 * @version 0.0.5
2717 */
2718
2719
2720
2721
2722
2723
2724class ReactTools {
2725
2726 /**
2727 * Performs reflection on a specific node.
2728 * @param {(HTMLElement|jQuery|Selector)} node - node or selector to reflect on.
2729 */
2730 static Reflect(node) {
2731 return Object(_reflection__WEBPACK_IMPORTED_MODULE_3__["default"])(node);
2732 }
2733
2734 static get rootInstance() {return document.getElementById("app-mount")._reactRootContainer._internalRoot.current;}
2735
2736 /**
2737 * Grabs the react internal instance of a specific node.
2738 * @param {(HTMLElement|jQuery)} node - node to obtain react instance of
2739 * @return {object} the internal react instance
2740 */
2741 static getReactInstance(node) {
2742 if (!(node instanceof window.jQuery) && !(node instanceof Element)) return undefined;
2743 const domNode = node instanceof window.jQuery ? node[0] : node;
2744 return domNode[Object.keys(domNode).find((key) => key.startsWith("__reactInternalInstance"))];
2745 }
2746
2747 /**
2748 * Grabs a value from the react internal instance. Allows you to grab
2749 * long depth values safely without accessing no longer valid properties.
2750 * @param {(HTMLElement|jQuery)} node - node to obtain react instance of
2751 * @param {string} path - path to the requested value
2752 * @return {(*|undefined)} the value requested or undefined if not found.
2753 */
2754 static getReactProperty(node, path) {
2755 return _utilities__WEBPACK_IMPORTED_MODULE_2__["default"].getNestedProp(this.getReactInstance(node), path);
2756 }
2757
2758 /**
2759 * Grabs a value from the react internal instance. Allows you to grab
2760 * long depth values safely without accessing no longer valid properties.
2761 * @param {(HTMLElement|jQuery)} node - node to obtain react instance of
2762 * @param {object} options - options for the search
2763 * @param {array} [options.include] - list of items to include from the search
2764 * @param {array} [options.exclude=["Popout", "Tooltip", "Scroller", "BackgroundFlash"]] - list of items to exclude from the search
2765 * @param {callable} [options.filter=_=>_] - filter to check the current instance with (should return a boolean)
2766 * @return {(*|null)} the owner instance or undefined if not found.
2767 */
2768 static getOwnerInstance(node, {include, exclude = ["Popout", "Tooltip", "Scroller", "BackgroundFlash"], filter = _ => _} = {}) {
2769 if (node === undefined) return undefined;
2770 const excluding = include === undefined;
2771 const nameFilter = excluding ? exclude : include;
2772 function getDisplayName(owner) {
2773 const type = owner.type;
2774 if (!type) return null;
2775 return type.displayName || type.name || null;
2776 }
2777 function classFilter(owner) {
2778 const name = getDisplayName(owner);
2779 return (name !== null && !!(nameFilter.includes(name) ^ excluding));
2780 }
2781
2782 let curr = this.getReactInstance(node);
2783 for (curr = curr && curr.return; !_utilities__WEBPACK_IMPORTED_MODULE_2__["default"].isNil(curr); curr = curr.return) {
2784 if (_utilities__WEBPACK_IMPORTED_MODULE_2__["default"].isNil(curr)) continue;
2785 const owner = curr.stateNode;
2786 if (!_utilities__WEBPACK_IMPORTED_MODULE_2__["default"].isNil(owner) && !(owner instanceof HTMLElement) && classFilter(curr) && filter(owner)) return owner;
2787 }
2788
2789 return null;
2790 }
2791
2792 /**
2793 * Creates and renders a react element that wraps dom elements.
2794 * @param {(HTMLElement|Array<HTMLElement>)} element - element or array of elements to wrap into a react element
2795 * @returns {object} - rendered react element
2796 */
2797 static createWrappedElement(element) {
2798 if (Array.isArray(element)) element = _domtools__WEBPACK_IMPORTED_MODULE_0__["default"].wrap(element);
2799 return _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].React.createElement(this.wrapElement(element));
2800 }
2801
2802 /**
2803 * Creates an unrendered react component that wraps dom elements.
2804 * @param {(HTMLElement|Array<HTMLElement>)} element - element or array of elements to wrap into a react component
2805 * @returns {object} - unrendered react component
2806 */
2807 static wrapElement(element) {
2808 if (Array.isArray(element)) element = _domtools__WEBPACK_IMPORTED_MODULE_0__["default"].wrap(element);
2809 return class ReactWrapper extends _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].React.Component {
2810 constructor(props) {
2811 super(props);
2812 this.element = element;
2813 }
2814
2815 componentDidMount() {this.refs.element.appendChild(this.element);}
2816 render() {return _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].React.createElement("div", {className: "react-wrapper", ref: "element"});}
2817 };
2818 }
2819}
2820
2821/***/ }),
2822
2823/***/ "./src/modules/reflection.js":
2824/*!***********************************!*\
2825 !*** ./src/modules/reflection.js ***!
2826 \***********************************/
2827/*! exports provided: default */
2828/***/ (function(module, __webpack_exports__, __webpack_require__) {
2829
2830"use strict";
2831__webpack_require__.r(__webpack_exports__);
2832/* harmony import */ var _logger__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./logger */ "./src/modules/logger.js");
2833/* harmony import */ var _webpackmodules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./webpackmodules */ "./src/modules/webpackmodules.js");
2834/* harmony import */ var _reactcomponents__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./reactcomponents */ "./src/modules/reactcomponents.js");
2835/**
2836 * BetterDiscord Reflection Module
2837 * Copyright (c) 2015-present JsSucks - https://github.com/JsSucks
2838 * All rights reserved.
2839 * https://betterdiscord.net
2840 *
2841 * This source code is licensed under the MIT license found in the
2842 * LICENSE file in the root directory of this source tree.
2843*/
2844
2845
2846
2847
2848
2849class Reflection {
2850 static reactInternalInstance(node) {
2851 if (!node) return null;
2852 if (!Object.keys(node) || !Object.keys(node).length) return null;
2853 const riiKey = Object.keys(node).find(k => k.startsWith("__reactInternalInstance"));
2854 return riiKey ? node[riiKey] : null;
2855 }
2856
2857 static findProp(node, prop) {
2858 const ii = this.reactInternalInstance(node);
2859 if (!ii) return null;
2860 const fir = this.findInReturn(ii, prop);
2861 if (fir) return fir;
2862 const fim = this.findInChildProps(ii, prop);
2863 if (fim) return fim;
2864 return null;
2865 }
2866
2867 static findInReturn(internalInstance, prop) {
2868 const r = internalInstance.return;
2869 if (!r) return null;
2870 let find = this.findMemoizedProp(r, prop);
2871 if (find) return find;
2872 find = this.findMemoizedState(r, prop);
2873 if (find) return find;
2874 return this.findInReturn(r, prop);
2875 }
2876
2877 static findMemoizedProp(obj, prop) {
2878 if (!obj.hasOwnProperty("memoizedProps")) return null;
2879 obj = obj.memoizedProps;
2880 return this.findPropIn(obj, prop);
2881 }
2882
2883 static findMemoizedState(obj, prop) {
2884 if (!obj.hasOwnProperty("memoizedState")) return null;
2885 obj = obj.memoizedState;
2886 return this.findPropIn(obj, prop);
2887 }
2888
2889 static findInChildProps(obj, prop) {
2890 try {
2891 const f = obj.children || obj.memoizedProps.children;
2892 if (!f.props) return null;
2893 if (!f.props.hasOwnProperty(prop)) return null;
2894 return f.props[prop];
2895 }
2896 catch (err) {
2897 return null;
2898 }
2899 }
2900
2901 static findPropIn(obj, prop) {
2902 if (obj && !(obj instanceof Array) && obj instanceof Object && obj.hasOwnProperty(prop)) return obj[prop];
2903 if (obj && obj instanceof Array) {
2904 const found = obj.find(mp => {
2905 if (mp.props && mp.props.hasOwnProperty(prop)) return true;
2906 });
2907 if (found) return found;
2908 }
2909 return null;
2910 }
2911
2912 static propIterator(obj, propNames) {
2913 if (obj === null || obj === undefined) return null;
2914 const curPropName = propNames.shift(1);
2915 if (!obj.hasOwnProperty(curPropName)) return null;
2916 const curProp = obj[curPropName];
2917 if (propNames.length === 0) {
2918 return curProp;
2919 }
2920 return this.propIterator(curProp, propNames);
2921 }
2922
2923 static getState(node) {
2924 const stateNode = this.getStateNode(node);
2925 if (stateNode) return stateNode.state;
2926 }
2927
2928 static getStateNode(node) {
2929 return this.getStateNodes(node)[0];
2930 }
2931
2932 static getStateNodes(node) {
2933 const instance = this.reactInternalInstance(node);
2934 const stateNodes = [];
2935 let lastInstance = instance;
2936
2937 while (lastInstance && lastInstance.return) {
2938 if (lastInstance.return.stateNode instanceof HTMLElement) break;
2939 if (lastInstance.return.stateNode) stateNodes.push(lastInstance.return.stateNode);
2940 lastInstance = lastInstance.return;
2941 }
2942
2943 return stateNodes;
2944 }
2945
2946 static getComponentStateNode(node, component) {
2947 if (component instanceof _reactcomponents__WEBPACK_IMPORTED_MODULE_2__["default"].ReactComponent) component = component.component;
2948
2949 for (const stateNode of this.getStateNodes(node)) {
2950 if (stateNode instanceof component) return stateNode;
2951 }
2952 }
2953
2954 static findStateNode(node, filter, first = true) {
2955 return this.getStateNodes(node)[first ? "find" : "filter"](filter);
2956 }
2957
2958 static getComponent(node) {
2959 return this.getComponents(node)[0];
2960 }
2961
2962 static getComponents(node) {
2963 const instance = this.reactInternalInstance(node);
2964 const components = [];
2965 let lastInstance = instance;
2966
2967 while (lastInstance && lastInstance.return) {
2968 if (typeof lastInstance.return.type === "string") break;
2969 if (lastInstance.return.type) components.push(lastInstance.return.type);
2970 lastInstance = lastInstance.return;
2971 }
2972
2973 return components;
2974 }
2975
2976 static findComponent(node, filter, first = true) {
2977 return this.getComponents(node)[first ? "find" : "filter"](filter);
2978 }
2979}
2980
2981const propsProxyHandler = {
2982 get(node, prop) {
2983 return Reflection.findProp(node, prop);
2984 }
2985};
2986
2987/* harmony default export */ __webpack_exports__["default"] = (function(node) {
2988 return new class ReflectionInstance {
2989 constructor(node) {
2990 if (typeof node === "string") node = document.querySelector(node);
2991 this.node = node instanceof window.jQuery ? node[0] : node;
2992 }
2993
2994 get el() { return this.node; }
2995 get element() { return this.node; }
2996
2997 get reactInternalInstance() {
2998 return Reflection.reactInternalInstance(this.node);
2999 }
3000
3001 get props() {
3002 return new Proxy(this.node, propsProxyHandler);
3003 }
3004 get state() {
3005 return Reflection.getState(this.node);
3006 }
3007
3008 get stateNode() {
3009 return Reflection.getStateNode(this.node);
3010 }
3011 get stateNodes() {
3012 return Reflection.getStateNodes(this.node);
3013 }
3014 getComponentStateNode(component) {
3015 return Reflection.getComponentStateNode(this.node, component);
3016 }
3017 findStateNode(filter) {
3018 if (typeof filter === "function") return Reflection.findStateNode(this.node, filter);
3019 if (filter) return Reflection.getComponentStateNode(this.node, filter);
3020 return Reflection.getStateNode(this.node);
3021 }
3022
3023 get component() {
3024 return Reflection.getComponent(this.node);
3025 }
3026 get components() {
3027 return Reflection.getComponents(this.node);
3028 }
3029 getComponentByProps(props, selector) {
3030 return Reflection.findComponent(this.node, _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["Filters"].byProperties(props, selector));
3031 }
3032 getComponentByPrototypes(props, selector) {
3033 return Reflection.findComponent(this.node, _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["Filters"].byPrototypeFields(props, selector));
3034 }
3035 getComponentByRegex(regex, selector) {
3036 return Reflection.findComponent(this.node, _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["Filters"].byCode(regex, selector));
3037 }
3038 getComponentByDisplayName(name) {
3039 return Reflection.findComponent(this.node, _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["Filters"].byDisplayName(name));
3040 }
3041
3042 forceUpdate(filter) {
3043 try {
3044 const stateNode = this.findStateNode(filter);
3045 if (!stateNode || !stateNode.forceUpdate) return;
3046 stateNode.forceUpdate();
3047 }
3048 catch (err) {
3049 _logger__WEBPACK_IMPORTED_MODULE_0__["default"].err("Reflection", err);
3050 }
3051 }
3052
3053 prop(propName) {
3054 const split = propName.split(".");
3055 const first = Reflection.findProp(this.node, split[0]);
3056 if (split.length === 1) return first;
3057 return Reflection.propIterator(first, split.slice(1));
3058 }
3059 }(node);
3060});
3061
3062
3063/***/ }),
3064
3065/***/ "./src/modules/utilities.js":
3066/*!**********************************!*\
3067 !*** ./src/modules/utilities.js ***!
3068 \**********************************/
3069/*! exports provided: default */
3070/***/ (function(module, __webpack_exports__, __webpack_require__) {
3071
3072"use strict";
3073__webpack_require__.r(__webpack_exports__);
3074/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return Utilities; });
3075/* harmony import */ var _logger__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./logger */ "./src/modules/logger.js");
3076/**
3077 * Random set of utilities that didn't fit elsewhere.
3078 * @module Utilities
3079 * @version 0.0.2
3080 */
3081
3082
3083
3084class Utilities {
3085
3086 /**
3087 * Stably sorts arrays since `.sort()` has issues.
3088 * @param {Array} list - array to sort
3089 * @param {function} comparator - comparator to sort by
3090 */
3091 static stableSort(list, comparator) {
3092 const entries = Array(length);
3093
3094 // wrap values with initial indices
3095 for (let index = 0; index < list.length; index++) {
3096 entries[index] = [index, list[index]];
3097 }
3098
3099 // sort with fallback based on initial indices
3100 entries.sort(function (a, b) {
3101 const comparison = Number(this(a[1], b[1]));
3102 return comparison || a[0] - b[0];
3103 }.bind(comparator));
3104
3105 // re-map original array to stable sorted values
3106 for (let index = 0; index < list.length; index++) {
3107 list[index] = entries[index][1];
3108 }
3109 }
3110
3111 /**
3112 * Generates an automatically memoizing version of an object.
3113 * @param {Object} object - object to memoize
3114 * @returns {Proxy} the proxy to the object that memoizes properties
3115 */
3116 static memoizeObject(object) {
3117 const proxy = new Proxy(object, {
3118 get: function(obj, mod) {
3119 if (!obj.hasOwnProperty(mod)) return undefined;
3120 if (Object.getOwnPropertyDescriptor(obj, mod).get) {
3121 const value = obj[mod];
3122 delete obj[mod];
3123 obj[mod] = value;
3124 }
3125 return obj[mod];
3126 },
3127 set: function(obj, mod, value) {
3128 if (obj.hasOwnProperty(mod)) return _logger__WEBPACK_IMPORTED_MODULE_0__["default"].err("MemoizedObject", "Trying to overwrite existing property");
3129 obj[mod] = value;
3130 return obj[mod];
3131 }
3132 });
3133
3134 Object.defineProperty(proxy, "hasOwnProperty", {value: function(prop) {
3135 return this[prop] !== undefined;
3136 }});
3137
3138 return proxy;
3139 }
3140
3141 /**
3142 * Wraps the method in a `try..catch` block.
3143 * @param {callable} method - method to wrap
3144 * @param {string} description - description of method
3145 * @returns {callable} wrapped version of method
3146 */
3147 static suppressErrors(method, description) {
3148 return (...params) => {
3149 try { return method(...params); }
3150 catch (e) { _logger__WEBPACK_IMPORTED_MODULE_0__["default"].err("Suppression", "Error occurred in " + description, e); }
3151 };
3152 }
3153
3154 /**
3155 * This only exists because Samo relied on lodash being there... fuck lodash.
3156 * @param {*} anything - whatever you want
3157 */
3158 static isNil(anything) {
3159 return anything == null;
3160 }
3161
3162 /**
3163 * Format template strings with placeholders (`${placeholder}`) into full strings.
3164 * Quick example: `PluginUtilities.formatString("Hello, ${user}", {user: "Zerebos"})`
3165 * would return "Hello, Zerebos".
3166 * @param {string} string - string to format
3167 * @param {object} values - object literal of placeholders to replacements
3168 * @returns {string} the properly formatted string
3169 */
3170 static formatTString(string, values) {
3171 for (const val in values) {
3172 let replacement = values[val];
3173 if (Array.isArray(replacement)) replacement = JSON.stringify(replacement);
3174 if (typeof(replacement) === "object" && replacement !== null) replacement = replacement.toString();
3175 string = string.replace(new RegExp(`\\$\\{${val}\\}`, "g"), replacement);
3176 }
3177 return string;
3178 }
3179
3180 /**
3181 * Format strings with placeholders (`{{placeholder}}`) into full strings.
3182 * Quick example: `PluginUtilities.formatString("Hello, {{user}}", {user: "Zerebos"})`
3183 * would return "Hello, Zerebos".
3184 * @param {string} string - string to format
3185 * @param {object} values - object literal of placeholders to replacements
3186 * @returns {string} the properly formatted string
3187 */
3188 static formatString(string, values) {
3189 for (const val in values) {
3190 let replacement = values[val];
3191 if (Array.isArray(replacement)) replacement = JSON.stringify(replacement);
3192 if (typeof(replacement) === "object" && replacement !== null) replacement = replacement.toString();
3193 string = string.replace(new RegExp(`{{${val}}}`, "g"), replacement);
3194 }
3195 return string;
3196 }
3197
3198 /**
3199 * Finds a value, subobject, or array from a tree that matches a specific filter. Great for patching render functions.
3200 * @param {object} tree React tree to look through. Can be a rendered object or an internal instance.
3201 * @param {callable} searchFilter Filter function to check subobjects against.
3202 */
3203 static findInReactTree(tree, searchFilter) {
3204 return this.findInTree(tree, searchFilter, {walkable: ["props", "children", "child", "sibling"]});
3205 }
3206
3207 /**
3208 * Finds a value, subobject, or array from a tree that matches a specific filter.
3209 * @param {object} tree Tree that should be walked
3210 * @param {callable} searchFilter Filter to check against each object and subobject
3211 * @param {object} options Additional options to customize the search
3212 * @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
3213 * @param {Array<string>} [options.ignore=[]] Array of strings to use as keys to exclude from the search, most helpful when `walkable = null`.
3214 */
3215 static findInTree(tree, searchFilter, {walkable = null, ignore = []} = {}) {
3216 if (typeof searchFilter === "string") {
3217 if (tree.hasOwnProperty(searchFilter)) return tree[searchFilter];
3218 }
3219 else if (searchFilter(tree)) {
3220 return tree;
3221 }
3222
3223 if (typeof tree !== "object" || tree == null) return undefined;
3224
3225 let tempReturn = undefined;
3226 if (tree instanceof Array) {
3227 for (const value of tree) {
3228 tempReturn = this.findInTree(value, searchFilter, {walkable, ignore});
3229 if (typeof tempReturn != "undefined") return tempReturn;
3230 }
3231 }
3232 else {
3233 const toWalk = walkable == null ? Object.keys(tree) : walkable;
3234 for (const key of toWalk) {
3235 if (!tree.hasOwnProperty(key) || ignore.includes(key)) continue;
3236 tempReturn = this.findInTree(tree[key], searchFilter, {walkable, ignore});
3237 if (typeof tempReturn != "undefined") return tempReturn;
3238 }
3239 }
3240 return tempReturn;
3241 }
3242
3243 /**
3244 * Gets a nested property (if it exists) safely. Path should be something like `prop.prop2.prop3`.
3245 * Numbers can be used for arrays as well like `prop.prop2.array.0.id`.
3246 * @param {Object} obj - object to get nested property of
3247 * @param {string} path - representation of the property to obtain
3248 */
3249 static getNestedProp(obj, path) {
3250 return path.split(/\s?\.\s?/).reduce(function(obj, prop) {
3251 return obj && obj[prop];
3252 }, obj);
3253 }
3254
3255 /**
3256 * Builds a classname string from any number of arguments. This includes arrays and objects.
3257 * When given an array all values from the array are added to the list.
3258 * When given an object they keys are added as the classnames if the value is truthy.
3259 * Copyright (c) 2018 Jed Watson https://github.com/JedWatson/classnames MIT License
3260 * @param {...Any} argument - anything that should be used to add classnames.
3261 */
3262 static className() {
3263 const classes = [];
3264 const hasOwn = {}.hasOwnProperty;
3265
3266 for (let i = 0; i < arguments.length; i++) {
3267 const arg = arguments[i];
3268 if (!arg) continue;
3269
3270 const argType = typeof arg;
3271
3272 if (argType === "string" || argType === "number") {
3273 classes.push(arg);
3274 }
3275 else if (Array.isArray(arg) && arg.length) {
3276 const inner = this.classNames.apply(null, arg);
3277 if (inner) {
3278 classes.push(inner);
3279 }
3280 }
3281 else if (argType === "object") {
3282 for (const key in arg) {
3283 if (hasOwn.call(arg, key) && arg[key]) {
3284 classes.push(key);
3285 }
3286 }
3287 }
3288 }
3289
3290 return classes.join(" ");
3291 }
3292
3293 /**
3294 * Safely adds to the prototype of an existing object by checking if the
3295 * property exists on the prototype.
3296 * @param {object} object - Object whose prototype to extend
3297 * @param {string} prop - Name of the prototype property to add
3298 * @param {callable} func - Function to run
3299 */
3300 static addToPrototype(object, prop, func) {
3301 if (!object.prototype) return;
3302 if (object.prototype[prop]) return;
3303 return object.prototype[prop] = func;
3304 }
3305
3306 /**
3307 * Deep extends an object with a set of other objects. Objects later in the list
3308 * of `extenders` have priority, that is to say if one sets a key to be a primitive,
3309 * it will be overwritten with the next one with the same key. If it is an object,
3310 * and the keys match, the object is extended. This happens recursively.
3311 * @param {object} extendee - Object to be extended
3312 * @param {...object} extenders - Objects to extend with
3313 * @returns {object} - A reference to `extendee`
3314 */
3315 static extend(extendee, ...extenders) {
3316 for (let i = 0; i < extenders.length; i++) {
3317 for (const key in extenders[i]) {
3318 if (extenders[i].hasOwnProperty(key)) {
3319 if (typeof extendee[key] === "object" && typeof extenders[i][key] === "object") this.extend(extendee[key], extenders[i][key]);
3320 else if (typeof extenders[i][key] === "object") extendee[key] = {}, this.extend(extendee[key], extenders[i][key]);
3321 else extendee[key] = extenders[i][key];
3322 }
3323 }
3324 }
3325 return extendee;
3326 }
3327
3328 /* Code below comes from our work on BDv2:
3329 * https://github.com/JsSucks/BetterDiscordApp/blob/master/common/modules/utils.js
3330 */
3331
3332 /**
3333 * Clones an object and all it's properties.
3334 * @param {Any} value The value to clone
3335 * @return {Any} The cloned value
3336 */
3337 static deepclone(value) {
3338 if (typeof value === "object") {
3339 if (value instanceof Array) return value.map(i => this.deepclone(i));
3340
3341 const clone = Object.assign({}, value);
3342
3343 for (const key in clone) {
3344 clone[key] = this.deepclone(clone[key]);
3345 }
3346
3347 return clone;
3348 }
3349
3350 return value;
3351 }
3352
3353 /**
3354 * Freezes an object and all it's properties.
3355 * @param {Any} object The object to freeze
3356 * @param {Function} exclude A function to filter object that shouldn't be frozen
3357 */
3358 static deepfreeze(object, exclude) {
3359 if (exclude && exclude(object)) return;
3360
3361 if (typeof object === "object" && object !== null) {
3362 const properties = Object.getOwnPropertyNames(object);
3363
3364 for (const property of properties) {
3365 this.deepfreeze(object[property], exclude);
3366 }
3367
3368 Object.freeze(object);
3369 }
3370
3371 return object;
3372 }
3373
3374 /**
3375 * Removes an item from an array. This differs from Array.prototype.filter as it mutates the original array instead of creating a new one.
3376 * @param {Array} array The array to filter
3377 * @param {Any} item The item to remove from the array
3378 * @return {Array}
3379 */
3380 static removeFromArray(array, item, filter) {
3381 let index;
3382 while ((index = filter ? array.findIndex(item) : array.indexOf(item)) > -1) array.splice(index, 1);
3383 return array;
3384}
3385
3386 /**
3387 * Checks if a file exists and is a file.
3388 * @param {String} path The file's path
3389 * @return {Promise}
3390 */
3391 static async fileExists(path) {
3392 const fs = __webpack_require__(/*! fs */ "fs");
3393 return new Promise((resolve, reject) => {
3394 fs.stat(path, (err, stats) => {
3395 if (err) {
3396 return reject({
3397 message: `No such file or directory: ${err.path}`,
3398 err
3399 });
3400 }
3401
3402 if (!stats.isFile()) {
3403 return reject({
3404 message: `Not a file: ${path}`,
3405 stats
3406 });
3407 }
3408
3409 resolve();
3410 });
3411 });
3412 }
3413
3414 /**
3415 * Returns the contents of a file.
3416 * @param {String} path The file's path
3417 * @return {Promise}
3418 */
3419 static async readFile(path) {
3420 try {
3421 await this.fileExists(path);
3422 }
3423 catch (err) {
3424 throw err;
3425 }
3426
3427 const fs = __webpack_require__(/*! fs */ "fs");
3428 return new Promise((resolve, reject) => {
3429 fs.readFile(path, "utf-8", (err, data) => {
3430 if (err) {
3431 return reject({
3432 message: `Could not read file: ${path}`,
3433 err
3434 });
3435 }
3436
3437 resolve(data);
3438 });
3439 });
3440 }
3441
3442}
3443
3444/***/ }),
3445
3446/***/ "./src/modules/webpackmodules.js":
3447/*!***************************************!*\
3448 !*** ./src/modules/webpackmodules.js ***!
3449 \***************************************/
3450/*! exports provided: Filters, default */
3451/***/ (function(module, __webpack_exports__, __webpack_require__) {
3452
3453"use strict";
3454__webpack_require__.r(__webpack_exports__);
3455/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Filters", function() { return Filters; });
3456/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return WebpackModules; });
3457/* harmony import */ var _discordmodules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./discordmodules */ "./src/modules/discordmodules.js");
3458/**
3459 * Random set of utilities that didn't fit elsewhere.
3460 * @module WebpackModules
3461 * @version 0.0.2
3462 */
3463
3464
3465 /**
3466 * Checks if a given module matches a set of parameters.
3467 * @callback module:WebpackModules.Filters~filter
3468 * @param {*} module - module to check
3469 * @returns {boolean} - True if the module matches the filter, false otherwise
3470 */
3471
3472/**
3473 * Filters for use with {@link module:WebpackModules} but may prove useful elsewhere.
3474 */
3475class Filters {
3476 /**
3477 * Generates a {@link module:WebpackModules.Filters~filter} that filters by a set of properties.
3478 * @param {Array<string>} props - Array of property names
3479 * @param {module:WebpackModules.Filters~filter} filter - Additional filter
3480 * @returns {module:WebpackModules.Filters~filter} - A filter that checks for a set of properties
3481 */
3482 static byProperties(props, filter = m => m) {
3483 return module => {
3484 const component = filter(module);
3485 if (!component) return false;
3486 return props.every(property => component[property] !== undefined);
3487 };
3488 }
3489
3490 /**
3491 * Generates a {@link module:WebpackModules.Filters~filter} that filters by a set of properties on the object's prototype.
3492 * @param {Array<string>} fields - Array of property names
3493 * @param {module:WebpackModules.Filters~filter} filter - Additional filter
3494 * @returns {module:WebpackModules.Filters~filter} - A filter that checks for a set of properties on the object's prototype
3495 */
3496 static byPrototypeFields(fields, filter = m => m) {
3497 return module => {
3498 const component = filter(module);
3499 if (!component) return false;
3500 if (!component.prototype) return false;
3501 return fields.every(field => component.prototype[field] !== undefined);
3502 };
3503 }
3504
3505 /**
3506 * Generates a {@link module:WebpackModules.Filters~filter} that filters by a regex.
3507 * @param {RegExp} search - A RegExp to check on the module
3508 * @param {module:WebpackModules.Filters~filter} filter - Additional filter
3509 * @returns {module:WebpackModules.Filters~filter} - A filter that checks for a set of properties
3510 */
3511 static byCode(search, filter = m => m) {
3512 return module => {
3513 const method = filter(module);
3514 if (!method) return false;
3515 return method.toString([]).search(search) !== -1;
3516 };
3517 }
3518
3519 /**
3520 * Generates a {@link module:WebpackModules.Filters~filter} that filters by strings.
3521 * @param {...String} search - A RegExp to check on the module
3522 * @returns {module:WebpackModules.Filters~filter} - A filter that checks for a set of strings
3523 */
3524 static byString(...strings) {
3525 return module => {
3526 const moduleString = module.toString([]);
3527 for (const s of strings) {
3528 if (!moduleString.includes(s)) return false;
3529 }
3530 return true;
3531 };
3532 }
3533
3534 /**
3535 * Generates a {@link module:WebpackModules.Filters~filter} that filters by a set of properties.
3536 * @param {string} name - Name the module should have
3537 * @param {module:WebpackModules.Filters~filter} filter - Additional filter
3538 * @returns {module:WebpackModules.Filters~filter} - A filter that checks for a set of properties
3539 */
3540 static byDisplayName(name) {
3541 return module => {
3542 return module && module.displayName === name;
3543 };
3544 }
3545
3546 /**
3547 * Generates a combined {@link module:WebpackModules.Filters~filter} from a list of filters.
3548 * @param {...module:WebpackModules.Filters~filter} filters - A list of filters
3549 * @returns {module:WebpackModules.Filters~filter} - Combinatory filter of all arguments
3550 */
3551 static combine(...filters) {
3552 return module => {
3553 return filters.every(filter => filter(module));
3554 };
3555 }
3556}
3557
3558class WebpackModules {
3559
3560 static find(filter, first = true) {return this.getModule(filter, first);}
3561 static findAll(filter) {return this.getModule(filter, false);}
3562 static findByUniqueProperties(props, first = true) {return first ? this.getByProps(...props) : this.getAllByProps(...props);}
3563 static findByDisplayName(name) {return this.getByDisplayName(name);}
3564
3565 /**
3566 * Finds a module using a filter function.
3567 * @param {Function} filter A function to use to filter modules
3568 * @param {Boolean} first Whether to return only the first matching module
3569 * @return {Any}
3570 */
3571 static getModule(filter, first = true) {
3572 const modules = this.getAllModules();
3573 const rm = [];
3574 for (const index in modules) {
3575 if (!modules.hasOwnProperty(index)) continue;
3576 const module = modules[index];
3577 const {exports} = module;
3578 let foundModule = null;
3579
3580 if (!exports) continue;
3581 if (exports.__esModule && exports.default && filter(exports.default)) foundModule = exports.default;
3582 if (filter(exports)) foundModule = exports;
3583 if (!foundModule) continue;
3584 if (first) return foundModule;
3585 rm.push(foundModule);
3586 }
3587 return first || rm.length == 0 ? undefined : rm;
3588 }
3589
3590 /**
3591 * Finds all modules matching a filter function.
3592 * @param {Function} filter A function to use to filter modules
3593 */
3594 static getModules(filter) {return this.getModule(filter, false);}
3595
3596 /**
3597 * Finds a module by its name.
3598 * @param {String} name The name of the module
3599 * @param {Function} fallback A function to use to filter modules if not finding a known module
3600 * @return {Any}
3601 */
3602 static getModuleByName(name, fallback) {
3603 if (_discordmodules__WEBPACK_IMPORTED_MODULE_0__["default"].hasOwnProperty(name)) return _discordmodules__WEBPACK_IMPORTED_MODULE_0__["default"][name];
3604 if (!fallback) return undefined;
3605 const module = this.getModule(fallback, true);
3606 return module ? _discordmodules__WEBPACK_IMPORTED_MODULE_0__["default"][name] = module : undefined;
3607 }
3608
3609 /**
3610 * Finds a module by its display name.
3611 * @param {String} name The display name of the module
3612 * @return {Any}
3613 */
3614 static getByDisplayName(name) {
3615 return this.getModule(Filters.byDisplayName(name), true);
3616 }
3617
3618 /**
3619 * Finds a module using its code.
3620 * @param {RegEx} regex A regular expression to use to filter modules
3621 * @param {Boolean} first Whether to return the only the first matching module
3622 * @return {Any}
3623 */
3624 static getByRegex(regex, first = true) {
3625 return this.getModule(Filters.byCode(regex), first);
3626 }
3627
3628 /**
3629 * Finds a single module using properties on its prototype.
3630 * @param {...string} prototypes Properties to use to filter modules
3631 * @return {Any}
3632 */
3633 static getByPrototypes(...prototypes) {
3634 return this.getModule(Filters.byPrototypeFields(prototypes), true);
3635 }
3636
3637 /**
3638 * Finds all modules with a set of properties of its prototype.
3639 * @param {...string} prototypes Properties to use to filter modules
3640 * @return {Any}
3641 */
3642 static getAllByPrototypes(...prototypes) {
3643 return this.getModule(Filters.byPrototypeFields(prototypes), false);
3644 }
3645
3646 /**
3647 * Finds a single module using its own properties.
3648 * @param {...string} props Properties to use to filter modules
3649 * @return {Any}
3650 */
3651 static getByProps(...props) {
3652 return this.getModule(Filters.byProperties(props), true);
3653 }
3654
3655 /**
3656 * Finds all modules with a set of properties.
3657 * @param {...string} props Properties to use to filter modules
3658 * @return {Any}
3659 */
3660 static getAllByProps(...props) {
3661 return this.getModule(Filters.byProperties(props), false);
3662 }
3663
3664 /**
3665 * Finds a single module using a set of strings.
3666 * @param {...String} props Strings to use to filter modules
3667 * @return {Any}
3668 */
3669 static getByString(...strings) {
3670 return this.getModule(Filters.byString(...strings), true);
3671 }
3672
3673 /**
3674 * Finds all modules with a set of strings.
3675 * @param {...String} strings Strings to use to filter modules
3676 * @return {Any}
3677 */
3678 static getAllByString(...strings) {
3679 return this.getModule(Filters.byString(...strings), false);
3680 }
3681
3682 /**
3683 * Discord's __webpack_require__ function.
3684 */
3685 static get require() {
3686 if (this._require) return this._require;
3687 const id = "zl-webpackmodules";
3688 const __webpack_require__ = typeof(window.webpackJsonp) == "function" ? window.webpackJsonp([], {
3689 [id]: (module, exports, __webpack_require__) => exports.default = __webpack_require__
3690 }, [id]).default : window.webpackJsonp.push([[], {
3691 [id]: (module, exports, __webpack_require__) => module.exports = __webpack_require__
3692 }, [[id]]]);
3693 delete __webpack_require__.m[id];
3694 delete __webpack_require__.c[id];
3695 return this._require = __webpack_require__;
3696 }
3697
3698 /**
3699 * Returns all loaded modules.
3700 * @return {Array}
3701 */
3702 static getAllModules() {
3703 return this.require.c;
3704 }
3705
3706}
3707
3708/***/ }),
3709
3710/***/ "./src/remote.js":
3711/*!***********************!*\
3712 !*** ./src/remote.js ***!
3713 \***********************/
3714/*! exports provided: default */
3715/***/ (function(module, __webpack_exports__, __webpack_require__) {
3716
3717"use strict";
3718__webpack_require__.r(__webpack_exports__);
3719/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
3720/* harmony import */ var ui__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ui */ "./src/ui/ui.js");
3721
3722
3723
3724const Library = {};
3725Library.ContextMenu = ui__WEBPACK_IMPORTED_MODULE_1__["ContextMenu"];
3726Library.Tooltip = ui__WEBPACK_IMPORTED_MODULE_1__["Tooltip"];
3727Library.Toasts = ui__WEBPACK_IMPORTED_MODULE_1__["Toasts"];
3728Library.Settings = ui__WEBPACK_IMPORTED_MODULE_1__["Settings"];
3729Library.Popouts = ui__WEBPACK_IMPORTED_MODULE_1__["Popouts"];
3730Library.Modals = ui__WEBPACK_IMPORTED_MODULE_1__["Modals"];
3731for (const mod in modules__WEBPACK_IMPORTED_MODULE_0__) Library[mod] = modules__WEBPACK_IMPORTED_MODULE_0__[mod];
3732
3733Library.buildPlugin = function(config) {
3734 const name = config.info.name;
3735 const BoundAPI = {
3736 Logger: {
3737 stacktrace: (message, error) => Library.Logger.stacktrace(name, message, error),
3738 log: (...message) => Library.Logger.log(name, ...message),
3739 error: (...message) => Library.Logger.err(name, ...message),
3740 err: (...message) => Library.Logger.err(name, ...message),
3741 warn: (...message) => Library.Logger.warn(name, ...message),
3742 info: (...message) => Library.Logger.info(name, ...message),
3743 debug: (...message) => Library.Logger.debug(name, ...message)
3744 },
3745 Patcher: {
3746 getPatchesByCaller: () => {return Library.Patcher.getPatchesByCaller(name);},
3747 unpatchAll: () => {return Library.Patcher.unpatchAll(name);},
3748 before: (moduleToPatch, functionName, callback, options = {}) => {return Library.Patcher.before(name, moduleToPatch, functionName, callback, options);},
3749 instead: (moduleToPatch, functionName, callback, options = {}) => {return Library.Patcher.instead(name, moduleToPatch, functionName, callback, options);},
3750 after: (moduleToPatch, functionName, callback, options = {}) => {return Library.Patcher.after(name, moduleToPatch, functionName, callback, options);}
3751 }
3752 };
3753
3754 return [Library.Structs.Plugin(config), Object.assign({}, Library, BoundAPI)];
3755};
3756
3757if (document.getElementById("ZLibraryCSS")) document.getElementById("ZLibraryCSS").remove();
3758document.head.append(Library.DOMTools.createElement(`<style id="ZLibraryCSS">${ui__WEBPACK_IMPORTED_MODULE_1__["Settings"].CSS + ui__WEBPACK_IMPORTED_MODULE_1__["Toasts"].CSS + Library.PluginUpdater.CSS}</style>`));
3759
3760/* harmony default export */ __webpack_exports__["default"] = (Library);
3761
3762
3763
3764/***/ }),
3765
3766/***/ "./src/structs/discord/channel.js":
3767/*!****************************************!*\
3768 !*** ./src/structs/discord/channel.js ***!
3769 \****************************************/
3770/*! exports provided: Channel, PermissionOverwrite, RolePermissionOverwrite, MemberPermissionOverwrite, GuildChannel, GuildTextChannel, GuildVoiceChannel, ChannelCategory, PrivateChannel, DirectMessageChannel, GroupChannel */
3771/***/ (function(module, __webpack_exports__, __webpack_require__) {
3772
3773"use strict";
3774__webpack_require__.r(__webpack_exports__);
3775/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Channel", function() { return Channel; });
3776/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PermissionOverwrite", function() { return PermissionOverwrite; });
3777/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RolePermissionOverwrite", function() { return RolePermissionOverwrite; });
3778/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MemberPermissionOverwrite", function() { return MemberPermissionOverwrite; });
3779/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GuildChannel", function() { return GuildChannel; });
3780/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GuildTextChannel", function() { return GuildTextChannel; });
3781/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GuildVoiceChannel", function() { return GuildVoiceChannel; });
3782/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ChannelCategory", function() { return ChannelCategory; });
3783/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PrivateChannel", function() { return PrivateChannel; });
3784/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DirectMessageChannel", function() { return DirectMessageChannel; });
3785/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GroupChannel", function() { return GroupChannel; });
3786/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
3787/* harmony import */ var structs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! structs */ "./src/structs/structs.js");
3788/* harmony import */ var _guild__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./guild */ "./src/structs/discord/guild.js");
3789/* harmony import */ var _message__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./message */ "./src/structs/discord/message.js");
3790/* harmony import */ var _user__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./user */ "./src/structs/discord/user.js");
3791/**
3792 * BetterDiscord Channel Struct
3793 * Copyright (c) 2018-present JsSucks
3794 * All rights reserved.
3795 *
3796 * This source code is licensed under the MIT license found at
3797 * https://github.com/JsSucks/BetterDiscordApp/blob/master/LICENSE
3798*/
3799
3800
3801
3802
3803
3804
3805
3806const cache = new WeakMap();
3807
3808/**
3809 * @memberof module:DiscordAPI
3810 */
3811class Channel {
3812
3813 constructor(data) {
3814 if (cache.has(data)) return cache.get(data);
3815 cache.set(data, this);
3816
3817 this.discordObject = data;
3818 }
3819
3820 static from(channel) {
3821 switch (channel.type) {
3822 default: return new Channel(channel);
3823 case 0: return new GuildTextChannel(channel);
3824 case 1: return new DirectMessageChannel(channel);
3825 case 2: return new GuildVoiceChannel(channel);
3826 case 3: return new GroupChannel(channel);
3827 case 4: return new ChannelCategory(channel);
3828 }
3829 }
3830
3831 static fromId(id) {
3832 const channel = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].ChannelStore.getChannel(id);
3833 if (channel) return Channel.from(channel);
3834 }
3835
3836 static get GuildChannel() { return GuildChannel; }
3837 static get GuildTextChannel() { return GuildTextChannel; }
3838 static get GuildVoiceChannel() { return GuildVoiceChannel; }
3839 static get ChannelCategory() { return ChannelCategory; }
3840 static get PrivateChannel() { return PrivateChannel; }
3841 static get DirectMessageChannel() { return DirectMessageChannel; }
3842 static get GroupChannel() { return GroupChannel; }
3843
3844 get id() { return this.discordObject.id; }
3845 get applicationId() { return this.discordObject.application_id; }
3846 get type() { return this.discordObject.type; }
3847 get name() { return this.discordObject.name; }
3848
3849 /**
3850 * Send a message in this channel.
3851 * @param {String} content The new message's content
3852 * @param {Boolean} parse Whether to parse the message or send it as it is
3853 * @return {Promise<Message>}
3854 */
3855 async sendMessage(content, parse = false) {
3856 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);
3857
3858 this.select();
3859
3860 if (parse) content = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageParser.parse(this.discordObject, content);
3861 else content = {content};
3862
3863 const response = await modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageActions._sendMessage(this.id, content);
3864 return _message__WEBPACK_IMPORTED_MODULE_3__["Message"].from(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageStore.getMessage(this.id, response.body.id));
3865 }
3866
3867 /**
3868 * Send a bot message in this channel that only the current user can see.
3869 * @param {String} content The new message's content
3870 * @return {Message}
3871 */
3872 sendBotMessage(content) {
3873 this.select();
3874 const message = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageParser.createBotMessage(this.id, content);
3875 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageActions.receiveMessage(this.id, message);
3876 return _message__WEBPACK_IMPORTED_MODULE_3__["Message"].from(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageStore.getMessage(this.id, message.id));
3877 }
3878
3879 /**
3880 * A list of messages in this channel.
3881 */
3882 get messages() {
3883 const messages = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageStore.getMessages(this.id).toArray();
3884 return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(messages, m => _message__WEBPACK_IMPORTED_MODULE_3__["Message"].from(m));
3885 }
3886
3887 /**
3888 * Jumps to the latest message in this channel.
3889 */
3890 jumpToPresent() {
3891 if (this.assertPermissions) this.assertPermissions("VIEW_CHANNEL", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.VIEW_CHANNEL);
3892 if (this.hasMoreAfter) modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageActions.jumpToPresent(this.id, modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordConstants.MAX_MESSAGES_PER_CHANNEL);
3893 else this.messages[this.messages.length - 1].jumpTo(false);
3894 }
3895
3896 get hasMoreAfter() {
3897 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageStore.getMessages(this.id).hasMoreAfter;
3898 }
3899
3900 /**
3901 * Sends an invite in this channel.
3902 * @param {String} code The invite code
3903 * @return {Promise<Message>}
3904 */
3905 async sendInvite(code) {
3906 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);
3907 const response = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageActions.sendInvite(this.id, code);
3908 return _message__WEBPACK_IMPORTED_MODULE_3__["Message"].from(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageStore.getMessage(this.id, response.body.id));
3909 }
3910
3911 /**
3912 * Opens this channel in the UI.
3913 */
3914 select() {
3915 if (this.assertPermissions) this.assertPermissions("VIEW_CHANNEL", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.VIEW_CHANNEL);
3916 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].NavigationUtils.transitionToGuild(this.guildId ? this.guildId : modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordConstants.ME, this.id);
3917 }
3918
3919 /**
3920 * Whether this channel is currently selected.
3921 */
3922 get isSelected() {
3923 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentChannel === this;
3924 }
3925
3926 /**
3927 * Updates this channel.
3928 * @return {Promise}
3929 */
3930 async updateChannel(body) {
3931 if (this.assertPermissions) this.assertPermissions("MANAGE_CHANNELS", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.MANAGE_CHANNELS);
3932 await modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].APIModule.patch({
3933 url: `${modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordConstants.Endpoints.CHANNELS}/${this.id}`,
3934 body
3935 });
3936 this.discordObject = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].ChannelStore.getChannel(this.id);
3937 cache.set(this.discordObject, this);
3938 }
3939
3940}
3941
3942
3943
3944class PermissionOverwrite {
3945 constructor(data, channel_id) {
3946 this.discordObject = data;
3947 this.channelId = channel_id;
3948 }
3949
3950 static from(data, channel_id) {
3951 switch (data.type) {
3952 default: return new PermissionOverwrite(data, channel_id);
3953 case "role": return new RolePermissionOverwrite(data, channel_id);
3954 case "member": return new MemberPermissionOverwrite(data, channel_id);
3955 }
3956 }
3957
3958 static get RolePermissionOverwrite() { return RolePermissionOverwrite; }
3959 static get MemberPermissionOverwrite() { return MemberPermissionOverwrite; }
3960
3961 get type() { return this.discordObject.type; }
3962 get allow() { return this.discordObject.allow; }
3963 get deny() { return this.discordObject.deny; }
3964
3965 get channel() {
3966 return Channel.fromId(this.channelId);
3967 }
3968
3969 get guild() {
3970 if (this.channel) return this.channel.guild;
3971 }
3972}
3973
3974class RolePermissionOverwrite extends PermissionOverwrite {
3975 get roleId() { return this.discordObject.id; }
3976
3977 get role() {
3978 if (this.guild) return this.guild.roles.find(r => r.id === this.roleId);
3979 }
3980}
3981
3982class MemberPermissionOverwrite extends PermissionOverwrite {
3983 get memberId() { return this.discordObject.id; }
3984
3985 get member() {
3986 return _user__WEBPACK_IMPORTED_MODULE_4__["GuildMember"].fromId(this.memberId);
3987 }
3988}
3989
3990class GuildChannel extends Channel {
3991 static get PermissionOverwrite() { return PermissionOverwrite; }
3992
3993 get guildId() { return this.discordObject.guild_id; }
3994 get parentId() { return this.discordObject.parent_id; } // Channel category
3995 get position() { return this.discordObject.position; }
3996 get nicks() { return this.discordObject.nicks; }
3997
3998 checkPermissions(perms) {
3999 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].Permissions.can(perms, modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentUser, this.discordObject);
4000 }
4001
4002 assertPermissions(name, perms) {
4003 if (!this.checkPermissions(perms)) throw new structs__WEBPACK_IMPORTED_MODULE_1__["InsufficientPermissions"](name);
4004 }
4005
4006 get category() {
4007 return Channel.fromId(this.parentId);
4008 }
4009
4010 /**
4011 * The current user's permissions on this channel.
4012 */
4013 get permissions() {
4014 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildPermissions.getChannelPermissions(this.id);
4015 }
4016
4017 get permissionOverwrites() {
4018 return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(Object.values(this.discordObject.permissionOverwrites), p => PermissionOverwrite.from(p, this.id));
4019 }
4020
4021 get guild() {
4022 return _guild__WEBPACK_IMPORTED_MODULE_2__["Guild"].fromId(this.guildId);
4023 }
4024
4025 /**
4026 * Whether this channel is the guild's default channel.
4027 */
4028 get isDefaultChannel() {
4029 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildChannelsStore.getDefaultChannel(this.guildId).id === this.id;
4030 }
4031
4032 /**
4033 * Opens this channel's settings window.
4034 * @param {String} section The section to open (see DiscordConstants.ChannelSettingsSections)
4035 */
4036 openSettings(section = "OVERVIEW") {
4037 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].ChannelSettingsWindow.setSection(section);
4038 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].ChannelSettingsWindow.open(this.id);
4039 }
4040
4041 /**
4042 * Updates this channel's name.
4043 * @param {String} name The channel's new name
4044 * @return {Promise}
4045 */
4046 updateName(name) {
4047 return this.updateChannel({name});
4048 }
4049
4050 /**
4051 * Changes the channel's position.
4052 * @param {Number} position The channel's new position
4053 * @return {Promise}
4054 */
4055 changeSortLocation(position = 0) {
4056 if (position instanceof GuildChannel) position = position.position;
4057 return this.updateChannel({position});
4058 }
4059
4060 /**
4061 * Updates this channel's permission overwrites.
4062 * @param {Array} permissionOverwrites An array of permission overwrites
4063 * @return {Promise}
4064 */
4065 updatePermissionOverwrites(permission_overwrites) {
4066 return this.updateChannel({permission_overwrites});
4067 }
4068
4069 /**
4070 * Updates this channel's category.
4071 * @param {ChannelCategory} category The new channel category
4072 * @return {Promise}
4073 */
4074 updateCategory(category) {
4075 return this.updateChannel({parent_id: category.id || category});
4076 }
4077}
4078
4079// Type 0 - GUILD_TEXT
4080class GuildTextChannel extends GuildChannel {
4081 get type() { return "GUILD_TEXT"; }
4082 get topic() { return this.discordObject.topic; }
4083 get nsfw() { return this.discordObject.nsfw; }
4084
4085 /**
4086 * Updates this channel's topic.
4087 * @param {String} topc The new channel topic
4088 * @return {Promise}
4089 */
4090 updateTopic(topic) {
4091 return this.updateChannel({topic});
4092 }
4093
4094 /**
4095 * Updates this channel's NSFW flag.
4096 * @param {Boolean} nsfw Whether the channel should be marked as NSFW
4097 * @return {Promise}
4098 */
4099 setNsfw(nsfw = true) {
4100 return this.updateChannel({nsfw});
4101 }
4102
4103 setNotNsfw() {
4104 return this.setNswf(false);
4105 }
4106}
4107
4108// Type 2 - GUILD_VOICE
4109class GuildVoiceChannel extends GuildChannel {
4110 get type() { return "GUILD_VOICE"; }
4111 get userLimit() { return this.discordObject.userLimit; }
4112 get bitrate() { return this.discordObject.bitrate; }
4113
4114 sendMessage() { throw new Error("Cannot send messages in a voice channel."); }
4115 get messages() { return new structs__WEBPACK_IMPORTED_MODULE_1__["List"](); }
4116 jumpToPresent() { throw new Error("Cannot select a voice channel."); }
4117 get hasMoreAfter() { return false; }
4118 sendInvite() { throw new Error("Cannot invite someone to a voice channel."); }
4119 select() { throw new Error("Cannot select a voice channel."); }
4120
4121 /**
4122 * Updates this channel's bitrate.
4123 * @param {Number} bitrate The new bitrate
4124 * @return {Promise}
4125 */
4126 updateBitrate(bitrate) {
4127 return this.updateChannel({bitrate});
4128 }
4129
4130 /**
4131 * Updates this channel's user limit.
4132 * @param {Number} userLimit The new user limit
4133 * @return {Promise}
4134 */
4135 updateUserLimit(user_limit) {
4136 return this.updateChannel({user_limit});
4137 }
4138}
4139
4140// Type 4 - GUILD_CATEGORY
4141class ChannelCategory extends GuildChannel {
4142 get type() { return "GUILD_CATEGORY"; }
4143 get parentId() { return undefined; }
4144 get category() { return undefined; }
4145
4146 sendMessage() { throw new Error("Cannot send messages in a channel category."); }
4147 get messages() { return new structs__WEBPACK_IMPORTED_MODULE_1__["List"](); }
4148 jumpToPresent() { throw new Error("Cannot select a channel category."); }
4149 get hasMoreAfter() { return false; }
4150 sendInvite() { throw new Error("Cannot invite someone to a channel category."); }
4151 select() { throw new Error("Cannot select a channel category."); }
4152 updateCategory() { throw new Error("Cannot set a channel category on another channel category."); }
4153
4154 /**
4155 * A list of channels in this category.
4156 */
4157 get channels() {
4158 return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(this.guild.channels, c => c.parentId === this.id);
4159 }
4160
4161 /**
4162 * Opens the create channel modal for this guild.
4163 * @param {Number} type The type of channel to create - either 0 (text), 2 (voice) or 4 (category)
4164 * @param {GuildChannel} clone A channel to clone permissions of
4165 */
4166 openCreateChannelModal(type, category, clone) {
4167 this.guild.openCreateChannelModal(type, this.id, this, clone);
4168 }
4169
4170 /**
4171 * Creates a channel in this category.
4172 * @param {Number} type The type of channel to create - either 0 (text) or 2 (voice)
4173 * @param {String} name A name for the new channel
4174 * @param {Array} permission_overwrites An array of PermissionOverwrite-like objects - leave to use the permissions of the category
4175 * @return {Promise<GuildChannel>}
4176 */
4177 createChannel(type, name, permission_overwrites) {
4178 return this.guild.createChannel(type, name, this, permission_overwrites);
4179 }
4180}
4181
4182class PrivateChannel extends Channel {
4183 get userLimit() { return this.discordObject.userLimit; }
4184 get bitrate() { return this.discordObject.bitrate; }
4185}
4186
4187// Type 1 - DM
4188class DirectMessageChannel extends PrivateChannel {
4189 get type() { return "DM"; }
4190 get recipientId() { return this.discordObject.recipients[0]; }
4191
4192 /**
4193 * The other user of this direct message channel.
4194 */
4195 get recipient() {
4196 return _user__WEBPACK_IMPORTED_MODULE_4__["User"].fromId(this.recipientId);
4197 }
4198}
4199
4200// Type 3 - GROUP_DM
4201class GroupChannel extends PrivateChannel {
4202 get ownerId() { return this.discordObject.ownerId; }
4203 get type() { return "GROUP_DM"; }
4204 get name() { return this.discordObject.name; }
4205 get icon() { return this.discordObject.icon; }
4206
4207 /**
4208 * A list of the other members of this group direct message channel.
4209 */
4210 get members() {
4211 return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(this.discordObject.recipients, id => _user__WEBPACK_IMPORTED_MODULE_4__["User"].fromId(id));
4212 }
4213
4214 /**
4215 * The owner of this group direct message channel. This is usually the person who created it.
4216 */
4217 get owner() {
4218 return _user__WEBPACK_IMPORTED_MODULE_4__["User"].fromId(this.ownerId);
4219 }
4220
4221 /**
4222 * Updates this channel's name.
4223 * @param {String} name The channel's new name
4224 * @return {Promise}
4225 */
4226 updateName(name) {
4227 return this.updateChannel({name});
4228 }
4229}
4230
4231
4232// export {Channel, GuildChannel, ChannelCategory, GuildTextChannel, GuildVoiceChannel, PrivateChannel, DirectMessageChannel, GroupChannel};
4233// export {PermissionOverwrite, RolePermissionOverwrite, MemberPermissionOverwrite};
4234
4235/***/ }),
4236
4237/***/ "./src/structs/discord/guild.js":
4238/*!**************************************!*\
4239 !*** ./src/structs/discord/guild.js ***!
4240 \**************************************/
4241/*! exports provided: Role, Emoji, Guild */
4242/***/ (function(module, __webpack_exports__, __webpack_require__) {
4243
4244"use strict";
4245__webpack_require__.r(__webpack_exports__);
4246/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Role", function() { return Role; });
4247/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Emoji", function() { return Emoji; });
4248/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Guild", function() { return Guild; });
4249/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
4250/* harmony import */ var structs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! structs */ "./src/structs/structs.js");
4251/* harmony import */ var _channel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./channel */ "./src/structs/discord/channel.js");
4252/* harmony import */ var _user__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./user */ "./src/structs/discord/user.js");
4253/**
4254 * BetterDiscord Guild Struct
4255 * Copyright (c) 2018-present JsSucks
4256 * All rights reserved.
4257 *
4258 * This source code is licensed under the MIT license found at
4259 * https://github.com/JsSucks/BetterDiscordApp/blob/master/LICENSE
4260*/
4261
4262
4263
4264
4265
4266
4267const roles = new WeakMap();
4268
4269class Role {
4270 constructor(data, guild_id) {
4271 if (roles.has(data)) return roles.get(data);
4272 roles.set(data, this);
4273
4274 this.discordObject = data;
4275 this.guildId = guild_id;
4276 }
4277
4278 get id() { return this.discordObject.id; }
4279 get name() { return this.discordObject.name; }
4280 get position() { return this.discordObject.position; }
4281 get originalPosition() { return this.discordObject.originalPosition; }
4282 get permissions() { return this.discordObject.permissions; }
4283 get managed() { return this.discordObject.managed; }
4284 get mentionable() { return this.discordObject.mentionable; }
4285 get hoist() { return this.discordObject.hoist; }
4286 get colour() { return this.discordObject.color; }
4287 get colourString() { return this.discordObject.colorString; }
4288
4289 get guild() {
4290 return Guild.fromId(this.guildId);
4291 }
4292
4293 get members() {
4294 return this.guild.members.filter(m => m.roles.includes(this));
4295 }
4296}
4297
4298const emojis = new WeakMap();
4299
4300class Emoji {
4301 constructor(data) {
4302 if (emojis.has(data)) return emojis.get(data);
4303 emojis.set(data, this);
4304
4305 this.discordObject = data;
4306 }
4307
4308 get id() { return this.discordObject.id; }
4309 get guildId() { return this.discordObject.guild_id; }
4310 get name() { return this.discordObject.name; }
4311 get managed() { return this.discordObject.managed; }
4312 get animated() { return this.discordObject.animated; }
4313 get allNamesString() { return this.discordObject.allNamesString; }
4314 get requireColons() { return this.discordObject.require_colons; }
4315 get url() { return this.discordObject.url; }
4316 get roles() { return this.discordObject.roles; }
4317
4318 get guild() {
4319 return Guild.fromId(this.guildId);
4320 }
4321}
4322
4323const guilds = new WeakMap();
4324
4325/**
4326 * @memberof module:DiscordAPI
4327 */
4328class Guild {
4329
4330 constructor(data) {
4331 if (guilds.has(data)) return guilds.get(data);
4332 guilds.set(data, this);
4333
4334 this.discordObject = data;
4335 }
4336
4337 static from(data) {
4338 return new Guild(data);
4339 }
4340
4341 static fromId(id) {
4342 const guild = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildStore.getGuild(id);
4343 if (guild) return Guild.from(guild);
4344 }
4345
4346 static get Role() { return Role; }
4347 static get Emoji() { return Emoji; }
4348
4349 get id() { return this.discordObject.id; }
4350 get ownerId() { return this.discordObject.ownerId; }
4351 get applicationId() { return this.discordObject.application_id; }
4352 get systemChannelId() { return this.discordObject.systemChannelId; }
4353 get name() { return this.discordObject.name; }
4354 get acronym() { return this.discordObject.acronym; }
4355 get icon() { return this.discordObject.icon; }
4356 get joinedAt() { return this.discordObject.joinedAt; }
4357 get verificationLevel() { return this.discordObject.verificationLevel; }
4358 get mfaLevel() { return this.discordObject.mfaLevel; }
4359 get large() { return this.discordObject.large; }
4360 get lazy() { return this.discordObject.lazy; }
4361 get voiceRegion() { return this.discordObject.region; }
4362 get afkChannelId() { return this.discordObject.afkChannelId; }
4363 get afkTimeout() { return this.discordObject.afkTimeout; }
4364 get explicitContentFilter() { return this.discordObject.explicitContentFilter; }
4365 get defaultMessageNotifications() { return this.discordObject.defaultMessageNotifications; }
4366 get splash() { return this.discordObject.splash; }
4367 get features() { return this.discordObject.features; }
4368
4369 get owner() {
4370 return this.members.find(m => m.userId === this.ownerId);
4371 }
4372
4373 get roles() {
4374 return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(Object.values(this.discordObject.roles), r => new Role(r, this.id))
4375 .sort((r1, r2) => r1.position === r2.position ? 0 : r1.position > r2.position ? 1 : -1);
4376 }
4377
4378 get channels() {
4379 const channels = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildChannelsStore.getChannels(this.id);
4380 const returnChannels = new structs__WEBPACK_IMPORTED_MODULE_1__["List"]();
4381 for (const category in channels) {
4382 if (channels.hasOwnProperty(category)) {
4383 if (!Array.isArray(channels[category])) continue;
4384 const channelList = channels[category];
4385 for (const channel of channelList) {
4386 // For some reason Discord adds a new category with the ID "null" and name "Uncategorized"
4387 if (channel.channel.id === "null") continue;
4388 returnChannels.push(_channel__WEBPACK_IMPORTED_MODULE_2__["Channel"].from(channel.channel));
4389 }
4390 }
4391 }
4392 return returnChannels;
4393 }
4394
4395 /**
4396 * Channels that don't have a parent. (Channel categories and any text/voice channel not in one.)
4397 */
4398 get mainChannels() {
4399 return this.channels.filter(c => !c.parentId);
4400 }
4401
4402 /**
4403 * The guild's default channel. (Usually the first in the list.)
4404 */
4405 get defaultChannel() {
4406 return _channel__WEBPACK_IMPORTED_MODULE_2__["Channel"].from(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildChannelsStore.getDefaultChannel(this.id));
4407 }
4408
4409 /**
4410 * The guild's AFK channel.
4411 */
4412 get afkChannel() {
4413 if (this.afkChannelId) return _channel__WEBPACK_IMPORTED_MODULE_2__["Channel"].fromId(this.afkChannelId);
4414 }
4415
4416 /**
4417 * The channel system messages are sent to.
4418 */
4419 get systemChannel() {
4420 if (this.systemChannelId) return _channel__WEBPACK_IMPORTED_MODULE_2__["Channel"].fromId(this.systemChannelId);
4421 }
4422
4423 /**
4424 * A list of GuildMember objects.
4425 */
4426 get members() {
4427 const members = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildMemberStore.getMembers(this.id);
4428 return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(members, m => new _user__WEBPACK_IMPORTED_MODULE_3__["GuildMember"](m, this.id));
4429 }
4430
4431 /**
4432 * The current user as a GuildMember of this guild.
4433 */
4434 get currentUser() {
4435 return this.members.find(m => m.user === modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentUser);
4436 }
4437
4438 /**
4439 * The total number of members in the guild.
4440 */
4441 get memberCount() {
4442 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MemberCountStore.getMemberCount(this.id);
4443 }
4444
4445 /**
4446 * An array of the guild's custom emojis.
4447 */
4448 get emojis() {
4449 return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].EmojiUtils.getGuildEmoji(this.id), e => new Emoji(e));
4450 }
4451
4452 checkPermissions(perms) {
4453 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].Permissions.can(perms, modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentUser, this.discordObject);
4454 }
4455
4456 assertPermissions(name, perms) {
4457 if (!this.checkPermissions(perms)) throw new structs__WEBPACK_IMPORTED_MODULE_1__["InsufficientPermissions"](name);
4458 }
4459
4460 /**
4461 * The current user's permissions on this guild.
4462 */
4463 get permissions() {
4464 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildPermissions.getGuildPermissions(this.id);
4465 }
4466
4467 getMember(id) {
4468 const member = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildMemberStore.getMember(this.id, id);
4469 if (member) return new _user__WEBPACK_IMPORTED_MODULE_3__["GuildMember"](member, this.id);
4470 }
4471
4472 isMember(id) {
4473 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildMemberStore.isMember(this.id, id);
4474 }
4475
4476 /**
4477 * Whether the user has not restricted direct messages from members of this guild.
4478 */
4479 get allowPrivateMessages() {
4480 return !modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].UserSettings.restrictedGuildIds.includes(this.id);
4481 }
4482
4483 /**
4484 * Marks all messages in the guild as read.
4485 */
4486 markAsRead() {
4487 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildActions.markGuildAsRead(this.id);
4488 }
4489
4490 /**
4491 * Selects the guild in the UI.
4492 */
4493 select() {
4494 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildActions.selectGuild(this.id);
4495 }
4496
4497 /**
4498 * Whether this guild is currently selected.
4499 */
4500 get isSelected() {
4501 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentGuild === this;
4502 }
4503
4504 /**
4505 * Opens this guild's settings window.
4506 * @param {String} section The section to open (see DiscordConstants.GuildSettingsSections)
4507 */
4508 openSettings(section = "OVERVIEW") {
4509 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildSettingsWindow.setSection(section);
4510 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildSettingsWindow.open(this.id);
4511 }
4512
4513 /**
4514 * Kicks members who don't have any roles and haven't been seen in the number of days passed.
4515 * @param {Number} days
4516 */
4517 pruneMembers(days) {
4518 this.assertPermissions("KICK_MEMBERS", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.KICK_MEMBERS);
4519 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].PruneMembersModal.prune(this.id, days);
4520 }
4521
4522 openPruneMumbersModal() {
4523 this.assertPermissions("KICK_MEMBERS", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.KICK_MEMBERS);
4524 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].PruneMembersModal.open(this.id);
4525 }
4526
4527 /**
4528 * Opens the create channel modal for this guild.
4529 * @param {Number} type The type of channel to create - either 0 (text), 2 (voice) or 4 (category)
4530 * @param {ChannelCategory} category The category to create the channel in
4531 * @param {GuildChannel} clone A channel to clone permissions, topic, bitrate and user limit of
4532 */
4533 openCreateChannelModal(type, category, clone) {
4534 this.assertPermissions("MANAGE_CHANNELS", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.MANAGE_CHANNELS);
4535 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].CreateChannelModal.open(type, this.id, category ? category.id : undefined, clone ? clone.id : undefined);
4536 }
4537
4538 /**
4539 * Creates a channel in this guild.
4540 * @param {Number} type The type of channel to create - either 0 (text), 2 (voice) or 4 (category)
4541 * @param {String} name A name for the new channel
4542 * @param {ChannelCategory} category The category to create the channel in
4543 * @param {Array} permission_overwrites An array of PermissionOverwrite-like objects - leave to use the permissions of the category
4544 * @return {Promise<GuildChannel>}
4545 */
4546 async createChannel(type, name, category, permission_overwrites) {
4547 this.assertPermissions("MANAGE_CHANNELS", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.MANAGE_CHANNELS);
4548 const response = await modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].APIModule.post({
4549 url: modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordConstants.Endpoints.GUILD_CHANNELS(this.id),
4550 body: {
4551 type, name,
4552 parent_id: category ? category.id : undefined,
4553 permission_overwrites: permission_overwrites ? permission_overwrites.map(p => ({
4554 type: p.type,
4555 id: (p.type === "user" ? p.userId : p.roleId) || p.id,
4556 allow: p.allow,
4557 deny: p.deny
4558 })) : undefined
4559 }
4560 });
4561
4562 return _channel__WEBPACK_IMPORTED_MODULE_2__["Channel"].fromId(response.body.id);
4563 }
4564
4565 openNotificationSettingsModal() {
4566 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].NotificationSettingsModal.open(this.id);
4567 }
4568
4569 openPrivacySettingsModal() {
4570 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].PrivacySettingsModal.open(this.id);
4571 }
4572
4573 nsfwAgree() {
4574 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildActions.nsfwAgree(this.id);
4575 }
4576
4577 nsfwDisagree() {
4578 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildActions.nsfwDisagree(this.id);
4579 }
4580
4581 /**
4582 * Changes the guild's position in the list.
4583 * @param {Number} index The new position
4584 */
4585 changeSortLocation(index) {
4586 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildActions.move(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].guildPositions.indexOf(this.id), index);
4587 }
4588
4589 /**
4590 * Updates this guild.
4591 * @return {Promise}
4592 */
4593 async updateGuild(body) {
4594 this.assertPermissions("MANAGE_GUILD", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.MANAGE_GUILD);
4595 await modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildSettingsWindow.saveGuild(this.id, body);
4596 this.discordObject = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildStore.getGuild(this.id);
4597 guilds.set(this.discordObject, this);
4598 }
4599
4600 /**
4601 * Updates this guild's name.
4602 * @param {String} name The new name
4603 * @return {Promise}
4604 */
4605 updateName(name) {
4606 return this.updateGuild({ name });
4607 }
4608
4609 /**
4610 * Updates this guild's voice region.
4611 * @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)
4612 * @return {Promise}
4613 */
4614 updateVoiceRegion(region) {
4615 return this.updateGuild({ region });
4616 }
4617
4618 /**
4619 * Updates this guild's verification level.
4620 * @param {Number} verificationLevel The new verification level (see https://discordapp.com/developers/docs/resources/guild#guild-object-verification-level)
4621 * @return {Promise}
4622 */
4623 updateVerificationLevel(verification_level) {
4624 return this.updateGuild({ verification_level });
4625 }
4626
4627 /**
4628 * Updates this guild's default message notification level.
4629 * @param {Number} defaultMessageNotifications The new default notification level (0: all messages, 1: only mentions)
4630 * @return {Promise}
4631 */
4632 updateDefaultMessageNotifications(default_message_notifications) {
4633 return this.updateGuild({ default_message_notifications });
4634 }
4635
4636 /**
4637 * Updates this guild's explicit content filter level.
4638 * @param {Number} explicitContentFilter The new explicit content filter level (0: disabled, 1: members without roles, 2: everyone)
4639 * @return {Promise}
4640 */
4641 updateExplicitContentFilter(explicit_content_filter) {
4642 return this.updateGuild({ explicit_content_filter });
4643 }
4644
4645 /**
4646 * Updates this guild's AFK channel.
4647 * @param {GuildVoiceChannel} afkChannel The new AFK channel
4648 * @return {Promise}
4649 */
4650 updateAfkChannel(afk_channel) {
4651 return this.updateGuild({ afk_channel_id: afk_channel.id || afk_channel });
4652 }
4653
4654 /**
4655 * Updates this guild's AFK timeout.
4656 * @param {Number} afkTimeout The new AFK timeout
4657 * @return {Promise}
4658 */
4659 updateAfkTimeout(afk_timeout) {
4660 return this.updateGuild({ afk_timeout });
4661 }
4662
4663 /**
4664 * Updates this guild's icon.
4665 * @param {Buffer|String} icon A buffer/base 64 encoded 128x128 JPEG image
4666 * @return {Promise}
4667 */
4668 updateIcon(icon) {
4669 return this.updateGuild({ icon: typeof icon === "string" ? icon : icon.toString("base64") });
4670 }
4671
4672 /**
4673 * Updates this guild's icon using a local file.
4674 * TODO
4675 * @param {String} icon_path The path to the new icon
4676 * @return {Promise}
4677 */
4678 async updateIconFromFile(icon_path) {
4679 const buffer = await modules__WEBPACK_IMPORTED_MODULE_0__["Utilities"].readFileBuffer(icon_path);
4680 return this.updateIcon(buffer);
4681 }
4682
4683 /**
4684 * Updates this guild's owner. (Should plugins really ever need to do this?)
4685 * @param {User|GuildMember} owner The user/guild member to transfer ownership to
4686 * @return {Promise}
4687 */
4688 updateOwner(owner) {
4689 return this.updateGuild({ owner_id: owner.user ? owner.user.id : owner.id || owner });
4690 }
4691
4692 /**
4693 * Updates this guild's splash image.
4694 * (I don't know what this is actually used for. The API documentation says it's VIP-only.)
4695 * @param {Buffer|String} icon A buffer/base 64 encoded 128x128 JPEG image
4696 * @return {Promise}
4697 */
4698 updateSplash(splash) {
4699 return this.updateGuild({ splash: typeof splash === "string" ? splash : splash.toString("base64") });
4700 }
4701
4702 /**
4703 * Updates this guild's splash image using a local file.
4704 * TODO
4705 * @param {String} splash_path The path to the new splash
4706 * @return {Promise}
4707 */
4708 async updateSplashFromFile(splash_path) {
4709 const buffer = await modules__WEBPACK_IMPORTED_MODULE_0__["Utilities"].readFileBuffer(splash_path);
4710 return this.updateSplash(buffer);
4711 }
4712
4713 /**
4714 * Updates this guild's system channel.
4715 * @param {GuildTextChannel} systemChannel The new system channel
4716 * @return {Promise}
4717 */
4718 updateSystemChannel(system_channel) {
4719 return this.updateGuild({ system_channel_id: system_channel.id || system_channel });
4720 }
4721
4722}
4723
4724
4725
4726/***/ }),
4727
4728/***/ "./src/structs/discord/message.js":
4729/*!****************************************!*\
4730 !*** ./src/structs/discord/message.js ***!
4731 \****************************************/
4732/*! exports provided: Reaction, Embed, Message, DefaultMessage, RecipientAddMessage, RecipientRemoveMessage, CallMessage, GroupChannelNameChangeMessage, GroupChannelIconChangeMessage, MessagePinnedMessage, GuildMemberJoinMessage */
4733/***/ (function(module, __webpack_exports__, __webpack_require__) {
4734
4735"use strict";
4736__webpack_require__.r(__webpack_exports__);
4737/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Reaction", function() { return Reaction; });
4738/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Embed", function() { return Embed; });
4739/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Message", function() { return Message; });
4740/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DefaultMessage", function() { return DefaultMessage; });
4741/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RecipientAddMessage", function() { return RecipientAddMessage; });
4742/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RecipientRemoveMessage", function() { return RecipientRemoveMessage; });
4743/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CallMessage", function() { return CallMessage; });
4744/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GroupChannelNameChangeMessage", function() { return GroupChannelNameChangeMessage; });
4745/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GroupChannelIconChangeMessage", function() { return GroupChannelIconChangeMessage; });
4746/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MessagePinnedMessage", function() { return MessagePinnedMessage; });
4747/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GuildMemberJoinMessage", function() { return GuildMemberJoinMessage; });
4748/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
4749/* harmony import */ var structs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! structs */ "./src/structs/structs.js");
4750/* harmony import */ var _channel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./channel */ "./src/structs/discord/channel.js");
4751/* harmony import */ var _user__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./user */ "./src/structs/discord/user.js");
4752/**
4753 * BetterDiscord Message Struct
4754 * Copyright (c) 2018-present JsSucks
4755 * All rights reserved.
4756 *
4757 * This source code is licensed under the MIT license found at
4758 * https://github.com/JsSucks/BetterDiscordApp/blob/master/LICENSE
4759*/
4760
4761
4762
4763
4764
4765
4766const reactions = new WeakMap();
4767
4768class Reaction {
4769 constructor(data, message_id, channel_id) {
4770 if (reactions.has(data)) return reactions.get(data);
4771 reactions.set(data, this);
4772
4773 this.discordObject = data;
4774 this.messageId = message_id;
4775 this.channelId = channel_id;
4776 }
4777
4778 get emoji() {
4779 const id = this.discordObject.emoji.id;
4780 if (!id || !this.guild) return this.discordObject.emoji;
4781 return this.guild.emojis.find(e => e.id === id);
4782 }
4783
4784 get count() { return this.discordObject.count; }
4785 get me() { return this.discordObject.me; }
4786
4787 get channel() {
4788 return _channel__WEBPACK_IMPORTED_MODULE_2__["Channel"].fromId(this.channel_id);
4789 }
4790
4791 get message() {
4792 if (this.channel) return this.channel.messages.find(m => m.id === this.messageId);
4793 }
4794
4795 get guild() {
4796 if (this.channel) return this.channel.guild;
4797 }
4798}
4799
4800const embeds = new WeakMap();
4801
4802class Embed {
4803 constructor(data, message_id, channel_id) {
4804 if (embeds.has(data)) return embeds.get(data);
4805 embeds.set(data, this);
4806
4807 this.discordObject = data;
4808 this.messageId = message_id;
4809 this.channelId = channel_id;
4810 }
4811
4812 get title() { return this.discordObject.title; }
4813 get type() { return this.discordObject.type; }
4814 get description() { return this.discordObject.description; }
4815 get url() { return this.discordObject.url; }
4816 get timestamp() { return this.discordObject.timestamp; }
4817 get colour() { return this.discordObject.color; }
4818 get footer() { return this.discordObject.footer; }
4819 get image() { return this.discordObject.image; }
4820 get thumbnail() { return this.discordObject.thumbnail; }
4821 get video() { return this.discordObject.video; }
4822 get provider() { return this.discordObject.provider; }
4823 get author() { return this.discordObject.author; }
4824 get fields() { return this.discordObject.fields; }
4825
4826 get channel() {
4827 return _channel__WEBPACK_IMPORTED_MODULE_2__["Channel"].fromId(this.channelId);
4828 }
4829
4830 get message() {
4831 if (this.channel) return this.channel.messages.find(m => m.id === this.messageId);
4832 }
4833
4834 get guild() {
4835 if (this.channel) return this.channel.guild;
4836 }
4837}
4838
4839const messages = new WeakMap();
4840
4841/**
4842 * @memberof module:DiscordAPI
4843 */
4844class Message {
4845
4846 constructor(data) {
4847 if (messages.has(data)) return messages.get(data);
4848 messages.set(data, this);
4849
4850 this.discordObject = data;
4851 }
4852
4853 static from(data) {
4854 switch (data.type) {
4855 default: return new Message(data);
4856 case 0: return new DefaultMessage(data);
4857 case 1: return new RecipientAddMessage(data);
4858 case 2: return new RecipientRemoveMessage(data);
4859 case 3: return new CallMessage(data);
4860 case 4: return new GroupChannelNameChangeMessage(data);
4861 case 5: return new GroupChannelIconChangeMessage(data);
4862 case 6: return new MessagePinnedMessage(data);
4863 case 7: return new GuildMemberJoinMessage(data);
4864 }
4865 }
4866
4867 static get DefaultMessage() { return DefaultMessage; }
4868 static get RecipientAddMessage() { return RecipientAddMessage; }
4869 static get RecipientRemoveMessage() { return RecipientRemoveMessage; }
4870 static get CallMessage() { return CallMessage; }
4871 static get GroupChannelNameChangeMessage() { return GroupChannelNameChangeMessage; }
4872 static get GroupChannelIconChangeMessage() { return GroupChannelIconChangeMessage; }
4873 static get MessagePinnedMessage() { return MessagePinnedMessage; }
4874 static get GuildMemberJoinMessage() { return GuildMemberJoinMessage; }
4875
4876 static get Reaction() { return Reaction; }
4877 static get Embed() { return Embed; }
4878
4879 get id() { return this.discordObject.id; }
4880 get channelId() { return this.discordObject.channel_id; }
4881 get nonce() { return this.discordObject.nonce; }
4882 get type() { return this.discordObject.type; }
4883 get timestamp() { return this.discordObject.timestamp; }
4884 get state() { return this.discordObject.state; }
4885 get nick() { return this.discordObject.nick; }
4886 get colourString() { return this.discordObject.colorString; }
4887
4888 get author() {
4889 if (this.discordObject.author && !this.webhookId) return _user__WEBPACK_IMPORTED_MODULE_3__["User"].from(this.discordObject.author);
4890 }
4891
4892 get channel() {
4893 return _channel__WEBPACK_IMPORTED_MODULE_2__["Channel"].fromId(this.channelId);
4894 }
4895
4896 get guild() {
4897 if (this.channel) return this.channel.guild;
4898 }
4899
4900 /**
4901 * Deletes the message.
4902 * @return {Promise}
4903 */
4904 delete() {
4905 if (!this.isDeletable) throw new Error(`Message type ${this.type} is not deletable.`);
4906 if (this.author !== modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentUser) {
4907 if (this.channel.assertPermissions) this.channel.assertPermissions("MANAGE_MESSAGES", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.MANAGE_MESSAGES);
4908 else if (!this.channel.owner === modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentUser) throw new structs__WEBPACK_IMPORTED_MODULE_1__["InsufficientPermissions"]("MANAGE_MESSAGES");
4909 }
4910
4911 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].APIModule.delete(`${modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordConstants.Endpoints.MESSAGES(this.channelId)}/${this.id}`);
4912 }
4913
4914 get isDeletable() {
4915 return this.type === "DEFAULT" || this.type === "CHANNEL_PINNED_MESSAGE" || this.type === "GUILD_MEMBER_JOIN";
4916 }
4917
4918 /**
4919 * Jumps to the message.
4920 */
4921 jumpTo(flash = true) {
4922 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageActions.jumpToMessage(this.channelId, this.id, flash);
4923 }
4924
4925}
4926
4927
4928
4929class DefaultMessage extends Message {
4930 get webhookId() { return this.discordObject.webhookId; }
4931 get type() { return "DEFAULT"; }
4932 get content() { return this.discordObject.content; }
4933 get contentParsed() { return this.discordObject.contentParsed; }
4934 get inviteCodes() { return this.discordObject.invites; }
4935 get attachments() { return this.discordObject.attachments; }
4936 get mentionIds() { return this.discordObject.mentions; }
4937 get mentionRoleIds() { return this.discordObject.mentionRoles; }
4938 get mentionEveryone() { return this.discordObject.mentionEveryone; }
4939 get editedTimestamp() { return this.discordObject.editedTimestamp; }
4940 get tts() { return this.discordObject.tts; }
4941 get mentioned() { return this.discordObject.mentioned; }
4942 get bot() { return this.discordObject.bot; }
4943 get blocked() { return this.discordObject.blocked; }
4944 get pinned() { return this.discordObject.pinned; }
4945 get activity() { return this.discordObject.activity; }
4946 get application() { return this.discordObject.application; }
4947
4948 get webhook() {
4949 if (this.webhookId) return this.discordObject.author;
4950 }
4951
4952 get mentions() {
4953 return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(this.mentionIds, id => _user__WEBPACK_IMPORTED_MODULE_3__["User"].fromId(id));
4954 }
4955
4956 get mention_roles() {
4957 return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(this.mentionRoleIds, id => this.guild.roles.find(r => r.id === id));
4958 }
4959
4960 get embeds() {
4961 return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(this.discordObject.embeds, r => new Embed(r, this.id, this.channelId));
4962 }
4963
4964 get reactions() {
4965 return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(this.discordObject.reactions, r => new Reaction(r, this.id, this.channelId));
4966 }
4967
4968 get edited() {
4969 return !!this.editedTimestamp;
4970 }
4971
4972 /**
4973 * Programmatically update the message's content.
4974 * @param {String} content The message's new content
4975 * @param {Boolean} parse Whether to parse the message or update it as it is
4976 * @return {Promise}
4977 */
4978 async edit(content, parse = false) {
4979 if (this.author !== modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentUser) throw new Error("Cannot edit messages sent by other users.");
4980 if (parse) content = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageParser.parse(this.discordObject, content);
4981 else content = {content};
4982
4983 const response = await modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].APIModule.patch({
4984 url: `${modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordConstants.Endpoints.MESSAGES(this.channelId)}/${this.id}`,
4985 body: content
4986 });
4987
4988 this.discordObject = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageStore.getMessage(this.id, response.body.id);
4989 messages.set(this.discordObject, this);
4990 }
4991
4992 /**
4993 * Start the edit mode of the UI.
4994 * @param {String} content A string to show in the message text area - if empty the message's current content will be used
4995 */
4996 startEdit(content) {
4997 if (this.author !== modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentUser) throw new Error("Cannot edit messages sent by other users.");
4998 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageActions.startEditMessage(this.channelId, this.id, content || this.content);
4999 }
5000
5001 /**
5002 * Exit the edit mode of the UI.
5003 */
5004 endEdit() {
5005 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].MessageActions.endEditMessage();
5006 }
5007}
5008
5009class RecipientAddMessage extends Message {
5010 get type() { return "RECIPIENT_ADD"; }
5011 get addedUserId() { return this.discordObject.mentions[0]; }
5012
5013 get addedUser() {
5014 return _user__WEBPACK_IMPORTED_MODULE_3__["User"].fromId(this.addedUserId);
5015 }
5016}
5017
5018class RecipientRemoveMessage extends Message {
5019 get type() { return "RECIPIENT_REMOVE"; }
5020 get removedUserId() { return this.discordObject.mentions[0]; }
5021
5022 get removedUser() {
5023 return _user__WEBPACK_IMPORTED_MODULE_3__["User"].fromId(this.removedUserId);
5024 }
5025
5026 get userLeft() {
5027 return this.author === this.removedUser;
5028 }
5029}
5030
5031class CallMessage extends Message {
5032 get type() { return "CALL"; }
5033 get mentionIds() { return this.discordObject.mentions; }
5034 get call() { return this.discordObject.call; }
5035
5036 get endedTimestamp() { return this.call.endedTimestamp; }
5037
5038 get mentions() {
5039 return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(this.mentionIds, id => _user__WEBPACK_IMPORTED_MODULE_3__["User"].fromId(id));
5040 }
5041
5042 get participants() {
5043 return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(this.call.participants, id => _user__WEBPACK_IMPORTED_MODULE_3__["User"].fromId(id));
5044 }
5045}
5046
5047class GroupChannelNameChangeMessage extends Message {
5048 get type() { return "CHANNEL_NAME_CHANGE"; }
5049 get newName() { return this.discordObject.content; }
5050}
5051
5052class GroupChannelIconChangeMessage extends Message {
5053 get type() { return "CHANNEL_ICON_CHANGE"; }
5054}
5055
5056class MessagePinnedMessage extends Message {
5057 get type() { return "CHANNEL_PINNED_MESSAGE"; }
5058}
5059
5060class GuildMemberJoinMessage extends Message {
5061 get type() { return "GUILD_MEMBER_JOIN"; }
5062}
5063
5064
5065/***/ }),
5066
5067/***/ "./src/structs/discord/user.js":
5068/*!*************************************!*\
5069 !*** ./src/structs/discord/user.js ***!
5070 \*************************************/
5071/*! exports provided: User, GuildMember */
5072/***/ (function(module, __webpack_exports__, __webpack_require__) {
5073
5074"use strict";
5075__webpack_require__.r(__webpack_exports__);
5076/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "User", function() { return User; });
5077/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GuildMember", function() { return GuildMember; });
5078/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
5079/* harmony import */ var structs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! structs */ "./src/structs/structs.js");
5080/* harmony import */ var _guild__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./guild */ "./src/structs/discord/guild.js");
5081/* harmony import */ var _channel__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./channel */ "./src/structs/discord/channel.js");
5082/**
5083 * BetterDiscord User Struct
5084 * Copyright (c) 2018-present JsSucks
5085 * All rights reserved.
5086 *
5087 * This source code is licensed under the MIT license found at
5088 * https://github.com/JsSucks/BetterDiscordApp/blob/master/LICENSE
5089*/
5090
5091
5092
5093
5094
5095
5096const users = new WeakMap();
5097
5098/**
5099 * @memberof module:DiscordAPI
5100 */
5101class User {
5102
5103 constructor(data) {
5104 if (users.has(data)) return users.get(data);
5105 users.set(data, this);
5106
5107 this.discordObject = data;
5108 }
5109
5110 static from(data) {
5111 return new User(data);
5112 }
5113
5114 static fromId(id) {
5115 const user = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserStore.getUser(id);
5116 if (user) return User.from(user);
5117 }
5118
5119 get id() { return this.discordObject.id; }
5120 get username() { return this.discordObject.username; }
5121 get usernameLowerCase() { return this.discordObject.usernameLowerCase; }
5122 get discriminator() { return this.discordObject.discriminator; }
5123 get avatar() { return this.discordObject.avatar; }
5124 get email() { return undefined; }
5125 get phone() { return undefined; }
5126 get flags() { return this.discordObject.flags; }
5127 get isBot() { return this.discordObject.bot; }
5128 get premium() { return this.discordObject.premium; }
5129 get verified() { return this.discordObject.verified; }
5130 get mfaEnabled() { return this.discordObject.mfaEnabled; }
5131 get mobile() { return this.discordObject.mobile; }
5132
5133 get tag() { return this.discordObject.tag; }
5134 get avatarUrl() { return this.discordObject.avatarURL; }
5135 get createdAt() { return this.discordObject.createdAt; }
5136
5137 get isClamied() { return this.discordObject.isClaimed(); }
5138 get isLocalBot() { return this.discordObject.isLocalBot(); }
5139 get isPhoneVerified() { return this.discordObject.isPhoneVerified(); }
5140
5141 get guilds() {
5142 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].guilds.filter(g => g.members.find(m => m.user === this));
5143 }
5144
5145 get status() {
5146 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserStatusStore.getStatus(this.id);
5147 }
5148
5149 get activity() {
5150 // type can be either 0 (normal/rich presence game), 1 (streaming) or 2 (listening to Spotify)
5151 // (3 appears as watching but is undocumented)
5152 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserStatusStore.getActivity(this.id);
5153 }
5154
5155 get note() {
5156 const note = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserNoteStore.getNote(this.id);
5157 if (note) return note;
5158 }
5159
5160 /**
5161 * Updates the note for this user.
5162 * @param {String} note The new note
5163 * @return {Promise}
5164 */
5165 updateNote(note) {
5166 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].APIModule.put({
5167 url: `${modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordConstants.Endpoints.NOTES}/${this.id}`,
5168 body: { note }
5169 });
5170 }
5171
5172 get privateChannel() {
5173 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].channels.find(c => c.type === "DM" && c.recipientId === this.id);
5174 }
5175
5176 async ensurePrivateChannel() {
5177 if (modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentUser === this)
5178 throw new Error("Cannot create a direct message channel to the current user.");
5179 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));
5180 }
5181
5182 async sendMessage(content, parse = true) {
5183 const channel = await this.ensurePrivateChannel();
5184 return channel.sendMessage(content, parse);
5185 }
5186
5187 get isFriend() {
5188 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].RelationshipStore.isFriend(this.id);
5189 }
5190
5191 get isBlocked() {
5192 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].RelationshipStore.isBlocked(this.id);
5193 }
5194
5195 addFriend() {
5196 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].RelationshipManager.addRelationship(this.id, {location: "Context Menu"});
5197 }
5198
5199 removeFriend() {
5200 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].RelationshipManager.removeRelationship(this.id, {location: "Context Menu"});
5201 }
5202
5203 block() {
5204 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].RelationshipManager.addRelationship(this.id, {location: "Context Menu"}, modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordConstants.RelationshipTypes.BLOCKED);
5205 }
5206
5207 unblock() {
5208 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].RelationshipManager.removeRelationship(this.id, {location: "Context Menu"});
5209 }
5210
5211 /**
5212 * Opens the profile modal for this user.
5213 * @param {String} section The section to open (see DiscordConstants.UserProfileSections)
5214 */
5215 openUserProfileModal(section = "USER_INFO") {
5216 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserProfileModal.open(this.id);
5217 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserProfileModal.setSection(section);
5218 }
5219}
5220
5221
5222
5223const guild_members = new WeakMap();
5224
5225class GuildMember {
5226 constructor(data, guild_id) {
5227 if (guild_members.has(data)) return guild_members.get(data);
5228 guild_members.set(data, this);
5229
5230 this.discordObject = data;
5231 this.guildId = guild_id;
5232 }
5233
5234 get userId() { return this.discordObject.userId; }
5235 get nickname() { return this.discordObject.nick; }
5236 get colourString() { return this.discordObject.colorString; }
5237 get hoistRoleId() { return this.discordObject.hoistRoleId; }
5238 get roleIds() { return this.discordObject.roles; }
5239
5240 get user() {
5241 return User.fromId(this.userId);
5242 }
5243
5244 get name() {
5245 return this.nickname || this.user.username;
5246 }
5247
5248 get guild() {
5249 return _guild__WEBPACK_IMPORTED_MODULE_2__["Guild"].fromId(this.guildId);
5250 }
5251
5252 get roles() {
5253 return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(this.roleIds, id => this.guild.roles.find(r => r.id === id))
5254 .sort((r1, r2) => r1.position === r2.position ? 0 : r1.position > r2.position ? 1 : -1);
5255 }
5256
5257 get hoistRole() {
5258 return this.guild.roles.find(r => r.id === this.hoistRoleId);
5259 }
5260
5261 checkPermissions(perms) {
5262 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].Permissions.can(perms, modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentUser.discordObject, this.guild.discordObject);
5263 }
5264
5265 assertPermissions(name, perms) {
5266 if (!this.checkPermissions(perms)) throw new structs__WEBPACK_IMPORTED_MODULE_1__["InsufficientPermissions"](name);
5267 }
5268
5269 /**
5270 * Opens the modal to change this user's nickname.
5271 */
5272 openChangeNicknameModal() {
5273 if (modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentUser === this.user)
5274 this.assertPermissions("CHANGE_NICKNAME", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.CHANGE_NICKNAME);
5275 else this.assertPermissions("MANAGE_NICKNAMES", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.MANAGE_NICKNAMES);
5276
5277 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].ChangeNicknameModal.open(this.guildId, this.userId);
5278 }
5279
5280 /**
5281 * Changes the user's nickname on this guild.
5282 * @param {String} nickname The user's new nickname
5283 * @return {Promise}
5284 */
5285 changeNickname(nick) {
5286 if (modules__WEBPACK_IMPORTED_MODULE_0__["DiscordAPI"].currentUser === this.user)
5287 this.assertPermissions("CHANGE_NICKNAME", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.CHANGE_NICKNAME);
5288 else this.assertPermissions("MANAGE_NICKNAMES", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.MANAGE_NICKNAMES);
5289
5290 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].APIModule.patch({
5291 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}`,
5292 body: { nick }
5293 });
5294 }
5295
5296 /**
5297 * Kicks this user from the guild.
5298 * @param {String} reason A reason to attach to the audit log entry
5299 * @return {Promise}
5300 */
5301 kick(reason = "") {
5302 this.assertPermissions("KICK_MEMBERS", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.KICK_MEMBERS);
5303 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildActions.kickUser(this.guildId, this.userId, reason);
5304 }
5305
5306 /**
5307 * Bans this user from the guild.
5308 * @param {Number} daysToDelete The number of days of the user's recent message history to delete
5309 * @param {String} reason A reason to attach to the audit log entry
5310 * @return {Promise}
5311 */
5312 ban(daysToDelete = 1, reason = "") {
5313 this.assertPermissions("BAN_MEMBERS", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.BAN_MEMBERS);
5314 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildActions.banUser(this.guildId, this.userId, daysToDelete, reason);
5315 }
5316
5317 /**
5318 * Removes the ban for this user.
5319 * @return {Promise}
5320 */
5321 unban() {
5322 this.assertPermissions("BAN_MEMBERS", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.BAN_MEMBERS);
5323 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildActions.unbanUser(this.guildId, this.userId);
5324 }
5325
5326 /**
5327 * Moves this user to another voice channel.
5328 * @param {GuildVoiceChannel} channel The channel to move this user to
5329 */
5330 move(channel) {
5331 this.assertPermissions("MOVE_MEMBERS", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.MOVE_MEMBERS);
5332 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildActions.setChannel(this.guildId, this.userId, channel.id);
5333 }
5334
5335 /**
5336 * Mutes this user for everyone in the guild.
5337 */
5338 mute(active = true) {
5339 this.assertPermissions("MUTE_MEMBERS", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.MUTE_MEMBERS);
5340 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildActions.setServerMute(this.guildId, this.userId, active);
5341 }
5342
5343 /**
5344 * Unmutes this user.
5345 */
5346 unmute() {
5347 this.mute(false);
5348 }
5349
5350 /**
5351 * Deafens this user.
5352 */
5353 deafen(active = true) {
5354 this.assertPermissions("DEAFEN_MEMBERS", modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordPermissions.DEAFEN_MEMBERS);
5355 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].GuildActions.setServerDeaf(this.guildId, this.userId, active);
5356 }
5357
5358 /**
5359 * Undeafens this user.
5360 */
5361 undeafen() {
5362 this.deafen(false);
5363 }
5364
5365 /**
5366 * Gives this user a role.
5367 * @param {Role} role The role to add
5368 * @return {Promise}
5369 */
5370 addRole(...roles) {
5371 const newRoles = this.roleIds.concat([]);
5372 let changed = false;
5373 for (let role of roles) {
5374 if (newRoles.includes(role.id || role)) continue;
5375 newRoles.push(role.id || role);
5376 changed = true;
5377 }
5378 if (!changed) return;
5379 return this.updateRoles(newRoles);
5380 }
5381
5382 /**
5383 * Removes a role from this user.
5384 * @param {Role} role The role to remove
5385 * @return {Promise}
5386 */
5387 removeRole(...roles) {
5388 const newRoles = this.roleIds.concat([]);
5389 let changed = false;
5390 for (let role of roles) {
5391 if (!newRoles.includes(role.id || role)) continue;
5392 modules__WEBPACK_IMPORTED_MODULE_0__["Utilities"].removeFromArray(newRoles, role.id || role);
5393 changed = true;
5394 }
5395 if (!changed) return;
5396 return this.updateRoles(newRoles);
5397 }
5398
5399 /**
5400 * Updates this user's roles.
5401 * @param {Array} roles An array of Role objects or role IDs
5402 * @return {Promise}
5403 */
5404 updateRoles(roles) {
5405 roles = roles.map(r => r.id || r);
5406 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].APIModule.patch({
5407 url: `${modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].DiscordConstants.Endpoints.GUILD_MEMBERS(this.guildId)}/${this.userId}`,
5408 body: { roles }
5409 });
5410 }
5411}
5412
5413
5414/***/ }),
5415
5416/***/ "./src/structs/discord/usersettings.js":
5417/*!*********************************************!*\
5418 !*** ./src/structs/discord/usersettings.js ***!
5419 \*********************************************/
5420/*! exports provided: UserSettings */
5421/***/ (function(module, __webpack_exports__, __webpack_require__) {
5422
5423"use strict";
5424__webpack_require__.r(__webpack_exports__);
5425/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "UserSettings", function() { return UserSettings; });
5426/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
5427/* harmony import */ var structs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! structs */ "./src/structs/structs.js");
5428/* harmony import */ var _guild__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./guild */ "./src/structs/discord/guild.js");
5429/**
5430 * BetterDiscord Channel Struct
5431 * Copyright (c) 2018-present JsSucks
5432 * All rights reserved.
5433 *
5434 * This source code is licensed under the MIT license found at
5435 * https://github.com/JsSucks/BetterDiscordApp/blob/master/LICENSE
5436*/
5437
5438
5439
5440
5441
5442
5443
5444/**
5445 * @memberof module:DiscordAPI
5446 */
5447class UserSettings {
5448 /**
5449 * Opens Discord's settings UI.
5450 */
5451 static open(section = "ACCOUNT") {
5452 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsWindow.setSection(section);
5453 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsWindow.open();
5454 }
5455
5456 /**
5457 * The user's current status. Either "online", "idle", "dnd" or "invisible".
5458 */
5459 static get status() { return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.status; }
5460
5461 /**
5462 * The user's selected explicit content filter level.
5463 * 0 == off, 1 == everyone except friends, 2 == everyone
5464 * Configurable in the privacy and safety panel.
5465 */
5466 static get explicitContentFilter() { return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.explicitContentFilter; }
5467
5468 /**
5469 * Whether to disallow direct messages from server members by default.
5470 */
5471 static get defaultGuildsRestricted() { return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.defaultGuildsRestricted; }
5472
5473 /**
5474 * An array of guilds to disallow direct messages from their members.
5475 * This is bypassed if the member is has another mutual guild with this disabled, or the member is friends with the current user.
5476 * Configurable in each server's privacy settings.
5477 */
5478 static get restrictedGuildIds() { return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.restrictedGuilds; }
5479
5480 static get restrictedGuilds() {
5481 return structs__WEBPACK_IMPORTED_MODULE_1__["List"].from(this.restrictedGuildIds, id => _guild__WEBPACK_IMPORTED_MODULE_2__["Guild"].fromId(id) || id);
5482 }
5483
5484 /**
5485 * An array of flags specifying who should be allowed to add the current user as a friend.
5486 * If everyone is checked, this will only have one item, "all". Otherwise it has either "mutual_friends", "mutual_guilds", both or neither.
5487 * Configurable in the privacy and safety panel.
5488 */
5489 static get friendSourceFlags() { return Object.keys(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.friendSourceFlags); }
5490 static get friendSourceEveryone() { return this.friend_source_flags.include("all"); }
5491 static get friendSourceMutual_friends() { return this.friend_source_flags.include("all") || this.friend_source_flags.include("mutual_friends"); }
5492 static get friendSourceMutual_guilds() { return this.friend_source_flags.include("all") || this.friend_source_flags.include("mutual_guilds"); }
5493 static get friendSourceAnyone() { return this.friend_source_flags.length > 0; }
5494
5495 /**
5496 * Whether to automatically add accounts from other platforms running on the user's computer.
5497 * Configurable in the connections panel.
5498 */
5499 static get detectPlatformAccounts() { return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.detectPlatformAccounts; }
5500
5501 /**
5502 * The number of seconds Discord will wait for activity before sending mobile push notifications.
5503 * Configurable in the notifications panel.
5504 */
5505 static get afkTimeout() { return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.afkTimeout; }
5506
5507 /**
5508 * Whether to display the currently running game as a status message.
5509 * Configurable in the games panel.
5510 */
5511 static get showCurrentGame() { return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.showCurrentGame; }
5512
5513 /**
5514 * Whether to show images uploaded directly to Discord.
5515 * Configurable in the text and images panel.
5516 */
5517 static get inlineAttachmentMedia() { return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.inlineAttachmentMedia; }
5518
5519 /**
5520 * Whether to show images linked in Discord.
5521 * Configurable in the text and images panel.
5522 */
5523 static get inlineEmbedMedia() { return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.inlineEmbedMedia; }
5524
5525 /**
5526 * Whether to automatically play GIFs when the Discord window is active without having to hover the mouse over the image.
5527 * Configurable in the text and images panel.
5528 */
5529 static get autoplayGifs() { return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.gifAutoPlay; }
5530
5531 /**
5532 * Whether to show content from HTTP[s] links as embeds.
5533 * Configurable in the text and images panel.
5534 */
5535 static get showEmbeds() { return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.renderEmbeds; }
5536
5537 /**
5538 * Whether to show a message's reactions.
5539 * Configurable in the text and images panel.
5540 */
5541 static get showReactions() { return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.renderReactions; }
5542
5543 /**
5544 * Whether to play animated emoji.
5545 * Configurable in the text and images panel.
5546 */
5547 static get animateEmoji() { return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.animateEmoji; }
5548
5549 /**
5550 * Whether to convert ASCII emoticons to emoji.
5551 * Configurable in the text and images panel.
5552 */
5553 static get convertEmoticons() { return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.convertEmoticons; }
5554
5555 /**
5556 * Whether to allow playing text-to-speech messages.
5557 * Configurable in the text and images panel.
5558 */
5559 static get allowTts() { return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.enableTTSCommand; }
5560
5561 /**
5562 * The user's selected theme. Either "dark" or "light".
5563 * Configurable in the appearance panel.
5564 */
5565 static get theme() { return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.theme; }
5566
5567 /**
5568 * Whether the user has enabled compact mode.
5569 * `true` if compact mode is enabled, `false` if cozy mode is enabled.
5570 * Configurable in the appearance panel.
5571 */
5572 static get displayCompact() { return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.messageDisplayCompact; }
5573
5574 /**
5575 * Whether the user has enabled developer mode.
5576 * Currently only adds a "Copy ID" option to the context menu on users, guilds and channels.
5577 * Configurable in the appearance panel.
5578 */
5579 static get developerMode() { return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.developerMode; }
5580
5581 /**
5582 * The user's selected language code.
5583 * Configurable in the language panel.
5584 */
5585 static get locale() { return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.locale; }
5586
5587 /**
5588 * The user's timezone offset in hours.
5589 * This is not configurable.
5590 */
5591 static get timezoneOffset() { return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserSettingsStore.timezoneOffset; }
5592}
5593
5594
5595
5596/***/ }),
5597
5598/***/ "./src/structs/dom/classname.js":
5599/*!**************************************!*\
5600 !*** ./src/structs/dom/classname.js ***!
5601 \**************************************/
5602/*! exports provided: default */
5603/***/ (function(module, __webpack_exports__, __webpack_require__) {
5604
5605"use strict";
5606__webpack_require__.r(__webpack_exports__);
5607/* harmony import */ var _selector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./selector */ "./src/structs/dom/selector.js");
5608
5609
5610/**
5611 * Representation of a Class Name
5612 * @memberof module:DOMTools
5613 **/
5614class ClassName {
5615 /**
5616 *
5617 * @param {string} name - name of the class to represent
5618 */
5619 constructor(name) {
5620 this.value = name;
5621 }
5622
5623 /**
5624 * Concatenates new class names to the current one using spaces.
5625 * @param {string} classNames - list of class names to add to this class name
5626 * @returns {ClassName} returns self to allow chaining
5627 */
5628 add(...classNames) {
5629 for (let i = 0; i < classNames.length; i++) this.value += " " + classNames[i];
5630 return this;
5631 }
5632
5633 /**
5634 * Returns the raw class name, this is how native function get the value.
5635 * @returns {string} raw class name.
5636 */
5637 toString() {
5638 return this.value;
5639 }
5640
5641 /**
5642 * Returns the raw class name, this is how native function get the value.
5643 * @returns {string} raw class name.
5644 */
5645 valueOf() {
5646 return this.value;
5647 }
5648
5649 /**
5650 * Returns the classname represented as {@link module:DOMTools.Selector}.
5651 * @returns {Selector} selector representation of this class name.
5652 */
5653 get selector() {
5654 return new _selector__WEBPACK_IMPORTED_MODULE_0__["default"](this.value);
5655 }
5656
5657 get single() {
5658 return this.value.split(" ")[0];
5659 }
5660
5661 get first() {
5662 return this.value.split(" ")[0];
5663 }
5664}
5665
5666/* harmony default export */ __webpack_exports__["default"] = (ClassName);
5667
5668/***/ }),
5669
5670/***/ "./src/structs/dom/observer.js":
5671/*!*************************************!*\
5672 !*** ./src/structs/dom/observer.js ***!
5673 \*************************************/
5674/*! exports provided: default */
5675/***/ (function(module, __webpack_exports__, __webpack_require__) {
5676
5677"use strict";
5678__webpack_require__.r(__webpack_exports__);
5679/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
5680/**
5681 * BetterDiscord Client DOM Module
5682 * Copyright (c) 2015-present JsSucks - https://github.com/JsSucks
5683 * All rights reserved.
5684 * https://betterdiscord.net
5685 *
5686 * This source code is licensed under the MIT license found in the
5687 * LICENSE file in the root directory of this source tree.
5688*/
5689
5690
5691
5692/**
5693 * Representation of a MutationObserver but with helpful utilities.
5694 * @memberof module:DOMTools
5695 **/
5696class DOMObserver {
5697 constructor(root, options) {
5698 this.observe = this.observe.bind(this);
5699 this.subscribe = this.subscribe.bind(this);
5700 this.observerCallback = this.observerCallback.bind(this);
5701
5702 this.active = false;
5703 this.root = root || document.getElementById("app-mount");
5704 this.options = options || {attributes: true, childList: true, subtree: true};
5705
5706 this.observer = new MutationObserver(this.observerCallback);
5707 this.observe();
5708 }
5709
5710 observerCallback(mutations) {
5711 for (const sub of this.subscriptions) {
5712 try {
5713 const filteredMutations = sub.filter ? mutations.filter(sub.filter) : mutations;
5714
5715 if (sub.group) {
5716 if (!filteredMutations.length) continue;
5717 sub.callback.call(sub.bind || sub, filteredMutations);
5718 }
5719 else {
5720 for (const mutation of filteredMutations) sub.callback.call(sub.bind || sub, mutation);
5721 }
5722 }
5723 catch (err) {
5724 modules__WEBPACK_IMPORTED_MODULE_0__["Logger"].stacktrace("DOMObserver", "Error in observer callback", err);
5725 }
5726 }
5727 }
5728
5729 /**
5730 * Starts observing the element. This will be called when attaching a callback.
5731 * You don't need to call this manually.
5732 */
5733 observe() {
5734 if (this.active) return;
5735 this.observer.observe(this.root, this.options);
5736 this.active = true;
5737 }
5738
5739 /**
5740 * Disconnects this observer. This stops callbacks being called, but does not unbind them.
5741 * You probably want to use observer.unsubscribeAll instead.
5742 */
5743 disconnect() {
5744 if (!this.active) return;
5745 this.observer.disconnect();
5746 this.active = false;
5747 }
5748
5749 reconnect() {
5750 if (this.active) {
5751 this.disconnect();
5752 this.observe();
5753 }
5754 }
5755
5756 get root() { return this._root; }
5757 set root(root) { this._root = root; this.reconnect(); }
5758
5759 get options() { return this._options; }
5760 set options(options) { this._options = options; this.reconnect(); }
5761
5762 get subscriptions() {
5763 return this._subscriptions || (this._subscriptions = []);
5764 }
5765
5766 /**
5767 * Subscribes to mutations.
5768 * @param {Function} callback A function to call when on a mutation
5769 * @param {Function} filter A function to call to filter mutations
5770 * @param {Any} bind Something to bind the callback to
5771 * @param {Boolean} group Whether to call the callback with an array of mutations instead of a single mutation
5772 * @return {Object}
5773 */
5774 subscribe(callback, filter, bind, group) {
5775 const subscription = {callback, filter, bind, group};
5776 this.subscriptions.push(subscription);
5777 this.observe();
5778 return subscription;
5779 }
5780
5781 /**
5782 * Removes a subscription and disconnect if there are none left.
5783 * @param {Object} subscription A subscription object returned by observer.subscribe
5784 */
5785 unsubscribe(subscription) {
5786 if (!this.subscriptions.includes(subscription)) subscription = this.subscriptions.find(s => s.callback === subscription);
5787 modules__WEBPACK_IMPORTED_MODULE_0__["Utilities"].removeFromArray(this.subscriptions, subscription);
5788 if (!this.subscriptions.length) this.disconnect();
5789 }
5790
5791 unsubscribeAll() {
5792 this.subscriptions.splice(0, this.subscriptions.length);
5793 this.disconnect();
5794 }
5795
5796 /**
5797 * Subscribes to mutations that affect an element matching a selector.
5798 * @param {Function} callback A function to call when on a mutation
5799 * @param {Function} filter A function to call to filter mutations
5800 * @param {Any} bind Something to bind the callback to
5801 * @param {Boolean} group Whether to call the callback with an array of mutations instead of a single mutation
5802 * @return {Object}
5803 */
5804 subscribeToQuerySelector(callback, selector, bind, group) {
5805 return this.subscribe(callback, mutation => {
5806 return mutation.target.matches(selector) // If the target matches the selector
5807 || Array.from(mutation.addedNodes).concat(Array.from(mutation.removedNodes)) // Or if either an added or removed node
5808 .find(n => n instanceof Element && (n.matches(selector) || n.querySelector(selector))); // match or contain an element matching the selector
5809 }, bind, group);
5810 }
5811}
5812
5813/* harmony default export */ __webpack_exports__["default"] = (DOMObserver);
5814
5815/***/ }),
5816
5817/***/ "./src/structs/dom/selector.js":
5818/*!*************************************!*\
5819 !*** ./src/structs/dom/selector.js ***!
5820 \*************************************/
5821/*! exports provided: default */
5822/***/ (function(module, __webpack_exports__, __webpack_require__) {
5823
5824"use strict";
5825__webpack_require__.r(__webpack_exports__);
5826/**
5827 * Representation of a Selector
5828 * @memberof module:DOMTools
5829 **/
5830class Selector {
5831 /**
5832 *
5833 * @param {string} classname - class to create selector for
5834 */
5835 constructor(className) {
5836 this.value = " ." + className.split(" ").join(".");
5837 }
5838
5839 /**
5840 * Returns the raw selector, this is how native function get the value.
5841 * @returns {string} raw selector.
5842 */
5843 toString() {
5844 return this.value;
5845 }
5846
5847 /**
5848 * Returns the raw selector, this is how native function get the value.
5849 * @returns {string} raw selector.
5850 */
5851 valueOf() {
5852 return this.value;
5853 }
5854
5855 selector(symbol, other) {
5856 this.value = `${this.toString()} ${symbol} ${other.toString()}`;
5857 return this;
5858 }
5859
5860 /**
5861 * Adds another selector as a direct child `>` to this one.
5862 * @param {string|DOMTools.Selector} other - Selector to add as child
5863 * @returns {DOMTools.Selector} returns self to allow chaining
5864 */
5865 child(other) {
5866 return this.selector(">", other);
5867 }
5868
5869 /**
5870 * Adds another selector as a adjacent sibling `+` to this one.
5871 * @param {string|DOMTools.Selector} other - Selector to add as adjacent sibling
5872 * @returns {DOMTools.Selector} returns self to allow chaining
5873 */
5874 adjacent(other) {
5875 return this.selector("+", other);
5876 }
5877
5878 /**
5879 * Adds another selector as a general sibling `~` to this one.
5880 * @param {string|DOMTools.Selector} other - Selector to add as sibling
5881 * @returns {DOMTools.Selector} returns self to allow chaining
5882 */
5883 sibling(other) {
5884 return this.selector("~", other);
5885 }
5886
5887 /**
5888 * Adds another selector as a descendent `(space)` to this one.
5889 * @param {string|DOMTools.Selector} other - Selector to add as descendent
5890 * @returns {DOMTools.Selector} returns self to allow chaining
5891 */
5892 descend(other) {
5893 return this.selector(" ", other);
5894 }
5895
5896 /**
5897 * Adds another selector to this one via `,`.
5898 * @param {string|DOMTools.Selector} other - Selector to add
5899 * @returns {DOMTools.Selector} returns self to allow chaining
5900 */
5901 and(other) {
5902 return this.selector(",", other);
5903 }
5904}
5905
5906/* harmony default export */ __webpack_exports__["default"] = (Selector);
5907
5908/***/ }),
5909
5910/***/ "./src/structs/errors/permissionserror.js":
5911/*!************************************************!*\
5912 !*** ./src/structs/errors/permissionserror.js ***!
5913 \************************************************/
5914/*! exports provided: default, InsufficientPermissions */
5915/***/ (function(module, __webpack_exports__, __webpack_require__) {
5916
5917"use strict";
5918__webpack_require__.r(__webpack_exports__);
5919/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "InsufficientPermissions", function() { return InsufficientPermissions; });
5920class PermissionsError extends Error {
5921 constructor(message) {
5922 super(message);
5923 this.name = "PermissionsError";
5924 }
5925}
5926
5927/**
5928 * @memberof module:DiscordAPI
5929 */
5930class InsufficientPermissions extends PermissionsError {
5931 constructor(message) {
5932 super(`Missing Permission — ${message}`);
5933 this.name = "InsufficientPermissions";
5934 }
5935}
5936
5937/* harmony default export */ __webpack_exports__["default"] = (PermissionsError);
5938
5939
5940/***/ }),
5941
5942/***/ "./src/structs/list.js":
5943/*!*****************************!*\
5944 !*** ./src/structs/list.js ***!
5945 \*****************************/
5946/*! exports provided: default */
5947/***/ (function(module, __webpack_exports__, __webpack_require__) {
5948
5949"use strict";
5950__webpack_require__.r(__webpack_exports__);
5951/**
5952 * @memberof module:DiscordAPI
5953 */
5954
5955/**
5956 * Extension of Array that adds simple utilities.
5957 */
5958class List extends Array {
5959
5960 constructor() {
5961 super(...arguments);
5962 }
5963
5964 /**
5965 * Allows multiple filters at once
5966 * @param {...callable} filters - set a filters to filter the list by
5967 */
5968 get(...filters) {
5969 return this.find(item => {
5970 for (const filter of filters) {
5971 for (const key in filter) {
5972 if (filter.hasOwnProperty(key)) {
5973 if (item[key] !== filter[key]) return false;
5974 }
5975 }
5976 }
5977 return true;
5978 });
5979 }
5980}
5981
5982/* harmony default export */ __webpack_exports__["default"] = (List);
5983
5984/***/ }),
5985
5986/***/ "./src/structs/listenable.js":
5987/*!***********************************!*\
5988 !*** ./src/structs/listenable.js ***!
5989 \***********************************/
5990/*! exports provided: default */
5991/***/ (function(module, __webpack_exports__, __webpack_require__) {
5992
5993"use strict";
5994__webpack_require__.r(__webpack_exports__);
5995/**
5996 * Acts as an interface for anything that should be listenable.
5997 */
5998class Listenable {
5999
6000 constructor() {
6001 this.listeners = [];
6002 }
6003
6004 /**
6005 * Adds a listener to the current object.
6006 * @param {callable} callback - callback for when the event occurs
6007 * @returns {callable} - a way to cancel the listener without needing to call `removeListener`
6008 */
6009 addListener(callback) {
6010 if (typeof(callback) !== "function") return;
6011 this.listeners.push(callback);
6012 return () => {
6013 this.listeners.splice(this.listeners.indexOf(callback), 1);
6014 };
6015 }
6016
6017 /**
6018 * Removes a listener from the current object.
6019 * @param {callable} callback - callback that was originally registered
6020 */
6021 removeListener(callback) {
6022 if (typeof(callback) !== "function") return;
6023 this.listeners.splice(this.listeners.indexOf(callback), 1);
6024 }
6025
6026 /**
6027 * Alerts the listeners that an event occurred. Data passed is optional
6028 * @param {*} [...data] - Any data desired to be passed to listeners
6029 */
6030 alertListeners(...data) {
6031 for (let l = 0; l < this.listeners.length; l++) this.listeners[l](...data);
6032 }
6033}
6034
6035/* harmony default export */ __webpack_exports__["default"] = (Listenable);
6036
6037/***/ }),
6038
6039/***/ "./src/structs/plugin.js":
6040/*!*******************************!*\
6041 !*** ./src/structs/plugin.js ***!
6042 \*******************************/
6043/*! exports provided: default */
6044/***/ (function(module, __webpack_exports__, __webpack_require__) {
6045
6046"use strict";
6047__webpack_require__.r(__webpack_exports__);
6048/* harmony import */ var _modules_pluginupdater__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../modules/pluginupdater */ "./src/modules/pluginupdater.js");
6049/* harmony import */ var _modules_logger__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../modules/logger */ "./src/modules/logger.js");
6050/* harmony import */ var _modules_reacttools__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../modules/reacttools */ "./src/modules/reacttools.js");
6051/* harmony import */ var _ui_modals__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../ui/modals */ "./src/ui/modals.js");
6052/* harmony import */ var _modules_pluginutilities__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../modules/pluginutilities */ "./src/modules/pluginutilities.js");
6053/* harmony import */ var _modules_utilities__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../modules/utilities */ "./src/modules/utilities.js");
6054/* harmony import */ var _modules_discordmodules__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../modules/discordmodules */ "./src/modules/discordmodules.js");
6055/* harmony import */ var _ui_settings__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../ui/settings */ "./src/ui/settings/index.js");
6056
6057
6058
6059
6060
6061
6062
6063
6064
6065/* harmony default export */ __webpack_exports__["default"] = (function(config) {
6066 return class Plugin {
6067 constructor() {
6068 this._config = config;
6069 this._enabled = false;
6070 if (typeof(config.defaultConfig) != "undefined") {
6071 this.defaultSettings = {};
6072 for (let s = 0; s < config.defaultConfig.length; s++) {
6073 const current = config.defaultConfig[s];
6074 if (current.type != "category") {this.defaultSettings[current.id] = current.value;}
6075 else {
6076 this.defaultSettings[current.id] = {};
6077 for (let s = 0; s < current.settings.length; s++) {
6078 const subCurrent = current.settings[s];
6079 this.defaultSettings[current.id][subCurrent.id] = subCurrent.value;
6080 }
6081 }
6082 }
6083 this._hasConfig = true;
6084 this.settings = _modules_utilities__WEBPACK_IMPORTED_MODULE_5__["default"].deepclone(this.defaultSettings);
6085 }
6086 }
6087 getName() { return this._config.info.name.replace(" ", ""); }
6088 getDescription() { return this._config.info.description; }
6089 getVersion() { return this._config.info.version; }
6090 getAuthor() { return this._config.info.authors.map(a => a.name).join(", "); }
6091 load() {
6092 const currentVersionInfo = _modules_pluginutilities__WEBPACK_IMPORTED_MODULE_4__["default"].loadData(this.getName(), "currentVersionInfo", {version: this.getVersion(), hasShownChangelog: false});
6093 if (currentVersionInfo.version != this.getVersion() || !currentVersionInfo.hasShownChangelog) {
6094 this.showChangelog();
6095 _modules_pluginutilities__WEBPACK_IMPORTED_MODULE_4__["default"].saveData(this.getName(), "currentVersionInfo", {version: this.getVersion(), hasShownChangelog: true});
6096 }
6097 _modules_pluginupdater__WEBPACK_IMPORTED_MODULE_0__["default"].checkForUpdate(this.getName(), this.getVersion(), this._config.info.github_raw);
6098 }
6099 async start() {
6100 _modules_logger__WEBPACK_IMPORTED_MODULE_1__["default"].info(this.getName(), `version ${this.getVersion()} has started.`);
6101 if (this.defaultSettings) this.settings = this.loadSettings();
6102 this._enabled = true;
6103 if (typeof(this.onStart) == "function") this.onStart();
6104 }
6105 stop() {
6106 _modules_logger__WEBPACK_IMPORTED_MODULE_1__["default"].info(this.getName(), `version ${this.getVersion()} has stopped.`);
6107 this._enabled = false;
6108 if (typeof(this.onStop) == "function") this.onStop();
6109 }
6110
6111 get isEnabled() {return this._enabled;}
6112 get strings() {
6113 if (!this._config.strings) return {};
6114 const locale = _modules_discordmodules__WEBPACK_IMPORTED_MODULE_6__["default"].UserSettingsStore.locale.split("-")[0];
6115 if (this._config.strings.hasOwnProperty(locale)) return this._config.strings[locale];
6116 if (this._config.strings.hasOwnProperty("en")) return this._config.strings.en;
6117 return this._config.strings;
6118 }
6119
6120 set strings(strings) {
6121 this._config.strings = strings;
6122 }
6123
6124 showSettingsModal() {
6125 if (typeof(this.getSettingsPanel) != "function") return;
6126 _ui_modals__WEBPACK_IMPORTED_MODULE_3__["default"].showModal(this.getName() + " Settings", _modules_reacttools__WEBPACK_IMPORTED_MODULE_2__["default"].createWrappedElement(this.getSettingsPanel()), {
6127 cancelText: "",
6128 confirmText: "Done",
6129 size: _ui_modals__WEBPACK_IMPORTED_MODULE_3__["default"].ModalSizes.MEDIUM
6130 });
6131 }
6132
6133 showChangelog(footer) {
6134 if (typeof(this._config.changelog) == "undefined") return;
6135 _ui_modals__WEBPACK_IMPORTED_MODULE_3__["default"].showChangelogModal(this.getName() + " Changelog", this.getVersion(), this._config.changelog, footer);
6136 }
6137
6138 saveSettings(settings) {
6139 _modules_pluginutilities__WEBPACK_IMPORTED_MODULE_4__["default"].saveSettings(this.getName(), this.settings ? this.settings : settings);
6140 }
6141
6142 loadSettings(defaultSettings) {
6143 return _modules_pluginutilities__WEBPACK_IMPORTED_MODULE_4__["default"].loadSettings(this.getName(), this.defaultSettings ? this.defaultSettings : defaultSettings);
6144 }
6145
6146 buildSetting(data) {
6147 const {name, note, type, value, onChange, id} = data;
6148 let setting = null;
6149 if (type == "color") {
6150 setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_7__["ColorPicker"](name, note, value, onChange, {disabled: data.disabled, presetColors: data.presetColors});
6151 }
6152 else if (type == "dropdown") {
6153 setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_7__["Dropdown"](name, note, value, data.options, onChange);
6154 }
6155 else if (type == "file") {
6156 setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_7__["FilePicker"](name, note, onChange);
6157 }
6158 else if (type == "keybind") {
6159 setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_7__["Keybind"](name, note, value, onChange);
6160 }
6161 else if (type == "radio") {
6162 setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_7__["RadioGroup"](name, note, value, data.options, onChange, {disabled: data.disabled});
6163 }
6164 else if (type == "slider") {
6165 const options = {};
6166 if (typeof(data.markers) != "undefined") options.markers = data.markers;
6167 if (typeof(data.stickToMarkers) != "undefined") options.stickToMarkers = data.stickToMarkers;
6168 setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_7__["Slider"](name, note, data.min, data.max, value, onChange, options);
6169 }
6170 else if (type == "switch") {
6171 setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_7__["Switch"](name, note, value, onChange, {disabled: data.disabled});
6172 }
6173 else if (type == "textbox") {
6174 setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_7__["Textbox"](name, note, value, onChange, {placeholder: data.placeholder || ""});
6175 }
6176 if (id) setting.id = id;
6177 return setting;
6178 }
6179
6180 buildSettingsPanel() {
6181 const config = this._config.defaultConfig;
6182 const buildGroup = (group) => {
6183 const {name, id, collapsible, shown, settings} = group;
6184 // this.settings[id] = {};
6185
6186 const list = [];
6187 for (let s = 0; s < settings.length; s++) {
6188 const current = Object.assign({}, settings[s]);
6189 current.value = this.settings[id][current.id];
6190 current.onChange = (value) => {
6191 this.settings[id][current.id] = value;
6192 };
6193 if (Object.keys(this.strings).length && this.strings.settings && this.strings.settings[id] && this.strings.settings[id][current.id]) {
6194 const {name, note} = this.strings.settings[id][current.id];
6195 current.name = name;
6196 current.note = note;
6197 }
6198 list.push(this.buildSetting(current));
6199 }
6200
6201 const settingGroup = new _ui_settings__WEBPACK_IMPORTED_MODULE_7__["SettingGroup"](name, {shown, collapsible}).append(...list);
6202 settingGroup.id = id;
6203 return settingGroup;
6204 };
6205 const list = [];
6206 for (let s = 0; s < config.length; s++) {
6207 const current = Object.assign({}, config[s]);
6208 if (current.type != "category") {
6209 current.value = this.settings[current.id];
6210 current.onChange = (value) => {
6211 this.settings[current.id] = value;
6212 };
6213 if (Object.keys(this.strings).length && this.strings.settings && this.strings.settings[current.id]) {
6214 const {name, note} = this.strings.settings[current.id];
6215 current.name = name;
6216 current.note = note;
6217 }
6218 list.push(this.buildSetting(current));
6219 }
6220 else {
6221 list.push(buildGroup(current));
6222 }
6223 }
6224
6225 return new _ui_settings__WEBPACK_IMPORTED_MODULE_7__["SettingPanel"](this.saveSettings.bind(this), ...list);
6226 }
6227 };
6228});
6229
6230/***/ }),
6231
6232/***/ "./src/structs/screen.js":
6233/*!*******************************!*\
6234 !*** ./src/structs/screen.js ***!
6235 \*******************************/
6236/*! exports provided: default */
6237/***/ (function(module, __webpack_exports__, __webpack_require__) {
6238
6239"use strict";
6240__webpack_require__.r(__webpack_exports__);
6241/**
6242 * Representation of the screen such as width and height.
6243 */
6244class Screen {
6245 /** Document/window width */
6246 static get width() { return Math.max(document.documentElement.clientWidth, window.innerWidth || 0); }
6247 /** Document/window height */
6248 static get height() { return Math.max(document.documentElement.clientHeight, window.innerHeight || 0); }
6249}
6250
6251/* harmony default export */ __webpack_exports__["default"] = (Screen);
6252
6253/***/ }),
6254
6255/***/ "./src/structs/structs.js":
6256/*!********************************!*\
6257 !*** ./src/structs/structs.js ***!
6258 \********************************/
6259/*! exports provided: List, Screen, Selector, ClassName, DOMObserver, InsufficientPermissions, Plugin, Listenable, 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 */
6260/***/ (function(module, __webpack_exports__, __webpack_require__) {
6261
6262"use strict";
6263__webpack_require__.r(__webpack_exports__);
6264/* harmony import */ var _list__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./list */ "./src/structs/list.js");
6265/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "List", function() { return _list__WEBPACK_IMPORTED_MODULE_0__["default"]; });
6266
6267/* harmony import */ var _screen__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./screen */ "./src/structs/screen.js");
6268/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Screen", function() { return _screen__WEBPACK_IMPORTED_MODULE_1__["default"]; });
6269
6270/* harmony import */ var _dom_selector__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./dom/selector */ "./src/structs/dom/selector.js");
6271/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Selector", function() { return _dom_selector__WEBPACK_IMPORTED_MODULE_2__["default"]; });
6272
6273/* harmony import */ var _dom_classname__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./dom/classname */ "./src/structs/dom/classname.js");
6274/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ClassName", function() { return _dom_classname__WEBPACK_IMPORTED_MODULE_3__["default"]; });
6275
6276/* harmony import */ var _dom_observer__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./dom/observer */ "./src/structs/dom/observer.js");
6277/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "DOMObserver", function() { return _dom_observer__WEBPACK_IMPORTED_MODULE_4__["default"]; });
6278
6279/* harmony import */ var _errors_permissionserror__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./errors/permissionserror */ "./src/structs/errors/permissionserror.js");
6280/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "InsufficientPermissions", function() { return _errors_permissionserror__WEBPACK_IMPORTED_MODULE_5__["InsufficientPermissions"]; });
6281
6282/* harmony import */ var _discord_user__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./discord/user */ "./src/structs/discord/user.js");
6283/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "User", function() { return _discord_user__WEBPACK_IMPORTED_MODULE_6__["User"]; });
6284
6285/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GuildMember", function() { return _discord_user__WEBPACK_IMPORTED_MODULE_6__["GuildMember"]; });
6286
6287/* harmony import */ var _discord_guild__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./discord/guild */ "./src/structs/discord/guild.js");
6288/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Role", function() { return _discord_guild__WEBPACK_IMPORTED_MODULE_7__["Role"]; });
6289
6290/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Emoji", function() { return _discord_guild__WEBPACK_IMPORTED_MODULE_7__["Emoji"]; });
6291
6292/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Guild", function() { return _discord_guild__WEBPACK_IMPORTED_MODULE_7__["Guild"]; });
6293
6294/* harmony import */ var _discord_channel__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./discord/channel */ "./src/structs/discord/channel.js");
6295/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Channel", function() { return _discord_channel__WEBPACK_IMPORTED_MODULE_8__["Channel"]; });
6296
6297/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "PermissionOverwrite", function() { return _discord_channel__WEBPACK_IMPORTED_MODULE_8__["PermissionOverwrite"]; });
6298
6299/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "RolePermissionOverwrite", function() { return _discord_channel__WEBPACK_IMPORTED_MODULE_8__["RolePermissionOverwrite"]; });
6300
6301/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "MemberPermissionOverwrite", function() { return _discord_channel__WEBPACK_IMPORTED_MODULE_8__["MemberPermissionOverwrite"]; });
6302
6303/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GuildChannel", function() { return _discord_channel__WEBPACK_IMPORTED_MODULE_8__["GuildChannel"]; });
6304
6305/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GuildTextChannel", function() { return _discord_channel__WEBPACK_IMPORTED_MODULE_8__["GuildTextChannel"]; });
6306
6307/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GuildVoiceChannel", function() { return _discord_channel__WEBPACK_IMPORTED_MODULE_8__["GuildVoiceChannel"]; });
6308
6309/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ChannelCategory", function() { return _discord_channel__WEBPACK_IMPORTED_MODULE_8__["ChannelCategory"]; });
6310
6311/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "PrivateChannel", function() { return _discord_channel__WEBPACK_IMPORTED_MODULE_8__["PrivateChannel"]; });
6312
6313/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "DirectMessageChannel", function() { return _discord_channel__WEBPACK_IMPORTED_MODULE_8__["DirectMessageChannel"]; });
6314
6315/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GroupChannel", function() { return _discord_channel__WEBPACK_IMPORTED_MODULE_8__["GroupChannel"]; });
6316
6317/* harmony import */ var _discord_message__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./discord/message */ "./src/structs/discord/message.js");
6318/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Reaction", function() { return _discord_message__WEBPACK_IMPORTED_MODULE_9__["Reaction"]; });
6319
6320/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Embed", function() { return _discord_message__WEBPACK_IMPORTED_MODULE_9__["Embed"]; });
6321
6322/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Message", function() { return _discord_message__WEBPACK_IMPORTED_MODULE_9__["Message"]; });
6323
6324/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "DefaultMessage", function() { return _discord_message__WEBPACK_IMPORTED_MODULE_9__["DefaultMessage"]; });
6325
6326/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "RecipientAddMessage", function() { return _discord_message__WEBPACK_IMPORTED_MODULE_9__["RecipientAddMessage"]; });
6327
6328/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "RecipientRemoveMessage", function() { return _discord_message__WEBPACK_IMPORTED_MODULE_9__["RecipientRemoveMessage"]; });
6329
6330/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "CallMessage", function() { return _discord_message__WEBPACK_IMPORTED_MODULE_9__["CallMessage"]; });
6331
6332/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GroupChannelNameChangeMessage", function() { return _discord_message__WEBPACK_IMPORTED_MODULE_9__["GroupChannelNameChangeMessage"]; });
6333
6334/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GroupChannelIconChangeMessage", function() { return _discord_message__WEBPACK_IMPORTED_MODULE_9__["GroupChannelIconChangeMessage"]; });
6335
6336/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "MessagePinnedMessage", function() { return _discord_message__WEBPACK_IMPORTED_MODULE_9__["MessagePinnedMessage"]; });
6337
6338/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GuildMemberJoinMessage", function() { return _discord_message__WEBPACK_IMPORTED_MODULE_9__["GuildMemberJoinMessage"]; });
6339
6340/* harmony import */ var _discord_usersettings__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./discord/usersettings */ "./src/structs/discord/usersettings.js");
6341/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "UserSettings", function() { return _discord_usersettings__WEBPACK_IMPORTED_MODULE_10__["UserSettings"]; });
6342
6343/* harmony import */ var _plugin__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./plugin */ "./src/structs/plugin.js");
6344/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Plugin", function() { return _plugin__WEBPACK_IMPORTED_MODULE_11__["default"]; });
6345
6346/* harmony import */ var _listenable__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./listenable */ "./src/structs/listenable.js");
6347/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Listenable", function() { return _listenable__WEBPACK_IMPORTED_MODULE_12__["default"]; });
6348
6349
6350
6351
6352
6353
6354
6355
6356
6357
6358
6359
6360
6361
6362
6363
6364
6365
6366
6367
6368/***/ }),
6369
6370/***/ "./src/styles/settings.css":
6371/*!*********************************!*\
6372 !*** ./src/styles/settings.css ***!
6373 \*********************************/
6374/*! no static exports found */
6375/***/ (function(module, exports) {
6376
6377module.exports = ".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\tcolor: white;\r\n\tbackground: #7289DA;\r\n\toutline: 0;\r\n\tborder: 0;\r\n\tpadding: 10px;\r\n\tvertical-align: top;\r\n\tmargin-top: -10px;\r\n\tmargin-left: -10px;\r\n\tborder-radius: 3px 0 0 3px;\r\n\tfont-size: 14px;\r\n font-weight: 500;\r\n\tfont-family: Whitney,Helvetica Neue,Helvetica,Arial,sans-serif;\r\n\tcursor: pointer;\r\n}\r\n"
6378
6379/***/ }),
6380
6381/***/ "./src/styles/toasts.css":
6382/*!*******************************!*\
6383 !*** ./src/styles/toasts.css ***!
6384 \*******************************/
6385/*! no static exports found */
6386/***/ (function(module, exports) {
6387
6388module.exports = ".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}"
6389
6390/***/ }),
6391
6392/***/ "./src/styles/updates.css":
6393/*!********************************!*\
6394 !*** ./src/styles/updates.css ***!
6395 \********************************/
6396/*! no static exports found */
6397/***/ (function(module, exports) {
6398
6399module.exports = "#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}"
6400
6401/***/ }),
6402
6403/***/ "./src/ui/contextmenu.js":
6404/*!*******************************!*\
6405 !*** ./src/ui/contextmenu.js ***!
6406 \*******************************/
6407/*! exports provided: updateDiscordMenu, Menu, ItemGroup, MenuItem, TextItem, ImageItem, SubMenuItem, ToggleItem */
6408/***/ (function(module, __webpack_exports__, __webpack_require__) {
6409
6410"use strict";
6411__webpack_require__.r(__webpack_exports__);
6412/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "updateDiscordMenu", function() { return updateDiscordMenu; });
6413/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Menu", function() { return Menu; });
6414/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ItemGroup", function() { return ItemGroup; });
6415/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MenuItem", function() { return MenuItem; });
6416/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TextItem", function() { return TextItem; });
6417/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ImageItem", function() { return ImageItem; });
6418/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SubMenuItem", function() { return SubMenuItem; });
6419/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ToggleItem", function() { return ToggleItem; });
6420/* harmony import */ var _modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../modules/discordclasses */ "./src/modules/discordclasses.js");
6421/* harmony import */ var _modules_discordselectors__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../modules/discordselectors */ "./src/modules/discordselectors.js");
6422/* harmony import */ var _modules_reacttools__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../modules/reacttools */ "./src/modules/reacttools.js");
6423/* harmony import */ var _modules_discordmodules__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../modules/discordmodules */ "./src/modules/discordmodules.js");
6424/* harmony import */ var _modules_domtools__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../modules/domtools */ "./src/modules/domtools.js");
6425/* harmony import */ var _structs_screen__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../structs/screen */ "./src/structs/screen.js");
6426/**
6427 * Self-made context menus that emulate Discord's own context menus.
6428 * @module ContextMenu
6429 * @version 0.1.0
6430 */
6431
6432
6433
6434
6435
6436
6437
6438
6439/**
6440 * Updates the location of a Discord menu, especially useful when adding items to the menu via DOM.
6441 * @param {HTMLElement|jQuery} menu - The original discord menu
6442 */
6443function updateDiscordMenu(menu) {
6444 if (!(menu instanceof window.jQuery) && !(menu instanceof Element)) return;
6445 const updateHeight = _modules_reacttools__WEBPACK_IMPORTED_MODULE_2__["default"].getReactProperty(menu, "return.stateNode.props.onHeightUpdate");
6446 if (updateHeight) updateHeight();
6447}
6448
6449/** Main menu class for creating custom context menus. */
6450class Menu {
6451 /**
6452 *
6453 * @param {boolean} [scroll=false] - should this menu be a scrolling menu (usually only used for submenus)
6454 */
6455 constructor(scroll = false) {
6456 this.theme = _modules_discordmodules__WEBPACK_IMPORTED_MODULE_3__["default"].UserSettingsStore.theme == "dark" ? "theme-dark" : "theme-light";
6457 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>`);
6458 this.scroll = scroll;
6459 if (!scroll) return;
6460 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>`);
6461 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>`);
6462 this.scrollerWrap.append(this.scroller);
6463 this.element.append(this.scrollerWrap);
6464 }
6465
6466 /**
6467 * Adds an item group to the menu. The group should already be populated.
6468 * @param {module:ContextMenu.ItemGroup} contextGroup - group to add to the menu
6469 * @returns {module:ContextMenu.Menu} returns self for chaining
6470 */
6471 addGroup(contextGroup) {
6472 if (this.scroll) this.scroller.append(contextGroup.getElement());
6473 else this.element.append(contextGroup.getElement());
6474 return this;
6475 }
6476
6477 /**
6478 * Adds items to the context menu directly. It is recommended to add to a group and use
6479 * {@link module:ContextMenu.Menu.addGroup} instead to behave as natively as possible.
6480 * @param {module:ContextMenu.MenuItem} contextItems - list of items to add to the context menu
6481 * @returns {module:ContextMenu.Menu} returns self for chaining
6482 */
6483 addItems(...contextItems) {
6484 for (let i = 0; i < contextItems.length; i++) {
6485 if (this.scroll) this.scroller.append(contextItems[i].getElement());
6486 else this.element.append(contextItems[i].getElement());
6487 }
6488 return this;
6489 }
6490
6491 /**
6492 * Shows the menu at a specific x and y position. This generally comes from the
6493 * pointer position on a right click event.
6494 * @param {number} x - x coordinate for the menu to show at
6495 * @param {number} y - y coordinate for the menu to show at
6496 */
6497 show(x, y) {
6498 const mouseX = x;
6499 const mouseY = y;
6500
6501 const parents = this.element.parents(this.parentSelector);
6502 const depth = parents.length;
6503 if (depth == 0) this.element.appendTo("#app-mount");
6504 this.element.css("top", mouseY + "px").css("left", mouseX + "px");
6505
6506 if (depth > 0) {
6507 const top = parents[parents.length - 1];
6508 const closest = parents[0];
6509 const negate = closest.hasClass(_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.invertChildX) ? -1 : 1;
6510 const value = negate * closest.find(_modules_discordselectors__WEBPACK_IMPORTED_MODULE_1__["default"].ContextMenu.item).outerWidth() + closest.offset().left - top.offset().left;
6511 this.element.css("margin-left", `${value}px`);
6512 }
6513
6514 if (mouseY + this.element.outerHeight() >= _structs_screen__WEBPACK_IMPORTED_MODULE_5__["default"].height) {
6515 this.element.addClass("invertY").addClass(_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.invertY);
6516 this.element.css("top", `${mouseY - this.element.outerHeight()}px`);
6517 if (depth > 0) this.element.css("top", `${(mouseY + this.element.parent().outerHeight()) - this.element.outerHeight()}px`);
6518 }
6519 if (this.element.offset().left + this.element.outerWidth() >= _structs_screen__WEBPACK_IMPORTED_MODULE_5__["default"].width) {
6520 this.element.addClass("invertX");
6521 this.element.css("left", `${mouseX - this.element.outerWidth()}px`);
6522 }
6523 if (this.element.offset().left + 2 * this.element.outerWidth() >= _structs_screen__WEBPACK_IMPORTED_MODULE_5__["default"].width) {
6524 this.element.addClass(_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.invertChildX);
6525 }
6526
6527 if (depth !== 0) return;
6528 _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(); });
6529 _modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].on(document, "click.zctx", (e) => { if (this.element.contains(e.target)) this.removeMenu(); });
6530 _modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].on(document, "keyup.zctx", (e) => { if (e.keyCode === 27) this.removeMenu(); });
6531 }
6532
6533 /** Allows you to remove the menu. */
6534 removeMenu() {
6535 this.element.remove();
6536 const childs = this.element.findAll(this.parentSelector);
6537 if (childs) childs.forEach(c => c.remove());
6538 _modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].off(document, ".zctx");
6539 }
6540
6541 /**
6542 * Used to attach a menu to a menu item. This is how to create a submenu.
6543 * If using {@link module:ContextMenu.SubMenuItem} then you do not need
6544 * to call this function as it is done automatically. If you want to attach
6545 * a submenu to an existing Discord context menu, then you should use this
6546 * method.
6547 * @param {(HTMLElement|jQuery)} menuItem - item to attach to
6548 */
6549 attachTo(menuItem) {
6550 this.menuItem = $(menuItem);
6551 menuItem.on("mouseenter", () => {
6552 this.element.appendTo(menuItem);
6553 const left = this.element.parents(this.parentSelector)[0].css("left");
6554 this.show(parseInt(left.replace("px", "")), menuItem.offset().top);
6555 });
6556 menuItem.on("mouseleave", () => { this.element.remove(); });
6557 }
6558
6559 get parentSelector() {return this.element.parents(".plugin-context-menu").length > this.element.parents(_modules_discordselectors__WEBPACK_IMPORTED_MODULE_1__["default"].ContextMenu.contextMenu).length ? ".plugin-context-menu" : _modules_discordselectors__WEBPACK_IMPORTED_MODULE_1__["default"].ContextMenu.contextMenu;}
6560}
6561
6562/** Class that represents a group of menu items. */
6563class ItemGroup {
6564 /** Creates an item group. */
6565 constructor() {
6566 this.element = _modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement(`<div class="${_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.itemGroup}"></div>`);
6567 }
6568
6569 /**
6570 * This is the method of adding menu items to a menu group.
6571 * @param {module:ContextMenu.MenuItem} contextItems - list of context menu items to add to this group
6572 * @returns {module:ContextMenu.ItemGroup} returns self for chaining
6573 */
6574 addItems(...contextItems) {
6575 for (let i = 0; i < contextItems.length; i++) {
6576 this.element.append(contextItems[i].getElement());
6577 }
6578 return this;
6579 }
6580
6581 /** @returns {HTMLElement} returns the DOM node for the group */
6582 getElement() { return this.element; }
6583}
6584
6585/**
6586 * Fires when the attached menu item it clicked.
6587 * @param {MouseEvent} event - the mouse event from clicking the item
6588 * @callback module:ContextMenu~clickEvent
6589 */
6590
6591 /**
6592 * Fires when the checkbox item changes state.
6593 * @param {boolean} isChecked - if the checkbox is now checked
6594 * @callback module:ContextMenu~onChange
6595 */
6596
6597/** Base class for all other menu items. */
6598class MenuItem {
6599 /**
6600 * @param {string} label - label to show on the menu item
6601 * @param {object} options - additional options for the item
6602 * @param {boolean} [options.danger=false] - should the item show as danger
6603 * @param {module:ContextMenu~clickEvent} [options.callback] - callback for when it is clicked
6604 */
6605 constructor(label, options = {}) {
6606 const {danger = false, callback} = options;
6607 this.element = _modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement(`<div class="${_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.item}"></div>`);
6608 this.label = label;
6609 if (danger) this.element.addClass(_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.danger);
6610 this.element.on("click", (event) => {
6611 if (!Array.from(this.element.children).some(c => c.isSameNode(event.target)) && !this.element.isSameNode(event.target)) return;
6612 if (typeof(callback) == "function") callback(event);
6613 else event.stopPropagation();
6614 });
6615 }
6616 getElement() { return this.element;}
6617}
6618
6619/**
6620 * Creates a text menu item that can have a hint.
6621 * @extends module:ContextMenu.MenuItem
6622 */
6623class TextItem extends MenuItem {
6624 /**
6625 * @param {string} label - label to show on the menu item
6626 * @param {object} options - additional options for the item
6627 * @param {string} [options.hint=""] - hint to show on the item (usually used for key combos)
6628 * @param {boolean} [options.danger=false] - should the item show as danger
6629 * @param {module:ContextMenu~clickEvent} [options.callback] - callback for when it is clicked
6630 */
6631 constructor(label, options = {}) {
6632 super(label, options);
6633 const {hint = ""} = options;
6634 this.element.append(_modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement(`<span>${label}</span>`));
6635 this.element.append(_modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement(`<div class="${_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.hint}">${hint}</div>`));
6636 }
6637}
6638
6639/**
6640 * Creates an image menu item that can have an image.
6641 * @extends module:ContextMenu.MenuItem
6642 */
6643class ImageItem extends MenuItem {
6644 /**
6645 * @param {string} label - label to show on the menu item
6646 * @param {string} imageSrc - link to the image to embed
6647 * @param {object} options - additional options for the item
6648 * @param {string} [options.hint=""] - hint to show on the item (usually used for key combos)
6649 * @param {boolean} [options.danger=false] - should the item show as danger
6650 * @param {module:ContextMenu~clickEvent} [options.callback] - callback for when it is clicked
6651 */
6652 constructor(label, imageSrc, options = {}) {
6653 super(label, options);
6654 this.element.addClass(_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.itemImage);
6655 this.element.append(_modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement(`<div class="${_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.label}">${label}</div>`));
6656 this.element.append(_modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement(`<img src="${imageSrc}">`));
6657 }
6658}
6659
6660/**
6661 * Creates a menu item with an attached submenu.
6662 * @extends module:ContextMenu.MenuItem
6663 */
6664class SubMenuItem extends MenuItem {
6665 /**
6666 * @param {string} label - label to show on the menu item
6667 * @param {module:ContextMenu.Menu} subMenu - context menu that should be attached to this item
6668 * @param {object} options - additional options for the item
6669 * @param {string} [options.hint=""] - hint to show on the item (usually used for key combos)
6670 * @param {boolean} [options.danger=false] - should the item show as danger
6671 * @param {module:ContextMenu~clickEvent} [options.callback] - callback for when it is clicked
6672 */
6673 constructor(label, subMenu, options = {}) {
6674 // if (!(subMenu instanceof ContextSubMenu)) throw "subMenu must be of ContextSubMenu type.";
6675 super(label, options);
6676 this.element.addClass(_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.itemSubMenu).text(label);
6677 this.subMenu = subMenu;
6678 this.subMenu.attachTo(this.getElement());
6679 }
6680}
6681
6682/**
6683 * Creates a menu item with a checkbox.
6684 * @extends module:ContextMenu.MenuItem
6685 */
6686class ToggleItem extends MenuItem {
6687 /**
6688 * @param {string} label - label to show on the menu item
6689 * @param {boolean} checked - should the item start out checked
6690 * @param {object} options - additional options for the item
6691 * @param {string} [options.hint=""] - hint to show on the item (usually used for key combos)
6692 * @param {boolean} [options.danger=false] - should the item show as danger
6693 * @param {module:ContextMenu~onChange} [options.callback] - callback for when the checkbox changes
6694 */
6695 constructor(label, checked, options = {}) {
6696 const {callback: onChange} = options;
6697 if (options.callback) delete options.callback;
6698 super(label, options);
6699 this.element.addClass(_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.itemToggle);
6700 this.element.append(_modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement(`<div class="${_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].ContextMenu.label}">${label}</div>`));
6701 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>`);
6702 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>`));
6703 this.checkbox.append(_modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement("<span>"));
6704 this.input = _modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement(`<input type="checkbox" class="${_modules_discordclasses__WEBPACK_IMPORTED_MODULE_0__["default"].Checkbox.checkboxElement}">`);
6705 this.input.checked = checked;
6706 this.checkbox.find(".checkbox-inner").append(this.input);
6707 this.checkbox.find(".checkbox-inner").append(_modules_domtools__WEBPACK_IMPORTED_MODULE_4__["default"].createElement("<span>"));
6708 this.element.append(this.checkbox);
6709 this.element.on("click", (e) => {
6710 if (!Array.from(this.element.children).some(c => c.isSameNode(e.target)) && !this.element.isSameNode(e.target)) return;
6711 e.stopPropagation();
6712 this.input.checked = !this.input.checked;
6713 if (typeof(onChange) == "function") onChange(this.input.checked);
6714 });
6715 }
6716}
6717
6718/***/ }),
6719
6720/***/ "./src/ui/emulatedtooltip.js":
6721/*!***********************************!*\
6722 !*** ./src/ui/emulatedtooltip.js ***!
6723 \***********************************/
6724/*! exports provided: default */
6725/***/ (function(module, __webpack_exports__, __webpack_require__) {
6726
6727"use strict";
6728__webpack_require__.r(__webpack_exports__);
6729/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return EmulatedTooltip; });
6730/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
6731/* harmony import */ var _structs_screen__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../structs/screen */ "./src/structs/screen.js");
6732/**
6733 * Tooltip that automatically show and hide themselves on mouseenter and mouseleave events.
6734 * Will also remove themselves if the node to watch is removed from DOM through
6735 * a MutationObserver.
6736 *
6737 * Note this is not using Discord's internals but normal DOM manipulation and emulates
6738 * Discord's own tooltips as closely as possible.
6739 *
6740 * @module EmulatedTooltip
6741 * @version 0.0.1
6742 */
6743
6744
6745
6746
6747const getClass = function(sideOrColor) {
6748 const upperCase = sideOrColor[0].toUpperCase() + sideOrColor.slice(1);
6749 const tooltipClass = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordClasses"].Tooltips[`tooltip${upperCase}`];
6750 if (tooltipClass) return tooltipClass;
6751 return null;
6752};
6753
6754const classExists = function(sideOrColor) {
6755 return getClass(sideOrColor) ? true : false;
6756};
6757
6758const toPx = function(value) {
6759 return `${value}px`;
6760};
6761
6762/* <div class="layer-v9HyYc da-layer" style="left: 234.5px; bottom: 51px;">
6763 <div class="tooltip-2QfLtc da-tooltip tooltipTop-XDDSxx tooltipBlack-PPG47z">
6764 <div class="tooltipPointer-3ZfirK da-tooltipPointer"></div>
6765 User Settings
6766 </div>
6767</div> */
6768
6769class EmulatedTooltip {
6770 /**
6771 *
6772 * @constructor
6773 * @param {(HTMLElement|jQuery)} node - DOM node to monitor and show the tooltip on
6774 * @param {string} tip - string to show in the tooltip
6775 * @param {object} options - additional options for the tooltip
6776 * @param {string} [options.style=black] - correlates to the discord styling/colors (black, brand, green, grey, red, yellow)
6777 * @param {string} [options.side=top] - can be any of top, right, bottom, left
6778 * @param {boolean} [options.preventFlip=false] - prevents moving the tooltip to the opposite side if it is too big or goes offscreen
6779 * @param {boolean} [options.disabled=false] - whether the tooltip should be disabled from showing on hover
6780 */
6781 constructor(node, text, options = {}) {
6782 const {style = "black", side = "top", preventFlip = false, disabled = false} = options;
6783 this.node = node instanceof jQuery ? node[0] : node;
6784 this.label = text;
6785 this.style = style.toLowerCase();
6786 this.side = side.toLowerCase();
6787 this.preventFlip = preventFlip;
6788 this.disabled = disabled;
6789
6790 if (!classExists(this.side)) return modules__WEBPACK_IMPORTED_MODULE_0__["Logger"].err("EmulatedTooltip", `Side ${this.side} does not exist.`);
6791 if (!classExists(this.style)) return modules__WEBPACK_IMPORTED_MODULE_0__["Logger"].err("EmulatedTooltip", `Style ${this.style} does not exist.`);
6792
6793 this.element = modules__WEBPACK_IMPORTED_MODULE_0__["DOMTools"].createElement(`<div class="${modules__WEBPACK_IMPORTED_MODULE_0__["DiscordClasses"].TooltipLayers.layer}">`);
6794 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>${this.label}</div>`);
6795 this.labelElement = this.tooltipElement.childNodes[1];
6796 this.element.append(this.tooltipElement);
6797
6798
6799 this.node.addEventListener("mouseenter", () => {
6800 if (this.disabled) return;
6801 this.show();
6802
6803 const observer = new MutationObserver((mutations) => {
6804 mutations.forEach((mutation) => {
6805 const nodes = Array.from(mutation.removedNodes);
6806 const directMatch = nodes.indexOf(this.node) > -1;
6807 const parentMatch = nodes.some(parent => parent.contains(this.node));
6808 if (directMatch || parentMatch) {
6809 this.hide();
6810 observer.disconnect();
6811 }
6812 });
6813 });
6814
6815 observer.observe(document.body, {subtree: true, childList: true});
6816 });
6817
6818 this.node.addEventListener("mouseleave", () => {
6819 this.hide();
6820 });
6821 }
6822
6823 /** Container where the tooltip will be appended. */
6824 get container() { return document.querySelector(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordSelectors"].TooltipLayers.layerContainer); }
6825 /** Boolean representing if the tooltip will fit on screen above the element */
6826 get canShowAbove() { return this.node.offset().top - this.element.outerHeight() >= 0; }
6827 /** Boolean representing if the tooltip will fit on screen below the element */
6828 get canShowBelow() { return this.node.offset().top + this.node.outerHeight() + this.element.outerHeight() <= _structs_screen__WEBPACK_IMPORTED_MODULE_1__["default"].height; }
6829 /** Boolean representing if the tooltip will fit on screen to the left of the element */
6830 get canShowLeft() { return this.node.offset().left - this.element.outerWidth() >= 0; }
6831 /** Boolean representing if the tooltip will fit on screen to the right of the element */
6832 get canShowRight() { return this.node.offset().left + this.node.outerWidth() + this.element.outerWidth() <= _structs_screen__WEBPACK_IMPORTED_MODULE_1__["default"].width; }
6833
6834 /** Hides the tooltip. Automatically called on mouseleave. */
6835 hide() {
6836 this.element.remove();
6837 this.tooltipElement.className = this._className;
6838 }
6839
6840 /** Shows the tooltip. Automatically called on mouseenter. Will attempt to flip if position was wrong. */
6841 show() {
6842 this.tooltipElement.className = `${modules__WEBPACK_IMPORTED_MODULE_0__["DiscordClasses"].Tooltips.tooltip} ${getClass(this.style)}`;
6843 this.labelElement.textContent = this.label;
6844 this.element.appendTo(this.container);
6845
6846 if (this.side == "top") {
6847 if (this.canShowAbove || (!this.canShowAbove && this.preventFlip)) this.showAbove();
6848 else this.showBelow();
6849 }
6850
6851 if (this.side == "bottom") {
6852 if (this.canShowBelow || (!this.canShowBelow && this.preventFlip)) this.showBelow();
6853 else this.showAbove();
6854 }
6855
6856 if (this.side == "left") {
6857 if (this.canShowLeft || (!this.canShowLeft && this.preventFlip)) this.showLeft();
6858 else this.showRight();
6859 }
6860
6861 if (this.side == "right") {
6862 if (this.canShowRight || (!this.canShowRight && this.preventFlip)) this.showRight();
6863 else this.showLeft();
6864 }
6865 }
6866
6867 /** Force showing the tooltip above the node. */
6868 showAbove() {
6869 this.tooltipElement.addClass(getClass("top"));
6870 this.element.css("top", toPx(this.node.offset().top - this.element.outerHeight() - 10));
6871 this.centerHorizontally();
6872 }
6873
6874 /** Force showing the tooltip below the node. */
6875 showBelow() {
6876 this.tooltipElement.addClass(getClass("bottom"));
6877 this.element.css("top", toPx(this.node.offset().top + this.node.outerHeight() + 10));
6878 this.centerHorizontally();
6879 }
6880
6881 /** Force showing the tooltip to the left of the node. */
6882 showLeft() {
6883 this.tooltipElement.addClass(getClass("left"));
6884 this.element.css("left", toPx(this.node.offset().left - this.element.outerWidth() - 10));
6885 this.centerVertically();
6886 }
6887
6888 /** Force showing the tooltip to the right of the node. */
6889 showRight() {
6890 this.tooltipElement.addClass(getClass("right"));
6891 this.element.css("left", toPx(this.node.offset().left + this.node.outerWidth() + 10));
6892 this.centerVertically();
6893 }
6894
6895 centerHorizontally() {
6896 const nodecenter = this.node.offset().left + (this.node.outerWidth() / 2);
6897 this.element.css("left", toPx(nodecenter - (this.element.outerWidth() / 2)));
6898 }
6899
6900 centerVertically() {
6901 const nodecenter = this.node.offset().top + (this.node.outerHeight() / 2);
6902 this.element.css("top", toPx(nodecenter - (this.element.outerHeight() / 2)));
6903 }
6904}
6905
6906/***/ }),
6907
6908/***/ "./src/ui/icons.js":
6909/*!*************************!*\
6910 !*** ./src/ui/icons.js ***!
6911 \*************************/
6912/*! exports provided: IconError, IconInfo, IconSuccess, IconWarning */
6913/***/ (function(module, __webpack_exports__, __webpack_require__) {
6914
6915"use strict";
6916__webpack_require__.r(__webpack_exports__);
6917/* harmony import */ var _icons_error__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./icons/error */ "./src/ui/icons/error.js");
6918/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "IconError", function() { return _icons_error__WEBPACK_IMPORTED_MODULE_0__["default"]; });
6919
6920/* harmony import */ var _icons_info__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./icons/info */ "./src/ui/icons/info.js");
6921/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "IconInfo", function() { return _icons_info__WEBPACK_IMPORTED_MODULE_1__["default"]; });
6922
6923/* harmony import */ var _icons_success__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./icons/success */ "./src/ui/icons/success.js");
6924/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "IconSuccess", function() { return _icons_success__WEBPACK_IMPORTED_MODULE_2__["default"]; });
6925
6926/* harmony import */ var _icons_warning__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./icons/warning */ "./src/ui/icons/warning.js");
6927/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "IconWarning", function() { return _icons_warning__WEBPACK_IMPORTED_MODULE_3__["default"]; });
6928
6929
6930
6931
6932
6933
6934/***/ }),
6935
6936/***/ "./src/ui/icons/error.js":
6937/*!*******************************!*\
6938 !*** ./src/ui/icons/error.js ***!
6939 \*******************************/
6940/*! exports provided: default */
6941/***/ (function(module, __webpack_exports__, __webpack_require__) {
6942
6943"use strict";
6944__webpack_require__.r(__webpack_exports__);
6945/**
6946 * Error Icon
6947 * @param {number} size - Size of the icon.
6948 */
6949/* harmony default export */ __webpack_exports__["default"] = (function(size) {
6950 return `<svg width="${size || 24}" height="${size || 24}" viewBox="0 0 24 24">
6951 <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" />
6952 </svg>`;
6953});
6954
6955/***/ }),
6956
6957/***/ "./src/ui/icons/info.js":
6958/*!******************************!*\
6959 !*** ./src/ui/icons/info.js ***!
6960 \******************************/
6961/*! exports provided: default */
6962/***/ (function(module, __webpack_exports__, __webpack_require__) {
6963
6964"use strict";
6965__webpack_require__.r(__webpack_exports__);
6966/**
6967 * Info Icon
6968 * @param {number} size - Size of the icon.
6969 */
6970/* harmony default export */ __webpack_exports__["default"] = (function(size) {
6971 return `<svg width="${size || 24}" height="${size || 24}" viewBox="0 0 24 24">
6972 <path d="M0 0h24v24H0z" fill="none"/>
6973 <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"/>
6974 </svg>`;
6975});
6976
6977/***/ }),
6978
6979/***/ "./src/ui/icons/success.js":
6980/*!*********************************!*\
6981 !*** ./src/ui/icons/success.js ***!
6982 \*********************************/
6983/*! exports provided: default */
6984/***/ (function(module, __webpack_exports__, __webpack_require__) {
6985
6986"use strict";
6987__webpack_require__.r(__webpack_exports__);
6988/**
6989 * Success Icon
6990 * @param {number} size - Size of the icon.
6991 */
6992/* harmony default export */ __webpack_exports__["default"] = (function(size) {
6993 return `<svg width="${size || 24}" height="${size || 24}" viewBox="0 0 24 24">
6994 <path d="M0 0h24v24H0z" fill="none"/>
6995 <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"/>
6996 </svg>`;
6997});
6998
6999/***/ }),
7000
7001/***/ "./src/ui/icons/warning.js":
7002/*!*********************************!*\
7003 !*** ./src/ui/icons/warning.js ***!
7004 \*********************************/
7005/*! exports provided: default */
7006/***/ (function(module, __webpack_exports__, __webpack_require__) {
7007
7008"use strict";
7009__webpack_require__.r(__webpack_exports__);
7010/**
7011 * Warning Icon
7012 * @param {number} size - Size of the icon.
7013 */
7014/* harmony default export */ __webpack_exports__["default"] = (function(size) {
7015 return `<svg width="${size || 24}" height="${size || 24}" viewBox="0 0 24 24">
7016 <path d="M0 0h24v24H0z" fill="none"/>
7017 <path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/>
7018 </svg>`;
7019});
7020
7021/***/ }),
7022
7023/***/ "./src/ui/modals.js":
7024/*!**************************!*\
7025 !*** ./src/ui/modals.js ***!
7026 \**************************/
7027/*! exports provided: default */
7028/***/ (function(module, __webpack_exports__, __webpack_require__) {
7029
7030"use strict";
7031__webpack_require__.r(__webpack_exports__);
7032/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return Modals; });
7033/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
7034/**
7035 * Allows an easy way to create and show modals.
7036 * @module Modals
7037 * @version 0.0.1
7038 */
7039
7040
7041
7042class Modals {
7043
7044 /** Sizes of modals. */
7045 static get ModalSizes() {return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].ConfirmationModal.Sizes;}
7046
7047 /**
7048 * Shows the user profile modal for a given user.
7049 * @param {string} userId - id of the user to show profile for
7050 */
7051 static showUserProfile(userId) {
7052 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].UserProfileModal.open(userId);
7053 }
7054
7055 /**
7056 * Acts as a wrapper for {@link module:Modals.showModal} where the `children` is a text element.
7057 * @param {string} title - title of the modal
7058 * @param {string} content - text to show inside the modal
7059 * @param {object} [options] - see {@link module:Modals.showModal}
7060 * @see module:Modals.showModal
7061 */
7062 static showConfirmationModal(title, content, options = {}) {
7063 this.showModal(title, modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].TextElement.default({color: modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].TextElement.Colors.PRIMARY, children: [content]}), options);
7064 }
7065
7066 /**
7067 * Shows a very simple alert modal that has title, content and an okay button.
7068 * @param {string} title - title of the modal
7069 * @param {string} body - text to show inside the modal
7070 */
7071 static showAlertModal(title, body) {
7072 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].ModalStack.push(function(props) {
7073 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].React.createElement(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].AlertModal, Object.assign({
7074 title: title,
7075 body: body,
7076 }, props));
7077 });
7078 }
7079
7080 /**
7081 * Shows a generic but very customizable modal.
7082 * @param {string} title - title of the modal
7083 * @param {(ReactElement|Array<ReactElement>)} children - a single or array of rendered react elements to act as children
7084 * @param {object} [options] - options to modify the modal
7085 * @param {boolean} [options.danger=false] - whether the main button should be red or not
7086 * @param {string} [options.confirmText=Okay] - text for the confirmation/submit button
7087 * @param {string} [options.cancelText=Cancel] - text for the cancel button
7088 * @param {callable} [options.onConfirm=NOOP] - callback to occur when clicking the submit button
7089 * @param {callable} [options.onCancel=NOOP] - callback to occur when clicking the cancel button
7090 * @param {module:Modals.ModalSizes} [options.size=module:Modals.ModalSizes.SMALL] - overall size of the modal
7091 */
7092 static showModal(title, children, options = {}) {
7093 const {danger = false, confirmText = "Okay", cancelText = "Cancel", onConfirm = () => {}, onCancel = () => {}, size = this.ModalSizes.SMALL} = options;
7094 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].ModalStack.push(function(props) {
7095 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].React.createElement(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].ConfirmationModal, Object.assign({
7096 header: title,
7097 red: danger,
7098 size: size,
7099 confirmText: confirmText,
7100 cancelText: cancelText,
7101 onConfirm: onConfirm,
7102 onCancel: onCancel,
7103 children: Array.isArray(children) ? children : [children]
7104 }, props));
7105 });
7106 }
7107
7108 /**
7109 * @interface
7110 * @name module:Modals~Changelog
7111 * @property {string} title - title of the changelog section
7112 * @property {string} [type=added] - type information of the section. Options: added, improved, fixed, progress.
7113 * @property {(Array<HTMLElement>|Array<string>)} items - itemized list of items to show in that section. Can be elements, strings, domstrings, or a mix of those.
7114 */
7115
7116 /**
7117 * Shows a changelog modal based on changelog data.
7118 * @param {string} title - title of the modal
7119 * @param {string} version - subtitle (usually version or date) of the modal
7120 * @param {module:Modals~Changelog} changelog - changelog to show inside the modal
7121 * @param {(HTMLElement|string)} footer - either an html element or text to show in the footer of the modal
7122 */
7123 static showChangelogModal(title, version, changelog, footer) {
7124 const changelogItems = [];
7125 for (let c = 0; c < changelog.length; c++) {
7126 const entry = changelog[c];
7127 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;
7128 const margin = c == 0 ? modules__WEBPACK_IMPORTED_MODULE_0__["DiscordClasses"].Changelog.marginTop : "";
7129 changelogItems.push(modules__WEBPACK_IMPORTED_MODULE_0__["DOMTools"].parseHTML(`<h1 class="${type} ${margin}">${entry.title}</h1>`));
7130 const list = modules__WEBPACK_IMPORTED_MODULE_0__["DOMTools"].parseHTML(`<ul></ul>`);
7131 for (let i = 0; i < entry.items.length; i++) {
7132 const listElem = modules__WEBPACK_IMPORTED_MODULE_0__["DOMTools"].parseHTML(`<li></li>`);
7133 if (entry.items[i] instanceof Element) listElem.append(entry.items[i]);
7134 else listElem.append(modules__WEBPACK_IMPORTED_MODULE_0__["DOMTools"].parseHTML(entry.items[i]));
7135 list.append(listElem);
7136 }
7137 changelogItems.push(list);
7138 }
7139 const renderHeader = function() {
7140 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].React.createElement(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].FlexChild.Child,
7141 {grow: 1, shrink: 1},
7142 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].React.createElement(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].Titles.default, {tag: modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].Titles.Tags.H4}, title),
7143 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].React.createElement(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].TextElement.default,
7144 {size: modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].TextElement.Sizes.SMALL, color: modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].TextElement.Colors.PRIMARY, className: modules__WEBPACK_IMPORTED_MODULE_0__["DiscordClasses"].Changelog.date.toString()},
7145 "Version " + version
7146 )
7147 );
7148 };
7149 const renderFooter = footer ? function() {
7150 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].React.createElement(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].TextElement.default,
7151 {size: modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].TextElement.Sizes.SMALL, color: modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].TextElement.Colors.PRIMARY},
7152 modules__WEBPACK_IMPORTED_MODULE_0__["ReactTools"].wrapElement(changelogItems)
7153 );
7154 } : null;
7155 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].ModalStack.push(function(props) {
7156 return modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].React.createElement(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].Changelog, Object.assign({
7157 className: modules__WEBPACK_IMPORTED_MODULE_0__["DiscordClasses"].Changelog.container.toString(),
7158 selectable: true,
7159 onScroll: _ => _,
7160 onClose: _ => _,
7161 renderHeader: renderHeader,
7162 renderFooter: renderFooter,
7163 children: [modules__WEBPACK_IMPORTED_MODULE_0__["ReactTools"].createWrappedElement(changelogItems)]
7164 }, props));
7165 });
7166 }
7167}
7168
7169/***/ }),
7170
7171/***/ "./src/ui/popouts.js":
7172/*!***************************!*\
7173 !*** ./src/ui/popouts.js ***!
7174 \***************************/
7175/*! exports provided: default */
7176/***/ (function(module, __webpack_exports__, __webpack_require__) {
7177
7178"use strict";
7179__webpack_require__.r(__webpack_exports__);
7180/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return Popouts; });
7181/* harmony import */ var structs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! structs */ "./src/structs/structs.js");
7182/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
7183/**
7184 * Allows an easy way to create and show popouts.
7185 * @module Popouts
7186 * @version 0.0.1
7187 */
7188
7189
7190
7191
7192class Popouts {
7193 /**
7194 * Shows the user popout for a user relative to a target element
7195 * @param {HTMLElement} target - Element to show the popout in relation to
7196 * @param {object} user - Discord User object for the user to show
7197 * @param {object} [options] - Options to modify the request
7198 * @param {string} [options.guild="currentGuildId"] - Id of the guild (uses current if not specified)
7199 * @param {string} [options.channel="currentChannelId"] - Id of the channel (uses current if not specified)
7200 * @param {string} [options.position="right"] - Positioning relative to element
7201 */
7202 static showUserPopout(target, user, options = {}) {
7203 const {guild = modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].SelectedGuildStore.getGuildId(), channel = modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].SelectedChannelStore.getChannelId()} = options;
7204 let {position = "right"} = options;
7205 target = modules__WEBPACK_IMPORTED_MODULE_1__["DOMTools"].resolveElement(target);
7206 if (target.getBoundingClientRect().right + 250 >= structs__WEBPACK_IMPORTED_MODULE_0__["Screen"].width) position = "left";
7207 modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].PopoutOpener.openPopout(target, {
7208 position: position,
7209 offsetX: 0,
7210 offsetY: 0,
7211 animationType: "default",
7212 preventInvert: false,
7213 zIndexBoost: 0,
7214 closeOnScroll: false,
7215 shadow: false,
7216 backdrop: false,
7217 toggleClose: true,
7218 render: (props) => {
7219 return modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].React.createElement(modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].UserPopout, Object.assign({}, props, {
7220 userId: user.id,
7221 guildId: guild,
7222 channelId: channel
7223 }));
7224 }
7225 }, "ZeresLibrary");
7226 }
7227}
7228
7229/***/ }),
7230
7231/***/ "./src/ui/settings/index.js":
7232/*!**********************************!*\
7233 !*** ./src/ui/settings/index.js ***!
7234 \**********************************/
7235/*! exports provided: CSS, SettingField, SettingGroup, SettingPanel, Textbox, ColorPicker, FilePicker, Slider, Switch, Dropdown, Keybind, RadioGroup, ReactSetting */
7236/***/ (function(module, __webpack_exports__, __webpack_require__) {
7237
7238"use strict";
7239__webpack_require__.r(__webpack_exports__);
7240/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CSS", function() { return CSS; });
7241/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./settingfield */ "./src/ui/settings/settingfield.js");
7242/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ReactSetting", function() { return _settingfield__WEBPACK_IMPORTED_MODULE_0__["ReactSetting"]; });
7243
7244/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "SettingField", function() { return _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"]; });
7245
7246/* harmony import */ var _settinggroup__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./settinggroup */ "./src/ui/settings/settinggroup.js");
7247/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "SettingGroup", function() { return _settinggroup__WEBPACK_IMPORTED_MODULE_1__["default"]; });
7248
7249/* harmony import */ var _settingpanel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./settingpanel */ "./src/ui/settings/settingpanel.js");
7250/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "SettingPanel", function() { return _settingpanel__WEBPACK_IMPORTED_MODULE_2__["default"]; });
7251
7252/* harmony import */ var _types_textbox__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./types/textbox */ "./src/ui/settings/types/textbox.js");
7253/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Textbox", function() { return _types_textbox__WEBPACK_IMPORTED_MODULE_3__["default"]; });
7254
7255/* harmony import */ var _types_color__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./types/color */ "./src/ui/settings/types/color.js");
7256/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ColorPicker", function() { return _types_color__WEBPACK_IMPORTED_MODULE_4__["default"]; });
7257
7258/* harmony import */ var _types_file__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./types/file */ "./src/ui/settings/types/file.js");
7259/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "FilePicker", function() { return _types_file__WEBPACK_IMPORTED_MODULE_5__["default"]; });
7260
7261/* harmony import */ var _types_slider__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./types/slider */ "./src/ui/settings/types/slider.js");
7262/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Slider", function() { return _types_slider__WEBPACK_IMPORTED_MODULE_6__["default"]; });
7263
7264/* harmony import */ var _types_switch__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./types/switch */ "./src/ui/settings/types/switch.js");
7265/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Switch", function() { return _types_switch__WEBPACK_IMPORTED_MODULE_7__["default"]; });
7266
7267/* harmony import */ var _types_dropdown__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./types/dropdown */ "./src/ui/settings/types/dropdown.js");
7268/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Dropdown", function() { return _types_dropdown__WEBPACK_IMPORTED_MODULE_8__["default"]; });
7269
7270/* harmony import */ var _types_keybind__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./types/keybind */ "./src/ui/settings/types/keybind.js");
7271/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Keybind", function() { return _types_keybind__WEBPACK_IMPORTED_MODULE_9__["default"]; });
7272
7273/* harmony import */ var _types_radiogroup__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./types/radiogroup */ "./src/ui/settings/types/radiogroup.js");
7274/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "RadioGroup", function() { return _types_radiogroup__WEBPACK_IMPORTED_MODULE_10__["default"]; });
7275
7276/**
7277 * An object that makes generating settings panel 10x easier.
7278 * @module Settings
7279 * @version 1.1.2
7280 */
7281
7282const CSS = __webpack_require__(/*! ../../styles/settings.css */ "./src/styles/settings.css");
7283
7284
7285
7286
7287
7288
7289
7290
7291
7292
7293
7294
7295
7296
7297/***/ }),
7298
7299/***/ "./src/ui/settings/settingfield.js":
7300/*!*****************************************!*\
7301 !*** ./src/ui/settings/settingfield.js ***!
7302 \*****************************************/
7303/*! exports provided: default, ReactSetting */
7304/***/ (function(module, __webpack_exports__, __webpack_require__) {
7305
7306"use strict";
7307__webpack_require__.r(__webpack_exports__);
7308/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ReactSetting", function() { return ReactSetting; });
7309/* harmony import */ var _structs_listenable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../structs/listenable */ "./src/structs/listenable.js");
7310/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
7311
7312
7313
7314/**
7315 * Setting field to extend to create new settings
7316 * @memberof module:Settings
7317 * @version 1.0.1
7318 */
7319class SettingField extends _structs_listenable__WEBPACK_IMPORTED_MODULE_0__["default"] {
7320 /**
7321 * @param {string} name - name label of the setting
7322 * @param {string} note - help/note to show underneath or above the setting
7323 * @param {callable} onChange - callback to perform on setting change
7324 * @param {(ReactComponent|HTMLElement)} settingtype - actual setting to render
7325 * @param {object} [props] - object of props to give to the setting and the settingtype
7326 * @param {boolean} [props.noteOnTop=false] - determines if the note should be shown above the element or not.
7327 */
7328 constructor(name, note, onChange, settingtype, props = {}) {
7329 super();
7330 this.name = name;
7331 this.note = note;
7332 if (typeof(onChange) == "function") this.addListener(onChange);
7333 this.inputWrapper = modules__WEBPACK_IMPORTED_MODULE_1__["DOMTools"].parseHTML(`<div class="plugin-input-container"></div>`);
7334 this.type = typeof(settingtype) == "function" ? settingtype : modules__WEBPACK_IMPORTED_MODULE_1__["ReactTools"].wrapElement(settingtype);
7335 this.props = props;
7336 modules__WEBPACK_IMPORTED_MODULE_1__["DOMTools"].onAdded(this.getElement(), () => {this.onAdded();});
7337 modules__WEBPACK_IMPORTED_MODULE_1__["DOMTools"].onRemoved(this.getElement(), () => {this.onRemoved();});
7338 }
7339
7340 /** @returns {HTMLElement} - root element for setting */
7341 getElement() { return this.inputWrapper; }
7342
7343 /** Fires onchange to listeners */
7344 onChange() {
7345 this.alertListeners(...arguments);
7346 }
7347
7348 /** Fired when root node added to DOM */
7349 onAdded() {
7350 const reactElement = modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].ReactDOM.render(modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].React.createElement(ReactSetting, Object.assign({
7351 title: this.name,
7352 type: this.type,
7353 note: this.note,
7354 }, this.props)), this.getElement());
7355
7356 if (this.props.onChange) reactElement.props.onChange = this.props.onChange(reactElement);
7357 reactElement.forceUpdate();
7358 }
7359
7360 /** Fired when root node removed from DOM */
7361 onRemoved() {
7362 modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].ReactDOM.unmountComponentAtNode(this.getElement());
7363 }
7364}
7365
7366/* harmony default export */ __webpack_exports__["default"] = (SettingField);
7367
7368class ReactSetting extends modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].React.Component {
7369 constructor(props) {
7370 super(props);
7371 }
7372
7373 get noteElement() {
7374 const className = this.props.noteOnTop ? modules__WEBPACK_IMPORTED_MODULE_1__["DiscordClasses"].Margins.marginBottom8 : modules__WEBPACK_IMPORTED_MODULE_1__["DiscordClasses"].Margins.marginTop8;
7375 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()});
7376 }
7377
7378 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()}); }
7379
7380 render() {
7381 const SettingElement = modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].React.createElement(this.props.type, this.props);
7382 return modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].React.createElement(modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].SettingsWrapper, {
7383 className: modules__WEBPACK_IMPORTED_MODULE_1__["DiscordClasses"].Margins.marginBottom20.toString(),
7384 title: this.props.title,
7385 children: [
7386 this.props.noteOnTop ? this.noteElement : SettingElement,
7387 this.props.noteOnTop ? SettingElement : this.noteElement,
7388 this.dividerElement
7389 ]
7390 });
7391 }
7392}
7393
7394
7395
7396/***/ }),
7397
7398/***/ "./src/ui/settings/settinggroup.js":
7399/*!*****************************************!*\
7400 !*** ./src/ui/settings/settinggroup.js ***!
7401 \*****************************************/
7402/*! exports provided: default */
7403/***/ (function(module, __webpack_exports__, __webpack_require__) {
7404
7405"use strict";
7406__webpack_require__.r(__webpack_exports__);
7407/* harmony import */ var _structs_listenable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../structs/listenable */ "./src/structs/listenable.js");
7408/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
7409/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./settingfield */ "./src/ui/settings/settingfield.js");
7410
7411
7412
7413
7414/**
7415 * Grouping of controls for easier management in settings panels.
7416 * @memberof module:Settings
7417 * @version 1.0.2
7418 */
7419class SettingGroup extends _structs_listenable__WEBPACK_IMPORTED_MODULE_0__["default"] {
7420 /**
7421 * @param {string} groupName - title for the group of settings
7422 * @param {object} [options] - additional options for the group
7423 * @param {callback} [options.callback] - callback called on settings changed
7424 * @param {boolean} [options.collapsible=true] - determines if the group should be collapsible
7425 * @param {boolean} [options.shown=false] - determines if the group should be expanded by default
7426 */
7427 constructor(groupName, options = {}) {
7428 super();
7429 const {collapsible = true, shown = false, callback = () => {}} = options;
7430 this.addListener(callback);
7431 this.onChange = this.onChange.bind(this);
7432
7433 const collapsed = shown || !collapsible ? "" : "collapsed";
7434 const group = modules__WEBPACK_IMPORTED_MODULE_1__["DOMTools"].parseHTML(`<div class="plugin-input-group">
7435 <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}">
7436 <span class="button-collapse ${collapsed}"></span> ${groupName}
7437 </h2>
7438 <div class="plugin-inputs collapsible ${collapsed}"></div>
7439 </div>`);
7440 const label = group.querySelector("h2");
7441 const controls = group.querySelector(".plugin-inputs");
7442
7443 this.group = group;
7444 this.label = label;
7445 this.controls = controls;
7446
7447 if (!collapsible) return;
7448 label.addEventListener("click", async () => {
7449 const button = label.querySelector(".button-collapse");
7450 const wasCollapsed = button.classList.contains("collapsed");
7451 group.parentElement.querySelectorAll(":scope > .plugin-input-group > .collapsible:not(.collapsed)").forEach((element) => {
7452 element.style.setProperty("height", element.scrollHeight + "px");
7453 element.classList.add("collapsed");
7454 setImmediate(() => {element.style.setProperty("height", "");});
7455 });
7456 group.parentElement.querySelectorAll(":scope > .plugin-input-group > h2 > .button-collapse").forEach(e => e.classList.add("collapsed"));
7457 if (!wasCollapsed) return;
7458 controls.style.setProperty("height", controls.scrollHeight + "px");
7459 controls.classList.remove("collapsed");
7460 button.classList.remove("collapsed");
7461 await new Promise(resolve => setTimeout(resolve, 300));
7462 controls.style.setProperty("height", "");
7463 });
7464 }
7465
7466 /** @returns {HTMLElement} - root node for the group. */
7467 getElement() {return this.group;}
7468
7469 /**
7470 * Adds multiple nodes to this group.
7471 * @param {(...HTMLElement|...jQuery|...module:Settings.SettingField|...module:Settings.SettingGroup)} nodes - list of nodes to add to the group container
7472 * @returns {module:Settings.SettingGroup} - returns self for chaining
7473 */
7474 append(...nodes) {
7475 for (let i = 0; i < nodes.length; i++) {
7476 if (nodes[i] instanceof jQuery || nodes[i] instanceof Element) this.controls.append(nodes[i]);
7477 else if (nodes[i] instanceof _settingfield__WEBPACK_IMPORTED_MODULE_2__["default"] || nodes[i] instanceof SettingGroup) this.controls.append(nodes[i].getElement());
7478 if (nodes[i] instanceof _settingfield__WEBPACK_IMPORTED_MODULE_2__["default"]) {
7479 nodes[i].addListener(((node) => (value) => {
7480 this.onChange(node.id || node.name, value);
7481 })(nodes[i]));
7482 }
7483 else if (nodes[i] instanceof SettingGroup) {
7484 nodes[i].addListener(((node) => (settingId, value) => {
7485 this.onChange(node.id || node.name, settingId, value);
7486 })(nodes[i]));
7487 }
7488 }
7489 return this;
7490 }
7491
7492 /**
7493 * Appends this node to another
7494 * @param {HTMLElement} node - node to attach the group to.
7495 * @returns {module:Settings.SettingGroup} - returns self for chaining
7496 */
7497 appendTo(node) {
7498 node.append(this.group);
7499 return this;
7500 }
7501
7502 /** Fires onchange to listeners */
7503 onChange() {
7504 this.alertListeners(...arguments);
7505 }
7506}
7507
7508/* harmony default export */ __webpack_exports__["default"] = (SettingGroup);
7509
7510/***/ }),
7511
7512/***/ "./src/ui/settings/settingpanel.js":
7513/*!*****************************************!*\
7514 !*** ./src/ui/settings/settingpanel.js ***!
7515 \*****************************************/
7516/*! exports provided: default */
7517/***/ (function(module, __webpack_exports__, __webpack_require__) {
7518
7519"use strict";
7520__webpack_require__.r(__webpack_exports__);
7521/* harmony import */ var _structs_listenable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../structs/listenable */ "./src/structs/listenable.js");
7522/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
7523/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./settingfield */ "./src/ui/settings/settingfield.js");
7524/* harmony import */ var _settinggroup__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./settinggroup */ "./src/ui/settings/settinggroup.js");
7525
7526
7527
7528
7529
7530/**
7531 * Grouping of controls for easier management in settings panels.
7532 * @memberof module:Settings
7533 * @version 1.0.2
7534 */
7535class SettingPanel extends _structs_listenable__WEBPACK_IMPORTED_MODULE_0__["default"] {
7536
7537 /**
7538 * Creates a new settings panel
7539 * @param {callable} onChange - callback to fire when settings change
7540 * @param {(...HTMLElement|...jQuery|...module:Settings.SettingField|...module:Settings.SettingGroup)} nodes - list of nodes to add to the panel container
7541 */
7542 constructor(onChange, ...nodes) {
7543 super();
7544 this.element = modules__WEBPACK_IMPORTED_MODULE_1__["DOMTools"].parseHTML(`<div class="plugin-form-container"></div>`);
7545 if (typeof(onChange) == "function") this.addListener(onChange);
7546 this.onChange = this.onChange.bind(this);
7547 this.append(...nodes);
7548 }
7549
7550 /**
7551 * Creates a new settings panel
7552 * @param {callable} onChange - callback to fire when settings change
7553 * @param {(...HTMLElement|...jQuery|...module:Settings.SettingField|...module:Settings.SettingGroup)} nodes - list of nodes to add to the panel container
7554 * @returns {HTMLElement} - root node for the panel.
7555 */
7556 static build(onChange, ...nodes) {
7557 return (new SettingPanel(onChange, ...nodes)).getElement();
7558 }
7559
7560 /** @returns {HTMLElement} - root node for the panel. */
7561 getElement() {return this.element;}
7562
7563 /**
7564 * Adds multiple nodes to this panel.
7565 * @param {(...HTMLElement|...jQuery|...SettingField|...SettingGroup)} nodes - list of nodes to add to the panel container
7566 * @returns {module:Settings.SettingPanel} - returns self for chaining
7567 */
7568 append(...nodes) {
7569 for (let i = 0; i < nodes.length; i++) {
7570 if (nodes[i] instanceof jQuery || nodes[i] instanceof Element) this.element.append(nodes[i]);
7571 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());
7572 if (nodes[i] instanceof _settingfield__WEBPACK_IMPORTED_MODULE_2__["default"]) {
7573 nodes[i].addListener(((node) => (value) => {
7574 this.onChange(node.id || node.name, value);
7575 })(nodes[i]));
7576 }
7577 else if (nodes[i] instanceof _settinggroup__WEBPACK_IMPORTED_MODULE_3__["default"]) {
7578 nodes[i].addListener(((node) => (settingId, value) => {
7579 this.onChange(node.id || node.name, settingId, value);
7580 })(nodes[i]));
7581 }
7582 }
7583 return this;
7584 }
7585
7586 /** Fires onchange to listeners */
7587 onChange() {
7588 this.alertListeners(...arguments);
7589 }
7590}
7591
7592/* harmony default export */ __webpack_exports__["default"] = (SettingPanel);
7593
7594/***/ }),
7595
7596/***/ "./src/ui/settings/types/color.js":
7597/*!****************************************!*\
7598 !*** ./src/ui/settings/types/color.js ***!
7599 \****************************************/
7600/*! exports provided: default */
7601/***/ (function(module, __webpack_exports__, __webpack_require__) {
7602
7603"use strict";
7604__webpack_require__.r(__webpack_exports__);
7605/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js");
7606/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
7607
7608
7609
7610
7611const presetColors = [1752220, 3066993, 3447003, 10181046, 15277667, 15844367, 15105570, 15158332, 9807270, 6323595, 1146986, 2067276, 2123412, 7419530, 11342935, 12745742, 11027200, 10038562, 9936031, 5533306];
7612
7613/**
7614 * Creates a color picker using Discord's built in color picker
7615 * as a base. Input and output using hex strings.
7616 * @memberof module:Settings
7617 * @version 0.1.0
7618 * @extends module:Settings.SettingField
7619 */
7620class ColorPicker extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] {
7621 /**
7622 * @param {string} name - name label of the setting
7623 * @param {string} note - help/note to show underneath or above the setting
7624 * @param {string} value - current hex color
7625 * @param {callable} onChange - callback to perform on setting change, callback receives hex string
7626 * @param {object} [options] - object of options to give to the setting
7627 * @param {boolean} [options.disabled=false] - should the setting be disabled
7628 * @param {Array<number>} [options.colors=presetColors] - preset list of colors
7629 */
7630 constructor(name, note, value, onChange, options = {}) {
7631 super(name, note, onChange, modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].ColorPicker, {
7632 disabled: options.disabled ? true : false,
7633 onChange: reactElement => color => {
7634 reactElement.props.value = color;
7635 reactElement.forceUpdate();
7636 this.onChange(modules__WEBPACK_IMPORTED_MODULE_1__["ColorConverter"].int2hex(color));
7637 },
7638 colors: Array.isArray(options.colors) ? options.colors : presetColors,
7639 defaultColor: typeof(value) == "number" ? value : modules__WEBPACK_IMPORTED_MODULE_1__["ColorConverter"].hex2int(value),
7640 value: 0
7641 });
7642 }
7643
7644 /** Default colors for ColorPicker */
7645 static get presetColors() {return presetColors;}
7646}
7647
7648
7649
7650/* harmony default export */ __webpack_exports__["default"] = (ColorPicker);
7651
7652/***/ }),
7653
7654/***/ "./src/ui/settings/types/dropdown.js":
7655/*!*******************************************!*\
7656 !*** ./src/ui/settings/types/dropdown.js ***!
7657 \*******************************************/
7658/*! exports provided: default */
7659/***/ (function(module, __webpack_exports__, __webpack_require__) {
7660
7661"use strict";
7662__webpack_require__.r(__webpack_exports__);
7663/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js");
7664/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
7665
7666
7667
7668/**
7669 * @interface
7670 * @name module:Settings~DropdownItem
7671 * @property {string} label - label to show in the dropdown
7672 * @property {*} value - actual value represented by label (this is passed via onChange)
7673 */
7674
7675/**
7676 * Creates a dropdown using discord's built in dropdown.
7677 * @memberof module:Settings
7678 * @version 0.0.1
7679 * @extends module:Settings.SettingField
7680 */
7681class Dropdown extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] {
7682 /**
7683 * @param {string} name - name label of the setting
7684 * @param {string} note - help/note to show underneath or above the setting
7685 * @param {*} defaultValue - currently selected value
7686 * @param {Array<module:Settings~DropdownItem>} values - array of all options available
7687 * @param {callable} onChange - callback to perform on setting change, callback item value
7688 * @param {object} [options] - object of options to give to the setting
7689 * @param {boolean} [options.clearable=false] - should be able to empty the field value
7690 * @param {boolean} [options.searchable=false] - should user be able to search the dropdown
7691 */
7692 constructor(name, note, defaultValue, values, onChange, options = {}) {
7693 const {clearable = false, searchable = false} = options;
7694 super(name, note, onChange, modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].Dropdown, {
7695 clearable: clearable,
7696 searchable: searchable,
7697 options: values,
7698 onChange: dropdown => opt => {
7699 dropdown.props.value = opt.value;
7700 dropdown.forceUpdate();
7701 this.onChange(opt.value);
7702 },
7703 value: defaultValue
7704 });
7705 }
7706}
7707
7708/* harmony default export */ __webpack_exports__["default"] = (Dropdown);
7709
7710/***/ }),
7711
7712/***/ "./src/ui/settings/types/file.js":
7713/*!***************************************!*\
7714 !*** ./src/ui/settings/types/file.js ***!
7715 \***************************************/
7716/*! exports provided: default */
7717/***/ (function(module, __webpack_exports__, __webpack_require__) {
7718
7719"use strict";
7720__webpack_require__.r(__webpack_exports__);
7721/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js");
7722/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
7723
7724
7725
7726/**
7727 * Creates a file picker using chromium's default.
7728 * @memberof module:Settings
7729 * @version 0.0.1
7730 * @extends module:Settings.SettingField
7731 */
7732class FilePicker extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] {
7733 /**
7734 * @param {string} name - name label of the setting
7735 * @param {string} note - help/note to show underneath or above the setting
7736 * @param {callable} onChange - callback to perform on setting change, callback receives File object
7737 */
7738 constructor(name, note, onChange) {
7739 const ReactFilePicker = modules__WEBPACK_IMPORTED_MODULE_1__["DOMTools"].parseHTML(`<input type="file" class="${modules__WEBPACK_IMPORTED_MODULE_1__["DiscordClasses"].BasicInputs.inputDefault.add("file-input")}">`);
7740 ReactFilePicker.addEventListener("change", (event) => {
7741 this.onChange(event.target.files[0]);
7742 });
7743 super(name, note, onChange, ReactFilePicker);
7744 }
7745}
7746
7747/* harmony default export */ __webpack_exports__["default"] = (FilePicker);
7748
7749/***/ }),
7750
7751/***/ "./src/ui/settings/types/keybind.js":
7752/*!******************************************!*\
7753 !*** ./src/ui/settings/types/keybind.js ***!
7754 \******************************************/
7755/*! exports provided: default */
7756/***/ (function(module, __webpack_exports__, __webpack_require__) {
7757
7758"use strict";
7759__webpack_require__.r(__webpack_exports__);
7760/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js");
7761/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
7762
7763
7764
7765/**
7766 * Creates a keybind setting using discord's built in keybind recorder.
7767 * @memberof module:Settings
7768 * @version 0.0.1
7769 * @extends module:Settings.SettingField
7770 */
7771class Keybind extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] {
7772 /**
7773 * @param {string} name - name label of the setting
7774 * @param {string} note - help/note to show underneath or above the setting
7775 * @param {Array<number>} value - array of keycodes
7776 * @param {callable} onChange - callback to perform on setting change, callback receives array of keycodes
7777 */
7778 constructor(label, help, value, onChange) {
7779 super(label, help, onChange, modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].Keybind, {
7780 defaultValue: value.map(a => [0, a]),
7781 onChange: element => value => {
7782 if (!Array.isArray(value)) return;
7783 element.props.value = value;
7784 this.onChange(value.map(a => a[1]));
7785 }
7786 });
7787 }
7788}
7789
7790/* harmony default export */ __webpack_exports__["default"] = (Keybind);
7791
7792/***/ }),
7793
7794/***/ "./src/ui/settings/types/radiogroup.js":
7795/*!*********************************************!*\
7796 !*** ./src/ui/settings/types/radiogroup.js ***!
7797 \*********************************************/
7798/*! exports provided: default */
7799/***/ (function(module, __webpack_exports__, __webpack_require__) {
7800
7801"use strict";
7802__webpack_require__.r(__webpack_exports__);
7803/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js");
7804/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
7805
7806
7807
7808/**
7809 * @interface
7810 * @name module:Settings~RadioItem
7811 * @property {string} name - label to show in the dropdown
7812 * @property {*} value - actual value represented by label (this is passed via onChange)
7813 * @property {string} desc - description/help text to show below name
7814 * @property {string} color - hex string to color the item
7815 */
7816
7817/**
7818 * Creates a radio group using discord's built in radios.
7819 * @memberof module:Settings
7820 * @version 0.0.1
7821 * @extends module:Settings.SettingField
7822 */
7823class RadioGroup extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] {
7824 /**
7825 * @param {string} name - name label of the setting
7826 * @param {string} note - help/note to show underneath or above the setting
7827 * @param {*} defaultValue - currently selected value
7828 * @param {Array<module:Settings~RadioItem>} values - array of all options available
7829 * @param {callable} onChange - callback to perform on setting change, callback item value
7830 * @param {object} [options] - object of options to give to the setting
7831 * @param {boolean} [options.disabled=false] - should the setting be disabled
7832 */
7833 constructor(name, note, defaultValue, values, onChange, options = {}) {
7834 super(name, note, onChange, modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].RadioGroup, {
7835 noteOnTop: true,
7836 disabled: options.disabled ? true : false,
7837 options: values,
7838 onChange: reactElement => option => {
7839 reactElement.props.value = option.value;
7840 reactElement.forceUpdate();
7841 this.onChange(option.value);
7842 },
7843 value: defaultValue
7844 });
7845 }
7846}
7847
7848/* harmony default export */ __webpack_exports__["default"] = (RadioGroup);
7849
7850
7851
7852/***/ }),
7853
7854/***/ "./src/ui/settings/types/slider.js":
7855/*!*****************************************!*\
7856 !*** ./src/ui/settings/types/slider.js ***!
7857 \*****************************************/
7858/*! exports provided: default */
7859/***/ (function(module, __webpack_exports__, __webpack_require__) {
7860
7861"use strict";
7862__webpack_require__.r(__webpack_exports__);
7863/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js");
7864/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
7865
7866
7867
7868//TODO: Documentation
7869
7870/**
7871 * Creates a slider/range using discord's built in slider.
7872 * @memberof module:Settings
7873 * @version 0.1.0
7874 * @extends module:Settings.SettingField
7875 */
7876class Slider extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] {
7877 /**
7878 *
7879 * @param {string} name - name label of the setting
7880 * @param {string} note - help/note to show underneath or above the setting
7881 * @param {number} min - minimum value allowed
7882 * @param {number} max - maximum value allowed
7883 * @param {number} value - currently selected value
7884 * @param {callable} onChange - callback to fire when setting is changed, callback receives number
7885 * @param {object} [options] - object of options to give to the setting
7886 * @param {boolean} [options.disabled=false] - should the setting be disabled
7887 * @param {object} [options.fillStyles] - object of css styles to add to active slider
7888 * @param {Array<number>} [options.markers] - array of vertical markers to show on the slider
7889 * @param {boolean} [options.stickToMarkers] - should the slider be forced to use markers
7890 * @param {boolean} [options.equidistant] - should the markers be scaled to be equidistant
7891 */
7892 constructor(name, note, min, max, value, onChange, options = {}) {
7893 const props = {
7894 onChange: _ => _,
7895 defaultValue: value,
7896 disabled: options.disabled ? true : false,
7897 minValue: min,
7898 maxValue: max,
7899 handleSize: 10
7900 };
7901 if (options.fillStyles) props.fillStyles = options.fillStyles;
7902 if (options.markers) props.markers = options.markers;
7903 if (options.stickToMarkers) props.stickToMarkers = options.stickToMarkers;
7904 if (typeof(options.equidistant) != "undefined") props.equidistant = options.equidistant;
7905 super(name, note, onChange, modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].Slider, Object.assign(props, {onValueChange: v => this.onChange(v)}));
7906 }
7907}
7908
7909/* harmony default export */ __webpack_exports__["default"] = (Slider);
7910
7911/***/ }),
7912
7913/***/ "./src/ui/settings/types/switch.js":
7914/*!*****************************************!*\
7915 !*** ./src/ui/settings/types/switch.js ***!
7916 \*****************************************/
7917/*! exports provided: default */
7918/***/ (function(module, __webpack_exports__, __webpack_require__) {
7919
7920"use strict";
7921__webpack_require__.r(__webpack_exports__);
7922/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js");
7923/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
7924
7925
7926
7927//TODO: Documentation
7928
7929/**
7930 * Creates a switch using discord's built in switch.
7931 * @memberof module:Settings
7932 * @version 0.1.0
7933 * @extends module:Settings.SettingField
7934 */
7935class Switch extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] {
7936 /**
7937 * @param {string} name - name label of the setting
7938 * @param {string} note - help/note to show underneath or above the setting
7939 * @param {boolean} isChecked - should switch be checked
7940 * @param {callable} onChange - callback to perform on setting change, callback receives boolean
7941 * @param {object} [options] - object of options to give to the setting
7942 * @param {boolean} [options.disabled=false] - should the setting be disabled
7943 */
7944 constructor(name, note, isChecked, onChange, options = {}) {
7945 super(name, note, onChange);
7946 this.disabled = options.disabled ? true : false;
7947 this.value = isChecked ? true : false;
7948 }
7949
7950 onAdded() {
7951 const reactElement = modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].ReactDOM.render(modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].React.createElement(modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].SwitchRow, {
7952 children: this.name,
7953 note: this.note,
7954 disabled: this.disabled,
7955 hideBorder: false,
7956 value: this.value,
7957 onChange: (e) => {
7958 const checked = e.currentTarget.checked;
7959 reactElement.props.value = checked;
7960 reactElement.forceUpdate();
7961 this.onChange(checked);
7962 }
7963 }), this.getElement());
7964 }
7965}
7966
7967/* harmony default export */ __webpack_exports__["default"] = (Switch);
7968
7969/***/ }),
7970
7971/***/ "./src/ui/settings/types/textbox.js":
7972/*!******************************************!*\
7973 !*** ./src/ui/settings/types/textbox.js ***!
7974 \******************************************/
7975/*! exports provided: default */
7976/***/ (function(module, __webpack_exports__, __webpack_require__) {
7977
7978"use strict";
7979__webpack_require__.r(__webpack_exports__);
7980/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js");
7981/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
7982
7983
7984
7985//TODO: Documentation
7986
7987/**
7988 * Creates a textbox using discord's built in textbox.
7989 * @memberof module:Settings
7990 * @version 0.1.0
7991 * @extends module:Settings.SettingField
7992 */
7993class Textbox extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] {
7994 /**
7995 * @param {string} name - name label of the setting
7996 * @param {string} note - help/note to show underneath or above the setting
7997 * @param {string} value - current text in box
7998 * @param {callable} onChange - callback to perform on setting change, callback receives text
7999 * @param {object} [options] - object of options to give to the setting
8000 * @param {string} [options.placeholder=""] - placeholder for when textbox is empty
8001 */
8002 constructor(name, note, value, onChange, options = {}) {
8003 super(name, note, onChange, modules__WEBPACK_IMPORTED_MODULE_1__["DiscordModules"].Textbox, {
8004 onChange: textbox => value => {
8005 textbox.props.value = value;
8006 textbox.forceUpdate();
8007 this.onChange(value);
8008 },
8009 value: value,
8010 placeholder: options.placeholder ? options.placeholder : ""
8011 });
8012 }
8013}
8014
8015/* harmony default export */ __webpack_exports__["default"] = (Textbox);
8016
8017/***/ }),
8018
8019/***/ "./src/ui/toasts.js":
8020/*!**************************!*\
8021 !*** ./src/ui/toasts.js ***!
8022 \**************************/
8023/*! exports provided: default */
8024/***/ (function(module, __webpack_exports__, __webpack_require__) {
8025
8026"use strict";
8027__webpack_require__.r(__webpack_exports__);
8028/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return Toast; });
8029/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
8030/* harmony import */ var ui__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ui */ "./src/ui/ui.js");
8031/**
8032 * Toast maker similar to Android.
8033 *
8034 * @module Toasts
8035 * @version 0.0.1
8036 */
8037
8038
8039
8040class Toast {
8041
8042 static get CSS() {return __webpack_require__(/*! ../styles/toasts.css */ "./src/styles/toasts.css");}
8043
8044 /** Shorthand for `type = "success"` for {@link module:Toasts.show} */
8045 static async success(content, options = {}) {return this.show(content, Object.assign(options, {type: "success"}));}
8046
8047 /** Shorthand for `type = "info"` for {@link module:Toasts.show} */
8048 static async info(content, options = {}) {return this.show(content, Object.assign(options, {type: "info"}));}
8049
8050 /** Shorthand for `type = "warning"` for {@link module:Toasts.show} */
8051 static async warning(content, options = {}) {return this.show(content, Object.assign(options, {type: "warning"}));}
8052
8053 /** Shorthand for `type = "error"` for {@link module:Toasts.show} */
8054 static async error(content, options = {}) {return this.show(content, Object.assign(options, {type: "error"}));}
8055
8056 /** Shorthand for `type = "default"` for {@link module:Toasts.show} */
8057 static async default(content, options = {}) {return this.show(content, Object.assign(options, {type: "default"}));}
8058
8059
8060 /**
8061 * Shows a simple toast, similar to Android, centered over
8062 * the textarea if it exists, and center screen otherwise.
8063 * Vertically it shows towards the bottom like in Android.
8064 * @param {string} content - The string to show in the toast.
8065 * @param {object} options - additional options for the toast
8066 * @param {string} [options.type] - Changes the type of the toast stylistically and semantically. {@link module:Toasts.ToastTypes}
8067 * @param {string} [options.icon] - URL to an optional icon
8068 * @param {number} [options.timeout=3000] - Adjusts the time (in ms) the toast should be shown for before disappearing automatically
8069 * @returns {Promise} - Promise that resolves when the toast is removed from the DOM
8070 */
8071 static async show(content, options = {}) {
8072 const {type = "", icon = "", timeout = 3000} = options;
8073 this.ensureContainer();
8074 const toast = modules__WEBPACK_IMPORTED_MODULE_0__["DOMTools"].parseHTML(this.buildToast(content, this.parseType(type), icon));
8075 document.querySelector(".toasts").appendChild(toast);
8076 await new Promise(resolve => setTimeout(resolve, timeout));
8077 toast.classList.add("closing");
8078 await new Promise(resolve => setTimeout(resolve, 300));
8079 toast.remove();
8080 if (!document.querySelectorAll(".toasts .toast").length) document.querySelector(".toasts").remove();
8081 }
8082
8083 static buildToast(message, type, icon) {
8084 const hasIcon = type || icon;
8085 const className = `toast ${hasIcon ? "toast-has-icon" : ""} ${type && type != "default" ? `toast-${type}` : ""}`;
8086 if (!icon && type) icon = type;
8087 return modules__WEBPACK_IMPORTED_MODULE_0__["Utilities"].formatString(`<div class="{{className}}">{{icon}}<div class="toast-text">{{message}}</div></div>`, {
8088 className: className,
8089 icon: hasIcon ? this.getIcon(icon) : "",
8090 message: message
8091 });
8092 }
8093
8094 static getIcon(icon) {
8095 let iconInner = `<img src="${icon}" width="20" height="20" />`;
8096 switch (icon) {
8097 case "success": iconInner = ui__WEBPACK_IMPORTED_MODULE_1__["Icons"].IconSuccess(20); break;
8098 case "warning": iconInner = ui__WEBPACK_IMPORTED_MODULE_1__["Icons"].IconWarning(20); break;
8099 case "info": iconInner = ui__WEBPACK_IMPORTED_MODULE_1__["Icons"].IconInfo(20); break;
8100 case "error": iconInner = ui__WEBPACK_IMPORTED_MODULE_1__["Icons"].IconError(20);
8101 }
8102 return modules__WEBPACK_IMPORTED_MODULE_0__["Utilities"].formatString(`<div class="toast-icon">{{icon}}</div>`, {icon: iconInner});
8103 }
8104
8105 static ensureContainer() {
8106 if (document.querySelector(".toasts")) return;
8107 const channelClass = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordSelectors"].ChannelList.sidebar;
8108 const container = channelClass ? document.querySelector(channelClass.adjacent("div")) : null;
8109 const memberlist = container ? container.querySelector(modules__WEBPACK_IMPORTED_MODULE_0__["DiscordSelectors"].MemberList.membersWrap) : null;
8110 const form = container ? container.querySelector("form") : null;
8111 const left = container ? container.getBoundingClientRect().left : 310;
8112 const right = memberlist ? memberlist.getBoundingClientRect().left : 0;
8113 const width = right ? right - container.getBoundingClientRect().left : container.offsetWidth;
8114 const bottom = form ? form.offsetHeight : 80;
8115 const toastWrapper = document.createElement("div");
8116 toastWrapper.classList.add("toasts");
8117 toastWrapper.style.setProperty("left", left + "px");
8118 toastWrapper.style.setProperty("width", width + "px");
8119 toastWrapper.style.setProperty("bottom", bottom + "px");
8120 document.querySelector("#app-mount").appendChild(toastWrapper);
8121 }
8122
8123 static parseType(type) {
8124 return this.ToastTypes.hasOwnProperty(type) ? this.ToastTypes[type] : "";
8125 }
8126
8127 /**
8128 * Enumeration of accepted types.
8129 */
8130 static get ToastTypes() {
8131 return {
8132 "default": "",
8133 "error": "error",
8134 "success": "success",
8135 "warning": "warning",
8136 "info": "info"
8137 };
8138 }
8139}
8140
8141/***/ }),
8142
8143/***/ "./src/ui/tooltip.js":
8144/*!***************************!*\
8145 !*** ./src/ui/tooltip.js ***!
8146 \***************************/
8147/*! exports provided: default */
8148/***/ (function(module, __webpack_exports__, __webpack_require__) {
8149
8150"use strict";
8151__webpack_require__.r(__webpack_exports__);
8152/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return Tooltip; });
8153/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js");
8154/* harmony import */ var _structs_screen__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../structs/screen */ "./src/structs/screen.js");
8155/**
8156 * Tooltips that automatically show and hide themselves on mouseenter and mouseleave events.
8157 * Will also remove themselves if the node to watch is removed from DOM through
8158 * a MutationObserver.
8159 *
8160 * @module Tooltip
8161 * @version 0.0.2
8162 */
8163
8164
8165
8166
8167class Tooltip {
8168 /**
8169 *
8170 * @constructor
8171 * @param {(HTMLElement|jQuery)} node - DOM node to monitor and show the tooltip on
8172 * @param {string} tip - string to show in the tooltip
8173 * @param {object} options - additional options for the tooltip
8174 * @param {string} [options.style=black] - correlates to the discord styling
8175 * @param {string} [options.side=top] - can be any of top, right, bottom, left
8176 * @param {boolean} [options.preventFlip=false] - prevents moving the tooltip to the opposite side if it is too big or goes offscreen
8177 * @param {boolean} [options.disabled=false] - whether the tooltip should be disabled from showing on hover
8178 */
8179 constructor(node, text, options = {}) {
8180 if (!(node instanceof jQuery) && !(node instanceof Element)) return undefined;
8181 this.node = node instanceof jQuery ? node[0] : node;
8182 const {style = "black", side = "top", disabled = false} = options;
8183 this.label = text;
8184 this.style = style;
8185 this.side = side;
8186 this.disabled = disabled;
8187 this.id = modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].KeyGenerator();
8188
8189 this.node.addEventListener("mouseenter", () => {
8190 if (this.disabled) return;
8191 this.show();
8192
8193 const observer = new MutationObserver((mutations) => {
8194 mutations.forEach((mutation) => {
8195 const nodes = Array.from(mutation.removedNodes);
8196 const directMatch = nodes.indexOf(this.node) > -1;
8197 const parentMatch = nodes.some(parent => parent.contains(this.node));
8198 if (directMatch || parentMatch) {
8199 this.hide();
8200 observer.disconnect();
8201 }
8202 });
8203 });
8204
8205 observer.observe(document.body, {subtree: true, childList: true});
8206 });
8207
8208 this.node.addEventListener("mouseleave", () => {
8209 this.hide();
8210 });
8211 }
8212
8213 /**
8214 * Disabled the tooltip and prevents it from showing on hover.
8215 */
8216 disable() {
8217 this.disabled = true;
8218 }
8219
8220 /**
8221 * Enables the tooltip and allows it to show on hover.
8222 */
8223 enable() {
8224 this.disabled = false;
8225 }
8226
8227 /** Hides the tooltip. Automatically called on mouseleave. */
8228 hide() {
8229 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].Tooltips.hide(this.id);
8230 }
8231
8232 /** Shows the tooltip. Automatically called on mouseenter. */
8233 show() {
8234 const {left, top, width, height} = this.node.getBoundingClientRect();
8235 modules__WEBPACK_IMPORTED_MODULE_0__["DiscordModules"].Tooltips.show(this.id, {
8236 position: this.side,
8237 text: this.label,
8238 color: this.style,
8239 targetWidth: width,
8240 targetHeight: height,
8241 windowWidth: _structs_screen__WEBPACK_IMPORTED_MODULE_1__["default"].width,
8242 windowHeight: _structs_screen__WEBPACK_IMPORTED_MODULE_1__["default"].height,
8243 x: left,
8244 y: top
8245 });
8246 }
8247}
8248
8249/***/ }),
8250
8251/***/ "./src/ui/ui.js":
8252/*!**********************!*\
8253 !*** ./src/ui/ui.js ***!
8254 \**********************/
8255/*! exports provided: Tooltip, EmulatedTooltip, Toasts, Popouts, Modals, Settings, ContextMenu, Icons */
8256/***/ (function(module, __webpack_exports__, __webpack_require__) {
8257
8258"use strict";
8259__webpack_require__.r(__webpack_exports__);
8260/* harmony import */ var _settings__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./settings */ "./src/ui/settings/index.js");
8261/* harmony reexport (module object) */ __webpack_require__.d(__webpack_exports__, "Settings", function() { return _settings__WEBPACK_IMPORTED_MODULE_0__; });
8262/* harmony import */ var _contextmenu__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./contextmenu */ "./src/ui/contextmenu.js");
8263/* harmony reexport (module object) */ __webpack_require__.d(__webpack_exports__, "ContextMenu", function() { return _contextmenu__WEBPACK_IMPORTED_MODULE_1__; });
8264/* harmony import */ var _icons__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./icons */ "./src/ui/icons.js");
8265/* harmony reexport (module object) */ __webpack_require__.d(__webpack_exports__, "Icons", function() { return _icons__WEBPACK_IMPORTED_MODULE_2__; });
8266/* harmony import */ var _tooltip__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./tooltip */ "./src/ui/tooltip.js");
8267/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Tooltip", function() { return _tooltip__WEBPACK_IMPORTED_MODULE_3__["default"]; });
8268
8269/* harmony import */ var _emulatedtooltip__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./emulatedtooltip */ "./src/ui/emulatedtooltip.js");
8270/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "EmulatedTooltip", function() { return _emulatedtooltip__WEBPACK_IMPORTED_MODULE_4__["default"]; });
8271
8272/* harmony import */ var _toasts__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./toasts */ "./src/ui/toasts.js");
8273/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Toasts", function() { return _toasts__WEBPACK_IMPORTED_MODULE_5__["default"]; });
8274
8275/* harmony import */ var _popouts__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./popouts */ "./src/ui/popouts.js");
8276/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Popouts", function() { return _popouts__WEBPACK_IMPORTED_MODULE_6__["default"]; });
8277
8278/* harmony import */ var _modals__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./modals */ "./src/ui/modals.js");
8279/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Modals", function() { return _modals__WEBPACK_IMPORTED_MODULE_7__["default"]; });
8280
8281
8282
8283
8284
8285
8286
8287
8288
8289
8290
8291
8292/***/ }),
8293
8294/***/ "electron":
8295/*!***********************************************!*\
8296 !*** external "window.require(\"electron\")" ***!
8297 \***********************************************/
8298/*! no static exports found */
8299/***/ (function(module, exports) {
8300
8301module.exports = window.require("electron");
8302
8303/***/ }),
8304
8305/***/ "fs":
8306/*!*****************************************!*\
8307 !*** external "window.require(\"fs\")" ***!
8308 \*****************************************/
8309/*! no static exports found */
8310/***/ (function(module, exports) {
8311
8312module.exports = window.require("fs");
8313
8314/***/ }),
8315
8316/***/ "path":
8317/*!*******************************************!*\
8318 !*** external "window.require(\"path\")" ***!
8319 \*******************************************/
8320/*! no static exports found */
8321/***/ (function(module, exports) {
8322
8323module.exports = window.require("path");
8324
8325/***/ }),
8326
8327/***/ "process":
8328/*!**************************!*\
8329 !*** external "process" ***!
8330 \**************************/
8331/*! no static exports found */
8332/***/ (function(module, exports) {
8333
8334module.exports = require("process");
8335
8336/***/ }),
8337
8338/***/ "request":
8339/*!**********************************************!*\
8340 !*** external "window.require(\"request\")" ***!
8341 \**********************************************/
8342/*! no static exports found */
8343/***/ (function(module, exports) {
8344
8345module.exports = window.require("request");
8346
8347/***/ })
8348
8349/******/ })["default"];