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

当前为 2024-04-07 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         LaTeX Question Itemizer with vspace
// @namespace    http://tampermonkey.net/
// @version      1.0
// @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
// @author       Andrew Lakkis
// @match        https://www.overleaf.com/*
// @license      MIT
// @grant        none
// ==/UserScript==

(function() {
    'use strict';


    // Create UI elements
    const containerDiv = document.createElement('div');
    const inputDiv = document.createElement('div');
    const outputDiv = document.createElement('div');
    const inputTextArea = document.createElement('textarea');
    const outputTextArea = document.createElement('textarea');
    const submitButton = document.createElement('button');
    const closeButton = document.createElement('button');
    const clearButton = document.createElement('button'); // New clear button
    const copyButton = document.createElement('button');
    const copyAndCloseButton = document.createElement('button');

    // UI setup
    inputTextArea.setAttribute('placeholder', 'Enter your text here...');
    submitButton.textContent = 'Transform';
    closeButton.textContent = 'Close';
    clearButton.textContent = 'Clear'; // Set text for clear button
    copyButton.textContent = 'Copy';
    copyAndCloseButton.textContent = 'Copy and Close';
    outputTextArea.setAttribute('readonly', true);

    inputDiv.appendChild(inputTextArea);
    inputDiv.appendChild(submitButton);
    inputDiv.appendChild(clearButton); // Append clear button to inputDiv
    inputDiv.appendChild(closeButton);
    outputDiv.appendChild(outputTextArea);
    outputDiv.appendChild(copyButton);
    outputDiv.appendChild(copyAndCloseButton);
    containerDiv.appendChild(inputDiv);
    containerDiv.style.transform = 'translateX(50px) translateY(100px)'; // shift the clear button 8px to the left in the search box
    containerDiv.appendChild(outputDiv);
    document.body.appendChild(containerDiv);

    // Styling
    containerDiv.style.position = 'fixed';
    containerDiv.style.top = '10px';
    containerDiv.style.left = '10px';
    containerDiv.style.backgroundColor = '#f0f0f0';
    containerDiv.style.border = '1px solid #ddd';
    containerDiv.style.padding = '10px';
    inputTextArea.style.width = '300px';
    inputTextArea.style.height = '150px';
    outputTextArea.style.width = '300px';
    outputTextArea.style.height = '150px';
    containerDiv.style.display = 'none';



    // Transform text function
    const transformText = (text) => {
        const lines = text.split('\n');
        let transformedText = '';
        let hasEnumerateStarted = false;
        let isFirstItem = true; // Flag to track the first item

        // this block will determine if there is already an itemized question, and fills it with vspace in between each pair of items if necessary
        if (text.includes('\\begin{enumerate}') && text.includes('\\item')) {
            return text.split('\n').reduce((acc, line, index, arr) => {
                // Check for item lines and not the last line of the array
                if (line.trim().startsWith('\\item') && index !== arr.length - 1) {
                    // Check the next line; if not another item, insert \vspace
                    return acc + line + '\n  \\vspace{0.1in}  \n\n\n\n\n\n \\vspace{0.2in} \n';
                }
                return acc + line + '\n';
            }, '');
        }


        for (let line of lines) {
            // Attempt to match the specific cases, including those ending with a period
            const match = line.match(/^(\((?:[a-e]|[1-9])\)|[a-e]\)|\d\.\s|\d\))\s*(.*)/i) ||
                  line.match(/^([a-e]|\d)\.\s*(.*)/i); // Directly match letters or digits followed by a period

            if (match) {
                let prefix = match[1];
                // Adjust prefix to ensure correct LaTeX format
                // This includes adding a closing parenthesis for letters or digits without any
                if (prefix.match(/^[a-e]$/) || prefix.match(/^\d$/)) {
                    prefix += '.'; // Append period for single letters or digits without any punctuation for consistency
                }

                if (!hasEnumerateStarted) {
                    transformedText += '\\begin{enumerate}';
                    hasEnumerateStarted = true;
                }
                // Check if it's the first item to skip \vspace
                if (isFirstItem) {
                    transformedText += `\n\n \\item[${prefix}] ${match[2]}\n`;
                    isFirstItem = false;
                } else {
                    transformedText += `\n  \\vspace{0.1in}  \n\n\n\n\n\n \\vspace{0.2in} \n\n \\item[${prefix}] ${match[2]}\n`;
                }
            } else {
                transformedText += line + '\n';
            }
        }

        if (hasEnumerateStarted) {
            transformedText += '\n \\vspace{0.1in} \n\n\n\n\n \\end{enumerate}\n  \\vspace{0.4in}';
        }

        return transformedText;
    };




    // Event listeners
    document.addEventListener('keydown', (e) => {
        if (e.key === 'u' && (e.ctrlKey || e.metaKey)) {
            containerDiv.style.display = 'block';
            inputTextArea.focus();
        }
    });

    submitButton.addEventListener('click', () => {
        const inputText = inputTextArea.value;
        const outputText = transformText(inputText);
        outputTextArea.value = outputText;
    });

    closeButton.addEventListener('click', () => {
        containerDiv.style.display = 'none';
    });


    clearButton.addEventListener('click', () => {
        inputTextArea.value = ''; // Clear the input text area
        inputTextArea.focus();
    });


    copyButton.addEventListener('click', () => {
        outputTextArea.select();
        document.execCommand('copy');
    });


    /* get this to work for paste event
    inputTextArea.addEventListener('keydown', () => {
        const inputText = inputTextArea.value;
        const outputText = transformText(inputText);
        outputTextArea.value = outputText;
    });
    */

    copyAndCloseButton.addEventListener('click', () => {
        outputTextArea.select();
        document.execCommand('copy');
        containerDiv.style.display = 'none';
    });
})();