// ==UserScript==
// @name 选中自动朗读
// @namespace http://tampermonkey.net/
// @version 0.5
// @description 选中自动朗读(🌈 支持大部份语言!!!🕶️)
// @license
// @author lgldlk
// @match *://*/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=chrome.com
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// @grant GM_openInTab
// @grant GM_getValue
// @grant GM_setValue
// @license MIT
// @sandbox JavaScript
// ==/UserScript==
// 定义一个防抖函数
function debounce(fn, delay) {
let timeout;
return function () {
clearTimeout(timeout);
timeout = setTimeout(() => {
fn.apply(this, arguments);
}, delay);
};
}
// franc对应bcp 47语言
const codeMap = {
cmn: 'zh-CN',
spa: 'es',
eng: 'en-GB',
rus: 'ru',
arb: 'ar',
ben: 'bn',
hin: 'hi',
por: 'pt',
ind: 'id',
jpn: 'ja',
fra: 'fr',
deu: 'de',
jav: 'jv',
kor: 'ko',
tel: 'te',
vie: 'vi',
mar: 'mr',
ita: 'it',
tam: 'ta',
tur: 'tr',
urd: 'ur',
guj: 'gu',
pol: 'pl',
ukr: 'uk',
kan: 'kn',
mai: 'mai',
mal: 'ml',
pes: 'fa',
mya: 'my',
swh: 'sw',
sun: 'su',
ron: 'ro',
pan: 'pa',
bho: 'bho',
amh: 'am',
hau: 'ha',
fuv: 'fuv',
bos: 'bs',
hrv: 'hr',
nld: 'nl',
srp: 'sr',
tha: 'th',
ckb: 'ku',
yor: 'yo',
uzn: 'uz',
zlm: 'ms',
ibo: 'ig',
npi: 'ne',
ceb: 'ceb',
skr: 'skr',
tgl: 'tl',
hun: 'hu',
azj: 'az',
sin: 'si',
koi: 'koi',
ell: 'el',
ces: 'cs',
mag: 'mag',
run: 'rn',
bel: 'be',
plt: 'mg',
qug: 'qug',
mad: 'mad',
nya: 'ny',
zyb: 'za',
pbu: 'ps',
kin: 'rw',
zul: 'zu',
bul: 'bg',
swe: 'sv',
lin: 'ln',
som: 'so',
hms: 'hms',
hnj: 'hnj',
ilo: 'ilo',
jpn: 'ja',
kaz: 'kk',
};
(function (window) {
'use strict';
const langScript = document.createElement('script');
langScript.type = 'module';
langScript.innerHTML = `
import { franc, francAll } from 'https://cdn.jsdelivr.net/npm/franc-min@6.1.0/+esm';
function debounce(fn, delay) {
let timeout;
return function () {
clearTimeout(timeout);
timeout = setTimeout(() => {
fn.apply(this, arguments);
}, delay);
};
}
const selectText = debounce(() => {
let selectText = String(document.getSelection());
document.dispatchEvent(
new CustomEvent('whatLang888', {
detail: {
fLang: franc(selectText),
text: selectText,
},
})
);
}, 300);
document.addEventListener('selectionchange', selectText);
`;
document.head.appendChild(langScript);
var menu_ALL = [['menu_disable', '🟢 已启用 (点击对当前网站禁用)', '🔴 已禁用 (点击对当前网站启用)', []]],
menu_ID = [];
let isDisable = menu_disable('check');
let allLanguage = true;
let limitTextLength = GM_getValue('menu_limitTextLength') ?? 500; // 超过多少字不朗读
// 菜单开关
function menu_switch(menu_status, Name, Tips) {
if (menu_status == 'true') {
GM_setValue(`${Name}`, false);
} else {
GM_setValue(`${Name}`, true);
}
registerMenuCommand(); // 重新注册脚本菜单
}
// 返回菜单值
function menu_value(menuName) {
for (let menu of menu_ALL) {
if (menu[0] == menuName) {
return menu[3];
}
}
}
for (let i = 0; i < menu_ALL.length; i++) {
// 如果读取到的值为 null 就写入默认值
if (GM_getValue(menu_ALL[i][0]) == null) {
GM_setValue(menu_ALL[i][0], menu_ALL[i][3]);
}
}
registerMenuCommand();
// 注册脚本菜单
function registerMenuCommand() {
if (menu_ID.length != []) {
for (let i = 0; i < menu_ID.length; i++) {
GM_unregisterMenuCommand(menu_ID[i]);
}
}
for (let i = 0; i < menu_ALL.length; i++) {
// 循环注册脚本菜单
menu_ALL[i][3] = GM_getValue(menu_ALL[i][0]);
if (menu_ALL[i][0] === 'menu_disable') {
// 启用/禁用护眼模式 (当前网站)
if (menu_disable('check')) {
// 当前网站是否已存在禁用列表中
menu_ID[i] = GM_registerMenuCommand(`${menu_ALL[i][2]}`, function () {
menu_disable('del');
});
return;
} else {
menu_ID[i] = GM_registerMenuCommand(`${menu_ALL[i][1]}`, function () {
menu_disable('add');
});
}
} else {
menu_ID[i] = GM_registerMenuCommand(`${menu_ALL[i][3] ? '🟢' : '🔴'} ${menu_ALL[i][1]}`, function () {
menu_switch(`${menu_ALL[i][3]}`, `${menu_ALL[i][0]}`, `${menu_ALL[i][2]}`);
});
}
}
menu_ID[menu_ID.length] = GM_registerMenuCommand('📻 限制多少字以上不朗读(目前:' + limitTextLength + ')', function () {
const aNumber = Number(window.prompt('请输入限制字数~', ''));
if (isNaN(aNumber)) {
alert('请输入数字');
return;
}
if (aNumber < 0) {
alert('请输入大于0的数字');
return;
}
GM_unregisterMenuCommand('📻 限制多少字以上不朗读(目前:' + limitTextLength + ')');
limitTextLength = aNumber;
GM_setValue('menu_limitTextLength', aNumber);
registerMenuCommand();
});
menu_ID[menu_ID.length] = GM_registerMenuCommand('📬 欢迎提出反馈和建议,我会非常重视您的意见。', function () {
window.GM_openInTab('https://greasyfork.org/zh-CN/scripts/471347/feedback', { active: true, insert: true, setParent: true });
});
}
// 启用/禁用护眼模式 (当前网站)
function menu_disable(type) {
switch (type) {
case 'check':
return check();
case 'add':
add();
break;
case 'del':
del();
break;
}
function check() {
// 存在返回真,不存在返回假
let websiteList = menu_value('menu_disable'); // 读取网站列表
if (websiteList.indexOf(location.host) === -1) return false; // 不存在返回假
return true;
}
function add() {
if (check()) return;
let websiteList = menu_value('menu_disable'); // 读取网站列表
websiteList.push(location.host); // 追加网站域名
GM_setValue('menu_disable', websiteList); // 写入配置
isDisable = true;
registerMenuCommand();
}
function del() {
if (!check()) return;
let websiteList = menu_value('menu_disable'), // 读取网站列表
index = websiteList.indexOf(location.host);
websiteList.splice(index, 1); // 删除网站域名
GM_setValue('menu_disable', websiteList); // 写入配置
isDisable = false;
registerMenuCommand();
}
}
const speakFunc = ({ detail }) => {
const { text, fLang } = detail;
if (isDisable) return;
if (!text.length || text.length > limitTextLength) return;
let ssu = new SpeechSynthesisUtterance(text);
ssu.lang = codeMap[fLang];
speechSynthesis.cancel();
speechSynthesis.speak(ssu);
};
document.addEventListener('whatLang888', speakFunc);
/*
啊,这是什么?😮失忆喷雾?喷一下💦。啊,这是什么?😮失忆喷雾?喷一下💦。啊,这是什么?😮失忆喷雾?喷一下💦。啊,这是什么?😮失忆喷雾?喷一下💦。啊,这是什么?😮失忆喷雾?喷一下💦。啊,这是什么?😮失忆喷雾?喷一下💦。啊,这是什么?😮失忆喷雾?喷一下💦。啊,这是什么?😮失忆喷雾?喷一下💦。
😮💭💦💭😮💦💭😮💦💭😮💦💭😮💦💭😮💦💭😮💦💭😮💦
😮💨▪🈁🫡😶❔😮😥🤔⛲🌫❔⛲☝👇💦▪😮💨▪🈁🫡😶❔😮😥🤔⛲🌫❔⛲☝👇💦▪😮💨▪🈁🫡😶❔😮😥🤔⛲🌫❔⛲☝👇💦▪😮💨▪🈁🫡😶❔😮😥🤔⛲🌫❔⛲☝👇💦▪😮💨▪🈁🫡😶❔😮😥🤔⛲🌫❔⛲☝👇💦▪😮💨▪🈁🫡😶❔😮😥🤔⛲🌫❔⛲☝👇💦▪
*/
})(window);