';
parts.forEach(part => {
part = part.trim();
if (!part) return;
if (/^\d+\.\s/.test(part)) {
const match = part.match(/^(\d+\.\s)(.+)/s);
if (match) {
const num = match[1];
const content = match[2].trim();
html += `${num}${content} `;
}
} else {
html += `${part} `;
}
});
html += '
';
return html;
}
function replaceWithNaturalFormat(element, translatedText) {
if (!element.hasAttribute('data-original-html')) {
element.setAttribute('data-original-html', element.innerHTML);
}
const formattedHTML = createNaturalFormat(translatedText);
element.innerHTML = formattedHTML;
translationCount++;
}
function replaceElementText(element, translatedText, showBadge = true) {
if (!element.hasAttribute('data-original-text')) {
element.setAttribute('data-original-text', element.textContent);
}
element.textContent = translatedText;
if (showBadge) {
const badge = document.createElement('span');
badge.className = 'gf-translation-badge';
badge.textContent = 'π';
element.appendChild(badge);
}
translationCount++;
}
async function processScriptTitles() {
const scriptLinks = document.querySelectorAll('h2 a.script-link');
let count = 0;
for (const link of scriptLinks) {
if (processedElements.has(link)) continue;
const titleText = link.textContent.trim();
if (shouldTranslate(titleText)) {
processedElements.add(link);
count++;
const translated = await translateText(titleText);
if (translated && translated !== titleText) {
replaceElementText(link, translated, true);
}
await new Promise(resolve => setTimeout(resolve, 300));
}
}
return count;
}
async function processScriptTitlesCITRUS() {
//const scriptLinks = document.querySelectorAll('h2 a.script-link');
// CITRUS - #script-table tbody td:nth-child(2) .thetitle > a > b
const scriptLinks = document.querySelectorAll('#script-table tbody td:nth-child(2) .thetitle > a > b');
let count = 0;
for (const link of scriptLinks) {
if (processedElements.has(link)) continue;
const titleText = link.textContent.trim();
if (shouldTranslate(titleText)) {
processedElements.add(link);
count++;
const translated = await translateText(titleText);
if (translated && translated !== titleText) {
replaceElementText(link, translated, true);
}
await new Promise(resolve => setTimeout(resolve, 300));
}
}
return count;
}
async function processDescriptionSpans() {
const descriptionSpans = document.querySelectorAll('span.script-description, span.description');
let count = 0;
for (const span of descriptionSpans) {
if (processedElements.has(span)) continue;
const spanText = span.textContent.trim();
if (spanText.length > 20 && shouldTranslate(spanText)) {
processedElements.add(span);
count++;
showStatus(`π ${count}...`, 800);
const translated = await translateText(spanText);
if (translated && translated !== spanText) {
replaceWithNaturalFormat(span, translated);
}
await new Promise(resolve => setTimeout(resolve, 400));
}
}
return count;
}
async function processDescriptionSpansCITRUS() {
//const descriptionSpans = document.querySelectorAll('span.script-description, span.description');
// CITRUS - body[pagetype="ListingPage"] #script-table td .thetitle + div
const descriptionSpans = document.querySelectorAll('body[pagetype="ListingPage"] #script-table td .thetitle + div');
let count = 0;
for (const span of descriptionSpans) {
if (processedElements.has(span)) continue;
const spanText = span.textContent.trim();
if (spanText.length > 20 && shouldTranslate(spanText)) {
processedElements.add(span);
count++;
showStatus(`π ${count}...`, 800);
const translated = await translateText(spanText);
if (translated && translated !== spanText) {
replaceWithNaturalFormat(span, translated);
}
await new Promise(resolve => setTimeout(resolve, 400));
}
}
return count;
}
async function processDetailPageHeaders() {
const headers = document.querySelectorAll('header h2, #script-info h2');
let count = 0;
for (const header of headers) {
if (processedElements.has(header)) continue;
const headerText = header.textContent.trim();
if (shouldTranslate(headerText)) {
processedElements.add(header);
count++;
const translated = await translateText(headerText);
if (translated && translated !== headerText) {
replaceElementText(header, translated, true);
}
await new Promise(resolve => setTimeout(resolve, 400));
}
}
return count;
}
async function processDetailPageDescriptions() {
const descriptions = document.querySelectorAll('#script-description, p.script-description');
let count = 0;
for (const desc of descriptions) {
if (processedElements.has(desc)) continue;
const descText = desc.textContent.trim();
if (descText.length > 20 && shouldTranslate(descText)) {
processedElements.add(desc);
count++;
const translated = await translateText(descText);
if (translated && translated !== descText) {
replaceWithNaturalFormat(desc, translated);
}
await new Promise(resolve => setTimeout(resolve, 500));
}
}
return count;
}
async function processAdditionalInfo() {
const additionalInfo = document.querySelector('#additional-info');
if (!additionalInfo) return 0;
let count = 0;
const elements = additionalInfo.querySelectorAll('p');
for (const element of elements) {
if (processedElements.has(element)) continue;
const text = element.textContent.trim();
if (text.length < 20) continue;
// Check for non-Latin characters (not just ratio)
if (shouldTranslate(text)) {
processedElements.add(element);
count++;
showStatus(`π ${count}...`, 800);
const translated = await translateText(text);
if (translated && translated !== text) {
replaceWithNaturalFormat(element, translated);
}
await new Promise(resolve => setTimeout(resolve, 500));
}
}
return count;
}
function restoreOriginalText() {
location.reload();
}
function makeDraggable(element) {
let isDragging = false, offsetX = 0, offsetY = 0;
const savedX = GM_getValue('panelX', null), savedY = GM_getValue('panelY', null);
if (savedX !== null && savedY !== null) {
element.style.left = savedX + 'px';
element.style.top = savedY + 'px';
} else {
element.style.right = '10px';
element.style.top = '10px';
}
element.addEventListener('mousedown', (e) => {
if (e.target.tagName === 'BUTTON' || e.target.closest('button')) return;
isDragging = true;
element.classList.add('dragging');
const rect = element.getBoundingClientRect();
offsetX = e.clientX - rect.left;
offsetY = e.clientY - rect.top;
e.preventDefault();
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
e.preventDefault();
let newX = e.clientX - offsetX, newY = e.clientY - offsetY;
const rect = element.getBoundingClientRect();
newX = Math.max(0, Math.min(newX, window.innerWidth - rect.width));
newY = Math.max(0, Math.min(newY, window.innerHeight - rect.height));
element.style.left = newX + 'px';
element.style.top = newY + 'px';
element.style.right = 'auto';
});
document.addEventListener('mouseup', () => {
if (!isDragging) return;
isDragging = false;
element.classList.remove('dragging');
const rect = element.getBoundingClientRect();
GM_setValue('panelX', rect.left);
GM_setValue('panelY', rect.top);
});
}
function updateCounter() {
const counter = document.getElementById('translation-count');
if (counter) counter.textContent = translationCount;
}
function addControlPanel() {
if (document.getElementById('gf-translator-panel')) return;
const panel = document.createElement('div');
panel.id = 'gf-translator-panel';
panel.innerHTML = `
π v16
β 0
No Spanish
`;
document.body.appendChild(panel);
makeDraggable(panel);
document.getElementById('gf-minimize-btn').addEventListener('click', (e) => {
e.stopPropagation();
const content = document.getElementById('gf-panel-content');
const btn = e.target;
if (content.classList.contains('hidden')) {
content.classList.remove('hidden');
panel.classList.remove('minimized');
btn.textContent = 'β';
} else {
content.classList.add('hidden');
panel.classList.add('minimized');
btn.textContent = 'β‘';
}
});
document.getElementById('translateNowBtn').addEventListener('click', async () => {
const btn = document.getElementById('translateNowBtn');
btn.disabled = true;
btn.textContent = 'β³';
const isDetailPage = document.querySelector('#script-info');
let total = 0;
if (isDetailPage) {
total += await processDetailPageHeaders();
total += await processDetailPageDescriptions();
total += await processAdditionalInfo();
} else {
total += await processScriptTitles();
total += await processScriptTitlesCITRUS();
total += await processDescriptionSpans();
total += await processDescriptionSpansCITRUS();
}
updateCounter();
btn.disabled = false;
btn.textContent = 'π Translate';
showStatus(`β ${total}!`, 3000);
});
document.getElementById('restoreBtn').addEventListener('click', restoreOriginalText);
document.getElementById('gf-close-btn-x').addEventListener('click', (e) => {
e.stopPropagation();
panel.style.display = 'none';
});
document.getElementById('closeBtn').addEventListener('click', () => panel.style.display = 'none');
updateCounter();
}
async function init() {
debugLog('π v16 - Skips Spanish');
await new Promise(resolve => setTimeout(resolve, 1500));
addControlPanel();
if (CONFIG.autoTranslate) {
showStatus('π Translating...', 2000);
const isDetailPage = document.querySelector('#script-info');
let total = 0;
if (isDetailPage) {
total += await processDetailPageHeaders();
total += await processDetailPageDescriptions();
total += await processAdditionalInfo();
} else {
total += await processScriptTitles();
total += await processScriptTitlesCITRUS();
total += await processDescriptionSpans();
total += await processDescriptionSpansCITRUS();
}
if (total > 0) showStatus(`β Done! ${total}`, 4000);
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();