Copy HTML to Anki

Copy specific parts of HTML text and send them to Anki, converting relative URLs to absolute URLs. Trigger with Ctrl+Shift+Y or via Tampermonkey menu.

Versão de: 07/11/2024. Veja: a última versão.

// ==UserScript==
// @name         Copy HTML to Anki
// @namespace    http://tampermonkey.net/
// @version      4.81
// @description  Copy specific parts of HTML text and send them to Anki, converting relative URLs to absolute URLs. Trigger with Ctrl+Shift+Y or via Tampermonkey menu.
// @author       nabe
// @match        *://*/*
// @grant        GM_xmlhttpRequest
// @grant        GM_registerMenuCommand
// @connect      localhost
// @run-at       document-end
// @license MIT
// ==/UserScript==
 
(function() {
    'use strict';
 
    function copyHtmlToAnki(generateRandomID = false) {
        // Function to convert relative URLs to absolute URLs
        function makeAbsolute(url) {
            return new URL(url, document.baseURI).href;
        }


        // Function to generate a random string of numbers
        function generateRandomString(length) {
            let result = '';
            const characters = '0123456789';
            for (let i = 0; i < length; i++) {
                result += characters.charAt(Math.floor(Math.random() * characters.length));
            }
            return result;
        }
        
            // New function to extract image URLs from a given document (e.g., docClone)
        function extractImageUrls(doc) {
            // Find all images within elements with class="group img" in the specified document
            let imageUrls = Array.from(doc.querySelectorAll('.group.img img'))
                .map(img => img.getAttribute('src'))
                .filter(src => src && !src.includes('.svg') && src !== 'Front Image')
                .map(src => makeAbsolute(src));
    
            console.log("Extracted Image URLs:", imageUrls);
            return imageUrls;
        }

 
        // Clone the document to manipulate it
        let docClone = document.documentElement.cloneNode(true);
 
        // Convert all relative URLs to absolute URLs
        let elements = docClone.querySelectorAll('[src], [href]');
        elements.forEach(function(element) {
            if (element.hasAttribute('src')) {
                element.setAttribute('src', makeAbsolute(element.getAttribute('src')));
            }
            if (element.hasAttribute('href')) {
                element.setAttribute('href', makeAbsolute(element.getAttribute('href')));
            }
        });
 
        // Extract the text content of specific parts needed
        let frontElement = docClone.querySelector('.container.card');
        let frontField = frontElement ? frontElement.innerHTML : '';
        let frontFieldText = frontElement ? frontElement.textContent.trim() : '';
        console.log("Front Field:", frontField);
        
        //Capture content from .photo-question
        let photoQuestionElement = docClone.querySelector('.solution.container .photo-question');
        let photoQuestion = photoQuestionElement ? photoQuestionElement.outerHTML : '';
        console.log("Photo Question HTML:", photoQuestion);

        let questionField = docClone.querySelector('form.question h3')?.innerText.trim() || '';
        console.log("Question Field:", questionField);
 
        let optionField = Array.from(docClone.querySelectorAll('.options .option'))
                             .map(option => option.innerText.trim())
                             .filter(text => text)
                             .map(text => `<li>${text}</lis>`)
                             .join('') || '';
        console.log("Option Field:", optionField);
 
        let backField = Array.from(docClone.querySelectorAll('.options .option.correct'))
                               .map(option => option.innerText.trim())
                               .filter(text => text)
                               .map(text => `<li>${text}</li>`)
                               .join('') || '';
        console.log("Answer Field:", backField);
 
        //let extraField = docClone.querySelector('.results.container.collected .feedback-container .text')?.innerText.trim() || '';
        //console.log("Additional Info Field:", extraField);
        let extraElement = docClone.querySelector('.results.container.collected .feedback-container .text');
        let extraField = extraElement ? extraElement.innerHTML : '';
        let extraFieldText = docClone.querySelector('.results.container.collected .feedback-container .text')?.innerText.trim() || '';
        console.log("Additional Info Field:", extraField);

        let webpageURL = window.location.href;
        console.log("Tag:", webpageURL);
        
        // Generate uniqueID
        let uniqueID = generateRandomID ? generateRandomString(10) : questionField.concat(backField).concat(extraField).concat(photoQuestion);
 
         // Use extractImageUrls with docClone to get image URLs
        let imageUrls = extractImageUrls(docClone);
 
        // Create the note fields
        let noteFields = {
            "Front": frontField.concat("<br>").concat(photoQuestion),
            "Question": questionField,
            "Options": '<div class="psol">'.concat(optionField).concat("</div>"),
            "Back": '<div class="psol">'.concat(backField).concat("</div>"),
            "Feedback": extraFieldText,
            "Extra": extraField,
            "Link": "<a href=".concat(webpageURL).concat(">Link To Card</a>"),
            "UniqueID": uniqueID,
            "Front Image": imageUrls.join('<br>')
        };
 
        console.log("Note fields to be sent to Anki:", noteFields);
 
        GM_xmlhttpRequest({
            method: "POST",
            url: "http://localhost:8765",
            data: JSON.stringify({
                "action": "addNote",
                "version": 6,
                "params": {
                    "note": {
                        "deckName": "Default",
                        "modelName": "uofcCard",
                        "fields": noteFields,
                        "tags": [webpageURL]
                    }
                }
            }),
            headers: {
                "Content-Type": "application/json"
            },
            onload: function(response) {
                console.log("Response from AnkiConnect:", response);
                if (response.status === 200) {
                    console.log("Note fields sent to Anki successfully!");
                } else {
                    console.error("Failed to send note fields to Anki.");
                }
            }
        });
    }
 
    // Add event listener for the keyboard shortcut (Ctrl+Shift+Y)
    document.addEventListener('keydown', function(event) {
        if (event.ctrlKey && event.shiftKey && event.code === 'KeyY') {
            copyHtmlToAnki();
        }
    });
 
    // Register the menu command to Tampermonkey menu
    GM_registerMenuCommand("Copy HTML to Anki", function() { copyHtmlToAnki(false); });
    GM_registerMenuCommand("Copy HTML to Anki with Random ID", function() { copyHtmlToAnki(true); });
})();