Unedit and Undelete for Reddit

Creates the option next to edited and deleted Reddit comments/posts to show the original comment from before it was edited

当前为 2022-06-07 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Unedit and Undelete for Reddit
// @namespace    http://tampermonkey.net/
// @version      3.7.0
// @description  Creates the option next to edited and deleted Reddit comments/posts to show the original comment from before it was edited
// @author       u/DenverCoder1
// @match        *://*reddit.com/*
// @include      https://*.reddit.com/*
// @include      https://reddit.com/*
// @grant        none
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/showdown.min.js
// @license      MIT
// ==/UserScript==

/* jshint esversion: 8 */

(function () {
    "use strict";

    /* check if website is an old reddit url or has an old reddit image header */
    var isOldReddit = /old\.reddit/.test(window.location.href) || !!document.querySelector("#header-img");

    /* timeout to check for new edited comments on page */
    var scriptTimeout = null;

    /* variable to store the element that is currently requesting content */
    var currentLoading;

    /* initialize showdown markdown converter */
    var mdConverter = new showdown.Converter();

    /* find the id of a comment */
    function getId(e, old) {
        var id = "";
        try {
            if (!old) {
                var comment = e?.closest(".Comment") || e?.closest("[class*=t1_]");
                id = Array.from(comment.classList).filter(function (x) {
                    return x.indexOf("_") > -1;
                })[0];
            } else {
                id = e.parentElement.parentElement.parentElement.id;
                /* old reddit submission */
                if (id === "" && isInSubmission(e)) {
                    id = window.location.href.match(/comments\/([A-Za-z0-9]{5,8})\//)[1];
                } else {
                /* old reddit comment */
                    id = id.split("_").slice(1).join("_");
                }
                if (id === "") {
                    id = e.parentElement.parentElement
                        .getElementsByClassName("reportform")[0]
                        .className.replace(/.*t1/, "t1");
                }
            }
        } catch (error) {
            return null;
        }
        return id;
    }

    /* get the container of the comment body */
    function getCommentBodyElement(id, old) {
        var el = null;
        try {
            /* redesign */
            if (!old) {
                var baseEl = document.getElementById(id);
                if (baseEl) {
                    if (baseEl.getElementsByClassName("RichTextJSON-root").length > 0)
                        el = baseEl.getElementsByClassName("RichTextJSON-root")[0];
                    else el = baseEl;
                }
                baseEl = document.querySelector(".Comment." + id);
                if (!el && baseEl) {
                    if (baseEl.getElementsByClassName("RichTextJSON-root").length > 0)
                        el = baseEl.getElementsByClassName("RichTextJSON-root")[0];
                    else el = baseEl.firstElementChild.lastElementChild;
                }
            } else {
            /* old reddit */
                if (document.querySelector("form[id*=" + id + "] div.md")) {
                    el = document.querySelector("form[id*=" + id + "] div.md");
                }
                if (!el) {
                    el = document.querySelector(".report-" + id).parentElement.parentElement;
                }
            }
        } catch (error) {
            return null;
        }
        return el;
    }

    /* check if surrounding elements imply element is in a selftext submission */
    function isInSubmission(e) {
        return e.parentElement.parentElement.className == "top-matter";
    }

    /* check that elements bounds are within the windows bounds */
    function isInViewport(e) {
        var rect = e.getBoundingClientRect();
        return (
            rect.top >= 0 &&
            rect.left >= 0 &&
            rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
            rect.right <= (window.innerWidth || document.documentElement.clientWidth)
        );
    }

    /* create new paragraph containing the body of the original comment/post */
    function showOriginalComment(x, commentBodyElement, postType, body) {
        /* create paragraph element */
        var origBody = document.createElement("p");
        origBody.className = "og";
        /* set text */
        origBody.innerHTML = mdConverter.makeHtml("\n\n### Original " + postType + ":\n\n" + body);
        /* paragraph styling */
        origBody.style.opacity = 0.96;
        origBody.style.fontSize = "14px";
        origBody.style.background = "#ffed4c5c";
        origBody.style.padding = "16px";
        origBody.style.color = "inherit";
        origBody.style.lineHeight = "20px";
        commentBodyElement.appendChild(origBody);
        /* scroll into view */
        setTimeout(function () {
            if (!isInViewport(origBody)) {
                origBody.scrollIntoView({ behavior: "smooth" });
            }
        }, 500);
    }

    /* create links and define click event */
    function createLink(x) {
        /* create link to "Show orginal" */
        var l = document.createElement("a");
        l.innerText = "Show original";
        l.className = x.className + " showOriginal";
        l.style.textDecoration = "underline";
        l.style.cursor = "pointer";
        l.style.marginLeft = "6px";
        x.parentElement.appendChild(l);
        x.className += " found";
        /* click event */
        l.addEventListener(
            "click",
            async function () {
                /* allow only 1 request at a time */
                if (typeof currentLoading != "undefined" && currentLoading !== null) {
                    return;
                }
                /* find id of selected comment */
                var id = getId(this, isOldReddit);
                /* create url for getting comment/post from pushshift api */
                var idURL = isInSubmission(this)
                    ? "https://api.pushshift.io/reddit/search/submission/?ids=" +
                      id +
                      "&sort=desc&sort_type=created_utc&fields=selftext,author,id"
                    : "https://api.pushshift.io/reddit/search/comment/?ids=" +
                      id +
                      "&sort=desc&sort_type=created_utc&fields=body,author,id,link_id";
                /* create url for getting author comments/posts from pushshift api */
                var author = this.parentElement.querySelector("a[href*=user]")?.innerText;
                var authorURL = isInSubmission(this)
                    ? "https://api.pushshift.io/reddit/search/submission/?author=" +
                      author +
                      "&size=200&sort=desc&sort_type=created_utc&fields=selftext,author,id"
                    : "https://api.pushshift.io/reddit/search/comment/?author=" +
                      author +
                      "&size=200&sort=desc&sort_type=created_utc&fields=body,author,id,link_id";

                /* set loading status */
                currentLoading = this;
                this.innerHTML = "loading...";

                /* request from pushshift api */
                await Promise.all([
                    fetch(idURL)
                        .then((resp) => resp.json())
                        .catch((error) => {
                            console.error("Error:", error);
                        }),
                    fetch(authorURL)
                        .then((resp) => resp.json())
                        .catch((error) => {
                            console.error("Error:", error);
                        }),
                ])
                    .then((responses) => {
                        responses.forEach((out) => {
                            /* locate the comment that was being loaded */
                            var loading = currentLoading;
                            // exit if already found
                            if (loading.innerHTML === "") {
                                return;
                            }
                            /* locate comment body */
                            var id = getId(loading, isOldReddit);
                            var commentBodyElement = getCommentBodyElement(id, isOldReddit);
                            var post = out?.data?.find((post) => post?.id === id?.split("_").pop());
                            console.log({ author, id, post });
                            /* check that comment was fetched and body element exists */
                            if (commentBodyElement && post?.body) {
                                /* create new paragraph containing the body of the original comment */
                                showOriginalComment(x, commentBodyElement, "comment", post.body);
                                /* remove loading status from comment */
                                loading.innerHTML = "";
                            } else if (commentBodyElement && post?.selftext) {
                            /* check if result has selftext instead of body (it is a submission post) */
                                /* create new paragraph containing the selftext of the original submission */
                                showOriginalComment(x, commentBodyElement, "post", post.selftext);
                                /* remove loading status from post */
                                loading.innerHTML = "";
                            } else if (out?.data?.length === 0) {
                            /* data was not returned or returned empty */
                                loading.innerHTML = "not found";
                                console.log("id: " + id);
                                console.log(out);
                            } else {
                            /* other issue occurred with displaying comment */
                                loading.innerHTML = "fetch failed";
                                console.log("id: " + id);
                                console.log(out);
                            }
                        });
                    })
                    .catch(function (err) {
                        throw err;
                    });

                /* reset status */
                currentLoading = null;
            },
            false
        );
    }

    /* locate comments and call function to add links to each */
    function findEditedComments() {
        /* when function runs, cancel timeout */
        if (scriptTimeout) {
            scriptTimeout = null;
        }
        /* list of comments which have been edited */
        var editedComments = [];
        /* Redesign */
        if (!isOldReddit) {
            /* fix styling of created paragraphs in new reddit */
            document.head.insertAdjacentHTML(
                "beforeend",
                "<style>p.og pre { font-family: monospace; background: #ffffff50; padding: 6px; margin: 6px 0; } p.og h1 { font-size: 2em; } p.og h2 { font-size: 1.5em; } p.og h3 { font-size: 1.17em; } p.og h4 { font-size: 1em; } p.og h5 { font-size: 0.83em; } p.og h6 { font-size: 0.67em; } p.og a { color: lightblue; text-decoration: underline; }</style>"
            );
            /* edited comments */
            editedComments = Array.from(document.querySelectorAll(".Comment div span")).filter(function (x, y, z) {
                return x.parentElement.querySelector("a.showOriginal") === null && x.innerText.substr(0, 6) == "edited";
            });
            /* include deleted comments by user */
            editedComments = editedComments.concat(
                Array.from(document.querySelectorAll(".Comment div span")).filter(function (x, y, z) {
                    return (
                        x.parentElement.querySelector("a.showOriginal") === null &&
                        x.innerText.substr(0, 15) == "Comment deleted"
                    );
                })
            );
            /* include deleted comments by moderator */
            editedComments = editedComments.concat(
                Array.from(document.querySelectorAll(".Comment div span")).filter(function (x, y, z) {
                    return (
                        x.parentElement.querySelector("a.showOriginal") === null &&
                        x.innerText.substr(0, 15) == "Comment removed"
                    );
                })
            );
        } else {
            /* Old Reddit */
            /* edited comments and submissions */
            editedComments = Array.from(document.querySelectorAll("time")).filter(function (x, y, z) {
                return Array.from(x.classList).indexOf("found") < 0 && x.title.substr(0, 11) == "last edited";
            });
            /* removed comments */
            editedComments = editedComments.concat(
                Array.from(document.querySelectorAll("em")).filter(function (x, y, z) {
                    return Array.from(x.classList).indexOf("found") < 0 && x.innerText === "[deleted]";
                })
            );
        }
        /* create links */
        editedComments.forEach(function (x, y, z) {
            createLink(x);
        });
    }

    /* check for new comments when you scroll */
    window.addEventListener(
        "scroll",
        function () {
            if (!scriptTimeout) {
                scriptTimeout = setTimeout(findEditedComments, 1000);
            }
        },
        true
    );

    findEditedComments();
})();