// ==UserScript== // @name 多邻国拼音选词快捷键简化版(快捷键不包括删除选词) // @namespace http://tampermonkey.net/ // @version 1.24 // @description 使用快捷键刷多邻国. 在主页面使用Enter键快速开始学习;在学习页使用ctrl键播放语音, 对中文选词题使用回车键为选词添加拼音(多音字可能有偏差),退格键删除选词. 如果官方和脚本的快捷键无法正常使用, 需要在`vimium-c`等快捷键相关插件中排除多邻国网站. 如果发生无法输入文字的情况可以尝试在网页限制解除/文本选中复制相关脚本中排除多邻国网站 参考多邻国选词快捷键https://greasyfork.org/zh-CN/scripts/493966-%E5%A4%9A%E9%82%BB%E5%9B%BD%E9%80%89%E8%AF%8D%E5%BF%AB%E6%8D%B7%E9%94%AE // @author Gelan // @match https://www.duolingo.cn/* // @match https://www.duolingo.com/* // @license MIT // @icon https://d35aaqx5ub95lt.cloudfront.net/images/super/fb7130289a205fadd2e196b9cc866555.svg // @require https://cdn.staticfile.org/jquery/3.3.1/jquery.min.js // @require http://cdn.staticfile.org/jquery/1.8.3/jquery.min.js // @require https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js // @require https://unpkg.com/pinyin-pro // @grant GM_log // @grant GM_addStyle // @downloadURL none // ==/UserScript== ;(function () { 'use strict' // 引入pinyin库 var { pinyin } = pinyinPro; // 题目区元素相关数据对象 // type -1: 无效 0: 选择题(自带[数字]快捷键) 1: 中文组句题 2: 配对题(自带[数字]快捷键) // 3: 填空题(自带[首字母]快捷键) 4: 听写题(不需要处理) 5: 听写填空题(不需要处理) // 6: 小故事 7: 补全题(自带[首字母]快捷键) 8: 口语题 // el: 主题目区元素 // el2:次题目区元素 var question = { type: -1 } var items = []; // 始初化题目数据对象方法 var init_question = function () { // 口语题 question.el = document.querySelector( 'div[data-test="challenge challenge-speak"], div[data-test="challenge challenge-listenSpeak"]' ) if (question.el) { question.type = 8 return } // 补全题(不需要处理) question.el = document.querySelector( 'div[data-test="challenge challenge-tapCloze"]' ) if (question.el) { question.type = 7 return } // 小故事 question.el = document.getElementsByClassName('kbjat') if (question.el.length) { question.el = question.el[0].children question.type = 6 // 每段数据所在属性名 question.prop_field = Object.keys(question.el[0]).find(p => p.startsWith('__reactFiber') ) return } // 听写填空题(不需要处理) question.el = document.querySelector( 'div[data-test="challenge challenge-listenComplete"]' ) if (question.el) { question.type = 5 return } // 听写题(不需要处理) question.el = document.querySelector( 'div[data-test="challenge challenge-listenTap"]' ) if (question.el) { question.type = 4 return } // 填空题(自带,不需要处理) question.el = document.querySelector( 'div[data-test="challenge challenge-tapComplete"]' ) if (question.el) { question.type = 3 return } // 配对题(自带,不需要处理) question.el = document.querySelector( 'div[data-test="challenge challenge-listenMatch"]' ) if (question.el) { question.el = question.el.children[0].children[1].children[0] question.type = 2 return } // 中文组句题 var challengeHeader = document.querySelector('h1[data-test="challenge-header"]'); if (challengeHeader) { var spanElement = challengeHeader.querySelector('span'); if (spanElement && spanElement.textContent.trim() === "用中文写出这句话") { question.el = document.querySelector('div[data-test="word-bank"]') question.el2 = question.el.parentElement.previousElementSibling.children[0].children[0].children[1] question.type = 1 return } } // 选择题(自带,不需要处理) question.el = document.querySelector('div[aria-label="choice"]') if (question.el) { question.type = 0 return } // 未知题型 question.type = -1 } // 防抖方法 function debounce (func, delay) { let timeout return function () { const _this = this const args = [...arguments] if (timeout) { clearTimeout(timeout) } timeout = setTimeout(() => { func.apply(_this, args) }, delay) } } // 为单词/短语添加拼音 var process_order = function () { var play_btn = document.querySelector('button[data-test="player-next"]') if (!play_btn) { return } init_question() if (question.type == 1) { if(document.querySelector('rt')) { return; } for (var i = 0; i < question.el.children.length; i++) { var span = question.el.children[i].querySelector('span[data-test="challenge-tap-token-text"]'); var rt = document.createElement('rt'); rt.style.cssText = 'color: gray; display: flex; flex-direction: row;'; rt.style.fontSize = parseFloat(window.getComputedStyle(span).fontSize) * 0.5 + 'px'; rt.textContent = pinyin(span.textContent, { toneType: 'none' }); span.parentElement.style.cssText = 'display: flex; flex-direction: column;'; span.parentElement.insertBefore(rt, span); } } } // 为单词/短语添加拼音方法(防抖) var process_order_debounce = debounce(process_order, 500) let matchIndex = 0; let pinyinIndex = 0; let matchs = []; let pinyins = []; let spancolor = 'red'; // 按键事件监听 document.addEventListener('keyup', function (event) { GM_log('按键:' + event.key) // 当前页 var page_name = window.location.pathname // 在主页 if (page_name == '/learn') { // 按Enter键直接学习(跳转/lesson页) if (event.key == 'Enter') { window.location.href = '/lesson' } } // 初始化题目区数据 if (event.key == 'Enter') { process_order_debounce() } // Tab键,跳过题目或自动答题(需下载Duolingo Pro BETA) if (event.key == 'Tab') { let solve = document.querySelector('#solveAllButton') if(solve) { solve.previousElementSibling.click() } else { document.querySelector('button[data-test="player-skip"]').click() } } // Backspace键, 删除最后一个选词 if (event.key == 'Backspace') { if (question.el2) { var selects = question.el2.children var cnt = selects.length var last_select = selects[cnt - 1] last_select.querySelector('button').click() } return } // Shift键, 在按钮选词和直接输入中切换 或 在减小难度和增大难度中切换 if (event.key == 'Shift') { document.querySelector('button[data-test="player-toggle-keyboard"]').click() return } // Control键, 点击扬声器按钮播放语音 if (event.key == 'Control') { if (question.type == 6) { // 小故事, 找最后一个已读的音频 var last_listen for (var i = 0; i < question.el.length; i++) { // 当前遍历的元素 var el = question.el[i] // 当前遍历的元素包含的类列表 var class_list = Array.from(el.classList) // 当前元素的音频是否已听过 var flag = el[question.prop_field].flags // 只有一个类, 答题区, 忽略 if (class_list.length == 1) { continue } // 有两个类 if (class_list.length == 2) { // flag>0时已经听过, flag=0时为标题, 设为最后听过的音频元素 last_listen = el continue } // 有三个类 if (class_list.length == 3) { if (flag == 0) { // 没有听过, 结束循环 break } else { // 有三个类但flag>0的情况, 第三个类不同于常规音频, // 表明该元素非音频, 属于有四个类(听力组句题)的在答情况 continue } } // 有四个类, 听力组句题, flag=0时未答, flag>0时已答 if (class_list.length == 4) { continue } } if (last_listen) { last_listen.querySelector('div[data-test="audio-button"]').click() } } else { // 常规题, 找第一个播放按钮 var els = document.getElementsByClassName('fs-exclude') if (els) { els[0].click() } } return } // 组句题 if (question.type == 1) { pinyins = document.querySelectorAll('rt'); spancolor = document.querySelector('span[data-test="challenge-tap-token-text"]').style.color; const keyPressed = event.key; matchs = []; for (let i = 0; i < pinyins.length; i++) { let pinyin = pinyins[i]; if (pinyin.innerText[pinyinIndex] === keyPressed && pinyin.parentElement.parentElement.getAttribute('aria-disabled') != 'true') { if (pinyinIndex === 0) { pinyin.style.color = 'lightblue'; } else { if (pinyin.style.color !== 'lightblue') { continue; } } if(pinyin.innerText[pinyinIndex] === ' ') { event.preventDefault(); } // 当一边文本即将匹配完,用.区分其他文本 if(pinyinIndex + 1 === pinyin.textContent.length && !pinyin.textContent.includes('.') && !pinyin.textContent.match(/\d+/g)) { pinyin.textContent += '.'; } matchs.push(pinyin); } else { pinyin.style.color = 'gray'; } } if (matchs.length === 1) { matchs[0].parentElement.parentElement.click(); pinyinIndex = 0; matchs = []; } else if (matchs.length > 1) { let num = 1; let asame = true; let basetext = matchs[0].textContent; // 判断相同文本 for (let i = 1; i < matchs.length; i++) { if (matchs[i].textContent !== basetext) { asame = false; break; } } if(asame === true) { if(matchs.length > 1) { for (let i = 0; i < matchs.length; i++) { if (!matchs[i].textContent.match(/\d+/g)) { matchs[i].textContent += num++; } } } } pinyinIndex++; } else { pinyinIndex = 0; matchs = []; } } }) })()