MediaWiki:Common.js
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
/* ============================================================
Marovi — Custom Reading Experience Scripts
Applied via MediaWiki:Common.js
============================================================ */
(function () {
'use strict';
/* --- Glossary Term Popups ---------------------------------- */
var popup = null;
var hideTimer = null;
function createPopup() {
if (popup) return popup;
popup = document.createElement('div');
popup.className = 'marovi-term-popup';
popup.style.display = 'none';
document.body.appendChild(popup);
popup.addEventListener('mouseenter', function () {
clearTimeout(hideTimer);
});
popup.addEventListener('mouseleave', function () {
hidePopup();
});
return popup;
}
function showPopup(term, event) {
var el = createPopup();
var definition = term.getAttribute('data-definition');
var article = term.getAttribute('data-article');
var termName = term.getAttribute('data-term');
if (!definition) return;
var html = '';
if (termName) {
html += '<div class="marovi-term-popup-title">' + escapeHtml(termName) + '</div>';
}
html += '<div class="marovi-term-popup-definition">' + escapeHtml(definition) + '</div>';
if (article) {
var articleUrl = mw.util.getUrl(article);
html += '<div class="marovi-term-popup-link"><a href="' + articleUrl + '">Read full article →</a></div>';
}
el.innerHTML = html;
el.style.display = 'block';
var rect = term.getBoundingClientRect();
var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
el.style.top = (rect.bottom + scrollTop + 8) + 'px';
el.style.left = Math.max(8, rect.left + scrollLeft - 20) + 'px';
var popupRect = el.getBoundingClientRect();
if (popupRect.right > window.innerWidth - 8) {
el.style.left = (window.innerWidth - popupRect.width - 8) + 'px';
}
}
function hidePopup() {
hideTimer = setTimeout(function () {
if (popup) {
popup.style.display = 'none';
}
}, 200);
}
function escapeHtml(text) {
var div = document.createElement('div');
div.appendChild(document.createTextNode(text));
return div.innerHTML;
}
function initGlossary() {
var terms = document.querySelectorAll('.marovi-term[data-definition]');
terms.forEach(function (term) {
term.addEventListener('mouseenter', function (e) {
clearTimeout(hideTimer);
showPopup(term, e);
});
term.addEventListener('mouseleave', function () {
hidePopup();
});
term.addEventListener('click', function (e) {
e.preventDefault();
clearTimeout(hideTimer);
showPopup(term, e);
});
});
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape' && popup) {
popup.style.display = 'none';
}
});
document.addEventListener('click', function (e) {
if (popup && !popup.contains(e.target) && !e.target.classList.contains('marovi-term')) {
popup.style.display = 'none';
}
});
}
/* --- Language Tabs ----------------------------------------- */
function initLanguageTabs() {
var title = mw.config.get('wgTitle');
var pageTitle = mw.config.get('wgPageName');
var namespaceName = mw.config.get('wgCanonicalNamespace');
if (namespaceName !== '' && namespaceName !== 'Main') return;
var parts = pageTitle.split('/');
var basePage = parts[0];
var currentLang = parts.length > 1 ? parts[parts.length - 1] : 'en';
var languages = [
{ code: 'en', label: 'EN', page: basePage },
{ code: 'es', label: 'ES', page: basePage + '/es' },
{ code: 'zh', label: '中文', page: basePage + '/zh' }
];
var pagesToCheck = languages
.filter(function (l) { return l.code !== currentLang; })
.map(function (l) { return l.page; });
if (pagesToCheck.length === 0) return;
new mw.Api().get({
action: 'query',
titles: pagesToCheck.join('|'),
formatversion: 2
}).then(function (result) {
var existingPages = {};
if (result.query && result.query.pages) {
result.query.pages.forEach(function (page) {
if (!page.missing) {
existingPages[page.title.replace(/ /g, '_')] = true;
}
});
}
existingPages[basePage.replace(/ /g, '_')] = true;
var availableLangs = languages.filter(function (l) {
return existingPages[l.page.replace(/ /g, '_')];
});
if (availableLangs.length < 2) return;
var container = document.createElement('div');
container.className = 'marovi-lang-tabs';
availableLangs.forEach(function (lang) {
var tab = document.createElement('a');
tab.className = 'marovi-lang-tab';
tab.textContent = lang.label;
tab.href = mw.util.getUrl(lang.page);
if (lang.code === currentLang) {
tab.classList.add('marovi-lang-tab--active');
}
container.appendChild(tab);
});
var heading = document.getElementById('firstHeading') || document.querySelector('.mw-first-heading');
if (heading && heading.parentNode) {
heading.parentNode.insertBefore(container, heading.nextSibling);
}
});
}
/* --- CJK Body Class --------------------------------------- */
function initCJKClass() {
var pageTitle = mw.config.get('wgPageName');
if (pageTitle.match(/\/zh$/)) {
document.body.classList.add('marovi-lang-zh');
}
}
/* --- MathJax loader --------------------------------------- */
// MediaWiki runs in $wgMathValidModes=['source'], so <math> tags are
// rendered as <span class="mwe-math-fallback-source-{inline,block} tex">
// containing $...$ / $$...$$ LaTeX. MathJax 3 reparses those on the client.
function initMathJax() {
if (!document.querySelector(
'.mwe-math-fallback-source-inline, .mwe-math-fallback-source-block'
)) {
return;
}
window.MathJax = {
tex: {
inlineMath: [['$', '$'], ['\\(', '\\)']],
displayMath: [['$$', '$$'], ['\\[', '\\]']],
processEscapes: true,
tags: 'none'
},
options: {
skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code'],
ignoreHtmlClass: 'mw-parser-output-nomath|no-mathjax',
processHtmlClass: 'mwe-math-fallback-source-inline|mwe-math-fallback-source-block|tex'
}
};
var script = document.createElement('script');
script.async = true;
script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js';
document.head.appendChild(script);
}
/* --- Init ------------------------------------------------- */
$(function () {
initGlossary();
initCJKClass();
initMathJax();
mw.loader.using('mediawiki.api').then(initLanguageTabs);
});
}());