Reddit Watcher

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

Fra 09.03.2022. Se den seneste versjonen.

// ==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 can use hooks to avoid redundant observers watching 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(); }
        };
    }
})();