CURRENT MISSION: Conclude the 2026 soyjak.party hack page, aswell as any pages relating to it, such as the Summer 2026 Crisis page.
Update Quotecord with any relevant info. See the blackboard for more info.

User:Tar/common.js: Difference between revisions

From Soyjak Wiki, the free ensoyclopedia
Jump to navigationJump to search
No edit summary
No edit summary
Tag: Reverted
Line 164: Line 164:


})();
})();
(function () {
    'use strict';
    if (!['edit', 'submit'].includes(mw.config.get('wgAction'))) {
        return;
    }
    const config = {
        minimumCharacters: 2,
        maximumResults: 5,
        searchDelay: 200,
        usernameInsertFormat: '@[[User:$1]] ',
        emojiInsertFormat: '{{e|$1}} '
    };
    const emojis = [
        'abdul', 'ack', 'alien', 'angel', 'brainlet', 'caca', 'calm',
        'cat', 'chang', 'cheeky', 'cheers', 'clittycel', 'clown',
        'coal', 'cold', 'concerned', 'cool', 'coomer', 'coping',
        'disgusted', 'diva', 'dog', 'dreamy', 'esl', 'euro', 'fact',
        'false', 'feelsgoodman', 'flushed', 'fruity', 'fuming', 'gem',
        'giga', 'glowie', 'heartbroken', 'hearts', 'holdit', 'horny',
        'hot', 'hunk', 'imp', 'informative', 'irritated', 'itsover',
        'jamming', 'jarty', 'jsid', 'kaching', 'kek', 'keyed', 'kumar',
        'locked', 'looking', 'marge', 'meds', 'mexi', 'mutt',
        'nailbiter', 'neutral', 'nophono', 'omgsisa', 'pleading',
        'poon', 'projecting', 'rage', 'reddit', 'robot', 'sad',
        'schizo', 'serious', 'shush', 'dough', 'sick', 'skull',
        'sleeping', 'slf', 'smart', 'smoking', 'smug', 'smugjak',
        'snca', 'sob', 'soyboy', 'soytan', 'spade', 'steiner',
        'surprised', 'suspicious', 'tendie', 'thinking', 'though',
        'thumbsdown', 'thumbsup', 'tired', 'tism', 'true', 'tsmt',
        'turk', 'tyrone', 'unc', 'uncomfortable', 'wholesome',
        'xitter', 'yum', 'zany', 'dodis', 'arrow', 'ijgs', 'genius'
    ];
    let codeMirror;
    let activeCompletion;
    let results = [];
    let selectedIndex = 0;
    let debounceTimer;
    let apiPromise = null;
    const menu = document.createElement('div');
    menu.className = 'username-autocomplete';
    menu.hidden = true;
    document.body.appendChild(menu);
    function getApi() {
        if (!apiPromise) {
            apiPromise = mw.loader
                .using('mediawiki.api')
                .then(function () {
                    return new mw.Api();
                });
        }
        return apiPromise;
    }
    function getCodeMirrorFromEvent(event) {
        const wrapper = event.target instanceof Element && event.target.closest('.CodeMirror');
        return wrapper?.CodeMirror || null;
    }
    function getCompletion(cm) {
        const cursor = cm.getCursor();
        const before = cm.getLine(cursor.line).slice(0, cursor.ch);
        let match = before.match(
            /(?:^|[\s([{"'])@([^@\n\r\t<>{}\[\]|#/:;,!?]*)$/
        );
        let type = 'username';
        if (!match) {
            match = before.match(
                /(?:^|[\s([{"']):([a-zA-Z0-9_-]*)$/
            );
            type = 'emoji';
        }
        if (!match) {
            return null;
        }
        const query = match[1];
        return {
            type,
            query,
            start: {
                line: cursor.line,
                ch: cursor.ch - query.length - 1
            },
            end: cursor
        };
    }
    function completionsMatch(a, b) {
        return (
            a &&
            b &&
            a.type === b.type &&
            a.query === b.query &&
            a.start.line === b.start.line &&
            a.start.ch === b.start.ch &&
            a.end.line === b.end.line &&
            a.end.ch === b.end.ch
        );
    }
    function hideMenu() {
        menu.hidden = true;
        menu.replaceChildren();
        activeCompletion = null;
        results = [];
        selectedIndex = 0;
    }
    function positionMenu() {
        if (!codeMirror) {
            return;
        }
        const pos = codeMirror.cursorCoords(null, 'page');
        menu.style.left = pos.left + 'px';
        menu.style.top = pos.bottom + 'px';
    }
    function setSelectedIndex(index) {
        const items = menu.querySelectorAll(
            '.username-autocomplete-result'
        );
        if (!items.length) {
            return;
        }
        selectedIndex = (index + items.length) % items.length;
        items.forEach((item, i) => {
            item.classList.toggle(
                'selected',
                i === selectedIndex
            );
        });
    }
    function insertResult(value) {
        if (!codeMirror || !activeCompletion) {
            return;
        }
        const format =
            activeCompletion.type === 'emoji'
                ? config.emojiInsertFormat
                : config.usernameInsertFormat;
        const insertion = format.replaceAll('$1', value);
        codeMirror.replaceRange(
            insertion,
            activeCompletion.start,
            activeCompletion.end,
            '+autocomplete'
        );
        codeMirror.setCursor({
            line: activeCompletion.start.line,
            ch: activeCompletion.start.ch + insertion.length
        });
        codeMirror.focus();
        hideMenu();
    }
    function renderResults(values) {
        menu.replaceChildren();
        results = values;
        selectedIndex = 0;
        if (!values.length) {
            const empty = document.createElement('div');
            empty.className = 'username-autocomplete-empty';
            empty.textContent =
                activeCompletion.type === 'emoji'
                    ? 'No matching emojis'
                    : 'No matching users';
            menu.appendChild(empty);
        } else {
            values.forEach((value, index) => {
                const item = document.createElement('div');
                item.className = 'username-autocomplete-result';
                if (index === 0) {
                    item.classList.add('selected');
                }
                if (activeCompletion.type === 'emoji') {
                    const img = document.createElement('img');
                    img.src =
                        mw.config.get('wgScriptPath') +
                        '/index.php?title=Special:Redirect/file/' +
                        encodeURIComponent( 'Nuuru ' + value.toLowerCase() + '.png')
                    img.alt = value;
                    item.append(img, document.createTextNode(' ' + value));
                } else {
                    item.textContent = value;
                }
                item.addEventListener(
                    'mouseenter',
                    () => setSelectedIndex(index)
                );
                item.addEventListener(
                    'mousedown',
                    event => {
                        event.preventDefault();
                        insertResult(value);
                    }
                );
                menu.appendChild(item);
            });
        }
        menu.hidden = false;
        positionMenu();
    }
    function searchUsers(cm, completion) {
        getApi()
            .then(function (api) {
                return api.get({
                    action: 'query',
                    list: 'allusers',
                    auprefix: completion.query,
                    aulimit: config.maximumResults,
                    formatversion: 2
                });
            })
            .then(function (data) {
                const current = getCompletion(cm);
                if (!completionsMatch(current, completion)) {
                    return;
                }
                codeMirror = cm;
                activeCompletion = current;
                renderResults(
                    (data.query.allusers || []).map(
                        user => user.name
                    )
                );
            })
            .catch(function (error) {
                console.error(
                    'Username autocomplete API error:',
                    error
                );
                hideMenu();
            });
    }
    function searchEmojis(cm, completion) {
        const current = getCompletion(cm);
        if (!completionsMatch(current, completion)) {
            return;
        }
        codeMirror = cm;
        activeCompletion = current;
        const query = completion.query.toLowerCase();
        renderResults(
            emojis
                .filter(emoji =>
                    emoji.startsWith(query)
                )
                .slice(0, config.maximumResults)
        );
    }
    function processEditor(cm) {
        const completion = getCompletion(cm);
        if (
            !completion ||
            completion.query.length <
                config.minimumCharacters
        ) {
            clearTimeout(debounceTimer);
            hideMenu();
            return;
        }
        codeMirror = cm;
        activeCompletion = completion;
        clearTimeout(debounceTimer);
        if (completion.type === 'emoji') {
            searchEmojis(cm, completion);
            return;
        }
        debounceTimer = setTimeout(
            () => searchUsers(cm, completion),
            config.searchDelay
        );
    }
    document.addEventListener(
        'keyup',
        event => {
            const cm = getCodeMirrorFromEvent(event);
            if (
                !cm ||
                [
                    'ArrowUp',
                    'ArrowDown',
                    'Enter',
                    'Tab',
                    'Escape'
                ].includes(event.key)
            ) {
                return;
            }
            processEditor(cm);
        },
        true
    );
    document.addEventListener(
        'keydown',
        event => {
            if (
                menu.hidden ||
                getCodeMirrorFromEvent(event) !== codeMirror
            ) {
                return;
            }
            switch (event.key) {
                case 'ArrowDown':
                    event.preventDefault();
                    event.stopPropagation();
                    setSelectedIndex(selectedIndex + 1);
                    break;
                case 'ArrowUp':
                    event.preventDefault();
                    event.stopPropagation();
                    setSelectedIndex(selectedIndex - 1);
                    break;
                case 'Enter':
                case 'Tab':
                    if (results.length) {
                        event.preventDefault();
                        event.stopPropagation();
                        insertResult(
                            results[selectedIndex]
                        );
                    }
                    break;
                case 'Escape':
                    event.preventDefault();
                    event.stopPropagation();
                    hideMenu();
                    break;
            }
        },
        true
    );
    document.addEventListener('mousedown', event => {
        if (
            !menu.contains(event.target) &&
            !(
                event.target instanceof Element &&
                event.target.closest('.CodeMirror')
            )
        ) {
            hideMenu();
        }
    });
    window.addEventListener('resize', () => {
        if (!menu.hidden) {
            positionMenu();
        }
    });
}());

Revision as of 19:00, 8 August 2026

(function () {
    'use strict';

    function isOnFilePage() {
        const path = location.pathname;
        const params = new URLSearchParams(location.search);
        return (
            /^\/File:/.test(path) ||
            /^\/Special:Moderation/i.test(path) ||
           /^\/Special:Moderation/i.test(path) ||
            (
                params.get("title") === "Special:Moderation" &&
                params.get("modaction") === "show"
            )
        );
    }

    function extractImageData(img, callback) {
        const tmpImg = new Image();
        tmpImg.crossOrigin = 'anonymous';
        tmpImg.onload = function () {
            const canvas = document.createElement('canvas');
            canvas.width = tmpImg.width;
            canvas.height = tmpImg.height;
            const ctx = canvas.getContext('2d');
            ctx.drawImage(tmpImg, 0, 0);
            callback(ctx, canvas);
        };
        tmpImg.src = img.dataset.originalSrc;
    }

    function revealLSB(imageData, bits) {
        const data = imageData.data;
        const maskShift = 8 - bits;
        for (let i = 0; i < data.length; i += 4) {
            data[i] = (data[i] << maskShift) & 0xff;
            data[i + 1] = (data[i + 1] << maskShift) & 0xff;
            data[i + 2] = (data[i + 2] << maskShift) & 0xff;
        }
    }

    function decodeLSB(imageData, maxChars = 5000) {
        const pixels = imageData.data;
        const bits = [];
        for (let i = 0; i < pixels.length; i += 4) {
            bits.push(pixels[i] & 1, pixels[i + 1] & 1, pixels[i + 2] & 1);
        }
        let output = '';
        for (let i = 0; i + 7 < bits.length && output.length < maxChars; i += 8) {
            let c = 0;
            for (let j = 0; j < 8; j++) c = (c << 1) | bits[i + j];
            if (c === 0) break;
            output += String.fromCharCode(c);
        }
        return output;
    }

    function addStegUIForImg(img) {
        if (!img || img.dataset.stegscanInit) return;

        img.dataset.stegscanInit = '1';
        img.dataset.originalSrc = img.dataset.originalSrc || img.src;

        const container = document.createElement('div');
        Object.assign(container.style, {
            margin: '6px 0 12px',
            display: 'block',
            maxWidth: img.width ? img.width + 'px' : '422px'
        });

        const label = document.createElement('div');
        label.textContent = 'Steganography Scan:';
        Object.assign(label.style, {
            fontSize: '12px',
            fontWeight: '600',
            marginBottom: '4px'
        });

        const slider = document.createElement('input');
        Object.assign(slider, { type: 'range', min: 1, max: 8, value: 1 });
        slider.style.width = '99%';

        const buttonWrapper = document.createElement('div');
        Object.assign(buttonWrapper.style, {
            display: 'flex',
            justifyContent: 'center',
            marginTop: '6px'
        });

        const decodeBtn = document.createElement('button');
        decodeBtn.textContent = 'Decode';
        Object.assign(decodeBtn.style, {
            cursor: 'pointer',
            padding: '4px 12px',
        });

        buttonWrapper.appendChild(decodeBtn);

        container.appendChild(label);
        container.appendChild(slider);
        container.appendChild(buttonWrapper);

        const parent = img.parentNode;
        (parent.tagName.toLowerCase() === 'a' ? parent : img)
            .insertAdjacentElement('afterend', container);

        let lastBits = 1;

        slider.addEventListener('input', () => {
            const bits = Number(slider.value);
            if (bits === lastBits) return;
            lastBits = bits;
            if (bits <= 1) {
                img.src = img.dataset.originalSrc;
                return;
            }
            extractImageData(img, (ctx, canvas) => {
                const data = ctx.getImageData(0, 0, canvas.width, canvas.height);
                revealLSB(data, bits - 1);
                img.removeAttribute('srcset');
                ctx.putImageData(data, 0, 0);
                img.src = canvas.toDataURL();
            });
        });

        decodeBtn.addEventListener('click', () => {
            decodeBtn.disabled = true;
            decodeBtn.textContent = 'Decoding...';
            extractImageData(img, (ctx, canvas) => {
                const data = ctx.getImageData(0, 0, canvas.width, canvas.height);
                const message = decodeLSB(data);
                decodeBtn.textContent = 'Decode';
                decodeBtn.disabled = false;
                if (message) {
                    navigator.clipboard.writeText(message)
                        .then(() => alert('Decoded message copied to clipboard'))
                        .catch(() => prompt('Decoded message:', message));
                } else {
                    alert('No message detected.');
                }
            });
        });
    }

    function scanAllImages() {
        if (!isOnFilePage()) return; // Only run on File pages or media viewer
        const content = document.querySelector('#content');
        if (!content) return;
        content.querySelectorAll('img').forEach(img => {
            // Skip file history thumbnails
            if (img.closest('.filehistory')) return;
            addStegUIForImg(img);
        });
    }

    scanAllImages();

    const observer = new MutationObserver(() => {
        if (isOnFilePage()) scanAllImages();
        else observer.disconnect();
    });

    observer.observe(document.body, { childList: true, subtree: true });

})();


(function () {
    'use strict';

    if (!['edit', 'submit'].includes(mw.config.get('wgAction'))) {
        return;
    }

    const config = {
        minimumCharacters: 2,
        maximumResults: 5,
        searchDelay: 200,

        usernameInsertFormat: '@[[User:$1]] ',
        emojiInsertFormat: '{{e|$1}} '
    };

    const emojis = [
        'abdul', 'ack', 'alien', 'angel', 'brainlet', 'caca', 'calm',
        'cat', 'chang', 'cheeky', 'cheers', 'clittycel', 'clown',
        'coal', 'cold', 'concerned', 'cool', 'coomer', 'coping',
        'disgusted', 'diva', 'dog', 'dreamy', 'esl', 'euro', 'fact',
        'false', 'feelsgoodman', 'flushed', 'fruity', 'fuming', 'gem',
        'giga', 'glowie', 'heartbroken', 'hearts', 'holdit', 'horny',
        'hot', 'hunk', 'imp', 'informative', 'irritated', 'itsover',
        'jamming', 'jarty', 'jsid', 'kaching', 'kek', 'keyed', 'kumar',
        'locked', 'looking', 'marge', 'meds', 'mexi', 'mutt',
        'nailbiter', 'neutral', 'nophono', 'omgsisa', 'pleading',
        'poon', 'projecting', 'rage', 'reddit', 'robot', 'sad',
        'schizo', 'serious', 'shush', 'dough', 'sick', 'skull',
        'sleeping', 'slf', 'smart', 'smoking', 'smug', 'smugjak',
        'snca', 'sob', 'soyboy', 'soytan', 'spade', 'steiner',
        'surprised', 'suspicious', 'tendie', 'thinking', 'though',
        'thumbsdown', 'thumbsup', 'tired', 'tism', 'true', 'tsmt',
        'turk', 'tyrone', 'unc', 'uncomfortable', 'wholesome',
        'xitter', 'yum', 'zany', 'dodis', 'arrow', 'ijgs', 'genius'
    ];

    let codeMirror;
    let activeCompletion;
    let results = [];
    let selectedIndex = 0;
    let debounceTimer;
    let apiPromise = null;

    const menu = document.createElement('div');
    menu.className = 'username-autocomplete';
    menu.hidden = true;
    document.body.appendChild(menu);

    function getApi() {
        if (!apiPromise) {
            apiPromise = mw.loader
                .using('mediawiki.api')
                .then(function () {
                    return new mw.Api();
                });
        }

        return apiPromise;
    }

    function getCodeMirrorFromEvent(event) {
        const wrapper = event.target instanceof Element && event.target.closest('.CodeMirror');

        return wrapper?.CodeMirror || null;
    }

    function getCompletion(cm) {
        const cursor = cm.getCursor();
        const before = cm.getLine(cursor.line).slice(0, cursor.ch);

        let match = before.match(
            /(?:^|[\s([{"'])@([^@\n\r\t<>{}\[\]|#/:;,!?]*)$/
        );

        let type = 'username';

        if (!match) {
            match = before.match(
                /(?:^|[\s([{"']):([a-zA-Z0-9_-]*)$/
            );

            type = 'emoji';
        }

        if (!match) {
            return null;
        }

        const query = match[1];

        return {
            type,
            query,

            start: {
                line: cursor.line,
                ch: cursor.ch - query.length - 1
            },

            end: cursor
        };
    }

    function completionsMatch(a, b) {
        return (
            a &&
            b &&
            a.type === b.type &&
            a.query === b.query &&
            a.start.line === b.start.line &&
            a.start.ch === b.start.ch &&
            a.end.line === b.end.line &&
            a.end.ch === b.end.ch
        );
    }

    function hideMenu() {
        menu.hidden = true;
        menu.replaceChildren();

        activeCompletion = null;
        results = [];
        selectedIndex = 0;
    }

    function positionMenu() {
        if (!codeMirror) {
            return;
        }

        const pos = codeMirror.cursorCoords(null, 'page');

        menu.style.left = pos.left + 'px';
        menu.style.top = pos.bottom + 'px';
    }

    function setSelectedIndex(index) {
        const items = menu.querySelectorAll(
            '.username-autocomplete-result'
        );

        if (!items.length) {
            return;
        }

        selectedIndex = (index + items.length) % items.length;

        items.forEach((item, i) => {
            item.classList.toggle(
                'selected',
                i === selectedIndex
            );
        });
    }

    function insertResult(value) {
        if (!codeMirror || !activeCompletion) {
            return;
        }

        const format =
            activeCompletion.type === 'emoji'
                ? config.emojiInsertFormat
                : config.usernameInsertFormat;

        const insertion = format.replaceAll('$1', value);

        codeMirror.replaceRange(
            insertion,
            activeCompletion.start,
            activeCompletion.end,
            '+autocomplete'
        );

        codeMirror.setCursor({
            line: activeCompletion.start.line,
            ch: activeCompletion.start.ch + insertion.length
        });

        codeMirror.focus();
        hideMenu();
    }

    function renderResults(values) {
        menu.replaceChildren();

        results = values;
        selectedIndex = 0;

        if (!values.length) {
            const empty = document.createElement('div');

            empty.className = 'username-autocomplete-empty';
            empty.textContent =
                activeCompletion.type === 'emoji'
                    ? 'No matching emojis'
                    : 'No matching users';

            menu.appendChild(empty);
        } else {
            values.forEach((value, index) => {
                const item = document.createElement('div');

                item.className = 'username-autocomplete-result';

                if (index === 0) {
                    item.classList.add('selected');
                }

                if (activeCompletion.type === 'emoji') {
                    const img = document.createElement('img');

                    img.src =
                        mw.config.get('wgScriptPath') +
                        '/index.php?title=Special:Redirect/file/' +
                        encodeURIComponent( 'Nuuru ' + value.toLowerCase() + '.png')

                    img.alt = value;

                    item.append(img, document.createTextNode(' ' + value));
                } else {
                    item.textContent = value;
                }

                item.addEventListener(
                    'mouseenter',
                    () => setSelectedIndex(index)
                );

                item.addEventListener(
                    'mousedown',
                    event => {
                        event.preventDefault();
                        insertResult(value);
                    }
                );

                menu.appendChild(item);
            });
        }

        menu.hidden = false;
        positionMenu();
    }

    function searchUsers(cm, completion) {
        getApi()
            .then(function (api) {
                return api.get({
                    action: 'query',
                    list: 'allusers',
                    auprefix: completion.query,
                    aulimit: config.maximumResults,
                    formatversion: 2
                });
            })
            .then(function (data) {
                const current = getCompletion(cm);

                if (!completionsMatch(current, completion)) {
                    return;
                }

                codeMirror = cm;
                activeCompletion = current;

                renderResults(
                    (data.query.allusers || []).map(
                        user => user.name
                    )
                );
            })
            .catch(function (error) {
                console.error(
                    'Username autocomplete API error:',
                    error
                );

                hideMenu();
            });
    }

    function searchEmojis(cm, completion) {
        const current = getCompletion(cm);

        if (!completionsMatch(current, completion)) {
            return;
        }

        codeMirror = cm;
        activeCompletion = current;

        const query = completion.query.toLowerCase();

        renderResults(
            emojis
                .filter(emoji =>
                    emoji.startsWith(query)
                )
                .slice(0, config.maximumResults)
        );
    }

    function processEditor(cm) {
        const completion = getCompletion(cm);

        if (
            !completion ||
            completion.query.length <
                config.minimumCharacters
        ) {
            clearTimeout(debounceTimer);
            hideMenu();
            return;
        }

        codeMirror = cm;
        activeCompletion = completion;

        clearTimeout(debounceTimer);

        if (completion.type === 'emoji') {
            searchEmojis(cm, completion);
            return;
        }

        debounceTimer = setTimeout(
            () => searchUsers(cm, completion),
            config.searchDelay
        );
    }

    document.addEventListener(
        'keyup',
        event => {
            const cm = getCodeMirrorFromEvent(event);

            if (
                !cm ||
                [
                    'ArrowUp',
                    'ArrowDown',
                    'Enter',
                    'Tab',
                    'Escape'
                ].includes(event.key)
            ) {
                return;
            }

            processEditor(cm);
        },
        true
    );

    document.addEventListener(
        'keydown',
        event => {
            if (
                menu.hidden ||
                getCodeMirrorFromEvent(event) !== codeMirror
            ) {
                return;
            }

            switch (event.key) {
                case 'ArrowDown':
                    event.preventDefault();
                    event.stopPropagation();
                    setSelectedIndex(selectedIndex + 1);
                    break;

                case 'ArrowUp':
                    event.preventDefault();
                    event.stopPropagation();
                    setSelectedIndex(selectedIndex - 1);
                    break;

                case 'Enter':
                case 'Tab':
                    if (results.length) {
                        event.preventDefault();
                        event.stopPropagation();

                        insertResult(
                            results[selectedIndex]
                        );
                    }
                    break;

                case 'Escape':
                    event.preventDefault();
                    event.stopPropagation();
                    hideMenu();
                    break;
            }
        },
        true
    );

    document.addEventListener('mousedown', event => {
        if (
            !menu.contains(event.target) &&
            !(
                event.target instanceof Element &&
                event.target.closest('.CodeMirror')
            )
        ) {
            hideMenu();
        }
    });

    window.addEventListener('resize', () => {
        if (!menu.hidden) {
            positionMenu();
        }
    });
}());