您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在古诗文网上生成完形填空练习,点击填空可显示原文
// ==UserScript== // @name 古诗文网完形填空生成器 // @namespace http://tampermonkey.net/ // @version 2.0.0 // @description 在古诗文网上生成完形填空练习,点击填空可显示原文 // @author ChatGPT // @match https://www.gushiwen.cn/shiwenv.aspx?id=* // @match https://www.gushiwen.cn/shiwenv_*.aspx // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; // 配置参数 const DEFAULT_PROBABILITY = 0.5; // 默认替换概率 // 创建控制面板 function createControlPanel() { // 创建折叠按钮 const toggleBtn = document.createElement('div'); toggleBtn.id = 'cloze-toggle-btn'; toggleBtn.innerHTML = '📝'; toggleBtn.style.position = 'fixed'; toggleBtn.style.top = '20px'; toggleBtn.style.right = '20px'; toggleBtn.style.width = '40px'; toggleBtn.style.height = '40px'; toggleBtn.style.backgroundColor = '#3498db'; toggleBtn.style.color = 'white'; toggleBtn.style.borderRadius = '50%'; toggleBtn.style.display = 'flex'; toggleBtn.style.justifyContent = 'center'; toggleBtn.style.alignItems = 'center'; toggleBtn.style.fontSize = '20px'; toggleBtn.style.cursor = 'pointer'; toggleBtn.style.zIndex = '10000'; toggleBtn.style.boxShadow = '0 2px 10px rgba(0,0,0,0.2)'; toggleBtn.style.transition = 'all 0.3s ease'; // 悬停效果 toggleBtn.addEventListener('mouseenter', () => { toggleBtn.style.transform = 'scale(1.1)'; toggleBtn.style.backgroundColor = '#2980b9'; }); toggleBtn.addEventListener('mouseleave', () => { toggleBtn.style.transform = 'scale(1)'; toggleBtn.style.backgroundColor = '#3498db'; }); // 创建面板容器 const panel = document.createElement('div'); panel.id = 'cloze-panel'; panel.style.position = 'fixed'; panel.style.top = '20px'; panel.style.right = '20px'; panel.style.backgroundColor = 'rgba(255, 255, 255, 0.95)'; panel.style.border = '1px solid #e0e0e0'; panel.style.borderRadius = '8px'; panel.style.padding = '15px'; panel.style.zIndex = '10000'; panel.style.boxShadow = '0 4px 12px rgba(0,0,0,0.15)'; panel.style.fontFamily = '"Microsoft YaHei", sans-serif'; panel.style.width = '260px'; panel.style.transition = 'all 0.3s ease'; panel.style.display = 'none'; // 初始隐藏 panel.innerHTML = ` <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:15px;"> <h3 style="margin:0; color:#2c3e50; font-size:16px;">完形填空设置</h3> <button id="closePanelBtn" style="background:none; border:none; font-size:20px; cursor:pointer; color:#7f8c8d;">×</button> </div> <div style="margin-bottom:15px;"> <label style="display:block; margin-bottom:8px; font-weight:bold; color:#34495e;">替换概率 (0-1):</label> <input type="number" id="probabilityInput" min="0" max="1" step="0.1" value="${DEFAULT_PROBABILITY}" style="width:100%; padding:8px; border:1px solid #ddd; border-radius:4px; box-sizing:border-box;"> </div> <button id="generateBtn" style="background-color:#3498db; color:white; border:none; padding:10px 15px; border-radius:4px; cursor:pointer; width:100%; font-weight:bold; transition:background-color 0.3s; margin-bottom:10px;">生成填空</button> <button id="resetBtn" style="background-color:#95a5a6; color:white; border:none; padding:10px 15px; border-radius:4px; cursor:pointer; width:100%; font-weight:bold; transition:background-color 0.3s;">恢复原文</button> <div style="margin-top:15px; font-size:12px; color:#7f8c8d; line-height:1.5;"> <p>说明:</p> <ul style="padding-left:15px; margin-top:5px;"> <li>点击"生成填空"根据概率随机挖空</li> <li>点击"恢复原文"可恢复原始文本</li> <li>段首缩进会被保留且不可挖空</li> <li>标点符号始终可见</li> <li><strong>点击填空区域可显示原文</strong></li> </ul> </div> `; document.body.appendChild(toggleBtn); document.body.appendChild(panel); // 添加事件监听 toggleBtn.addEventListener('click', () => { panel.style.display = 'block'; toggleBtn.style.display = 'none'; }); document.getElementById('closePanelBtn').addEventListener('click', () => { panel.style.display = 'none'; toggleBtn.style.display = 'flex'; }); document.getElementById('generateBtn').addEventListener('click', generateCloze); document.getElementById('resetBtn').addEventListener('click', resetOriginal); } // 恢复原文 function resetOriginal() { const contentDiv = document.querySelector('.contson'); if (!contentDiv) return; if (contentDiv.dataset.original) { contentDiv.innerHTML = contentDiv.dataset.original; } } // 生成完形填空 function generateCloze() { // 获取用户设置的概率 const probability = parseFloat(document.getElementById('probabilityInput').value) || DEFAULT_PROBABILITY; // 找到诗文容器 const contentDiv = document.querySelector('.contson'); if (!contentDiv) { alert('未找到诗文内容!'); return; } // 保存原始内容以便重新生成 if (!contentDiv.dataset.original) { contentDiv.dataset.original = contentDiv.innerHTML; } else { // 重置为原始HTML,保留格式 contentDiv.innerHTML = contentDiv.dataset.original; } // 获取原始HTML内容 const originalHTML = contentDiv.innerHTML; // 创建新的HTML内容 let newHTML = originalHTML; // 处理段落:保留缩进和换行 if (originalHTML.includes('<p>') || originalHTML.includes('</p>')) { // 处理<p>标签分段 newHTML = processParagraphsWithPTags(originalHTML, probability); } else { // 处理<br>标签分段 const paragraphs = originalHTML.split('<br>'); if (paragraphs.length > 1) { newHTML = paragraphs.map(para => processParagraph(para, probability)).join('<br>'); } else { newHTML = processParagraph(originalHTML, probability); } } // 应用新内容 contentDiv.innerHTML = newHTML; // 添加样式 addStyles(); // 添加点击事件监听 addClickEvents(); } // 处理包含<p>标签的段落 function processParagraphsWithPTags(html, probability) { // 使用正则表达式分割<p>标签 const pTagRegex = /<p[^>]*>([\s\S]*?)<\/p>/gi; const matches = []; let lastIndex = 0; let match; // 查找所有<p>标签 while ((match = pTagRegex.exec(html)) !== null) { // 保存<p>标签前的文本 if (match.index > lastIndex) { matches.push({ type: 'text', content: html.substring(lastIndex, match.index) }); } // 保存<p>标签内容 matches.push({ type: 'p', content: match[1], fullTag: match[0] }); lastIndex = match.index + match[0].length; } // 保存最后一段文本 if (lastIndex < html.length) { matches.push({ type: 'text', content: html.substring(lastIndex) }); } // 处理每个段落 let processedHTML = ''; for (const item of matches) { if (item.type === 'p') { // 处理<p>标签内的内容 const processedContent = processParagraph(item.content, probability); processedHTML += item.fullTag.replace(item.content, processedContent); } else { // 处理非<p>标签的文本内容 processedHTML += processParagraph(item.content, probability); } } return processedHTML; } // 处理单个段落 function processParagraph(html, probability) { // 匹配段首空白(包括空格、全角空格和 ) const leadingSpaceRegex = /^(\s| | )+/; const spaceMatch = html.match(leadingSpaceRegex); let leadingSpaces = ''; let content = html; if (spaceMatch) { leadingSpaces = spaceMatch[0]; content = html.substring(spaceMatch[0].length); } // 根据标点符号切分句子 const sentences = splitSentences(content); // 处理每个句子 let processedContent = leadingSpaces; // 保留段首空白(不可挖空) for (const sentence of sentences) { // 跳过空句子 if (!sentence.trim()) continue; // 随机决定是否替换 if (Math.random() < probability) { // 创建带标点的下划线填空 const blankWithPunctuation = createBlankWithPunctuation(sentence); processedContent += blankWithPunctuation; } else { // 保留原句 processedContent += `<span class="cloze-sentence">${sentence}</span>`; } } return processedContent; } // 根据标点符号切分句子 function splitSentences(text) { // 中文标点符号:,。!?; const punctuation = /([,。!?;:“”])/g; const parts = text.split(punctuation); // 重新组合标点符号 const sentences = []; for (let i = 0; i < parts.length; i += 2) { const sentence = parts[i] + (parts[i+1] || ''); if (sentence.trim()) { sentences.push(sentence); } } return sentences; } // 创建带标点的下划线填空 function createBlankWithPunctuation(sentence) { // 分离文字和标点 const textParts = []; let currentText = ''; let currentPunctuation = ''; // 遍历每个字符 for (const char of sentence) { if (/[,。!?;:“”]/.test(char)) { // 如果是标点符号 if (currentText) { textParts.push({text: currentText, punctuation: ''}); currentText = ''; } currentPunctuation = char; textParts.push({text: '', punctuation: currentPunctuation}); } else { // 如果是文字 currentText += char; if (currentPunctuation) { textParts.push({text: currentText, punctuation: currentPunctuation}); currentText = ''; currentPunctuation = ''; } } } // 处理剩余的文本 if (currentText) { textParts.push({text: currentText, punctuation: currentPunctuation || ''}); } // 构建HTML let html = ''; for (const part of textParts) { if (part.punctuation) { // 标点符号始终可见 html += `<span class="cloze-punctuation">${part.punctuation}</span>`; } if (part.text) { // 文字部分替换为下划线,并存储原始文本 html += `<span class="cloze-blank" data-original="${escapeHTML(part.text)}">${part.text}</span>`; } } return html; } // HTML转义函数 function escapeHTML(str) { return str.replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } // 添加样式 function addStyles() { // 防止重复添加样式 if (document.getElementById('cloze-styles')) return; const style = document.createElement('style'); style.id = 'cloze-styles'; style.innerHTML = ` .cloze-blank { display: inline-block; position: relative; margin: 0 1px; padding: 0 2px; background-color: #f8f9fa; border-radius: 3px; vertical-align: baseline; line-height: 1.5; cursor: pointer; transition: all 0.3s ease; color: transparent !important; /* 文字透明 */ } .cloze-blank::after { content: ""; position: absolute; left: 0; bottom: 0; width: 100%; height: 2px; background-color: #3498db; z-index: 1; } .cloze-blank:hover { background-color: #e3f2fd; } .cloze-blank.revealed { background-color: #e8f5e9; color: #2e7d32 !important; /* 显示原文时文字颜色 */ font-family: inherit; letter-spacing: normal; } .cloze-blank.revealed::after { background-color: #81c784; } .cloze-sentence { display: inline; background-color: transparent; padding: 0; line-height: 1.5; } .cloze-punctuation { display: inline; margin: 0 1px; padding: 0; line-height: 1.5; color: #333; } .contson { line-height: 2; font-size: 18px; padding: 20px; background-color: #fcfcfc; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); } #cloze-toggle-btn:hover { transform: scale(1.1); background-color: #2980b9; } `; document.head.appendChild(style); } // 添加点击事件 function addClickEvents() { const blanks = document.querySelectorAll('.cloze-blank'); blanks.forEach(blank => { blank.addEventListener('click', function() { // 切换显示状态 this.classList.toggle('revealed'); }); }); } // 初始化 window.addEventListener('load', function() { createControlPanel(); // 保存原始内容 const contentDiv = document.querySelector('.contson'); if (contentDiv && !contentDiv.dataset.original) { contentDiv.dataset.original = contentDiv.innerHTML; } }); })();