// ==UserScript==
// @name Reslator - LNMTL Plugin
// @author Spawner
// @version 1.2.7
// @namespace reader_translators
// @match https://lnmtl.com/chapter/*
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @grant GM_setValue
// @grant GM_getValue
// @license MIT
// @run-at document-start
// @connect fanyi.sogou.com
// @connect fanyi.baidu.com
// @connect test.niutrans.vip
// @connect translate.googleapis.com
// @connect fanyi.yeekit.com
// @require http://code.jquery.com/jquery-3.4.1.min.js
// @description 6/1/2020, 1:51:50 PM
// ==/UserScript==
/*
| Plugin : Reslator
| Version : 1.2.7
| Author : Spawner
| Description : Reader & Translator, which gives the user a better experience.
CHANGELOG :
1.0 - Initial release
1.1 - Add Glossary support
1.2 - + Fixed : Word higlighter
+ Raw on click delay
1.2.1 - Fixed comments section
1.2.2 - + Improve performance
+ Fixing a small issue in the glossary
1.2.3 - + Performance improvement
+ Add secondary theme switcher
+ Save raw/original text state
1.2.4 - Improve baidu speed + Fix translation display
1.2.5 - Fix niutrans API
1.2.6 - Added a new provider
1.2.7 - + Navigation between chapters has become much smoother.
+ Changing the main font of the reader for a better a better one.
+ Adding a font size controller.
+ Fixed font delay problem.
*/
let iframeHandler;
let uniqueWords = new Map();
let autoThemeState = GM_SuperValue.get( 'autoThemeState' , false );
let autoReaderState = GM_SuperValue.get( 'autoReaderState', false );
let autoRawState = GM_SuperValue.get( 'autoRawState' , false );
let providerOriginalState = GM_SuperValue.get( 'providerOriginalState', false );
let providerGoogleState = GM_SuperValue.get( 'providerGoogleState' , false );
let providerSogouState = GM_SuperValue.get( 'providerSogouState' , false );
let providerNiutransState = GM_SuperValue.get( 'providerNiutransState', false );
let providerBaiduState = GM_SuperValue.get( 'providerBaiduState' , false );
let providerYeekitState = GM_SuperValue.get( 'providerYeekitState' , false );
let secondaryTheme = GM_SuperValue.get( 'secondaryTheme' , "" );
let fontSizeValue = GM_SuperValue.get( 'fontSizeValue' , "" );
let fontSizeValueDefault = GM_SuperValue.get( 'fontSizeValueDefault' , "" );
const providers = [ "Google", "Sogou", "Niutrans", "Baidu", "Yeekit" ];
let isTranslated =
{
Google : [providerGoogleState , false],
Niutrans : [providerNiutransState, false],
Sogou : [providerSogouState , false],
Baidu : [providerBaiduState , false],
Yeekit : [providerYeekitState , false]
};
const providerObject =
{
Google:
{
maxSize : 5000,
timeout : 0,
color : ['48, 175, 219', 0.04]
},
Niutrans:
{
maxSize : 4000,
timeout : 400,
color : ['235, 77, 75', 0.06]
},
Sogou:
{
maxSize : 4000,
timeout : 1700,
color : ['255, 164, 58', 0.06]
},
Baidu:
{
maxSize : 2000,
timeout : 30,
color : ['140, 122, 230', 0.1]
},
Yeekit:
{
maxSize : 4000,
timeout : 500,
color : ['120, 224, 143', 0.1]
},
DeepL:
{
maxSize : 4000,
timeout : 500,
color : ['75, 75, 75', 0.1]
},
}
/**
* Show text
*
* If no translation is enabled, show the original text
* otherwise hide it.
*/
if( !providerGoogleState && !providerSogouState && !providerNiutransState && !providerBaiduState )
providerOriginalState = true;
if( fontSizeValue === "" || fontSizeValueDefault === "" )
{
fontSizeValue = "20";
fontSizeValueDefault = "19";
}
/**
* Apply the theme properly.
*
* The reason to set the display as 'none' and opacity as '0'
* is to avoid the iframe onload screen flash, we also need to hide the scrollbar
* since we will be using the iframe one.
*/
if ( autoReaderState )
{
GM_addStyle(
`
html
{
background-color : ${autoThemeState ? '#E9E9E9' : '#25282F'};
}
#app
{
display : none;
opacity : 0;
overflow-y : hidden;
}
`
);
}
/*
* Initializing our CSS Styles
*
* The iframe ( #pageContainer ) needs to be created at document-start
* to avoid any performance issues.
*/
GM_addStyle( `
#pageContainer, #pagePreload
{
font-family : Open sans;
position : fixed;
top : 0px;
left : 0px;
overflow-y : scroll;
width : 100%;
height : 100%;
display : block;
border-width : 0px;
}
`);
/* * Global variables for theme & settings Colors
*
* Using the :root selector to easily
* switch between colors.
*/
let CSSRoot = `
:root
{
--background-color : #E9E9E9;
--close-btn-back-color : #fff;
--close-btn-fore-color : #000;
--title-color : #000;
--chapter-color : #000;
--raw-color : #424242;
--settings-t-clicked-color : #E9E9E9;
--settings-label-color : rgba(51, 47, 53, 0.4);
--settings-check-back-color : linear-gradient(to right, #000000, #434343);
--checked-background : #029992;
--checked-background-rgb : 2, 153, 146;
}
[data-theme="dark"]
{
--background-color : #25282F;
--close-btn-back-color : #000;
--close-btn-fore-color : #fff;
--title-color : #fff;
--chapter-color : #E9E9E9;
--raw-color : #424242;
--settings-t-clicked-color : #25282F;
--settings-label-color : #E9E9E9;
--settings-check-back-color : #E9E9E847;
--checked-background : #029992;
--checked-background-rgb : 2, 153, 146;
}
[data-sec-theme="gold"]
{
--checked-background : #F39B3A;
--checked-background-rgb : 243, 155, 58;
}
[data-sec-theme="moon"]
{
--checked-background : #9c88ff;
--checked-background-rgb : 156, 136, 255;
}
[data-sec-theme="bomb"]
{
--checked-background : #eb4d4b;
--checked-background-rgb : 235, 77, 75;
}
[data-sec-theme="default"]
{
--checked-background : #029992;
--checked-background-rgb : 2, 153, 146;
}
`;
/* * Our iframe body styles
*
* Note : To avoid the scroll flickering issue we will
* be using will-change : transform as a temporary solution.
*/
let CSSContainer = `
html
{
background-color : var(--background-color);
}
@keyframes fadein
{
from
{
opacity : 0;
}
to
{
opacity : 1;
}
}
.chapterBody
{
position : relative !important;
font-family : "Source Sans Pro" !important;
text-align : justify !important;
animation : fadein 3s;
will-change : transform;
overflow : visible;
color : var(--chapter-color);
max-width : 72%;
min-height : 100%;
margin : 0 auto;
padding-bottom : 80px;
}
.pageContainerBackground
{
animation : fadein 1s;
min-height : 100%;
}
.pageContainerSettings
{
transition : all 0.35s ease-in-out;
background-color : transparent !important;
margin : auto;
}
.pageSplit
{
animation : fadein 3s;
border-bottom : 1px solid rgba(var(--checked-background-rgb), 0.3) !important;
/*margin : 0px 0px 10px;*/
}
h1.header-title
{
color : var(--checked-background);
animation : fadein 3s;
font-family : Poppins;
font-weight : 300;
font-size : 2.25em;
text-align : center;
padding-bottom : .3em;
line-height : 1.2;
margin : 3em auto .2em;
color : var(--title-color);
text-shadow : 0 0 4px rgba(0, 0, 100,.5);
}
`;
let CSSForm = `
.settingsForm
{
will-change : transform;
position : relative;
justify-content : center;
margin-left : -16;
margin-top : 20;
flex-wrap : wrap;
}
form
{
font-size : 13px;
letter-spacing : .2em;
font-family : sans-serif;
display : flex;
margin-top : 0em;
}
label
{
user-select : none;
color : var(--settings-label-color);
text-transform : uppercase;
font-family : sans-serif;
display : flex;
font-weight : bold;
padding : 7px 16px;
/* margin-right : -4px; */
}
input[type=checkbox],
input[type=radio]
{
color : #000;
position : normal;
visibility : hidden;
display : none;
}
input[type=checkbox][value=o-translator-p]:checked+label
{
background : #fff;
cursor : pointer;
}
input[type=checkbox][value=o-translator-p]:hover+label
{
color : #000;
cursor : pointer;
}
input[type=checkbox][value=g-translator-p]:checked+label
{
color : #fff;
background : var(--settings-check-back-color);
cursor : pointer;
}
input[type=checkbox][value=s-translator-p]:checked+label
{
color : #fff;
background : var(--settings-check-back-color);
cursor : pointer;
}
input[type=checkbox][value=n-translator-p]:checked+label
{
color : #fff;
background : var(--settings-check-back-color);
cursor : pointer;
}
input[type=checkbox][value=b-translator-p]:checked+label
{
color : #fff;
background : var(--settings-check-back-color);
cursor : pointer;
}
input[type=checkbox][value=y-translator-p]:checked+label
{
color : #fff;
background : var(--settings-check-back-color);
cursor : pointer;
}
input[type=checkbox][name=g-translator-p]:checked+label,
input[type=radio][name=g-translator-p]:checked+label
{
color : #fff;
background : #000;
cursor : pointer;
}
input[type=checkbox]+label:hover,
input[type=radio]+label:hover
{
/* transition : all 0.25s ease-in-out; */
color : #fff;
cursor : pointer;
}
input[type=checkbox]+label,
input[type=radio]+label
{
transition : all 0.15s cubic-bezier(0.4, 0, 0.6, 1) 0s;
background : var(--settings-t-clicked-color);
color : #545454;
}
input[type=radio][value=raw]:checked+label
{
color : #fff;
background : #7DD7FB;
cursor : default;
}
input[type=radio][value=mode-enabled]:checked+label
{
transition : all 200ms ease;
background : var(--checked-background);
color : #fff;
cursor : default;
}
input[type=radio][value=mode-disabled]:checked+label
{
background : #25282F;
color : #fff;
cursor : default;
}
input[type=radio][value=theme-enabled]:checked+label
{
background : #fff;
color : #000;
cursor : default;
}
input[type=radio][value=theme-disabled]:checked+label
{
background : #000;
color : #fff;
cursor : default;
}
.radio-group,
.checkbox-group
{
font-size : 9px;
letter-spacing : .2em;
border : solid 0px rgb(125, 214, 255);
display : flex;
margin : 0px 0px 30px;
border-radius : 20px;
overflow : hidden;
box-shadow : rgba(0, 0, 0, 0.3) 0px 8px 16px 0px;
opacity : 0.7;
}
.radio-group:hover,
.checkbox-group
{
opacity : 1;
transition : 1.0s;
}
`;
let CSSChapterBody = `
.sentence
{
animation : fadein 2s;
}
.sentence.or
{
margin-block-end: -0.5em !important;
word-spacing : 1px;
text-align : initial;
font-weight : 300;
line-height : 1.7;
margin-top : 3.1em;
font-size : 20px;
}
.sentence.google.default
{
padding : 0px;
background : transparent;
border-left-style : none;
word-spacing : 1px;
text-align : initial;
font-weight : 300;
line-height : 1.7;
margin-top : 3.1em;
font-size : 20px;
}
.sentence.sogou.default
{
padding : 0px;
background : transparent;
border-left-style : none;
word-spacing : 1px;
text-align : initial;
font-weight : 300;
line-height : 1.7;
margin-top : 3.1em;
font-size : 20px;
}
.sentence.niutrans.default
{
padding : 0px;
background : transparent;
border-left-style : none;
word-spacing : 1px;
text-align : initial;
font-weight : 300;
line-height : 1.7;
margin-top : 3.1em;
font-size : 20px;
}
.sentence.baidu.default
{
padding : 0px;
background : transparent;
border-left-style : none;
word-spacing : 1px;
text-align : initial;
font-weight : 300;
line-height : 1.7;
margin-top : 3.1em;
font-size : 20px;
}
.sentence.yeekit
{
margin-block-end: -0.5em !important;
word-spacing : 1px;
text-align : initial;
animation : fadein 0.4s;
line-height : 1.7;
font-size : 17px;
border-left-style : solid;
border-color : transparent transparent transparent rgb(${providerObject["Yeekit"]["color"][0]});
padding : 10 10px;
background : rgba(${providerObject["Yeekit"]["color"][0]}, ${providerObject["Yeekit"]["color"][1]});
margin : 25 0;
}
.sentence.yeekit.default
{
padding : 0px;
background : transparent;
border-left-style : none;
word-spacing : 1px;
text-align : initial;
font-weight : 300;
line-height : 1.7;
margin-top : 3.1em;
font-size : 20px;
}
.sentence.sogou
{
margin-block-end: -0.5em !important;
word-spacing : 1px;
text-align : initial;
animation : fadein 1s;
line-height : 1.7;
font-size : 17px;
border-left-style : solid;
border-color : transparent transparent transparent rgb(${providerObject["Sogou"]["color"][0]});
padding : 10 10px;
background : rgba(${providerObject["Sogou"]["color"][0]}, ${providerObject["Sogou"]["color"][1]});
margin : 25 0;
}
.sentence.google
{
margin-block-end: -0.5em !important;
word-spacing : 1px;
text-align : initial;
animation : fadein 1s;
line-height : 1.7;
font-size : 18px;
border-left-style : solid;
border-color : transparent transparent transparent rgb(${providerObject["Google"]["color"][0]});
padding : 10 10px;
background : rgba(${providerObject["Google"]["color"][0]}, ${providerObject["Google"]["color"][1]});
margin : 25 0;
}
.sentence.niutrans
{
margin-block-end: -0.5em !important;
animation : fadein 1s;
line-height : 1.7;
font-size : 18px;
border-left-style : solid;
border-color : transparent transparent transparent rgb(${providerObject["Niutrans"]["color"][0]});
padding : 10 10px;
background : rgba(${providerObject["Niutrans"]["color"][0]}, ${providerObject["Niutrans"]["color"][1]});
margin : 25 0;
}
.sentence.baidu
{
margin-block-end: -0.5em !important;
word-spacing : 1px;
text-align : initial;
animation : fadein 1s;
line-height : 1.7;
font-size : 18px;
border-left-style : solid;
border-color : transparent transparent transparent rgb(${providerObject["Baidu"]["color"][0]});
padding : 10 10px;
background : rgba(${providerObject["Baidu"]["color"][0]}, ${providerObject["Baidu"]["color"][1]});
margin : 25 0;
}
.sentence.deepl
{
margin-block-end: -0.5em !important;
word-spacing : 1px;
text-align : initial;
animation : fadein 1s;
line-height : 1.7;
font-size : 18px;
border-left-style : solid;
border-color : transparent transparent transparent rgb(${providerObject["DeepL"]["color"][0]});
padding : 10 10px;
background : rgba(${providerObject["DeepL"]["color"][0]}, ${providerObject["DeepL"]["color"][1]});
margin : 25 0;
}
.sentence.edit
{
overflow: hidden;
background: none;
outline: none;
resize: none;
margin-block-end: -0.5em !important;
word-spacing: 1px;
text-align: initial;
animation: fadein 0.5s;
line-height: 1.7;
font-size: 18px;
border-left-style: solid;
padding: 10 20px;
margin: 25 0;
background-color: var(--background-color);
box-shadow: 0 10px 10px 0 rgba(0, 0, 0, 0.1), 0 0 1px 0 rgba(0, 0, 0, 0.2);
color: var(--title-color);
border-radius: 13px;
border-left-color: var(--checked-background);
}
.original
{
display : inline;
word-spacing : 1px;
text-align : initial;
font-weight : 600;
font-size : 19px;
/*margin-bottom : 3.8em;*/
color : var(--checked-background);
}
.highlighter
{
font-weight : 500;
/*animation : fadein 1s;*/
}
`;
let CSSTooltip = `
.tooltip
{
position : relative;
display : inline;
}
.tooltip .tooltip-text
{
visibility : hidden;
width : 120px;
background-color : #555;
color : #fff;
text-align : center;
/* border-radius : 6px; */
padding : 3px 0;
position : absolute;
z-index : 1;
opacity : 0;
transition : opacity 0.3s;
}
/* Tooltip top content */
.top .tooltip-text
{
bottom : 100%;
left : 50%;
margin-left : -60px; /* 120/2 = 60 */
}
.tooltip .tooltip-text::after
{
content : "";
position : absolute;
border-width : 5px;
border-style : solid;
}
/* Tooltip top arrow */
.top .tooltip-text::after
{
margin-left : -5px;
left : 50%;
top : 100%;
border-color : var(--checked-background) transparent transparent transparent;
}
.tooltip:hover .tooltip-text
{
visibility : visible;
opacity : 1;
}
.highlighter-default
{
/* animation : fadein 1s; */
/* font-weight : 600; */
}
`;
let CSSNextPrevArrow = `
.arrow
{
position : fixed;
top : 90%;
width : 4vmin;
height : 4vmin;
background : transparent;
border-top : 1vmin solid gray;
border-right : 1vmin solid black;
box-shadow : 0 0 0 black;
transition : all 200ms ease;
opacity : 0.4;
}
.arrow.left
{
cursor : pointer;
/* left : 55px; */
right : 3%;
top : 88%;
transform : translate3d(0, -50%, 0) rotate(-135deg);
}
.arrow.right
{
cursor : pointer;
right : 4%;
top : 78%;
transform : translate3d(0,-50%,0) rotate(45deg);
}
.arrow:hover
{
border-color : #2d3436;
box-shadow : 0.5vmin -0.5vmin 0 white;
opacity : 1;
}
.arrow: before
{
content : '';
position : absolute;
top : 50%;
left : 50%;
transform : translate(-40%,-60%) rotate(45deg);
width : 200%;
height : 200%;
}
`;
let CSSCloseBtn =
`
.close-button
{
box-shadow : rgba(0, 0, 0, 0.4) 0px 8px 16px 0px;
width : 5vmin;
height : 5vmin;
box-shadow : 0px 10 10px 10px rgba(0, 0, 0, 0.25);
border-radius : 50%;
background : var(--close-btn-back-color);
position : fixed;
right : 3.6%;
top : 67%;
display : block;
z-index : 200;
text-indent : -9999px;
cursor : pointer;
}
.close-button:before,
.close-button:after
{
content : '';
width : 55%;
height : 2px;
background : var(--close-btn-fore-color);
position : absolute;
top : 48%;
left : 22%;
-webkit-transform : rotate(-45deg);
-moz-transform : rotate(-45deg);
-ms-transform : rotate(-45deg);
-o-transform : rotate(-45deg);
transform : rotate(-45deg);
-webkit-transition: all 0.3s ease-out;
-moz-transition : all 0.3s ease-out;
-ms-transition : all 0.3s ease-out;
-o-transition : all 0.3s ease-out;
transition : all 0.3s ease-out;
}
.close-button:after
{
-webkit-transform : rotate(45deg);
-moz-transform : rotate(45deg);
-ms-transform : rotate(45deg);
-o-transform : rotate(45deg);
transform : rotate(45deg);
-webkit-transition: all 0.3s ease-out;
-moz-transition : all 0.3s ease-out;
-ms-transition : all 0.3s ease-out;
-o-transition : all 0.3s ease-out;
transition : all 0.3s ease-out;
}
.close-button:hover:before,
.close-button:hover:after
{
-webkit-transform: rotate(180deg);
-moz-transform : rotate(180deg);
-ms-transform : rotate(180deg);
-o-transform : rotate(180deg);
transform : rotate(180deg);
}
`;
let CSSBtnTheme = `
.fa,
.fab,
.fal,
.far,
.fas
{
line-height: 0;
}
ul
{
animation: fadein 2s;
padding: 0;
position: relative;
margin: 0 0 100 0;
transform: scale(0.65);
justify-content: start;
display: inline-block;
flex-direction: row;
flex-wrap: nowrap;
/* justify-content: end; */
align-items: unsafe;
/* align-content: end; */
margin: 0 -5vmin;
/* margin: 0 500px; */
/* margin-bottom: 7.8em !important; */
padding-inline-start: 0px !important;
display: inline-flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-start;
align-items: baseline;
align-content: stretch;
}
ul li
{
animation : fadein 1s;
list-style : none;
margin : 0 7px;
display : block;
}
ul li:after, .rr-font--update.rr-inc:after
{
pointer-events: none;
content: "";
position: absolute;
top: -140px;
bottom: -230px;
right: 0;
z-index: 10;
/* padding-left: 320; */
border-right: 1.0px solid rgba(var(--checked-background-rgb), 0.3);
transform: rotate(30deg);
}
ul li a
{
position : relative;
display : list-item;
width : 60px;
height : 60px;
text-align : center;
line-height : 63px;
background : var(--background-color);
border-radius: 50%;
font-size : 35px;
color : darkgray;
transition : .5s;
}
ul li a:hover
{
cursor : pointer;
}
ul li a::before
{
content : '';
position : absolute;
top : 0;
left : 0;
width : 100%;
height : 100%;
border-radius: 50%;
background : #009992;
transition : .5s;
transform : scale(.9);
z-index : -1;
}
ul li a:hover
{
color : black;
}
ul li a:hover::before
{
transform : scale(1.1);
background: #009992;
}
ul li:nth-child(1) a::before
{
background: #ffee10;
}
ul li:nth-child(1) a:hover::before
{
box-shadow: 0 0 10px #e67e22;
}
ul li:nth-child(1) a:hover
{
box-shadow : 0 0 5px #e67e22;
text-shadow: 0 0 5px #e67e22;
}
ul li:nth-child(2) a::before
{
background: #95a5a6;
}
ul li:nth-child(2) a:hover::before
{
box-shadow: 0 0 10px #9b59b6;
}
ul li:nth-child(2) a:hover
{
box-shadow : 0 0 5px #9b59b6;
text-shadow: 0 0 5px #9b59b6;
}
ul li:nth-child(3) a::before
{
background: #e74c3c;
}
ul li:nth-child(3) a:hover::before
{
box-shadow: 0 0 10px #e74c3c;
}
ul li:nth-child(3) a:hover
{
box-shadow : 0 0 5px #e74c3c;
text-shadow: 0 0 5px #e74c3c;
}
ul li:nth-child(4) a:hover::before
{
box-shadow: 0 0 10px #029992;
}
ul li:nth-child(4) a:hover
{
box-shadow : 0 0 5px #029992;
text-shadow: 0 0 5px #029992;
}
/*ul li a
{
color: transparent;
}*/
`;
let CSSTagBtn = `
.settings-container
{
animation: fadein 3s;
overflow: hidden;
border-bottom: 1px solid rgba(var(--checked-background-rgb), 0.3);
margin-bottom: 150px;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-start;
align-items: center;
align-content: stretch;
}
.raw-block
{
/* margin-bottom: 4.8em; */
margin: 16px 0;
/* display: -webkit-box; */
align-items: center;
margin-bottom: 4.5em;
}
.tag-num
{
user-select : none;
font-size : 10px;
line-height : 18px;
position : relative;
display : inline-flex;
height : 18px;
padding : 0 6px;
letter-spacing: .25px;
color : black;
border-radius : 18px;
background : #c0c2cc;
margin : 0 7px;
}
.tag-num:hover
{
cursor : pointer;
transition : all 400ms ease;
background : black;
color : white;
}
.rr-font--update.rr-dec {
font-size: 15px;
margin-left: 30px;
}
.rr-font--update.rr-inc
{
font-size: 22px;
margin-right: 20px;
position: relative;
margin-left: 15px;
}
.rr-font--update.rr-inc:hover
{
/*transform: scale(1.3);*/
color: var(--checked-background);
transition: all 0.3s ease-out;
}
.rr-font--update.rr-dec:hover
{
color: var(--checked-background);
transition: all 0.3s ease-out;
}
.rr-font--update
{
user-select : none;
color : darkgray;
font-family : Open sans;
width : 30px;
font-weight : 700;
font-size : 18px;
cursor : pointer;
}
.btn-custom
{
font-family: "Source sans Pro";
outline: 0;
border-radius: 16px;
border: 1px solid var(--checked-background);
background: transparent;
color: var(--checked-background);
width: 120px;
height: 24px;
cursor: pointer;
text-transform: uppercase;
margin-left: 30px;
}
.btn-custom:hover
{
transition: background 0.3s ease-in-out;
background: white;
}
#font-indicator
{
color: var(--checked-background);
font-family: Source sans pro;
font-weight: 600;
}
.sentence.or,[class^="default"]
{
font-size: ${fontSizeValue};
}
[class^="sentence"]:not(.or)
{
font-weight: 300;
font-size: ${fontSizeValueDefault};
}
`;
/*
| Plugin Mobile support.
*/
let CSSMediaQuery = `
@media only screen and (max-width: 600px)
{
label
{ display: inline-block; font-size: 95%; }
.radio-group, .checkbox-group
{ display: grid; }
.chapterBody
{ margin: 7%;max-width: 82%; }
.sentence.or
{ font-size: 92%; text-align: initial; font-weight: 300; }
.settingsForm
{ flex-wrap: wrap; display: contents; }
.original
{ font-size: 87%; text-align: initial; font-weight: 500; }
.sentence.google
{ font-size: 87%; text-align: initial; font-weight: 300; }
.sentence.google.default
{ font-size: 92%; text-align: initial; font-weight: 300; }
.sentence.niutrans
{ font-size: 87%; text-align: initial; font-weight: 300;}
.sentence.niutrans.default
{ font-size: 92%; text-align: initial; font-weight: 300; }
.sentence.baidu
{ font-size: 87%; text-align: initial; font-weight: 300; }
.sentence.baidu.default
{ font-size: 92%; text-align: initial; font-weight: 300; }
.sentence.sogou
{ font-size: 87%; text-align: initial; font-weight: 300; }
.sentence.sogou.default
{ font-size: 92%; text-align: initial; font-weight: 300; }
.sentence.yeekit
{ font-size: 87%; text-align: initial; font-weight: 300; }
.sentence.yeekit.default
{ font-size: 92%; text-align: initial; font-weight: 300; }
.close-button
{ max-width: 7vmin; max-height: 7vmin; min-width: 7vmin; min-height: 7vmin; right: 3%; top: 57%; background: #393A3D; border-radius: 50%; }
.close-button:before, .close-button:after
{ background:white; }
.arrow.left
{ top: 76%; }
.arrow.right
{ top: 68%; right: 5%; }
.tooltip
{ display: inline; }
h1.header-title
{ font-size: 150% }
.rr-font--update.rr-dec
{ font-size: 15px !important; margin-left: 35px !important; }
.rr-font--update.rr-inc
{ margin-right: 15px !important; margin-left: 5px !important; }
.btn-custom
{ height: 54px !important; text-align: center !important;}
ul li
{ margin: 0 12px 4 !important; }
ul li:after, .rr-font--update.rr-inc:after
{ border-right: 2px solid rgba(var(--checked-background-rgb), 1); }
ul
{ animation:fadein 2s;padding:0!important;position:relative!important;margin:0 0 100!important;transform:scale(.65)!important;justify-content:center!important;display:flex!important;flex-direction:row!important;align-items:unsafe!important;margin:0 -10vm!important;margin:0 -10vmin!important;flex-wrap:wrap!important;justify-content:space-evenly!important;align-items:baseline!important;align-content:center!important}
}
`
;
window.onload = async function ()
{
/*
| Adding Glossary support
*/
await new Promise( resolve => setTimeout( resolve, 10 ) );
if ( $('style[type="text/css"]').text().includes( 'noWordWrapping' ) &&
!( window.sessionStorage.getItem("userjs_UGMTLComplete") &&
window.sessionStorage.getItem("userjs_UGMTLComplete") > window.performance.timing.fetchStart )
)
{
await new Promise ( resolve => document.addEventListener( 'userjs_UGMTLComplete' , resolve ) );
}
else
{
/*
| Swaping Chinese/English words to have a better translation.
*/
mainBodyWordsReplacer( );
}
/*
| Inject the iframe to the DOM.
*/
$( '#app' ).get( 0 ).insertAdjacentHTML( 'afterEnd', `<iframe id="pageContainer" style="display:${autoReaderState ? 'block' : 'none'}">` );
/*
| Get raw & original text from main page.
*/
const originalSentences = $( '.chapter-body' ).find( '.translated' ).text().replace( /(\xAD)/g, '' ).trim().split( '\n' );
const originalRaw = $( '.chapter-body' ).find( '.original' ).text().trim().split( '\n' );
const chapterTitle = $( '.chapter-title' )[ 0 ].textContent;
/*
| Add a button for the reader mode.
*/
$( '.js-toggle-original' ).after( '<button class="btn btn-enabled reader-mode">READER MODE</button>' );
$( '.btn.btn-enabled.reader-mode' ).css( 'boxShadow', 'rgb(236, 240, 241) 0px 0px 8px' );
$( '.btn.btn-enabled.reader-mode' ).css( 'color' , 'black' );
/*
| Reader btn onClick event.
*/
$( '.reader-mode' ).click( function ()
{
$( '#app' ).css( { 'display': 'none', 'opacity': '0' } );
$( '#pageContainer' ).show();
} );
/*
| We will need this for the tooltip & highlighting words.
| The values produces non-ut8 characters so we must remove them
| so that there will be no problem replacing them using regex.
*/
$( '.translated t' ).each( function ( index )
{
let chineseValue = $( this ).attr( 'data-title' );
let value = $( this )
.attr( 'data-title', $( this ).text() )
.text()
.trimLeft()
;
const replacedValue = value.replace( /[`~!@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/(\xAD)]/g, '' );
if( replacedValue != "" || replacedValue.length != 0 )
uniqueWords.set( replacedValue , chineseValue );
} );
/*
| This what will be using to control the iframe DOM
*/
iframeHandler = $( "#pageContainer" ).contents();
/*
| Toggle theme state.
*/
autoThemeState ?
iframeHandler.find( ':root' ).eq( 0 ).attr( 'data-theme', 'light' ) :
iframeHandler.find( ':root' ).eq( 0 ).attr( 'data-theme', 'dark' )
;
/*
| Append the needed styles for the iframe.
*/
addStyles( );
/*
| Add iframe form.
*/
addForm( );
/*
| Apply secondary theme.
*/
iframeHandler.find( ':root' ).eq( 0 ).attr( 'data-sec-theme', secondaryTheme );
/*
| Toggle secondary colors.
*/
iframeHandler.find( '.drag' ).click( function() { iframeHandler.find( ':root' ).eq( 0 ).attr( 'data-sec-theme', 'gold' ); GM_SuperValue.set( 'secondaryTheme', "gold" ); });
iframeHandler.find( '.moon' ).click( function() { iframeHandler.find( ':root' ).eq( 0 ).attr( 'data-sec-theme', 'moon' ); GM_SuperValue.set( 'secondaryTheme', "moon" ); });
iframeHandler.find( '.bomb' ).click( function() { iframeHandler.find( ':root' ).eq( 0 ).attr( 'data-sec-theme', 'bomb' ); GM_SuperValue.set( 'secondaryTheme', "bomb" ); });
iframeHandler.find( '.norm' ).click( function() { iframeHandler.find( ':root' ).eq( 0 ).attr( 'data-sec-theme', 'default' ); GM_SuperValue.set( 'secondaryTheme', "" ); });
/*
| If one provider is running then removed color/border translation identifier.
*/
iframeHandler.find( "input[name='items[]']" ).change(function(e) { onlyOneRunning(); });
for( let index in providers )
{
let provider = providers[ index ];
if( eval( 'provider' + provider + 'State' ) )
{
providerSelector( originalRaw, provider.toLowerCase() , providerObject[ provider ][ "maxSize" ] );
}
}
iframeHandler.find( '.close-button' ).click( function ()
{
$( '#app' ).css( { 'display': 'block', 'opacity': '1', 'overflow-y': 'scroll' } );
$( '#pageContainer' ).hide();
} );
/*
| Hooking next/prev page on click
| Replacing href results a bug, so I'll be using this approach.
*/
iframeHandler.find( '.arrow.right' ).click( function () { window.location.assign( $( this ).attr( 'value' ) ); } );
iframeHandler.find( '.arrow.left' ).click( function () { window.location.assign( $( this ).attr( 'value' ) ); } );
/*
| Add original and raw text ( with display:none )
| Also there is a non-utf8 coming out of nowhere so we must clean that
*/
for ( let i in originalSentences )
{
for ( const [key, value] of uniqueWords.entries() )
{
originalSentences[i] = originalSentences[i].replace
(
new RegExp( '(?<![<>])(' + key + ')(?![<>])' , 'g' ),
`<span class="highlighter"><div class="tooltip top">$&<span class="tooltip-text">${value}</span></div></span>`
)
;
}
iframeHandler.find( '.chapterBody' ).append( `<div class="sentence or">${originalSentences[i]}</div>` );
iframeHandler.find( '.chapterBody' ).append( `<div class="raw-block"><p class="original">${originalRaw[i]}</p><a class="tag-num">${i}</a></div>` );
}
if( !autoRawState )
iframeHandler.find( '.raw-block' ).hide();
if ( !providerOriginalState )
iframeHandler.find( '.sentence.or' ).hide();
// Should be optimized...
iframeHandler.find( '.rr-dec' ).click( function()
{
let currentFontSize = parseInt( iframeHandler.find( '.sentence.or,[class^="default"]' ).css('font-size') ) - 1;
let currentFontSizeDefault = parseInt( iframeHandler.find( '[class^="sentence"]:not(.or,.default)' ).css('font-size') ) - 1;
if ( currentFontSize <= 17 )
return;
iframeHandler.find( '[class^="sentence"]:not(.or)' ).css( 'font-size', currentFontSizeDefault );
iframeHandler.find( '.sentence.or,[class^="default"]' ).css( 'font-size', currentFontSize );
iframeHandler.find( '#font-indicator' ).text( currentFontSize );
GM_SuperValue.set( 'fontSizeValueDefault', currentFontSizeDefault );
GM_SuperValue.set( 'fontSizeValue', currentFontSize );
} );
iframeHandler.find( '.rr-inc' ).click( function()
{
let currentFontSize = parseInt( iframeHandler.find( '.sentence.or,[class^="default"]' ).css('font-size') ) + 1;
let currentFontSizeDefault = parseInt( iframeHandler.find( '[class^="sentence"]:not(.or,.default)' ).css('font-size') ) + 1;
if ( currentFontSize >= 30 )
return;
iframeHandler.find( '[class^="sentence"]:not(.or)' ).css( 'font-size', currentFontSizeDefault );
iframeHandler.find( '.sentence.or' ).css( 'font-size', currentFontSize );
iframeHandler.find( '#font-indicator' ).text( currentFontSize );
GM_SuperValue.set( 'fontSizeValueDefault', currentFontSizeDefault );
GM_SuperValue.set( 'fontSizeValue', currentFontSize );
} );
providerChangedEvent( originalRaw );
settingsChangedEvent( );
iframeHandler.find( '.btn-custom' ).on( 'click', function()
{
let self = $( this );
let stringBuilder = `${chapterTitle}\n\n\n`;
self.text( 'Copied ;-)' );
setTimeout( function ( ) { self.text( 'Copy Edited Text' ); }, 600 );
let rawEdits = iframeHandler.find( '.sentence.edit' ).toArray( ).map( ( p ) => p.textContent );
for ( let index in rawEdits )
{
stringBuilder += `${rawEdits[ index ]}\n\n\n`;
}
window.navigator.clipboard.writeText( stringBuilder );
} );
iframeHandler.find( '.tag-num' ).on( 'click', function()
{
let index = parseInt( this.textContent );
if ( $( this ).attr( 'value' ) )
{
iframeHandler.find( '.tag-num' ).eq( index ).next( ).filter( '.sentence.edit' ).fadeToggle(
200 );
return;
}
iframeHandler.find( '.raw-block' ).eq( index ).append(
'<div class="sentence edit" contenteditable="plaintext-only" spellcheck="false"/>'
);
$( this ).attr( 'value', 'clicked' );
} );
}
function addStyles()
{
iframeHandler.find( 'head' ).append( $
(
`
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="preload" onload="this.rel = 'stylesheet'" as="style" href='https://fonts.googleapis.com/css?family=Open+Sans:300,400,600|Poppins|Source+Sans+Pro:300,400,500,600'>
<link rel="preload" onload="this.rel = 'stylesheet'" as="style" href='https://use.fontawesome.com/releases/v5.6.3/css/all.css'>
${$( '.next > a' ).attr( 'href' ) != undefined ? `<link rel="prefetch" href="${$( '.next > a' ).attr( 'href' )}">` : '' }
<style type='text/css'>
${CSSRoot}
${CSSContainer}
${CSSCloseBtn}
${CSSForm}
${CSSChapterBody}
${CSSTooltip}
${CSSNextPrevArrow}
${CSSMediaQuery}
${CSSBtnTheme}
${CSSTagBtn}
</style>
`
)
);
}
function addForm( )
{
const chapterTitle = $( '.chapter-title' )[ 0 ].textContent.split( ':' );
const readerState = autoReaderState ? '' : 'checked';
const themeState = autoThemeState ? '' : 'checked';
const rawState = autoRawState ? '' : 'checked';
const pageNext = $( '.next > a' ).attr( 'href' );
const pagePrevious = $( '.previous > a' ).attr( 'href' );
const nextChapterButton = pageNext != null ? ( '<a value="' + pageNext + '" class="arrow right"></a>' ) : '';
const prevChapterButton = pagePrevious != null ? ( '<a value="' + pagePrevious + '" class="arrow left"></a>' ) : '';
const isOriginalEnabled = providerOriginalState ? 'checked' : '';
const isGoogleEnabled = providerGoogleState ? 'checked' : '';
const isSogouEnabled = providerSogouState ? 'checked' : '';
const isNiutransEnabled = providerNiutransState ? 'checked' : '';
const isBaiduEnabled = providerBaiduState ? 'checked' : '';
const isYeekitEnabled = providerYeekitState ? 'checked' : '';
iframeHandler.find( 'body' ).append
(
`
<div id="clean-reader">
<div class="pageContainerBackground">
<a class="close-button">Close</a>
${prevChapterButton}
${nextChapterButton}
<form class="settingsForm">
<label>Provider</label>
<div class="radio-group">
<input type="checkbox" id="Original-translator" value="o-translator-p" name="items[]" ${isOriginalEnabled}>
<label for="Original-translator">Original</label>
<input type="checkbox" id="Google-translator" value="g-translator-p" name="items[]" ${isGoogleEnabled}>
<label for="Google-translator"><span style="color:rgb(${providerObject["Google"]["color"][0]});text-shadow: 0 0 3px #30AFDB, 0 0 5px #30AFDB;padding-right: 6">◼ </span> Google</label>
<input type="checkbox" id="Sogou-translator" value="s-translator-p" name="items[]" ${isSogouEnabled}>
<label for="Sogou-translator"><span style="color:rgb(${providerObject["Sogou"]["color"][0]});text-shadow: 0 0 3px #e74c3c, 0 0 5px #e74c3c;padding-right: 6">◼ </span> Sogou</label>
<input type="checkbox" id="Niutrans-translator" value="n-translator-p" name="items[]" ${isNiutransEnabled}>
<label for="Niutrans-translator"><span style="color:rgb(${providerObject["Niutrans"]["color"][0]});text-shadow: 0 0 3px #e74c3c, 0 0 5px #e74c3c;padding-right: 6">◼ </span> Niutrans</label>
<input type="checkbox" id="Baidu-translator" value="b-translator-p" name="items[]" ${isBaiduEnabled}>
<label for="Baidu-translator"><span style="color:rgb(${providerObject["Baidu"]["color"][0]});text-shadow: 0 0 3px #6F68F2, 0 0 5px #6F68F2;padding-right: 6">◼ </span> Baidu</label>
<input type="checkbox" id="Yeekit-translator" value="y-translator-p" name="items[]" ${isYeekitEnabled}>
<label for="Yeekit-translator"><span style="color:rgb(${providerObject["Yeekit"]["color"][0]});text-shadow: 0 0 3px #6F68F2, 0 0 5px #78e08f;padding-right: 6">◼ </span> Yeekit</label>
</div>
<label>Raw</label>
<div class="radio-group">
<input type="radio" id="option-one-a" name="raw" value="mode-enabled" checked>
<label for="option-one-a">Enable</label>
<input type="radio" id="option-two-2" name="raw" value="mode-disabled" ${rawState}>
<label for="option-two-2">Disable</label>
</div>
<label>Reader</label>
<div class="radio-group">
<input type="radio" id="option-1" name="mode" value="mode-enabled" data-theme="dark" checked>
<label for="option-1">Always</label>
<input type="radio" id="option-2" name="mode" value="mode-disabled" data-theme="light" ${readerState}>
<label for="option-2">Never</label>
</div>
<label>Theme</label>
<div class="radio-group theme">
<input type="radio" id="option-1-1" name="theme" value="theme-enabled" checked>
<label for="option-1-1">Light</label>
<input type="radio" id="option-2-2" name="theme" value="theme-disabled" ${themeState}>
<label for="option-2-2">Dark</label>
</div>
</form>
<h1 class="header-title">${chapterTitle[1]} - <span style="transition : all 200ms ease;color:var(--checked-background);font-weight:bold">${chapterTitle[0].replace( '#', '' )}</h1>
<div class="pageSplit"></div>
<div class="settings-container">
<ul>
<li>
<a class="drag"><i class="fab fa-d-and-d"></i></a>
</li>
<li>
<a class="moon"><i class="fas fa-bowling-ball"></i></a>
</li>
<li>
<a class="bomb"><i class="fas fa-bomb"></i></a>
</li>
<li>
<a class="norm"><i class="fas fa-tint"></i> </a>
</li>
</ul><span class="rr-font--update rr-dec">A-</span><div id="font-indicator">${fontSizeValue}</div><span class="rr-font--update rr-inc">A+</span>
<button class="btn-custom">Copy edited text</button></div>
<div class="chapterBody">
</div>
</div>
</div>
`
)
;
}
function providerChangedEvent ( raws )
{
const addEventHandler = ( iframe, provider ) =>
{
const capitalizeProvider = provider.charAt( 0 ).toUpperCase( ) + provider.slice( 1 );
iframe.find( `#${capitalizeProvider}-translator` ).change( function ( )
{
GM_SuperValue.set( `provider${capitalizeProvider}State`, this.checked );
this.checked && !isTranslated[ capitalizeProvider ][ 1 ] ?
providerSelector( raws, provider, providerObject[ capitalizeProvider ][ 'maxSize' ] ) :
iframe.find( `.sentence.${provider}` ).fadeToggle( 300 )
;
} );
};
addEventHandler( iframeHandler, 'google' );
addEventHandler( iframeHandler, 'sogou' );
addEventHandler( iframeHandler, 'niutrans' );
addEventHandler( iframeHandler, 'baidu' );
addEventHandler( iframeHandler, 'yeekit' );
iframeHandler.find( '#Original-translator' ).change( function ( )
{
GM_SuperValue.set( 'providerOriginalState', this.checked );
iframeHandler.find( '.sentence.or' ).delay( 100 ).fadeToggle( 100 );
} );
}
function settingsChangedEvent ( )
{
/* READER CHANGING STATE */
iframeHandler.find( 'input[type=radio][name=mode]' ).change( function ( )
{
autoReaderState = $( this ).val( ).includes( 'enabled' );
GM_SuperValue.set( 'autoReaderState', autoReaderState );
} );
/* RAW CHANGING STATE */
iframeHandler.find( 'input[type=radio][name=raw]' ).click( function ( )
{
autoRawState = $( this ).val( ).includes( 'enabled' );
GM_SuperValue.set( 'autoRawState', autoRawState );
autoRawState ?
iframeHandler.find( '.raw-block' ).delay( 100 ).fadeIn( 300 ) :
iframeHandler.find( '.raw-block' ).fadeOut( 200 );
} );
/* THEME CHANGING STATE */
iframeHandler.find( 'input[type=radio][name=theme]' ).click( function ( )
{
autoThemeState = $( this ).val( ).includes( 'enabled' );
GM_SuperValue.set( 'autoThemeState', autoThemeState );
autoThemeState ?
iframeHandler.find( ':root' ).eq( 0 ).attr( 'data-theme', 'light' ) :
iframeHandler.find( ':root' ).eq( 0 ).attr( 'data-theme', 'dark' );
} );
}
function onlyOneRunning ()
{
let runningProviders = [];
iframeHandler.find( "input[name='items[]']:checked" ).each( function () { runningProviders.push( $( this ).attr( 'id' ).replace( '-translator', '' ).toLowerCase() ); } );
if( runningProviders.length == 1 )
{
iframeHandler.find( `.sentence.${ runningProviders[0] }` ).attr( 'class', `sentence ${ runningProviders[0] } default` );
iframeHandler.find( '.highlighter-default' ).css( 'font-weight', '500' );
}
else
{
for( let id in runningProviders )
{
iframeHandler.find( `.sentence.${ runningProviders[ id ] }.default` ).attr( 'class', `sentence ${ runningProviders[ id ] }` );
iframeHandler.find( '.highlighter-default' ).css( 'font-weight', '500' );
}
}
return runningProviders.length;
}
const disableCheckbox = ( elementId, status ) => iframeHandler.find( elementId ).prop( 'disabled', status );
async function providerSelector ( raw, name, chunksize )
{
const taskDelay = ( m ) => new Promise( r => setTimeout( r, m ) )
let translatedChunks = [];
let chunks = []
chunks = separateIntoChunks( raw, chunksize );
chunks[0] = chunks[0].trimLeft();
switch ( name )
{
case "sogou":
{
disableCheckbox( '#Sogou-translator', true );
for ( let id in chunks )
{
let value = await sogouSetCookies();
translatedChunks[id] = await sogouTranslator( chunks[id], value );
await taskDelay( providerObject["Sogou"]["timeout"] );
}
// Translation is finished.
isTranslated["Sogou"][1] = true;
disableCheckbox( '#Sogou-translator', false );
}
break;
case "google":
{
disableCheckbox( '#Google-translator', true );
for ( let id in chunks )
{
translatedChunks[id] = await googleTranslator( chunks[id] );
await taskDelay( providerObject["Google"]["timeout"] );
}
// Translation is finished.
isTranslated["Google"][1] = true;
disableCheckbox( '#Google-translator', false );
}
break;
case "niutrans":
{
disableCheckbox( '#Niutrans-translator', true );
for ( let id in chunks )
{
translatedChunks[id] = await niutransTranslator( chunks[id] );
await taskDelay( providerObject["Niutrans"]["timeout"] );
}
// Translation is finished.
isTranslated["Niutrans"][1] = true;
disableCheckbox( '#Niutrans-translator', false );
}
break;
case "baidu":
{
disableCheckbox( '#Baidu-translator', true );
let timer = -performance.now();
const tokens = await baiduReceiveTokens();
for ( let id in chunks )
{
translatedChunks[id] = await baiduTranslator( chunks[id], tokens );
await taskDelay( providerObject["Baidu"]["timeout"] );
}
timer += performance.now();
console.log( "Time: " + ( timer / 1000 ).toFixed( 5 ) + " sec." )
// Translation is finished.
isTranslated["Baidu"][1] = true;
disableCheckbox( '#Baidu-translator', false );
}
case "yeekit":
{
disableCheckbox( '#Yeekit-translator', true );
for ( let id in chunks )
{
translatedChunks[id] = await yeekitTranslator( chunks[id] );
await taskDelay( providerObject["Yeekit"]["timeout"] );
}
// Translation is finished.
isTranslated["Yeekit"][1] = true;
disableCheckbox( '#Yeekit-translator', false );
}
break;
}
let finalResult = seperateChunksIntoPars( translatedChunks );
createSentence( finalResult, name, onlyOneRunning() );
}
function mainBodyWordsReplacer ()
{
$( '.original t' ).each
(
function ()
{
const textSpace = $( this ).get( 0 ).previousSibling.nodeName == 'T' ? ' ' : '';
const textValue = $( this ).text( );
$( this ).text( textSpace + $( this ).attr( 'data-title' ) );
$( this ).attr( 'data-title', textValue );
}
);
}
function createSentence ( paragraphs, provider, runningProviders )
{
if( runningProviders == 1 )
{
provider.concat( " default" );
}
paragraphs.forEach( ( sentence, index ) =>
{
for ( const [key, value] of uniqueWords.entries() )
sentence = sentence.replace( new RegExp( '(?<![<>])(' + key + ')(?![<>])' , 'g' ), `<span class="highlighter-default">$&</span>` );
iframeHandler.find( '.raw-block' ).eq( index ).before( `<p class="sentence ${provider}">` + sentence + '</p>' );
} );
onlyOneRunning( );
}
function googleTranslator ( text )
{
return new Promise(
( resolve ) => $.ajax( 'https://translate.googleapis.com/translate_a/single?client=gtx&sl=zh-CN&tl=en&dt=t',
{
method: 'POST',
data : { q: text },
dataType: "json"
} ).done( ( t ) =>
{
let paragraph = "";
for ( let i = 0; i < t[0].length; i++ )
{
paragraph += t[0][i][0];
}
resolve( paragraph );
} )
.fail( function( xhr, textStatus, errorThrown )
{
GM_SuperValue.set( 'providerGoogleState', false );
disableCheckbox( '#Google-translator', false );
iframeHandler.find( '#Google-translator' ).prop( 'checked', false );
} )
);
}
function sogouTranslator ( text, id )
{
const userId = () =>
{
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace( /[xy]/g, function ( name )
{
let M = 0 | 16 * Math.random();
var pid = "x" == name ? M : 8 | 3 & M;
return pid.toString( 16 );
} );
};
return new Promise( ( resolve, reject ) =>
{
var formData =
{
'from' : 'zh-CHS',
'to' : 'en',
'text' : text,
'client' : 'pc',
'fr' : 'browser_pc',
'pid' : 'sogou-dict-vr',
'dict' : 'true',
'word_group' : 'true',
'second_query': 'true',
'uuid' : userId,
'needQc' : '1',
's' : md5( 'zh-CHS' + 'en' + text + '8511813095152' )
};
GM_xmlhttpRequest(
{
method : "POST",
url : "https://fanyi.sogou.com/reventondc/translateV2",
data : $.param( formData ),
headers:
{
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Accept" : "application/json",
"Referer" : "https://fanyi.sogou.com/",
"User-agent" : window.useragent,
"Cookie" : "SNUID=" + id
}
,
onload: function ( result )
{
try
{
var jsonObj = JSON.parse( result.response );
resolve( jsonObj.data.translate.dit );
}
catch ( error )
{
GM_SuperValue.set( 'providerSogouState', false );
disableCheckbox( '#Sogou-translator', false );
iframeHandler.find( '#Sogou-translator' ).prop( 'checked', false );
}
}
} );
} );
}
function sogouSetCookies ()
{
let dateExpires = new Date;
let match = ".sogou.com";
//let random = Math.floor( Math.random() * ( 9000000 - 1000 ) ) + 1000
let id = "1"; // Must be randomized in the next version
return new Promise( ( resolve ) =>
{
GM_xmlhttpRequest(
{
method : "GET",
url : "https://fanyi.sogou.com/",
headers:
{
"User-agent": window.useragent,
"Cookie" : setCookie( "SNUID", id, dateExpires.toGMTString(), match, "/" )
}
,
onload: function ( result )
{
resolve( id )
}
} );
} );
}
function niutransTranslator ( text )
{
return new Promise( ( resolve ) =>
{
GM_xmlhttpRequest(
{
method : "GET",
url : "http://test.niutrans.vip/NiuTransServer/testtrans?&from=auto&to=en&src_text=" + encodeURIComponent( text ),
headers:
{
"Accept-Encoding" : "gzip, deflate",
"User-Agent": window.useragent
}
,
onload: function ( result )
{
try
{
var myResponse = JSON.parse( result.response ).tgt_text;
resolve( [...myResponse.split( '\n \n' )].join( '\n\n' ) );
//console.log(result);
}
catch ( error )
{
GM_SuperValue.set( 'providerNiutransState', false );
disableCheckbox( '#Niutrans-translator', false );
iframeHandler.find( '#Niutrans-translator' ).prop( 'checked', false );
}
}
} );
} );
}
async function baiduTranslator ( text, _tokens )
{
const formData =
{
'from' : 'zh',
'to' : 'en',
'query' : text,
'transtype' : 'realtime',
'simple_means_flag': 3,
'sign' : sign( text, _tokens[0] ),
'token' : _tokens[1],
'domain' : 'common'
};
return new Promise( ( resolve ) =>
{
GM_xmlhttpRequest(
{
method : "POST",
url : "https://fanyi.baidu.com/v2transapi",
data : $.param( formData ),
headers:
{
"Content-Type" : "application/x-www-form-urlencoded; charset=UTF-8",
"Accept" : "application/json",
"Referer" : "https://fanyi.baidu.com",
"Accept-Encoding" : "gzip, deflate",
"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
}
,
onload: function ( result )
{
try
{
const jsonObj = JSON.parse( result.responseText )
resolve( jsonObj.trans_result.data.map( p => p.dst ).join( '\n\n' ) );
}
catch ( error )
{
GM_SuperValue.set( 'providerBaiduState', false );
disableCheckbox( '#Baidu-translator', false );
iframeHandler.find( '#Baidu-translator' ).prop( 'checked', false );
}
}
} );
} );
}
function baiduReceiveTokens ()
{
return new Promise( ( resolve ) =>
{
GM_xmlhttpRequest(
{
method : "GET",
url : "https://fanyi.baidu.com/",
headers:
{
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
}
,
onload: function ( result )
{
try
{
const windowToken = result.responseText.match( /window\.gtk = '(.*?)'/ )[1];
const commonToken = result.responseText.match( /token: '(.*?)',/ )[1];
resolve( [windowToken, commonToken] );
}
catch ( error )
{
disableCheckbox( '#Baidu-translator', false );
iframeHandler.find( '#Baidu-translator' ).prop( 'checked', false );
}
}
} );
} );
}
function yeekitTranslator( text )
{
return new Promise( ( resolve ) =>
{
GM_xmlhttpRequest(
{
method: 'POST',
url: 'http://fanyi.yeekit.com/zyyt/translate/translate',
data: JSON.stringify(
{
"srcl" : "nzh",
"tgtl" : "nen",
"app_source": 9001,
"text" : text,
"domain" : "auto"
}),
headers:
{
"Content-Type": "application/json;charset=UTF-8",
"Referer" : "http://fanyi.yeekit.com/",
"Origin" :"http://fanyi.yeekit.com",
"User-Agent" : window.useragent
},
onload: function ( result )
{
try
{
let jsonObj = JSON.parse( result.response );
resolve( jsonObj.data )
}
catch (error)
{
GM_SuperValue.set( 'providerYeekitState', false );
disableCheckbox( '#Yeekit-translator', false );
iframeHandler.find( '#Yeekit-translator' ).prop( 'checked', false );
}
}
});
} );
}
/*
| Utilities
*/
function setCookie ( a, val, url, c, name )
{
return a = [a, "=", val], url && a.push( ";expires=", url ), c && a.push( ";domain=", c ), name && a.push( ";path=", name ), document.cookie = a.join( "" ), a.join( "" );
}
function separateIntoChunks ( paragraphs, size )
{
let chunks = [];
let currentchunk = "";
for ( let i = 0; i < paragraphs.length; i++ )
{
if ( ( currentchunk + paragraphs[i] ).length >= size )
{
chunks.push( currentchunk );
currentchunk = paragraphs[i];
}
else
{
currentchunk = currentchunk + "\n\n" + paragraphs[i];
}
}
if ( paragraphs.length != 0 )
{
chunks.push( currentchunk );
}
return chunks;
}
function seperateChunksIntoPars ( chunks, splitby = "\n\n" )
{
let pars = [];
chunks.forEach( ( chunk ) => chunk.split( splitby ).forEach( ( par ) => pars.push( par ) ) );
return pars;
}
function a ( r )
{
if ( Array.isArray( r ) )
{
for ( var o = 0, t = Array( r.length ); o < r.length; o++ )
t[o] = r[o];
return t
}
return Array.from( r )
}
function n ( a, o )
{
var s = 0;
for ( ; s < o.length - 2; s = s + 3 )
{
var d = o.charAt( s + 2 );
d = d >= "a" ? d.charCodeAt( 0 ) - 87 : Number( d );
d = "+" === o.charAt( s + 1 ) ? a >>> d : a << d;
a = "+" === o.charAt( s ) ? a + d & 4294967295 : a ^ d;
}
return a;
}
function sign ( r, gtk = 0 )
{
var i = null;
var o = r.match( /[\uD800-\uDBFF][\uDC00-\uDFFF]/g );
if ( null === o )
{
var t = r.length;
t > 30 && ( r = "" + r.substr( 0, 10 ) + r.substr( Math.floor( t / 2 ) - 5, 10 ) + r.substr( -10, 10 ) )
} else
{
for ( var e = r.split( /[\uD800-\uDBFF][\uDC00-\uDFFF]/ ), C = 0, h = e.length, f = []; h > C; C++ )
"" !== e[C] && f.push.apply( f, a( e[C].split( "" ) ) ),
C !== h - 1 && f.push( o[C] );
var g = f.length;
g > 30 && ( r = f.slice( 0, 10 ).join( "" ) + f.slice( Math.floor( g / 2 ) - 5, Math.floor( g / 2 ) + 5 ).join( "" ) + f.slice( -10 ).join( "" ) )
}
var u = void 0
, l = "" + String.fromCharCode( 103 ) + String.fromCharCode( 116 ) + String.fromCharCode( 107 );
u = null !== i ? i : ( i = gtk || "" ) || "";
for ( var d = u.split( "." ), m = Number( d[0] ) || 0, s = Number( d[1] ) || 0, S = [], c = 0, v = 0; v < r.length; v++ )
{
var A = r.charCodeAt( v );
128 > A ? S[c++] = A : ( 2048 > A ? S[c++] = A >> 6 | 192 : ( 55296 === ( 64512 & A ) && v + 1 < r.length && 56320 === ( 64512 & r.charCodeAt( v + 1 ) ) ? ( A = 65536 + ( ( 1023 & A ) << 10 ) + ( 1023 & r.charCodeAt( ++v ) ),
S[c++] = A >> 18 | 240,
S[c++] = A >> 12 & 63 | 128 ) : S[c++] = A >> 12 | 224,
S[c++] = A >> 6 & 63 | 128 ),
S[c++] = 63 & A | 128 )
}
for ( var p = m, F = "" + String.fromCharCode( 43 ) + String.fromCharCode( 45 ) + String.fromCharCode( 97 ) + ( "" + String.fromCharCode( 94 ) + String.fromCharCode( 43 ) + String.fromCharCode( 54 ) ), D = "" + String.fromCharCode( 43 ) + String.fromCharCode( 45 ) + String.fromCharCode( 51 ) + ( "" + String.fromCharCode( 94 ) + String.fromCharCode( 43 ) + String.fromCharCode( 98 ) ) + ( "" + String.fromCharCode( 43 ) + String.fromCharCode( 45 ) + String.fromCharCode( 102 ) ), b = 0; b < S.length; b++ )
p += S[b],
p = n( p, F );
return p = n( p, D ),
p ^= s,
0 > p && ( p = ( 2147483647 & p ) + 2147483648 ),
p %= 1e6,
p.toString() + "." + ( p ^ m )
}
function md5 ( str )
{
var k = [],
i = 0;
for ( i = 0; i < 64; )
k[i] = 0 | ( Math.abs( Math.sin( ++i ) ) * 4294967296 );
var b, c, d, j,
x = [],
str2 = unescape( encodeURI( str ) ),
a = str2.length,
h = [( b = 1732584193 ), ( c = -271733879 ), ~b, ~c];
for ( i = 0; i <= a; )
x[i >> 2] |= ( str2.charCodeAt( i ) || 128 ) << ( 8 * ( i++ % 4 ) );
x[( str = ( ( a + 8 ) >> 6 ) * 16 + 14 )] = a * 8;
i = 0;
for ( ; i < str; i += 16 )
{
a = h;
j = 0;
for ( ; j < 64; )
{
a = [
( d = a[3] ),
( b = a[1] | 0 ) +
( ( ( d =
a[0] + [
( b & ( c = a[2] ) ) | ( ~b & d ),
( d & b ) | ( ~d & c ),
b ^ c ^ d,
c ^ ( b | ~d )
][( a = j >> 4 )] +
( k[j] + ( x[( [j, 5 * j + 1, 3 * j + 5, 7 * j][a] % 16 ) + i] | 0 ) ) ) <<
( a = [7, 12, 17, 22, 5, 9, 14, 20, 4, 11, 16, 23, 6, 10, 15, 21][
4 * a + ( j++ % 4 )
] ) ) |
( d >>> ( 32 - a ) ) ),
b,
c
];
}
for ( j = 4; j; )
{
h[--j] = h[j] + a[j];
}
}
str = "";
for ( ; j < 32; )
{
str += ( ( h[j >> 3] >> ( ( 1 ^ ( j++ & 7 ) ) * 4 ) ) & 15 ).toString( 16 );
}
return str;
}