Stepik Invisible Character Button

Adds invisible character button to Stepik comment editor

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==UserScript==
// @name         Stepik Invisible Character Button
// @namespace    https://stepik.org/
// @version      1.0
// @description  Adds invisible character button to Stepik comment editor
// @match        https://stepik.org/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    const INVISIBLE_CHAR = '\u200B'; // Zero-width space

    function insertInvisibleChar() {
        const editor = document.querySelector('.rich-text-editor__content[contenteditable="true"]');
        if (!editor) return;

        // Insert invisible character at cursor position
        const selection = window.getSelection();
        const range = selection.getRangeAt(0);

        const textNode = document.createTextNode(INVISIBLE_CHAR);
        range.insertNode(textNode);

        // Move cursor after inserted character
        range.setStartAfter(textNode);
        range.setEndAfter(textNode);
        selection.removeAllRanges();
        selection.addRange(range);

        // Trigger input events to enable submit button
        editor.dispatchEvent(new Event('input', { bubbles: true }));
        editor.dispatchEvent(new Event('change', { bubbles: true }));
        editor.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true }));

        // Focus editor
        editor.focus();
    }

    function createButton() {
        const btn = document.createElement('a');
        btn.id = 'invisible-char-btn';
        btn.className = 'cke_button cke_button_off';
        btn.title = 'Insert Invisible Character';
        btn.tabIndex = '-1';
        btn.setAttribute('role', 'button');
        btn.style.cursor = 'pointer';

        const icon = document.createElement('span');
        icon.className = 'cke_button_icon';
        icon.style.cssText = 'background: none; width: 16px; height: 16px; display: inline-block;';
        icon.innerHTML = '⧉';
        icon.style.fontSize = '16px';
        icon.style.lineHeight = '16px';

        const label = document.createElement('span');
        label.className = 'cke_button_label';
        label.textContent = 'Invisible Char';
        label.style.display = 'none';

        btn.appendChild(icon);
        btn.appendChild(label);

        btn.addEventListener('click', (e) => {
            e.preventDefault();
            insertInvisibleChar();
        });

        return btn;
    }

    function addButton() {
        const toolgroup = document.querySelector('.cke_toolgroup');
        if (!toolgroup) {
            setTimeout(addButton, 500);
            return;
        }

        if (document.getElementById('invisible-char-btn')) return;

        const btn = createButton();
        toolgroup.appendChild(btn);
    }

    // Wait for editor to load
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', () => setTimeout(addButton, 1000));
    } else {
        setTimeout(addButton, 1000);
    }

    // Re-add button if comment widget reloads
    const observer = new MutationObserver(() => {
        if (document.querySelector('.cke_toolgroup') && !document.getElementById('invisible-char-btn')) {
            addButton();
        }
    });

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