data-manager2

process html, store and filter data

Fra og med 06.09.2025. Se den nyeste version.

Dette script bør ikke installeres direkte. Det er et bibliotek, som andre scripts kan inkludere med metadirektivet // @require https://update.greasyforks.org/scripts/548610/1656007/data-manager2.js

// ==UserScript==
// @name         data-manager2
// @namespace    Violentmonkey Scripts
// @version      1.5
// @license      MIT
// @description  process html, store and filter data
// @author       smartacephale
// @match        *://*/*
// @grant        unsafeWindow
// @grant        GM_addStyle
// @downloadURL https://update.greasyforks.org/scripts/494204/data-manager.user.js
// @updateURL https://update.greasyforks.org/scripts/494204/data-manager.meta.js
// ==/UserScript==

class DataFilter {
    constructor(rules, state) {
        this.state = state;
        this.rules = rules;

        const methods = Object.getOwnPropertyNames(this);
        this.filters = methods.reduce((acc, k) => {
            if (k in this.state) {
                acc[k] = this[k];
                GM_addStyle(`.filter-${k.toLowerCase().slice(6)} { display: none !important; }`);
            }
            return acc;
        }, {});
    }

    filterPublic = () => {
        return (v) => {
            const isPublic = !this.rules.IS_PRIVATE(v.element);
            return {
                tag: 'filter-public',
                condition: this.state.filterPublic && isPublic
            };
        }
    }

    filterPrivate = () => {
        return (v) => {
            const isPrivate = this.rules.IS_PRIVATE(v.element);
            return {
                tag: 'filter-private',
                condition: this.state.filterPrivate && isPrivate
            };
        }
    }

    filterDuration = () => {
        return (v) => {
            const notInRange = v.duration < this.state.filterDurationFrom || v.duration > this.state.filterDurationTo;
            return {
                tag: 'filter-duration',
                condition: this.state.filterDuration && notInRange
            };
        }
    }

    filterExclude = () => {
        const tags = DataManager.filterDSLToRegex(this.state.filterExcludeWords);
        return (v) => {
            const containTags = tags.some(tag => tag.test(v.title));
            return {
                tag: 'filter-exclude',
                condition: this.state.filterExclude && containTags
            };
        }
    }

    filterInclude = () => {
        const tags = DataManager.filterDSLToRegex(this.state.filterIncludeWords);
        return (v) => {
            const containTagsNot = tags.some(tag => !tag.test(v.title));
            return {
                tag: 'filter-include',
                condition: this.state.filterInclude && containTagsNot
            };
        }
    }
}

class DataManager {
    constructor(rules, state) {
        this.rules = rules;
        this.state = state;
        this.data = new Map();
        this.lazyImgLoader = new unsafeWindow.bhutils.LazyImgLoader((target) => !this.isFiltered(target));
        this.dataFilters = new DataFilter(rules, state).filters;
    }

    static filterDSLToRegex(str) {
        const toFullWord = w => `(^|\ )${w}($|\ )`;
        const str_ = str.replace(/f\:(\w+)/g, (_, w) => toFullWord(w));
        return unsafeWindow.bhutils.stringToWords(str_).map(expr => new RegExp(expr, 'i'));
    }

    isFiltered(el) {
        return el.className.includes('filtered');
    }

    applyFilters = (filters, offset = 0) => {
        const filtersToApply = Object.keys(filters)
            .filter(k => Object.hasOwn(this.dataFilters, k))
            .map(k => this.dataFilters[k]());

        if (filtersToApply.length === 0) return;

        let updates = [];
        let offset_counter = 1;
        for (const v of this.data.values()) {
            if (++offset_counter > offset) {
                for (const f of filtersToApply) {
                    const {tag, condition} = f(v);
                    updates.push(() => v.element.classList.toggle(tag, condition));
                }
            }
        }

        requestAnimationFrame(() => {
            updates.forEach(update => update());
        });
    }

    filterAll = (offset) => {
        const filters = Object.assign({}, ...Object.keys(this.dataFilters).map(f => ({ [f]: this.state[f] })));
        this.applyFilters(filters, offset);
    }

    handleLoadedHTML = (html, container, removeDuplicates = false, shouldLazify = true) => {
        const thumbs = this.rules.GET_THUMBS(html);
        const data_offset = this.data.size;

        for (const thumbElement of thumbs) {
            const url = this.rules.THUMB_URL(thumbElement);
            if (!url || this.data.has(url)) {
                if (removeDuplicates) thumbElement.remove();
                continue;
            }

            const { title, duration } = this.rules.THUMB_DATA(thumbElement);
            this.data.set(url, { element: thumbElement, duration, title });

            if (shouldLazify) {
                const { img, imgSrc } = this.rules.THUMB_IMG_DATA(thumbElement);
                this.lazyImgLoader.lazify(thumbElement, img, imgSrc);
            }

            const parent = container || this.rules.CONTAINER;
            if (!parent.contains(thumbElement)) parent.appendChild(thumbElement);
        }

        this.filterAll(data_offset);
    };
}
长期地址
遇到问题?请前往 GitHub 提 Issues。