LaTeX Question Itemizer with vspace

Open a text box with CMD u or CTRL u and paste a HW question you have to automatically get it in \item format

  1. // ==UserScript==
  2. // @name LaTeX Question Itemizer with vspace
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0
  5. // @description Open a text box with CMD u or CTRL u and paste a HW question you have to automatically get it in \item format
  6. // @author Andrew Lakkis
  7. // @match https://www.overleaf.com/*
  8. // @license MIT
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15.  
  16. // Create UI elements
  17. const containerDiv = document.createElement('div');
  18. const inputDiv = document.createElement('div');
  19. const outputDiv = document.createElement('div');
  20. const inputTextArea = document.createElement('textarea');
  21. const outputTextArea = document.createElement('textarea');
  22. const submitButton = document.createElement('button');
  23. const closeButton = document.createElement('button');
  24. const clearButton = document.createElement('button'); // New clear button
  25. const copyButton = document.createElement('button');
  26. const copyAndCloseButton = document.createElement('button');
  27.  
  28. // UI setup
  29. inputTextArea.setAttribute('placeholder', 'Enter your text here...');
  30. submitButton.textContent = 'Transform';
  31. closeButton.textContent = 'Close';
  32. clearButton.textContent = 'Clear'; // Set text for clear button
  33. copyButton.textContent = 'Copy';
  34. copyAndCloseButton.textContent = 'Copy and Close';
  35. outputTextArea.setAttribute('readonly', true);
  36.  
  37. inputDiv.appendChild(inputTextArea);
  38. inputDiv.appendChild(submitButton);
  39. inputDiv.appendChild(clearButton); // Append clear button to inputDiv
  40. inputDiv.appendChild(closeButton);
  41. outputDiv.appendChild(outputTextArea);
  42. outputDiv.appendChild(copyButton);
  43. outputDiv.appendChild(copyAndCloseButton);
  44. containerDiv.appendChild(inputDiv);
  45. containerDiv.style.transform = 'translateX(50px) translateY(100px)'; // shift the clear button 8px to the left in the search box
  46. containerDiv.appendChild(outputDiv);
  47. document.body.appendChild(containerDiv);
  48.  
  49. // Styling
  50. containerDiv.style.position = 'fixed';
  51. containerDiv.style.top = '10px';
  52. containerDiv.style.left = '10px';
  53. containerDiv.style.backgroundColor = '#f0f0f0';
  54. containerDiv.style.border = '1px solid #ddd';
  55. containerDiv.style.padding = '10px';
  56. inputTextArea.style.width = '300px';
  57. inputTextArea.style.height = '150px';
  58. outputTextArea.style.width = '300px';
  59. outputTextArea.style.height = '150px';
  60. containerDiv.style.display = 'none';
  61.  
  62.  
  63.  
  64. // Transform text function
  65. const transformText = (text) => {
  66. const lines = text.split('\n');
  67. let transformedText = '';
  68. let hasEnumerateStarted = false;
  69. let isFirstItem = true; // Flag to track the first item
  70.  
  71. // this block will determine if there is already an itemized question, and fills it with vspace in between each pair of items if necessary
  72. if (text.includes('\\begin{enumerate}') && text.includes('\\item')) {
  73. return text.split('\n').reduce((acc, line, index, arr) => {
  74. // Check for item lines and not the last line of the array
  75. if (line.trim().startsWith('\\item') && index !== arr.length - 1) {
  76. // Check the next line; if not another item, insert \vspace
  77. return acc + line + '\n \\vspace{0.1in} \n\n\n\n\n\n \\vspace{0.2in} \n';
  78. }
  79. return acc + line + '\n';
  80. }, '');
  81. }
  82.  
  83.  
  84. for (let line of lines) {
  85. // Attempt to match the specific cases, including those ending with a period
  86. const match = line.match(/^(\((?:[a-e]|[1-9])\)|[a-e]\)|\d\.\s|\d\))\s*(.*)/i) ||
  87. line.match(/^([a-e]|\d)\.\s*(.*)/i); // Directly match letters or digits followed by a period
  88.  
  89. if (match) {
  90. let prefix = match[1];
  91. // Adjust prefix to ensure correct LaTeX format
  92. // This includes adding a closing parenthesis for letters or digits without any
  93. if (prefix.match(/^[a-e]$/) || prefix.match(/^\d$/)) {
  94. prefix += '.'; // Append period for single letters or digits without any punctuation for consistency
  95. }
  96.  
  97. if (!hasEnumerateStarted) {
  98. transformedText += '\\begin{enumerate}';
  99. hasEnumerateStarted = true;
  100. }
  101. // Check if it's the first item to skip \vspace
  102. if (isFirstItem) {
  103. transformedText += `\n\n \\item[${prefix}] ${match[2]}\n`;
  104. isFirstItem = false;
  105. } else {
  106. transformedText += `\n \\vspace{0.1in} \n\n\n\n\n\n \\vspace{0.2in} \n\n \\item[${prefix}] ${match[2]}\n`;
  107. }
  108. } else {
  109. transformedText += line + '\n';
  110. }
  111. }
  112.  
  113. if (hasEnumerateStarted) {
  114. transformedText += '\n \\vspace{0.1in} \n\n\n\n\n \\end{enumerate}\n \\vspace{0.4in}';
  115. }
  116.  
  117. return transformedText;
  118. };
  119.  
  120.  
  121.  
  122.  
  123. // Event listeners
  124. document.addEventListener('keydown', (e) => {
  125. if (e.key === 'u' && (e.ctrlKey || e.metaKey)) {
  126. containerDiv.style.display = 'block';
  127. inputTextArea.focus();
  128. }
  129. });
  130.  
  131. submitButton.addEventListener('click', () => {
  132. const inputText = inputTextArea.value;
  133. const outputText = transformText(inputText);
  134. outputTextArea.value = outputText;
  135. });
  136.  
  137. closeButton.addEventListener('click', () => {
  138. containerDiv.style.display = 'none';
  139. });
  140.  
  141.  
  142. clearButton.addEventListener('click', () => {
  143. inputTextArea.value = ''; // Clear the input text area
  144. inputTextArea.focus();
  145. });
  146.  
  147.  
  148. copyButton.addEventListener('click', () => {
  149. outputTextArea.select();
  150. document.execCommand('copy');
  151. });
  152.  
  153.  
  154. /* get this to work for paste event
  155. inputTextArea.addEventListener('keydown', () => {
  156. const inputText = inputTextArea.value;
  157. const outputText = transformText(inputText);
  158. outputTextArea.value = outputText;
  159. });
  160. */
  161.  
  162. copyAndCloseButton.addEventListener('click', () => {
  163. outputTextArea.select();
  164. document.execCommand('copy');
  165. containerDiv.style.display = 'none';
  166. });
  167. })();