LaTeX Unicode Shortcuts

Highlight text then press [ALT+X] to convert LaTeX commands to their unicode equivalent (ex. \pi → π)

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

Vous devrez installer une extension telle que Tampermonkey 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         LaTeX Unicode Shortcuts
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Highlight text then press [ALT+X] to convert LaTeX commands to their unicode equivalent (ex. \pi → π)
// @author       eyl327
// @match        *://*/*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    var convert;

    var dictLoaded = false;

    /* source url for shortcut file */
    var dictionarySource = "https://raw.githubusercontent.com/eyl327/LaTeX-Gboard-Dictionary/master/dictionary.txt";

    /* fetch text file when requested */
    function loadAsset(url, callback) {
        var xhr = new XMLHttpRequest();
        xhr.open("GET", url, false);
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (xhr.status === 200 || xhr.status === 0) {
                    callback(xhr.responseText);
                }
            }
        }
        xhr.send();
    }

    /* on dictionary loaded callback */
    function loaded(response) {
        console.log("LaTeX Unicode Shortcuts has been loaded.");
        /* generate dictionary from text file */
        var dictArr = response.split("\n").slice(1);
        var dictionary = {};
        for (var i = 0, len = dictArr.length; i < len; ++i) {
            var kvp = dictArr[i].split("\t");
            dictionary[kvp[0]] = kvp[1];
        }
        /* conversion function */
        convert = function (text) {
            var result = text.replace(/{([A-Za-z0-9])}/g, '$1'); // {R} => R
            for (var key in dictionary) {
                var pattern = new RegExp(key.replace(/([[^$.|\\?*+(){}])/g, '\\$1') + "\\b", 'g'); // clean and escape key
                var replaced = result.replace(pattern, dictionary[key]);
                if (replaced.length < result.length) {
                    result = replaced;
                }
            }
            return result;
        };
        dictLoaded = true;
    }

    /* get caret position within input box */
    function getCaretPositionInputBox(el) {
        if ("selectionStart" in el && document.activeElement == el) {
            return {
                start: el.selectionStart,
                end: el.selectionEnd
            };
        }
        else if (el.createTextRange) {
            var sel = document.selection.createRange();
            if (sel.parentElement() === el) {
                var range = el.createTextRange();
                range.moveToBookmark(sel.getBookmark());
                for (var len = 0;
                    range.compareEndPoints("EndToStart", range) > 0;
                    range.moveEnd("character", -1)) {
                    len++;
                }
                range.setEndPoint("StartToStart", el.createTextRange());
                for (var pos = { start: 0, end: len };
                    range.compareEndPoints("EndToStart", range) > 0;
                    range.moveEnd("character", -1)) {
                    pos.start++;
                    pos.end++;
                }
                return pos;
            }
        }
        return -1;
    }

    /* set caret position within input box */
    function setCaretPosition(el, pos) {
        if (el.setSelectionRange) {
            el.focus();
            el.setSelectionRange(pos, pos);
        }
        else if (el.createTextRange) {
            var range = el.createTextRange();
            range.collapse(true);
            range.moveEnd('character', pos);
            range.moveStart('character', pos);
            range.select();
        }
    }

    function overwriteInputBoxText(activeEl, before, convertedText, after) {
        // overwrite text
        activeEl.value = before + convertedText + after;
        // set cursor to be at end of selection
        setCaretPosition(activeEl, before.length + convertedText.length);
    }

    function replaceConversionInElement(activeEl, fullText, start, end) {
        var textToConvert = fullText.substring(start, end);
        var before = fullText.substring(0, start);
        var after = fullText.substring(end, fullText.length);
        // convert selection
        var convertedText = convert(textToConvert);
        if ("value" in activeEl) {
            overwriteInputBoxText(activeEl, before, convertedText, after);
        }
    }

    /* convert hilighted text in active element */
    function convertSelectionInputBox(activeEl) {
        var caretRange = getCaretPositionInputBox(activeEl);
        var selStart = caretRange.start;
        var selEnd = caretRange.end;
        var fullText = activeEl.value;
        /* if selection is empty, find word at caret */
        if (selStart == selEnd) {
            // Find beginning and end of word
            var left = fullText.slice(0, selStart + 1).search(/\S+$/);
            var right = fullText.slice(selStart).search(/(\s|$)/);
            /* convert the word at the caret selection */
            replaceConversionInElement(activeEl, fullText, left, right + selStart)
        }
        /* else convert the selection */
        else {
            replaceConversionInElement(activeEl, fullText, selStart, selEnd);
        }
    }

    /* convert hilighted text in active element */
    function convertSelectionContentEditable(element) {
        var NodeTree = {
            // Used to find all DOM nodes in window.getSelection()
            getInnerNodes: function (anchor, focus) {
                var ancestor = NodeTree.lowestCommonAncestor(anchor, focus);
                var childList = NodeTree.findChildrenList(ancestor);
                var [i, j] = [childList.indexOf(anchor), childList.indexOf(focus)].sort();
                return childList.slice(i, j + 1);
            },
            getNodeChain: function (node) {
                var chain = [];
                chain.push(node);
                while (node.parentNode) {
                    node = node.parentNode;
                    chain.push(node);
                }
                return chain.reverse();
            },
            lowestCommonAncestor: function (anchor, focus) {
                var uChain = NodeTree.getNodeChain(anchor);
                var vChain = NodeTree.getNodeChain(focus);
                var i;
                for (i = 0; i < uChain.length; i++) {
                    if (uChain[i] !== vChain[i]) {
                        break
                    }
                }
                return uChain[i - 1]
            },
            findChildrenList: function (node) {
                var list = []
                var find = function (n) {
                    if (!n) {
                        return;
                    }
                    list.push(n);
                    for (var child of Array.from(n.childNodes || [])) {
                        find(child);
                    }
                }
                find(node);
                return list;
            }
        }

        var sel = element.ownerDocument.getSelection();

        var selAN = sel.anchorNode;
        var selFN = sel.focusNode;

        var nodesBetweenNodes = NodeTree.getInnerNodes(selAN, selFN);

        var startNode = nodesBetweenNodes[0];
        var endNode = nodesBetweenNodes[nodesBetweenNodes.length - 1];

        var selAO = sel.anchorOffset;
        var selFO = sel.focusOffset;

        var [startCursor, endCursor] = (startNode === selAN && selAO <= selFO) ? [selAO, selFO] : [selFO, selAO];

        var cursor;

        for (var node of nodesBetweenNodes) {
            if (node.nodeType === 3) { // 3 = text type
                var selStart = (node === nodesBetweenNodes[0]) ? startCursor : 0;
                var selEnd = (node === nodesBetweenNodes[nodesBetweenNodes.length - 1]) ? endCursor : node.nodeValue.length;
                var text = node.nodeValue;
                selEnd = Math.min(text.length, selEnd);

                var convertStart = selStart;
                var convertEnd = selEnd;

                // cursor is not a hilighted selection
                if (selStart == selEnd) {
                    // Find beginning and end of word
                    convertStart = text.slice(0, selStart + 1).search(/\S+$/);
                    convertEnd = text.slice(selEnd).search(/(\s|$)/) + selStart;
                }

                /* convert the word at the caret selection */
                var textToConvert = text.substring(convertStart, convertEnd);
                var before = text.substring(0, convertStart);
                var after = text.substring(convertEnd, text.length);
                var convertedText = convert(textToConvert);

                // replace in element
                var result = before + convertedText + after;
                cursor = Math.min(result.length, before.length + convertedText.length);
                node.nodeValue = result;
            }
        }
        sel.collapse(endNode, cursor)
    }

    /* detect ALT+X keyboard shortcut */
    function enableLaTeXShortcuts(event) {
        if (event.altKey && event.keyCode == 88) { // ALT+X
            // load dictionary when first pressed
            if (!dictLoaded) {
                loadAsset(dictionarySource, loaded);
            }
            // convert selection
            var activeEl = document.activeElement;
            var activeElTag = activeEl.tagName.toLowerCase();
            if (activeElTag == "textarea" || activeElTag == "input") {
                convertSelectionInputBox(activeEl);
            }
            else if (activeEl.contentEditable) {
                convertSelectionContentEditable(activeEl);
            }
        }
    }

    document.addEventListener('keydown', enableLaTeXShortcuts, true);

})();