2018-03-08 09:40:29 +01:00
|
|
|
/**
|
|
|
|
* BetterDiscord Reflection Module
|
|
|
|
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
|
|
|
|
* All rights reserved.
|
|
|
|
* https://betterdiscord.net
|
|
|
|
*
|
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
|
|
|
*/
|
|
|
|
|
2018-04-27 16:51:59 +02:00
|
|
|
import { Filters } from 'modules';
|
2018-03-17 22:42:16 +01:00
|
|
|
import { ClientLogger as Logger } from 'common';
|
|
|
|
|
2018-03-08 09:59:51 +01:00
|
|
|
class Reflection {
|
2018-03-08 09:40:29 +01:00
|
|
|
static reactInternalInstance(node) {
|
2018-03-08 22:40:38 +01:00
|
|
|
if (!node) return null;
|
2018-03-08 09:40:29 +01:00
|
|
|
if (!Object.keys(node) || !Object.keys(node).length) return null;
|
|
|
|
const riiKey = Object.keys(node).find(k => k.startsWith('__reactInternalInstance'));
|
|
|
|
return riiKey ? node[riiKey] : null;
|
|
|
|
}
|
|
|
|
|
|
|
|
static findProp(node, prop) {
|
|
|
|
const ii = this.reactInternalInstance(node);
|
|
|
|
if (!ii) return null;
|
|
|
|
const fir = this.findInReturn(ii, prop);
|
|
|
|
if (fir) return fir;
|
2018-03-10 13:27:17 +01:00
|
|
|
const fim = this.findInChildProps(ii, prop);
|
|
|
|
if (fim) return fim;
|
2018-03-08 09:40:29 +01:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
static findInReturn(internalInstance, prop) {
|
|
|
|
const r = internalInstance.return;
|
|
|
|
if (!r) return null;
|
2018-03-08 18:30:21 +01:00
|
|
|
let find = this.findMemoizedProp(r, prop);
|
2018-03-08 09:40:29 +01:00
|
|
|
if (find) return find;
|
2018-03-08 18:30:21 +01:00
|
|
|
find = this.findMemoizedState(r, prop);
|
|
|
|
if (find) return find;
|
|
|
|
return this.findInReturn(r, prop);
|
2018-03-08 09:40:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static findMemoizedProp(obj, prop) {
|
|
|
|
if (!obj.hasOwnProperty('memoizedProps')) return null;
|
|
|
|
obj = obj.memoizedProps;
|
|
|
|
return this.findPropIn(obj, prop);
|
|
|
|
}
|
|
|
|
|
|
|
|
static findMemoizedState(obj, prop) {
|
|
|
|
if (!obj.hasOwnProperty('memoizedState')) return null;
|
|
|
|
obj = obj.memoizedState;
|
|
|
|
return this.findPropIn(obj, prop);
|
|
|
|
}
|
|
|
|
|
2018-03-10 13:27:17 +01:00
|
|
|
static findInChildProps(obj, prop) {
|
|
|
|
try {
|
|
|
|
const f = obj.children || obj.memoizedProps.children;
|
|
|
|
if (!f.props) return null;
|
|
|
|
if (!f.props.hasOwnProperty(prop)) return null;
|
|
|
|
return f.props[prop];
|
|
|
|
} catch (err) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-08 09:40:29 +01:00
|
|
|
static findPropIn(obj, prop) {
|
|
|
|
if (obj && !(obj instanceof Array) && obj instanceof Object && obj.hasOwnProperty(prop)) return obj[prop];
|
|
|
|
if (obj && obj instanceof Array) {
|
|
|
|
const found = obj.find(mp => {
|
|
|
|
if (mp.props && mp.props.hasOwnProperty(prop)) return true;
|
|
|
|
});
|
|
|
|
if (found) return found;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
2018-03-08 10:17:33 +01:00
|
|
|
|
|
|
|
static propIterator(obj, propNames) {
|
|
|
|
if (obj === null || obj === undefined) return null;
|
|
|
|
const curPropName = propNames.shift(1);
|
|
|
|
if (!obj.hasOwnProperty(curPropName)) return null;
|
|
|
|
const curProp = obj[curPropName];
|
|
|
|
if (propNames.length === 0) {
|
|
|
|
return curProp;
|
|
|
|
}
|
|
|
|
return this.propIterator(curProp, propNames);
|
|
|
|
}
|
2018-03-13 23:34:03 +01:00
|
|
|
|
|
|
|
static getState(node) {
|
2018-03-14 08:12:00 +01:00
|
|
|
try {
|
|
|
|
return this.reactInternalInstance(node).return.stateNode.state;
|
|
|
|
} catch (err) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-17 22:35:09 +01:00
|
|
|
static getStateNode(node) {
|
|
|
|
try {
|
|
|
|
return this.reactInternalInstance(node).return.stateNode;
|
|
|
|
} catch (err) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-27 16:51:59 +02:00
|
|
|
static getComponent(node) {
|
|
|
|
return this.getComponents(node)[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
static getComponents(node) {
|
|
|
|
const instance = this.reactInternalInstance(node);
|
|
|
|
const components = [];
|
|
|
|
let lastInstance = instance;
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (lastInstance.return.type) components.push(lastInstance.return.type);
|
|
|
|
lastInstance = lastInstance.return;
|
|
|
|
if (typeof lastInstance.return.type === 'string') return components;
|
|
|
|
} while (lastInstance.return);
|
|
|
|
|
|
|
|
return components;
|
|
|
|
}
|
|
|
|
|
|
|
|
static findComponent(node, filter, first = true) {
|
|
|
|
return this.getComponents(node)[first ? 'find' : 'filter'](filter);
|
2018-03-13 23:34:03 +01:00
|
|
|
}
|
2018-03-08 09:40:29 +01:00
|
|
|
}
|
2018-03-08 09:59:51 +01:00
|
|
|
|
2018-04-27 16:51:59 +02:00
|
|
|
const propsProxyHandler = {
|
|
|
|
get(node, prop) {
|
|
|
|
return Reflection.findProp(node, prop);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-03-08 09:59:51 +01:00
|
|
|
export default function (node) {
|
2018-03-08 10:18:21 +01:00
|
|
|
return new class {
|
2018-03-08 09:59:51 +01:00
|
|
|
constructor(node) {
|
2018-04-27 16:51:59 +02:00
|
|
|
if (typeof node === 'string') node = document.querySelector(node);
|
2018-03-08 09:59:51 +01:00
|
|
|
this.node = this.el = this.element = node;
|
|
|
|
}
|
|
|
|
get props() {
|
2018-04-27 16:51:59 +02:00
|
|
|
return new Proxy(this.node, propsProxyHandler);
|
2018-03-08 09:59:51 +01:00
|
|
|
}
|
2018-03-13 23:34:03 +01:00
|
|
|
get state() {
|
|
|
|
return Reflection.getState(this.node);
|
|
|
|
}
|
2018-03-17 22:35:09 +01:00
|
|
|
get stateNode() {
|
|
|
|
return Reflection.getStateNode(this.node);
|
|
|
|
}
|
2018-03-08 09:59:51 +01:00
|
|
|
get reactInternalInstance() {
|
|
|
|
return Reflection.reactInternalInstance(this.node);
|
|
|
|
}
|
2018-03-14 08:12:00 +01:00
|
|
|
get component() {
|
|
|
|
return Reflection.getComponent(this.node);
|
|
|
|
}
|
2018-04-27 16:51:59 +02:00
|
|
|
get components() {
|
|
|
|
return Reflection.getComponents(this.node);
|
|
|
|
}
|
|
|
|
getComponentByProps(props, selector) {
|
|
|
|
return Reflection.findComponent(this.node, Filters.byProperties(props, selector));
|
|
|
|
}
|
|
|
|
getComponentByPrototypes(props, selector) {
|
|
|
|
return Reflection.findComponent(this.node, Filters.byPrototypeFields(props, selector));
|
|
|
|
}
|
|
|
|
getComponentByRegex(filter) {
|
|
|
|
return Reflection.findComponent(this.node, Filters.byCode(displayName));
|
|
|
|
}
|
|
|
|
getComponentByDisplayName(name) {
|
|
|
|
return Reflection.findComponent(this.node, Filters.byDisplayName(name));
|
|
|
|
}
|
2018-03-17 22:35:09 +01:00
|
|
|
forceUpdate() {
|
|
|
|
try {
|
|
|
|
const stateNode = Reflection.getStateNode(this.node);
|
|
|
|
if (!stateNode || !stateNode.forceUpdate) return;
|
|
|
|
stateNode.forceUpdate();
|
|
|
|
} catch (err) {
|
|
|
|
Logger.err('Reflection', err);
|
|
|
|
}
|
|
|
|
}
|
2018-03-08 09:59:51 +01:00
|
|
|
prop(propName) {
|
2018-03-08 10:17:33 +01:00
|
|
|
const split = propName.split('.');
|
|
|
|
const first = Reflection.findProp(this.node, split[0]);
|
|
|
|
if (split.length === 1) return first;
|
|
|
|
return Reflection.propIterator(first, split.slice(1));
|
2018-03-08 09:59:51 +01:00
|
|
|
}
|
|
|
|
}(node);
|
|
|
|
}
|