您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Always refresh the page before copying HTML to Anki. You can choose normal or random ID via floating menu or keyboard shortcut. After reload, it auto-copies once then clears the flag so you can repeat when needed.
// ==UserScript== // @name Copy HTML to Anki (Refresh Before Each Copy) // @namespace http://tampermonkey.net/ // @version 5.0 // @description Always refresh the page before copying HTML to Anki. You can choose normal or random ID via floating menu or keyboard shortcut. After reload, it auto-copies once then clears the flag so you can repeat when needed. // @author nabe // @match https://cards.ucalgary.ca/card/* // @grant GM_xmlhttpRequest // @connect localhost // @run-at document-end // @license MIT // ==/UserScript== (function() { 'use strict'; // LOCALSTORAGE KEY used to store whether we want to run "copyHtmlToAnki" // after the page reloads, and whether we want random ID or not. const REFRESH_COPY_KEY = 'doRefreshCopyMode'; // possible values: "normal" or "random" (or null/undefined if not set) /******************************************************** * 1) Check localStorage on load. If set => do the copy. ********************************************************/ const pendingMode = localStorage.getItem(REFRESH_COPY_KEY); if (pendingMode) { console.log('[RefreshAnki] Detected pendingMode =', pendingMode); // Clear so we only do it once localStorage.removeItem(REFRESH_COPY_KEY); // Actually do the copy with or without random ID if (pendingMode === 'random') { copyHtmlToAnki(true); } else { copyHtmlToAnki(false); } } /******************************************************** * 2) MAIN FUNCTION - Copy HTML to Anki ********************************************************/ function copyHtmlToAnki(generateRandomID = false) { console.log(`[RefreshAnki] copyHtmlToAnki() called. generateRandomID = ${generateRandomID}`); // Helper: convert relative URLs to absolute function makeAbsolute(url) { return new URL(url, document.baseURI).href; } // Helper: random numeric string 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; } // Clone the entire document let docClone = document.documentElement.cloneNode(true); // Convert all [src] and [href] to absolute let elements = docClone.querySelectorAll('[src], [href]'); elements.forEach(el => { if (el.hasAttribute('src')) { el.setAttribute('src', makeAbsolute(el.getAttribute('src'))); } if (el.hasAttribute('href')) { el.setAttribute('href', makeAbsolute(el.getAttribute('href'))); } }); // Example extractions (adjust selectors as needed): let frontElement = docClone.querySelector('.container.card'); let frontField = frontElement ? frontElement.innerHTML : ''; let frontImageUrl = docClone.querySelector('.group.img a.img-fill')?.getAttribute('href') || ''; let photoQuestionElement = docClone.querySelector('.solution.container .photo-question'); let photoQuestion = photoQuestionElement ? photoQuestionElement.outerHTML : ''; let questionField = docClone.querySelector('form.question h3')?.innerText.trim() || ''; let optionField = Array.from(docClone.querySelectorAll('.options .option')) .map(opt => opt.innerText.trim()) .filter(txt => txt) .map(txt => `<li>${txt}</li>`) .join('') || ''; let backField = Array.from(docClone.querySelectorAll('.options .option.correct')) .map(opt => opt.innerText.trim()) .filter(txt => txt) .map(txt => `<li>${txt}</li>`) .join('') || ''; let extraElement = docClone.querySelector('.results.container.collected .feedback-container .text'); let extraField = extraElement ? extraElement.innerHTML : ''; let extraFieldText = extraElement ? extraElement.innerText.trim() : ''; let webpageURL = window.location.href; // Build unique ID let uniqueID = generateRandomID ? generateRandomString(10) : questionField + backField + extraField + photoQuestion; // Construct note fields let noteFields = { "Front": frontField + "<br>" + photoQuestion, "Question": questionField, "Options": `<div class="psol">${optionField}</div>`, "Back": `<div class="psol">${backField}</div>`, "Feedback": extraFieldText, "Extra": extraField, "Link": `<a href="${webpageURL}">Link To Card</a>`, "UniqueID": uniqueID, "Front Image": `<img src="${frontImageUrl}">` }; console.log('[RefreshAnki] Sending note fields to AnkiConnect:', noteFields); // Send to AnkiConnect 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("Successfully added note to Anki!"); } else { console.error("Failed to send note fields to Anki."); } } }); } /******************************************************** * 3) REFRESH-THEN-COPY HELPER ********************************************************/ // Instead of copying immediately, we set localStorage // so after reload the script calls copyHtmlToAnki(). function refreshThenCopy(generateRandomID) { const mode = generateRandomID ? 'random' : 'normal'; console.log(`[RefreshAnki] refreshThenCopy() was called, setting doRefreshCopyMode=${mode} then reloading...`); localStorage.setItem(REFRESH_COPY_KEY, mode); location.reload(); } /******************************************************** * 4) OPTIONAL KEYBOARD SHORTCUT => Refresh & Copy ********************************************************/ // If you prefer a direct copy (without refresh) on the shortcut, // then just call copyHtmlToAnki(true). But you specifically want // a refresh each time, so let's do it that way: document.addEventListener('keydown', function(e) { if (e.ctrlKey && e.shiftKey && e.code === 'KeyY') { // Refresh page, then copy with random ID refreshThenCopy(true); } }); /******************************************************** * 5) FLOATING MENU => Refresh & Copy ********************************************************/ const menuContainer = document.createElement('div'); menuContainer.style.position = 'fixed'; menuContainer.style.top = '10px'; menuContainer.style.left = '10px'; menuContainer.style.zIndex = 9999; menuContainer.style.backgroundColor = 'rgba(255, 255, 255, 0.9)'; menuContainer.style.border = '1px solid #ccc'; menuContainer.style.padding = '5px'; menuContainer.style.borderRadius = '5px'; menuContainer.style.fontFamily = 'sans-serif'; menuContainer.style.fontSize = '14px'; const title = document.createElement('div'); title.textContent = 'Anki Refresh Menu'; title.style.fontWeight = 'bold'; title.style.marginBottom = '5px'; menuContainer.appendChild(title); // Button 1: Refresh + Copy (No Random ID) const btnNormal = document.createElement('button'); btnNormal.textContent = 'Refresh & Copy (No Random ID)'; btnNormal.style.display = 'block'; btnNormal.style.marginBottom = '5px'; btnNormal.onclick = () => refreshThenCopy(false); menuContainer.appendChild(btnNormal); // Button 2: Refresh + Copy (Random ID) const btnRandom = document.createElement('button'); btnRandom.textContent = 'Refresh & Copy (Random ID)'; btnRandom.style.display = 'block'; btnRandom.onclick = () => refreshThenCopy(true); menuContainer.appendChild(btnRandom); document.body.appendChild(menuContainer); })();