您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Highlight text then press [ALT+X] to convert LaTeX commands to their unicode equivalent (ex. \pi → π)
// ==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); })();