MDK Trim

Trim automatiquement les espaces au début et à la fin d'une sélection lors d'un double-clic.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         MDK Trim
// @namespace    MDK Scripts
// @version      2026.05.27
// @description  Trim automatiquement les espaces au début et à la fin d'une sélection lors d'un double-clic.
// @author       MDK
// @license      MIT
// @match        *://*/*
// @exclude      *://docs.google.com/*
// @exclude      *://*.notion.so/*
// @exclude      *://*.figma.com/*
// @exclude      *://*.canva.com/*
// @exclude      *://*.miro.com/*
// @exclude      *://*.vscode.dev/*
// @exclude      *://github.dev/*
// @exclude      *://*.codepen.io/*
// @exclude      *://*.stackblitz.com/*
// @exclude      *://*.teams.microsoft.com/*
// @exclude      *://*.office.com/*
// @icon         data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='48' height='48' viewBox='0 0 24 24' fill='none' stroke='%23007bff' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><circle cx='6' cy='6' r='3'/><circle cx='6' cy='18' r='3'/><line x1='8.12' y1='8.12' x2='20' y2='20'/><line x1='20' y1='4' x2='8.12' y2='15.88'/></svg>
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    const TEXT_INPUT_TYPES = ['text', 'search', 'url', 'tel', 'email', 'password'];
    let lastDblClick = 0;

    function getTrimOffsets(text) {
        const start = text.match(/^\s+/u)?.[0].length || 0;
        const end = text.match(/\s+$/u)?.[0].length || 0;
        return { start, end };
    }

    function isTextInput(el) {
        return el && 
               !el.disabled && 
               (el.tagName === 'TEXTAREA' || (el.tagName === 'INPUT' && TEXT_INPUT_TYPES.includes(el.type)));
    }

    function getDeepActiveElement() {
        let el = document.activeElement;
        while (el && el.shadowRoot && el.shadowRoot.activeElement) {
            el = el.shadowRoot.activeElement;
        }
        return el;
    }

    document.addEventListener('dblclick', (event) => {
        const now = performance.now();
        // Ignore le script si la touche Alt (ou Option sur Mac) est enfoncée
        if (now - lastDblClick < 50 || event.altKey) return;
        lastDblClick = now;

        setTimeout(() => {
            const activeEl = getDeepActiveElement();

            // 1. INPUT / TEXTAREA
            if (isTextInput(activeEl)) {
                const start = activeEl.selectionStart;
                const end = activeEl.selectionEnd;
                if (start === end) return;

                const text = activeEl.value.substring(start, end);
                if (!text) return;

                if (text.trim() === '') {
                    activeEl.setSelectionRange(start, start);
                    return;
                }

                const { start: s, end: e } = getTrimOffsets(text);
                if (!s && !e) return;

                activeEl.setSelectionRange(start + s, end - e);
                return;
            }

            // 2. TEXTE HTML & CONTENTEDITABLE
            const selection = window.getSelection();
            if (!selection || selection.rangeCount === 0 || selection.type === 'Caret') return;

            const text = selection.toString();
            if (!text) return;

            const range = selection.getRangeAt(0);

            if (text.trim() === '') {
                range.collapse(true);
                return;
            }

            const { start: s, end: e } = getTrimOffsets(text);
            if (!s && !e) return;

            try {
                if (range.startContainer.nodeType === Node.TEXT_NODE && (range.startOffset + s) <= range.startContainer.length) {
                    range.setStart(range.startContainer, range.startOffset + s);
                }
                if (range.endContainer.nodeType === Node.TEXT_NODE && (range.endOffset - e) >= 0) {
                    range.setEnd(range.endContainer, range.endOffset - e);
                }
                selection.removeAllRanges();
                selection.addRange(range);
            } catch (err) {
                // Échec silencieux
            }
        }, 0);
    });
})();