// ==UserScript==
// @name Duolingo PRO
// @namespace https://duolingopro.net
// @version 3.1BETA.03
// @description The fastest Duolingo XP gainer, working as of November 2025.
// @author anonymousHackerIV
// @match https://*.duolingo.com/*
// @match https://*.duolingo.cn/*
// @icon https://www.duolingopro.net/static/favicons/duo/128/light/primary.png
// @grant GM_log
// @downloadURL none
// ==/UserScript==
let storageLocal;
let storageSession;
let versionNumber = "05";
let storageLocalVersion = "05";
let storageSessionVersion = "05";
let versionName = "BETA.03";
let versionFull = "3.1BETA.03";
let versionFormal = "3.1 BETA.03";
let serverURL = "https://www.duolingopro.net";
let apiURL = "https://api.duolingopro.net";
let greasyfork = true;
let alpha = false;
let hidden = false;
let lastPage;
let currentPage = 1;
let windowBlurState = true;
let solvingIntervalId;
let isAutoMode;
let findReactMainElementClass = '_3yE3H';
let reactTraverseUp = 1;
const debug = false;
const flag01 = false;
const flag02 = false;
const flag03 = false;
// USAGE OR MODIFICATION OF THIS SCRIPT IMPLIES YOU AGREE TO THE TERMS AND CONDITIONS PRESENTED IN THE SCRIPT. IF YOU DO NOT AGREE, DO NOT USE OR MODIFY THIS SCRIPT.
(function buildOrMigrateStorageLocal() {
let tempRandom16 = Array.from({ length: 16 }, () => 'abcdefghijklmnopqrstuvwxyz0123456789'[Math.floor(Math.random() * 36)]).join('');
let tempTimestamp = Date.now();
const DEFAULTS = {
version: versionNumber,
terms: "00",
random16: tempRandom16,
pins: {
home: ["DLP_Get_XP_1_ID", "DLP_Get_GEM_1_ID"],
legacy: ["DLP_Get_PATH_1_ID", "DLP_Get_PRACTICE_1_ID"]
},
settings: {
autoUpdate: !greasyfork,
showSolveButtons: true,
showAutoServerButton: alpha,
muteLessons: false,
anonymousUsageData: alpha,
solveSpeed: 0.9
},
stats: {
modern: {
xp: 0,
gem: 0,
streak: 0,
super: 0,
heart_refill: 0,
streak_freeze: 0,
double_xp_boost: 0
},
legacy: {
path: {
lessons: 0,
questions: 0
},
practice: {
lessons: 0,
questions: 0
},
listen: {
lessons: 0,
questions: 0
},
lesson: {
lessons: 0,
questions: 0
}
},
tracking_since: tempTimestamp
},
chats: [],
notifications: [{ id: "0000" }],
tips: { seeMore1: false },
languagePackVersion: "00",
onboarding: false,
storageVersion: storageLocalVersion
};
function isPlainObject(v) {
return Object.prototype.toString.call(v) === "[object Object]";
}
function safeParse(json) {
try {
const v = JSON.parse(json);
return isPlainObject(v) ? v : null;
} catch {
return null;
}
}
function mergeWithPrune(existing, defaults) {
const result = Array.isArray(defaults) ? [] : {};
for (const key of Object.keys(defaults)) {
const defVal = defaults[key];
const hasExisting = existing && Object.prototype.hasOwnProperty.call(existing, key);
const exVal = hasExisting ? existing[key] : undefined;
if (isPlainObject(defVal)) {
if (isPlainObject(exVal)) {
result[key] = mergeWithPrune(exVal, defVal);
} else {
result[key] = mergeWithPrune({}, defVal); // take full default subtree
}
} else if (Array.isArray(defVal)) {
result[key] = Array.isArray(exVal) ? exVal : defVal.slice();
} else {
// primitives / everything else: keep existing if present, else default
result[key] = hasExisting ? exVal : defVal;
}
}
// Unknown keys in `existing` are intentionally NOT copied (pruned)
return result;
}
const raw = localStorage.getItem("DLP_Local_Storage");
const existing = safeParse(raw);
// Migrate: replace old "DLP_Get_GEMS_1_ID" pin with new "DLP_Get_GEM_1_ID"
if (existing && existing.pins && Array.isArray(existing.pins.home)) {
const legacyIndex = existing.pins.home.indexOf("DLP_Get_GEMS_1_ID");
if (legacyIndex !== -1) {
existing.pins.home[legacyIndex] = "DLP_Get_GEM_1_ID";
}
}
// Fresh write if missing or unparsable
if (!existing) {
const fresh = { ...DEFAULTS, storageVersion: storageLocalVersion };
localStorage.setItem("DLP_Local_Storage", JSON.stringify(fresh));
storageLocal = fresh;
return;
}
// Up-to-date -> just use it
if (existing.storageVersion === storageLocalVersion) {
storageLocal = existing;
return;
}
// Migrate: keep existing values where keys match, add missing defaults, drop extras
const migrated = mergeWithPrune(existing, DEFAULTS);
// Ensure we actually bump the version so we don't re-migrate on next load
migrated.storageVersion = storageLocalVersion;
localStorage.setItem("DLP_Local_Storage", JSON.stringify(migrated));
storageLocal = migrated;
})();
function saveStorageLocal() {
localStorage.setItem("DLP_Local_Storage", JSON.stringify(storageLocal));
}
if (sessionStorage.getItem("DLP_Session_Storage") == null || JSON.parse(sessionStorage.getItem("DLP_Session_Storage")).storageVersion !== storageSessionVersion) {
sessionStorage.setItem("DLP_Session_Storage", JSON.stringify({
"legacy": {
"page": 0,
"status": false,
"path": {
"type": "lesson",
"amount": 0
},
"practice": {
"type": "lesson",
"amount": 0
},
"listen": {
"type": "lesson",
"amount": 0
},
"lesson": {
"section": 1,
"unit": 1,
"level": 1,
"type": "lesson",
"amount": 0
}
},
"notifications": [
{
"id": "0001"
}
],
"storageVersion": storageSessionVersion
}));
storageSession = JSON.parse(sessionStorage.getItem("DLP_Session_Storage"));
} else {
storageSession = JSON.parse(sessionStorage.getItem("DLP_Session_Storage"));
}
function saveStorageSession() {
sessionStorage.setItem("DLP_Session_Storage", JSON.stringify(storageSession));
}
if (alpha) apiURL = "https://api.duolingopro.net/alpha";
let systemLanguage = document.cookie.split('; ').find(row => row.startsWith('lang=')).split('=')[1];
let systemText = {
en: {
1: "Switch to Legacy",
2: "Show",
3: "Connecting",
4: "Donate",
5: "Support",
6: "Settings",
7: "What's New",
8: "How much XP would you like to gain?",
9: "GET",
10: "How many Gems would you like to gain?",
12: "Would you like to redeem 3 days of Super Duolingo?",
13: "REDEEM",
14: "Terms & Conditions",
15: "See More",
16: "Back",
17: "How many lessons would you like to solve on the path?",
18: "START",
19: "How many practices would you like to solve?",
21: "How many listening practices would you like to solve? (Requires Super Duolingo)",
23: "Which and how many lessons would you like to repeat?",
25: "Please read and accept the Terms & Conditions to use Duolingo PRO 3.1.",
26: "These are the Terms & Conditions you agreed to use Duolingo PRO 3.1.",
27: "LOADING TERMS & CONDITIONS YOU CANNOT USE THIS SOFTWARE UNTIL TERMS & CONDITIONS ARE LOADED",
28: "DECLINE",
29: "ACCEPT",
30: "Without accepting the Terms & Conditions, you cannot use Duolingo PRO 3.1.",
31: "BACK",
32: "Settings",
34: "Automatic Updates",
35: "Duolingo PRO 3.1 will automatically update itself when there's a new version available.",
37: "SAVE",
38: "Feedback",
39: "Help us make Duolingo PRO 3.1 better.",
40: "Write here as much as you can with as many details as possible.",
41: "Feedback Type: ",
42: "BUG REPORT",
43: "SUGGESTION",
44: "Add Attachment: (Optional)",
45: "UPLOAD",
47: "SEND",
48: "What's New",
51: "LEARN MORE",
52: "Welcome to",
53: "The next generation of Duolingo PRO is here, with Instant XP, Magnet UI, all powerful than ever. ",
54: "START",
100: "SOLVE",
101: "SOLVE ALL",
102: "PAUSE SOLVE",
103: "Hide",
104: "Show",
105: "Switch to 3.1",
106: "Switch to Legacy",
107: "STOP",
108: "Connected",
109: "Error",
110: "SEND",
111: "SENDING",
112: "SENT",
113: "LOADING",
114: "DONE",
115: "FAILED",
116: "SAVING AND APPLYING",
200: "Under Construction",
201: "The Gems function is currently under construction. We plan to make it accessible to everyone soon.",
202: "Update Available",
203: "You are using an outdated version of Duolingo PRO. Please update Duolingo PRO or turn on automatic updates.",
204: "Feedback Sent",
205: "Your feedback was successfully sent, and our developers will look over it. Keep in mind, we cannot respond back to your feedback.",
206: "Error Sending Feedback",
207: "Your feedback was not sent. This might be because you are using an outdated or a modified version of Duolingo PRO.",
208: "Unknown Error",
209: "Please try again later. An unknown error occurred. Number: ",
210: "hour",
211: "hours",
212: "minute",
213: "minutes",
214: "and",
215: "{hours} {hourUnit}",
216: "{minutes} {minuteUnit}",
217: "{hourPhrase} {conjunction} {minutePhrase}",
218: "XP Successfully Received",
219: "You received {amount} XP. You can request up to {remainingXP} XP before your limit resets back to {totalLimit} XP in {timeMessage}. To boost your limits, donate .",
220: "Super Duolingo Successfully Redeemed",
221: "You redeemed a 3 day Super Duolingo trial. You can request another 3 day Super Duolingo trial in {timeMessage}.",
222: "Limit Warning",
223: "You can only request up to {limitAmount} XP before your limit resets back to {totalLimitAmount} XP in {timeMessage}. To boost your limits, donate .",
224: "Limit Reached",
225: "You reached your XP limit for the next {timeMessage}. To boost your limits, donate .",
227: "You already redeemed a 3 day Super Duolingo trial. You can request another 3 day Super Duolingo trial in {timeMessage}.",
229: "REFILL",
230: "GEMS testing",
231: "Error Connecting",
232: "Duolingo PRO was unable to connect to our servers. This may be because our servers are temporarily unavailable or you are using an outdated version. Check for server status or updates .",
233: "Update Duolingo PRO",
234: "You are using an outdated version of Duolingo PRO. Please update Duolingo PRO ."
},
};
let CSS1;
let HTML2;
let CSS2;
let HTML3;
let HTML4;
let HTML5;
let CSS5;
let HTML6;
let CSS6;
let HTML7;
let CSS7;
function Two() {
CSS1 = `
@font-face {
font-family: 'Duolingo PRO Rounded';
src: url(${serverURL}/static/fonts/V7R100DB1/Duolingo-PRO-Rounded-Semibold.woff2) format('woff2');
font-weight: 600;
}
:root {
--DLP-red-hex: #ff3b30;
--DLP-orange-hex: #ff9500;
--DLP-yellow-hex: #ffcc00;
--DLP-green-hex: #34c759;
--DLP-teal-hex: #00c7be;
--DLP-cyan-hex: #5ac8fa;
--DLP-blue-hex: #007aff;
--DLP-indigo-hex: #5856d6;
--DLP-purple-hex: #af52de;
--DLP-pink-hex: #ff2d55;
--DLP-red-rgb: rgb(255, 59, 48);
--DLP-orange-rgb: rgb(255, 149, 0);
--DLP-yellow-rgb: rgb(255, 204, 0);
--DLP-green-rgb: rgb(52, 199, 89);
--DLP-teal-rgb: rgb(0, 199, 190);
--DLP-cyan-rgb: rgb(90, 200, 250);
--DLP-blue-rgb: rgb(0, 122, 255);
--DLP-indigo-rgb: rgb(88, 86, 214);
--DLP-purple-rgb: rgb(175, 82, 222);
--DLP-pink-rgb: rgb(255, 45, 85);
--DLP-red: 255, 59, 48;
--DLP-orange: 255, 149, 0;
--DLP-yellow: 255, 204, 0;
--DLP-green: 52, 199, 89;
--DLP-teal: 0, 199, 190;
--DLP-cyan: 90, 200, 250;
--DLP-blue: 0, 122, 255;
--DLP-indigo: 88, 86, 214;
--DLP-purple: 175, 82, 222;
--DLP-pink: 255, 45, 85;
}
@media (prefers-color-scheme: dark) {
:root {
--DLP-red-hex: #ff453a;
--DLP-orange-hex: #ff9f0a;
--DLP-yellow-hex: #ffd60a;
--DLP-green-hex: #30d158;
--DLP-teal-hex: #63e6e2;
--DLP-cyan-hex: #64d2ff;
--DLP-blue-hex: #0a84ff;
--DLP-indigo-hex: #5e5ce6;
--DLP-purple-hex: #bf5af2;
--DLP-pink-hex: #ff375f;
--DLP-red-rgb: rgb(255, 69, 58);
--DLP-orange-rgb: rgb(255, 159, 10);
--DLP-yellow-rgb: rgb(255, 214, 10);
--DLP-green-rgb: rgb(48, 209, 88);
--DLP-teal-rgb: rgb(99, 230, 226);
--DLP-cyan-rgb: rgb(100, 210, 255);
--DLP-blue-rgb: rgb(10, 132, 255);
--DLP-indigo-rgb: rgb(94, 92, 230);
--DLP-purple-rgb: rgb(191, 90, 242);
--DLP-pink-rgb: rgb(255, 55, 95);
--DLP-red: 255, 69, 58;
--DLP-orange: 255, 159, 10;
--DLP-yellow: 255, 214, 10;
--DLP-green: 48, 209, 88;
--DLP-teal: 99, 230, 226;
--DLP-cyan: 100, 210, 255;
--DLP-blue: 10, 132, 255;
--DLP-indigo: 94, 92, 230;
--DLP-purple: 191, 90, 242;
--DLP-pink: 255, 55, 95;
--DLP-background: var(--color-snow);
}
}
`;
HTML2 = `
${systemText[systemLanguage][8]}
${systemText[systemLanguage][10]}
${systemText[systemLanguage][12]}
Would you like to redeem an XP Boost?
How many Streak Freezes would you like to get?
How many days would you like to increase your Streak by?
Would you like to refill your Hearts to full?
${systemText[systemLanguage][14]}
${systemText[systemLanguage][7]}
${systemText[systemLanguage][8]}
${systemText[systemLanguage][10]}
${systemText[systemLanguage][12]}
Would you like to redeem an XP Boost?
How many Streak Freezes would you like to get?
How many days would you like to increase your Streak by?
Would you like to refill your Hearts to full?
You are using an outdated version of Duolingo PRO. Please update Duolingo PRO or turn on automatic updates.
Duolingo PRO failed to connect. This might be happening because of an issue on our system or your device. Try updating Duolingo PRO. If the issue persists afterwards, join our Discord Server to get support.
We are currently unable to receive new requests due to high demand. Join our Discord Server to learn more. You can help us handle more demand by donating on Patreon while getting exclusive features and higher limits.
${systemText[systemLanguage][17]}
${systemText[systemLanguage][19]}
${systemText[systemLanguage][21]}
${systemText[systemLanguage][23]}
${systemText[systemLanguage][17]}
${systemText[systemLanguage][19]}
${systemText[systemLanguage][21]}
${systemText[systemLanguage][23]}
${systemText[systemLanguage][25]}
${systemText[systemLanguage][26]}
${systemText[systemLanguage][30]}
Legacy Solve Speed
Legacy will solve each question every this amount of seconds. The lower speed you set, the more mistakes Legacy can make.
${systemText[systemLanguage][34]}
${systemText[systemLanguage][35]}
${systemText[systemLanguage][39]}
${systemText[systemLanguage][41]}
${systemText[systemLanguage][44]}
${systemText[systemLanguage][52]}
${systemText[systemLanguage][53]}
It may take a few hours for a developer to respond to you. You will be notified in Duolingo PRO when there's a reply.
Send a message to start talking with a support member.
We hope to have solved your issue. If not, you can start a new chat.
`;
CSS2 = `
.DLP_NoSelect {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.DLP_Text_Style_1 {
font-family: "Duolingo PRO Rounded";
font-size: 16px;
font-style: normal;
font-weight: 500;
line-height: normal;
color: rgb(var(--color-wolf), 0.8);
margin: 0;
-webkit-font-smoothing: antialiased;
}
.DLP_Text_Style_2 {
font-family: "Duolingo PRO Rounded";
font-size: 24px;
font-style: normal;
font-weight: 500;
line-height: normal;
margin: 0;
-webkit-font-smoothing: antialiased;
}
.DLP_Magnetic_Hover_1 {
transition: filter 0.4s cubic-bezier(0.16, 1, 0.32, 1), transform 0.4s cubic-bezier(0.16, 1, 0.32, 1);
cursor: pointer;
}
.DLP_Magnetic_Hover_1:hover {
filter: brightness(0.9);
transform: scale(1.05);
}
.DLP_Magnetic_Hover_1:active {
filter: brightness(0.9);
transform: scale(0.9);
}
.DLP_Hover_1 {
transition: filter 0.4s cubic-bezier(0.16, 1, 0.32, 1);
cursor: pointer;
}
.DLP_Hover_1:hover {
filter: brightness(0.9);
}
.DLP_Hover_1:active {
filter: brightness(0.9);
}
.DLP_Row {
display: flex;
flex-direction: row;
}
.DLP_Col {
display: flex;
flex-direction: column;
}
.DLP_Auto {
justify-content: space-between;
}
.DLP_Hug {
justify-content: center;
}
.DLP_Fill_Row {
align-self: stretch;
}
.DLP_Fill_Col {
flex: 1 0 0;
}
.DLP_Left {
justify-content: flex-start;
}
.DLP_Right {
justify-content: flex-end;
}
.DLP_Top {
align-items: flex-start;
}
.DLP_Center {
align-items: center;
}
.DLP_Bottom {
align-items: flex-end;
}
.DLP_Gap_0 {
gap: 0px;
}
.DLP_Gap_2 {
gap: 2px;
}
.DLP_Gap_4 {
gap: 4px;
}
.DLP_Gap_6 {
gap: 6px;
}
.DLP_Gap_8 {
gap: 8px;
}
.DLP_Gap_12 {
gap: 12px;
}
.DLP_Gap_16 {
gap: 16px;
}
.DLP_Gap_24 {
gap: 24px;
}
.DLP_Gap_32 {
gap: 32px;
}
.DLP_Main {
display: inline-flex;
flex-direction: column;
justify-content: flex-end;
align-items: flex-end;
gap: 8px;
position: fixed;
right: 16px;
bottom: 16px;
z-index: 2;
}
@media (max-width: 699px) {
.DLP_Main {
display: inline-flex;
flex-direction: column;
justify-content: flex-end;
align-items: flex-end;
gap: 8px;
position: fixed;
right: 16px;
bottom: 16px;
z-index: 2;
margin-bottom: 80px;
}
}
.DLP_Main_Box {
display: flex;
width: 312px;
padding: 16px;
box-sizing: border-box;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 8px;
overflow: hidden;
border-radius: 20px;
outline: 2px solid rgb(var(--color-eel), 0.10);
outline-offset: -2px;
background: rgb(var(--color-snow), 0.90);
backdrop-filter: blur(16px);
}
.DLP_Main_Box_Divider {
display: flex;
box-sizing: border-box;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 8px;
width: 100%;
}
svg {
flex-shrink: 0;
}
.DLP_HStack_Auto {
display: flex;
align-items: center;
justify-content: space-between;
align-self: stretch;
}
.DLP_HStack_Auto_Top {
display: flex;
align-items: flex-start;
justify-content: space-between;
align-self: stretch;
}
.DLP_HStack_0 {
display: flex;
align-items: center;
gap: 0;
align-self: stretch;
}
.DLP_HStack_4 {
display: flex;
align-items: center;
gap: 4px;
align-self: stretch;
}
.DLP_HStack_6 {
display: flex;
align-items: center;
gap: 6px;
align-self: stretch;
}
.DLP_HStack_8 {
display: flex;
align-items: center;
gap: 8px;
align-self: stretch;
}
.DLP_VStack_0 {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 0;
align-self: stretch;
}
.DLP_VStack_4 {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 4px;
align-self: stretch;
}
.DLP_VStack_6 {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 6px;
align-self: stretch;
}
.DLP_VStack_8 {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 8px;
align-self: stretch;
}
.DLP_VStack_12 {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 12px;
align-self: stretch;
}
.DLP_Hide_Scrollbar {
scrollbar-width: none;
-ms-overflow-style: none;
}
.DLP_Hide_Scrollbar::-webkit-scrollbar {
display: none;
}
.DLP_Button_Style_1 {
display: flex;
height: 40px;
padding: 10px 12px 10px 10px;
box-sizing: border-box;
align-items: center;
gap: 6px;
flex: 1 0 0;
border-radius: 8px;
}
.DLP_Input_Style_1 {
border: none;
outline: none;
background: none;
font-family: "Duolingo PRO Rounded";
font-size: 16px;
font-style: normal;
font-weight: 500;
line-height: normal;
color: rgb(var(--DLP-blue));
width: 100%;
width: 100%; /* Full width */
height: auto; /* Let the height be controlled dynamically */
min-height: 1.2em; /* Set minimum height for one line */
max-height: calc(1.2em * 5); /* Limit to 5 lines */
line-height: 1.2em; /* Adjust the line height */
overflow-y: hidden; /* Hide vertical scrollbar */
resize: none; /* Prevent manual resizing */
padding: 0; /* Remove padding to eliminate extra space */
margin: 0; /* Remove margin to eliminate extra space */
box-sizing: border-box; /* Include padding in height calculation */
}
.DLP_Input_Style_1::placeholder {
color: rgba(var(--DLP-blue), 0.50);
}
.DLP_Input_Input_Style_1 {
border: none;
outline: none;
background: none;
text-align: right;
font-family: "Duolingo PRO Rounded";
font-size: 16px;
font-style: normal;
font-weight: 500;
line-height: normal;
color: rgb(var(--DLP-blue));
width: 100%;
}
.DLP_Input_Input_Style_1::placeholder {
color: rgba(var(--DLP-blue), 0.50);
}
.DLP_Input_Style_1_Active {
display: flex;
height: 48px;
padding: 16px;
box-sizing: border-box;
align-items: center;
flex: 1 0 0;
gap: 6px;
border-radius: 8px;
outline: 2px solid rgba(var(--DLP-blue), 0.20);
outline-offset: -2px;
background: rgba(var(--DLP-blue), 0.10);
}
.DLP_Input_Button_Style_1_Active {
display: flex;
height: 48px;
padding: 12px 12px 12px 14px;
box-sizing: border-box;
justify-content: center;
align-items: center;
gap: 6px;
border-radius: 8px;
outline: 2px solid rgba(0, 0, 0, 0.20);
outline-offset: -2px;
background: rgb(var(--DLP-blue));
}
@keyframes DLP_Rotate_360_Animation_1 {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@keyframes DLP_Pulse_Opacity_Animation_1 {
0% {
opacity: 1;
}
16.66666666% {
opacity: 0.75;
}
33.33333333% {
opacity: 1;
}
100% {
opacity: 1;
}
}
@keyframes DLP_Pulse_Opacity_Animation_2 {
0% {
opacity: 0.75;
}
50% {
opacity: 0.5;
}
100% {
opacity: 0.75;
}
}
.DLP_Scroll_Box_Style_1 {
display: flex;
height: 200px;
padding: 14px 16px;
box-sizing: border-box;
justify-content: center;
align-items: flex-start;
gap: 8px;
align-self: stretch;
border-radius: 8px;
outline: 2px solid rgb(var(--color-eel), 0.10);
outline-offset: -2px;
background: rgb(var(--color-snow), 0.90);
position: relative;
}
.DLP_Scroll_Box_Text_Style_1 {
font-family: "Duolingo PRO Rounded";
font-size: 16px;
font-style: normal;
font-weight: 500;
line-height: normal;
color: rgb(var(--color-wolf));
margin: 0;
overflow-y: scroll;
overflow-x: hidden;
position: absolute;
top: 0;
bottom: 0;
right: 16px;
left: 16px;
padding-top: 16px;
padding-bottom: 16px;
}
.DLP_Scroll_Box_Text_Style_1::-webkit-scrollbar {
transform: translateX(16px);
}
.DLP_Button_Style_2 {
display: flex;
height: 48px;
box-sizing: border-box;
justify-content: center;
align-items: center;
gap: 6px;
flex: 1 0 0;
border-radius: 8px;
}
.DLP_Toggle_Style_1 {
display: flex;
width: 48px;
height: 48px;
padding: 16px;
box-sizing: border-box;
justify-content: center;
align-items: center;
gap: 6px;
border-radius: 8px;
transition: background 0.8s cubic-bezier(0.16, 1, 0.32, 1), filter 0.4s cubic-bezier(0.16, 1, 0.32, 1), transform 0.4s cubic-bezier(0.16, 1, 0.32, 1);
}
.DLP_Toggle_Style_1_ON {
outline: 2px solid rgba(0, 0, 0, 0.20);
outline-offset: -2px;
background: rgb(var(--DLP-green));
}
.DLP_Toggle_Style_1_OFF {
outline: 2px solid rgba(0, 0, 0, 0.20);
outline-offset: -2px;
background: rgb(var(--DLP-pink));
}
.DLP_Large_Input_Box_Style_1 {
display: flex;
padding: 16px;
box-sizing: border-box;
justify-content: center;
align-items: flex-start;
align-self: stretch;
border-radius: 8px;
border: none;
outline: 2px solid rgb(var(--color-eel), 0.10);
outline-offset: -2px;
background: rgb(var(--color-snow), 0.90);
color: rgb(var(--color-eel), 0.50);
font-size: 16px;
font-weight: 500;
font-family: Duolingo PRO Rounded, 'din-round' !important;
resize: vertical;
transition: .2s;
}
.DLP_Large_Input_Box_Style_1::placeholder {
font-weight: 500;
color: rgb(var(--color-eel), 0.25);
}
.DLP_Large_Input_Box_Style_1:focus {
outline: 2px solid rgb(var(--DLP-blue));
}
.DLP_Feedback_Type_Button_Style_1_ON {
outline: 2px solid rgba(0, 0, 0, 0.20);
outline-offset: -2px;
background: rgb(var(--DLP-pink));
}
.DLP_Feedback_Type_Button_Style_1_ON .DLP_Text_Style_1 {
color: #FFF;
}
.DLP_Feedback_Type_Button_Style_1_OFF {
outline: 2px solid rgba(255, 45, 85, 0.20);
outline-offset: -2px;
background: rgba(255, 45, 85, 0.10);
}
.DLP_Feedback_Type_Button_Style_1_OFF .DLP_Text_Style_1 {
color: rgb(var(--DLP-pink));
}
.DLP_Feedback_Type_Button_Style_2_ON {
outline: 2px solid rgba(0, 0, 0, 0.20);
outline-offset: -2px;
background: rgb(var(--DLP-green));
}
.DLP_Feedback_Type_Button_Style_2_ON .DLP_Text_Style_1 {
color: #FFF;
}
.DLP_Feedback_Type_Button_Style_2_OFF {
outline: 2px solid rgba(52, 199, 89, 0.20);
outline-offset: -2px;
background: rgba(52, 199, 89, 0.10);
}
.DLP_Feedback_Type_Button_Style_2_OFF .DLP_Text_Style_1 {
color: rgb(var(--DLP-green));
}
.DLP_Notification_Main {
display: flex;
justify-content: center;
align-items: center;
transition: 0.8s cubic-bezier(0.16, 1, 0.32, 1);
width: 300px;
position: fixed;
left: calc(50% - (300px / 2));
z-index: 210;
bottom: 16px;
border-radius: 16px;
}
.DLP_Notification_Box {
display: flex;
width: 300px;
padding: 16px;
box-sizing: border-box;
flex-direction: column;
justify-content: flex-start;
align-items: center;
gap: 4px;
border-radius: 16px;
outline: 2px solid rgb(var(--color-eel), 0.10);
outline-offset: -2px;
background: rgb(var(--color-snow), 0.90);
backdrop-filter: blur(16px);
transition: 0.8s cubic-bezier(0.16, 1, 0.32, 1);
filter: blur(16px);
opacity: 0;
}
._2V6ug._1ursp._7jW2t._2hkLC._1wiIJ {
width: 36px !important;
height: 38px !important;
}
._2V6ug._1ursp._7jW2t._2hkLC._1wiIJ::before {
border-radius: 20px !important;
}
.DLP_Tooltip {
position: fixed;
display: inline-flex;
height: 40px;
padding: 10px 14px;
box-sizing: border-box;
margin: 0;
align-items: center;
gap: 6px;
flex-shrink: 0;
border-radius: 24px;
outline: 2px solid rgb(var(--color-eel), 0.10);
outline-offset: -2px;
background: rgb(var(--color-snow), 0.90);
backdrop-filter: blur(4px);
filter: blur(8px);
font-family: Duolingo PRO Rounded;
z-index: 10;
opacity: 0;
transition: opacity 0.5s ease-in-out, filter 0.5s ease-in-out;
white-space: nowrap;
pointer-events: none;
}
.DLP_Tooltip.DLP_Tooltip_Visible {
opacity: 1;
filter: blur(0px);
}
.DLP_Attachment_Box_1 {
width: 96px;
height: 96px;
aspect-ratio: 1/1;
object-fit: cover;
overflow: hidden;
position: relative;
border-radius: 8px;
outline: 2px solid rgba(var(--color-black-white), 0.20);
outline-offset: -2px;
background: rgba(var(--color-black-text), 0.20); /* Gotta change */
}
.DLP_Attachment_Box_1_Content {
width: 100%;
height: 100%;
aspect-ratio: 1/1;
object-fit: cover;
}
.DLP_Attachment_Box_1_Hover {
display: flex;
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
flex-direction: column;
justify-content: center;
align-items: center;
flex-shrink: 0;
background: rgba(var(--color-snow), 0.50);
backdrop-filter: blur(8px);
outline: inherit;
outline-offset: inherit;
border-radius: inherit;
}
.DLP_Attachment_Box_Large_View_1 {
display: flex;
position: fixed;
inset: 0;
width: 100%;
height: 100vh;
justify-content: center;
align-items: center;
flex-shrink: 0;
background: rgba(var(--color-snow), 0.00);
backdrop-filter: blur(0px);
z-index: 211;
transition: 0.4s cubic-bezier(0.16, 1, 0.32, 1);
}
.DLP_Attachment_Box_Drop_1 {
display: flex;
height: 48px;
padding: 16px;
justify-content: center;
align-items: center;
gap: 6px;
border-radius: 8px;
/* outline: 2px dashed rgba(var(--DLP-blue), 0.20); */
outline: 2px solid rgba(var(--DLP-blue), 0.20);
outline-offset: -2px;
background: linear-gradient(0deg, rgba(var(--DLP-blue), 0.10) 0%, rgba(var(--DLP-blue), 0.10) 100%), rgba(var(--color-snow), 0.90);
}
@keyframes slideRight {
0% {
transform: translateX(-150px);
}
20% {
transform: translateX(200px);
}
100% {
transform: translateX(200px);
}
}
`;
HTML3 = `
`;
HTML4 = `
.solving-btn {
position: relative;
min-width: 150px;
font-size: 17px;
border: none;
border-bottom: 4px solid #2b70c9;
border-radius: 16px;
padding: 13px 16px;
transition: filter .0s;
font-weight: 700;
letter-spacing: .8px;
background: #1cb0f6;
color: rgb(var(--color-snow));
cursor: pointer;
}
.solve-btn {
position: relative;
min-width: 100px;
font-size: 17px;
border: none;
border-bottom: 4px solid #ff9600;
border-radius: 16px;
padding: 13px 16px;
transition: filter .0s;
font-weight: 700;
letter-spacing: .8px;
background: #ffc800;
color: rgb(var(--color-snow));
cursor: pointer;
}
.auto-solver-btn:hover {
filter: brightness(1.1);
}
.auto-solver-btn:active {
border-bottom: 0px;
margin-bottom: 4px;
top: 4px;
}
`;
HTML5 = `
`;
CSS5 = `
.DLP_AutoServer_Text_Style_1 {
font-family: "Duolingo PRO Rounded";
font-size: 16px;
font-style: normal;
font-weight: 500;
line-height: normal;
margin: 0;
-webkit-font-smoothing: antialiased;
}
.DLP_AutoServer_Text_Style_2 {
font-family: "Duolingo PRO Rounded";
font-size: 24px;
font-style: normal;
font-weight: 500;
line-height: normal;
margin: 0;
-webkit-font-smoothing: antialiased;
}
.DLP_AutoServer_Mother_Box {
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
display: flex;
width: 100%;
height: 100vh;
justify-content: center;
align-items: center;
flex-shrink: 0;
z-index: 210;
transition: filter 0.4s cubic-bezier(0.16, 1, 0.32, 1), opacity 0.4s cubic-bezier(0.16, 1, 0.32, 1);
background: rgba(var(--color-snow), 0.50);
backdrop-filter: blur(16px);
}
.DLP_AutoServer_Box {
display: flex;
width: 512px;
height: 512px;
flex-direction: column;
align-items: center;
flex-shrink: 0;
overflow-y: scroll;
overflow-x: hidden;
scrollbar-width: none;
-ms-overflow-style: none;
border-radius: 20px;
border: 2px solid rgba(var(--color-eel), 0.10);
background: rgba(var(--color-snow), 0.90);
backdrop-filter: blur(16px);
box-sizing: border-box;
}
.DLP_AutoServer_Menu_Bar {
display: flex;
width: 100%;
height: 64px;
padding: 16px;
justify-content: space-between;
align-items: center;
position: sticky;
top: 0;
right: 0;
left: 0;
background: rgba(var(--color-snow), 0.80);
backdrop-filter: blur(8px);
z-index: 2;
}
.DLP_AutoServer_Scroll_Box {
display: flex;
padding: 0 16px 16px 16px;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 8px;
align-self: stretch;
}
.DLP_AutoServer_Default_Box {
display: flex;
padding: 16px;
justify-content: center;
align-items: center;
gap: 16px;
align-self: stretch;
border-radius: 8px;
outline: 2px solid rgba(0, 0, 0, 0.10);
outline-offset: -2px;
}
`;
HTML6 = `
`;
CSS6 = `
.DPAutoServerButtonMainMenu {
display: flex;
box-sizing: border-box;
justify-content: center;
align-items: center;
gap: 16px;
flex-shrink: 0;
border-radius: 12px;
cursor: pointer;
}
.DPAutoServerButtonMainMenu:hover {
background: rgba(var(--DLP-blue), 0.10);
}
.DPAutoServerButtonMainMenu:active {
filter: brightness(.9);
}
.DPAutoServerButtonMainMenu:hover .DPAutoServerElementsMenu {
opacity: 1 !important;
}
.DPAutoServerButtonMainMenuMedium {
width: 56px;
height: 52px;
padding: 8px;
}
.DPAutoServerButtonMainMenuLarge {
width: 222px;
height: 52px;
padding: 16px 17px;
}
`;
HTML7 = `
`;
CSS7 = `
.vCIrKKxykXwXyUza {
outline: 2px solid rgb(var(--color-swan));
outline-offset: -2px;
height: 40px;
width: auto;
padding: 0 16px;
gap: 6px;
display: inline-flex;
justify-content: center;
align-items: center;
flex-wrap: nowrap;
flex-shrink: 0;
border-radius: 32px;
background: rgb(var(--color-snow), 0.84);
backdrop-filter: blur(16px);
overflow: hidden;
transition: all 0.4s cubic-bezier(0.16, 1, 0.32, 1);
cursor: pointer;
}
.vCIrKKxykXwXyUza p {
white-space: nowrap;
}
.vCIrKKxykXwXyUza svg {
flex-shrink: 0;
}
`;
}
function One() {
Two();
document.head.appendChild(Object.assign(document.createElement('style'), { type: 'text/css', textContent: CSS1 }));
document.body.insertAdjacentHTML('beforeend', HTML2);
document.head.appendChild(Object.assign(document.createElement('style'), { type: 'text/css', textContent: CSS2 }));
document.body.insertAdjacentHTML('beforeend', HTML5);
document.head.appendChild(Object.assign(document.createElement('style'), { type: 'text/css', textContent: CSS5 }));
let DPAutoServerButtonMainMenuElement = null;
let DPAutoServerButtonMainMenuStyle = null;
function DPAutoServerButtonMainMenuFunction() {
try {
if (storageLocal.settings.showAutoServerButton && alpha) {
let targetElement = document.querySelector('._2uLXp');
if (!targetElement || document.querySelector('.DPAutoServerButtonMainMenu')) return;
DPAutoServerButtonMainMenuStyle = document.createElement('style');
DPAutoServerButtonMainMenuStyle.type = 'text/css';
DPAutoServerButtonMainMenuStyle.innerHTML = CSS6;
document.head.appendChild(DPAutoServerButtonMainMenuStyle);
let targetDivLast = document.querySelector('[data-test="profile-tab"]');
if (targetElement && targetDivLast) {
targetElement.lastChild.insertAdjacentHTML('beforebegin', HTML6);
let otherTargetDiv = document.querySelector('.DPAutoServerButtonMainMenu');
otherTargetDiv.addEventListener('click', () => {
manageAutoServerWindowVisibility(true);
});
let lastWidth = targetElement.offsetWidth;
const resizeObserver = new ResizeObserver(entries => {
for (let entry of entries) {
if (entry.target.offsetWidth !== lastWidth) {
otherTargetDiv.remove();
DPAutoServerButtonMainMenuFunction();
lastWidth = entry.target.offsetWidth;
}
}
});
resizeObserver.observe(targetElement);
if (targetElement.offsetWidth < 100) {
otherTargetDiv.classList.add('DPAutoServerButtonMainMenuMedium');
document.querySelectorAll('.DPAutoServerElementsMenu').forEach(function(element) {
element.remove();
});
} else {
otherTargetDiv.classList.add('DPAutoServerButtonMainMenuLarge');
}
}
}
} catch(error) {}
}
setInterval(DPAutoServerButtonMainMenuFunction, 500);
document.querySelector('.DLP_AutoServer_Mother_Box').querySelector('#DLP_AutoServer_Close_Button_1_ID').addEventListener('click', () => {
manageAutoServerWindowVisibility(false);
});
document.querySelector('.DLP_AutoServer_Mother_Box').addEventListener('click', (event) => {
if (event.target === event.currentTarget) {
manageAutoServerWindowVisibility(false);
}
});
function manageAutoServerWindowVisibility(state) {
if (state) {
document.querySelector('.DLP_AutoServer_Mother_Box').style.display = "";
document.querySelector('.DLP_AutoServer_Mother_Box').offsetHeight;
document.querySelector('.DLP_AutoServer_Mother_Box').style.opacity = "1";
document.querySelector('.DLP_AutoServer_Mother_Box').style.filter = "blur(0px)";
} else {
document.querySelector('.DLP_AutoServer_Mother_Box').style.opacity = "0";
document.querySelector('.DLP_AutoServer_Mother_Box').style.filter = "blur(8px)";
setTimeout(() => {
document.querySelector('.DLP_AutoServer_Mother_Box').style.display = "none";
}, 400);
}
}
let counterPaused = false;
function DuolingoProCounterOneFunction() {
function handleMuteTab(value) {
if (isBusySwitchingPages) return;
isBusySwitchingPages = true;
muteTab(value);
let button = document.querySelector('#DLP_Inset_Button_2_ID');
if (value) {
setButtonState(button, {button: 'rgb(var(--color-snow), 0.84)', outline: 'rgb(var(--color-swan))', text: 'rgb(var(--color-black-text))', icon: 'rgb(var(--color-black-text))'}, {text: 'Muted', icon: ''}, {text: '', icon: ' '}, () => {
setTimeout(() => {
isBusySwitchingPages = false;
}, 400);
});
} else {
setButtonState(button, {button: 'rgb(var(--color-snow), 0.84)', outline: 'rgb(var(--color-swan))', text: 'rgb(var(--color-black-text))', icon: 'rgb(var(--color-black-text))'}, {text: 'Mute', icon: ''}, {text: '', icon: ' '}, () => {
setTimeout(() => {
isBusySwitchingPages = false;
}, 400);
});
}
}
if ((window.location.pathname.includes('/lesson') || window.location.pathname.includes('/practice')) && storageSession.legacy.status) {
let theBarThing = document.querySelector('#DLP_The_Bar_Thing_Box');
if (!theBarThing) {
document.head.appendChild(Object.assign(document.createElement('style'), { type: 'text/css', textContent: CSS7 }));
//const targetElement1 = document.querySelector('.I-Avc._1zcW8');
const targetElement1 = document.querySelector('._1zcW8');
const targetElement2 = document.querySelector('.mAxZF');
if (targetElement1) {
targetElement1.insertAdjacentHTML('beforeend', HTML7);
theBarThing = document.querySelector('#DLP_The_Bar_Thing_Box');
targetElement1.style.display = "flex";
document.querySelector('[role="progressbar"]').style.width = "100%";
} else if (targetElement2) {
targetElement2.insertAdjacentHTML('beforeend', HTML7);
theBarThing = document.querySelector('#DLP_The_Bar_Thing_Box');
theBarThing.style.marginLeft = '24px';
document.querySelector('._15ch1').style.pointerEvents = 'all';
}
else if (debug) console.log('Element with class ._1zcW8 or .mAxZF not found');
let muteButton = theBarThing.querySelector('.DLP_Inset_Button_2_ID');
let expandButton = theBarThing.querySelector('.DLP_Inset_Button_3_ID');
let expandButtonIcon = expandButton.querySelector('.DLP_Inset_Icon_1_ID');
let theBarThingExtended = false;
function theBarThingExtend(button, visibility, noAnimation) {
if (visibility) {
button.style.display = "";
button.style.width = "";
button.style.padding = "";
button.style.transition = '';
let remember0010 = button.offsetWidth;
button.style.width = "0px";
requestAnimationFrame(() => {
button.style.width = remember0010 + "px";
button.style.padding = "";
button.style.filter = "blur(0px)";
button.style.opacity = "1";
button.style.margin = "";
});
} else {
button.style.transition = '';
button.style.width = button.offsetWidth + "px";
requestAnimationFrame(() => {
button.style.width = "4px";
button.style.padding = "0";
button.style.filter = "blur(8px)";
button.style.margin = "0 -4px";
button.style.opacity = "0";
});
if (!noAnimation) {
setTimeout(function() {
button.style.display = "none";
}, 400);
} else {
button.style.display = "none";
}
}
}
theBarThingExtend(muteButton, false, true);
expandButton.addEventListener('click', () => {
if (theBarThingExtended) {
expandButtonIcon.style.transform = "rotate(0deg)";
theBarThingExtended = false;
theBarThingExtend(muteButton, false);
} else {
expandButtonIcon.style.transform = "rotate(180deg)";
theBarThingExtended = true;
theBarThingExtend(muteButton, true);
}
});
let counterButton = theBarThing.querySelector('.DLP_Inset_Button_1_ID');
counterButton.addEventListener('click', () => {
if (isBusySwitchingPages) return;
isBusySwitchingPages = true;
if (theBarThing.querySelector('#DLP_Inset_Button_1_ID').querySelector('#DLP_Inset_Text_1_ID').innerHTML === 'Click Again to Stop Legacy') {
setButtonState(counterButton, {button: 'rgb(var(--color-snow), 0.84)', outline: 'rgb(var(--color-swan))', text: 'rgb(var(--color-black-text))', icon: 'rgb(var(--color-black-text))'}, {text: 'Stopping', icon: undefined}, {text: '', icon: ''}, () => {
storageSession.legacy.status = false;
saveStorageSession();
setTimeout(() => {
isBusySwitchingPages = false;
window.location.href = "https://duolingo.com";
}, 400);
});
} else {
counterPaused = true;
setButtonState(counterButton, {button: 'rgb(var(--color-snow), 0.84)', outline: 'rgb(var(--color-swan))', text: 'rgb(var(--color-black-text))', icon: 'rgb(var(--color-black-text))'}, {text: 'Click Again to Stop', icon: undefined}, {text: '', icon: ''}, () => {
setTimeout(() => {
isBusySwitchingPages = false;
setTimeout(() => {
if (storageSession.legacy.status) counterPaused = false;
}, 4000);
}, 400);
});
}
});
if (storageLocal.settings.muteLessons) {
handleMuteTab(true);
}
document.querySelector('#DLP_Inset_Button_2_ID').addEventListener('click', () => {
storageLocal.settings.muteLessons = !storageLocal.settings.muteLessons;
saveStorageLocal();
handleMuteTab(storageLocal.settings.muteLessons);
});
}
function updateCounter() {
let button = theBarThing.querySelector('.DLP_Inset_Button_1_ID');
let text = button.querySelector('.DLP_Inset_Text_1_ID');
if (storageSession.legacy[storageSession.legacy.status].type === 'infinity' && text.textContent !== 'Infinity') {
isBusySwitchingPages = true;
setButtonState(button, {button: 'rgb(var(--color-snow), 0.84)', outline: 'rgb(var(--color-swan))', text: 'rgb(var(--color-black-text))', icon: 'rgb(var(--color-black-text))'}, {text: 'Infinity', icon: ''}, {text: '', icon: ''}, () => {
setTimeout(() => {
isBusySwitchingPages = false;
}, 400);
});
} else if (storageSession.legacy[storageSession.legacy.status].type === 'xp' && text.textContent !== String(storageSession.legacy[storageSession.legacy.status].amount + ' XP Left')) {
isBusySwitchingPages = true;
setButtonState(button, {button: 'rgb(var(--color-snow), 0.84)', outline: 'rgb(var(--color-swan))', text: 'rgb(var(--color-black-text))', icon: 'rgb(var(--color-black-text))'}, {text: String(storageSession.legacy[storageSession.legacy.status].amount + ' XP Left'), icon: ''}, {text: '', icon: ''}, () => {
setTimeout(() => {
isBusySwitchingPages = false;
}, 400);
});
} else if (window.location.pathname === '/practice') {
if (storageSession.legacy[storageSession.legacy.status].amount === 1 && text.textContent !== 'Last Practice') {
isBusySwitchingPages = true;
setButtonState(button, {button: 'rgb(var(--color-snow), 0.84)', outline: 'rgb(var(--color-swan))', text: 'rgb(var(--color-black-text))', icon: 'rgb(var(--color-black-text))'}, {text: 'Last Practice', icon: ''}, {text: '', icon: ''}, () => {
setTimeout(() => {
isBusySwitchingPages = false;
}, 400);
});
} else if (storageSession.legacy[storageSession.legacy.status].amount === 0 && text.textContent !== 'Finishing Up') {
isBusySwitchingPages = true;
setButtonState(button, {button: 'rgb(var(--color-snow), 0.84)', outline: 'rgb(var(--color-swan))', text: 'rgb(var(--color-black-text))', icon: 'rgb(var(--color-black-text))'}, {text: 'Finishing Up', icon: ''}, {text: '', icon: ''}, () => {
setTimeout(() => {
isBusySwitchingPages = false;
}, 400);
});
} else if (storageSession.legacy[storageSession.legacy.status].amount > 1 && text.textContent !== String(storageSession.legacy[storageSession.legacy.status].amount + ' Practices Left')) {
isBusySwitchingPages = true;
setButtonState(button, {button: 'rgb(var(--color-snow), 0.84)', outline: 'rgb(var(--color-swan))', text: 'rgb(var(--color-black-text))', icon: 'rgb(var(--color-black-text))'}, {text: String(storageSession.legacy[storageSession.legacy.status].amount + ' Practices Left'), icon: ''}, {text: '', icon: ''}, () => {
setTimeout(() => {
isBusySwitchingPages = false;
}, 400);
});
}
} else if (storageSession.legacy[storageSession.legacy.status].type === 'lesson') {
if (storageSession.legacy[storageSession.legacy.status].amount === 1 && text.textContent !== 'Last Lesson') {
isBusySwitchingPages = true;
setButtonState(button, {button: 'rgb(var(--color-snow), 0.84)', outline: 'rgb(var(--color-swan))', text: 'rgb(var(--color-black-text))', icon: 'rgb(var(--color-black-text))'}, {text: 'Last Lesson', icon: ''}, {text: '', icon: ''}, () => {
setTimeout(() => {
isBusySwitchingPages = false;
}, 400);
});
} else if (storageSession.legacy[storageSession.legacy.status].amount === 0 && text.textContent !== 'Finishing Up') {
isBusySwitchingPages = true;
setButtonState(button, {button: 'rgb(var(--color-snow), 0.84)', outline: 'rgb(var(--color-swan))', text: 'rgb(var(--color-black-text))', icon: 'rgb(var(--color-black-text))'}, {text: 'Finishing Up', icon: ''}, {text: '', icon: ''}, () => {
setTimeout(() => {
isBusySwitchingPages = false;
}, 400);
});
} else if (storageSession.legacy[storageSession.legacy.status].amount > 1 && text.textContent !== String(storageSession.legacy[storageSession.legacy.status].amount + ' Lessons Left')) {
isBusySwitchingPages = true;
setButtonState(button, {button: 'rgb(var(--color-snow), 0.84)', outline: 'rgb(var(--color-swan))', text: 'rgb(var(--color-black-text))', icon: 'rgb(var(--color-black-text))'}, {text: String(storageSession.legacy[storageSession.legacy.status].amount + ' Lessons Left'), icon: ''}, {text: '', icon: ''}, () => {
setTimeout(() => {
isBusySwitchingPages = false;
}, 400);
});
}
}
}
if (!counterPaused) updateCounter();
}
}
setInterval(DuolingoProCounterOneFunction, 500);
window.onfocus = () => {
windowBlurState = true;
};
window.onblur = () => {
windowBlurState = false;
};
function addButtons() {
if (!storageLocal.settings.showSolveButtons) return;
if (window.location.pathname === '/learn' && document.querySelector('a[data-test="global-practice"]')) return;
if (document.querySelector("#solveAllButton")) return;
document.querySelector('[data-test="quit-button"]')?.addEventListener('click', function() {
solving("stop");
//storageSession.legacy.status = false;
//saveStorageSession();
});
function createButton(id, text, styleClass, eventHandlers) {
const button = document.createElement('button');
button.id = id;
button.innerText = text;
button.className = styleClass;
Object.keys(eventHandlers).forEach(event => {
button.addEventListener(event, eventHandlers[event]);
});
return button;
}
const nextButton = document.querySelector('[data-test="player-next"]');
const storiesContinueButton = document.querySelector('[data-test="stories-player-continue"]');
const storiesDoneButton = document.querySelector('[data-test="stories-player-done"]');
const target = nextButton || storiesContinueButton || storiesDoneButton;
if (document.querySelector('[data-test="story-start"]') && storageSession.legacy.status) {
document.querySelector('[data-test="story-start"]').click();
}
if (!target) {
const startButton = document.querySelector('[data-test="start-button"]');
if (!startButton) {
return;
}
const solveAllButton = createButton("solveAllButton", "COMPLETE SKILL", "solve-all-btn", {
'click': () => {
solving(true);
setInterval(() => {
const startButton = document.querySelector('[data-test="start-button"]');
if (startButton && startButton.innerText.startsWith("START")) {
startButton.click();
}
}, 1000);
startButton.click();
}
});
startButton.parentNode.appendChild(solveAllButton);
} else {
if (document.querySelector('.MYehf') !== null) {
document.querySelector('.MYehf').style.display = "flex";
document.querySelector('.MYehf').style.gap = "20px";
} else if (document.querySelector(".FmlUF") !== null) { // Story
findReactMainElementClass = '_3TJzR';
reactTraverseUp = 0;
document.querySelector('._3TJzR').style.display = "flex";
document.querySelector('._3TJzR').style.gap = "20px";
}
const buttonsCSS = document.createElement('style');
buttonsCSS.innerHTML = HTML4;
document.head.appendChild(buttonsCSS);
const solveCopy = createButton('solveAllButton', systemText[systemLanguage][101], 'auto-solver-btn solving-btn', { click: solving });
const pauseCopy = createButton('', systemText[systemLanguage][100], 'auto-solver-btn solve-btn', { click: solve });
target.parentElement.appendChild(pauseCopy);
target.parentElement.appendChild(solveCopy);
if (storageSession.legacy.status) {
solving("start");
}
}
}
setInterval(addButtons, 500);
let notificationCount = 0;
let currentNotification = [];
let notificationsHovered = false;
const notificationMain = document.querySelector('.DLP_Notification_Main');
notificationMain.addEventListener('mouseenter', () => {
notificationsHovered = true;
});
notificationMain.addEventListener('mouseleave', () => {
notificationsHovered = false;
});
function showNotification(icon, head, body, time = 0) {
notificationCount++;
let notificationID = notificationCount;
currentNotification.push(notificationID);
let element = new DOMParser().parseFromString(HTML3, 'text/html').body.firstChild;
element.id = 'DLP_Notification_Box_' + notificationID + '_ID';
notificationMain.appendChild(element);
initializeMagneticHover(element.querySelector('.DLP_Inset_Icon_2_ID'));
let iconElement = element.querySelector('.DLP_Inset_Icon_1_ID');
if (icon === "") {
iconElement.style.display = 'none';
playHaptic();
} else if (icon === "checkmark") {
iconElement.style.color = "rgb(var(--DLP-green))";
iconElement.textContent = "";
playHaptic("success");
} else if (icon === "warning") {
iconElement.style.color = "rgb(var(--DLP-orange))";
iconElement.textContent = "";
playHaptic("warning");
} else if (icon === "error") {
iconElement.style.color = "rgb(var(--DLP-pink))";
iconElement.textContent = "";
playHaptic("error");
} else {
iconElement.style.color = icon.color;
iconElement.textContent = icon.icon;
playHaptic();
}
element.querySelector('.DLP_Inset_Text_1_ID').innerHTML = head;
if (body && body !== "") {
element.querySelector('.DLP_Inset_Text_2_ID').innerHTML = body;
} else {
element.querySelector('.DLP_Inset_Text_2_ID').style.display = "none";
}
let notification = document.querySelector(
'#DLP_Notification_Box_' + notificationID + '_ID'
);
let notificationHeight = notification.offsetHeight;
notification.style.bottom = '-' + notificationHeight + 'px';
setTimeout(() => {
requestAnimationFrame(() => {
notification.style.bottom = "16px";
notification.style.filter = "blur(0px)";
notification.style.opacity = "1";
});
}, 50);
let isBusyDisappearing = false;
let timerData = null;
if (time !== 0) {
timerData = {
remaining: time * 1000,
lastTimestamp: Date.now(),
timeoutHandle: null,
paused: false,
};
timerData.timeoutHandle = setTimeout(internalDisappear, timerData.remaining);
}
let repeatInterval = setInterval(() => {
if (document.body.offsetWidth <= 963) {
requestAnimationFrame(() => {
notificationMain.style.width = "300px";
notificationMain.style.position = "fixed";
notificationMain.style.left = "16px";
});
} else {
requestAnimationFrame(() => {
notificationMain.style.width = "";
notificationMain.style.position = "";
notificationMain.style.left = "";
});
}
if (isBusyDisappearing) return;
if (timerData) {
if (notificationsHovered && !timerData.paused) {
clearTimeout(timerData.timeoutHandle);
let elapsed = Date.now() - timerData.lastTimestamp;
timerData.remaining -= elapsed;
timerData.paused = true;
}
if (!notificationsHovered && timerData.paused) {
timerData.paused = false;
timerData.lastTimestamp = Date.now();
timerData.timeoutHandle = setTimeout(internalDisappear, timerData.remaining);
}
}
if (notificationsHovered) {
let allIDs = currentNotification.slice();
let bottoms = {};
let currentBottom = 16;
for (let i = allIDs.length - 1; i >= 0; i--) {
let notifEl = document.querySelector(
'#DLP_Notification_Box_' + allIDs[i] + '_ID'
);
if (!notifEl) continue;
notifEl.style.width = "";
notifEl.style.height = "";
notifEl.style.transform = "";
bottoms[allIDs[i]] = currentBottom;
currentBottom += notifEl.offsetHeight + 8;
}
notification.style.bottom = bottoms[notificationID] + "px";
let totalHeight = 0;
for (let i = 0; i < allIDs.length; i++) {
let notifEl = document.querySelector(
'#DLP_Notification_Box_' + allIDs[i] + '_ID'
);
if (notifEl) {
totalHeight += notifEl.offsetHeight;
}
}
if (allIDs.length > 1) {
totalHeight += (allIDs.length - 1) * 8;
}
notificationMain.style.height = totalHeight + "px";
} else {
notificationMain.style.height = '';
notification.style.bottom = "16px";
if (currentNotification[currentNotification.length - 1] !== notificationID) {
notification.style.height = notificationHeight + 'px';
requestAnimationFrame(() => {
let latestNotif = document.querySelector(
'#DLP_Notification_Box_' +
String(currentNotification[currentNotification.length - 1]) +
'_ID'
);
if (latestNotif) {
notification.style.height = latestNotif.offsetHeight + 'px';
}
notification.style.width = "284px";
notification.style.transform = "translateY(-8px)";
});
} else {
requestAnimationFrame(() => {
notification.style.height = notificationHeight + "px";
notification.style.width = "";
notification.style.transform = "";
});
}
}
}, 20);
function internalDisappear() {
if (timerData && timerData.timeoutHandle) {
clearTimeout(timerData.timeoutHandle);
}
if (isBusyDisappearing) return;
isBusyDisappearing = true;
currentNotification.splice(currentNotification.indexOf(notificationID), 1);
requestAnimationFrame(() => {
notification.style.bottom = "-" + notificationHeight + "px";
notification.style.filter = "blur(16px)";
notification.style.opacity = "0";
});
clearInterval(repeatInterval);
setTimeout(() => {
notification.remove();
if (currentNotification.length === 0) {
notificationMain.style.height = '';
}
}, 800);
}
function disappear() {
internalDisappear();
}
notification.querySelector('.DLP_Inset_Icon_2_ID').addEventListener("click", disappear);
return {
close: disappear
};
}
let isBusySwitchingPages = false;
let pages = {
"DLP_Onboarding_Start_Button_1_ID": [5],
"DLP_Switch_Legacy_Button_1_ID": [3],
"DLP_Universal_Back_1_Button_1_ID": [1],
"DLP_Main_Settings_1_Button_1_ID": [7],
//"DLP_Main_Feedback_1_Button_1_ID": [8],
"DLP_Main_Feedback_1_Button_1_ID": [11],
"DLP_Main_Whats_New_1_Button_1_ID": [9],
"DLP_Main_See_More_1_Button_1_ID": [2],
"DLP_Main_Terms_1_Button_1_ID": [5],
"DLP_Secondary_Settings_1_Button_1_ID": [7],
"DLP_Secondary_Feedback_1_Button_1_ID": [8],
"DLP_Secondary_Whats_New_1_Button_1_ID": [9],
"DLP_Secondary_See_More_1_Button_1_ID": [4],
"DLP_Secondary_Terms_1_Button_1_ID": [5],
"DLP_Terms_Back_Button_1_ID": [1],
"DLP_Terms_Accept_Button_1_ID": [1],
"DLP_Terms_Decline_Button_1_ID": [6],
"DLP_Terms_Declined_Back_Button_1_ID": [5]
};
function goToPage(to, buttonID) {
if (isBusySwitchingPages) return;
isBusySwitchingPages = true;
let mainBox = document.querySelector(`.DLP_Main_Box`);
let toNumber = to;
let fromPage = document.querySelector(`#DLP_Main_Box_Divider_${currentPage}_ID`);
let toPage = document.querySelector(`#DLP_Main_Box_Divider_${toNumber}_ID`);
let mainBoxNewToBeWidth = mainBox.offsetWidth;
if (buttonID === 'DLP_Main_Terms_1_Button_1_ID' || buttonID === 'DLP_Secondary_Terms_1_Button_1_ID') {
document.querySelector(`#DLP_Terms_1_Text_1_ID`).style.display = 'none';
document.querySelector(`#DLP_Terms_1_Button_1_ID`).style.display = 'none';
document.querySelector(`#DLP_Terms_1_Text_2_ID`).style.display = 'block';
document.querySelector(`#DLP_Terms_1_Button_2_ID`).style.display = 'block';
} else if (buttonID === 'DLP_Terms_Back_Button_1_ID') {
toNumber = lastPage;
toPage = document.querySelector(`#DLP_Main_Box_Divider_${toNumber}_ID`);
setTimeout(() => {
document.querySelector(`#DLP_Terms_1_Text_1_ID`).style.display = 'block';
document.querySelector(`#DLP_Terms_1_Button_1_ID`).style.display = 'block';
document.querySelector(`#DLP_Terms_1_Text_2_ID`).style.display = 'none';
document.querySelector(`#DLP_Terms_1_Button_2_ID`).style.display = 'none';
}, 400);
} else if (buttonID === 'DLP_Universal_Back_1_Button_1_ID' || to === -1) {
toNumber = lastPage;
toPage = document.querySelector(`#DLP_Main_Box_Divider_${toNumber}_ID`);
} else if (buttonID === 'DLP_Switch_Legacy_Button_1_ID') {
let button = document.querySelector('#DLP_Switch_Legacy_Button_1_ID');
console.log(storageSession.legacy.page);
if (storageSession.legacy.page !== 0) {
toNumber = 1;
toPage = document.querySelector(`#DLP_Main_Box_Divider_${toNumber}_ID`);
setButtonState(button, {button: 'linear-gradient(0deg, rgba(var(--DLP-blue), 0.10) 0%, rgba(var(--DLP-blue), 0.10) 100%), rgba(var(--color-snow), 0.80)', outline: 'rgba(var(--DLP-blue), 0.20)', text: 'rgb(var(--DLP-blue))', icon: 'rgb(var(--DLP-blue))'}, {text: systemText[systemLanguage][106], icon: ''}, {text: '', icon: ''});
storageSession.legacy.page = 0;
saveStorageSession();
} else {
toNumber = 3;
toPage = document.querySelector(`#DLP_Main_Box_Divider_${toNumber}_ID`);
setButtonState(button, {button: 'linear-gradient(0deg, rgba(var(--DLP-blue), 0.10) 0%, rgba(var(--DLP-blue), 0.10) 100%), rgba(var(--color-snow), 0.80)', outline: 'rgba(var(--DLP-blue), 0.20)', text: 'rgb(var(--DLP-blue))', icon: 'rgb(var(--DLP-blue))'}, {text: systemText[systemLanguage][105], icon: ''}, {text: '', icon: ''});
storageSession.legacy.page = 1;
saveStorageSession();
}
} else if (buttonID === 'DLP_Terms_Accept_Button_1_ID') {
storageLocal.terms = newTermID;
saveStorageLocal();
connectToServer();
} else if (buttonID === 'DLP_Onboarding_Start_Button_1_ID') {
storageLocal.onboarding = true;
saveStorageLocal();
goToPage(1);
} else if (buttonID === 'DLP_Main_Feedback_1_Button_1_ID') {
setTimeout(() => {
const chatBox = document.querySelector('#DLP_Main_Box_Divider_11_ID')?.querySelector('.DLP_Chat_Box_1_ID_1');
chatBox.scrollTop = chatBox.scrollHeight;
}, 420);
} else if (toNumber === 7) {
const trackingSinceDateString = new Date(storageLocal.stats.tracking_since).toLocaleDateString(systemLanguage, { month: 'short', day: 'numeric', year: 'numeric' });
let modernStatsBox = document.querySelector('#DLP_Main_Box_Divider_7_ID').querySelector('#DLP_Settings_Modern_Stats_Main_Box_1_ID');
modernStatsBox.children[0].lastElementChild.innerHTML = "since " + trackingSinceDateString;
modernStatsBox.children[1].lastElementChild.innerHTML = storageLocal.stats.modern.xp;
modernStatsBox.children[2].lastElementChild.innerHTML = storageLocal.stats.modern.gem;
modernStatsBox.children[3].lastElementChild.innerHTML = storageLocal.stats.modern.streak;
modernStatsBox.children[4].lastElementChild.innerHTML = storageLocal.stats.modern.heart_refill;
let legacyStatsBox = document.querySelector('#DLP_Main_Box_Divider_7_ID').querySelector('#DLP_Settings_Legacy_Stats_Main_Box_1_ID');
legacyStatsBox.children[0].lastElementChild.innerHTML = "since " + trackingSinceDateString;
legacyStatsBox.children[1].lastElementChild.innerHTML = (storageLocal.stats.legacy.listen.lessons + storageLocal.stats.legacy.path.lessons + storageLocal.stats.legacy.practice.lessons + storageLocal.stats.legacy.lesson.lessons);
legacyStatsBox.children[2].lastElementChild.innerHTML = (storageLocal.stats.legacy.listen.questions + storageLocal.stats.legacy.path.questions + storageLocal.stats.legacy.practice.questions + storageLocal.stats.legacy.lesson.questions);
}
if (toNumber === 11) {
if (newReplyButtonActive) {
newReplyButtonActive = false;
updateConnetionButtonStyles(document.getElementById("DLP_Main_Feedback_1_Button_1_ID"), {button: 'linear-gradient(0deg, rgba(var(--DLP-blue), 0.10) 0%, rgba(var(--DLP-blue), 0.10) 100%), rgba(var(--color-snow), 0.80)', outline: 'rgba(var(--DLP-blue), 0.20)', text: 'rgb(var(--DLP-blue))', icon: 'rgb(var(--DLP-blue))'}, {text: systemText[systemLanguage][5], icon: ''}, {text: '', icon: ''});
}
}
if (toNumber === 2) mainBoxNewToBeWidth = "600";
else if (toNumber === 5) mainBoxNewToBeWidth = "400";
else if (toNumber === 7) mainBoxNewToBeWidth = "400";
else if (toNumber === 8) mainBoxNewToBeWidth = "400";
else if (toNumber === 9) mainBoxNewToBeWidth = "400";
else if (toNumber === 11) mainBoxNewToBeWidth = "400";
else mainBoxNewToBeWidth = "312";
if ([1, 2, 3, 4].includes(toNumber)) legacyButtonVisibility(true);
else legacyButtonVisibility(false);
if (toNumber === 3) {
storageSession.legacy.page = 1;
saveStorageSession();
} else if (toNumber === 4) {
storageSession.legacy.page = 2;
saveStorageSession();
}
let mainBoxOldWidth = mainBox.offsetWidth;
let mainBoxOldHeight = mainBox.offsetHeight;
let fromBoxOldWidth = fromPage.offsetWidth;
let fromBoxOldHeight = fromPage.offsetHeight;
console.log(fromBoxOldWidth, fromBoxOldHeight);
mainBox.style.transition = "";
fromPage.style.display = "none";
toPage.style.display = "block";
mainBox.offsetHeight;
mainBox.style.width = `${mainBoxNewToBeWidth}px`;
let mainBoxNewWidth = mainBoxNewToBeWidth;
let mainBoxNewHeight = mainBox.offsetHeight;
let toBoxOldWidth = toPage.offsetWidth;
let toBoxOldHeight = toPage.offsetHeight;
console.log(toBoxOldWidth, toBoxOldHeight);
fromPage.style.display = "block";
toPage.style.display = "none";
mainBox.style.width = `${mainBoxOldWidth}px`;
mainBox.style.height = `${mainBoxOldHeight}px`;
mainBox.offsetHeight;
if (flag02) mainBox.style.transition = "0.8s linear(0.00, -0.130, 0.164, 0.450, 0.687, 0.861, 0.973, 1.04, 1.06, 1.07, 1.06, 1.04, 1.03, 1.02, 1.01, 1.00, 0.999, 0.997, 0.997, 0.997, 0.998, 0.998, 0.999, 0.999, 1.00)";
else mainBox.style.transition = "0.8s cubic-bezier(0.16, 1, 0.32, 1)";
mainBox.offsetHeight;
mainBox.style.width = `${mainBoxNewToBeWidth}px`;
mainBox.style.height = `${mainBoxNewHeight}px`;
fromPage.style.transform = `scaleX(1) scaleY(1)`;
fromPage.style.width = `${fromBoxOldWidth}px`;
fromPage.style.height = `${fromBoxOldHeight}px`;
if (flag02) fromPage.style.transition = "opacity 0.4s cubic-bezier(0.16, 1, 0.32, 1), filter 0.4s cubic-bezier(0.16, 1, 0.32, 1), transform 1.5s linear(0.00, -0.130, 0.164, 0.450, 0.687, 0.861, 0.973, 1.04, 1.06, 1.07, 1.06, 1.04, 1.03, 1.02, 1.01, 1.00, 0.999, 0.997, 0.997, 0.997, 0.998, 0.998, 0.999, 0.999, 1.00)";
else fromPage.style.transition = "opacity 0.4s cubic-bezier(0.16, 1, 0.32, 1), filter 0.4s cubic-bezier(0.16, 1, 0.32, 1), transform 0.8s cubic-bezier(0.16, 1, 0.32, 1)";
fromPage.offsetHeight;
fromPage.style.opacity = "0";
fromPage.style.filter = "blur(4px)";
fromPage.style.transform = `scaleX(${toBoxOldWidth / fromBoxOldWidth}) scaleY(${toBoxOldHeight / fromBoxOldHeight})`;
toPage.style.width = `${toBoxOldWidth}px`;
toPage.style.height = `${toBoxOldHeight}px`;
toPage.style.opacity = "0";
toPage.style.filter = "blur(4px)";
toPage.style.transform = `scaleX(${fromBoxOldWidth / toBoxOldWidth}) scaleY(${fromBoxOldHeight / toBoxOldHeight})`;
if (flag02) toPage.style.transition = "opacity 0.4s cubic-bezier(0.16, 1, 0.32, 1), filter 0.4s cubic-bezier(0.16, 1, 0.32, 1), transform 1.5s linear(0.00, -0.130, 0.164, 0.450, 0.687, 0.861, 0.973, 1.04, 1.06, 1.07, 1.06, 1.04, 1.03, 1.02, 1.01, 1.00, 0.999, 0.997, 0.997, 0.997, 0.998, 0.998, 0.999, 0.999, 1.00)";
else toPage.style.transition = "opacity 0.4s cubic-bezier(0.16, 1, 0.32, 1), filter 0.4s cubic-bezier(0.16, 1, 0.32, 1), transform 0.8s cubic-bezier(0.16, 1, 0.32, 1)";
toPage.offsetHeight;
toPage.style.transform = `scaleX(1) scaleY(1)`;
setTimeout(() => {
fromPage.style.display = "none";
fromPage.style.width = ``;
fromPage.style.height = ``;
fromPage.style.transform = ``;
toPage.style.display = "block";
toPage.offsetHeight;
toPage.style.opacity = "1";
toPage.style.filter = "blur(0px)";
setTimeout(() => {
toPage.style.opacity = "";
toPage.style.filter = "";
toPage.style.transition = "";
fromPage.style.transition = "";
toPage.style.opacity = "";
toPage.style.filter = "";
mainBox.style.height = "";
toPage.style.width = ``;
toPage.style.height = ``;
toPage.style.transform = ``;
lastPage = currentPage;
currentPage = toNumber;
isBusySwitchingPages = false;
}, 400);
}, 400);
}
Object.keys(pages).forEach(function (key) {
document.querySelectorAll(`#${key}`).forEach(element => {
element.addEventListener("click", function () {
if (isBusySwitchingPages || isGetButtonsBusy) return;
goToPage(pages[key][0], key);
});
});
});
document.getElementById('DLP_Hide_Button_1_ID').addEventListener("click", function () {
if (isBusySwitchingPages) return;
hidden = !hidden;
hide(hidden);
});
function hide(value) {
if (isBusySwitchingPages) return;
isBusySwitchingPages = true;
let button = document.querySelector(`#DLP_Hide_Button_1_ID`);
let main = document.querySelector(`.DLP_Main`);
let mainBox = document.querySelector(`.DLP_Main_Box`);
let mainBoxHeight = mainBox.offsetHeight;
main.style.transition = "0.8s cubic-bezier(0.16, 1, 0.32, 1)";
mainBox.style.transition = "0.8s cubic-bezier(0.16, 1, 0.32, 1)";
if (value) {
setButtonState(button, {button: 'linear-gradient(0deg, rgba(var(--DLP-blue), 0.10) 0%, rgba(var(--DLP-blue), 0.10) 100%), rgba(var(--color-snow), 0.80)', outline: 'rgba(var(--DLP-blue), 0.20)', text: 'rgb(var(--DLP-blue))', icon: 'rgb(var(--DLP-blue))'}, {text: systemText[systemLanguage][104], icon: ''}, {text: '', icon: ''});
main.style.bottom = `-${mainBoxHeight - 8}px`;
legacyButtonVisibility(false);
mainBox.style.filter = "blur(8px)";
mainBox.style.opacity = "0";
} else {
setButtonState(button, {button: 'rgb(var(--DLP-blue)', outline: 'rgba(0, 0, 0, 0.20)', text: '#FFF', icon: '#FFF'}, {text: systemText[systemLanguage][103], icon: ''}, {text: '', icon: ''});
main.style.bottom = "16px";
if (currentPage === 1 || currentPage === 3) legacyButtonVisibility(true);
mainBox.style.filter = "";
mainBox.style.opacity = "";
}
setTimeout(() => {
main.style.transition = "";
mainBox.style.transition = "";
isBusySwitchingPages = false;
}, 800);
}
document.querySelector(`.DLP_Main`).style.bottom = `-${document.querySelector(`.DLP_Main_Box`).offsetHeight - 8}px`;
document.querySelector(`.DLP_Main_Box`).style.opacity = "0";
document.querySelector(`.DLP_Main_Box`).style.filter = "blur(8px)";
document.querySelector(`#DLP_Switch_Legacy_Button_1_ID`).style.filter = "blur(8px)";
document.querySelector(`#DLP_Switch_Legacy_Button_1_ID`).style.opacity = "0";
document.querySelector(`#DLP_Switch_Legacy_Button_1_ID`).style.display = "none";
hide(false, false);
function legacyButtonVisibility(value) {
let legacyButton = document.querySelector(`#DLP_Switch_Legacy_Button_1_ID`);
legacyButton.style.transition = 'width 0.8s cubic-bezier(0.77,0,0.18,1), opacity 0.8s cubic-bezier(0.16, 1, 0.32, 1), filter 0.8s cubic-bezier(0.16, 1, 0.32, 1), transform 0.4s cubic-bezier(0.16, 1, 0.32, 1)';
if (value) {
legacyButton.style.display = "";
legacyButton.offsetWidth;
legacyButton.style.filter = "";
legacyButton.style.opacity = "";
} else {
legacyButton.style.filter = "blur(8px)";
legacyButton.style.opacity = "0";
setTimeout(() => {
legacyButton.style.display = "none";
}, 800);
}
}
function handleVisibility() {
if (document.querySelector('.MYehf') !== null || window.location.pathname.includes('/lesson') || window.location.pathname === '/practice') {
document.querySelector('.DLP_Main').style.display = 'none';
} else {
document.querySelector('.DLP_Main').style.display = '';
}
}
setInterval(handleVisibility, 200);
let isGetButtonsBusy = false;
function setButtonState(button, color, content, animation, callback) {
try {
let textElement = button.querySelector('.DLP_Inset_Text_1_ID');
let iconElement = button.querySelector('.DLP_Inset_Icon_1_ID');
let previousText = textElement.textContent;
let previousIcon = undefined;
if (iconElement.style.display !== 'none') {
previousIcon = {
icon: iconElement.textContent,
color: iconElement.style.color
};
}
textElement.textContent = content.text;
if (content.icon !== '') {
if (content.icon !== undefined) iconElement.textContent = content.icon;
} else {
iconElement.style.display = 'none';
}
let buttonNewWidth = button.offsetWidth;
textElement.textContent = previousText;
if (previousIcon !== undefined) {
iconElement.textContent = previousIcon.icon;
iconElement.style.color = previousIcon.color;
iconElement.style.display = '';
}
button.style.transition = 'width 0.8s cubic-bezier(0.77,0,0.18,1), background 0.8s cubic-bezier(0.16, 1, 0.32, 1), outline 0.8s cubic-bezier(0.16, 1, 0.32, 1), filter 0.4s cubic-bezier(0.16, 1, 0.32, 1), transform 0.4s cubic-bezier(0.16, 1, 0.32, 1)';
button.style.width = `${button.offsetWidth}px`;
requestAnimationFrame(() => {
textElement.style.transition = '0.4s';
if (previousIcon !== undefined) iconElement.style.transition = '0.4s';
textElement.style.filter = 'blur(4px)';
if (previousIcon !== undefined) iconElement.style.filter = 'blur(4px)';
textElement.style.opacity = '0';
if (previousIcon !== undefined) iconElement.style.opacity = '0';
button.style.width = `${buttonNewWidth}px`;
button.style.background = color.button;
button.style.outline = `solid 2px ${color.outline}`;
});
setTimeout(() => {
textElement.style.animation = '';
if (content.icon !== '') iconElement.style.animation = '';
textElement.style.transition = '0s';
if (content.icon !== '') iconElement.style.transition = '0s';
textElement.style.color = color.text;
if (content.icon !== '') iconElement.style.color = color.icon;
void textElement.offsetWidth;
textElement.style.transition = '0.4s';
if (content.icon !== '') iconElement.style.transition = '0.4s';
textElement.textContent = content.text;
if (content.icon !== '') {
if (content.icon !== undefined) iconElement.textContent = content.icon;
} else {
iconElement.style.display = 'none';
}
requestAnimationFrame(() => {
textElement.style.filter = 'blur(0px)';
if (content.icon !== '') iconElement.style.filter = 'blur(0px)';
textElement.style.opacity = '1';
if (content.icon !== '') iconElement.style.opacity = '1';
});
setTimeout(() => {
textElement.style.animation = animation.text;
if (content.icon !== '') iconElement.style.animation = animation.icon;
button.style.width = '';
}, 400);
if (callback) callback();
}, 400);
} catch (e) {
console.log('setButton error', e);
}
}
const tooltipObserver = new MutationObserver((mutationsList) => {
mutationsList.forEach(mutation => {
if (mutation.type === 'attributes' && mutation.attributeName === 'data-dlp-tooltip') {
tooltipCreate(mutation.target);
console.log('Attribute changed: registered');
} else if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE && node.hasAttribute('data-dlp-tooltip')) {
tooltipCreate(node);
console.log('New element with attribute: registered');
}
});
}
});
});
tooltipObserver.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['data-dlp-tooltip']
});
const tooltipData = new WeakMap(); // Store tooltip data associated with elements
function tooltipCreate(element) {
if (!flag01) return;
// Check if there's an existing tooltip for this element and hide it
if (tooltipData.has(element)) {
hideTooltip(element);
}
let timeoutId = null;
let currentTooltip = null; // Use a local variable here
const showTooltipForElement = (event) => { // Pass event to showTooltipForElement
timeoutId = setTimeout(() => {
currentTooltip = showTooltip(element, event); // Pass event to showTooltip
tooltipData.set(element, { tooltip: currentTooltip, timeoutId: timeoutId }); // Store data
}, 1000);
};
const hideTooltipForElement = () => {
clearTimeout(timeoutId);
hideTooltip(element);
};
const positionTooltipForElement = (event) => { // Pass event to positionTooltipForElement
if(!currentTooltip) return; // Use the local currentTooltip
positionTooltip(currentTooltip, event); // Pass tooltip and event to positionTooltip
};
element.addEventListener('mouseenter', showTooltipForElement);
element.addEventListener('mouseleave', hideTooltipForElement);
element.addEventListener('mousemove', positionTooltipForElement);
// Store the listeners so we can remove them later if needed (though not explicitly required by the prompt, good practice)
tooltipData.set(element, {
timeoutId: null,
tooltip: null,
listeners: {
mouseenter: showTooltipForElement,
mouseleave: hideTooltipForElement,
mousemove: positionTooltipForElement
}
});
console.log('Tooltip listeners attached to element');
// Immediately show tooltip if mouse is already over and attribute is just added/changed
if (element.matches(':hover')) {
// Simulate mousemove event to position tooltip correctly on initial hover if attribute is added dynamically
const mockEvent = new MouseEvent('mousemove', {
clientX: element.getBoundingClientRect().left, // Or any reasonable default cursor position
clientY: element.getBoundingClientRect().top
});
showTooltipForElement(mockEvent);
}
};
function showTooltip(element, event) { // Accept event in showTooltip
const tooltipText = element.dataset.dlpTooltip;
let tooltip = document.createElement('div'); // Create a new tooltip each time
tooltip.classList.add('DLP_Tooltip');
document.body.appendChild(tooltip);
tooltip.textContent = tooltipText;
tooltip.offsetHeight; // Trigger reflow for transition
tooltip.classList.add('DLP_Tooltip_Visible');
positionTooltip(tooltip, event); // Pass tooltip and event to positionTooltip
console.log('created tooltip');
return tooltip; // Return the created tooltip
}
function positionTooltip(tooltip, event){ // Accept tooltip and event in positionTooltip
if (!tooltip || !event) return; // Exit if tooltip or event is null
const tooltipRect = tooltip.getBoundingClientRect();
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
const cursorX = event.clientX;
const cursorY = event.clientY;
const tooltipWidth = tooltipRect.width;
const tooltipHeight = tooltipRect.height;
const offsetX = 10; // Horizontal offset from cursor
const offsetY = 10; // Vertical offset from cursor
let preferredPosition = 'bottom-right'; // Default position
let tooltipLeft, tooltipTop, tooltipBottom, tooltipRight;
// Check bottom-right position
tooltipLeft = cursorX + offsetX;
tooltipTop = cursorY + offsetY;
if (tooltipLeft + tooltipWidth <= viewportWidth && tooltipTop + tooltipHeight <= viewportHeight) {
preferredPosition = 'bottom-right';
} else if (cursorX - offsetX - tooltipWidth >= 0 && tooltipTop + tooltipHeight <= viewportHeight) { // Check bottom-left
tooltipLeft = cursorX - offsetX - tooltipWidth;
tooltipTop = cursorY + offsetY;
preferredPosition = 'bottom-left';
} else if (tooltipLeft + tooltipWidth <= viewportWidth && cursorY - offsetY - tooltipHeight >= 0) { // Check top-right
tooltipLeft = cursorX + offsetX;
tooltipTop = cursorY - offsetY - tooltipHeight;
preferredPosition = 'top-right';
} else if (cursorX - offsetX - tooltipWidth >= 0 && cursorY - offsetY - tooltipHeight >= 0) { // Check top-left
tooltipLeft = cursorX - offsetX - tooltipWidth;
tooltipTop = cursorY - offsetY - tooltipHeight;
preferredPosition = 'top-left';
} else { // Fallback to bottom-right if none fit (might go off-screen)
tooltipLeft = cursorX + offsetX;
tooltipTop = cursorY + offsetY;
preferredPosition = 'bottom-right';
}
tooltip.style.left = tooltipLeft + 'px';
tooltip.style.top = tooltipTop + 'px';
tooltip.style.bottom = 'auto'; // Ensure bottom is not overriding top
tooltip.style.right = 'auto'; // Ensure right is not overriding left
}
function hideTooltip(element) {
if (!tooltipData.has(element)) return; // Exit if no tooltip data for this element
const data = tooltipData.get(element);
const tooltip = data.tooltip;
if (tooltip) {
tooltip.classList.remove('DLP_Tooltip_Visible');
setTimeout(() => {
if (tooltip && tooltip.parentNode) {
tooltip.parentNode.removeChild(tooltip);
}
tooltipData.delete(element); // Clear tooltip data when hidden
console.log('tooltip removed');
}, 500);
} else {
tooltipData.delete(element); // Clear data even if no tooltip element (to avoid memory leak)
}
}
const DLP_Get_PATH_1_ID = document.getElementById("DLP_Get_PATH_1_ID");
const DLP_Get_PATH_2_ID = document.getElementById("DLP_Get_PATH_2_ID");
const DLP_Get_PRACTICE_1_ID = document.getElementById("DLP_Get_PRACTICE_1_ID");
const DLP_Get_PRACTICE_2_ID = document.getElementById("DLP_Get_PRACTICE_2_ID");
const DLP_Get_LISTEN_1_ID = document.getElementById("DLP_Get_LISTEN_1_ID");
const DLP_Get_LISTEN_2_ID = document.getElementById("DLP_Get_LISTEN_2_ID");
const DLP_Get_LESSON_1_ID = document.getElementById("DLP_Get_LESSON_1_ID");
const DLP_Get_LESSON_2_ID = document.getElementById("DLP_Get_LESSON_2_ID");
function inputCheck2() {
const ids = {
"DLP_Get_PATH_1_ID": ["path"],
"DLP_Get_PATH_2_ID": ["path"],
"DLP_Get_PRACTICE_1_ID": ["practice"],
"DLP_Get_PRACTICE_2_ID": ["practice"],
"DLP_Get_LISTEN_1_ID": ["listen"],
"DLP_Get_LISTEN_2_ID": ["listen"],
"DLP_Get_LESSON_1_ID": ["lesson"],
"DLP_Get_LESSON_2_ID": ["lesson"]
};
Object.keys(ids).forEach(id => {
const element = document.getElementById(id);
if (!element) return;
const input = element.querySelector('#DLP_Inset_Input_1_ID');
const button = element.querySelector('#DLP_Inset_Button_1_ID');
if (!input || !button) return;
function updateButtonState() {
const isEmpty = input.value.length === 0;
button.style.opacity = isEmpty ? '0.5' : '';
button.style.pointerEvents = isEmpty ? 'none' : '';
};
const category = ids[id][0];
input.addEventListener("input", function () {
this.value = this.value.replace(/[^0-9]/g, "");
if (this.value.length === 1 && this.value[0] === '0') this.value = this.value.slice(1);
if (this.value.length > 6) this.value = this.value.slice(0, 6);
updateButtonState();
//if (!storageSession.legacy[category]) storageSession.legacy[category] = [];
storageSession.legacy[category].amount = Number(this.value);
saveStorageSession();
});
if (['DLP_Get_LESSON_1_ID', 'DLP_Get_LESSON_2_ID'].includes(id)) {
const input3 = element.querySelector('#DLP_Inset_Input_3_ID');
const input4 = element.querySelector('#DLP_Inset_Input_4_ID');
input3.addEventListener("input", function () {
this.value = this.value.replace(/[^0-9]/g, "");
if (this.value.length === 1 && this.value[0] === '0') this.value = this.value.slice(1);
if (this.value.length > 2) this.value = this.value.slice(0, 2);
//if (!storageSession.legacy[category]) storageSession.legacy[category] = [];
storageSession.legacy[category].unit = Number(this.value);
saveStorageSession();
});
input3.addEventListener("blur", function () {
if (this.value.trim() === "") {
this.value = "1";
storageSession.legacy[category].unit = 1;
saveStorageSession();
}
});
input4.addEventListener("input", function () {
this.value = this.value.replace(/[^0-9]/g, "");
if (this.value.length === 1 && this.value[0] === '0') this.value = this.value.slice(1);
if (this.value.length > 2) this.value = this.value.slice(0, 2);
//if (!storageSession.legacy[category]) storageSession.legacy[category] = [];
storageSession.legacy[category].level = Number(this.value);
saveStorageSession();
});
input4.addEventListener("blur", function () {
if (this.value.trim() === "") {
this.value = "1";
storageSession.legacy[category].level = 1;
saveStorageSession();
}
});
}
if (storageSession.legacy[category].amount !== 0) input.value = storageSession.legacy[category].amount; updateButtonState();
});
Object.keys(ids).forEach(id => {
const element = document.getElementById(id);
if (!element) return;
const input = element.querySelector('#DLP_Inset_Input_1_ID');
const button = element.querySelector('#DLP_Inset_Button_1_ID');
if (!input || !button) return;
function updateButtonState() {
const isEmpty = input.value.length === 0;
button.style.opacity = isEmpty ? '0.5' : '';
button.style.pointerEvents = isEmpty ? 'none' : '';
};
const category = ids[id][0];
input.addEventListener("input", function () {
this.value = this.value.replace(/[^0-9]/g, "");
if (this.value.length === 1 && this.value[0] === '0') this.value = this.value.slice(1);
if (this.value.length > 6) this.value = this.value.slice(0, 6);
updateButtonState();
if (!storageSession.legacy[category]) storageSession.legacy[category] = [];
storageSession.legacy[category].amount = Number(this.value);
saveStorageSession();
});
if (storageSession.legacy[category].amount !== 0) input.value = storageSession.legacy[category].amount; updateButtonState();
});
function updatePinnedItems() {
const pinnedIds = storageLocal.pins.legacy || [];
for (const id in ids) {
if (id.endsWith("1_ID")) {
const element = document.getElementById(id);
if (element) {
if (pinnedIds.includes(id)) {
element.style.display = 'flex';
} else {
element.style.display = 'none';
}
}
}
}
};
updatePinnedItems();
Object.keys(ids).forEach(id => {
if (id.endsWith("2_ID")) {
const pinIcon = document.querySelector(`#${id} > .DLP_HStack_8 > .DLP_Inset_Icon_1_ID`);
const modifiedId = id.replace("2_ID", "1_ID");
function updatePinViews() {
if (storageLocal.pins.legacy.includes(modifiedId)) {
pinIcon.textContent = "";
pinIcon.style.color = "rgb(var(--DLP-blue))";
} else {
pinIcon.textContent = "";
pinIcon.style.color = "rgba(var(--color-eel), 0.50)";
}
}
updatePinViews();
function updatePins(isAdding) {
const index = storageLocal.pins.legacy.indexOf(modifiedId);
if (isAdding && index === -1) {
if (storageLocal.pins.legacy.length > Math.floor(((window.innerHeight) / 200) - 1)) {
showNotification("warning", "Pin Limit Reached", "You've pinned too many functions. Please unpin one to continue.", 15);
} else {
storageLocal.pins.legacy.push(modifiedId);
}
} else if (!isAdding && index !== -1) {
storageLocal.pins.legacy.splice(index, 1);
} else {
console.log("Something unexpected happened: djr9234.");
}
updatePinViews();
saveStorageLocal();
updatePinnedItems();
}
pinIcon.addEventListener('click', () => {
updatePins(!storageLocal.pins.legacy.includes(modifiedId));
});
}
});
}
inputCheck2();
function setupButton1Events(baseId, page, type) {
const button1 = document.querySelector(`#${baseId}_ID`).querySelector('#DLP_Inset_Button_1_ID');
const input1 = document.querySelector(`#${baseId}_ID`).querySelector('#DLP_Inset_Input_1_ID');
function clickHandler() {
if (isGetButtonsBusy) return;
isGetButtonsBusy = true;
const buttonElement = document.querySelector(`#${baseId}_ID`).querySelector('#DLP_Inset_Button_1_ID');
if (!storageSession.legacy.status && storageSession.legacy[type].amount > 0) {
setButtonState(buttonElement, {button: 'linear-gradient(0deg, rgba(var(--DLP-blue), 0.10) 0%, rgba(var(--DLP-blue), 0.10) 100%), rgba(var(--color-snow), 0.80)', outline: 'rgba(var(--DLP-blue), 0.20)', text: 'rgb(var(--DLP-blue))', icon: 'rgb(var(--DLP-blue))'}, {text: systemText[systemLanguage][107], icon: ''}, {text: '', icon: ''});
storageSession.legacy.page = page;
storageSession.legacy.status = type;
saveStorageSession();
} else if (storageSession.legacy.status === type) {
setButtonState(buttonElement, {button: 'rgb(var(--DLP-blue))', outline: 'rgba(0, 0, 0, 0.20)', text: '#FFF', icon: '#FFF'}, {text: systemText[systemLanguage][18], icon: ''}, {text: '', icon: ''});
storageSession.legacy.status = false;
saveStorageSession();
}
setTimeout(() => {
isGetButtonsBusy = false;
}, 800);
};
button1.addEventListener('click', clickHandler);
input1.onkeyup = function (event) {
if (event.keyCode === 13) {
if (isGetButtonsBusy) return;
isGetButtonsBusy = true;
const buttonElement = document.querySelector(`#${baseId}_ID`).querySelector('#DLP_Inset_Button_1_ID');
if (!storageSession.legacy.status && storageSession.legacy[type].amount > 0) {
setButtonState(buttonElement, {button: 'linear-gradient(0deg, rgba(var(--DLP-blue), 0.10) 0%, rgba(var(--DLP-blue), 0.10) 100%), rgba(var(--color-snow), 0.80)', outline: 'rgba(var(--DLP-blue), 0.20)', text: 'rgb(var(--DLP-blue))', icon: 'rgb(var(--DLP-blue))'}, {text: systemText[systemLanguage][107], icon: ''}, {text: '', icon: ''});
storageSession.legacy.page = page;
storageSession.legacy.status = type;
saveStorageSession();
}
setTimeout(() => {
isGetButtonsBusy = false;
}, 800);
}
};
}
function setupButton2Events(baseId, type) {
const button2 = document.querySelector(`#${baseId}_ID`).querySelector('#DLP_Inset_Button_2_ID');
function clickHandler() {
const icon = button2.querySelector('.DLP_Inset_Icon_1_ID');
const input = button2.parentElement.querySelector('#DLP_Inset_Input_1_ID');
const button1 = button2.parentElement.querySelector('#DLP_Inset_Button_1_ID');
function animateElement(element, visibility, duration = 400) {
if (visibility) {
element.style.display = 'block';
element.style.filter = 'blur(4px)';
element.style.opacity = '0';
element.style.transition = '0.4s';
requestAnimationFrame(() => {
element.style.filter = 'blur(0px)';
element.style.opacity = '1';
});
setTimeout(() => {
element.style.filter = '';
element.style.opacity = '';
element.style.transition = '';
}, duration);
} else {
element.style.display = 'block';
element.style.filter = 'blur(0px)';
element.style.opacity = '1';
element.style.transition = '0.4s';
requestAnimationFrame(() => {
element.style.filter = 'blur(4px)';
element.style.opacity = '0';
});
setTimeout(() => {
element.style.display = 'none';
element.style.filter = '';
element.style.opacity = '';
element.style.transition = '';
}, duration);
}
}
function syncGetButtonState(mode) {
if (!button1) return;
if (mode === 'infinity') {
button1.style.opacity = '';
button1.style.pointerEvents = '';
} else {
const isEmpty = input && input.value.length === 0;
button1.style.opacity = isEmpty ? '0.5' : '';
button1.style.pointerEvents = isEmpty ? 'none' : '';
}
}
if (storageSession.legacy[type].type === 'lesson') {
let inputTo;
button2.setAttribute("data-dlp-tooltip", "Lesson Mode");
if (input.style.display === 'none') inputTo = 'show';
syncGetButtonState('lesson');
animateElement(icon, false);
setTimeout(() => {
icon.textContent = '';
animateElement(icon, true);
}, 400);
if (inputTo === 'show') setTimeout(() => animateElement(input, true), 400);
} else if (storageSession.legacy[type].type === 'xp') {
let inputTo;
button2.setAttribute("data-dlp-tooltip", "XP Mode");
if (input.style.display === 'none') inputTo = 'show';
syncGetButtonState('xp');
animateElement(icon, false);
setTimeout(() => {
icon.textContent = 'XP';
animateElement(icon, true);
}, 400);
if (inputTo === 'show') setTimeout(() => animateElement(input, true), 400);
} else if (storageSession.legacy[type].type === 'infinity') {
let inputTo;
button2.setAttribute("data-dlp-tooltip", "Infinity Mode");
if (input.style.display !== 'none') inputTo = 'hide';
syncGetButtonState('infinity');
animateElement(icon, false);
setTimeout(() => {
icon.textContent = '';
animateElement(icon, true);
}, 400);
if (inputTo === 'hide') animateElement(input, false);
}
};
clickHandler();
button2.addEventListener('click', () => {
if (isGetButtonsBusy) return;
isGetButtonsBusy = true;
if (storageSession.legacy[type].type === 'lesson') {
storageSession.legacy[type].type = 'xp';
saveStorageSession();
} else if (storageSession.legacy[type].type === 'xp') {
storageSession.legacy[type].type = 'infinity';
saveStorageSession();
} else if (storageSession.legacy[type].type === 'infinity') {
storageSession.legacy[type].type = 'lesson';
saveStorageSession();
}
clickHandler();
setTimeout(() => {
isGetButtonsBusy = false;
}, 800);
});
}
for (const type of ['PATH', 'PRACTICE', 'LISTEN', 'LESSON']) {
for (let i = 1; i <= 2; i++) {
const baseId = `DLP_Get_${type}_${i}`;
setupButton1Events(baseId, i, type.toLowerCase());
setupButton2Events(baseId, type.toLowerCase());
}
}
if (storageSession.legacy.status === 'path' && storageSession.legacy.path.amount > 0) {
if (storageSession.legacy.page === 1) {
setButtonState(DLP_Get_PATH_1_ID.querySelector('#DLP_Inset_Button_1_ID'), {button: 'linear-gradient(0deg, rgba(var(--DLP-blue), 0.10) 0%, rgba(var(--DLP-blue), 0.10) 100%), rgba(var(--color-snow), 0.80)', outline: 'rgba(var(--DLP-blue), 0.20)', text: 'rgb(var(--DLP-blue))', icon: 'rgb(var(--DLP-blue))'}, {text: systemText[systemLanguage][107], icon: ''}, {text: '', icon: ''});
} else if (storageSession.legacy.page === 2) {
setButtonState(DLP_Get_PATH_2_ID.querySelector('#DLP_Inset_Button_1_ID'), {button: 'linear-gradient(0deg, rgba(var(--DLP-blue), 0.10) 0%, rgba(var(--DLP-blue), 0.10) 100%), rgba(var(--color-snow), 0.80)', outline: 'rgba(var(--DLP-blue), 0.20)', text: 'rgb(var(--DLP-blue))', icon: 'rgb(var(--DLP-blue))'}, {text: systemText[systemLanguage][107], icon: ''}, {text: '', icon: ''});
}
} else if (storageSession.legacy.status === 'practice' && storageSession.legacy.practice.amount > 0) {
if (storageSession.legacy.page === 1) {
setButtonState(DLP_Get_PRACTICE_1_ID.querySelector('#DLP_Inset_Button_1_ID'), {button: 'linear-gradient(0deg, rgba(var(--DLP-blue), 0.10) 0%, rgba(var(--DLP-blue), 0.10) 100%), rgba(var(--color-snow), 0.80)', outline: 'rgba(var(--DLP-blue), 0.20)', text: 'rgb(var(--DLP-blue))', icon: 'rgb(var(--DLP-blue))'}, {text: systemText[systemLanguage][107], icon: ''}, {text: '', icon: ''});
} else if (storageSession.legacy.page === 2) {
setButtonState(DLP_Get_PRACTICE_2_ID.querySelector('#DLP_Inset_Button_1_ID'), {button: 'linear-gradient(0deg, rgba(var(--DLP-blue), 0.10) 0%, rgba(var(--DLP-blue), 0.10) 100%), rgba(var(--color-snow), 0.80)', outline: 'rgba(var(--DLP-blue), 0.20)', text: 'rgb(var(--DLP-blue))', icon: 'rgb(var(--DLP-blue))'}, {text: systemText[systemLanguage][107], icon: ''}, {text: '', icon: ''});
}
} else if (storageSession.legacy.status === 'listen' && storageSession.legacy.listen.amount > 0) {
if (storageSession.legacy.page === 1) {
setButtonState(DLP_Get_LISTEN_1_ID.querySelector('#DLP_Inset_Button_1_ID'), {button: 'linear-gradient(0deg, rgba(var(--DLP-blue), 0.10) 0%, rgba(var(--DLP-blue), 0.10) 100%), rgba(var(--color-snow), 0.80)', outline: 'rgba(var(--DLP-blue), 0.20)', text: 'rgb(var(--DLP-blue))', icon: 'rgb(var(--DLP-blue))'}, {text: systemText[systemLanguage][107], icon: ''}, {text: '', icon: ''});
} else if (storageSession.legacy.page === 2) {
setButtonState(DLP_Get_LISTEN_2_ID.querySelector('#DLP_Inset_Button_1_ID'), {button: 'linear-gradient(0deg, rgba(var(--DLP-blue), 0.10) 0%, rgba(var(--DLP-blue), 0.10) 100%), rgba(var(--color-snow), 0.80)', outline: 'rgba(var(--DLP-blue), 0.20)', text: 'rgb(var(--DLP-blue))', icon: 'rgb(var(--DLP-blue))'}, {text: systemText[systemLanguage][107], icon: ''}, {text: '', icon: ''});
}
} else if (storageSession.legacy.status === 'lesson' && storageSession.legacy.lesson.amount > 0) {
if (storageSession.legacy.page === 1) {
setButtonState(DLP_Get_LESSON_1_ID.querySelector('#DLP_Inset_Button_1_ID'), {button: 'linear-gradient(0deg, rgba(var(--DLP-blue), 0.10) 0%, rgba(var(--DLP-blue), 0.10) 100%), rgba(var(--color-snow), 0.80)', outline: 'rgba(var(--DLP-blue), 0.20)', text: 'rgb(var(--DLP-blue))', icon: 'rgb(var(--DLP-blue))'}, {text: systemText[systemLanguage][107], icon: ''}, {text: '', icon: ''});
} else if (storageSession.legacy.page === 2) {
setButtonState(DLP_Get_LESSON_2_ID.querySelector('#DLP_Inset_Button_1_ID'), {button: 'linear-gradient(0deg, rgba(var(--DLP-blue), 0.10) 0%, rgba(var(--DLP-blue), 0.10) 100%), rgba(var(--color-snow), 0.80)', outline: 'rgba(var(--DLP-blue), 0.20)', text: 'rgb(var(--DLP-blue))', icon: 'rgb(var(--DLP-blue))'}, {text: systemText[systemLanguage][107], icon: ''}, {text: '', icon: ''});
}
}
let pageSwitching = false;
function process1() {
if (window.location.href.includes('/lesson') || window.location.href.includes('/practice') || window.location.href.includes('/practice-hub/listening-practice')) return;
if (storageSession.legacy.status && storageSession.legacy[storageSession.legacy.status].amount > 0) {
if (pageSwitching) return;
pageSwitching = true;
setTimeout(() => {
checkChest();
}, 2000);
} else {
pageSwitching = false;
}
}
setInterval(process1, 500);
function process2() {
if (storageSession.legacy.status && storageSession.legacy[storageSession.legacy.status].amount > 0) {
if (storageSession.legacy.status === 'path') {
window.location.href = "https://duolingo.com/lesson";
} else if (storageSession.legacy.status === 'practice') {
window.location.href = "https://duolingo.com/practice";
} else if (storageSession.legacy.status === 'listen') {
window.location.href = "https://duolingo.com/practice-hub/listening-practice";
} else if (storageSession.legacy.status === 'lesson') {
//storageSession.legacy[storageSession.legacy.status].section
window.location.href = `https://duolingo.com/lesson/unit/${storageSession.legacy[storageSession.legacy.status].unit}/level/${storageSession.legacy[storageSession.legacy.status].level}`;
}
} else {
pageSwitching = false;
}
}
let checkChestCount = 0;
function checkChest() {
try {
if (document.readyState === 'complete') {
const imageUrl = 'https://d35aaqx5ub95lt.cloudfront.net/images/path/09f977a3e299d1418fde0fd053de0beb.svg';
const images = document.querySelectorAll('.TI9Is');
if (!images.length) {
setTimeout(function () {
process2();
}, 2000);
} else {
let imagesProcessed = 0;
let chestFound = false;
images.forEach(image => {
if (image.src === imageUrl) {
image.click();
chestFound = true;
setTimeout(function () {
process2();
}, 2000);
}
imagesProcessed++;
if (imagesProcessed >= images.length && !chestFound) {
process2();
}
});
}
} else {
setTimeout(function () {
checkChestCount++;
checkChest();
}, 100);
}
} catch (error) {
setTimeout(function () {
process2();
}, 2000);
}
};
if (storageSession.legacy.page === 1) {
document.querySelector(`#DLP_Main_Box_Divider_${currentPage}_ID`).style.display = 'none';
document.querySelector(`#DLP_Main_Box_Divider_3_ID`).style.display = 'block';
currentPage = 3;
let button = document.querySelector('#DLP_Switch_Legacy_Button_1_ID');
setButtonState(button, {button: 'linear-gradient(0deg, rgba(var(--DLP-blue), 0.10) 0%, rgba(var(--DLP-blue), 0.10) 100%), rgba(var(--color-snow), 0.80)', outline: 'rgba(var(--DLP-blue), 0.20)', text: 'rgb(var(--DLP-blue))', icon: 'rgb(var(--DLP-blue))'}, {text: systemText[systemLanguage][105], icon: ''}, {text: '', icon: ''});
} else if (storageSession.legacy.page === 2) {
document.querySelector(`#DLP_Main_Box_Divider_${currentPage}_ID`).style.display = 'none';
document.querySelector(`#DLP_Main_Box_Divider_4_ID`).style.display = 'block';
lastPage = 3;
currentPage = 4;
let button = document.querySelector('#DLP_Switch_Legacy_Button_1_ID');
setButtonState(button, {button: 'linear-gradient(0deg, rgba(var(--DLP-blue), 0.10) 0%, rgba(var(--DLP-blue), 0.10) 100%), rgba(var(--color-snow), 0.80)', outline: 'rgba(var(--DLP-blue), 0.20)', text: 'rgb(var(--DLP-blue))', icon: 'rgb(var(--DLP-blue))'}, {text: systemText[systemLanguage][105], icon: ''}, {text: '', icon: ''});
}
if (storageLocal.pins.home.includes("DLP_Get_XP_1_ID")) {
document.querySelector("#DLP_Get_Heart_Refill_2_ID > .DLP_HStack_8 > #DLP_Inset_Icon_1_ID");
}
function inputCheck1() {
const ids = {
"DLP_Get_XP_1_ID": ["xp"],
"DLP_Get_XP_2_ID": ["xp"],
"DLP_Get_GEM_1_ID": ["gem"],
"DLP_Get_GEM_2_ID": ["gem"],
"DLP_Get_SUPER_1_ID": ["super"],
"DLP_Get_SUPER_2_ID": ["super"],
"DLP_Get_DOUBLE_XP_BOOST_1_ID": ["double_xp_boost"],
"DLP_Get_DOUBLE_XP_BOOST_2_ID": ["double_xp_boost"],
"DLP_Get_Streak_Freeze_1_ID": ["streak_freeze"],
"DLP_Get_Streak_Freeze_2_ID": ["streak_freeze"],
"DLP_Get_Heart_Refill_1_ID": ["heart_refill"],
"DLP_Get_Heart_Refill_2_ID": ["heart_refill"],
"DLP_Get_Streak_1_ID": ["streak"],
"DLP_Get_Streak_2_ID": ["streak"]
};
Object.keys(ids).forEach(id => {
const element = document.getElementById(id);
if (!element) return;
const input = element.querySelector('#DLP_Inset_Input_1_ID');
const button = element.querySelector('#DLP_Inset_Button_1_ID');
if (!input || !button) return;
function updateButtonState() {
const isEmpty = input.value.length === 0;
button.style.opacity = isEmpty ? '0.5' : '';
button.style.pointerEvents = isEmpty ? 'none' : '';
};
const category = ids[id][0];
input.addEventListener("input", function () {
this.value = this.value.replace(/[^0-9]/g, "");
if (this.value.length === 1 && this.value[0] === '0') this.value = this.value.slice(1);
if (this.value.length > 9) this.value = this.value.slice(0, 9);
updateButtonState();
});
if (!input.value) updateButtonState();
});
function updatePinnedItems() {
const pinnedIds = storageLocal.pins.home || [];
for (const id in ids) {
if (id.endsWith("1_ID")) {
const element = document.getElementById(id);
if (element) {
if (pinnedIds.includes(id)) {
element.style.display = 'flex';
} else {
element.style.display = 'none';
}
}
}
}
};
updatePinnedItems();
Object.keys(ids).forEach(id => {
if (id.endsWith("2_ID")) {
const pinIcon = document.querySelector(`#${id} > .DLP_HStack_8 > .DLP_Inset_Icon_1_ID`);
const modifiedId = id.replace("2_ID", "1_ID");
function updatePinViews() {
if (storageLocal.pins.home.includes(modifiedId)) {
pinIcon.textContent = "";
pinIcon.style.color = "rgb(var(--DLP-blue))";
} else {
pinIcon.textContent = "";
pinIcon.style.color = "rgba(var(--color-eel), 0.50)";
}
}
updatePinViews();
function updatePins(isAdding) {
const index = storageLocal.pins.home.indexOf(modifiedId);
if (isAdding && index === -1) {
if (storageLocal.pins.home.length > Math.floor(((window.innerHeight) / 200) - 1)) {
showNotification("warning", "Pin Limit Reached", "You've pinned too many functions. Please unpin one to continue.", 15);
} else {
storageLocal.pins.home.push(modifiedId);
}
} else if (!isAdding && index !== -1) {
storageLocal.pins.home.splice(index, 1);
} else {
console.log("Something unexpected happened: djr9234.");
}
updatePinViews();
saveStorageLocal();
updatePinnedItems();
}
pinIcon.addEventListener('click', () => {
updatePins(!storageLocal.pins.home.includes(modifiedId));
});
}
});
}
inputCheck1();
function initializeMagneticHover(element) {
let mouseDown = false;
let originalZIndex = null;
element.addEventListener('pointermove', (e) => {
const rect = element.getBoundingClientRect();
const x = e.clientX - rect.left - rect.width / 2;
const y = e.clientY - rect.top - rect.height / 2;
if (mouseDown) {
element.style.transform = `translate(${x * 0.1}px, ${y * 0.1}px) scale(0.9)`;
} else {
element.style.transform = `translate(${x * 0.1}px, ${y * 0.1}px) scale(1.1)`;
}
if (!originalZIndex) {
if (element.style.zIndex) originalZIndex = parseInt(element.style.zIndex);
else originalZIndex = 0;
}
element.style.zIndex = originalZIndex + 1;
});
element.addEventListener('pointerleave', () => {
element.style.transform = 'translate(0, 0) scale(1)';
element.style.zIndex = originalZIndex;
mouseDown = false;
});
element.addEventListener('pointerdown', (e) => {
mouseDown = true;
const rect = element.getBoundingClientRect();
const x = e.clientX - rect.left - rect.width / 2;
const y = e.clientY - rect.top - rect.height / 2;
if (mouseDown) {
element.style.transform = `translate(${x * 0.1}px, ${y * 0.1}px) scale(0.9)`;
} else {
element.style.transform = `translate(${x * 0.1}px, ${y * 0.1}px) scale(1.1)`;
}
});
element.addEventListener('pointerup', (e) => {
mouseDown = false;
const rect = element.getBoundingClientRect();
const x = e.clientX - rect.left - rect.width / 2;
const y = e.clientY - rect.top - rect.height / 2;
const isPointerWithinElement = e.clientX >= rect.left && e.clientX <= rect.right && e.clientY >= rect.top && e.clientY <= rect.bottom;
if (isPointerWithinElement) {
playHaptic();
}
if (mouseDown) {
element.style.transform = `translate(${x * 0.1}px, ${y * 0.1}px) scale(0.9)`;
} else {
element.style.transform = `translate(${x * 0.1}px, ${y * 0.1}px) scale(1.1)`;
}
});
}
document.querySelectorAll('.DLP_Magnetic_Hover_1').forEach(element => {
initializeMagneticHover(element);
});
function initializeDefaultHover(element) {
element.addEventListener('pointerup', (e) => {
const rect = element.getBoundingClientRect();
const isPointerWithinElement = e.clientX >= rect.left && e.clientX <= rect.right && e.clientY >= rect.top && e.clientY <= rect.bottom;
if (isPointerWithinElement) {
playHaptic();
}
});
}
document.querySelectorAll('.DLP_Hover_1').forEach(element => {
initializeDefaultHover(element);
});
let DLP_Server_Connection_Button = document.getElementById("DLP_Main_1_Server_Connection_Button_1_ID");
let DLP_Server_Connection_Button_2 = document.getElementById("DLP_Secondary_1_Server_Connection_Button_1_ID");
DLP_Server_Connection_Button.addEventListener('click', () => {
if (DLP_Server_Connection_Button.getAttribute("data-dlp-connection-status") === "outdated") {
window.open("https://duolingopro.net/update/userscript", "_blank");
} else if (DLP_Server_Connection_Button.getAttribute("data-dlp-connection-status") === "error") {
window.open("https://status.duolingopro.net", "_blank");
}
});
function updateConnetionButtonStyles(button, color, content, animation) {
let iconToChange = button.querySelector(".DLP_Inset_Icon_1_ID");
let textToChange = button.querySelector(".DLP_Inset_Text_1_ID");
textToChange.style.animation = '';
iconToChange.style.animation = '';
void button.offsetWidth;
requestAnimationFrame(() => {
textToChange.style.filter = 'blur(4px)';
textToChange.style.opacity = '0';
iconToChange.style.filter = 'blur(4px)';
iconToChange.style.opacity = '0';
button.style.background = color.button;
button.style.outline = `2px solid ${color.outline}`;
});
setTimeout(() => {
textToChange.style.animation = 'none';
iconToChange.style.animation = 'none';
requestAnimationFrame(() => {
textToChange.style.transition = '0s';
iconToChange.style.transition = '0s';
textToChange.textContent = content.text;
iconToChange.textContent = content.icon;
textToChange.style.color = color.text;
iconToChange.style.color = color.icon;
void button.offsetWidth;
textToChange.style.transition = '0.4s';
iconToChange.style.transition = '0.4s';
void button.offsetWidth;
textToChange.style.filter = 'blur(0px)';
iconToChange.style.filter = 'blur(0px)';
textToChange.style.opacity = '1';
iconToChange.style.opacity = '1';
setTimeout(() => {
textToChange.style.animation = animation.text;
iconToChange.style.animation = animation.icon;
}, 400);
});
}, 400);
}
let serverConnectedBefore = 'no';
let serverConnectedBeforeNotification;
let newTermID;
let chatMemory = [];
let chatTempSendList = [];
const pendingTempMessages = new Map();
let chatMemoryFingerprints = [];
let chatMessageLookup = new Map();
function normalizeMessageValue(value) {
if (Array.isArray(value)) {
return value.map(normalizeMessageValue);
}
if (value && typeof value === 'object') {
const sortedKeys = Object.keys(value).sort();
const normalizedObject = {};
sortedKeys.forEach(key => {
normalizedObject[key] = normalizeMessageValue(value[key]);
});
return normalizedObject;
}
if (value === undefined || Number.isNaN(value)) {
return null;
}
return value;
}
function computeMessageFingerprint(message) {
const relevantData = {
accent: message?.accent ?? '',
author: message?.author ?? '',
deleted: message?.deleted ?? false,
edited: message?.edited ?? false,
files: Array.isArray(message?.files) ? message.files.slice() : [],
message_id: message?.message_id ?? null,
message: message?.message ?? '',
profile_picture: message?.profile_picture ?? '',
role: message?.role ?? '',
send_time: message?.send_time ?? null,
status: message?.status ?? '',
reply_to: message?.reply_to ?? null
};
try {
return JSON.stringify(normalizeMessageValue(relevantData));
} catch (error) {
console.error('Failed to compute message fingerprint', error);
return JSON.stringify({
message_id: message?.message_id ?? null,
send_time: message?.send_time ?? null
});
}
}
function resolveMessageKey(msg) {
if (!msg || typeof msg !== 'object') return null;
if (msg?.message_id !== undefined && msg?.message_id !== null) {
return String(msg.message_id);
}
if (msg?.send_time !== undefined && msg?.send_time !== null) {
return String(msg.send_time);
}
return null;
}
function areArraysEqual(arrayA = [], arrayB = []) {
if (arrayA.length !== arrayB.length) return false;
for (let i = 0; i < arrayA.length; i++) {
if (arrayA[i] !== arrayB[i]) return false;
}
return true;
}
let newReplyButtonActive = false;
let userBioData = false;
let kqjzvmbt = false;
function connectToServer() {
let mainInputsDiv1 = document.getElementById('DLP_Main_Inputs_1_Divider_1_ID');
const chatKeyValue = storageLocal?.chatKey?.[0] ?? false;
//fetch(apiURL + '/server', {
fetch('https://api.duolingopro.net/server', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
version: versionFormal,
key: storageLocal.random16,
...(chatKeyValue && { chat_key: chatKeyValue })
})
})
.then(response => response.json())
.then(data => {
if (data.global || data.versions) {
console.log(data.chats);
if (!userBioData && !fetchingUserBioData) {
fetchUserBioData();
}
if (chatKeyValue) {
if (!data.chats) {
if (kqjzvmbt) {
storageLocal.chatKey.shift();
saveStorageLocal();
}
kqjzvmbt = true;
} else {
buildChat(data);
}
}
function buildChat(data) {
const chatParent = document.querySelector('#DLP_Main_Box_Divider_11_ID').lastElementChild;
const chatBox = chatParent?.querySelector('.DLP_Chat_Box_1_ID_1');
if (!chatBox) return;
if (typeof data === 'undefined' || typeof data.chats === 'undefined' || !Array.isArray(data.chats.messages)) return;
if (chatParent?.querySelector('#DLP_Inset_Group_3')?.style.display !== 'none') chatParent.querySelector('#DLP_Inset_Group_3').style.display = 'none';
if (chatBox?.style.display === 'none') chatBox.style.display = 'flex';
if (data.chats.solved) {
chatParent.querySelector('#DLP_Inset_Group_1').style.display = 'none';
chatParent.querySelector('#DLP_Inset_Group_2').style.display = '';
}
const incomingMessages = data.chats.messages.filter(msg => !msg?.deleted && msg?.status !== 'deleted');
const nextFingerprints = incomingMessages.map(computeMessageFingerprint);
const hasChanges = nextFingerprints.length !== chatMemoryFingerprints.length || nextFingerprints.some((fingerprint, index) => fingerprint !== chatMemoryFingerprints[index]);
if (hasChanges) {
const previousLength = chatMemory.length;
const wasAtBottom = Math.abs(chatBox.scrollHeight - (chatBox.scrollTop + chatBox.clientHeight)) < 5;
const scrollOffsetFromBottom = chatBox.scrollHeight - chatBox.scrollTop;
chatBox.innerHTML = '';
const combinedMessages = [];
let sequenceCounter = 0;
const resolveTimestamp = (msg) => {
const rawTimestamp = msg?.send_time;
if (rawTimestamp === undefined || rawTimestamp === null) {
return Number.MAX_SAFE_INTEGER;
}
const numericTimestamp = Number(rawTimestamp);
if (!Number.isFinite(numericTimestamp)) {
return Number.MAX_SAFE_INTEGER;
}
return numericTimestamp < 1e12 ? numericTimestamp * 1000 : numericTimestamp;
};
incomingMessages.forEach(message => {
combinedMessages.push({
message,
tempId: false,
sequence: sequenceCounter++
});
});
pendingTempMessages.forEach((tempMessage, tempId) => {
combinedMessages.push({
message: tempMessage,
tempId,
sequence: sequenceCounter++
});
});
combinedMessages.sort((a, b) => {
const timeA = resolveTimestamp(a.message);
const timeB = resolveTimestamp(b.message);
if (timeA === timeB) {
return a.sequence - b.sequence;
}
return timeA - timeB;
});
chatMessageLookup.clear();
incomingMessages.forEach(msg => {
const key = resolveMessageKey(msg);
if (key) {
chatMessageLookup.set(key, msg);
}
if (msg?.send_time !== undefined && msg?.send_time !== null) {
const sendKey = String(msg.send_time);
if (sendKey && sendKey !== key) {
chatMessageLookup.set(sendKey, msg);
}
}
});
combinedMessages.forEach(({ message, tempId }) => {
createMessage(message, false, tempId || false);
});
const hasNewMessages = incomingMessages.length > previousLength;
if (hasNewMessages || wasAtBottom) {
chatBox.scrollTop = chatBox.scrollHeight;
} else {
const newScrollTop = chatBox.scrollHeight - scrollOffsetFromBottom;
chatBox.scrollTop = newScrollTop < 0 ? 0 : newScrollTop;
}
}
chatMemory = incomingMessages.map(message => ({ ...message }));
chatMemoryFingerprints = nextFingerprints;
const knownMessageIds = (storageLocal.chats ?? []).map(id => (id === null || id === undefined) ? id : String(id));
if (currentPage === 11) {
const newMessageIds = chatMemory.map(resolveMessageKey);
if (!areArraysEqual(knownMessageIds, newMessageIds)) {
storageLocal.chats = newMessageIds;
saveStorageLocal();
}
} else {
incomingMessages.forEach(msg => {
const messageKey = resolveMessageKey(msg);
const sendTimeKey = (msg?.send_time !== undefined && msg?.send_time !== null) ? String(msg.send_time) : null;
const alreadyKnown = (messageKey && knownMessageIds.includes(messageKey)) || (sendTimeKey && knownMessageIds.includes(sendTimeKey));
if (!alreadyKnown && !newReplyButtonActive) {
newReplyButtonActive = true;
updateConnetionButtonStyles(document.getElementById("DLP_Main_Feedback_1_Button_1_ID"), {button: 'rgb(var(--DLP-blue))', outline: 'rgba(0, 0, 0, 0.20)', text: '#FFF', icon: '#FFF'}, {text: 'New Reply', icon: ''}, {text: 'DLP_Pulse_Opacity_Animation_1 6s ease-in-out infinite', icon: 'DLP_Pulse_Opacity_Animation_1 6s ease-in-out infinite'});
showNotification({icon: "", color: "rgb(var(--DLP-blue))"}, "Support Team Response", "You have a new message from our support team.", 30);
}
});
}
}
const globalData = data.global;
const versionData = data.versions[versionFull];
const warnings = versionData.warnings || [];
const termsText = Object.entries(globalData.terms)[0][1];
newTermID = Object.entries(globalData.terms)[0][0];
//console.log('Global Warning:', globalData.warning);
//console.log('Notifications:', globalData.notifications);
document.querySelector(`#DLP_Terms_Main_Text_1_ID`).innerHTML = termsText;
if (versionData.status === 'latest') {
if (storageLocal.terms === newTermID) {
if (serverConnectedBefore !== 'yes') {
updateReleaseNotes(warnings);
mainInputsDiv1.style.opacity = '1';
mainInputsDiv1.style.pointerEvents = 'auto';
updateConnetionButtonStyles(DLP_Server_Connection_Button, {button: 'rgb(var(--DLP-green))', outline: 'rgba(0, 0, 0, 0.20)', text: '#FFF', icon: '#FFF'}, {text: systemText[systemLanguage][108], icon: ''}, {text: 'DLP_Pulse_Opacity_Animation_1 6s ease-in-out infinite', icon: 'DLP_Pulse_Opacity_Animation_1 6s ease-in-out infinite'});
updateConnetionButtonStyles(DLP_Server_Connection_Button_2, {button: 'rgb(var(--DLP-green))', outline: 'rgba(0, 0, 0, 0.20)', text: '#FFF', icon: '#FFF'}, {text: systemText[systemLanguage][108], icon: ''}, {text: 'DLP_Pulse_Opacity_Animation_1 6s ease-in-out infinite', icon: 'DLP_Pulse_Opacity_Animation_1 6s ease-in-out infinite'});
DLP_Server_Connection_Button.setAttribute("data-dlp-connection-status", "connected");
DLP_Server_Connection_Button_2.setAttribute("data-dlp-connection-status", "connected");
if (serverConnectedBefore === 'error' || serverConnectedBeforeNotification) {
serverConnectedBeforeNotification.close();
serverConnectedBeforeNotification = false;
}
serverConnectedBefore = 'yes';
}
} else {
if (storageLocal.onboarding) {
if (currentPage !== 5 && currentPage !== 6) goToPage(5);
document.querySelector(`#DLP_Main_Box_Divider_5_ID`).querySelector(`#DLP_Terms_1_Text_1_ID`).innerHTML = "We have updated our Terms & Conditions. Please read them carefully and accept to continue using Duolingo PRO 3.1.";
} else {
if (currentPage !== 10) goToPage(10);
}
}
} else if (serverConnectedBefore !== 'outdated') {
updateConnetionButtonStyles(DLP_Server_Connection_Button, {button: 'rgb(var(--DLP-orange))', outline: 'rgba(0, 0, 0, 0.20)', text: '#FFF', icon: '#FFF'}, {text: 'Outdated', icon: ''}, {text: 'DLP_Pulse_Opacity_Animation_1 6s ease-in-out infinite', icon: 'DLP_Pulse_Opacity_Animation_1 6s ease-in-out infinite'});
updateConnetionButtonStyles(DLP_Server_Connection_Button_2, {button: 'rgb(var(--DLP-orange))', outline: 'rgba(0, 0, 0, 0.20)', text: '#FFF', icon: '#FFF'}, {text: 'Outdated', icon: ''}, {text: 'DLP_Pulse_Opacity_Animation_1 6s ease-in-out infinite', icon: 'DLP_Pulse_Opacity_Animation_1 6s ease-in-out infinite'});
DLP_Server_Connection_Button.setAttribute("data-dlp-connection-status", "outdated");
DLP_Server_Connection_Button_2.setAttribute("data-dlp-connection-status", "outdated");
if (serverConnectedBefore === 'no') {
mainInputsDiv1.style.opacity = '0.5';
mainInputsDiv1.style.pointerEvents = 'none';
showNotification("warning", systemText[systemLanguage][233], systemText[systemLanguage][234], 0);
} else if (serverConnectedBefore === 'error' || serverConnectedBeforeNotification) {
serverConnectedBeforeNotification.close();
serverConnectedBeforeNotification = false;
}
serverConnectedBefore = 'outdated';
}
//if (storageLocal.languagePackVersion !== versionData.languagePackVersion) {
// fetch(serverURL + "/static/3.0/resources/language_pack.json")
// .then(response => response.json())
// .then(data => {
// if (data[versionFull]) {
// storageLocal.languagePack = data[versionFull];
// console.log(data[versionFull]);
// storageLocal.languagePackVersion = versionData.languagePackVersion;
// saveStorageLocal();
// }
// })
// .catch(error => console.error('Error fetching systemText:', error));
//}
} else {
console.error(`Version ${versionFull} not found in the data`);
}
})
.catch(error => {
console.error('Error fetching data:', error);
if (serverConnectedBefore !== 'error') {
mainInputsDiv1.style.opacity = '0.5';
mainInputsDiv1.style.pointerEvents = 'none';
updateConnetionButtonStyles(DLP_Server_Connection_Button, {button: 'rgb(var(--DLP-pink))', outline: 'rgba(0, 0, 0, 0.20)', text: '#FFF', icon: '#FFF'}, {text: systemText[systemLanguage][109], icon: ''}, {text: 'DLP_Pulse_Opacity_Animation_1 6s ease-in-out infinite', icon: 'DLP_Pulse_Opacity_Animation_1 6s ease-in-out infinite'});
updateConnetionButtonStyles(DLP_Server_Connection_Button_2, {button: 'rgb(var(--DLP-pink))', outline: 'rgba(0, 0, 0, 0.20)', text: '#FFF', icon: '#FFF'}, {text: systemText[systemLanguage][109], icon: ''}, {text: 'DLP_Pulse_Opacity_Animation_1 6s ease-in-out infinite', icon: 'DLP_Pulse_Opacity_Animation_1 6s ease-in-out infinite'});
DLP_Server_Connection_Button.setAttribute("data-dlp-connection-status", "error");
DLP_Server_Connection_Button_2.setAttribute("data-dlp-connection-status", "error");
serverConnectedBeforeNotification = showNotification("error", systemText[systemLanguage][231], systemText[systemLanguage][232], 0);
serverConnectedBefore = 'error';
}
});
}
connectToServer();
setTimeout(() => {
connectToServer();
}, 1000);
setInterval(() => {
//if (windowBlurState) connectToServer();
if (document.visibilityState === "visible" || isAutoMode) connectToServer();
}, 4000);
let fetchingUserBioData = false;
async function fetchUserBioData() {
fetchingUserBioData = true;
console.log('FETHCING FOR YOU YOUR HONOR');
const userResponse = await fetch('https://www.duolingo.com/2017-06-30/users/' + JSON.parse(atob(document.cookie.split(';').find(cookie => cookie.includes('jwt_token')).split('=')[1].split('.')[1])).sub + '?fields=name,username,picture');
if (!userResponse.ok) {
fetchingUserBioData = false;
return;
}
const userData = await userResponse.json();
console.log(userData);
userBioData = {
username: (userData.name && userData.name.trim().length > 0) ? userData.name : userData.username,
profile_picture: "https:" + userData.picture + "/xlarge"
};
fetchingUserBioData = false;
}
function createMessage(message, isBefore=false, isTemp=false) {
function formatTimeAgo(timestamp) {
// If the timestamp is in seconds (10 digits), convert to ms
if (timestamp < 1e12) {
timestamp = timestamp * 1000;
}
const now = Date.now();
const diff = now - timestamp; // Difference in milliseconds
const seconds = Math.floor(diff / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
const weeks = Math.floor(days / 7);
const months = Math.floor(days / 30);
const years = Math.floor(days / 365);
if (seconds < 60) {
return "now";
} else if (minutes < 60) {
return `${minutes}m ago`;
} else if (hours < 24) {
return `${hours}h ago`;
} else if (days < 7) {
return `${days}d ago`;
} else if (weeks < 4) {
return `${weeks}w ago`;
} else if (months < 12) {
return `${months}m ago`;
} else {
return `${years}y ago`;
}
}
function toMilliseconds(ts) {
return ts < 1e12 ? ts * 1000 : ts;
}
function updateTimeAgo(element, timestamp) {
function update() {
if (!document.contains(element)) {
clearInterval(intervalId);
return;
}
const newText = formatTimeAgo(timestamp);
if (element.textContent !== newText) {
element.textContent = newText;
}
}
update();
const intervalId = setInterval(update, 1000);
}
const chatBox = document.querySelector('#DLP_Main_Box_Divider_11_ID')?.querySelector('.DLP_Chat_Box_1_ID_1');
const messageKey = (() => {
if (message?.message_id !== undefined && message?.message_id !== null) {
return String(message.message_id);
}
if (message?.send_time !== undefined && message?.send_time !== null) {
return String(message.send_time);
}
if (isTemp) {
return `temp-${isTemp}`;
}
return '';
})();
let lastChatChild = chatBox.lastElementChild;
if (isBefore) lastChatChild = isBefore.previousElementSibling;
const tempState = isTemp ? pendingTempMessages.get(isTemp) : null;
const failedTemp = Boolean(tempState?.sendFailed);
function getReplyKey(message) {
const rawReplyTo = message?.reply_to;
if (typeof rawReplyTo === 'number' && Number.isFinite(rawReplyTo)) {
return String(rawReplyTo);
}
if (typeof rawReplyTo === 'string') {
const trimmed = rawReplyTo.trim();
if (/^\d+$/.test(trimmed)) {
return trimmed;
}
}
if (typeof rawReplyTo === 'bigint') {
return rawReplyTo.toString();
}
return null;
}
function hasNumericReply(message) {
return Boolean(getReplyKey(message));
}
function createReplyPreview(message) {
const replyKey = getReplyKey(message);
if (!replyKey) {
return null;
}
function deriveTargetFromDom(key) {
if (!chatBox) return null;
const messageNodes = chatBox.querySelectorAll('[data-message-id]');
let matchedNode = null;
for (const node of messageNodes) {
if (node.getAttribute('data-message-id') === key) {
matchedNode = node;
break;
}
}
if (!matchedNode) {
const groupCandidate = chatBox.querySelector(`[data-group-id="${key}"]`);
if (groupCandidate) {
matchedNode = groupCandidate.querySelector('[data-message-id]');
}
}
if (!matchedNode) return null;
const result = {
message_id: key,
message: (matchedNode.textContent || '').trim()
};
const sendAttr = matchedNode.getAttribute('data-message-sent');
if (sendAttr && sendAttr !== '') {
const numericSend = Number(sendAttr);
result.send_time = Number.isFinite(numericSend) ? numericSend : sendAttr;
}
const groupNode = matchedNode.closest('[data-group-id]');
if (groupNode) {
if (!result.message) {
const fallbackNode = groupNode.querySelector('[data-message-id]');
if (fallbackNode && fallbackNode !== matchedNode) {
const fallbackText = (fallbackNode.textContent || '').trim();
if (fallbackText) {
result.message = fallbackText;
}
}
}
const authorNameAttr = groupNode.getAttribute('data-author-name');
if (authorNameAttr) {
result.author = authorNameAttr;
}
const headerNode = groupNode.querySelector('[data-chat-header="true"]');
if (headerNode) {
const authorElement = headerNode.querySelector('.DLP_HStack_6 p.DLP_Text_Style_1');
if (authorElement) {
const authorText = authorElement.textContent || '';
if (authorText.trim()) {
result.author = authorText.trim();
}
let accentColor = authorElement.style?.color?.trim();
if ((!accentColor || accentColor === '') && typeof window !== 'undefined' && document.contains(authorElement)) {
try {
accentColor = window.getComputedStyle(authorElement).color;
} catch (error) {
console.error('Failed to compute accent color for reply preview', error);
}
}
if (accentColor) {
result.accent = accentColor;
}
}
const avatarElement = headerNode.querySelector('div[style*="background"]');
if (avatarElement) {
const styleAttr = avatarElement.getAttribute('style') || '';
const urlMatch = styleAttr.match(/url\((['"]?)(.*?)\1\)/);
if (urlMatch && urlMatch[2]) {
result.profile_picture = urlMatch[2];
}
}
}
}
if ((!result.message || result.message === '') && matchedNode.classList?.contains('DLP_Hide_Scrollbar')) {
result.message = 'Attachment';
}
return result;
}
let targetMessage = chatMessageLookup.get(replyKey);
if (!targetMessage && chatMemory.length) {
targetMessage = chatMemory.find(existing => resolveMessageKey(existing) === replyKey);
}
function isMeaningful(value, type) {
if (value === undefined || value === null) return false;
const trimmed = String(value).trim();
if (!trimmed) return false;
if (type === 'author' && trimmed === 'The User Who Was Replied') return false;
if (type === 'message' && trimmed === 'Reply content') return false;
return true;
}
const domMessage = deriveTargetFromDom(replyKey);
if (domMessage) {
const merged = targetMessage ? { ...targetMessage } : {};
if (isMeaningful(domMessage.author, 'author')) {
merged.author = domMessage.author;
}
if (isMeaningful(domMessage.profile_picture)) {
merged.profile_picture = domMessage.profile_picture;
}
if (isMeaningful(domMessage.accent)) {
merged.accent = domMessage.accent;
}
if (isMeaningful(domMessage.message, 'message')) {
if (!isMeaningful(merged.message, 'message') || domMessage.message !== 'Attachment') {
merged.message = domMessage.message;
}
} else if (!isMeaningful(merged.message, 'message') && domMessage.message) {
merged.message = domMessage.message;
}
if (domMessage.send_time !== undefined && domMessage.send_time !== null && (merged.send_time === undefined || merged.send_time === null || merged.send_time === '')) {
merged.send_time = domMessage.send_time;
}
if (!isMeaningful(merged.message_id)) {
merged.message_id = replyKey;
}
targetMessage = merged;
chatMessageLookup.set(replyKey, targetMessage);
const derivedSendKey = targetMessage?.send_time;
if (derivedSendKey !== undefined && derivedSendKey !== null) {
const sendKey = String(derivedSendKey);
if (sendKey && sendKey !== replyKey) {
chatMessageLookup.set(sendKey, targetMessage);
}
}
}
const previewWrapper = document.createElement('div');
const targetKey = resolveMessageKey(targetMessage) ?? replyKey;
const targetSendTime = targetMessage?.send_time ?? '';
const previewAccent = targetMessage?.accent && targetMessage.accent !== '' ? targetMessage.accent : (message?.accent || 'rgb(var(--DLP-blue))');
const previewAuthor = targetMessage?.author ?? message?.author ?? 'Unknown user';
const previewAvatar = targetMessage?.profile_picture ?? message?.profile_picture ?? '';
const avatarBackground = previewAvatar ? `background: url(${previewAvatar}) 50% center / cover no-repeat white;` : 'background: rgba(var(--color-snow), 1);';
const previewMessage = (targetMessage?.message && targetMessage.message.trim() !== '') ? targetMessage.message : 'Original message unavailable';
previewWrapper.innerHTML = `
`;
return previewWrapper.firstElementChild;
}
function ensureReplyPreview(container, message) {
if (!container) return;
const replyKey = getReplyKey(message);
const existingPreview = container.querySelector('[data-reply-preview="true"]');
if (!replyKey) {
if (existingPreview) existingPreview.remove();
return;
}
const headerElement = container.querySelector('[data-chat-header="true"]');
if (!headerElement) {
if (existingPreview) existingPreview.remove();
return;
}
if (existingPreview) {
existingPreview.remove();
}
const previewElement = createReplyPreview(message);
if (previewElement) {
container.insertBefore(previewElement, headerElement);
}
}
function createStartersMessage(message) {
const temp = document.createElement('div');
temp.innerHTML = `
${message.role}
${formatTimeAgo(message.send_time)}
`;
const newElement = temp.firstElementChild;
chatBox.appendChild(newElement);
lastChatChild = newElement;
ensureReplyPreview(lastChatChild, message);
const timeElement = lastChatChild.querySelector('[data-time-element="true"]');
if (timeElement) {
updateTimeAgo(timeElement, message.send_time);
}
createContinuationMessage(message, true);
}
function createContinuationMessage(message, skipGroupChecks = false) {
const firstMessageTimestampAttr = lastChatChild.getAttribute('data-group-sent') ?? lastChatChild.getAttribute('data-group-timestamp');
const firstMessageTimestamp = firstMessageTimestampAttr ? parseInt(firstMessageTimestampAttr) : 0;
const replyKey = getReplyKey(message);
if (!skipGroupChecks) {
if (replyKey) {
createStartersMessage(message);
return;
}
if (toMilliseconds(message.send_time) - toMilliseconds(firstMessageTimestamp) > 900000) { // 15 minutes, 900,000 milliseconds
createStartersMessage(message);
return;
}
}
if (replyKey) {
ensureReplyPreview(lastChatChild, message);
}
if (message.message !== "") {
const continuationStyles = [
'align-self: stretch',
'white-space: pre-line',
'overflow-wrap: anywhere',
'word-break: break-word'
];
if (isTemp && !failedTemp) {
continuationStyles.push('animation: DLP_Pulse_Opacity_Animation_2 2s ease-in-out infinite');
}
if (failedTemp) {
continuationStyles.push('color: rgba(var(--DLP-pink))');
}
const continuationStyleAttr = continuationStyles.join('; ') + ';';
const temp = document.createElement('div');
temp.innerHTML = `
${message.message}
`;
const newElement = temp.firstElementChild;
lastChatChild.appendChild(newElement);
}
createAttachmentMessage(message);
}
function expandAttachment(lastAttachment) {
let expanded = false;
function getElementPosition(element) {
if (!element || !(element instanceof Element)) {
return false;
}
const rect = element.getBoundingClientRect();
return {
top: rect.top,
right: rect.right,
bottom: rect.bottom,
left: rect.left,
width: rect.width,
height: rect.height
};
}
async function getElementDimensions(element) {
return new Promise((resolve, reject) => {
if (!(element instanceof HTMLImageElement) && !(element instanceof HTMLVideoElement)) {
return reject(new Error('Element must be an image or video'));
}
if (element instanceof HTMLImageElement) {
if (element.complete) {
return resolve({ width: element.naturalWidth, height: element.naturalHeight });
}
element.addEventListener('load', () => {
resolve({ width: element.naturalWidth, height: element.naturalHeight });
}, { once: true });
element.addEventListener('error', () => {
reject(new Error('Failed to load image'));
}, { once: true });
} else if (element instanceof HTMLVideoElement) {
if (element.readyState >= 1) {
return resolve({ width: element.videoWidth, height: element.videoHeight });
}
element.addEventListener('loadedmetadata', () => {
resolve({ width: element.videoWidth, height: element.videoHeight });
}, { once: true });
element.addEventListener('error', () => {
reject(new Error('Failed to load video'));
}, { once: true });
}
});
}
function getMaxDimensions(element) {
const computedStyle = window.getComputedStyle(element);
const maxWidth = computedStyle.maxWidth;
const maxHeight = computedStyle.maxHeight;
const result = { width: null, height: null };
function parseCalcOrPixel(value, dimension) {
// If value is in pixels, return it
if (value.endsWith('px')) {
return parseFloat(value);
}
// Handle calc(100% - Npx)
if (value.includes('calc')) {
const match = value.match(/calc\(100% - (\d+\.?\d*?)px\)/);
if (match) {
const subtractedPx = parseFloat(match[1]);
const parent = element.parentElement;
if (dimension === 'width') {
return parent ? parent.getBoundingClientRect().width - subtractedPx : window.innerWidth - subtractedPx;
} else if (dimension === 'height') {
return parent ? parent.getBoundingClientRect().height - subtractedPx : window.innerHeight - subtractedPx;
}
}
}
return false; // Fallback
}
// Calculate max-width and max-height in pixels
result.width = parseCalcOrPixel(maxWidth, 'width');
result.height = parseCalcOrPixel(maxHeight, 'height');
return result;
}
async function fitToWindow() {
const max = getMaxDimensions(lastAttachment);
const lastAttachmentContent = lastAttachment.querySelector('.DLP_Attachment_Box_1_Content');
const orig = await getElementDimensions(lastAttachmentContent);
const scale = Math.min(max.width / orig.width, max.height / orig.height);
const w = Math.floor(orig.width * scale);
const h = Math.floor(orig.height * scale);
lastAttachment.style.width = `${w}px`;
lastAttachment.style.height = `${h}px`;
}
lastAttachment.addEventListener('mouseenter', () => {
if (expanded) return;
lastAttachment.querySelector('.DLP_Attachment_Box_1_Hover').style.display = '';
});
lastAttachment.addEventListener('mouseleave', () => {
if (expanded) return;
lastAttachment.querySelector('.DLP_Attachment_Box_1_Hover').style.display = 'none';
});
lastAttachment.querySelector('.DLP_Attachment_Box_1_Hover p').addEventListener('click', async () => {
expanded = true;
lastAttachment.querySelector('.DLP_Attachment_Box_1_Hover').style.display = 'none';
let pos = getElementPosition(lastAttachment);
const tempHover = document.createElement('div');
tempHover.style.width = pos.width + 'px';
tempHover.style.height = pos.height + 'px';
tempHover.style.opacity = '0';
// append tempHover right before lastAttachment
lastAttachment.parentNode.insertBefore(tempHover, lastAttachment);
const largeViewMotherBox = document.createElement('div');
largeViewMotherBox.className = 'DLP_Attachment_Box_Large_View_1';
document.body.appendChild(largeViewMotherBox);
let closeBtn = `
`;
largeViewMotherBox.insertAdjacentHTML('beforeend', closeBtn);
function getTransformToMatchPosition(element) {
const parentRect = largeViewMotherBox.getBoundingClientRect();
const centerX = parentRect.width / 2;
const centerY = parentRect.height / 2;
const elementRect = element.getBoundingClientRect();
const elementCenterX = elementRect.width / 2;
const elementCenterY = elementRect.height / 2;
const shiftX = pos.left + elementCenterX - centerX;
const shiftY = pos.top + elementCenterY - centerY;
return { translateX: shiftX, translateY: shiftY };
}
largeViewMotherBox.appendChild(lastAttachment);
translate = getTransformToMatchPosition(lastAttachment);
lastAttachment.style.transform = `translate(${translate.translateX}px, ${translate.translateY}px)`;
void lastAttachment.offsetHeight;
lastAttachment.style.transition = '0.4s cubic-bezier(0.16, 1, 0.32, 1)';
void lastAttachment.offsetHeight;
lastAttachment.style.transform = 'translate(0px, 0px)';
largeViewMotherBox.style.background = 'rgba(var(--color-snow), 0.50)';
largeViewMotherBox.style.backdropFilter = 'blur(16px)';
lastAttachment.style.aspectRatio = 'unset';
let lastAttachmentContent = lastAttachment.querySelector('.DLP_Attachment_Box_1_Content');
lastAttachmentContent.style.aspectRatio = 'unset';
lastAttachmentContent.style.maxWidth = '100%';
lastAttachmentContent.style.maxHeight = '100%';
lastAttachmentContent.style.width = 'auto';
lastAttachmentContent.style.height = 'auto';
lastAttachment.style.maxWidth = 'calc(100% - 32px)';
lastAttachment.style.maxHeight = 'calc(100% - 142px)';
lastAttachment.style.display = 'inline-flex';
lastAttachment.style.width = 'auto';
lastAttachment.style.height = 'auto';
void lastAttachment.offsetHeight;
const maxDimensions = getMaxDimensions(lastAttachment);
const elementDimensions = await getElementDimensions(lastAttachmentContent);
// compute uniform scale to fit within maxDimensions
const scale = Math.min(maxDimensions.width / elementDimensions.width, maxDimensions.height / elementDimensions.height);
// calculate final pixel dimensions
let newWidth = Math.floor(elementDimensions.width * scale);
let newHeight = Math.floor(elementDimensions.height * scale);
//let newWidth = lastAttachmentContent.offsetWidth;
//let newHeight = lastAttachmentContent.offsetHeight;
lastAttachment.style.width = '';
lastAttachment.style.height = '';
void lastAttachment.offsetHeight;
lastAttachment.style.width = newWidth + 'px';
lastAttachment.style.height = newHeight + 'px';
lastAttachmentContent.style.width = '100%';
lastAttachmentContent.style.height = '100%';
if (lastAttachmentContent.tagName.toLowerCase() === 'video') {
lastAttachmentContent.controls = true;
lastAttachmentContent.autoplay = false;
lastAttachmentContent.muted = false;
lastAttachmentContent.currentTime = 0;
lastAttachmentContent.play();
}
window.addEventListener('resize', fitToWindow);
largeViewMotherBox.addEventListener('click', (event) => {
if (largeViewMotherBox === event.target && lastAttachment !== event.target) {
window.removeEventListener('resize', fitToWindow);
if (lastAttachmentContent.tagName.toLowerCase() === 'video') {
lastAttachmentContent.controls = false;
lastAttachmentContent.autoplay = true;
lastAttachmentContent.muted = true;
lastAttachmentContent.play();
}
lastAttachment.style.transform = `translate(${translate.translateX}px, ${translate.translateY}px)`;
lastAttachment.style.width = pos.width + 'px';
lastAttachment.style.height = pos.height + 'px';
largeViewMotherBox.style.background = 'rgba(var(--color-snow), 0.00)';
largeViewMotherBox.style.backdropFilter = 'blur(0px)';
setTimeout(() => {
tempHover.parentNode.insertBefore(lastAttachment, tempHover);
lastAttachment.style.transform = '';
tempHover.remove();
largeViewMotherBox.remove();
lastAttachment.style.aspectRatio = '';
lastAttachment.querySelector('.DLP_Attachment_Box_1_Content').style.aspectRatio = '';
lastAttachment.querySelector('.DLP_Attachment_Box_1_Content').style.maxWidth = '';
lastAttachment.querySelector('.DLP_Attachment_Box_1_Content').style.maxHeight = '';
lastAttachment.querySelector('.DLP_Attachment_Box_1_Content').style.width = '';
lastAttachment.querySelector('.DLP_Attachment_Box_1_Content').style.height = '';
lastAttachment.style.maxWidth = '';
lastAttachment.style.maxHeight = '';
lastAttachment.style.display = '';
lastAttachment.style.transition = '';
void lastAttachment.offsetHeight;
expanded = false;
}, 400);
}
});
});
}
async function createAttachmentMessage(message) {
async function contentType(url) {
const imageExts = ['jpg','jpeg','png','gif','webp','bmp','svg'];
const videoExts = ['mp4','webm','ogg','mov','m4v'];
let ft = '';
try {
const u = new URL(url);
ft = (u.searchParams.get('filetype') || '').toLowerCase();
} catch (e) {
const qs = url.split('?')[1] || '';
const match = qs.match(/(?:^|&)filetype=([^&]+)/i);
ft = match ? decodeURIComponent(match[1]).toLowerCase() : '';
}
if (imageExts.includes(ft)) return 'image';
if (videoExts.includes(ft)) return 'video';
// 3) give up
return 'other';
}
if (message.files.length > 0) {
const temp2 = document.createElement('div');
temp2.innerHTML = `
`;
const newElement2 = temp2.firstElementChild;
lastChatChild.appendChild(newElement2);
let attachmentParent = lastChatChild.lastElementChild;
for (let i = 0; i < message.files.length; i++) {
const file = message.files[i];
const temp = document.createElement('div');
let extensionType = await contentType(file);
if (extensionType === 'image') {
temp.innerHTML = `
`;
} else if (extensionType === 'video') {
temp.innerHTML = `
`;
} else {
temp.innerHTML = `
`;
}
const newElement = temp.firstElementChild;
attachmentParent.appendChild(newElement);
expandAttachment(newElement);
}
}
}
const sameAuthor = lastChatChild !== null && message.author === lastChatChild.getAttribute('data-author-name');
const messageHasReply = hasNumericReply(message);
if (sameAuthor && !messageHasReply) {
createContinuationMessage(message);
} else {
createStartersMessage(message);
}
}
async function intelligentLeaderboardBasedWarningLimit() {
const defaultBoardId = "7d9f5dd1-8423-491a-91f2-2532052038ce";
const tournamentBoardId = "4b668ba6-288d-4b78-81a3-7b213175ae2c";
const baseUrl = "https://duolingo-leaderboards-prod.duolingo.com/leaderboards/";
function getDuolingoUserIdFromJwt(token) {
try {
const p = token.split('.')[1];
const decoded = decodeURIComponent(atob(p.replace(/-/g, '+').replace(/_/g, '/').padEnd(p.length + (4 - p.length % 4) % 4, '=')).split('').map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)).join(''));
return JSON.parse(decoded).sub;
} catch (e) {
console.error("Failed to decode JWT:", e);
return null;
}
}
function processLeaderboardData(data, userId) {
if (!data || !data.active || !data.active.cohort || !data.active.cohort.rankings) {
console.log("Chosen leaderboard data is invalid or inactive.");
return null;
}
const rankings = data.active.cohort.rankings;
const topN = 5;
const topScores = [...rankings].sort((a, b) => b.score - a.score).slice(0, topN).map(user => user.score);
const userRanking = rankings.find(u => u.user_id === userId);
const userScore = userRanking ? userRanking.score : 0;
const avgTopScore = topScores.length ? Math.round(topScores.reduce((sum, val) => sum + val, 0) / topScores.length) : 0;
const intelligentAmount = Math.max(0, avgTopScore - userScore);
console.log(`Using leaderboard: ${data.active.contest.contest_id}`);
console.log(`Average top ${topN} score:`, avgTopScore);
console.log(`Your score:`, userScore);
console.log(`Calculated intelligent warning limit:`, intelligentAmount);
return intelligentAmount;
}
const jwtToken = document.cookie.split('; ').find(cookie => cookie.startsWith('jwt_token='))?.split('=')[1];
if (!jwtToken) {
console.error("JWT token not found. Cannot proceed.");
return null;
}
const userID = getDuolingoUserIdFromJwt(jwtToken);
if (!userID) {
console.error("Could not extract User ID from JWT.");
return null;
}
const spedTimestamp = Date.now();
const fetchOptions = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${jwtToken}`,
},
};
try {
const [tournamentResult, defaultResult] = await Promise.allSettled([
fetch(`${baseUrl}${tournamentBoardId}/users/${userID}?_=${spedTimestamp}`, fetchOptions).then(res => res.json()),
fetch(`${baseUrl}${defaultBoardId}/users/${userID}?_=${spedTimestamp}`, fetchOptions).then(res => res.json()),
]);
let selectedData = null;
if (tournamentResult.status === 'fulfilled' && tournamentResult.value.active) {
console.log("Tournament leaderboard is active. Using it for calculation.");
selectedData = tournamentResult.value;
} else if (defaultResult.status === 'fulfilled' && defaultResult.value.active) {
console.log("Default leaderboard is active. Using it for calculation.");
selectedData = defaultResult.value;
} else {
console.log("No active leaderboards found (neither tournament nor default).");
if (tournamentResult.status === 'rejected') console.error("Tournament fetch failed:", tournamentResult.reason);
if (defaultResult.status === 'rejected') console.error("Default fetch failed:", defaultResult.reason);
return null;
}
const intelligentAmount = processLeaderboardData(selectedData, userID);
// return intelligentAmount ?? null;
return 20;
} catch (error) {
console.error("An unexpected error occurred during the fetch process:", error);
return null;
}
}
function updateReleaseNotes(warnings) {
const releaseNotesContainer = document.getElementById('DLP_Release_Notes_List_1_ID');
const controlsContainer = document.getElementById('DLP_Release_Notes_Controls');
const warningCounterDisplay = controlsContainer.querySelector('.DLP_Inset_Text_1_ID');
const prevButton = controlsContainer.querySelector('.DLP_Inset_Icon_1_ID');
const nextButton = controlsContainer.querySelector('.DLP_Inset_Icon_2_ID');
releaseNotesContainer.innerHTML = '';
let currentWarningIndex = 0;
const totalWarnings = warnings.length;
function updateCounterDisplay(current, total, displayElement) {
displayElement.textContent = `${current}/${total}`;
}
function updateButtonOpacity(current, total, prevButton, nextButton) {
if (current === 0) {
prevButton.style.opacity = '0.5';
prevButton.style.pointerEvents = 'none';
} else {
prevButton.style.opacity = '1';
prevButton.style.pointerEvents = 'auto';
}
if (current === total - 1) {
nextButton.style.opacity = '0.5';
nextButton.style.pointerEvents = 'none';
} else {
nextButton.style.opacity = '1';
nextButton.style.pointerEvents = 'auto';
}
}
warnings.forEach((warning, index) => {
if (warning.head && warning.body && warning.icon) {
const warningHTML = `
${warning.icon}
${warning.head}
${warning.tag}
${warning.body}
`;
releaseNotesContainer.insertAdjacentHTML('beforeend', warningHTML);
}
});
updateCounterDisplay(currentWarningIndex + 1, totalWarnings, warningCounterDisplay);
updateButtonOpacity(currentWarningIndex, totalWarnings, prevButton, nextButton);
prevButton.addEventListener('click', () => {
if (isBusySwitchingPages) return;
isBusySwitchingPages = true;
if (currentWarningIndex > 0) {
const oldWarning = document.getElementById(`warning-${currentWarningIndex}`);
const newWarning = document.getElementById(`warning-${currentWarningIndex - 1}`);
oldWarning.style.filter = 'blur(16px)';
oldWarning.style.opacity = '0';
newWarning.style.filter = 'blur(16px)';
newWarning.style.opacity = '0';
setTimeout(() => {
oldWarning.style.display = 'none';
newWarning.style.display = 'flex';
newWarning.offsetHeight;
newWarning.style.filter = 'blur(0px)';
newWarning.style.opacity = '1';
currentWarningIndex--;
updateCounterDisplay(currentWarningIndex + 1, totalWarnings, warningCounterDisplay);
updateButtonOpacity(currentWarningIndex, totalWarnings, prevButton, nextButton);
setTimeout(() => {
isBusySwitchingPages = false;
}, 400);
}, 400);
}
});
nextButton.addEventListener('click', () => {
if (isBusySwitchingPages) return;
isBusySwitchingPages = true;
if (currentWarningIndex < totalWarnings - 1) {
const oldWarning = document.getElementById(`warning-${currentWarningIndex}`);
const newWarning = document.getElementById(`warning-${currentWarningIndex + 1}`);
oldWarning.style.filter = 'blur(16px)';
oldWarning.style.opacity = '0';
newWarning.style.filter = 'blur(16px)';
newWarning.style.opacity = '0';
setTimeout(() => {
oldWarning.style.display = 'none';
newWarning.style.display = 'flex';
newWarning.offsetHeight;
newWarning.style.filter = 'blur(0px)';
newWarning.style.opacity = '1';
currentWarningIndex++;
updateCounterDisplay(currentWarningIndex + 1, totalWarnings, warningCounterDisplay);
updateButtonOpacity(currentWarningIndex, totalWarnings, prevButton, nextButton);
setTimeout(() => {
isBusySwitchingPages = false;
}, 400);
}, 400);
}
});
}
let DLP_Feedback_Text_Input_1_ID = document.getElementById("DLP_Feedback_Text_Input_1_ID");
let DLP_Feedback_Type_Bug_Report_Button_1_ID = document.getElementById("DLP_Feedback_Type_Bug_Report_Button_1_ID");
let DLP_Feedback_Type_Suggestion_Button_1_ID = document.getElementById("DLP_Feedback_Type_Suggestion_Button_1_ID");
let DLP_Feedback_Attachment_Upload_Button_1_ID = document.getElementById("DLP_Feedback_Attachment_Upload_Button_1_ID");
let DLP_Feedback_Attachment_Input_Hidden_1_ID = document.getElementById("DLP_Feedback_Attachment_Input_Hidden_1_ID");
let DLP_Feedback_Send_Button_1_ID = document.getElementById("DLP_Feedback_Send_Button_1_ID");
let sendFeedbackStatus = '';
DLP_Feedback_Send_Button_1_ID.addEventListener('click', () => {
if (sendFeedbackStatus !== '') return;
let FeedbackText = DLP_Feedback_Text_Input_1_ID.value;
sendFeedbackServer(feedbackType, FeedbackText);
setButtonState(DLP_Feedback_Send_Button_1_ID, {button: 'linear-gradient(0deg, rgba(var(--DLP-blue), 0.10) 0%, rgba(var(--DLP-blue), 0.10) 100%), rgba(var(--color-snow), 0.80)', outline: 'rgba(var(--DLP-blue), 0.20)', text: 'rgb(var(--DLP-blue))', icon: 'rgb(var(--DLP-blue))'}, {text: systemText[systemLanguage][111], icon: ''}, {text: '', icon: 'DLP_Rotate_360_Animation_1 4s ease-in-out infinite'}, () => {
function f() {
if (sendFeedbackStatus === 'sent') {
setButtonState(DLP_Feedback_Send_Button_1_ID, {button: 'linear-gradient(0deg, rgba(var(--DLP-green), 0.10) 0%, rgba(var(--DLP-green), 0.10) 100%), rgba(var(--color-snow), 0.80)', outline: 'rgba(var(--DLP-green), 0.20)', text: 'rgb(var(--DLP-green))', icon: 'rgb(var(--DLP-green))'}, {text: systemText[systemLanguage][112], icon: ''}, {text: '', icon: ' '}, () => {
confetti();
});
} else if (sendFeedbackStatus === 'error') {
setButtonState(DLP_Feedback_Send_Button_1_ID, {button: 'linear-gradient(0deg, rgba(var(--DLP-pink), 0.10) 0%, rgba(var(--DLP-pink), 0.10) 100%), rgba(var(--color-snow), 0.80)', outline: 'rgba(var(--DLP-pink), 0.20)', text: 'rgb(var(--DLP-pink))', icon: 'rgb(var(--DLP-pink))'}, {text: systemText[systemLanguage][115], icon: ''}, {text: '', icon: ' '}, () => {
});
} else if (sendFeedbackStatus === 'sending') {
setTimeout(() => { f(); }, 800);
}
}
f();
});
});
let feedbackType = 'Suggestion';
DLP_Feedback_Type_Bug_Report_Button_1_ID.addEventListener('click', () => {
feedbackType = 'Bug Report';
DLP_Feedback_Type_Bug_Report_Button_1_ID.classList.add('DLP_Feedback_Type_Button_Style_1_ON');
DLP_Feedback_Type_Bug_Report_Button_1_ID.classList.remove('DLP_Feedback_Type_Button_Style_1_OFF');
DLP_Feedback_Type_Suggestion_Button_1_ID.classList.add('DLP_Feedback_Type_Button_Style_2_OFF');
DLP_Feedback_Type_Suggestion_Button_1_ID.classList.remove('DLP_Feedback_Type_Button_Style_2_ON');
});
DLP_Feedback_Type_Suggestion_Button_1_ID.addEventListener('click', () => {
feedbackType = 'Suggestion';
DLP_Feedback_Type_Bug_Report_Button_1_ID.classList.add('DLP_Feedback_Type_Button_Style_1_OFF');
DLP_Feedback_Type_Bug_Report_Button_1_ID.classList.remove('DLP_Feedback_Type_Button_Style_1_ON');
DLP_Feedback_Type_Suggestion_Button_1_ID.classList.add('DLP_Feedback_Type_Button_Style_2_ON');
DLP_Feedback_Type_Suggestion_Button_1_ID.classList.remove('DLP_Feedback_Type_Button_Style_2_OFF');
});
let currentFileName = '';
setInterval(() => {
if (DLP_Feedback_Attachment_Input_Hidden_1_ID.files.length > 0) {
let fileName = DLP_Feedback_Attachment_Input_Hidden_1_ID.files[0].name;
if (currentFileName === fileName) return;
currentFileName = fileName;
DLP_Feedback_Attachment_Upload_Button_1_ID.querySelector('.DLP_Inset_Text_1_ID').style.filter = 'blur(4px)';
DLP_Feedback_Attachment_Upload_Button_1_ID.querySelector('.DLP_Inset_Text_1_ID').style.opacity = '0';
DLP_Feedback_Attachment_Upload_Button_1_ID.querySelector('.DLP_Inset_Icon_1_ID').style.filter = 'blur(4px)';
DLP_Feedback_Attachment_Upload_Button_1_ID.querySelector('.DLP_Inset_Icon_1_ID').style.opacity = '0';
DLP_Feedback_Attachment_Upload_Button_1_ID.style.background = 'rgb(var(--DLP-blue))';
DLP_Feedback_Attachment_Upload_Button_1_ID.style.outline = '2px solid rgba(0, 0, 0, 0.20)';
setTimeout(() => {
DLP_Feedback_Attachment_Upload_Button_1_ID.querySelector('.DLP_Inset_Icon_1_ID').style.display = 'none';
DLP_Feedback_Attachment_Upload_Button_1_ID.querySelector('.DLP_Inset_Text_1_ID').textContent = fileName;
DLP_Feedback_Attachment_Upload_Button_1_ID.querySelector('.DLP_Inset_Text_1_ID').style.color = '#FFF';
DLP_Feedback_Attachment_Upload_Button_1_ID.querySelector('.DLP_Inset_Text_1_ID').style.filter = '';
DLP_Feedback_Attachment_Upload_Button_1_ID.querySelector('.DLP_Inset_Text_1_ID').style.opacity = '';
}, 400);
}
}, 1000);
DLP_Feedback_Attachment_Upload_Button_1_ID.addEventListener('click', () => {
DLP_Feedback_Attachment_Input_Hidden_1_ID.click();
});
DLP_Feedback_Send_Button_1_ID.style.pointerEvents = 'none';
DLP_Feedback_Send_Button_1_ID.style.opacity = '0.5';
DLP_Feedback_Text_Input_1_ID.addEventListener("input", function () {
if (DLP_Feedback_Text_Input_1_ID.value.replace(/\s/g, "").length <= 16) {
DLP_Feedback_Send_Button_1_ID.style.pointerEvents = 'none';
DLP_Feedback_Send_Button_1_ID.style.opacity = '0.5';
} else {
DLP_Feedback_Send_Button_1_ID.style.pointerEvents = '';
DLP_Feedback_Send_Button_1_ID.style.opacity = '';
}
});
async function sendFeedbackServer(head, body) {
try {
sendFeedbackStatus = 'sending';
let payload = {
head: head,
body: body,
version: versionFormal
};
if (DLP_Feedback_Attachment_Input_Hidden_1_ID.files.length > 0) {
const file = DLP_Feedback_Attachment_Input_Hidden_1_ID.files[0];
const base64File = await new Promise((resolve) => {
const reader = new FileReader();
reader.onloadend = () => {
resolve(reader.result);
};
reader.readAsDataURL(file);
});
payload.file = base64File;
}
const response = await fetch(apiURL + "/feedback", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${document.cookie.split(';').find(cookie => cookie.includes('jwt_token')).split('=')[1]}`
},
body: JSON.stringify(payload)
});
const responseData = await response.json();
if (responseData.status) sendFeedbackStatus = 'sent';
else sendFeedbackStatus = 'error';
showNotification(responseData.notification.icon, responseData.notification.head, responseData.notification.body, responseData.notification.duration);
} catch (error) {
console.error('Error:', error);
sendFeedbackStatus = 'error';
showNotification("error", systemText[systemLanguage][206], systemText[systemLanguage][207], 30);
}
}
async function handleClick(button, id, amount) {
const ANIM_MS = 820;
let status = 'loading';
const loadingStart = Date.now();
setButtonState(
button,
{ button: 'rgba(var(--DLP-blue), 0.10)', outline: 'rgba(var(--DLP-blue), 0.20)', text: 'rgb(var(--DLP-blue))', icon: 'rgb(var(--DLP-blue))' },
{ text: systemText[systemLanguage][113], icon: '' },
{ text: '', icon: 'DLP_Rotate_360_Animation_1 4s ease-in-out infinite' }
);
let nextAnimationEndsAt = loadingStart + ANIM_MS;
setTimeout(() => { f(); }, Math.max(0, nextAnimationEndsAt - Date.now()));
try {
if (flag03) {
const intelligentAmount = await intelligentLeaderboardBasedWarningLimit();
console.log(`Intelligent amount: ${intelligentAmount}`);
const overrideXp = button.dataset.overrideXp === 'true';
if (id === 'xp' && amount > intelligentAmount && !overrideXp) {
button.dataset.overrideXp = 'true';
showNotification(
'warning',
'That is a lot of XP...',
`You're about to gain more XP than recommended. Click CONFIRM to continue.`,
10
);
const elapsed = Date.now() - loadingStart;
const delay = Math.max(0, ANIM_MS - elapsed);
setTimeout(() => {
setButtonState(
button,
{ button: 'rgb(var(--DLP-blue))', outline: 'rgba(0, 0, 0, 0.20)', text: '#FFF', icon: '#FFF' },
{ text: 'CONFIRM', icon: '' },
{ text: '', icon: '' }
);
nextAnimationEndsAt = Date.now() + ANIM_MS;
}, delay);
// Keep status 'loading' so the poller waits until the user confirms or cancels
return;
}
// Reset the override flag when actually proceeding
button.dataset.overrideXp = 'false';
}
const response = await fetch(apiURL + '/request', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${document.cookie.split(';').find(cookie => cookie.includes('jwt_token')).split('=')[1]}`
},
body: JSON.stringify({ type: id, amount, version: versionFull })
});
if (!response.ok) throw new Error('Request failed with status ' + response.status);
const reader = response.body.getReader();
const decoder = new TextDecoder();
let done = false;
let buffer = '';
while (!done) {
const { value, done: doneReading } = await reader.read();
done = doneReading;
buffer += decoder.decode(value, { stream: true });
let openBraces = 0;
let start = 0;
for (let i = 0; i < buffer.length; i++) {
const ch = buffer[i];
if (ch === '{') {
openBraces++;
} else if (ch === '}') {
openBraces--;
if (openBraces === 0) {
const jsonStr = buffer.substring(start, i + 1).trim();
try {
const data = JSON.parse(jsonStr);
if (data.status === 'completed') {
status = 'done';
done = true;
showNotification(data.notification.icon, data.notification.head, data.notification.body, data.notification.duration);
if (storageLocal.stats.tracking_since === 0) storageLocal.stats.tracking_since = Date.now();
storageLocal.stats.modern[id] += amount;
saveStorageLocal();
const input = button.parentElement.querySelector('#DLP_Inset_Input_1_ID');
if (input) {
input.value = '';
setTimeout(() => input.dispatchEvent(new Event('input')), 2400);
}
} else if (data.status == 'failed') {
status = 'error';
done = true;
showNotification(data.notification.icon, data.notification.head, data.notification.body, data.notification.duration);
console.log(data);
} else if (data.status === 'rejected') {
status = 'rejected';
done = true;
showNotification(data.notification.icon, data.notification.head, data.notification.body, data.notification.duration);
const input = button.parentElement.querySelector('#DLP_Inset_Input_1_ID');
if (data.max_amount && input) {
input.value = data.max_amount;
setTimeout(() => input.dispatchEvent(new Event('input')), 2400);
}
} else {
console.log(`Percentage: ${data.percentage}%`);
button.querySelector('.DLP_Inset_Text_1_ID').innerHTML = data.percentage + '%';
}
// Trim processed chunk and reset counters
buffer = buffer.substring(i + 1);
i = -1;
start = 0;
openBraces = 0;
} catch (e) {
// ignore and continue streaming
}
}
} else if (openBraces === 0 && buffer[i].trim() !== '') {
start = i;
}
}
}
} catch (error) {
console.error('Error during request:', error);
status = 'error';
}
function f() {
const now = Date.now();
// If an animation is still running, wait precisely until it ends
if (now < nextAnimationEndsAt) {
setTimeout(() => { f(); }, nextAnimationEndsAt - now);
return;
}
if (status === 'done') {
setButtonState(
button,
{ button: 'rgba(var(--DLP-green), 0.10)', outline: 'rgba(var(--DLP-green), 0.20)', text: 'rgb(var(--DLP-green))', icon: 'rgb(var(--DLP-green))' },
{ text: systemText[systemLanguage][114], icon: '' },
{ text: '', icon: '' }
);
nextAnimationEndsAt = Date.now() + ANIM_MS;
confetti();
setTimeout(() => {
let buttonContentText = systemText[systemLanguage][9];
if (id === 'super' || id === 'double_xp_boost') buttonContentText = systemText[systemLanguage][13];
else if (id === 'heart_refill') buttonContentText = systemText[systemLanguage][229];
setButtonState(
button,
{ button: 'rgb(var(--DLP-blue))', outline: 'rgba(0, 0, 0, 0.20)', text: '#FFF', icon: '#FFF' },
{ text: buttonContentText, icon: '' },
{ text: '', icon: '' }
);
nextAnimationEndsAt = Date.now() + ANIM_MS;
setTimeout(() => { isGetButtonsBusy = false; }, ANIM_MS);
}, (ANIM_MS * 2));
} else if (status === 'error') {
setButtonState(
button,
{ button: 'rgb(var(--DLP-pink))', outline: 'rgba(0, 0, 0, 0.20)', text: '#FFF', icon: '#FFF' },
{ text: systemText[systemLanguage][115], icon: '' },
{ text: '', icon: '' }
);
nextAnimationEndsAt = Date.now() + ANIM_MS;
setTimeout(() => {
setButtonState(
button,
{ button: 'rgb(var(--DLP-blue))', outline: 'rgba(0, 0, 0, 0.20)', text: '#FFF', icon: '#FFF' },
{ text: systemText[systemLanguage][9], icon: '' },
{ text: '', icon: '' }
);
nextAnimationEndsAt = Date.now() + ANIM_MS;
setTimeout(() => { isGetButtonsBusy = false; }, ANIM_MS);
}, (ANIM_MS * 2));
} else if (status === 'rejected') {
setButtonState(
button,
{ button: 'rgb(var(--DLP-blue))', outline: 'rgba(0, 0, 0, 0.20)', text: '#FFF', icon: '#FFF' },
{ text: systemText[systemLanguage][9], icon: '' },
{ text: '', icon: '' }
);
nextAnimationEndsAt = Date.now() + ANIM_MS;
setTimeout(() => { isGetButtonsBusy = false; }, ANIM_MS);
} else {
// Still waiting for async work or user confirmation; check again after any new animation
setTimeout(() => { f(); }, ANIM_MS);
}
}
}
const getButtonsList1 = [
{ base: 'DLP_Get_XP', type: 'xp', input: true },
{ base: 'DLP_Get_GEM', type: 'gem', input: true },
{ base: 'DLP_Get_Streak', type: 'streak', input: true },
{ base: 'DLP_Get_SUPER', type: 'super' },
{ base: 'DLP_Get_DOUBLE_XP_BOOST', type: 'double_xp_boost' },
{ base: 'DLP_Get_Streak_Freeze', type: 'streak_freeze', input: true },
{ base: 'DLP_Get_Heart_Refill', type: 'heart_refill' }
];
function setupGetButtons(base, type, hasInput) {
[1, 2].forEach(n => {
const parent = document.getElementById(`${base}_${n}_ID`);
if (!parent) return;
const button = parent.querySelector('#DLP_Inset_Button_1_ID');
const handler = () => {
if (isGetButtonsBusy && !(type === 'xp' && button.dataset.overrideXp === 'true')) return;
isGetButtonsBusy = true;
handleClick(button, type, hasInput ? Number(parent.querySelector('#DLP_Inset_Input_1_ID').value) : 1);
};
button.addEventListener('click', handler);
if (hasInput) {
const input = parent.querySelector('#DLP_Inset_Input_1_ID');
input.onkeyup = e => e.keyCode === 13 && handler();
}
});
};
getButtonsList1.forEach(({ base, type, input }) => setupGetButtons(base, type, input));
let DLP_Settings_Save_Button_1_ID = document.getElementById("DLP_Settings_Save_Button_1_ID");
DLP_Settings_Save_Button_1_ID.addEventListener('click', () => {
if (isBusySwitchingPages) return;
isBusySwitchingPages = true;
storageLocal.settings.autoUpdate = DLP_Settings_Var.autoUpdate;
storageLocal.settings.showAutoServerButton = DLP_Settings_Var.showAutoServerButton;
storageLocal.settings.showSolveButtons = DLP_Settings_Var.showSolveButtons;
storageLocal.settings.anonymousUsageData = DLP_Settings_Var.anonymousUsageData;
storageLocal.settings.solveSpeed = Number(settingsLegacySolveSpeedInputSanitizeValue(DLP_Settings_Legacy_Solve_Speed_1_ID.querySelector('#DLP_Inset_Input_1_ID').value, true));
saveStorageLocal();
setButtonState(DLP_Settings_Save_Button_1_ID, {button: 'linear-gradient(0deg, rgba(var(--DLP-green), 0.10) 0%, rgba(var(--DLP-green), 0.10) 100%), rgba(var(--color-snow), 0.80)', outline: 'rgba(var(--DLP-green), 0.20)', text: 'rgb(var(--DLP-green))', icon: 'rgb(var(--DLP-green))'}, {text: systemText[systemLanguage][116], icon: ''}, {text: '', icon: ' '}, () => {
//confetti();
setTimeout(() => {
//goToPage(-1);
location.reload();
}, 1600);
//setTimeout(() => {
// setButtonState(DLP_Settings_Save_Button_1_ID, systemText[systemLanguage][37], DLP_Settings_Save_Button_1_ID.querySelector('#DLP_Inset_Icon_1_ID'), DLP_Settings_Save_Button_1_ID.querySelector('#DLP_Inset_Icon_3_ID'), 'rgb(var(--DLP-blue))', '2px solid rgba(0, 0, 0, 0.20)', '#FFF', 400);
// isBusySwitchingPages = false;
//}, 2400);
});
});
let DLP_Settings_Var = {
autoUpdate: storageLocal.settings.autoUpdate,
showAutoServerButton: storageLocal.settings.showAutoServerButton,
showSolveButtons: storageLocal.settings.showSolveButtons,
solveSpeed: storageLocal.settings.solveSpeed,
anonymousUsageData: storageLocal.settings.anonymousUsageData
};
let DLP_Settings_Toggle_Busy = false;
let DLP_Settings_Show_Solve_Buttons_1_ID = document.getElementById("DLP_Settings_Show_Solve_Buttons_1_ID");
let DLP_Settings_Show_AutoServer_Button_1_ID = document.getElementById("DLP_Settings_Show_AutoServer_Button_1_ID");
let DLP_Settings_Legacy_Solve_Speed_1_ID = document.getElementById("DLP_Settings_Legacy_Solve_Speed_1_ID");
let DLP_Settings_Help_Us_Make_Better_Button_1_ID = document.getElementById("DLP_Settings_Help_Us_Make_Better_Button_1_ID");
let DLP_Settings_Auto_Update_Toggle_1_ID = document.getElementById("DLP_Settings_Auto_Update_Toggle_1_ID");
handleToggleClick(DLP_Settings_Show_Solve_Buttons_1_ID.querySelector('#DLP_Inset_Toggle_1_ID'), DLP_Settings_Var.showSolveButtons);
if (alpha) handleToggleClick(DLP_Settings_Show_AutoServer_Button_1_ID.querySelector('#DLP_Inset_Toggle_1_ID'), DLP_Settings_Var.showAutoServerButton);
else DLP_Settings_Show_AutoServer_Button_1_ID.remove();
handleToggleClick(DLP_Settings_Auto_Update_Toggle_1_ID.querySelector('#DLP_Inset_Toggle_1_ID'), DLP_Settings_Var.autoUpdate);
handleToggleClick(DLP_Settings_Help_Us_Make_Better_Button_1_ID.querySelector('#DLP_Inset_Toggle_1_ID'), DLP_Settings_Var.anonymousUsageData);
DLP_Settings_Legacy_Solve_Speed_1_ID.querySelector('#DLP_Inset_Input_1_ID').value = DLP_Settings_Var.solveSpeed;
DLP_Settings_Legacy_Solve_Speed_1_ID.querySelector('#DLP_Inset_Input_1_ID').addEventListener("input", function () {
this.value = settingsLegacySolveSpeedInputSanitizeValue(this.value, false);
});
DLP_Settings_Legacy_Solve_Speed_1_ID.querySelector('#DLP_Inset_Input_1_ID').addEventListener("blur", function () {
this.value = settingsLegacySolveSpeedInputSanitizeValue(this.value, true);
DLP_Settings_Var.solveSpeed = Number(this.value);
});
function settingsLegacySolveSpeedInputSanitizeValue(value, completeSanitization) {
value = value.replace(/[^0-9.,]/g, '');
let match = value.match(/[.,]/g);
if (match && match.length > 1) {
value = value.slice(0, value.lastIndexOf(match[match.length - 1]));
}
let decimalIndex = value.indexOf('.');
if (decimalIndex !== -1) {
value = value.slice(0, decimalIndex + 3);
}
let digitCount = value.replace(/[^0-9]/g, '').length;
if (digitCount > 3) {
value = value.replace(/(\d{3})\d+/, '$1');
}
if (/^0\d/.test(value) && !value.startsWith("0.")) {
value = value.replace(/^0+/, '0');
}
if (!completeSanitization) return value;
value = value.replace(',', '.');
value = parseFloat(value);
if (!isNaN(value)) {
if (value < 0.6) value = 0.6;
} else {
value = 0.8;
}
return value;
}
function handleToggleClick(button, state) {
try {
let iconToChange = button.querySelector(".DLP_Inset_Icon_1_ID");
iconToChange.style.transition = '0.4s';
void button.offsetWidth;
iconToChange.style.filter = 'blur(4px)';
iconToChange.style.opacity = '0';
if (state) {
button.classList.add('DLP_Toggle_Style_1_ON');
button.classList.remove('DLP_Toggle_Style_1_OFF');
} else {
button.classList.add('DLP_Toggle_Style_1_OFF');
button.classList.remove('DLP_Toggle_Style_1_ON');
}
setTimeout(() => {
iconToChange.textContent = state ? '' : '';
void button.offsetWidth;
iconToChange.style.filter = 'blur(0px)';
iconToChange.style.opacity = '1';
}, 400);
} catch (e) {
console.error(e);
}
}
DLP_Settings_Auto_Update_Toggle_1_ID.querySelector('#DLP_Inset_Toggle_1_ID').addEventListener('click', () => {
if (DLP_Settings_Toggle_Busy) return;
if (!greasyfork) {
DLP_Settings_Var.autoUpdate = !DLP_Settings_Var.autoUpdate;
DLP_Settings_Toggle_Busy = true;
handleToggleClick(DLP_Settings_Auto_Update_Toggle_1_ID.querySelector('#DLP_Inset_Toggle_1_ID'), DLP_Settings_Var.autoUpdate);
setTimeout(() => {
DLP_Settings_Toggle_Busy = false;
}, 800);
}
});
DLP_Settings_Show_Solve_Buttons_1_ID.querySelector('#DLP_Inset_Toggle_1_ID').addEventListener('click', () => {
if (DLP_Settings_Toggle_Busy) return;
DLP_Settings_Var.showSolveButtons = !DLP_Settings_Var.showSolveButtons;
DLP_Settings_Toggle_Busy = true;
handleToggleClick(DLP_Settings_Show_Solve_Buttons_1_ID.querySelector('#DLP_Inset_Toggle_1_ID'), DLP_Settings_Var.showSolveButtons);
setTimeout(() => {
DLP_Settings_Toggle_Busy = false;
}, 800);
});
DLP_Settings_Show_AutoServer_Button_1_ID.querySelector('#DLP_Inset_Toggle_1_ID').addEventListener('click', () => {
if (DLP_Settings_Toggle_Busy) return;
DLP_Settings_Var.showAutoServerButton = !DLP_Settings_Var.showAutoServerButton;
DLP_Settings_Toggle_Busy = true;
handleToggleClick(DLP_Settings_Show_AutoServer_Button_1_ID.querySelector('#DLP_Inset_Toggle_1_ID'), DLP_Settings_Var.showAutoServerButton);
setTimeout(() => {
DLP_Settings_Toggle_Busy = false;
}, 800);
});
DLP_Settings_Help_Us_Make_Better_Button_1_ID.querySelector('#DLP_Inset_Toggle_1_ID').addEventListener('click', () => {
if (DLP_Settings_Toggle_Busy) return;
if (alpha) return;
DLP_Settings_Var.anonymousUsageData = !DLP_Settings_Var.anonymousUsageData;
DLP_Settings_Toggle_Busy = true;
handleToggleClick(DLP_Settings_Help_Us_Make_Better_Button_1_ID.querySelector('#DLP_Inset_Toggle_1_ID'), DLP_Settings_Var.anonymousUsageData);
setTimeout(() => {
DLP_Settings_Toggle_Busy = false;
}, 800);
});
function confetti() {
let canvas = document.getElementById("DLP_Confetti_Canvas");
if (!canvas.confettiInitialized) {
let ctx = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let cx = ctx.canvas.width / 2;
let cy = ctx.canvas.height / 2;
canvas.ctx = ctx;
canvas.cx = cx;
canvas.cy = cy;
canvas.confetti = [];
canvas.animationId = null;
canvas.confettiInitialized = true;
let resizeCanvas = () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.cx = canvas.ctx.canvas.width / 2;
canvas.cy = canvas.ctx.canvas.height / 2;
};
const resizeObserver = new ResizeObserver(() => {
resizeCanvas();
});
resizeObserver.observe(canvas);
let render = () => {
canvas.ctx.clearRect(0, 0, canvas.width, canvas.height);
canvas.confetti.forEach((confetto, index) => {
let width = confetto.dimensions.x * confetto.scale.x;
let height = confetto.dimensions.y * confetto.scale.y;
canvas.ctx.translate(confetto.position.x, confetto.position.y);
canvas.ctx.rotate(confetto.rotation);
confetto.velocity.x -= confetto.velocity.x * drag;
confetto.velocity.y = Math.min(
confetto.velocity.y + gravity,
terminalVelocity,
);
confetto.velocity.x +=
Math.random() > 0.5 ? Math.random() : -Math.random();
confetto.position.x += confetto.velocity.x;
confetto.position.y += confetto.velocity.y;
if (confetto.position.y >= canvas.height) canvas.confetti.splice(index, 1);
if (confetto.position.x > canvas.width) confetto.position.x = 0;
if (confetto.position.x < 0) confetto.position.x = canvas.width;
canvas.ctx.fillStyle = confetto.color.front;
canvas.ctx.fillRect(-width / 2, -height / 2, width, height);
canvas.ctx.setTransform(1, 0, 0, 1, 0, 0);
});
canvas.animationId = window.requestAnimationFrame(render);
};
render();
}
const gravity = 0.5;
const terminalVelocity = 10;
const drag = 0.01;
const colors = [
{ front: "#FF2D55", back: "#FF2D55" },
{ front: "#FF9500", back: "#FF9500" },
{ front: "#FFCC00", back: "#FFCC00" },
{ front: "#34C759", back: "#34C759" },
{ front: "#5AC8FA", back: "#5AC8FA" },
{ front: "#007AFF", back: "#007AFF" },
{ front: "#5856D6", back: "#5856D6" },
{ front: "#AF52DE", back: "#AF52DE" },
];
const confettiSizeRange = {
min: 5,
max: 15
};
let randomRange = (min, max) => Math.random() * (max - min) + min;
const confettiCount = 400;
for (let i = 0; i < confettiCount; i++) {
canvas.confetti.push({
color: colors[Math.floor(randomRange(0, colors.length))],
dimensions: {
x: randomRange(confettiSizeRange.min, confettiSizeRange.max),
y: randomRange(confettiSizeRange.min, confettiSizeRange.max),
},
position: {
x: randomRange(0, canvas.width),
y: canvas.height - 1,
},
rotation: randomRange(0, 2 * Math.PI),
scale: {
x: 1,
y: 1,
},
velocity: {
x: randomRange(-25, 25),
y: randomRange(0, -50),
},
});
}
}
function playHaptic(type) {
function isIPhone() {
return (/iPhone/.test(navigator.userAgent) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1));
}
function isAndroid() {
return /Android/.test(navigator.userAgent) && 'vibrate' in navigator;
}
// iOS haptic trick
function iosHaptic() {
try {
const label = document.createElement("label");
label.ariaHidden = "true";
label.style.display = "none";
const input = document.createElement("input");
input.type = "checkbox";
input.setAttribute("switch", "");
label.appendChild(input);
document.head.appendChild(label);
label.click();
document.head.removeChild(label);
} catch {
console.log("iOS haptic error");
}
}
// Android/Browser Vibration API
function androidHaptic(pattern) {
try {
if (navigator.vibrate) {
navigator.vibrate(pattern);
}
} catch {
console.log("Android haptic error");
}
}
if (isIPhone()) {
if (type === "success") {
iosHaptic();
setTimeout(iosHaptic, 80);
} else if (type === "warning" || type === "error") {
iosHaptic();
setTimeout(iosHaptic, 100);
setTimeout(iosHaptic, 200);
setTimeout(iosHaptic, 280);
} else {
iosHaptic();
}
} else if (isAndroid()) {
if (type === "success") {
androidHaptic([30, 40, 30]);
} else if (type === "fail" || type === "error") {
androidHaptic([30, 40, 30, 40, 30]);
} else {
androidHaptic(30);
}
} else if (navigator.vibrate) {
// fallback for other platforms supporting Vibration API
if (type === "success") {
navigator.vibrate([30, 40, 30]);
} else if (type === "fail" || type === "error") {
navigator.vibrate([30, 40, 30, 40, 30]);
} else {
navigator.vibrate(30);
}
}
// else: no-op
}
async function generateEarnKey() {
const endpoint = `https://api.duolingopro.net/earn/connect/generate`;
try {
const response = await fetch(endpoint, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${document.cookie.split(';').find(cookie => cookie.includes('jwt_token')).split('=')[1]}`
},
});
if (response.ok) {
const data = await response.json();
if (data.earn_key) {
console.log('Earn Key:', data.earn_key);
return data.earn_key;
} else {
throw new Error('Earn key not found in the response.');
}
} else if (response.status === 401) {
throw new Error('Unauthorized: Invalid or missing authentication token.');
} else if (response.status === 429) {
throw new Error('Rate limit exceeded: Please try again later.');
} else if (response.status === 500) {
const errorData = await response.json();
throw new Error(`Server Error: ${errorData.detail || 'An unexpected error occurred.'}`);
} else {
const errorData = await response.json();
throw new Error(`Error ${response.status}: ${errorData.detail || 'An unexpected error occurred.'}`);
}
} catch (error) {
console.error('Error generating earn key:', error.message);
throw error;
}
}
let earnButtonAssignedLink = false;
document.querySelectorAll("#DLP_Main_Earn_Button_1_ID, #DLP_Secondary_Earn_Button_1_ID").forEach(button => {
button.addEventListener('click', () => {
button.style.opacity = '0.5';
button.style.pointerEvents = 'none';
generateEarnKey()
.then(earnKey => {
console.log('Successfully retrieved earn key:', earnKey);
button.setAttribute("onclick", `window.open('${serverURL}/earn/connect/link/${earnKey}', '_blank');`);
if (!earnButtonAssignedLink) {
earnButtonAssignedLink = true;
window.open(serverURL + "/earn/connect/link/" + earnKey, "_blank");
}
})
.catch(error => {
console.error('Failed to retrieve earn key:', error.message);
})
.finally(() => {
button.style.opacity = '';
button.style.pointerEvents = '';
});
});
});
let currentChatId = 1;
let allTexts = {}; // { [chatId]: text }
let allAttachments = {}; // { [chatId]: [ {id, file}, … ] }
function setupSupportPage() {
const container = document.getElementById("DLP_Main_Box_Divider_11_ID");
const chatBox = container.querySelector('.DLP_Chat_Box_1_ID_1');
const attachmentVisualButton = container.querySelector('#DLP_Inset_Button_1_ID');
const sendButton = container.querySelector("#DLP_Inset_Button_2_ID");
const attachmentInput = container.querySelector("#DLP_Attachment_Input_1");
const messageInput = container.querySelector("#DLP_Inset_Input_1_ID");
const activeContainer = container.querySelector('.DLP_Input_Style_1_Active');
let messageSendInProgress = false;
function resetMessageInputState() {
messageInput.value = '';
messageInput.style.height = '1.2em';
if (activeContainer) activeContainer.style.height = '48px';
messageInput.scrollTop = 0;
checkSendButton();
}
function setupCard() {
let card = document.getElementById("DLP_Main_Box_Divider_11_ID").querySelector("#DLP_Inset_Card_1");
let cardExpanded = false;
let cardAnimating = false;
let descriptionText = card.querySelectorAll(':scope > .DLP_Text_Style_1');
card.addEventListener('click', () => {
if (cardAnimating) return;
cardAnimating = true;
if (!cardExpanded) {
let cardHeight = card.offsetHeight;
let textHeight = false;
if (descriptionText.length > 0) {
textHeight = Array.from(descriptionText).map(() => "0");
descriptionText.forEach(element => {
element.style.display = 'block';
element.style.height = 'auto';
});
}
void card.offsetHeight;
let newCardHeight = card.offsetHeight;
let newTextHeight = false;
if (descriptionText.length > 0) {
newTextHeight = Array.from(descriptionText).map(element => element.offsetHeight);
}
if (descriptionText.length > 0) {
descriptionText.forEach(element => {
element.style.height = '0px';
});
}
card.style.height = `${cardHeight}px`;
void card.offsetHeight;
if (descriptionText.length > 0) {
descriptionText.forEach(element => {
element.style.filter = 'blur(0px)';
element.style.opacity = '1';
});
}
card.style.height = `${newCardHeight}px`;
if (descriptionText.length > 0) {
descriptionText.forEach(element => {
element.style.height = `${newTextHeight[Array.from(descriptionText).indexOf(element)]}px`;
});
}
card.querySelector('.DLP_HStack_6').style.opacity = '0.5';
card.querySelector('.DLP_HStack_6').lastElementChild.style.transition = 'all 0.4s cubic-bezier(0.16, 1, 0.32, 1)';
card.querySelector('.DLP_HStack_6').lastElementChild.style.transform = 'rotate(90deg)';
setTimeout(() => {
card.style.height = 'auto';
if (descriptionText.length > 0) {
descriptionText.forEach(element => {
element.style.height = 'auto';
});
}
cardExpanded = true;
cardAnimating = false;
}, 400);
} else {
let cardHeight = card.offsetHeight;
let textHeight = false;
if (descriptionText.length > 0) {
textHeight = Array.from(descriptionText).map(element => element.offsetHeight);
descriptionText.forEach(element => {
element.style.display = 'none';
});
}
void card.offsetHeight;
let newCardHeight = card.offsetHeight;
let newTextHeight = false;
if (descriptionText.length > 0) {
newTextHeight = Array.from(descriptionText).map(() => "0");
descriptionText.forEach(element => {
element.style.display = 'block';
element.style.height = `${textHeight[Array.from(descriptionText).indexOf(element)]}px`;
});
}
card.style.height = `${cardHeight}px`;
void card.offsetHeight;
if (descriptionText.length > 0) {
descriptionText.forEach(element => {
element.style.filter = 'blur(4px)';
element.style.opacity = '0';
});
}
card.style.height = `${newCardHeight}px`;
if (descriptionText.length > 0) {
descriptionText.forEach(element => {
element.style.height = '0px';
});
}
card.querySelector('.DLP_HStack_6').style.opacity = '1';
card.querySelector('.DLP_HStack_6').lastElementChild.style.transition = 'all 0.4s cubic-bezier(0.16, 1, 0.32, 1)';
card.querySelector('.DLP_HStack_6').lastElementChild.style.transform = 'rotate(0deg)';
setTimeout(() => {
card.style.height = 'auto';
if (descriptionText.length > 0) {
descriptionText.forEach(element => {
element.style.display = 'none';
});
}
cardExpanded = false;
cardAnimating = false;
}, 400);
}
});
}
setupCard();
function markTempMessageFailed(tempId) {
const tempState = pendingTempMessages.get(tempId);
if (tempState) {
tempState.sendFailed = true;
}
const tempElements = chatBox.querySelectorAll(`[data-is-temp="${tempId}"]`);
tempElements.forEach(element => {
element.style.animation = '';
element.style.color = 'rgba(var(--DLP-pink))';
});
}
function setupSendButton() {
sendButton.addEventListener('click', async () => {
if (messageSendInProgress) return;
if (!storageLocal.chatKey || storageLocal.chatKey.length === 0) {
if (container?.querySelector('#DLP_Inset_Group_3')?.style.display !== 'none') container.querySelector('#DLP_Inset_Group_3').style.display = 'none';
if (chatBox?.style.display === 'none') chatBox.style.display = 'flex';
try {
let response = await fetch(apiURL + "/chats/create", {
method: "GET",
headers: {
"Authorization": `Bearer ${document.cookie.split(';').find(cookie => cookie.includes('jwt_token')).split('=')[1]}`
}
});
let data = await response.json();
console.log("Server Response:", data);
storageLocal.chatKey = [data.chat_key];
saveStorageLocal();
} catch (error) {
console.error("Fetch error:", error);
}
}
let formData = new FormData();
formData.append("message", messageInput.value);
let fileUrls = [];
for (const attachment of allAttachments[currentChatId] ?? []) {
const file = attachment.file;
console.log("attaching", file);
formData.append("files", file);
const url = URL.createObjectURL(file);
console.log("url", url);
fileUrls.push(url);
}
let chatTempSendNumber = chatTempSendList.length ? chatTempSendList[chatTempSendList.length - 1] + 1 : 1;
const tempMessageId = `temp-${chatTempSendNumber}`;
let tempData = {
"accent": '#007AFF',
"author": userBioData.username,
"edited": false,
"files": fileUrls,
"message": messageInput.value,
"profile_picture": userBioData.profile_picture,
"role": "You",
"send_time": Number(Date.now()),
"message_id": tempMessageId
};
createMessage(tempData, false, chatTempSendNumber);
pendingTempMessages.set(chatTempSendNumber, {
...tempData,
files: [...tempData.files]
});
chatTempSendList.push(chatTempSendNumber);
chatBox.scrollTop = chatBox.scrollHeight;
allAttachments[currentChatId] = [];
renderAttachmentsPreview();
messageSendInProgress = true;
checkSendButton();
resetMessageInputState();
try {
let response = await fetch(apiURL + "/chats/send_message", {
method: "POST",
headers: alpha
? {
'Authorization': `Bearer ${document.cookie.split(';').find(cookie => cookie.includes('jwt_token')).split('=')[1]}`,
'X-Chat-Key': `${storageLocal.chatKey[0]}`
}
: {
'X-Chat-Key': `${storageLocal.chatKey[0]}`
},
body: formData
});
let responseData = await response.json();
console.log("Server Response:", responseData);
const isNewMessageFormat = response.ok && responseData && typeof responseData === 'object' && Object.prototype.hasOwnProperty.call(responseData, 'message_id');
if (isNewMessageFormat) {
const wasAtBottom = Math.abs(chatBox.scrollHeight - (chatBox.scrollTop + chatBox.clientHeight)) < 5;
chatBox.querySelectorAll(`[data-is-temp="${chatTempSendNumber}"]`).forEach(element => {
element.remove();
});
createMessage(responseData);
const tempIndex = chatTempSendList.indexOf(chatTempSendNumber);
if (tempIndex !== -1) {
chatTempSendList.splice(tempIndex, 1);
}
pendingTempMessages.delete(chatTempSendNumber);
if (wasAtBottom) {
chatBox.scrollTop = chatBox.scrollHeight;
}
} else {
if (responseData?.status === false && responseData?.notification) {
showNotification(responseData.notification.icon, responseData.notification.head, responseData.notification.body, responseData.notification.duration);
} else {
showNotification("error", "Send Failed", "We could not verify that your message was delivered. Please try again.", 8);
}
markTempMessageFailed(chatTempSendNumber);
}
} catch (error) {
console.error("Fetch error:", error);
markTempMessageFailed(chatTempSendNumber);
} finally {
messageSendInProgress = false;
checkSendButton();
}
});
}
setupSendButton();
function setupTextInput() {
const sendButton = container.querySelector("#DLP_Inset_Button_2_ID");
resetMessageInputState();
messageInput.addEventListener('input', function () {
messageInput.style.height = '1.2em';
const lineHeight = parseInt(getComputedStyle(messageInput).lineHeight);
const maxRows = 5;
const maxHeight = lineHeight * maxRows;
const newHeight = Math.min((messageInput.scrollHeight - 32), maxHeight);
messageInput.style.height = newHeight + 'px';
if (newHeight < 20) {
activeContainer.style.height = '48px';
} else {
activeContainer.style.height = (newHeight + 32) + 'px';
}
checkSendButton();
});
messageInput.addEventListener('keydown', function (event) {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault();
if (sendButton.style.pointerEvents !== 'none') {
sendButton.click();
}
}
});
}
setupTextInput();
let nextAttachmentId = 0;
let attachmentDropBoxExpanded = false;
const MAX_ATTACHMENT_FILE_SIZE = 50 * 1024 * 1024; // 50 MB
const MAX_ATTACHMENT_FILE_COUNT = 3;
function setupAttachmentsInput() {
const attachmentBox = container.querySelector('#DLP_Attachment_Preview_Parent');
const attachmentBoxDrop = attachmentBox.querySelector('.DLP_Attachment_Box_Drop_1');
attachmentBoxDrop.addEventListener('dragenter', event => {
event.preventDefault();
//attachmentBoxDrop.style.outline = '2px solid rgba(var(--DLP-blue), 0.20)';
attachmentBoxDrop.firstElementChild.style.opacity = '1';
});
attachmentBoxDrop.addEventListener('dragleave', event => {
event.preventDefault();
if (attachmentBoxDrop.contains(event.relatedTarget)) return;
//attachmentBoxDrop.style.outline = '2px dashed rgba(var(--DLP-blue), 0.20)';
attachmentBoxDrop.firstElementChild.style.opacity = '0.5';
});
window.addEventListener('dragover', (event) => {
event.preventDefault();
if (attachmentInput.disabled) return;
if (event.dataTransfer && event.dataTransfer.types.includes('Files')) {
if (attachmentBox.style.display === 'none') {
attachmentBox.style.display = '';
}
[...attachmentBox.children].forEach(child => {
if (child !== attachmentBoxDrop) {
child.style.display = 'none';
}
});
attachmentBoxDrop.style.display = '';
}
});
window.addEventListener('dragleave', (event) => {
if (event.clientX <= 0 || event.clientY <= 0 || event.clientX >= window.innerWidth || event.clientY >= window.innerHeight) {
if (attachmentBox.children.length === 1 && attachmentBox.children[0] === attachmentBoxDrop) {
attachmentBox.style.display = 'none';
}
[...attachmentBox.children].forEach(child => {
if (child !== attachmentBoxDrop) {
child.style.display = '';
}
});
attachmentBoxDrop.style.display = 'none';
//attachmentBoxDrop.style.outline = '2px dashed a(var(--DLP-blue), 0.20)';
attachmentBoxDrop.firstElementChild.style.opacity = '0.5';
}
});
window.addEventListener('drop', (event) => {
event.preventDefault();
[...attachmentBox.children].forEach(child => {
if (child !== attachmentBoxDrop) {
child.style.display = '';
}
});
attachmentBoxDrop.style.display = 'none';
//attachmentBoxDrop.style.outline = '2px dashed rgba(var(--DLP-blue), 0.20)';
attachmentBoxDrop.firstElementChild.style.opacity = '0.5';
});
attachmentBoxDrop.addEventListener('drop', (event) => {
event.preventDefault();
attachmentBoxDrop.style.display = 'none';
const selectedFiles = Array.from(event.dataTransfer.files);
triggerInputAttachments(selectedFiles);
});
attachmentVisualButton.addEventListener('click', () => attachmentInput.click());
attachmentInput.addEventListener('change', (event) => {
const selectedFiles = Array.from(event.target.files);
triggerInputAttachments(selectedFiles);
});
}
setupAttachmentsInput();
function triggerInputAttachments(selectedFiles) {
if (!allAttachments[currentChatId]) {
allAttachments[currentChatId] = [];
}
const validFiles = [];
selectedFiles.forEach(file => {
if (file.size > MAX_ATTACHMENT_FILE_SIZE) {
showNotification("warning", "File Too Large", `${file.name} is over 10 MB, please choose a smaller file.`, 10);
} else {
validFiles.push(file);
}
});
const remainingSlots = MAX_ATTACHMENT_FILE_COUNT - allAttachments[currentChatId]?.length;
if (validFiles.length > remainingSlots) {
showNotification("warning", "Too Many Files", `You can only attach up to ${MAX_ATTACHMENT_FILE_COUNT} files at once.`, 10);
validFiles.length = remainingSlots;
}
validFiles.forEach(file => {
allAttachments[currentChatId]?.push({ id: String(nextAttachmentId++), file }); // wrap each in an {id, file} and append
});
updateAttachmentsInput();
renderAttachmentsPreview();
checkSendButton();
attachmentInput.value = '';
}
function updateAttachmentsInput() {
const dt = new DataTransfer();
allAttachments[currentChatId]?.forEach(a => dt.items.add(a.file));
attachmentInput.files = dt.files;
}
function removeAttachmentById(id) {
allAttachments[currentChatId] = allAttachments[currentChatId]?.filter(a => a.id !== id);
updateAttachmentsInput();
renderAttachmentsPreview();
checkSendButton();
}
function renderAttachmentsPreview() {
const attachmentBox = container.querySelector('#DLP_Attachment_Preview_Parent');
const attachmentBoxDrop = attachmentBox.querySelector('.DLP_Attachment_Box_Drop_1');
const currentIds = new Set(allAttachments[currentChatId]?.map(a => a.id));
// 1) remove deleted attachments from the DOM
Array.from(attachmentBox.children).forEach(child => {
const childId = child.getAttribute('data-id');
if (!currentIds.has(childId) && child !== attachmentBoxDrop) {
attachmentBox.removeChild(child);
}
});
// 2) add new attachments to the DOM
allAttachments[currentChatId]?.forEach(({ id, file }) => {
if (attachmentBox.querySelector(`[data-id="${id}"]`)) return;
const url = URL.createObjectURL(file);
const box = document.createElement('div');
box.className = 'DLP_Attachment_Box_1';
box.setAttribute('data-id', id);
box.style.position = 'relative';
let media;
if (file.type.startsWith('image/')) {
media = document.createElement('img');
media.src = url;
media.className = 'DLP_Attachment_Box_1_Content';
} else if (file.type.startsWith('video/')) {
media = document.createElement('video');
media.src = url;
media.autoplay = true;
media.muted = true;
media.loop = true;
media.className = 'DLP_Attachment_Box_1_Content';
} else {
media = document.createElement('div');
media.style.display = 'flex';
media.style.width = '100%';
media.style.height = '100%';
media.style.paddingTop = '6px';
media.style.flexDirection = 'column';
media.style.justifyContent = 'center';
media.style.alignItems = 'center';
media.style.gap = '6px';
media.style.flexShrink = '0';
mediaChild1 = document.createElement('p');
mediaChild1.className = 'DLP_Text_Style_1 DLP_NoSelect';
mediaChild1.style.fontSize = '24px';
mediaChild1.textContent = '';
media.appendChild(mediaChild1);
mediaChild2 = document.createElement('p');
mediaChild2.className = 'DLP_Text_Style_1 DLP_NoSelect';
mediaChild2.style.opacity = '0.5';
mediaChild2.textContent = 'File';
//mediaChild2.textContent = file.name;
media.appendChild(mediaChild2);
}
// Create and append delete button
const hover = document.createElement('div');
hover.className = 'DLP_Attachment_Box_1_Hover';
hover.style.display = 'none';
box.addEventListener('mouseenter', () => {
hover.style.display = '';
});
box.addEventListener('mouseleave', () => {
hover.style.display = 'none';
});
const btn = document.createElement('p');
btn.className = 'DLP_Text_Style_1 DLP_Magnetic_Hover_1 DLP_NoSelect';
if ((file.type.startsWith('image/') || file.type.startsWith('video/'))) btn.textContent = '';
else btn.textContent = '';
btn.addEventListener('click', () => removeAttachmentById(id));
hover.appendChild(btn);
box.appendChild(media);
box.appendChild(hover);
attachmentBox.appendChild(box);
if (false) {
if (file.type.startsWith('image/')) {
media.addEventListener('load', () => updateContrast(box));
if (media.complete) updateContrast(box);
} else if (file.type.startsWith('video/')) {
media.addEventListener('loadeddata', () => {
const iv = setInterval(() => {
if (!document.contains(media)) {
clearInterval(iv);
} else {
updateContrast(box);
}
}, 250);
updateContrast(box);
});
} else {
updateContrast(box);
}
}
});
// Show or hide the attachmentBox and adjust padding/navigation
if ((allAttachments[currentChatId]?.length ?? 0) === 0 && attachmentDropBoxExpanded) {
attachmentBox.style.display = 'none';
attachmentDropBoxExpanded = false;
chatBox.scrollTop = chatBox.scrollHeight;
//const nav = document.querySelector('#DLP_Main_Navigation_Box_5_ID .DLP_Col.DLP_Fill_Col.DLP_Fill_Row.DLP_Gap_8');
//nav.style.paddingBottom = `${parseFloat(getComputedStyle(nav).paddingBottom) - 104}px`;
} else if (allAttachments[currentChatId]?.length > 0 && !attachmentDropBoxExpanded) {
attachmentBox.style.display = '';
attachmentDropBoxExpanded = true;
chatBox.scrollTop = chatBox.scrollHeight;
//const nav = document.querySelector('#DLP_Main_Navigation_Box_5_ID .DLP_Col.DLP_Fill_Col.DLP_Fill_Row.DLP_Gap_8');
//nav.style.paddingBottom = `${parseFloat(getComputedStyle(nav).paddingBottom) + 104}px`;
//void container.offsetHeight;
//document.querySelector('#DLP_Main_Navigation_Box_5_ID').scrollTop += 104;
}
// Disable input if there are too many files
if (allAttachments[currentChatId]?.length >= MAX_ATTACHMENT_FILE_COUNT) {
attachmentInput.disabled = true;
attachmentVisualButton.style.opacity = '0.5';
attachmentVisualButton.style.pointerEvents = 'none';
} else {
attachmentInput.disabled = false;
attachmentVisualButton.style.opacity = '';
attachmentVisualButton.style.pointerEvents = '';
}
}
function setupCreateNewChatButton() {
let theButton = container.querySelector('#DLP_Inset_Group_2').querySelector('#DLP_Inset_Button_3_ID');
theButton.addEventListener('click', async () => {
theButton.style.opacity = "0.5";
theButton.style.pointerEvents = "none";
try {
let response = await fetch(apiURL + "/chats/create", {
method: "GET",
headers: {
"Authorization": `Bearer ${document.cookie.split(';').find(cookie => cookie.includes('jwt_token')).split('=')[1]}`
}
});
let data = await response.json();
console.log("Server Response:", data);
storageLocal.chatKey = [data.chat_key];
saveStorageLocal();
chatBox.innerHTML = '';
container.querySelector('#DLP_Inset_Group_1').style.display = "";
container.querySelector('#DLP_Inset_Group_2').style.display = "none";
theButton.style.opacity = "";
theButton.style.pointerEvents = "";
} catch (error) {
console.error("Fetch error:", error);
theButton.style.opacity = "";
theButton.style.pointerEvents = "";
}
});
}
setupCreateNewChatButton();
function checkSendButton() {
if (messageSendInProgress) {
sendButton.style.opacity = "0.5";
sendButton.style.pointerEvents = "none";
return;
}
if (messageInput.value.trim() !== "" || (allAttachments[currentChatId]?.length ?? 0) > 0) {
sendButton.style.opacity = "";
sendButton.style.pointerEvents = "";
} else {
sendButton.style.opacity = "0.5";
sendButton.style.pointerEvents = "none";
}
}
checkSendButton();
}
setupSupportPage();
const originalPlay = HTMLAudioElement.prototype.play;
function muteTab(value) {
HTMLAudioElement.prototype.play = function () {
if (value) {
this.muted = true;
} else {
this.muted = false;
}
return originalPlay.apply(this, arguments);
};
}
document.addEventListener('keydown', function(event) {
if ((event.ctrlKey || event.metaKey) && event.key === 'Enter') {
if (event.shiftKey) {
solving();
} else {
solve();
}
}
});
let currentQuestionId = null;
let hasLoggedForCurrent = false;
async function logOnce(flag, sol, dom) {
// flag: 1 = solved, 2 = wrong, 3 = stuck
if (!hasLoggedForCurrent) {
if (alpha) {
console.log(flag);
//console.log(sol);
//console.log(dom);
console.log(sol.challengeGeneratorIdentifier.generatorId);
}
hasLoggedForCurrent = true;
if (storageLocal.settings.anonymousUsageData) {
if (flag === 2) showNotification("error", "Legacy Solved Incorrectly", "Legacy has detected that it solved a question incorrectly. A report has been made under ID: " + sol.challengeGeneratorIdentifier.generatorId, 10);
else if (flag === 3) showNotification("error", "Legacy is Stuck", "Legacy has detected that it is stuck on a question. A report has been made under ID: " + sol.challengeGeneratorIdentifier.generatorId, 10);
const payload = {
version: versionFormal,
random: storageLocal.random16,
flag: flag,
sol: sol,
dom: dom.outerHTML
};
console.log(sol);
const response = await fetch("https://api.duolingopro.net/analytics/legacy", {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
} else {
if (flag === 2) showNotification("error", "Legacy Solved Incorrectly", "Legacy has detected that it solved a question incorrectly. Turn on share anonymous usage data in settings to help us fix this bug.", 10);
else if (flag === 3) showNotification("error", "Legacy is Stuck", "Legacy has detected that it is stuck on a question. Turn on share anonymous usage data in settings to help us fix this bug.", 10);
}
}
}
function updateSolveButtonText(text) {
document.getElementById("solveAllButton").innerText = text;
}
function solving(value) {
if (value === "start") isAutoMode = true;
else if (value === "stop") isAutoMode = false;
else isAutoMode = !isAutoMode;
updateSolveButtonText(isAutoMode ? systemText[systemLanguage][102] : systemText[systemLanguage][101]);
solvingIntervalId = isAutoMode ? (function() {
let initialUrl = window.location.href;
return setInterval(function() {
if (window.location.href !== initialUrl) {
isAutoMode = false;
clearInterval(solvingIntervalId);
updateSolveButtonText(isAutoMode ? systemText[systemLanguage][102] : systemText[systemLanguage][101]);
return;
} else {
solve();
}
}, storageLocal.settings.solveSpeed * 1000);
})() : clearInterval(solvingIntervalId);
}
let hcwNIIOdaQqCZRDL = false;
function solve() {
const practiceAgain = document.querySelector('[data-test="player-practice-again"]');
const sessionCompleteSlide = document.querySelector('[data-test="session-complete-slide"]');
const selectorsForSkip = [
'[data-test="practice-hub-ad-no-thanks-button"]',
'.vpDIE',
'[data-test="plus-no-thanks"]',
'._1N-oo._36Vd3._16r-S._1ZBYz._23KDq._1S2uf.HakPM',
'._8AMBh._2vfJy._3Qy5R._28UWu._3h0lA._1S2uf._1E9sc',
'._1Qh5D._36g4N._2YF0P._28UWu._3h0lA._1S2uf._1E9sc',
'[data-test="story-start"]',
'._3bBpU._1x5JY._1M9iF._36g4N._2YF0P.T7I0c._2EnxW.MYehf',
'._2V6ug._1ursp._7jW2t._28UWu._3h0lA._1S2uf._1E9sc', // No Thanks Legendary Button
'._1rcV8._1VYyp._1ursp._7jW2t._1gKir', // Language Score
'._2V6ug._1ursp._7jW2t._3zgLG' // Create Profile Later
];
selectorsForSkip.forEach(selector => {
const element = document.querySelector(selector);
if (element) element.click();
});
const status = storageSession.legacy.status;
const type = status ? storageSession.legacy[status]?.type : null;
let amount;
if (sessionCompleteSlide !== null && isAutoMode && storageSession.legacy.status) {
if (!hcwNIIOdaQqCZRDL) {
hcwNIIOdaQqCZRDL = true;
if (type === 'lesson') {
storageSession.legacy[status].amount -= 1;
saveStorageSession();
(((storageLocal.stats ??= {}).legacy ??= {})[status] ??= { lessons: 0 }).lessons++;
saveStorageLocal();
amount = status ? storageSession.legacy[status]?.amount : null;
if (amount > 0) {
if (practiceAgain !== null) {
practiceAgain.click();
return;
} else {
location.reload();
}
} else {
storageSession.legacy[status].amount = 0;
storageSession.legacy.status = false;
saveStorageSession();
window.location.href = "https://duolingo.com";
return;
}
} else if (type === 'xp') {
storageSession.legacy[status].amount -= findSubReact(document.getElementsByClassName("_1XNQX")[0]).xpGoalSessionProgress.totalXpThisSession;
saveStorageSession();
(((storageLocal.stats ??= {}).legacy ??= {})[status] ??= { lessons: 0 }).lessons++;
saveStorageLocal();
amount = status ? storageSession.legacy[status]?.amount : null;
if (amount > 0) {
if (practiceAgain !== null) {
practiceAgain.click();
return;
} else {
location.reload();
}
} else {
storageSession.legacy[status].amount = 0;
storageSession.legacy.status = false;
saveStorageSession();
window.location.href = "https://duolingo.com";
return;
}
} else if (type === 'infinity') {
(((storageLocal.stats ??= {}).legacy ??= {})[status] ??= { lessons: 0 }).lessons++;
saveStorageLocal();
if (practiceAgain !== null) {
practiceAgain.click();
return;
} else {
location.reload();
}
}
}
}
try {
window.sol = findReact(document.getElementsByClassName(findReactMainElementClass)[0]).props.currentChallenge;
} catch (error) {
console.log(error);
//let next = document.querySelector('[data-test="player-next"]');
//if (next) {
// next.click();
//}
//return;
}
//if (!window.sol) {
// return;
//}
let challengeType;
if (window.sol) {
challengeType = determineChallengeType();
} else {
challengeType = 'error';
nextClickFunc();
}
let questionKey;
if (window.sol && window.sol.id) {
questionKey = window.sol.id;
} else if (window.sol) {
// Fallback if no 'id' property: use type + prompt
questionKey = JSON.stringify({
type: window.sol.type,
prompt: window.sol.prompt || ''
});
} else {
questionKey = null;
}
if (questionKey !== currentQuestionId) {
currentQuestionId = questionKey;
hasLoggedForCurrent = false;
}
if (challengeType === 'error') {
nextClickFunc();
} else if (challengeType) {
if (debug) document.getElementById("solveAllButton").innerText = challengeType;
handleChallenge(challengeType);
nextClickFunc();
} else {
nextClickFunc();
}
}
function nextClickFunc() {
setTimeout(function () {
try {
let nextButtonNormal = document.querySelector('[data-test="player-next"]');
let storiesContinueButton = document.querySelector('[data-test="stories-player-continue"]');
let storiesDoneButton = document.querySelector('[data-test="stories-player-done"]');
let nextButtonAriaValueNormal = nextButtonNormal ? nextButtonNormal.getAttribute('aria-disabled') : null;
let nextButtonAriaValueStoriesContinue = storiesContinueButton ? storiesContinueButton.disabled : null;
let nextButton = nextButtonNormal || storiesContinueButton || storiesDoneButton;
let nextButtonAriaValue = nextButtonAriaValueNormal || nextButtonAriaValueStoriesContinue || storiesDoneButton;
if (nextButton) {
if (nextButtonAriaValue === 'true' || nextButtonAriaValue === true) {
requestAnimationFrame(() => {
if (document.querySelectorAll('._35QY2._3jIlr.f2zGP._18W4a.xtPuL').length > 0) {
} else {
if (nextButtonAriaValue === 'true') {
//console.log('The next button is disabled.');
logOnce(3, window.sol, document.querySelector('.RMEuZ._1GVfY'));
}
}
});
} else if (nextButtonAriaValue === 'false' || nextButtonAriaValue === false) {
nextButton.click();
requestAnimationFrame(() => {
if (document.querySelector('[data-test="player-next"]').classList.contains('_2oGJR')) { // _1rcV8 _1VYyp _1ursp _7jW2t _3DbUj _2VWgj _3S8jJ
logOnce(1, window.sol, document.querySelector('.RMEuZ._1GVfY'));
const status = storageSession.legacy.status;
(((storageLocal.stats ??= {}).legacy ??= {})[status] ??= { questions: 0 }).questions++;
saveStorageLocal();
//mainSolveStatistics('question', 1);
if (isAutoMode) {
setTimeout(function () {
nextButton.click();
}, 100);
}
} else if (document.querySelector('[data-test="player-next"]').classList.contains('_3S8jJ')) {
logOnce(2, window.sol, document.querySelector('.RMEuZ._1GVfY'));
//if (solveSpeed < 0.6) {
// solveSpeed = 0.6;
// localStorage.setItem('duopro.autoSolveDelay', solveSpeed);
//}
} else {
console.log('The element does not have the class ._9C_ii or .NAidc or the element is not found.');
}
});
} else {
console.log('The aria-disabled attribute is not set or has an unexpected value.');
//notificationCall("what", "Idk");
nextButton.click();
}
} else {
console.log('Element with data-test="player-next" or data-test="stories-player-continue" not found.');
}
} catch (error) { }
}, 100);
}
function LhEqEHHc() {
const randomImageValue = Math.random().toString(36).substring(2, 15);
//questionErrorLogs(findReact(document.getElementsByClassName(findReactMainElementClass)[0]).props.currentChallenge, document.body.innerHTML, randomImageValue);
//const challengeAssistElement = document.querySelector('[data-test="challenge challenge-assist"]');
const challengeAssistElement = document.querySelector('._3x0ok');
if (challengeAssistElement) {
} else {
console.log('Element not found');
}
}
function determineChallengeType() {
try {
//console.log(window.sol);
if (document.getElementsByClassName("FmlUF").length > 0) {
// Story
if (window.sol.type === "arrange") {
return "Story Arrange"
} else if (window.sol.type === "multiple-choice" || window.sol.type === "select-phrases") {
return "Story Multiple Choice"
} else if (window.sol.type === "point-to-phrase") {
return "Story Point to Phrase"
} else if (window.sol.type === "match") {
return "Story Pairs"
}
} else {
// Lesson
if (document.querySelectorAll('[data-test*="challenge-speak"]').length > 0) {
hcwNIIOdaQqCZRDL = false;
return 'Challenge Speak';
} else if (window.sol.type === 'tapCompleteTable') {
return 'Tap Complete Table';
} else if (window.sol.type === 'typeCloze') {
return 'Type Cloze';
} else if (window.sol.type === 'typeClozeTable') {
return 'Type Cloze Table';
} else if (window.sol.type === 'tapClozeTable') {
return 'Tap Cloze Table';
} else if (window.sol.type === 'typeCompleteTable') {
return 'Type Complete Table';
} else if (window.sol.type === 'patternTapComplete') {
return 'Pattern Tap Complete';
} else if (document.querySelectorAll('[data-test*="challenge-name"]').length > 0 && document.querySelectorAll('[data-test="challenge-choice"]').length > 0) {
hcwNIIOdaQqCZRDL = false;
return 'Challenge Name';
} else if (window.sol.type === 'listenMatch') {
hcwNIIOdaQqCZRDL = false;
return 'Listen Match';
} else if (document.querySelectorAll('[data-test="challenge challenge-listenSpeak"]').length > 0) {
hcwNIIOdaQqCZRDL = false;
return 'Listen Speak';
} else if (document.querySelectorAll('[data-test="challenge-choice"]').length > 0) {
hcwNIIOdaQqCZRDL = false;
if (document.querySelectorAll('[data-test="challenge-text-input"]').length > 0) {
return 'Challenge Choice with Text Input';
} else {
return 'Challenge Choice'
}
} else if (document.querySelectorAll('[data-test$="challenge-tap-token"]').length > 0) {
hcwNIIOdaQqCZRDL = false;
if (window.sol.pairs !== undefined) {
return 'Pairs';
} else if (window.sol.correctTokens !== undefined) {
return 'Tokens Run';
} else if (window.sol.correctIndices !== undefined) {
return 'Indices Run';
}
} else if (document.querySelectorAll('[data-test="challenge-tap-token-text"]').length > 0) {
hcwNIIOdaQqCZRDL = false;
return 'Fill in the Gap';
} else if (document.querySelectorAll('[data-test="challenge-text-input"]').length > 0) {
hcwNIIOdaQqCZRDL = false;
return 'Challenge Text Input';
} else if (document.querySelectorAll('[data-test*="challenge-partialReverseTranslate"]').length > 0) {
hcwNIIOdaQqCZRDL = false;
return 'Partial Reverse';
} else if (document.querySelectorAll('textarea[data-test="challenge-translate-input"]').length > 0) {
hcwNIIOdaQqCZRDL = false;
return 'Challenge Translate Input';
} else if (document.querySelectorAll('[data-test="session-complete-slide"]').length > 0) {
return 'Session Complete';
} else if (document.querySelectorAll('[data-test="daily-quest-progress-slide"]').length > 0) {
return 'Daily Quest Progress';
} else if (document.querySelectorAll('[data-test="streak-slide"]').length > 0) {
return 'Streak';
} else if (document.querySelectorAll('[data-test="leaderboard-slide"]').length > 0) { // needs maintainance
return 'Leaderboard';
} else {
return false;
}
}
} catch (error) {
console.log(error);
return 'error';
}
}
function handleChallenge(challengeType) {
// Implement logic to handle different challenge types
// This function should encapsulate the logic for each challenge type
if (challengeType === 'Challenge Speak' || challengeType === 'Listen Match' || challengeType === 'Listen Speak') {
const buttonSkip = document.querySelector('button[data-test="player-skip"]');
buttonSkip?.click();
} else if (challengeType === 'Challenge Choice' || challengeType === 'Challenge Choice with Text Input') {
// Text input
if (challengeType === 'Challenge Choice with Text Input') {
let elm = document.querySelectorAll('[data-test="challenge-text-input"]')[0];
let nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
nativeInputValueSetter.call(elm, window.sol.correctSolutions ? window.sol.correctSolutions[0].split(/(?<=^\S+)\s/)[1] : (window.sol.displayTokens ? window.sol.displayTokens.find(t => t.isBlank).text : window.sol.prompt));
let inputEvent = new Event('input', {
bubbles: true
});
elm.dispatchEvent(inputEvent);
} else if (challengeType === 'Challenge Choice') {
document.querySelectorAll("[data-test='challenge-choice']")[window.sol.correctIndex].click();
}
} else if (challengeType === 'Pairs') {
let nl = document.querySelectorAll('[data-test*="challenge-tap-token"]:not(span)');
if (document.querySelectorAll('[data-test="challenge-tap-token-text"]').length === nl.length) {
window.sol.pairs?.forEach((pair) => {
for (let i = 0; i < nl.length; i++) {
const nlInnerText = nl[i].querySelector('[data-test="challenge-tap-token-text"]').innerText.toLowerCase().trim();
try {
if (
(
nlInnerText === pair.transliteration.toLowerCase().trim() ||
nlInnerText === pair.character.toLowerCase().trim()
)
&& !nl[i].disabled
) {
nl[i].click()
}
} catch (TypeError) {
if (
(
nlInnerText === pair.learningToken.toLowerCase().trim() ||
nlInnerText === pair.fromToken.toLowerCase().trim()
)
&& !nl[i].disabled
) {
nl[i].click()
}
}
}
})
}
} else if (challengeType === 'Story Pairs') {
const nl = document.querySelectorAll('[data-test*="challenge-tap-token"]:not(span)');
const textElements = document.querySelectorAll('[data-test="challenge-tap-token-text"]');
const textToElementMap = new Map();
for (let i = 0; i < nl.length; i++) {
const text = textElements[i].innerText.toLowerCase().trim();
textToElementMap.set(text, nl[i]);
}
for (const key in window.sol.dictionary) {
if (window.sol.dictionary.hasOwnProperty(key)) {
const value = window.sol.dictionary[key];
const keyPart = key.split(":")[1].toLowerCase().trim();
const normalizedValue = value.toLowerCase().trim();
const element1 = textToElementMap.get(keyPart);
const element2 = textToElementMap.get(normalizedValue);
if (element1 && !element1.disabled) element1.click();
if (element2 && !element2.disabled) element2.click();
}
}
} else if (challengeType === 'Tap Complete Table') {
solveTapCompleteTable();
} else if (challengeType === 'Tokens Run') {
correctTokensRun();
} else if (challengeType === 'Indices Run') {
correctIndicesRun();
} else if (challengeType === 'Fill in the Gap') {
correctIndicesRun();
} else if (challengeType === 'Challenge Text Input') {
let elm = document.querySelectorAll('[data-test="challenge-text-input"]')[0];
let nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
nativeInputValueSetter.call(elm, window.sol.correctSolutions ? window.sol.correctSolutions[0] : (window.sol.displayTokens ? window.sol.displayTokens.find(t => t.isBlank).text : window.sol.prompt));
let inputEvent = new Event('input', {
bubbles: true
});
elm.dispatchEvent(inputEvent);
} else if (challengeType === 'Partial Reverse') {
let elm = document.querySelector('[data-test*="challenge-partialReverseTranslate"]')?.querySelector("span[contenteditable]");
let nativeInputNodeTextSetter = Object.getOwnPropertyDescriptor(Node.prototype, "textContent").set
nativeInputNodeTextSetter.call(elm, window.sol?.displayTokens?.filter(t => t.isBlank)?.map(t => t.text)?.join()?.replaceAll(',', ''));
let inputEvent = new Event('input', {
bubbles: true
});
elm.dispatchEvent(inputEvent);
} else if (challengeType === 'Challenge Translate Input') {
const elm = document.querySelector('textarea[data-test="challenge-translate-input"]');
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value").set;
nativeInputValueSetter.call(elm, window.sol.correctSolutions ? window.sol.correctSolutions[0] : window.sol.prompt);
let inputEvent = new Event('input', {
bubbles: true
});
elm.dispatchEvent(inputEvent);
} else if (challengeType === 'Challenge Name') {
let articles = findReact(document.getElementsByClassName(findReactMainElementClass)[0]).props.currentChallenge.articles;
let correctSolutions = findReact(document.getElementsByClassName(findReactMainElementClass)[0]).props.currentChallenge.correctSolutions[0];
let matchingArticle = articles.find(article => correctSolutions.startsWith(article));
let matchingIndex = matchingArticle !== undefined ? articles.indexOf(matchingArticle) : null;
let remainingValue = correctSolutions.substring(matchingArticle.length);
let selectedElement = document.querySelector(`[data-test="challenge-choice"]:nth-child(${matchingIndex + 1})`);
if (selectedElement) {
selectedElement.click();
}
let elm = document.querySelector('[data-test="challenge-text-input"]');
let nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
nativeInputValueSetter.call(elm, remainingValue);
let inputEvent = new Event('input', {
bubbles: true
});
elm.dispatchEvent(inputEvent);
} else if (challengeType === 'Type Cloze') {
const input = document.querySelector('input[type="text"].b4jqk');
if (!input) return;
let targetToken = window.sol.displayTokens.find(t => t.damageStart !== undefined);
let correctWord = targetToken?.text || "";
let correctEnding = "";
if (typeof targetToken?.damageStart === "number") {
correctEnding = correctWord.slice(targetToken.damageStart);
}
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
nativeInputValueSetter.call(input, correctEnding);
input.dispatchEvent(new Event("input", { bubbles: true }));
input.dispatchEvent(new Event("change", { bubbles: true }));
} else if (challengeType === 'Type Cloze Table') {
const tableRows = document.querySelectorAll('tbody tr');
window.sol.displayTableTokens.slice(1).forEach((rowTokens, i) => {
const answerCell = rowTokens[1]?.find(t => typeof t.damageStart === "number");
if (answerCell && tableRows[i]) {
const input = tableRows[i].querySelector('input[type="text"].b4jqk');
if (!input) return;
const correctWord = answerCell.text;
const correctEnding = correctWord.slice(answerCell.damageStart);
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
nativeInputValueSetter.call(input, correctEnding);
input.dispatchEvent(new Event("input", { bubbles: true }));
input.dispatchEvent(new Event("change", { bubbles: true }));
}
});
} else if (challengeType === 'Tap Cloze Table') {
const tableRows = document.querySelectorAll('tbody tr');
window.sol.displayTableTokens.slice(1).forEach((rowTokens, i) => {
const answerCell = rowTokens[1]?.find(t => typeof t.damageStart === "number");
if (!answerCell || !tableRows[i]) return;
const wordBank = document.querySelector('[data-test="word-bank"], .eSgkc');
const wordButtons = wordBank ? Array.from(wordBank.querySelectorAll('button[data-test*="challenge-tap-token"]:not([aria-disabled="true"])')) : [];
const correctWord = answerCell.text;
const correctEnding = correctWord.slice(answerCell.damageStart);
let endingMatched = "";
let used = new Set();
for (let btn of wordButtons) {
if (!correctEnding.startsWith(endingMatched + btn.innerText)) continue;
btn.click();
endingMatched += btn.innerText;
used.add(btn);
if (endingMatched === correctEnding) break;
}
});
} else if (challengeType === 'Type Complete Table') {
const tableRows = document.querySelectorAll('tbody tr');
window.sol.displayTableTokens.slice(1).forEach((rowTokens, i) => {
const answerCell = rowTokens[1]?.find(t => t.isBlank);
if (!answerCell || !tableRows[i]) return;
const input = tableRows[i].querySelector('input[type="text"].b4jqk');
if (!input) return;
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
nativeInputValueSetter.call(input, answerCell.text);
input.dispatchEvent(new Event("input", { bubbles: true }));
input.dispatchEvent(new Event("change", { bubbles: true }));
});
} else if (challengeType === 'Pattern Tap Complete') {
const wordBank = document.querySelector('[data-test="word-bank"], .eSgkc');
if (!wordBank) return;
const choices = window.sol.choices;
const correctIndex = window.sol.correctIndex ?? 0;
const correctText = choices[correctIndex];
const buttons = Array.from(wordBank.querySelectorAll('button[data-test*="challenge-tap-token"]:not([aria-disabled="true"])'));
const targetButton = buttons.find(btn => btn.innerText.trim() === correctText);
if (targetButton) {
targetButton.click();
}
} else if (challengeType === 'Session Complete') {
} else if (challengeType === 'Story Arrange') {
let choices = document.querySelectorAll('[data-test*="challenge-tap-token"]:not(span)');
for (let i = 0; i < window.sol.phraseOrder.length; i++) {
choices[window.sol.phraseOrder[i]].click();
}
} else if (challengeType === 'Story Multiple Choice') {
let choices = document.querySelectorAll('[data-test="stories-choice"]');
choices[window.sol.correctAnswerIndex].click();
} else if (challengeType === 'Story Point to Phrase') {
let choices = document.querySelectorAll('[data-test="challenge-tap-token-text"]');
var correctIndex = -1;
for (let i = 0; i < window.sol.parts.length; i++) {
if (window.sol.parts[i].selectable === true) {
correctIndex += 1;
if (window.sol.correctAnswerIndex === i) {
choices[correctIndex].parentElement.click();
}
}
}
}
}
function correctTokensRun() {
const all_tokens = document.querySelectorAll('[data-test$="challenge-tap-token"]');
const correct_tokens = window.sol.correctTokens;
const clicked_tokens = [];
correct_tokens.forEach(correct_token => {
const matching_elements = Array.from(all_tokens).filter(element => element.textContent.trim() === correct_token.trim());
if (matching_elements.length > 0) {
const match_index = clicked_tokens.filter(token => token.textContent.trim() === correct_token.trim()).length;
if (match_index < matching_elements.length) {
matching_elements[match_index].click();
clicked_tokens.push(matching_elements[match_index]);
} else {
clicked_tokens.push(matching_elements[0]);
}
}
});
}
function correctIndicesRun() {
if (window.sol.correctIndices) {
window.sol.correctIndices?.forEach(index => {
document.querySelectorAll('div[data-test="word-bank"] [data-test*="challenge-tap-token"]:not(span)')[index].click();
});
}
}
function solveTapCompleteTable() {
const solutionRows = window.sol.displayTableTokens.slice(1);
const tableRowElements = document.querySelectorAll('tbody tr');
const wordBank = document.querySelector('div[data-test="word-bank"]');
const wordBankButtons = wordBank ? wordBank.querySelectorAll('button[data-test*="-challenge-tap-token"]') : [];
const usedWordBankIndexes = new Set();
solutionRows.forEach((solutionRow, rowIndex) => {
const answerCellData = solutionRow[1];
const correctToken = answerCellData.find(token => token.isBlank);
if (correctToken) {
const correctAnswerText = correctToken.text;
const currentRowElement = tableRowElements[rowIndex];
let buttons = currentRowElement.querySelectorAll('button[data-test*="-challenge-tap-token"]');
let clicked = false;
if (buttons.length > 0) {
for (let button of buttons) {
const buttonTextElm = button.querySelector('[data-test="challenge-tap-token-text"]');
if (buttonTextElm && buttonTextElm.innerText.trim() === correctAnswerText && !button.disabled) {
button.click();
clicked = true;
break;
}
}
}
if (!clicked && wordBankButtons.length > 0) {
for (let i = 0; i < wordBankButtons.length; i++) {
if (usedWordBankIndexes.has(i)) continue;
const button = wordBankButtons[i];
const buttonTextElm = button.querySelector('[data-test="challenge-tap-token-text"]');
if (buttonTextElm && buttonTextElm.innerText.trim() === correctAnswerText && !button.disabled) {
button.click();
usedWordBankIndexes.add(i);
break;
}
}
}
}
});
}
function findSubReact(dom, traverseUp = reactTraverseUp) {
const key = Object.keys(dom).find(key => key.startsWith("__reactProps"));
return dom?.[key]?.children?.props?.slide;
}
function findReact(dom, traverseUp = reactTraverseUp) {
const key = Object.keys(dom).find(key => {
return key.startsWith("__reactFiber$") // react 17+
|| key.startsWith("__reactInternalInstance$"); // react <17
});
const domFiber = dom[key];
if (domFiber == null) return null;
// react <16
if (domFiber._currentElement) {
let compFiber = domFiber._currentElement._owner;
for (let i = 0; i < traverseUp; i++) {
compFiber = compFiber._currentElement._owner;
}
return compFiber._instance;
}
// react 16+
const GetCompFiber = fiber => {
//return fiber._debugOwner; // this also works, but is __DEV__ only
let parentFiber = fiber.return;
while (typeof parentFiber.type == "string") {
parentFiber = parentFiber.return;
}
return parentFiber;
};
let compFiber = GetCompFiber(domFiber);
for (let i = 0; i < traverseUp; i++) {
compFiber = GetCompFiber(compFiber);
}
return compFiber.stateNode;
}
window.findReact = findReact;
window.findSubReact = findSubReact;
window.ss = solving;
}
try {
if (false) {
if (storageLocal.languagePackVersion !== "00") {
if (!storageLocal.languagePack.hasOwnProperty(systemLanguage)) systemLanguage = "en";
systemText = storageLocal.languagePack;
One();
} else {
systemLanguage = "en";
One();
}
} else {
systemLanguage = "en";
One();
}
} catch (error) {
console.log(error);
One();
}