MediaWiki:Common.js: Difference between revisions
Appearance
No edit summary |
No edit summary |
||
| Line 197: | Line 197: | ||
collapseAllTOC(); | collapseAllTOC(); | ||
}); | |||
/* ============================================ | |||
* Sync chevron arrow with collapsible state | |||
* ============================================ */ | |||
$(function () { | |||
$('.category-block-toggle span[class*="mw-customtoggle-cb-"]').each(function () { | |||
var $toggle = $(this); | |||
var match = $toggle.attr('class').match(/mw-customtoggle-(\S+)/); | |||
if (!match) return; | |||
var $target = $('#mw-customcollapsible-' + match[1]); | |||
if (!$target.length) return; | |||
function syncArrow() { | |||
if ($target.hasClass('mw-collapsed')) { | |||
$toggle.removeClass('is-expanded'); | |||
} else { | |||
$toggle.addClass('is-expanded'); | |||
} | |||
} | |||
// Initial state | |||
syncArrow(); | |||
// Update on click | |||
$toggle.on('click', function () { | |||
setTimeout(syncArrow, 50); | |||
}); | |||
}); | |||
}); | }); | ||
Revision as of 08:09, 13 May 2026
(function () {
function esc(text) {
return String(text || "").replace(/[&<>"]/g, function (m) {
return { "&": "&", "<": "<", ">": ">", '"': """ }[m];
});
}
function renderLink(item, extraClass) {
var cls = extraClass ? ' class="' + extraClass + '"' : "";
var href = item.href ? esc(item.href) : "#";
return '<a' + cls + ' href="' + href + '">' + esc(item.label) + "</a>";
}
function buildFooter(data) {
var columns = (data.columns || []).map(function (col) {
var items = (col.items || []).map(function (item) {
return "<li>" + renderLink(item, "soji-footer-link") + "</li>";
}).join("");
return [
'<section class="soji-footer-col">',
"<h2>" + esc(col.title) + "</h2>",
"<ul>" + items + "</ul>",
"</section>"
].join("");
}).join("");
var policies = (data.policies || []).map(function (item, i) {
return (i ? '<span class="soji-footer-sep">|</span>' : "") +
renderLink(item, "soji-footer-link");
}).join("");
var social = (data.social || []).map(function (item) {
var href = item.href ? esc(item.href) : "#";
return '<a class="soji-footer-social-item" href="' + href + '" aria-label="' + esc(item.label) + '">' +
esc(item.short || item.label.charAt(0)) + "</a>";
}).join("");
var subscribe = data.subscribe
? renderLink(data.subscribe, "soji-footer-subscribe")
: "";
return [
'<section id="soji-site-footer" class="soji-site-footer">',
'<div class="soji-footer-shell">',
'<div class="soji-footer-grid">' + columns + "</div>",
'<div class="soji-footer-bottom">',
'<div class="soji-footer-meta">',
'<div class="soji-footer-policy-row">' + policies + "</div>",
'<div class="soji-footer-copy">' + esc(data.copyright || "") + "</div>",
"</div>",
'<div class="soji-footer-actions">',
subscribe,
'<div class="soji-footer-social">',
'<span class="soji-footer-social-label">Connect</span>',
'<div class="soji-footer-social-list">' + social + "</div>",
"</div>",
"</div>",
"</div>",
"</div>",
"</section>"
].join("");
}
function injectFooter(data) {
var footer = document.getElementById("footer");
if (!footer || document.getElementById("soji-site-footer")) return;
footer.insertAdjacentHTML("beforebegin", buildFooter(data));
document.body.classList.add("has-soji-footer");
}
function init() {
fetch("/index.php?title=MediaWiki:FooterData.json&action=raw")
.then(function (r) { return r.text(); })
.then(function (text) { return JSON.parse(text); })
.then(injectFooter)
.catch(function (err) { console.error("Footer load failed:", err); });
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
})();
$(function () {
if (mw.config.get('wgNamespaceNumber') < 0) return;
var title = mw.config.get('wgTitle');
var pageName = mw.config.get('wgPageName').replace(/_/g, ' ');
// Page Main Page: không cần breadcrumb
if (pageName === 'Main Page') return;
// Lấy category trực tiếp của page hoặc của category page hiện tại
var initialCats = mw.config.get('wgCategories') || [];
if (!initialCats.length) return;
var api = new mw.Api();
var chain = [];
var seen = {};
// Đi ngược lên cây qua API
function walkUp(catName) {
if (!catName || seen[catName] || catName === 'Main Page') {
return $.Deferred().resolve();
}
seen[catName] = true;
chain.unshift(catName);
return api.get({
action: 'query',
titles: 'Category:' + catName,
prop: 'categories',
cllimit: 1,
format: 'json',
formatversion: 2
}).then(function (data) {
var pages = (data.query && data.query.pages) || [];
if (!pages.length || !pages[0].categories) return;
var parent = pages[0].categories[0].title.replace(/^Category:/, '');
return walkUp(parent);
});
}
walkUp(initialCats[0]).then(function () {
var parts = [
'<a href="' + mw.util.getUrl('Main Page') + '">Main Page</a>'
];
chain.forEach(function (c) {
parts.push('<a href="' + mw.util.getUrl(c) + '">' + c + '</a>');
});
parts.push('<strong>' + title + '</strong>');
var html = '<nav class="custom-breadcrumb">' + parts.join(' > ') + '</nav>';
$('#mw-content-text').before(html);
});
});
$(function () {
// Tự động thêm target="_blank" cho mọi link tới file .pdf
$('a[href$=".pdf"], a[href*=".pdf?"], a[href*="/file/"][href*=".pdf"]').each(function () {
$(this).attr('target', '_blank');
$(this).attr('rel', 'noopener noreferrer');
// Thêm icon PDF nhỏ
if (!$(this).find('.pdf-icon').length) {
$(this).append(' <span class="pdf-icon" title="Open PDF in new tab">📄</span>');
}
});
});
$(function () {
// Force "View" links to open in new tab
$('.doc-view a').attr({
target: '_blank',
rel: 'noopener noreferrer'
});
// Force "Download" links to trigger file download
$('.doc-download a').each(function () {
var $a = $(this);
var href = $a.attr('href');
// Extract filename from URL
var match = href.match(/\/([^\/]+\.(?:pdf|zip|doc|docx|xls|xlsx))$/i);
if (match) {
$a.attr('download', match[1]);
} else {
$a.attr('download', '');
}
});
});
/* ============================================
* Auto-collapse TOC sub-headings on page load
* Only show H2 (main Heading) by default
* ============================================ */
$(function () {
var attempts = 0;
var maxAttempts = 15;
function collapseAllTOC() {
// Find all expanded toggle buttons in TOC
var $toggles = $('.vector-toc-toggle[aria-expanded="true"]');
if ($toggles.length > 0) {
$toggles.each(function () {
// Click toggle to collapse (triggers Vue.js handler)
this.click();
});
} else if (attempts < maxAttempts) {
// TOC chưa render xong, retry
attempts++;
setTimeout(collapseAllTOC, 100);
}
}
collapseAllTOC();
});
/* ============================================
* Sync chevron arrow with collapsible state
* ============================================ */
$(function () {
$('.category-block-toggle span[class*="mw-customtoggle-cb-"]').each(function () {
var $toggle = $(this);
var match = $toggle.attr('class').match(/mw-customtoggle-(\S+)/);
if (!match) return;
var $target = $('#mw-customcollapsible-' + match[1]);
if (!$target.length) return;
function syncArrow() {
if ($target.hasClass('mw-collapsed')) {
$toggle.removeClass('is-expanded');
} else {
$toggle.addClass('is-expanded');
}
}
// Initial state
syncArrow();
// Update on click
$toggle.on('click', function () {
setTimeout(syncArrow, 50);
});
});
});