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 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 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';
    });
})();