MediaWiki:Common.js: Difference between revisions
No edit summary |
No edit summary |
||
| Line 1: | Line 1: | ||
(function () { | (function () { | ||
var currentScript = 'deva'; | var currentScript = 'deva'; | ||
var initialized = false; | |||
// ── | // ── IAST transliteration (inline, no external library) ───────── | ||
function devanagariToIAST(text) { | function devanagariToIAST(text) { | ||
var CONSONANTS = { | var CONSONANTS = { | ||
'क':'k','ख':'kh','ग':'g','घ':'gh','ङ':'ṅ', | 'क':'k','ख':'kh','ग':'g','घ':'gh','ङ':'ṅ', | ||
| Line 25: | Line 18: | ||
'ा':'ā','ि':'i','ी':'ī','ु':'u','ू':'ū', | 'ा':'ā','ि':'i','ी':'ī','ु':'u','ू':'ū', | ||
'ृ':'ṛ','ॄ':'ṝ','े':'e','ै':'ai','ो':'o','ौ':'au' | 'ृ':'ṛ','ॄ':'ṝ','े':'e','ै':'ai','ो':'o','ौ':'au' | ||
}; | |||
var VOWELS = { | |||
'अ':'a','आ':'ā','इ':'i','ई':'ī','उ':'u','ऊ':'ū', | |||
'ऋ':'ṛ','ॠ':'ṝ','ए':'e','ऐ':'ai','ओ':'o','औ':'au','ऽ':"'" | |||
}; | }; | ||
var MISC = { | var MISC = { | ||
| Line 32: | Line 29: | ||
}; | }; | ||
var HALANTA = '्'; | var HALANTA = '्'; | ||
var chars = Array.from(text); | var chars = Array.from(text); | ||
var result = ''; | var result = ''; | ||
var i = 0; | var i = 0; | ||
while (i < chars.length) { | while (i < chars.length) { | ||
var ch = chars[i]; | var ch = chars[i]; | ||
if (CONSONANTS[ch]) { | if (CONSONANTS[ch]) { | ||
var | var base = CONSONANTS[ch]; | ||
var next = chars[i + 1]; | |||
if (next === HALANTA) { | |||
result += base; i += 2; | |||
} else if (DIACRITICS[next]) { | |||
result += base + DIACRITICS[next]; i += 2; | |||
} else if (next === 'ं' || next === 'ः') { | |||
result += base + 'a' + MISC[next]; i += 2; | |||
} else { | |||
result += base + 'a'; i++; | |||
} | } | ||
} else if (VOWELS[ch]) { result += VOWELS[ch]; i++; } | |||
else if (DIACRITICS[ch]) { result += DIACRITICS[ch]; i++; } | |||
else if (MISC[ch]) { result += MISC[ch]; i++; } | |||
} else if (VOWELS[ch]) { | else { result += ch; i++; } | ||
} | } | ||
return result; | return result; | ||
} | } | ||
// ── | // ── Character maps for Kannada / Tamil ───────────────────────── | ||
var SCRIPT_MAP = { | var SCRIPT_MAP = { | ||
kn: { | kn: { | ||
| Line 124: | Line 95: | ||
[/न्त/g,'ंत'],[/न्द/g,'ंद'],[/म्ब/g,'ंब'],[/म्भ/g,'ंभ'] | [/न्त/g,'ंत'],[/न्द/g,'ंद'],[/म्ब/g,'ंब'],[/म्भ/g,'ंभ'] | ||
]; | ]; | ||
function transliterateText(text, script) { | function transliterateText(text, script) { | ||
| Line 134: | Line 100: | ||
var map = SCRIPT_MAP[script]; | var map = SCRIPT_MAP[script]; | ||
if (!map) return text; | if (!map) return text; | ||
return Array.from( | var t = text; | ||
PRE.forEach(function (p) { t = t.replace(p[0], p[1]); }); | |||
return Array.from(t).map(function (ch) { | |||
return map[ch] !== undefined ? map[ch] : ch; | return map[ch] !== undefined ? map[ch] : ch; | ||
}).join(''); | }).join(''); | ||
} | } | ||
// ── | // ── Collect all transliteratable text nodes once ──────────────── | ||
// | // Store originals as data-deva on a wrapper span. | ||
var | // On script switch, update textContent of those spans — never | ||
// touch innerHTML of the whole content block. | |||
var translatableSpans = []; | |||
function | function tagTextNodes() { | ||
var content = document.querySelector('.mw-parser-output'); | var content = document.querySelector('.mw-parser-output'); | ||
if (!content) return; | if (!content) return; | ||
var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT); | var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT); | ||
var nodes = []; | var nodes = []; | ||
| Line 180: | Line 124: | ||
var p = node.parentNode; | var p = node.parentNode; | ||
if (!p) return; | if (!p) return; | ||
// Skip already tagged | |||
if (p.hasAttribute && p.hasAttribute('data-deva')) return; | |||
// Skip UI elements | |||
if (p.closest) { | if (p.closest) { | ||
if (p.closest('.gr-script-bar') || | if (p.closest('.gr-script-bar') || | ||
| Line 187: | Line 134: | ||
} | } | ||
var orig = node.textContent; | var orig = node.textContent; | ||
var | if (!orig.trim()) return; | ||
if ( | // Wrap in span with original stored | ||
var span = document.createElement('span'); | |||
span.setAttribute('data-deva', orig); | |||
span.textContent = orig; | |||
p.replaceChild(span, node); | |||
translatableSpans.push(span); | |||
}); | |||
} | |||
function applyScript(script) { | |||
currentScript = script; | |||
translatableSpans.forEach(function (span) { | |||
if (!span.parentNode) return; // detached | |||
var orig = span.getAttribute('data-deva'); | |||
if (script === 'deva') { | |||
span.textContent = orig; | |||
} else { | |||
span.textContent = transliterateText(orig, script); | |||
} | } | ||
}); | }); | ||
// Update active button | |||
// | |||
var bar = document.querySelector('.gr-script-bar'); | var bar = document.querySelector('.gr-script-bar'); | ||
if (bar) { | if (bar) { | ||
| Line 204: | Line 164: | ||
} | } | ||
// ── | // ── Build switcher bar ───────────────────────────────────────── | ||
function buildBar() { | function buildBar() { | ||
var title = document.querySelector('.gr-doc-title'); | var title = document.querySelector('.gr-doc-title'); | ||
| Line 214: | Line 174: | ||
bar.innerHTML = | bar.innerHTML = | ||
'<span class="gr-script-label">change script to</span>' + | '<span class="gr-script-label">change script to</span>' + | ||
'<a class="gr-script-btn" data-script="deva">देवनागरी</a>' + | '<a class="gr-script-btn active" data-script="deva">देवनागरी</a>' + | ||
'<a class="gr-script-btn" data-script="kn">ಕನ್ನಡ</a>' + | '<a class="gr-script-btn" data-script="kn">ಕನ್ನಡ</a>' + | ||
'<a class="gr-script-btn" data-script="ta">தமிழ்</a>' + | '<a class="gr-script-btn" data-script="ta">தமிழ்</a>' + | ||
| Line 220: | Line 180: | ||
title.after(bar); | title.after(bar); | ||
bar.querySelectorAll('.gr-script-btn').forEach(function (btn) { | bar.querySelectorAll('.gr-script-btn').forEach(function (btn) { | ||
| Line 234: | Line 189: | ||
} | } | ||
// ── | // ── TOC active highlight (CSS-driven, MutationObserver backup) ── | ||
function watchTocActive() { | function watchTocActive() { | ||
var toc = document.querySelector('.vector-toc'); | var toc = document.querySelector('.vector-toc'); | ||
| Line 269: | Line 197: | ||
var observer = new MutationObserver(function (mutations) { | var observer = new MutationObserver(function (mutations) { | ||
mutations.forEach(function (m) { | mutations.forEach(function (m) { | ||
if ( | if (m.attributeName !== 'class') return; | ||
var li = m.target; | var li = m.target; | ||
var link = li.querySelector(':scope > .vector-toc-link'); | var link = li.querySelector(':scope > .vector-toc-link'); | ||
| Line 286: | Line 214: | ||
observer.observe(li, { attributes: true, attributeFilter: ['class'] }); | observer.observe(li, { attributes: true, attributeFilter: ['class'] }); | ||
}); | }); | ||
} | |||
// ── Init ─────────────────────────────────────────────────────── | |||
function init() { | |||
if (initialized) return; | |||
initialized = true; | |||
tagTextNodes(); | |||
buildBar(); | |||
watchTocActive(); | |||
} | |||
// Wait for MW to finish rendering before tagging nodes | |||
if (window.mw) { | |||
mw.hook('wikipage.content').add(function () { | |||
// Reset on each page load | |||
translatableSpans = []; | |||
initialized = false; | |||
setTimeout(init, 100); | |||
}); | |||
} else { | |||
if (document.readyState === 'loading') { | |||
document.addEventListener('DOMContentLoaded', init); | |||
} else { | |||
init(); | |||
} | |||
} | } | ||
})(); | })(); | ||