LaTeX Unicode Shortcuts

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

当前为 2020-07-21 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name LaTeX Unicode Shortcuts
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1
  5. // @description Highlight text then press [ALT+X] to convert LaTeX commands to their unicode equivalent (ex. \pi → π)
  6. // @author eyl327
  7. // @match *://*/*
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11. (function () {
  12. 'use strict';
  13.  
  14. var convert;
  15.  
  16. var dictLoaded = false;
  17.  
  18. /* source url for shortcut file */
  19. var dictionarySource = "https://raw.githubusercontent.com/eyl327/LaTeX-Gboard-Dictionary/master/dictionary.txt";
  20.  
  21. /* fetch text file when requested */
  22. function loadAsset(url, callback) {
  23. var xhr = new XMLHttpRequest();
  24. xhr.open("GET", url, false);
  25. xhr.onreadystatechange = function () {
  26. if (xhr.readyState === 4) {
  27. if (xhr.status === 200 || xhr.status === 0) {
  28. callback(xhr.responseText);
  29. }
  30. }
  31. }
  32. xhr.send();
  33. }
  34.  
  35. /* on dictionary loaded callback */
  36. function loaded(response) {
  37. console.log("LaTeX Unicode Shortcuts has been loaded.");
  38. /* generate dictionary from text file */
  39. var dictArr = response.split("\n").slice(1);
  40. var dictionary = {};
  41. for (var i = 0, len = dictArr.length; i < len; ++i) {
  42. var kvp = dictArr[i].split("\t");
  43. dictionary[kvp[0]] = kvp[1];
  44. }
  45. /* conversion function */
  46. convert = function (text) {
  47. var result = text.replace(/{([A-Za-z0-9])}/g, '$1'); // {R} => R
  48. for (var key in dictionary) {
  49. var pattern = new RegExp(key.replace(/([[^$.|\\?*+(){}])/g, '\\$1') + "\\b", 'g'); // clean and escape key
  50. var replaced = result.replace(pattern, dictionary[key]);
  51. if (replaced.length < result.length) {
  52. result = replaced;
  53. }
  54. }
  55. return result;
  56. };
  57. dictLoaded = true;
  58. }
  59.  
  60. /* get caret position within input box */
  61. function getCaretPosition(el) {
  62. if ("selectionStart" in el && document.activeElement == el) {
  63. return {
  64. start: el.selectionStart,
  65. end: el.selectionEnd
  66. };
  67. }
  68. else if (el.createTextRange) {
  69. var sel = document.selection.createRange();
  70. if (sel.parentElement() === el) {
  71. var range = el.createTextRange();
  72. range.moveToBookmark(sel.getBookmark());
  73. for (var len = 0;
  74. range.compareEndPoints("EndToStart", range) > 0;
  75. range.moveEnd("character", -1)) {
  76. len++;
  77. }
  78. range.setEndPoint("StartToStart", el.createTextRange());
  79. for (var pos = { start: 0, end: len };
  80. range.compareEndPoints("EndToStart", range) > 0;
  81. range.moveEnd("character", -1)) {
  82. pos.start++;
  83. pos.end++;
  84. }
  85. return pos;
  86. }
  87. }
  88. return -1;
  89. }
  90.  
  91. /* set caret position within input box */
  92. function setCaretPosition(el, pos) {
  93. if (el.setSelectionRange) {
  94. el.focus();
  95. el.setSelectionRange(pos, pos);
  96. }
  97. else if (el.createTextRange) {
  98. var range = el.createTextRange();
  99. range.collapse(true);
  100. range.moveEnd('character', pos);
  101. range.moveStart('character', pos);
  102. range.select();
  103. }
  104. }
  105.  
  106. function replaceConversionInElement(activeEl, start, end) {
  107. var fullText = activeEl.value;
  108. var textToConvert = fullText.substring(start, end);
  109. var before = fullText.substring(0, start);
  110. var after = fullText.substring(end, fullText.length);
  111. // convert selection
  112. var convertedText = convert(textToConvert);
  113. // overwrite text
  114. activeEl.value = before + convertedText + after
  115. // set cursor to be at end of selection
  116. setCaretPosition(activeEl, before.length + convertedText.length);
  117. }
  118.  
  119. /* convert hilighted text in active element */
  120. function convertSelection(activeEl) {
  121. var caretRange = getCaretPosition(activeEl);
  122. var selStart = caretRange.start;
  123. var selEnd = caretRange.end;
  124. /* if selection is empty, find word at caret */
  125. if (selStart == selEnd) {
  126. var fullText = activeEl.value;
  127. // Find beginning and end of word
  128. var left = fullText.slice(0, selStart + 1).search(/\S+$/);
  129. var right = fullText.slice(selStart).search(/(\s|$)/);
  130. /* convert the word at the caret selection */
  131. replaceConversionInElement(activeEl, left, right + selStart)
  132. }
  133. /* else convert the selection */
  134. else {
  135. replaceConversionInElement(activeEl, selStart, selEnd);
  136. }
  137. }
  138.  
  139. /* detect ALT+X keyboard shortcut */
  140. async function enableLaTeXShortcuts(event) {
  141. if (event.altKey && event.keyCode == 88) { // ALT+X
  142. // load dictionary when first pressed
  143. if (!dictLoaded) {
  144. await loadAsset(dictionarySource, loaded);
  145. }
  146. // convert selection
  147. var activeEl = document.activeElement;
  148. var activeElTag = activeEl.tagName.toLowerCase();
  149. if (activeElTag == "textarea" || activeElTag == "input") {
  150. convertSelection(activeEl);
  151. }
  152. }
  153. }
  154.  
  155. document.addEventListener('keydown', enableLaTeXShortcuts, false);
  156.  
  157. })();