// ==UserScript== // @name Answer2fill (Windows 98 Style with Shadow DOM) // @namespace http://tampermonkey.net/ // @version 1.2.3 // @description auto-filling answers with Windows 98 style interface using Shadow DOM // @match https://*/* // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @downloadURL https://update.greasyfork.icu/scripts/500296/Answer2fill%20%28Windows%2098%20Style%20with%20Shadow%20DOM%29.user.js // @updateURL https://update.greasyfork.icu/scripts/500296/Answer2fill%20%28Windows%2098%20Style%20with%20Shadow%20DOM%29.meta.js // ==/UserScript== (function () { "use strict"; const GLOBAL = { fillAnswerDelay: 300, }; const DEFAULT_SELECTORS = { "czvtc.cj-edu.com": { subjectContainer: ".el-container .all_subject>.el-row" }, "learning.mhtall.com": { subjectContainer: "#div_item" }, "168wangxiao.com": { subjectContainer: ".Answer-area" }, }; let questions = []; const SELECTORS = JSON.parse( GM_getValue("domainSelectors", JSON.stringify(DEFAULT_SELECTORS)) ); const currentDomain = window.location.hostname; // Windows 98 style CSS const styles = ` :host { all: initial; font-family: 'MS Sans Serif', Arial, sans-serif; font-size: 12px; line-height: 1.2; } #auto-fill-container { position: fixed; top: 10px; right: 10px; background-color: #c0c0c0; border: 2px outset #ffffff; box-shadow: 2px 2px 0 #000000; color: #000000; padding: 3px; border-radius: 0; z-index: 9999999; width: 300px; } #bulk-input, #fill-button, .progress-bar, #subjectContainer, #save-selector { width: calc(100% - 4px); margin: 2px; box-sizing: border-box; outline: none; } #bulk-input { height: 100px; resize: none; } button { font-family: 'MS Sans Serif', Arial, sans-serif; background-color: #c0c0c0; border: 2px outset #ffffff; padding: 2px 8px; color: #000000; cursor: pointer; } button:active { border-style: inset; } input[type="text"], textarea { border: 2px inset #ffffff; background-color: #ffffff; padding: 2px; width: 100%; } .win98-tab { display: inline-block; padding: 3px 8px; background-color: #c0c0c0; border: 2px outset #ffffff; border-bottom: none; margin-right: 2px; cursor: pointer; } .win98-tab.active { background-color: #dfdfdf; border-style: inset; border-bottom: none; } #tab-content { border: 2px inset #ffffff; padding: 10px; background-color: #dfdfdf; } #title-bar { background: linear-gradient(to right, #000080, #1084d0); padding: 2px 4px; margin-bottom: 4px; color: #ffffff; font-weight: bold; display: flex; justify-content: space-between; align-items: center; cursor: move; } .title-bar-controls { display: flex; } .title-bar-controls button { width: 16px; height: 14px; border: 1px outset #ffffff; background-color: #c0c0c0; margin-left: 2px; font-size: 9px; line-height: 1; display: flex; align-items: center; justify-content: center; color: #000000; font-weight: bold; padding: 0; } #status-bar { border-top: 2px groove #ffffff; padding: 2px 4px; font-size: 11px; margin-top: 4px; } .progress-bar { background-color: #ffffff; border: 1px inset #808080; height: 15px; margin-top: 5px; } .progress-bar-fill { width: 0%; height: 100%; background-color: #000080; transition: width 0.3s ease-in-out; } .tooltip { position: relative; display: inline-block; } .tooltip .tooltiptext { visibility: hidden; width: 120px; background-color: #ffffe1; color: #000; text-align: center; border: 1px solid #000; padding: 5px; position: absolute; z-index: 1; bottom: 125%; left: 50%; margin-left: -60px; opacity: 0; transition: opacity 0.3s; } .tooltip:hover .tooltiptext { visibility: visible; opacity: 1; } #help-dialog { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: #c0c0c0; border: 2px outset #ffffff; box-shadow: 2px 2px 0 #000000; padding: 2px; z-index: 10000; display: none; width: 300px; } #help-dialog-title { background: linear-gradient(to right, #000080, #1084d0); color: #ffffff; padding: 2px 4px; font-weight: bold; display: flex; justify-content: space-between; align-items: center; } #help-content { padding: 10px; max-height: 400px; overflow-y: auto; } #help-content h3, #help-content p, #help-content ul, #help-content li { margin: revert; padding: revert; } #subjectContainer { width: calc(100% - 4px) !important; } #fill-button, #save-selector { width: calc(100% - 4px); margin: 2px; } `; function getSelectorsForCurrentDomain() { return SELECTORS[currentDomain] || null; } function createMainInterface() { const container = document.createElement("div"); container.id = "auto-fill-wrapper"; container.style.position = "fixed"; container.style.top = "10px"; container.style.right = "10px"; container.style.zIndex = "9999999"; document.body.appendChild(container); const shadow = container.attachShadow({ mode: 'open' }); const styleElement = document.createElement('style'); styleElement.textContent = styles; shadow.appendChild(styleElement); const mainContainer = document.createElement('div'); mainContainer.id = 'auto-fill-container'; mainContainer.style.display = GM_getValue("PanelVisible", true) ? "block" : "none"; mainContainer.innerHTML = `
Auto-Fill Panel

