Answer2fill

Automatically selects language based on user's browser settings (EN/ZH). Fills answers, submits, and navigates.

目前為 2025-10-12 提交的版本,檢視 最新版本

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

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

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

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

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

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

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Answer2fill
// @name:zh      填充答案
// @namespace    http://tampermonkey.net/
// @version      1.3.0
// @description  Automatically selects language based on user's browser settings (EN/ZH). Fills answers, submits, and navigates.
// @description:zh  根据浏览器语言自动选择界面语言 (英/中)。自动填入答案、提交并跳转到下一章节。
// @match        *://*/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @grant        GM_notification
// ==/UserScript==

(function () {
  "use strict";

  // --- START: Internationalization (i18n) ---
  const translations = {
    en: {
      panelTitle: "Auto-Fill Panel",
      help: "Help",
      minimize: "Minimize",
      close: "Close",
      chapter: "Chapter",
      detecting: "Detecting...",
      questions: "Questions",
      fillAnswersTab: "Fill Answers",
      fillAnswersTooltip: "Enter and fill answers",
      configureTab: "Configure",
      configureTooltip: "Set up selectors",
      answerInputPlaceholder: "Paste answers here for all chapters (e.g., A,B,C)",
      fillButton: "Fill, Submit & Next",
      fillButtonTooltip: "Start filling answers and navigate",
      pasteButton: "Paste & Format",
      pasteButtonTooltip: "Paste from clipboard and format answers automatically",
      saveConfigButton: "Save Configuration",
      saveConfigTooltip: "Save current selectors",
      totalQuestionsPlaceholder: "Expected Total Questions (Optional)",
      totalQuestionsTooltip: "Total questions for validation before starting.",
      questionContainerPlaceholder: "Question Container",
      questionContainerTooltip: "CSS selector for individual question containers",
      chapterElementPlaceholder: "Chapter Title Element",
      chapterElementTooltip: "CSS selector for the chapter title element",
      submitButtonPlaceholder: "Submit Button",
      submitButtonTooltip: "CSS selector for the submit button",
      nextChapterButtonPlaceholder: "Next Chapter Button",
      nextChapterButtonTooltip: "CSS selector for the next chapter button",
      helpTitle: "Auto-Fill Panel Help",
      helpFillTabTitle: "Fill Answers Tab:",
      helpFillTabDesc1: "Enter answers for all chapters in the text area.",
      helpFillTabDesc2: 'Click "Fill, Submit & Next" to start.',
      helpConfigTabTitle: "Configure Tab:",
      helpConfigTotalQuestions: "Expected Total Questions: (Optional) Enter the total number of questions to validate against the number of answers you provide.",
      helpConfigQuestionContainer: "Question Container: CSS selector for the element that contains each question.",
      helpConfigNextChapter: "Next Chapter Button: CSS selector for the button to go to the next section.",
      helpConfigSubmitButton: "Submit Button: CSS selector for the button that submits the answers.",
      helpConfigChapterElement: "Chapter Title Element: CSS selector for the element displaying the chapter name.",
      statusReady: "Ready",
      statusConfigSaved: "Configuration saved",
      statusNoQuestions: "No questions found. Check configuration.",
      statusValidationError: (provided, expected) => `Error: Provided ${provided} answers, expected ${expected}.`,
      statusFilling: (count) => `Filling ${count} answers...`,
      statusChapterFilled: "Chapter filled. Submitting...",
      statusClicking: (name) => `Clicking ${name}...`,
      statusElementNotFound: (name) => `${name} not found.`,
      statusNoAnswersLeft: "Found questions, but no answers remaining.",
      statusSkippingChapter: "No questions found in this chapter. Skipping...",
      statusNoMoreChapters: "No more chapters available. Workflow finished.",
      statusLoadingNext: "Loading next chapter...",
      togglePanelMenu: "Toggle Auto-Fill Panel",
      statusPasteSuccess: (count) => `Pasted and formatted ${count} answers.`,
      statusPasteError: "Failed to read clipboard. Check permissions or content.",
      statusPasteInfo: "Use Ctrl+V to paste into the text box.",
    },
    zh: {
      panelTitle: "自动填充面板",
      help: "帮助",
      minimize: "最小化",
      close: "关闭",
      chapter: "章节",
      detecting: "检测中...",
      questions: "题目",
      fillAnswersTab: "填入答案",
      fillAnswersTooltip: "输入并填充答案",
      configureTab: "配置",
      configureTooltip: "设置选择器",
      answerInputPlaceholder: "在此粘贴所有章节的答案 (例如: A,B,C)",
      fillButton: "填充 & 下一章",
      fillButtonTooltip: "开始填充答案并导航",
      pasteButton: "粘贴 & 格式化",
      pasteButtonTooltip: "从剪贴板粘贴并自动格式化答案",
      saveConfigButton: "保存配置",
      saveConfigTooltip: "保存当前选择器",
      totalQuestionsPlaceholder: "预期总题数 (可选)",
      totalQuestionsTooltip: "开始前用于验证答案总数。",
      questionContainerPlaceholder: "问题容器",
      questionContainerTooltip: "单个问题容器的 CSS 选择器",
      chapterElementPlaceholder: "章节标题元素",
      chapterElementTooltip: "显示章节标题的元素的 CSS 选择器",
      submitButtonPlaceholder: "提交按钮",
      submitButtonTooltip: "提交按钮的 CSS 选择器",
      nextChapterButtonPlaceholder: "下一章按钮",
      nextChapterButtonTooltip: "下一章按钮的 CSS 选择器",
      helpTitle: "自动填充面板帮助",
      helpFillTabTitle: "“填入答案”选项卡:",
      helpFillTabDesc1: "在文本区域输入所有章节的答案。",
      helpFillTabDesc2: '点击“填充, 提交 & 下一章”开始。',
      helpConfigTabTitle: "“配置”选项卡:",
      helpConfigTotalQuestions: "预期总题数: (可选) 输入问题总数,用于与您提供的答案数量进行验证。",
      helpConfigQuestionContainer: "问题容器: 包含每个问题的元素的 CSS 选择器。",
      helpConfigNextChapter: "下一章按钮: 前往下一部分的按钮的 CSS 选择器。",
      helpConfigSubmitButton: "提交答案的按钮的 CSS 选择器。",
      helpConfigChapterElement: "章节标题元素: 显示章节名称的元素的 CSS 选择器。",
      statusReady: "准备就绪",
      statusConfigSaved: "配置已保存",
      statusNoQuestions: "未找到问题,请检查配置。",
      statusValidationError: (provided, expected) => `错误: 提供了 ${provided} 个答案,预期为 ${expected} 个。`,
      statusFilling: (count) => `正在填充 ${count} 个答案...`,
      statusChapterFilled: "本章已填充,正在提交...",
      statusClicking: (name) => `正在点击 ${name}...`,
      statusElementNotFound: (name) => `未找到 ${name}。`,
      statusNoAnswersLeft: "找到问题,但没有剩余答案。",
      statusSkippingChapter: "本章未找到问题,正在跳过...",
      statusNoMoreChapters: "没有更多章节了,流程结束。",
      statusLoadingNext: "正在加载下一章...",
      togglePanelMenu: "切换自动填充面板",
      statusPasteSuccess: (count) => `已粘贴并格式化 ${count} 个答案。`,
      statusPasteError: "读取剪贴板失败,请检查权限或内容。",
      statusPasteInfo: "请使用 Ctrl+V 将答案粘贴到输入框中。",
    },
  };

  function getBrowserLanguage() {
    const lang = (navigator.languages && navigator.languages[0]) ? navigator.languages[0] : navigator.language;
    return lang.toLowerCase().startsWith('zh') ? 'zh' : 'en';
  }

  const i18n = translations[getBrowserLanguage()];
  // --- END: Internationalization (i18n) ---


  const GLOBAL = {
    fillAnswerDelay: 300,
    navigationDelay: 2000,
  };

  const DEFAULT_SELECTORS = {
    "hn.12348.gov.cn": {
      subjectContainer: ".neiinput",
      nextChapterButton: "a.next",
      submitButton: ".tijiao",
      chapterElement: "div.ContentTitle"
    },
  };

  let questions = [];
  const SELECTORS = JSON.parse(
    GM_getValue("domainSelectors", JSON.stringify(DEFAULT_SELECTORS))
  );
  const currentDomain = window.location.hostname;

  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;
    }
    .config-input {
      width: calc(100% - 4px) !important;
      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"], input[type="number"], 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;
    }
    #fill-button,#paste-button, #save-selector {
      width: calc(100% - 4px);
      margin: 2px;
    }
	#button-bar button {
      flex: 1;
  `;
  function getSelectorsForCurrentDomain() {
    return SELECTORS[currentDomain] || {};
  }

  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 = `
      <div id="title-bar">
        <span>${i18n.panelTitle}</span>
        <div class="title-bar-controls">
          <button id="help-button" class="tooltip">?<span class="tooltiptext">${i18n.help}</span></button>
          <button id="minimize-button" class="tooltip">_<span class="tooltiptext">${i18n.minimize}</span></button>
          <button id="close-button" class="tooltip">X<span class="tooltiptext">${i18n.close}</span></button>
        </div>
      </div>
      <div id="panel-content">
        <div id="info-panel" style="display: flex; justify-content: space-between; padding: 3px 5px; margin: 0 2px 4px 2px; border: 2px inset #ffffff; background-color: #dfdfdf;">
        <span id="chapter-display">${i18n.chapter}: ${i18n.detecting}</span>
        <span id="question-display">${i18n.questions}: --</span>
        </div>
        <div id="tab-buttons">
          <span id="fill-tab-button" class="win98-tab active tooltip">${i18n.fillAnswersTab}<span class="tooltiptext">${i18n.fillAnswersTooltip}</span></span>
          <span id="config-tab-button" class="win98-tab tooltip">${i18n.configureTab}<span class="tooltiptext">${i18n.configureTooltip}</span></span>
        </div>
        <div id="tab-content">
          <div id="fill-tab">
            <textarea id="bulk-input" placeholder="${i18n.answerInputPlaceholder}"></textarea>
            <div id="button-bar" style="display: flex; gap: 2px;">
                <button id="paste-button" class="tooltip">${i18n.pasteButton}<span class="tooltiptext">${i18n.pasteButtonTooltip}</span></button>
                <button id="fill-button" class="tooltip">${i18n.fillButton}<span class="tooltiptext">${i18n.fillButtonTooltip}</span></button>
            </div>
            <div class="progress-bar">
              <div class="progress-bar-fill"></div>
            </div>
          </div>
          <div id="config-tab" style="display:none;">
            <div id="selector-inputs"></div>
            <button id="save-selector" class="tooltip">${i18n.saveConfigButton}<span class="tooltiptext">${i18n.saveConfigTooltip}</span></button>
          </div>
        </div>
        <div id="status-bar">${i18n.statusReady}</div>
      </div>
    `;
    shadow.appendChild(mainContainer);

    const helpDialog = document.createElement("div");
    helpDialog.id = "help-dialog";
    helpDialog.innerHTML = `
      <div id="help-dialog-title">
        <span>${i18n.help}</span>
        <button id="close-help">X</button>
      </div>
      <div id="help-content">
        <h3>${i18n.helpTitle}</h3>
        <p><strong>${i18n.helpFillTabTitle}</strong></p>
        <ul>
          <li>${i18n.helpFillTabDesc1}</li>
          <li>${i18n.helpFillTabDesc2}</li>
        </ul>
        <p><strong>${i18n.helpConfigTabTitle}</strong></p>
        <ul>
          <li><strong>${i18n.helpConfigTotalQuestions.split(':')[0]}:</strong>${i18n.helpConfigTotalQuestions.split(':')[1]}</li>
          <li><strong>${i18n.helpConfigQuestionContainer.split(':')[0]}:</strong>${i18n.helpConfigQuestionContainer.split(':')[1]}</li>
          <li><strong>${i18n.helpConfigNextChapter.split(':')[0]}:</strong>${i18n.helpConfigNextChapter.split(':')[1]}</li>
          <li><strong>${i18n.helpConfigSubmitButton.split(':')[0]}:</strong>${i18n.helpConfigSubmitButton.split(':')[1]}</li>
          <li><strong>${i18n.helpConfigChapterElement.split(':')[0]}:</strong>${i18n.helpConfigChapterElement.split(':')[1]}</li>
        </ul>
      </div>
    `;
    shadow.appendChild(helpDialog);

    addEventListeners(shadow);

    updateConfigTab(shadow);
    return shadow;
  }

  async function handlePasteAndFormatClick() {
    if (navigator.clipboard && window.isSecureContext) {
        try {
            const text = await navigator.clipboard.readText();
            if (text) {
                const shadow = document.getElementById("auto-fill-wrapper").shadowRoot;
                const answers = parseAnswers(text);
                shadow.getElementById("bulk-input").value = answers.join(',');
                updateStatusBar(i18n.statusPasteSuccess(answers.length));
            }
        } catch (err) {
            console.error("Clipboard read failed:", err);
            updateStatusBar(i18n.statusPasteError);
            GM_notification({ text: i18n.statusPasteInfo, title: i18n.panelTitle, timeout: 4000 });
        }
    } else {
        // Fallback for HTTP or unsupported browsers
        updateStatusBar(i18n.statusPasteInfo);
        GM_notification({ text: i18n.statusPasteInfo, title: i18n.panelTitle, timeout: 4000 });
    }
  }
  
  function handlePaste(e) {
      e.preventDefault();
      
      const pastedText = (e.clipboardData || window.clipboardData).getData('text');
      
      if (pastedText) {
          const answers = parseAnswers(pastedText);
          e.target.value = answers.join(','); // Set the formatted text in the textarea
          updateStatusBar(i18n.statusPasteSuccess(answers.length));
      }
  }


  // This function is kept for potential future use but is not currently called.
  // The 'copy' command is generally more reliable across contexts than 'paste'.
  async function copyToClipboard(text) {
      if (navigator.clipboard && window.isSecureContext) {
          try {
              await navigator.clipboard.writeText(text);
              return true;
          } catch (err) {
              console.error("Async copy failed, falling back:", err);
          }
      }

      // Fallback to execCommand
      const textArea = document.createElement("textarea");
      textArea.value = text;
      textArea.style.position = "fixed"; // Avoid scrolling to bottom
      textArea.style.top = "0";
      textArea.style.left = "0";
      textArea.style.opacity = "0";

      document.body.appendChild(textArea);
      textArea.focus();
      textArea.select();

      let success = false;
      try {
          success = document.execCommand("copy");
      } catch (err) {
          console.error("Fallback copy failed:", err);
      }

      document.body.removeChild(textArea);
      return success;
  }

  function addEventListeners(shadow) {
    const mainContainer = shadow.getElementById('auto-fill-container');
    mainContainer.addEventListener("click", handleContainerClick);
    mainContainer.addEventListener("mousedown", handleContainerMouseDown);

    shadow.getElementById("paste-button").addEventListener("click", handlePasteAndFormatClick);

    const bulkInput = shadow.getElementById("bulk-input");
    bulkInput.addEventListener("keydown", handleBulkInputKeydown);
    
    // --- MODIFIED: Add paste event listener ---
    bulkInput.addEventListener("paste", handlePaste);

    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") {
      fillAnswersWorkflow();
    }
  };

  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();
      fillAnswersWorkflow();
    } else if (e.key === "Backspace" || e.key === "Delete") {
      e.stopPropagation();
    }
  };

  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");
    panelContent.style.display = panelContent.style.display === "none" ? "block" : "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 = `
      <div class="tooltip" style="width:100%;">
        <input id="totalQuestions" class="config-input" type="number" value="${currentSelectors.totalQuestions || ""}" placeholder="${i18n.totalQuestionsPlaceholder}">
        <span class="tooltiptext">${i18n.totalQuestionsTooltip}</span>
      </div>
      <div class="tooltip" style="width:100%;">
        <input id="subjectContainer" class="config-input" type="text" value="${currentSelectors.subjectContainer || ""}" placeholder="${i18n.questionContainerPlaceholder}">
        <span class="tooltiptext">${i18n.questionContainerTooltip}</span>
      </div>
       <div class="tooltip" style="width:100%;">
        <input id="chapterElement" class="config-input" type="text" value="${currentSelectors.chapterElement || ""}" placeholder="${i18n.chapterElementPlaceholder}">
        <span class="tooltiptext">${i18n.chapterElementTooltip}</span>
      </div>
      <div class="tooltip" style="width:100%;">
        <input id="submitButton" class="config-input" type="text" value="${currentSelectors.submitButton || ""}" placeholder="${i18n.submitButtonPlaceholder}">
        <span class="tooltiptext">${i18n.submitButtonTooltip}</span>
      </div>
      <div class="tooltip" style="width:100%;">
        <input id="nextChapterButton" class="config-input" type="text" value="${currentSelectors.nextChapterButton || ""}" placeholder="${i18n.nextChapterButtonPlaceholder}">
        <span class="tooltiptext">${i18n.nextChapterButtonTooltip}</span>
      </div>
    `;
  }

  function saveSelectors() {
    const shadow = document.getElementById("auto-fill-wrapper").shadowRoot;
    if (!SELECTORS[currentDomain]) {
        SELECTORS[currentDomain] = {};
    }
    SELECTORS[currentDomain] = {
      totalQuestions: shadow.getElementById("totalQuestions").value,
      subjectContainer: shadow.getElementById("subjectContainer").value,
      nextChapterButton: shadow.getElementById("nextChapterButton").value,
      submitButton: shadow.getElementById("submitButton").value,
      chapterElement: shadow.getElementById("chapterElement").value,
    };
    GM_setValue("domainSelectors", JSON.stringify(SELECTORS));
    detectQuestions();
    switchTab("fill");
    updateStatusBar(i18n.statusConfigSaved);
  }

  function updateQuestionCount() {
    const shadow = document.getElementById("auto-fill-wrapper").shadowRoot;
    const chapterDisplay = shadow.getElementById("chapter-display");
    const questionDisplay = shadow.getElementById("question-display");
    const currentSelectors = getSelectorsForCurrentDomain();

    let chapterName = "N/A";
    if (currentSelectors.chapterElement) {
        const chapterEl = document.querySelector(currentSelectors.chapterElement);
        if (chapterEl) {
            let name = chapterEl.textContent.trim();
            if (name.length > 10) {
                name = name.substring(0, 10) + "...";
            }
            chapterName = name;
        }
    }

    if (chapterDisplay) {
        chapterDisplay.textContent = `${i18n.chapter}: ${chapterName}`;
    }

    if (questionDisplay) {
        questionDisplay.textContent = `${i18n.questions}: ${questions.length}`;
    }
  }

  function detectQuestions(isInitialCall = false) {
    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 && isInitialCall) {
        updateStatusBar(i18n.statusNoQuestions);
        switchTab("config");
    }
  }

  function parseAnswers(input) {
      const finalAnswers = [];
      if (!input) return finalAnswers;

      // Regex for complex formats like "1-7. DDBB" or "8.ABC"
      const complexRegex = /(?:\d+-\d+\.\s*)((?:[A-Z]\s*)+)|(?:\d+\.\s*([A-Z]+))/g;
      const matches = [...input.matchAll(complexRegex)];

      if (matches.length > 0) {
          for (const match of matches) {
              if (match[1]) { // Range format like "D D B B" or "BBBAAB"
                  const cleaned = match[1].replace(/\s+/g, '');
                  finalAnswers.push(...cleaned.split(''));
              } else if (match[2]) { // Single question format like "ABC"
                  finalAnswers.push(match[2]);
              }
          }
          return finalAnswers;
      }

      const cleanedInput = input.toUpperCase().replace(/[^A-Z,\s]/g, '');
      return cleanedInput.split(/,|\s+/).filter(Boolean);
  }

  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));
        }
      }
    }
  };

  async function fillSingleChapter(answersForChapter) {
      const totalQuestions = Math.min(questions.length, answersForChapter.length);
      if (totalQuestions === 0) return;

      updateStatusBar(i18n.statusFilling(totalQuestions));

      for (let i = 0; i < totalQuestions; i++) {
          const options = Array.isArray(questions[i]) ? questions[i] : questions[i].querySelectorAll('input[type="radio"],input[type="checkbox"]');
          const answer = answersForChapter[i];
          await fillAnswer(options, answer);
          updateProgressBar((i + 1) / totalQuestions);
      }
  }

  async function clickElement(selector, description) {
      if (!selector) {
          console.log(`${description} selector not configured.`);
          return false;
      }
      const element = document.querySelector(selector);
      if (element) {
          updateStatusBar(i18n.statusClicking(description));
          element.click();
          await new Promise(resolve => setTimeout(resolve, GLOBAL.navigationDelay));
          return true;
      } else {
          updateStatusBar(i18n.statusElementNotFound(description));
          return false;
      }
  }

  async function fillAnswersWorkflow() {
    const shadow = document.getElementById("auto-fill-wrapper").shadowRoot;
    const currentSelectors = getSelectorsForCurrentDomain();
    let allAnswers = parseAnswers(shadow.getElementById("bulk-input").value);

    const expectedTotalStr = currentSelectors.totalQuestions || "0";
    const expectedTotal = parseInt(expectedTotalStr, 10);
    const providedTotal = allAnswers.length;

    if (expectedTotal > 0 && providedTotal !== expectedTotal) {
        updateStatusBar(i18n.statusValidationError(providedTotal, expectedTotal));
        return;
    }

    while (true) {
        detectQuestions();
        await new Promise(resolve => setTimeout(resolve, 100));

        if (questions.length > 0) {
            if (allAnswers.length === 0) {
                updateStatusBar(i18n.statusNoAnswersLeft);
                break;
            }

            const answersForThisChapter = allAnswers.slice(0, questions.length);
            allAnswers = allAnswers.slice(questions.length);

            await fillSingleChapter(answersForThisChapter);
            updateStatusBar(i18n.statusChapterFilled);

            await clickElement(currentSelectors.submitButton, i18n.submitButtonPlaceholder);
        } else {
            updateStatusBar(i18n.statusSkippingChapter);
        }

        const hasNext = await clickElement(currentSelectors.nextChapterButton, i18n.nextChapterButtonPlaceholder);

        if (!hasNext) {
            updateStatusBar(i18n.statusNoMoreChapters);
            break;
        }

        updateStatusBar(i18n.statusLoadingNext);
        await new Promise(resolve => setTimeout(resolve, GLOBAL.navigationDelay + 1000));
    }

    updateProgressBar(0);
  }

  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() {
    createMainInterface();
    setTimeout(() => detectQuestions(true), 2000);
    GM_registerMenuCommand(i18n.togglePanelMenu, togglePanelVisibility);
  }

  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", init);
  } else {
    init();
  }
})();