215 lines
7.5 KiB
JavaScript
215 lines
7.5 KiB
JavaScript
/* eslint-disable no-console */
|
|
|
|
export default class Utilities {
|
|
/** Document/window width */
|
|
static get screenWidth() { return Math.max(document.documentElement.clientWidth, window.innerWidth || 0); }
|
|
/** Document/window height */
|
|
static get screenHeight() { return Math.max(document.documentElement.clientHeight, window.innerHeight || 0); }
|
|
|
|
static stripBOM(content) {
|
|
if (content.charCodeAt(0) === 0xFEFF) {
|
|
content = content.slice(1);
|
|
}
|
|
return content;
|
|
}
|
|
|
|
static getTextArea() {
|
|
return $(".channelTextArea-1LDbYG textarea");
|
|
}
|
|
|
|
static getInternalInstance(node) {
|
|
return node[Object.keys(node).find(k => k.startsWith("__reactInternalInstance"))] || null;
|
|
}
|
|
|
|
static insertText(textarea, text) {
|
|
textarea.focus();
|
|
textarea.selectionStart = 0;
|
|
textarea.selectionEnd = textarea.value.length;
|
|
document.execCommand("insertText", false, text);
|
|
}
|
|
|
|
static injectCss(uri) {
|
|
$("<link/>", {
|
|
type: "text/css",
|
|
rel: "stylesheet",
|
|
href: uri
|
|
}).appendTo($("head"));
|
|
}
|
|
|
|
static injectJs(uri) {
|
|
return new Promise(resolve => {
|
|
$("<script/>", {
|
|
type: "text/javascript",
|
|
src: uri,
|
|
onload: resolve
|
|
}).appendTo($("body"));
|
|
});
|
|
}
|
|
|
|
static escapeID(id) {
|
|
return id.replace(/^[^a-z]+|[^\w-]+/gi, "");
|
|
}
|
|
|
|
static log(moduleName, message) {
|
|
console.log(`%c[BandagedBD]%c [${moduleName}]%c ${message}`, "color: #3a71c1; font-weight: 700;", "color: #3a71c1;", "");
|
|
}
|
|
|
|
static warn(moduleName, message) {
|
|
console.warn(`%c[BandagedBD]%c [${moduleName}]%c ${message}`, "color: #E8A400; font-weight: 700;", "color: #E8A400;", "");
|
|
}
|
|
|
|
static err(moduleName, message, error) {
|
|
console.log(`%c[BandagedBD]%c [${moduleName}]%c ${message}`, "color: red; font-weight: 700;", "color: red;", "");
|
|
if (error) {
|
|
console.groupCollapsed("%cError: " + error.message, "color: red;");
|
|
console.error(error.stack);
|
|
console.groupEnd();
|
|
}
|
|
}
|
|
|
|
static escape(s) {
|
|
return s.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
}
|
|
|
|
static testJSON(data) {
|
|
try {
|
|
JSON.parse(data);
|
|
return true;
|
|
}
|
|
catch (err) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static suppressErrors(method, message) {
|
|
return (...params) => {
|
|
try { return method(...params); }
|
|
catch (e) { this.err("SuppressedError", "Error occurred in " + message, e); }
|
|
};
|
|
}
|
|
|
|
static monkeyPatch(what, methodName, options) {
|
|
const {before, after, instead, once = false, silent = false, force = false} = options;
|
|
const displayName = options.displayName || what.displayName || what.name || what.constructor.displayName || what.constructor.name;
|
|
if (!silent) console.log("patch", methodName, "of", displayName); // eslint-disable-line no-console
|
|
if (!what[methodName]) {
|
|
if (force) what[methodName] = function() {};
|
|
else return console.error(methodName, "does not exist for", displayName); // eslint-disable-line no-console
|
|
}
|
|
const origMethod = what[methodName];
|
|
const cancel = () => {
|
|
if (!silent) console.log("unpatch", methodName, "of", displayName); // eslint-disable-line no-console
|
|
what[methodName] = origMethod;
|
|
};
|
|
what[methodName] = function() {
|
|
const data = {
|
|
thisObject: this,
|
|
methodArguments: arguments,
|
|
cancelPatch: cancel,
|
|
originalMethod: origMethod,
|
|
callOriginalMethod: () => data.returnValue = data.originalMethod.apply(data.thisObject, data.methodArguments)
|
|
};
|
|
if (instead) {
|
|
const tempRet = Utilities.suppressErrors(instead, "`instead` callback of " + what[methodName].displayName)(data);
|
|
if (tempRet !== undefined) data.returnValue = tempRet;
|
|
}
|
|
else {
|
|
if (before) Utilities.suppressErrors(before, "`before` callback of " + what[methodName].displayName)(data);
|
|
data.callOriginalMethod();
|
|
if (after) Utilities.suppressErrors(after, "`after` callback of " + what[methodName].displayName)(data);
|
|
}
|
|
if (once) cancel();
|
|
return data.returnValue;
|
|
};
|
|
what[methodName].__monkeyPatched = true;
|
|
if (!what[methodName].__originalMethod) what[methodName].__originalMethod = origMethod;
|
|
what[methodName].displayName = "patched " + (what[methodName].displayName || methodName);
|
|
return cancel;
|
|
}
|
|
|
|
static onRemoved(node, callback) {
|
|
const observer = new MutationObserver((mutations) => {
|
|
for (let m = 0; m < mutations.length; m++) {
|
|
const mutation = mutations[m];
|
|
const nodes = Array.from(mutation.removedNodes);
|
|
const directMatch = nodes.indexOf(node) > -1;
|
|
const parentMatch = nodes.some(parent => parent.contains(node));
|
|
if (directMatch || parentMatch) {
|
|
observer.disconnect();
|
|
callback();
|
|
}
|
|
}
|
|
});
|
|
|
|
observer.observe(document.body, {subtree: true, childList: true});
|
|
}
|
|
|
|
/**
|
|
* Generates an automatically memoizing version of an object.
|
|
* @author Zerebos
|
|
* @param {Object} object - object to memoize
|
|
* @returns {Proxy} the proxy to the object that memoizes properties
|
|
*/
|
|
static memoizeObject(object) {
|
|
const proxy = new Proxy(object, {
|
|
get: function(obj, mod) {
|
|
if (!obj.hasOwnProperty(mod)) return undefined;
|
|
if (Object.getOwnPropertyDescriptor(obj, mod).get) {
|
|
const value = obj[mod];
|
|
delete obj[mod];
|
|
obj[mod] = value;
|
|
}
|
|
return obj[mod];
|
|
},
|
|
set: function(obj, mod, value) {
|
|
if (obj.hasOwnProperty(mod)) return this.err("MemoizedObject", "Trying to overwrite existing property");
|
|
obj[mod] = value;
|
|
return obj[mod];
|
|
}
|
|
});
|
|
|
|
Object.defineProperty(proxy, "hasOwnProperty", {value: function(prop) {
|
|
return this[prop] !== undefined;
|
|
}});
|
|
|
|
return proxy;
|
|
}
|
|
|
|
/**
|
|
* Builds a classname string from any number of arguments. This includes arrays and objects.
|
|
* When given an array all values from the array are added to the list.
|
|
* When given an object they keys are added as the classnames if the value is truthy.
|
|
* Copyright (c) 2018 Jed Watson https://github.com/JedWatson/classnames MIT License
|
|
* @param {...Any} argument - anything that should be used to add classnames.
|
|
*/
|
|
static className() {
|
|
const classes = [];
|
|
const hasOwn = {}.hasOwnProperty;
|
|
|
|
for (let i = 0; i < arguments.length; i++) {
|
|
const arg = arguments[i];
|
|
if (!arg) continue;
|
|
|
|
const argType = typeof arg;
|
|
|
|
if (argType === "string" || argType === "number") {
|
|
classes.push(arg);
|
|
}
|
|
else if (Array.isArray(arg) && arg.length) {
|
|
const inner = this.classNames.apply(null, arg);
|
|
if (inner) {
|
|
classes.push(inner);
|
|
}
|
|
}
|
|
else if (argType === "object") {
|
|
for (const key in arg) {
|
|
if (hasOwn.call(arg, key) && arg[key]) {
|
|
classes.push(key);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return classes.join(" ");
|
|
}
|
|
} |