Detecting questions...

Fill AnswersEnter and fill answers ConfigureSet up selectors
Ready
`; shadow.appendChild(mainContainer); // Help dialog const helpDialog = document.createElement("div"); helpDialog.id = "help-dialog"; helpDialog.innerHTML = `
Help

Auto-Fill Panel Help

Fill Answers Tab:

Configure Tab:

Other Features:

`; shadow.appendChild(helpDialog); addEventListeners(shadow); updateConfigTab(shadow); return shadow; } function addEventListeners(shadow) { const mainContainer = shadow.getElementById('auto-fill-container'); mainContainer.addEventListener("click", handleContainerClick); mainContainer.addEventListener("mousedown", handleContainerMouseDown); const bulkInput = shadow.getElementById("bulk-input"); bulkInput.addEventListener("keydown", handleBulkInputKeydown); bulkInput.addEventListener("input", handleBulkInputChange); shadow.getElementById("close-button").addEventListener("click", togglePanelVisibility); shadow.getElementById("minimize-button").addEventListener("click", minimizePanel); shadow.getElementById("help-button").addEventListener("click", toggleHelpDialog); shadow.getElementById("close-help").addEventListener("click", toggleHelpDialog); } const handleContainerClick = (e) => { const target = e.target; if (target.id === "fill-tab-button" || target.id === "config-tab-button") { switchTab(target.id.replace("-tab-button", "")); } else if (target.id === "save-selector") { saveSelectors(); } else if (target.id === "fill-button") { fillAnswers(); } }; const handleContainerMouseDown = (e) => { if (e.target.id === "title-bar") { let offsetX = e.clientX - e.target.getBoundingClientRect().left; let offsetY = e.clientY - e.target.getBoundingClientRect().top; function mouseMoveHandler(e) { const shadow = document.getElementById("auto-fill-wrapper").shadowRoot; const container = shadow.getElementById("auto-fill-container"); container.style.right = "auto"; container.style.left = `${e.clientX - offsetX}px`; container.style.top = `${e.clientY - offsetY}px`; } function mouseUpHandler() { document.removeEventListener("mousemove", mouseMoveHandler); document.removeEventListener("mouseup", mouseUpHandler); } document.addEventListener("mousemove", mouseMoveHandler); document.addEventListener("mouseup", mouseUpHandler); } }; const handleBulkInputKeydown = (e) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); qucikAnswers(); } else if (e.key === "Backspace" || e.key === "Delete") { e.stopPropagation(); } }; const handleBulkInputChange = (e) => { const input = e.target; input.value = input.value.replace(/[^A-Za-z,\s]/g, ''); }; function switchTab(tabName) { const shadow = document.getElementById("auto-fill-wrapper").shadowRoot; shadow.getElementById("fill-tab").style.display = tabName === "fill" ? "block" : "none"; shadow.getElementById("config-tab").style.display = tabName === "config" ? "block" : "none"; shadow.getElementById("fill-tab-button").classList.toggle("active", tabName === "fill"); shadow.getElementById("config-tab-button").classList.toggle("active", tabName === "config"); } function togglePanelVisibility() { const container = document.getElementById("auto-fill-wrapper").shadowRoot.getElementById("auto-fill-container"); const newVisibility = container.style.display === "none"; container.style.display = newVisibility ? "block" : "none"; GM_setValue("PanelVisible", newVisibility); } function minimizePanel() { const shadow = document.getElementById("auto-fill-wrapper").shadowRoot; const panelContent = shadow.getElementById("panel-content"); if (panelContent.style.display === "none") { panelContent.style.display = "block"; } else { panelContent.style.display = "none"; } } function toggleHelpDialog() { const shadow = document.getElementById("auto-fill-wrapper").shadowRoot; const helpDialog = shadow.getElementById("help-dialog"); helpDialog.style.display = helpDialog.style.display === "none" || helpDialog.style.display === "" ? "block" : "none"; } function updateConfigTab(shadow) { const currentSelectors = SELECTORS[currentDomain] || DEFAULT_SELECTORS[currentDomain] || {}; shadow.getElementById("selector-inputs").innerHTML = `
Enter the CSS selector for question containers
`; } function saveSelectors() { const shadow = document.getElementById("auto-fill-wrapper").shadowRoot; SELECTORS[currentDomain] = { subjectContainer: shadow.getElementById("subjectContainer").value, }; GM_setValue("domainSelectors", JSON.stringify(SELECTORS)); detectQuestions(); switchTab("fill"); updateStatusBar("Configuration saved"); } function updateQuestionCount() { const shadow = document.getElementById("auto-fill-wrapper").shadowRoot; const countElement = shadow.getElementById("question-count"); if (countElement) { countElement.textContent = `Detected questions: ${questions.length}`; } } function detectQuestions() { const currentSelectors = getSelectorsForCurrentDomain(); if (currentSelectors && currentSelectors.subjectContainer) { questions = Array.from( document.querySelectorAll(currentSelectors.subjectContainer) ).filter( (item) => item.querySelectorAll('input[type="radio"],input[type="checkbox"]').length > 1 ); } if (questions.length === 0) { const optionsGroup = new Map(); document.querySelectorAll('input[type="radio"],input[type="checkbox"]').forEach(opt => { const name = opt.getAttribute("name"); if (name) { if (!optionsGroup.has(name)) { optionsGroup.set(name, []); } optionsGroup.get(name).push(opt); } }); questions = Array.from(optionsGroup.values()); } updateQuestionCount(); if (questions.length === 0) switchTab("config"); } function parseAnswers(input) { return input.replace(/\s/g, "").toUpperCase().split(","); } const qucikAnswers = async () => { const shadow = document.getElementById("auto-fill-wrapper").shadowRoot; const currentSelectors = getSelectorsForCurrentDomain(); const answers = parseAnswers(shadow.getElementById("bulk-input").value); const fillPromises = []; for (let i = 0; i < Math.min(questions.length, answers.length); i++) { const subject = currentSelectors && currentSelectors.subjectContainer ? Array.from(document.querySelectorAll(currentSelectors.subjectContainer)) .filter(item => item.querySelectorAll('input[type="radio"],input[type="checkbox"]').length > 0)[i] : null; const options = subject ? subject.querySelectorAll('input[type="radio"],input[type="checkbox"]') : questions[i]; const answer = answers[i]; fillPromises.push(fillAnswer(options, answer)); } await Promise.all(fillPromises); }; const fillAnswers = async () => { const shadow = document.getElementById("auto-fill-wrapper").shadowRoot; const currentSelectors = getSelectorsForCurrentDomain(); const answers = parseAnswers(shadow.getElementById("bulk-input").value); const totalQuestions = Math.min(questions.length, answers.length); let completedQuestions = 0; updateStatusBar("Filling answers..."); for (let i = 0; i < totalQuestions; i++) { const subject = currentSelectors && currentSelectors.subjectContainer ? Array.from(document.querySelectorAll(currentSelectors.subjectContainer)) .filter(item => item.querySelectorAll('input[type="radio"],input[type="checkbox"]').length > 0)[i] : null; const options = subject ? subject.querySelectorAll('input[type="radio"],input[type="checkbox"]') : questions[i]; const answer = answers[i]; await fillAnswer(options, answer); completedQuestions++; updateProgressBar(completedQuestions / totalQuestions); } updateStatusBar("Answers filled successfully"); }; const fillAnswer = async (options, answer) => { for (const char of answer) { const optionIndex = char.charCodeAt(0) - 65; if (optionIndex >= 0 && optionIndex < options.length) { const option = options[optionIndex]; if (!option.checked) { const clickTarget = option.style.display === "none" ? option.parentElement : option; clickTarget.click(); await new Promise(resolve => setTimeout(resolve, GLOBAL.fillAnswerDelay)); } } } }; function updateProgressBar(progress) { const shadow = document.getElementById("auto-fill-wrapper").shadowRoot; const progressBarFill = shadow.querySelector('.progress-bar-fill'); progressBarFill.style.width = `${progress * 100}%`; } function updateStatusBar(message) { const shadow = document.getElementById("auto-fill-wrapper").shadowRoot; const statusBar = shadow.getElementById('status-bar'); statusBar.textContent = message; } function init() { const shadow = createMainInterface(); setTimeout(() => detectQuestions(), 2000); GM_registerMenuCommand("Auto-Fill Panel", () => { togglePanelVisibility(); }); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", init); } else { init(); } })();