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
Tag: Reverted
Restored revision 446174 by Tar (talk)
Tags: twinkle Undo
 
Line 1: Line 1:
(function () {
(function () {
     'use strict';
     'use strict';


    if (!['edit', 'submit'].includes(mw.config.get('wgAction'))) {
     function isOnFilePage() {
        return;
         const path = location.pathname;
    }
         const params = new URLSearchParams(location.search);
 
    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 ? 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 (
         return (
             a &&
             /^\/File:/.test(path) ||
            b &&
             /^\/Special:Moderation/i.test(path) ||
             a.type === b.type &&
          /^\/Special:Moderation/i.test(path) ||
            a.query === b.query &&
             (
             a.start.line === b.start.line &&
                params.get("title") === "Special:Moderation" &&
            a.start.ch === b.start.ch &&
                params.get("modaction") === "show"
            a.end.line === b.end.line &&
             )
             a.end.ch === b.end.ch
         );
         );
     }
     }


     function hideMenu() {
     function extractImageData(img, callback) {
         menu.hidden = true;
         const tmpImg = new Image();
         menu.replaceChildren();
        tmpImg.crossOrigin = 'anonymous';
 
         tmpImg.onload = function () {
        activeCompletion = null;
            const canvas = document.createElement('canvas');
         results = [];
            canvas.width = tmpImg.width;
         selectedIndex = 0;
            canvas.height = tmpImg.height;
            const ctx = canvas.getContext('2d');
            ctx.drawImage(tmpImg, 0, 0);
            callback(ctx, canvas);
         };
         tmpImg.src = img.dataset.originalSrc;
     }
     }


     function positionMenu() {
     function revealLSB(imageData, bits) {
         if (!codeMirror) {
         const data = imageData.data;
             return;
        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;
         }
         }
        const pos = codeMirror.cursorCoords(null, 'page');
        menu.style.left = pos.left + 'px';
        menu.style.top = pos.bottom + 'px';
     }
     }


     function setSelectedIndex(index) {
     function decodeLSB(imageData, maxChars = 5000) {
         const items = menu.querySelectorAll(
         const pixels = imageData.data;
             '.username-autocomplete-result'
        const bits = [];
         );
        for (let i = 0; i < pixels.length; i += 4) {
 
             bits.push(pixels[i] & 1, pixels[i + 1] & 1, pixels[i + 2] & 1);
        if (!items.length) {
        }
             return;
        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;
         selectedIndex = (index + items.length) % items.length;
 
        items.forEach((item, i) => {
            item.classList.toggle(
                'selected',
                i === selectedIndex
            );
        });
     }
     }


     function insertResult(value) {
     function addStegUIForImg(img) {
         if (!codeMirror || !activeCompletion) {
         if (!img || img.dataset.stegscanInit) return;
            return;
        }


         const format =
         img.dataset.stegscanInit = '1';
            activeCompletion.type === 'emoji'
        img.dataset.originalSrc = img.dataset.originalSrc || img.src;
                ? config.emojiInsertFormat
                : config.usernameInsertFormat;


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


         codeMirror.replaceRange(
         const label = document.createElement('div');
            insertion,
        label.textContent = 'Steganography Scan:';
            activeCompletion.start,
         Object.assign(label.style, {
            activeCompletion.end,
             fontSize: '12px',
            '+autocomplete'
            fontWeight: '600',
        );
             marginBottom: '4px'
 
         codeMirror.setCursor({
             line: activeCompletion.start.line,
             ch: activeCompletion.start.ch + insertion.length
         });
         });


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


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


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


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


            empty.className = 'username-autocomplete-empty';
        container.appendChild(label);
            empty.textContent =
        container.appendChild(slider);
                activeCompletion.type === 'emoji'
        container.appendChild(buttonWrapper);
                    ? 'No matching emojis'
                    : 'No matching users';


            menu.appendChild(empty);
        const parent = img.parentNode;
         } else {
         (parent.tagName.toLowerCase() === 'a' ? parent : img)
            values.forEach((value, index) => {
            .insertAdjacentElement('afterend', container);
                const item = document.createElement('div');


                item.className = 'username-autocomplete-result';
        let lastBits = 1;


                if (index === 0) {
        slider.addEventListener('input', () => {
                    item.classList.add('selected');
            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();
            });
        });


                if (activeCompletion.type === 'emoji') {
        decodeBtn.addEventListener('click', () => {
                    const img = document.createElement('img');
            decodeBtn.disabled = true;
 
            decodeBtn.textContent = 'Decoding...';
                    img.src =
            extractImageData(img, (ctx, canvas) => {
                        mw.config.get('wgScriptPath') +
                const data = ctx.getImageData(0, 0, canvas.width, canvas.height);
                         '/index.php?title=Special:Redirect/file/' +
                const message = decodeLSB(data);
                        encodeURIComponent( 'Nuuru ' + value.toLowerCase() + '.png')
                decodeBtn.textContent = 'Decode';
 
                decodeBtn.disabled = false;
                    img.alt = value;
                if (message) {
 
                    navigator.clipboard.writeText(message)
                    item.append(img, document.createTextNode(' ' + value));
                         .then(() => alert('Decoded message copied to clipboard'))
                        .catch(() => prompt('Decoded message:', message));
                 } else {
                 } else {
                     item.textContent = value;
                     alert('No message detected.');
                 }
                 }
                item.addEventListener(
                    'mouseenter',
                    () => setSelectedIndex(index)
                );
                item.addEventListener(
                    'mousedown',
                    event => {
                        event.preventDefault();
                        insertResult(value);
                    }
                );
                menu.appendChild(item);
             });
             });
         }
         });
 
        menu.hidden = false;
        positionMenu();
     }
     }


     function searchUsers(cm, completion) {
     function scanAllImages() {
         getApi()
         if (!isOnFilePage()) return; // Only run on File pages or media viewer
            .then(function (api) {
        const content = document.querySelector('#content');
                return api.get({
        if (!content) return;
                    action: 'query',
        content.querySelectorAll('img').forEach(img => {
                    list: 'allusers',
             // Skip file history thumbnails
                    auprefix: completion.query,
             if (img.closest('.filehistory')) return;
                    aulimit: config.maximumResults,
            addStegUIForImg(img);
                    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) {
     scanAllImages();
        const current = getCompletion(cm);


        if (!completionsMatch(current, completion)) {
    const observer = new MutationObserver(() => {
            return;
         if (isOnFilePage()) scanAllImages();
        }
         else observer.disconnect();
 
     });
        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':
    observer.observe(document.body, { childList: true, subtree: true });
                    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();
        }
    });
}());

Latest revision as of 04:07, 14 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 });

})();