Test-English egzersizlerini basit şekilde takip eder.
// ==UserScript==
// @name Test-English Simple Progress Tracker
// @namespace https://test-english.com/
// @version 1.0.0
// @description Test-English egzersizlerini basit şekilde takip eder.
// @match https://test-english.com/*
// @match https://www.test-english.com/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_addStyle
// @license MIT
// ==/UserScript==
(function () {
'use strict';
// ============================================================
// AYARLAR
// ============================================================
const STORAGE_KEY = 'testEnglishSimpleProgress';
// ============================================================
// VERİ
// ============================================================
function getData() {
return GM_getValue(STORAGE_KEY, {});
}
function saveData(data) {
GM_setValue(STORAGE_KEY, data);
}
// ============================================================
// SAYFA BİLGİSİ
// ============================================================
function getPageKey() {
return window.location.pathname
.replace(/\/+$/, '');
}
function getExerciseNumber() {
const path = getPageKey();
// Örnek:
// /grammar-points/a1/present-simple-forms-of-to-be/
// = Exercise 1
// Örnek:
// /grammar-points/a1/present-simple-forms-of-to-be/2/
// = Exercise 2
const match = path.match(/\/(\d+)$/);
if (match) {
return parseInt(match[1], 10);
}
return 1;
}
function getTitle() {
const h1 = document.querySelector('h1');
if (h1) {
return h1.textContent
.trim()
.replace(/\s+/g, ' ');
}
return document.title
.replace(/\s*\|\s*Test-English.*$/i, '')
.trim();
}
// ============================================================
// SAYFANIN DAHA ÖNCE ÇÖZÜLÜP ÇÖZÜLMEDİĞİNİ KONTROL ET
// ============================================================
function getCurrentResult() {
const data = getData();
const pageKey = getPageKey();
return data[pageKey] || null;
}
function getTotalStatistics() {
const data = getData();
let totalSolved = 0;
let totalCorrect = 0;
let totalWrong = 0;
Object.values(data).forEach(function (result) {
if (!result || !result.completed) {
return;
}
/*
* Doğru ve yanlış bilgisi girilmişse
* bu exercise'in sorularını toplama dahil et.
*/
if (
typeof result.correct === 'number' &&
typeof result.wrong === 'number'
) {
totalCorrect += result.correct;
totalWrong += result.wrong;
totalSolved +=
result.correct +
result.wrong;
}
});
return {
totalSolved: totalSolved,
totalCorrect: totalCorrect,
totalWrong: totalWrong
};
}
// ============================================================
// EGZERSİZİ TAMAMLANDI OLARAK KAYDET
// ============================================================
function markCompleted() {
const data = getData();
const pageKey = getPageKey();
const exercise = getExerciseNumber();
/*
* Test-English sonuç ekranındaki
* "Correct answers: 9/10" bilgisini yakala.
*/
const text =
document.body.innerText || '';
const match =
text.match(
/Correct answers:\s*(\d+)\s*\/\s*(\d+)/i
);
let correct = null;
let wrong = null;
if (match) {
const total =
parseInt(match[2], 10);
correct =
parseInt(match[1], 10);
wrong =
total - correct;
}
/*
* Skoru da yakala.
*/
const scoreMatch =
text.match(
/Your score is\s*(\d+(?:\.\d+)?)\s*%/i
);
let score = null;
if (scoreMatch) {
score =
parseFloat(
scoreMatch[1]
);
}
/*
* Sonucu kaydet.
*/
data[pageKey] = {
title: getTitle(),
exercise: exercise,
completed: true,
correct: correct,
wrong: wrong,
score: score,
completedAt:
new Date().toISOString()
};
saveData(data);
showCompletedMessage();
createManualScorePanel();
}
// ============================================================
// CHECK ANSWERS BUTONUNU BUL
// ============================================================
function findCheckAnswersButton() {
const elements =
document.querySelectorAll(
'button, input[type="button"], input[type="submit"], a'
);
for (const element of elements) {
const text =
(
element.innerText ||
element.value ||
''
)
.trim()
.toLowerCase();
if (
text.includes('check answers')
) {
return element;
}
}
return null;
}
// ============================================================
// BUTONA TIKLAMAYI DİNLE
// ============================================================
function setupCheckAnswers() {
const button =
findCheckAnswersButton();
if (!button) {
return;
}
if (
button.dataset.teTrackerAttached ===
'true'
) {
return;
}
button.dataset.teTrackerAttached =
'true';
button.addEventListener(
'click',
function () {
/*
* Test-English'in sonucu oluşturması
* için kısa bir süre bekle.
*/
const delays = [
300,
700,
1200,
2000
];
delays.forEach(
function (delay) {
setTimeout(
function () {
const text =
document.body.innerText ||
'';
if (
/Test completed!/i.test(
text
) &&
/Correct answers:/i.test(
text
)
) {
markCompleted();
}
},
delay
);
}
);
}
);
}
// ============================================================
// TAMAMLANDI MESAJI
// ============================================================
function showCompletedMessage() {
const old =
document.getElementById(
'te-completed-message'
);
if (old) {
old.remove();
}
const statistics =
getTotalStatistics();
const message =
document.createElement('div');
message.id =
'te-completed-message';
message.innerHTML = `
<div class="te-completed-icon">
✓
</div>
<div class="te-completed-content">
<strong>
Bu test daha önce çözüldü
</strong>
<div class="te-completed-small">
Exercise ${getExerciseNumber()}
</div>
<div class="te-total-statistics">
📊 Toplam çözülen:
<strong>
${statistics.totalSolved}
</strong>
<br>
<span>
Doğru:
${statistics.totalCorrect}
</span>
·
<span>
Yanlış:
${statistics.totalWrong}
</span>
</div>
</div>
`;
const header =
document.querySelector('header');
if (header) {
header.insertAdjacentElement(
'afterend',
message
);
} else {
document.body.prepend(
message
);
}
}
// ============================================================
// MANUEL DOĞRU / YANLIŞ GİRİŞİ
// ============================================================
function createManualScorePanel() {
const result =
getCurrentResult();
if (
!result ||
!result.completed
) {
return;
}
const old =
document.getElementById(
'te-score-panel'
);
if (old) {
old.remove();
}
const panel =
document.createElement(
'div'
);
panel.id =
'te-score-panel';
panel.innerHTML = `
<div class="te-score-title">
Test sonucu
</div>
<div class="te-score-row">
<label>
Doğru
</label>
<input
id="te-correct-input"
type="number"
min="0"
value="${
result.correct !== null
? result.correct
: ''
}"
>
</div>
<div class="te-score-row">
<label>
Yanlış
</label>
<input
id="te-wrong-input"
type="number"
min="0"
value="${
result.wrong !== null
? result.wrong
: ''
}"
>
</div>
<div
id="te-score-display"
class="te-score-display"
>
${
result.score !== null
? 'Skor: ' + result.score + '%'
: ''
}
</div>
<button
id="te-save-score"
>
Sonucu Kaydet
</button>
`;
/*
* Tamamlandı mesajının hemen altına koy.
*/
const completedMessage =
document.getElementById(
'te-completed-message'
);
if (completedMessage) {
completedMessage.insertAdjacentElement(
'afterend',
panel
);
} else {
document.body.prepend(
panel
);
}
document
.getElementById(
'te-save-score'
)
.addEventListener(
'click',
saveManualScore
);
}
function saveManualScore() {
const data =
getData();
const pageKey =
getPageKey();
if (
!data[pageKey]
) {
return;
}
const correctInput =
document.getElementById(
'te-correct-input'
);
const wrongInput =
document.getElementById(
'te-wrong-input'
);
const correct =
correctInput.value === ''
? null
: parseInt(
correctInput.value,
10
);
const wrong =
wrongInput.value === ''
? null
: parseInt(
wrongInput.value,
10
);
data[pageKey].correct =
correct;
data[pageKey].wrong =
wrong;
/*
* Eğer doğru + yanlış varsa
* toplamı hesaplayabiliriz.
*/
if (
correct !== null &&
wrong !== null &&
correct + wrong > 0
) {
data[pageKey].total =
correct + wrong;
data[pageKey].score =
Math.round(
(
correct /
(correct + wrong)
) * 100
);
}
saveData(data);
const button =
document.getElementById(
'te-save-score'
);
button.textContent =
'✓ Kaydedildi';
setTimeout(
function () {
button.textContent =
'Sonucu Kaydet';
},
1500
);
}
// ============================================================
// UI
// ============================================================
function createUI() {
const result =
getCurrentResult();
if (
result &&
result.completed
) {
showCompletedMessage();
createManualScorePanel();
}
}
// ============================================================
// CSS
// ============================================================
GM_addStyle(`
#te-completed-message {
position: relative;
z-index: 9999;
width: calc(100% - 20px);
box-sizing: border-box;
margin: 10px;
padding: 12px 16px;
display: flex;
align-items: center;
gap: 10px;
background: #e9f7ec;
color: #222222;
border: 1px solid #b8dfc0;
border-radius: 8px;
font-family:
Arial,
Helvetica,
sans-serif;
font-size: 13px;
}
.te-completed-icon {
width: 30px;
height: 30px;
border-radius: 50%;
background: #35a854;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
font-weight: bold;
}
.te-completed-small {
margin-top: 3px;
color: #777777;
font-size: 11px;
}
#te-score-panel {
position: fixed;
top: 85px;
right: 20px;
z-index: 999999;
width: 220px;
padding: 14px;
background: #ffffff;
border: 1px solid #dddddd;
border-radius: 10px;
box-shadow:
0 5px 20px
rgba(0, 0, 0, 0.18);
font-family:
Arial,
Helvetica,
sans-serif;
}
.te-score-title {
font-size: 13px;
font-weight: bold;
margin-bottom: 10px;
}
.te-score-row {
display: flex;
align-items: center;
justify-content:
space-between;
margin-bottom: 7px;
font-size: 12px;
}
.te-score-row input {
width: 100px;
padding: 5px;
border:
1px solid #cccccc;
border-radius: 5px;
}
#te-save-score {
width: 100%;
margin-top: 5px;
padding: 7px;
border: none;
border-radius: 5px;
background: #222222;
color: white;
cursor: pointer;
}
#te-save-score:hover {
background: #444444;
}
.te-total-statistics {
margin-top: 8px;
padding-top: 8px;
border-top:
1px solid #cfe3d2;
color: #555555;
font-size: 12px;
line-height: 1.7;
}
.te-total-statistics strong {
color: #222222;
font-weight: bold;
}
.te-total-statistics span {
color: #666666;
}
`);
// ============================================================
// BAŞLAT
// ============================================================
function init() {
/*
* Sadece bir kez çalışıyoruz.
*
* MutationObserver yok.
* Sürekli tarama yok.
* Sayfayı yenileme yok.
*/
setupCheckAnswers();
createUI();
}
if (
document.readyState ===
'loading'
) {
document.addEventListener(
'DOMContentLoaded',
init
);
} else {
init();
}
})();