// ********用户自定义配置区****************** // 是否默认显示中文翻译 false=不显示 true=显示 (点击"词性导航"白色块可显示/隐藏中文翻译) var show_translation = true; // 是否默认显示词性导航 false=不显示 true=显示 var show_nav = true; // 是否默认启用英文点译功能(单句显示/隐藏中文) false=否 true=是 var touch_translate = true; // 是否默认启用在线tts发音(需要高版本浏览器内核。发音图标为灰色,左喇叭为英式,右喇叭为美式。Mdict不支持。) false=否 true=是 var enable_online_tts = true; // 是否默认展开义项 false=否 true=是 var unfold_sense = true; // ********用户自定义配置区****************** // 以下用户请不要修改 // 例句,释义,在线tts发音,需要高版本浏览器内核. Mdict不支持 // enable_online_tts = false; // 英音发音配置 英音男1 - 英音男2 ; 英音女1 - 英音女3 gb_tts = "英音女1"; // 美音发音配置 美音男1 - 美音男5 ; 美音女1 - 美音女4 us_tts = "美音男1"; var globalAudio = new Audio(); // // 是否默认选中词性导航all false=否 true=是 var select_navAll = true; // var initialization = false; var dict_entry_index = {}; dylan_mwa = { select_entry: function() { selected_category = $(".mwa-perfection-nav span.active").text(); entrys = $("body-content.mwa-perfection div#ld_entries_v2_all").find(".entry"); if (selected_category === "All") { entrys.css("display", "block"); $("body-content.mwa-perfection .hw_d.boxy.m_hidden").removeClass("active"); $("body-content.mwa-perfection div#ld_entries_v2_all").each(function(){ $(this).show(); }); } else { nav_spans = $(".mwa-perfection-nav span"); selected_index = -1; for (var i = 0; i < nav_spans.length; i++) { if (nav_spans.eq(i).hasClass("active")) { entrys.eq(i).css("display", "block"); entrys.eq(i).find(".hw_d.boxy.m_hidden").addClass("active"); selected_index = i; } else { entrys.eq(i).css("display", "none"); entrys.eq(i).find(".hw_d.boxy.m_hidden").removeClass("active"); } } entries_index = 0; $("body-content.mwa-perfection div#ld_entries_v2_all").each(function(){ if (dict_entry_index[selected_index] == entries_index) { $(this).show(); } else { $(this).hide(); } entries_index++; }); } } }; document.addEventListener('DOMContentLoaded', function () { // 防止重复初始化 if ($("body-content.mwa-perfection #is-mwa-loaded").length != 0) { dict_entry_index = JSON.parse($("body-content.mwa-perfection #is-mwa-loaded").text()) return } else { $('').appendTo('body-content.mwa-perfection'); } // 隐藏欧路自带的hr标签 $("body-content.mwa-perfection").parent().find("hr").not('#ld_entries_v2_all hr').hide(); // 是否显示中文翻译 false=不显示 true=显示 !show_translation && $("body-content.mwa-perfection .mw_zh").hide(); // 是否显示词性导航 false=不显示 true=显示 if (show_nav) { // 创建entry与entries的映射表,用来点击词性导航时,隐藏多余的entries entry_num = 0; entries_num = 0; $("body-content.mwa-perfection div#ld_entries_v2_all").each(function(){ $(this).find(".entry").each(function(){ dict_entry_index[entry_num] = entries_num; entry_num++; }); entries_num++; }); $("body-content.mwa-perfection #is-mwa-loaded").text(JSON.stringify(dict_entry_index, null, 2)); // 添加词性导航栏 $("body-content.mwa-perfection").parent().prepend("
"); // $("body-content.mwa-perfection #ld_entries_v2_all").before(""); // 添加词性 $("body-content.mwa-perfection div#ld_entries_v2_all").find(".entry").each(function(){ var category = $(this).find("div.hw_d span.fl").text(); if (category === "") { if ($(this).parent().find("div.sms").length == 1) { gram_internal = $(this).parent().find(".gram_internal"); uro_line_fl = $(this).parent().find(".uro_line > span.fl"); if (gram_internal.length == 1) { category = gram_internal.text(); } else if (uro_line_fl.length == 1) { category = uro_line_fl.text(); } else { category = "*"; } } else if ($(this).parent().find("div.cxs span.cl").length == 1) { category = $(this).parent().find("div.cxs span.cl").text(); if (category.length > 3) { if (category.slice(-3) == " of") { category = category.slice(0, -3); } } } else { category = "*"; } } $(".mwa-perfection-nav").append(`${category}`); }); // 添加All分类 $(".mwa-perfection-nav").append("All"); // 是否默认选中词性导航all false=否 true=是 if (select_navAll) { $(".mwa-perfection-nav span:last-child").addClass("active"); // 词性导航滚动到最右边 if (select_navAll) { var nav_mwa = $(".mwa-perfection-nav"); // 由于手机欧路滚动失效,所以加10000个像素 nav_mwa.scrollLeft(nav_mwa.scrollLeft() + nav_mwa.width() + 10000); } } // 导航栏事件开始 var clickTimer; show_nav && $(".mwa-perfection-nav").find("span").on("click", function() { if ($(this).hasClass("active")) { clearTimeout(clickTimer); thisobj = this clickTimer = setTimeout(function() { console.log("导航栏单击事件触发1"); if ($(thisobj).hasClass("active")) { if (show_translation) { $("body-content.mwa-perfection .mw_zh").fadeOut("fast"); console.log("隐藏"); } else { $("body-content.mwa-perfection .mw_zh").fadeIn("fast"); console.log("显示"); } show_translation = !show_translation } else { $(".mwa-perfection-nav span").removeClass("active"); $(thisobj).addClass("active"); dylan_mwa.select_entry(); } }, 250); } else { console.log("导航栏单击事件触发2"); if ($(this).hasClass("active")) { if (show_translation) { $("body-content.mwa-perfection .mw_zh").fadeOut("fast"); console.log("隐藏"); } else { $("body-content.mwa-perfection .mw_zh").fadeIn("fast"); console.log("显示"); } show_translation = !show_translation } else { $(".mwa-perfection-nav span").removeClass("active"); $(this).addClass("active"); dylan_mwa.select_entry(); } } }); show_nav && $(".mwa-perfection-nav").find("span").on("dblclick", function() { if ($(this).hasClass("active")) { clearTimeout(clickTimer); console.log("导航栏双击事件触发"); // 不直接使用全局变量是因为欧路有时候js会重复加载 if ($("body-content.mwa-perfection #concise_meaning").length == 0) { concise_meaning = false; $('').appendTo('body-content.mwa-perfection'); } else { concise_meaning = true; $("body-content.mwa-perfection #concise_meaning").remove(); } if (!concise_meaning) { // 全部折叠 $("body-content.mwa-perfection .sense .vis_w, body-content.mwa-perfection .sense .usageref_block, body-content.mwa-perfection .sense .dxs.dxs_nonl, body-content.mwa-perfection .sense .usage_par").slideUp("fast"); // console.log("全部折叠"); } else { // 全部展开 $("body-content.mwa-perfection .sense .vis_w, body-content.mwa-perfection .sense .usageref_block, body-content.mwa-perfection .sense .dxs.dxs_nonl, body-content.mwa-perfection .sense .usage_par").slideDown("fast"); // console.log("全部展开"); } concise_meaning = !concise_meaning } }); // 导航栏事件结束 // // 添加词性导航栏单击事件 // $(".mwa-perfection-nav span").click(function(){ // if ($(this).hasClass("active")) { // if (show_translation) { // $("body-content.mwa-perfection .mw_zh").fadeOut("fast"); // console.log("隐藏"); // } else { // $("body-content.mwa-perfection .mw_zh").fadeIn("fast"); // console.log("显示"); // } // show_translation = !show_translation // } else { // $(".mwa-perfection-nav span").removeClass("active"); // $(this).addClass("active"); // dylan_mwa.select_entry(); // } // }); } // 是否启用英文点译功能(单句显示/隐藏中文) false=否 true是 if (touch_translate) { // 项义点击事件:显示和隐藏中文 $("body-content.mwa-perfection span.def_text").click(function(event){ event.stopPropagation(); $(this).find(".mw_zh").fadeToggle("fast"); }); // 例句点击事件:显示和隐藏中文 $("body-content.mwa-perfection ul.vis > li.vi").click(function(event){ event.stopPropagation(); if ($(this).find(".mw_zh").is(":visible")) { $(this).find(".mw_zh").slideUp("fast"); } else { $(this).find(".mw_zh").fadeToggle("fast"); } }); // Usage用法释义点击事件:显示和隐藏中文 $("body-content.mwa-perfection span.ud_text").click(function(event){ event.stopPropagation(); $(this).find(".mw_zh").fadeToggle("fast"); }); // 固定搭配释义点击事件:显示和隐藏中文 $("body-content.mwa-perfection span.un_text").click(function(event){ event.stopPropagation(); $(this).find(".mw_zh").fadeToggle("fast"); }); } // 是否默认展开义项 !unfold_sense && $("body-content.mwa-perfection .sense .vis_w").hide("fast"); // 义项数字点击事件 $("body-content.mwa-perfection .sn_block_num").click(function(event){ event.stopPropagation(); ele_vis_w = $(this).parents(".sblock.sblock_entry, .sblock.sblock_dro").find(".sense .vis_w"); ele_usageref_block = $(this).parents(".sblock.sblock_entry, .sblock.sblock_dro").find(".sense .usageref_block"); ele_dxs_nonl = $(this).parents(".sblock.sblock_entry, .sblock.sblock_dro").find(".sense .dxs_nonl"); ele_usage_par = $(this).parents(".sblock.sblock_entry, .sblock.sblock_dro").find(".sense .usage_par"); bool_isvisible = ele_vis_w.is(":visible") || ele_usageref_block.is(":visible") || ele_dxs_nonl.is(":visible") || ele_usage_par.is(":visible") if (bool_isvisible) { $(this).parents(".sblock.sblock_entry, .sblock.sblock_dro").find(".sense .vis_w, .sense .usageref_block, .sense .dxs.dxs_nonl, .sense .usage_par").slideUp("fast"); } else { $(this).parents(".sblock.sblock_entry, .sblock.sblock_dro").find(".sense .vis_w, .sense .usageref_block, .sense .dxs.dxs_nonl, .sense .usage_par").slideDown("fast"); } }); $("body-content.mwa-perfection h2.dre").click(function(event){ event.stopPropagation(); ele_vis_w = $(this).parents(".dro").find(".sense .vis_w"); ele_usageref_block = $(this).parents(".sblock.sblock_entry, .sblock.sblock_dro").find(".sense .usageref_block"); ele_dxs_nonl = $(this).parents(".sblock.sblock_entry, .sblock.sblock_dro").find(".sense .dxs_nonl"); ele_usage_par = $(this).parents(".sblock.sblock_entry, .sblock.sblock_dro").find(".sense .usage_par"); bool_isvisible = ele_vis_w.is(":visible") || ele_usageref_block.is(":visible") || ele_dxs_nonl.is(":visible") || ele_usage_par.is(":visible") if (bool_isvisible) { $(this).parents(".dro").find(".sense .vis_w, .sense .usageref_block, .sense .dxs.dxs_nonl, .sense .usage_par").slideUp("fast"); } else { $(this).parents(".dro").find(".sense .vis_w, .sense .usageref_block, .sense .dxs.dxs_nonl, .sense .usage_par").slideDown("fast"); } }); // 添加机读 enable_online_tts && $("body-content.mwa-perfection div.vi_content").after('') // 例句机读事件 enable_online_tts && $("body-content.mwa-perfection example-audio-ai a.audio_uk").click(function(){ event.stopPropagation(); selectedTts = gb_tts; eleExText = $(this).parent().siblings("div.vi_content").clone(); eleExText.find(".mw_zh").remove(); speak(eleExText.text().replace(/\(.*?\)/g, "").replace(/\[.*?\]/g, "")); }) enable_online_tts && $("body-content.mwa-perfection example-audio-ai a.audio_us").click(function(){ event.stopPropagation(); selectedTts = us_tts; eleExText = $(this).parent().siblings("div.vi_content").clone(); eleExText.find(".mw_zh").remove(); speak(eleExText.text().replace(/\(.*?\)/g, "").replace(/\[.*?\]/g, "")); }) // console.log($("#is-mwa-loaded").text()); // dict_entry_index = JSON.parse($("#is-mwa-loaded").text()) // console.log(dict_entry_index) }) // ################# //region tts 功能 var ttsConfig = { "美音女1": { locale: "en-US", voice: "en-US-MichelleNeural", pitch: "+0Hz", rate: "+0%", volume: "+0%", }, "美音女2": { locale: "en-US", voice: "en-US-AriaNeural", pitch: "+0Hz", rate: "+20%", volume: "+0%", }, "美音女3": { locale: "en-US", voice: "en-US-AnaNeural", pitch: "+0Hz", rate: "+20%", volume: "+0%", }, "美音女4": { locale: "en-US", voice: "en-US-JennyNeural", pitch: "+0Hz", rate: "+0%", volume: "+0%", }, "美音男1": { locale: "en-US", voice: "en-US-ChristopherNeural", pitch: "+0Hz", rate: "+0%", volume: "+0%", }, "美音男2": { locale: "en-US", voice: "en-US-EricNeural", pitch: "+0Hz", rate: "+0%", volume: "+0%", }, "美音男3": { locale: "en-US", voice: "en-US-GuyNeural", pitch: "+0Hz", rate: "+0%", volume: "+0%", }, "美音男4": { locale: "en-US", voice: "en-US-RogerNeural", pitch: "+0Hz", rate: "+0%", volume: "+0%", }, "美音男5": { locale: "en-US", voice: "en-US-SteffanNeural", pitch: "+0Hz", rate: "+0%", volume: "+0%", }, "英音女1": { locale: "en-GB", voice: "en-GB-SoniaNeural", pitch: "+0Hz", rate: "+0%", volume: "+0%", }, "英音女2": { locale: "en-GB", voice: "en-GB-MaisieNeural", pitch: "+0Hz", rate: "+0%", volume: "+0%", }, "英音女3": { locale: "en-GB", voice: "en-GB-LibbyNeural", pitch: "+0Hz", rate: "+0%", volume: "+0%", }, "英音男1": { locale: "en-GB", voice: "en-GB-RyanNeural", pitch: "+0Hz", rate: "+0%", volume: "+0%", }, "英音男2": { locale: "en-GB", voice: "en-GB-ThomasNeural", pitch: "+0Hz", rate: "+0%", volume: "+0%", }, } var selectedTts = "美音女1"; function create_edge_TTS( timeout = 10, auto_reconnect = true) { const TRUSTED_CLIENT_TOKEN = "6A5AA1D4EAFF4E9FB37E23D68491D6F4"; const VOICES_URL = `https://speech.platform.bing.com/consumer/speech/synthesize/readaloud/voices/list?trustedclienttoken=${TRUSTED_CLIENT_TOKEN}`; const SYNTH_URL = `wss://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1?TrustedClientToken=${TRUSTED_CLIENT_TOKEN}`; const BINARY_DELIM = "Path:audio\r\n"; const VOICE_LANG_REGEX = /\w{2}-\w{2}/; let _outputFormat = "audio-24khz-48kbitrate-mono-mp3"; /* 修改发音人和语言 */ let _voiceLocale = 'en-US', _voice = 'en-US-MichelleNeural', // 调整音色 _pitch = '+0Hz', // 调整速度 _rate = '+20%', // 调整音量 _volume = '+0%'; const _queue = { message: [], url_resolve: {}, url_reject: {}, }; let ready = false; function _SSMLTemplate(input) { return ` ${input} `; } function uuidv4() { return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)); } let socket = null; create_new_ws(); function setFormat(format) { if (format) _outputFormat = format; /**/ socket.send(`Content-Type:application/json; charset=utf-8\r\nPath:speech.config\r\n\r\n { "context": { "synthesis": { "audio": { "metadataoptions": { "sentenceBoundaryEnabled": "false", "wordBoundaryEnabled": "false" }, "outputFormat": "${_outputFormat}" } } } } `); } async function createURL(requestId) { let index_message = 0; for (let message of _queue.message) { const isbinary = message instanceof Blob; if (!isbinary) continue; const data = await message.text(); //console.log(data); //does the message order change? //console.log(await event.data.arrayBuffer()); const Id = /X-RequestId:(.*?)\r\n/gm.exec(data)[1]; if (Id !== requestId) continue; if (data.charCodeAt(0) === 0x00 && data.charCodeAt(1) === 0x67 && data.charCodeAt(2) === 0x58) { // Last (empty) audio fragment console.log(`Last (empty) audio fragment`) const blob = new Blob(_queue[requestId], { 'type': 'audio/mp3' }); _queue[requestId] = null; //release memory const url = URL.createObjectURL(blob); console.log(url) //URL.revokeObjectURL(url); _queue.url_resolve[requestId](url); //return url } else { const index = data.indexOf(BINARY_DELIM) + BINARY_DELIM.length; const audioData = message.slice(index); _queue[requestId].push(audioData); _queue.message[index_message] = null; //release blob memory } ++index_message; } } function onopen(event) { console.log('open'); //socket.send('Hello Server!'); //socket.close() setFormat(); ready = true; } async function onmessage(event) { const isbinary = event.data instanceof Blob; console.log(`Message from server, type: ${typeof (event.data)} Blob: ${isbinary}`); _queue.message.push(event.data) if (!isbinary) { //console.log(event.data); const requestId = /X-RequestId:(.*?)\r\n/gm.exec(event.data)[1]; if (event.data.includes("Path:turn.end")) { // end of turn createURL(requestId); } } else { } } function onerror(event) { ready = false; console.log('WebSocket error: ', event); } function onclose(event) { ready = false; console.log('WebSocket close: ', event); //may be closed by remote } function addSocketListeners() { socket.addEventListener('open', onopen); // Listen for messages socket.addEventListener('message', onmessage); // Listen for possible errors socket.addEventListener('error', onerror); // Listen for possible errors socket.addEventListener('close', onclose); } function create_new_ws() { //try { // Create WebSocket connection. socket = new WebSocket(SYNTH_URL); addSocketListeners(); } let toStream = function (input) { let requestSSML = _SSMLTemplate(input); const requestId = uuidv4().replaceAll('-', ''); const request = `X-RequestId:${requestId}\r\nContent-Type:application/ssml+xml\r\nPath:ssml\r\n\r\n ` + requestSSML.trim(); _queue[requestId] = []; return new Promise((resolve, reject) => { _queue.url_resolve[requestId] = resolve, _queue.url_reject[requestId] = reject; if (!ready) { if (auto_reconnect) { create_new_ws(); socket.addEventListener('open', _ => socket.send(request)); setTimeout(_ => { if (!ready) reject('reconnect timeout') }, timeout * 1000); } else reject('socket error or timeout'); } else { socket.send(request) } }); } async function play(input, play_count = 1, play_span = 5000) { const url = await toStream(input); let play_resolve = function () { }; globalAudio.pause(); globalAudio.src = url; // var audio = new Audio(url); console.log('before play' + globalAudio.duration) //NaN //let play_count = 3; //repeat times globalAudio.onended = (e) => { console.log(e); if (--play_count > 0) { console.log("play----"); setTimeout(_ => globalAudio.play(), play_span); } else { //URL.revokeObjectURL(url); play_resolve(url); console.log('play end'); } } await globalAudio.play(); console.log('after play' + globalAudio.duration); return new Promise((resolve, reject) => { play_resolve = resolve }); } return new Promise((resolve, reject) => { setTimeout(_ => reject('socket open timeout'), timeout * 1000); // Connection opened socket.addEventListener('open', function (event) { resolve({ _: play, toStream, setVoice: (voice, locale) => { _voice = voice; if (!locale) { const voiceLangMatch = VOICE_LANG_REGEX.exec(_voice); if (!voiceLangMatch) throw new Error("Could not infer voiceLocale from voiceName!"); _voiceLocale = voiceLangMatch[0]; } else { _voiceLocale = locale; } }, setFormat, isReady: _ => ready }) }); }); } var tts; async function init() { tts = await create_edge_TTS(); } typeof Promise != "undefined" && init(); //------------------------------- async function speak(chn) { try { await tts._(chn) } catch (e) { console.log('catch error:') console.log(e) } }