您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
增强型阅读脚本,能够加粗部分单词,并高亮显示词汇表中的特定词汇,同时通过美观的悬浮框展示定义。可通过Alt+B(Windows/Linux)或Control+B(Mac)进行切换。
- // ==UserScript==
- // @name CogniRead
- // @namespace http://tampermonkey.net/
- // @version 1.0
- // @description 增强型阅读脚本,能够加粗部分单词,并高亮显示词汇表中的特定词汇,同时通过美观的悬浮框展示定义。可通过Alt+B(Windows/Linux)或Control+B(Mac)进行切换。
- // @match *://*/*
- // @grant none
- // @license MIT
- // @author codeboy
- // @icon https://cdn.icon-icons.com/icons2/609/PNG/512/book-glasses_icon-icons.com_56355.png
- // ==/UserScript==
- (function () {
- 'use strict';
- let cogniEnabled = false;
- let wordData = {};
- // Word list URLs
- const wordListSources = {
- 'TOEFL': 'https://raw.githubusercontent.com/CodeBoy2006/english-wordlists/master/TOEFL_abridged.txt',
- 'GRE': 'https://raw.githubusercontent.com/CodeBoy2006/english-wordlists/master/GRE_abridged.txt',
- 'OALD8': 'https://raw.githubusercontent.com/CodeBoy2006/english-wordlists/master/OALD8_abridged_edited.txt',
- 'TEM-8': 'https://raw.githubusercontent.com/CodeBoy2006/english-wordlists/refs/heads/master/%E8%8B%B1%E8%AF%AD%E4%B8%93%E4%B8%9A%E6%98%9F%E6%A0%87%E5%85%AB%E7%BA%A7%E8%AF%8D%E6%B1%87.txt'
- };
- // Load word lists
- async function fetchWordLists() {
- for (let [listName, url] of Object.entries(wordListSources)) {
- try {
- const response = await fetch(url);
- if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
- const text = await response.text();
- wordData[listName] = processWordList(text);
- console.log(`Loaded ${listName} wordlist with ${Object.keys(wordData[listName]).length} words.`);
- } catch (error) {
- console.error(`Failed to load ${listName} wordlist:`, error);
- }
- }
- }
- // Process word list text
- function processWordList(text) {
- const lines = text.split('\n');
- const dictionary = {};
- lines.forEach(line => {
- const [word, ...rest] = line.split(/\s+/);
- if (word) {
- dictionary[word.toLowerCase()] = rest.join(' ');
- }
- });
- return dictionary;
- }
- // Common suffixes
- const suffixes = ['s', 'es', 'ed', 'ing', 'er', 'est', 'ly'];
- // Match word in lists
- function matchWordInLists(word) {
- word = word.toLowerCase();
- // Direct match
- for (let list in wordData) {
- if (wordData[list].hasOwnProperty(word)) {
- return { word, list, definition: wordData[list][word] };
- }
- }
- // Match after removing common suffixes
- for (let suffix of suffixes) {
- if (word.endsWith(suffix)) {
- let stem = word.slice(0, -suffix.length);
- for (let list in wordData) {
- if (wordData[list].hasOwnProperty(stem)) {
- return { word: stem, list, definition: wordData[list][stem] };
- }
- }
- }
- }
- // Special cases
- if (word.endsWith('ies')) {
- let stem = word.slice(0, -3) + 'y';
- for (let list in wordData) {
- if (wordData[list].hasOwnProperty(stem)) {
- return { word: stem, list, definition: wordData[list][stem] };
- }
- }
- }
- if (word.endsWith('ves')) {
- let stem = word.slice(0, -3) + 'f';
- for (let list in wordData) {
- if (wordData[list].hasOwnProperty(stem)) {
- return { word: stem, list, definition: wordData[list][stem] };
- }
- }
- }
- return null;
- }
- function isWordInData(word) {
- return matchWordInLists(word) !== null;
- }
- // Style word
- function styleWord(word) {
- const match = word.match(/^([\w'-]+)([^\w'-]*)$/);
- if (!match) return word;
- const [, cleanWord, punctuation] = match;
- const wordLower = cleanWord.toLowerCase();
- const isHighlighted = isWordInData(cleanWord);
- let boldLength = Math.ceil(cleanWord.length / 2);
- let formattedWord = `<b>${cleanWord.slice(0, boldLength)}</b>${cleanWord.slice(boldLength)}`;
- if (isHighlighted) {
- formattedWord = `<span class="cogni-highlight" data-word="${wordLower}">${formattedWord}</span>`;
- } else {
- formattedWord = `<span class="cogni-word">${formattedWord}</span>`;
- }
- return formattedWord + punctuation;
- }
- // Handle text node
- function handleTextNode(textNode) {
- const words = textNode.textContent.split(/(\s+)/);
- const formattedText = words.map(word => word.trim() ? styleWord(word) : word).join('');
- const span = document.createElement('span');
- span.innerHTML = formattedText;
- textNode.replaceWith(span);
- }
- // Traverse and style text
- function traverseAndStyleText(node) {
- if (node.nodeType === Node.TEXT_NODE) {
- if (node.textContent.trim().length > 0) {
- handleTextNode(node);
- }
- } else if (node.nodeType === Node.ELEMENT_NODE) {
- if (!['SCRIPT', 'STYLE', 'TEXTAREA'].includes(node.tagName)) {
- Array.from(node.childNodes).forEach(traverseAndStyleText);
- }
- }
- }
- // Initialize tooltip
- function initTooltip() {
- let tooltip = document.createElement('div');
- tooltip.id = 'cogniread-tooltip';
- tooltip.style.cssText = `
- position: fixed;
- background: #ffffff;
- border: none;
- border-radius: 8px;
- padding: 12px 16px;
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.08);
- display: none;
- z-index: 9999;
- max-width: 300px;
- font-size: 14px;
- line-height: 1.6;
- color: #333333;
- pointer-events: none;
- transition: opacity 0.2s ease-in-out, transform 0.2s ease-in-out;
- opacity: 0;
- transform: translateY(10px);
- `;
- document.body.appendChild(tooltip);
- return tooltip;
- }
- // Display tooltip
- function displayTooltip(word, event) {
- const tooltip = document.getElementById('cogniread-tooltip');
- if (!tooltip) return;
- const matchedWord = matchWordInLists(word);
- if (matchedWord) {
- const { word: matchedWordText, list: sourceList, definition } = matchedWord;
- tooltip.innerHTML = `
- <strong>${word}</strong> ${word !== matchedWordText ? `(${matchedWordText})` : ''}<br>
- ${definition}<br>
- <small>Source: ${sourceList}</small>
- `;
- tooltip.style.display = 'block';
- requestAnimationFrame(() => {
- adjustTooltipPosition(tooltip, event);
- tooltip.style.opacity = '1';
- tooltip.style.transform = 'translateY(0)';
- });
- }
- }
- function removeTooltip() {
- const tooltip = document.getElementById('cogniread-tooltip');
- if (tooltip) {
- tooltip.style.opacity = '0';
- tooltip.style.transform = 'translateY(10px)';
- setTimeout(() => {
- if (tooltip.style.opacity === '0') {
- tooltip.style.display = 'none';
- }
- }, 200);
- }
- }
- function adjustTooltipPosition(tooltip, event) {
- const tooltipRect = tooltip.getBoundingClientRect();
- let left = event.clientX + 10;
- let top = event.clientY + 10;
- if ((left + tooltipRect.width) > window.innerWidth) {
- left = event.clientX - tooltipRect.width - 10;
- }
- if ((top + tooltipRect.height) > window.innerHeight) {
- top = event.clientY - tooltipRect.height - 10;
- }
- tooltip.style.left = `${left}px`;
- tooltip.style.top = `${top}px`;
- }
- const updateTooltipPositionDebounced = debounce((tooltip, event) => {
- adjustTooltipPosition(tooltip, event);
- }, 10);
- function debounce(func, wait) {
- let timeout;
- return function executedFunction(...args) {
- const later = () => {
- clearTimeout(timeout);
- func(...args);
- };
- clearTimeout(timeout);
- timeout = setTimeout(later, wait);
- };
- }
- // Inject CSS styles
- function addStyles() {
- const style = document.createElement('style');
- style.innerHTML = `
- .cogni-word b { color: #000080; }
- .cogni-highlight { background-color: yellow; cursor: pointer; }
- #cogniread-tooltip { font-family: Arial, sans-serif; }
- `;
- document.head.appendChild(style);
- }
- // Enable CogniRead
- async function enableCogniRead() {
- console.log("Applying CogniRead...");
- await fetchWordLists();
- addStyles();
- traverseAndStyleText(document.body);
- initTooltip();
- console.log("CogniRead is now active.");
- }
- // Toggle CogniRead
- function toggleCogniRead() {
- cogniEnabled = !cogniEnabled;
- if (cogniEnabled) {
- enableCogniRead();
- } else {
- location.reload();
- }
- }
- // Set keyboard shortcut
- const isMacOS = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
- document.addEventListener('keydown', function(e) {
- if ((isMacOS && e.ctrlKey && e.key.toLowerCase() === 'b') || (!isMacOS && e.altKey && e.key.toLowerCase() === 'b')) {
- e.preventDefault();
- toggleCogniRead();
- }
- });
- // Event delegation for tooltip display
- document.body.addEventListener('mouseover', function(e) {
- const target = e.target.closest('.cogni-highlight');
- if (target) {
- const word = target.getAttribute('data-word');
- displayTooltip(word, e);
- }
- });
- document.body.addEventListener('mousemove', function(e) {
- const tooltip = document.getElementById('cogniread-tooltip');
- if (tooltip && tooltip.style.display !== 'none') {
- updateTooltipPositionDebounced(tooltip, e);
- }
- });
- document.body.addEventListener('mouseout', function(e) {
- if (!e.relatedTarget || !e.relatedTarget.closest('.cogni-highlight')) {
- removeTooltip();
- }
- });
- window.addEventListener('scroll', debounce(function() {
- const tooltip = document.getElementById('cogniread-tooltip');
- if (tooltip && tooltip.style.display !== 'none') {
- removeTooltip();
- }
- }, 100));
- console.log("CogniRead script loaded. Use Ctrl+B (Mac) or Alt+B (Windows/Linux) to toggle.");
- })();