Reddit Watcher

Consolidated mutation observer handler for use by other reddit userscripts. By itself doesn't do much, but other scripts and use hooks to avoid redundant observers on the same thing.

Från och med 2022-03-08. Se den senaste versionen.

// ==UserScript==
// @name         Reddit Watcher
// @namespace    https://lawrenzo.com/p/reddit-watcher
// @description  Consolidated mutation observer handler for use by other reddit userscripts. By itself doesn't do much, but other scripts and use hooks to avoid redundant observers on the same thing.
// @version      1.0.0
// @author       Lawrence Sim
// @license      WTFPL (http://www.wtfpl.net)
// @grant        none
// @match        *://*.reddit.com/*
// ==/UserScript==
(function() {
    if(window && !window.redditWatcher) {
        var debug = false;
        function watcherAbstract(getFunc, opts, name) {
            return {
                name: name || "unnamed",
                defaultOpts: opts || {childList:true},
                listeners: {
                    update: [],
                    change: []
                },
                onUpdate: function(task) {
                    this.listeners.update.push(task);
                },
                update: function(mutated) {
                    if(debug) console.log(this.name + " watcher updated");
                    this.listeners.update.forEach(task => task(this.last, mutated));
                },
                onChange: function(task) {
                   this.listeners.change.push(task);
                },
                changed: function() {
                    if(debug) console.log(this.name + " watcher changed");
                    this.listeners.change.forEach(task => task(this.last));
                },
                last: null,
                watcher: null,
                get: getFunc,
                reset: function() {
                    if(debug) console.log(this.name + " watcher reset");
                    if(this.watcher) this.watcher.disconnect();
                    this.update();
                    this.watcher = new MutationObserver(this.update.bind(this));
                    this.observe(this.last, this.defaultOpts);
                },
                observe: function(elem, opts) {
                    if(this.watcher) this.watcher.observe(elem, opts);
                },
                check: function() {
                    if(debug) console.log(this.name + " watcher checked");
                    let latest = this.get();
                    if(latest && latest !== this.last) {
                        this.last = latest;
                        this.changed();
                        this.reset();
                        return true;
                    }
                }
            };
        }
        var firstPass = true,
            lastFirstPost = null,
            feedWatcher = watcherAbstract(
                function() {
                    let listing = document.querySelector(".ListingLayout-outerContainer"),
                        firstPost = listing.querySelector("div[data-testid='post-container']"),
                        feed = firstPost && firstPost.parentNode;
                    if(!firstPass && firstPost !== lastFirstPost) {
                        this.changed();
                        lastFirstPost = firstPost;
                    }
                    firstPass = false;
                    while(feed && !feed.nextSibling) {
                        if(feed == listing) return null;
                        feed = feed.parentNode || null;
                    }
                    return feed && feed.parentNode;
                },
                {childList:true},
                "feed"
            ),
            listingWatcher = watcherAbstract(
                () => document.querySelector(".ListingLayout-outerContainer"),
                {childList:true, subtree:true},
                "listing"
            ),
            bodyWatcher = watcherAbstract(
                () => document.body,
                {childList:true, subtree:true},
                "body"
            );
        listingWatcher.onChange(() => feedWatcher.check());
        listingWatcher.onUpdate(() => feedWatcher.check());
        bodyWatcher.onChange(() => listingWatcher.check());
        bodyWatcher.onUpdate(() => listingWatcher.check());
        bodyWatcher.check();
        window.redditWatcher = {
            body: bodyWatcher,
            listing: listingWatcher,
            feed: feedWatcher,
            update: function() { this.body.update(); }
        };
    }
})();