Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // @flow
- /**
- * Renders text to the DOM and notifies when it is mutated outside of React
- *
- * This component renders a piece of text, then uses the MutationObserver API
- * to watch if that text is changed (for instance, by a user with the browser
- * devtools open). It then can optionally fire a callback or attempt to revert
- * the change. This is intended to be used as a rudimentary anti-cheating
- * measure for students who are trying to "fake" progress on KA for their
- * parent/teacher/battle school squad commander by messing with the browser.
- *
- * A quick warning: this component will detect mutations that are made by
- * userscripts and browser extensions, such as the Google Translate extension.
- * There are legitimate reasons users may want to transform the DOM of the page
- * (machine translation being an obvious one) so this component should be used
- * on the smallest possible piece of text that will produce the desired effect.
- * In other words, you should prefer this:
- *
- * You are <MutationWatcher value={"55%"}/> done with all math ever
- *
- * over this:
- *
- * <MutationWatcher value={"You are 55% done with all math ever"}/>
- *
- * However, breaking strings up this way can have a negative impact on
- * translation clarity -- another reason to use this component sparingly.
- */
- import React from "react";
- type Props = {|
- // The text to display
- value: string,
- // Will fire when the element contents are mutated
- onMutate?: (tag: string, originalVal: string, mutatedVal: string) => any,
- // Whether to revert the DOM to the way it was before the mutation
- revertMutations: boolean,
- // A description of the content that is included in the callback
- tag: string,
- // Type of element to wrap with -- defaults to "span"
- wrapperType: string,
- // Props to pass to the wrapper element
- wrapperProps: {},
- // Whether to mark a mutate_dom_attempt conversion
- markConversion: boolean,
- |};
- type State = {};
- type DefaultProps = {
- wrapperType: string,
- wrapperProps: {},
- markConversion: boolean,
- };
- const markMutateDOMAttempt = (
- tag: string,
- originalValue: string,
- modifiedValue: string,
- ) => markConversion("mutate_dom_attempt", {tag, originalValue, modifiedValue});
- class MutationWatcher extends React.Component<DefaultProps, Props, State> {
- props: Props;
- _observer: MutationObserver;
- _node: Element;
- static defaultProps = {
- revertMutations: false,
- wrapperProps: {},
- wrapperType: "span",
- markConversion: false,
- };
- state = {};
- componentDidMount() {
- if (window.MutationObserver) {
- this._observer = new MutationObserver(
- this.handleValueMutated.bind(this),
- );
- this._observer.observe(this._node, {
- characterData: true,
- subtree: true,
- });
- }
- }
- componentWillUnmount() {
- if (this._observer) {
- this._observer.disconnect();
- }
- }
- handleValueMutated() {
- const {
- markConversion,
- onMutate,
- revertMutations,
- tag,
- value,
- } = this.props;
- const domValue = this._node.textContent;
- if (domValue && value !== domValue) {
- if (onMutate) {
- onMutate(tag, value, domValue);
- }
- if (markConversion) {
- markMutateDOMAttempt(tag, value, domValue);
- }
- if (revertMutations) {
- this._node.textContent = value;
- }
- }
- }
- render() {
- const {value, wrapperType, wrapperProps} = this.props;
- return React.createElement(
- wrapperType,
- {
- ...wrapperProps,
- ref: ref => (this._node = ref),
- },
- value,
- );
- }
- }
- export default MutationWatcher;
Add Comment
Please, Sign In to add comment