MediaWiki:Common.js: Perbedaan antara revisi
Tidak ada ringkasan suntingan Tanda: Suntingan perangkat seluler Suntingan peramban seluler |
Tidak ada ringkasan suntingan Tanda: Suntingan perangkat seluler Suntingan peramban seluler |
||
| (32 revisi perantara oleh pengguna yang sama tidak ditampilkan) | |||
| Baris 1: | Baris 1: | ||
/* ========================================================== | /* ========================================================== | ||
🧠 MIPPEDIA DATA - SMART CONTEXT + AUTO THUMBNAIL | ⚡ MIPPEDIA SPEED OPTIMIZER - PURE SPEED & STABLE (V1.1) | ||
Features: | Features: Instant Link Pre-fetching Engine (No Animation/Pure Performance). | ||
Dynamic | Optimization: Zero-flicker, scoped lifecycle protection, advanced anti-cache clutter. | ||
Target: Built for maximum long-term stability and bulletproof caching. | |||
========================================================= */ | |||
(function() { | |||
// Ambil nama host secara dinamis (contoh: data.mippedia.org) | |||
var currentHost = window.location.hostname; | |||
// --- 🗲 ENGINE PRE-FETCH CORES --- | |||
function injectPrefetchLink(url) { | |||
// Jaring Pengaman 1: Jangan injeksi jika link prefetch untuk URL ini sudah ada di head browser | |||
if (document.querySelector('link[href="' + url + '"]')) return; | |||
var prefetcher = document.createElement('link'); | |||
prefetcher.rel = 'prefetch'; | |||
prefetcher.href = url; | |||
// Jaring Pengaman 2: Failsafe anti-eror cache jika network mendadak sibuk | |||
prefetcher.onerror = function() { prefetcher.remove(); }; | |||
document.head.appendChild(prefetcher); | |||
} | |||
function initPrefetchDispatch(href) { | |||
// Memanfaatkan requestIdleCallback agar proses prefetch hanya berjalan saat browser senggang | |||
// sehingga tidak akan membuat scrolling halaman terasa berat/patah-patah | |||
if (window.requestIdleCallback) { | |||
window.requestIdleCallback(function() { injectPrefetchLink(href); }); | |||
} else { | |||
setTimeout(function() { injectPrefetchLink(href); }, 20); | |||
} | |||
} | |||
// --- 🎮 COGNITIVE EVENT LISTENERS (UNIVERSAL HOVER & TOUCH) --- | |||
// Mendengarkan area konten utama MediaWiki (Desktop & Tampilan Mobile HP) | |||
$(document).on('mouseenter pointerdown', '#mw-content-text a, .mw-body-content a, #bodyContent a, .mw-parser-output a', function() { | |||
var $link = $(this); | |||
var href = $link.attr('href'); | |||
// --- 🛡️ FILTER STRATA TINGGI: ABREVIASI LINK BERGUNA (ANTI CRASH CACHE) --- | |||
// 1. Abaikan link kosong, javascript void, atau buka tab baru (_blank) | |||
if (!href || href.startsWith('#') || href.startsWith('javascript:') || $link.attr('target') === '_blank') return; | |||
// 2. Abaikan link merah (artikel belum ada), link sunting teks, riwayat versi, dan aksi form MediaWiki | |||
if ($link.hasClass('new') || href.includes('action=') || href.includes('diff=') || href.includes('oldid=')) return; | |||
// 3. Abaikan link perkakas krusial pengurus / sistem otentikasi (hindari prefetch logout tidak sengaja) | |||
if (href.includes('Istimewa:') || href.includes('Special:') || href.includes('do=logout')) return; | |||
// Pastikan hanya mengeksekusi routing internal ekosistem Mippedia | |||
var isInternal = href.indexOf(currentHost) !== -1 || href.startsWith('/') || !href.includes('://'); | |||
if (isInternal) { | |||
initPrefetchDispatch(href); | |||
} | |||
}); | |||
})(); | |||
/* ========================================================== | |||
🌐 MIPPEDIA TAB ROUTER INTEGRATOR - UNIVERSAL TRANSITION (V1.2) | |||
Features: Pure Pre-Navigation Routing Simulation on Tab Title. | |||
Optimization: Universal trigger for any navigation, 0% page-load flicker. | |||
Target: Clean, lightweight, long-term safety. | |||
========================================================= */ | |||
(function() { | |||
// Ambil nama host secara dinamis (contoh: data.mippedia.org) | |||
var currentHost = window.location.hostname; | |||
var routingTimeout = null; | |||
var rawOriginalTitle = document.title; | |||
// --- ENGINE CORE: UBAH JUDUL JADI DOMAIN --- | |||
function executeUniversalRouting() { | |||
if (routingTimeout) clearTimeout(routingTimeout); | |||
// Tangkap judul asli terupdate sebagai jaring pengaman | |||
var liveTitle = document.title; | |||
if (liveTitle !== currentHost) { | |||
rawOriginalTitle = liveTitle; | |||
} | |||
// SET DOMAIN DI AWAL (Saat aksi diklik / sebelum pindah halaman) | |||
document.title = currentHost; | |||
// Failsafe: Jika dalam 3 detik halaman tidak jadi pindah (batal/koneksi drop), | |||
// kembalikan ke judul semula agar tidak tersangkut. | |||
routingTimeout = setTimeout(function() { | |||
if (document.title === currentHost) { | |||
document.title = rawOriginalTitle; | |||
} | |||
}, 3000); | |||
} | |||
// --- ⚡ PEMICU 1: UNIVERSAL CLICK CONTROLLER (APAPUN YANG DIKLIK) --- | |||
// Menggunakan Vanilla JS Event Listener di tingkat window agar mencakup seluruh elemen tanpa tebang pilih | |||
window.addEventListener('click', function(e) { | |||
// Cari tahu apakah elemen yang diklik berada di dalam tag <a> (Link) | |||
var anchor = e.target.closest('a'); | |||
if (anchor) { | |||
var href = anchor.getAttribute('href'); | |||
var target = anchor.getAttribute('target'); | |||
// Abaikan jika link kosong, link jangkar internal halaman (#), atau buka di tab baru (_blank) | |||
if (!href || href.startsWith('#') || href.startsWith('javascript:') || target === '_blank') { | |||
return; | |||
} | |||
// Eksekusi jika perpindahan terjadi di domain yang sama (internal routing) | |||
var isInternal = href.indexOf(currentHost) !== -1 || href.startsWith('/') || !href.includes('://'); | |||
if (isInternal) { | |||
executeUniversalRouting(); | |||
} | |||
} else { | |||
// JIKA YANG DIKLIK BUKAN LINK (Tapi tombol biasa, widget, atau elemen aksi pemindah halaman lainnya) | |||
// Kita cek apakah elemen tersebut memiliki indikasi memicu fungsi perpindahan/loading | |||
var elementTag = e.target.tagName.toLowerCase(); | |||
if (elementTag === 'button' || e.target.closest('button') || e.target.getAttribute('role') === 'button') { | |||
executeUniversalRouting(); | |||
} | |||
} | |||
}, { passive: true }); | |||
// --- 💾 PEMICU 2: UNIVERSAL SUBMIT CONTROLLER (SIMPAN/FORM) --- | |||
// Memicu efek domain saat submit form (Simpan halaman, cari artikel, dll) | |||
window.addEventListener('submit', function() { | |||
executeUniversalRouting(); | |||
}); | |||
// --- 🛡️ SAFETY GUARDIAN: ANTI STUCK ON TAB BLUR --- | |||
// Jika user buru-buru pindah tab sebelum halaman sempat berganti, kembalikan judul asli | |||
window.addEventListener('blur', function() { | |||
if (routingTimeout) clearTimeout(routingTimeout); | |||
if (document.title === currentHost) { | |||
document.title = rawOriginalTitle; | |||
} | |||
}); | |||
window.addEventListener('pagehide', function() { | |||
if (routingTimeout) clearTimeout(routingTimeout); | |||
if (document.title === currentHost) { | |||
document.title = rawOriginalTitle; | |||
} | |||
}); | |||
})(); | |||
/* ========================================================== | |||
🧠 MIPPEDIA DATA - SMART CONTEXT + AUTO THUMBNAIL (V10.1) | |||
Features: Priority Check (ID > EN > CONCISE), Auto-Age, | |||
Auto-Bold, Smart Links, Dynamic Context, | |||
Targeted Isolation (Safe for Forms), Dynamic Noindex, | |||
Aesthetic Centered Empty State + 🚀 Instant Action Hub (3 Buttons) | |||
Fixes: Mobile View Visibility & Custom Short URL Route (No /w/) | |||
========================================================= */ | |||
(function() { | (function() { | ||
$(document).ready(function() { | $(document).ready(function() { | ||
var $descSection = $('#mip-desc-section'), $descBox = $('#mip-auto-description'), | var $descSection = $('#mip-desc-section'), $descBox = $('#mip-auto-description'), | ||
$sourceInfo = $('#mip-source-info'), $portalLinks = $('#mip-portal-links'), | $sourceInfo = $('#mip-source-info'), $portalLinks = $('#mip-portal-links'), | ||
$projectPortal = $('#mip-project-portal'); | $projectPortal = $('#mip-project-portal'), $mainContent = $('#mw-content-text'); | ||
if (!$descBox.length) return; | if (!$descBox.length) return; | ||
| Baris 16: | Baris 166: | ||
currentYear = 2026; | currentYear = 2026; | ||
// --- KONFIGURASI PROYEK --- | |||
var projects = [ | var projects = [ | ||
{ id: 'id', name: 'Mippedia bahasa Indonesia', url: 'https://id.mippedia.org/api.php', base: 'https://id.mippedia.org/wiki/', label: ' | { id: 'id', name: 'Mippedia bahasa Indonesia', url: 'https://id.mippedia.org/api.php', base: 'https://id.mippedia.org/wiki/', label: 'Mippedia Indonesia' }, | ||
{ id: 'en', name: 'Mippedia bahasa Inggris', url: 'https://en.mippedia.org/api.php', base: 'https://en.mippedia.org/wiki/', label: ' | { id: 'en', name: 'Mippedia bahasa Inggris', url: 'https://en.mippedia.org/api.php', base: 'https://en.mippedia.org/wiki/', label: 'Mippedia Inggris' }, | ||
{ id: 'concise', name: 'Mippedia bahasa Indonesia ringkas', url: 'https://concise.mippedia.org/api.php', base: 'https://concise.mippedia.org/wiki/', label: ' | { id: 'concise', name: 'Mippedia bahasa Indonesia ringkas', url: 'https://concise.mippedia.org/api.php', base: 'https://concise.mippedia.org/wiki/', label: 'Mippedia Ringkas' } | ||
]; | ]; | ||
// --- | // --- 🎨 ANIMASI, HOVER EFFECT & RESPONSIVE STYLE --- | ||
if (!$('#mip-clean-style').length) { | |||
$('head').append( | |||
'<style id="mip-clean-style">' + | |||
'@keyframes mipFadeInUp {' + | |||
'from { opacity: 0; transform: translateY(15px); }' + | |||
'to { opacity: 1; transform: translateY(0); }' + | |||
'}' + | |||
'#mip-empty-state-notice {' + | |||
'display: block !important;' + /* Memaksa tampil di seluler/mobile */ | |||
'animation: mipFadeInUp 0.6s cubic-bezier(0.16, 1, 0.3, 1) forwards;' + | |||
'}' + | |||
'.mip-btn-container {' + | |||
'display: flex;' + | |||
'flex-wrap: wrap;' + /* Supaya otomatis turun ke bawah kalau di HP */ | |||
'justify-content: center;' + | |||
'margin-bottom: 30px;' + | |||
'padding-top: 5px;' + | |||
'}' + | |||
'.mip-action-btn {' + | |||
'display: inline-flex; align-items: center; justify-content: center;' + | |||
'padding: 10px 18px; margin: 6px; font-size: 0.88em; font-weight: 600;' + | |||
'color: #6a5acd !important; background: #ffffff; border: 1.5px solid #6a5acd;' + | |||
'border-radius: 30px; text-decoration: none !important; cursor: pointer;' + | |||
'transition: all 0.25s ease-in-out; box-shadow: 0 2px 5px rgba(106,92,205,0.08);' + | |||
'flex-grow: 1; max-width: 220px; min-width: 150px;' + /* Penyesuaian porsi tombol di HP */ | |||
'}' + | |||
'.mip-action-btn:hover {' + | |||
'color: #ffffff !important; background: #6a5acd;' + | |||
'transform: translateY(-2px); box-shadow: 0 4px 12px rgba(106,92,205,0.2);' + | |||
'}' + | |||
'.mip-action-btn:active {' + | |||
'transform: translateY(0);' + | |||
'}' + | |||
'</style>' | |||
); | |||
} | |||
// --- FUNGSI TOOLS --- | |||
function applyAutoBold(text) { return text.replace(new RegExp('(' + pageTitle + ')', 'gi'), '<strong>$1</strong>'); } | function applyAutoBold(text) { return text.replace(new RegExp('(' + pageTitle + ')', 'gi'), '<strong>$1</strong>'); } | ||
function cleanExtract(text) { return text.replace(/\[\d+\]/g, '').replace(/\{\{[^}]+\}\}/g, '').replace(/\(\s*\)/g, '').replace(/\s\s+/g, ' ').trim(); } | function cleanExtract(text) { return text.replace(/\[\d+\]/g, '').replace(/\{\{[^}]+\}\}/g, '').replace(/\(\s*\)/g, '').replace(/\s\s+/g, ' ').trim(); } | ||
| Baris 31: | Baris 220: | ||
var bulan = ["Januari", "Februari", "Maret", "April", "Mei", "Juni", "Juli", "Agustus", "September", "Oktober", "November", "Desember"]; | var bulan = ["Januari", "Februari", "Maret", "April", "Mei", "Juni", "Juli", "Agustus", "September", "Oktober", "November", "Desember"]; | ||
var tglSkrg = hari[date.getDay()] + ", " + date.getDate() + " " + bulan[date.getMonth()] + " " + date.getFullYear(); | var tglSkrg = hari[date.getDay()] + ", " + date.getDate() + " " + bulan[date.getMonth()] + " " + date.getFullYear(); | ||
return text.replace(/(hari ini|saat ini|sekarang)/gi, '$1 (' + tglSkrg + ')') | |||
return text | |||
.replace(/(\d{1,2}\s(?:Januari|Februari|Maret|April|Mei|Juni|Juli|Agustus|September|Oktober|November|Desember)\s(\d{4}))/gi, function(m, f, y) { | .replace(/(\d{1,2}\s(?:Januari|Februari|Maret|April|Mei|Juni|Juli|Agustus|September|Oktober|November|Desember)\s(\d{4}))/gi, function(m, f, y) { | ||
return f + " <span style='color: #444; font-weight: bold;'>– usia " + (currentYear - parseInt(y)) + " tahun</span>"; | return f + " <span style='color: #444; font-weight: bold;'>– usia " + (currentYear - parseInt(y)) + " tahun</span>"; | ||
| Baris 50: | Baris 237: | ||
} | } | ||
function buildSmartLinks(currentId) { | // --- SMART LINKS PORTAL --- | ||
function buildSmartLinks(currentId, validatedEnTitle) { | |||
$portalLinks.empty(); | $portalLinks.empty(); | ||
var validLinks = []; | var validLinks = []; | ||
var checkRequests = projects.filter(p => p.id !== currentId).map(function(p) { | var checkRequests = projects.filter(p => p.id !== currentId).map(function(p) { | ||
return $.ajax({ url: p.url, data: { action: 'query', titles: | var targetTitle = (p.id === 'en' && validatedEnTitle) ? validatedEnTitle : pageTitle; | ||
.then(function(data) { if (data.query.pages["-1"] === undefined) { validLinks.push('<a href="' + p.base + encodeURIComponent( | if (p.id === 'en' && !validatedEnTitle) return $.Deferred().resolve(); | ||
return $.ajax({ url: p.url, data: { action: 'query', titles: targetTitle, format: 'json', origin: '*' }, dataType: 'json' }) | |||
.then(function(data) { | |||
if (data.query.pages["-1"] === undefined) { | |||
validLinks.push('<a href="' + p.base + encodeURIComponent(targetTitle) + '" target="_blank" style="color: #6a5acd; text-decoration: underline; font-weight: bold;">' + p.label + '</a>'); | |||
} | |||
}); | |||
}); | }); | ||
$.when.apply($, checkRequests).done(function() { | $.when.apply($, checkRequests).done(function() { | ||
if (validLinks.length > 0) { $portalLinks.html(validLinks.join(' <span style="color:#ccc; margin: 0 5px;">•</span> ')); $projectPortal.fadeIn(); } | if (validLinks.length > 0) { $portalLinks.html(validLinks.join(' <span style="color:#ccc; margin: 0 5px;">•</span> ')); $projectPortal.fadeIn(); } | ||
| Baris 62: | Baris 259: | ||
} | } | ||
// --- SISTEM | // --- 🧠 RENDER EMPTY STATE + INTERACTIVE HUB --- | ||
function toggleMippediaLayout(isEmpty) { | |||
var $existingNotice = $('#mip-empty-state-notice'); | |||
var userGroups = mw.config.get('wgUserGroups') || []; | |||
var isSysop = userGroups.indexOf('sysop') !== -1; | |||
var $wikiDataElements = $mainContent.children().not('#mip-empty-state-notice, #mip-desc-section, script, style, .printfooter'); | |||
var $editElements = $('#ca-edit, .mw-editsection, .wb-editsection, .mip-edit-trigger, [id*="edit"], [class*="edit-pencil"]'); | |||
var $metaRobots = $('meta[name="robots"]'); | |||
if (isEmpty) { | |||
if (!$existingNotice.length) { | |||
// Perbaikan Jalur URL tanpa sub-folder /w/ langsung menembak Short URL root parameter | |||
var createUrlID = 'https://id.mippedia.org/index.php?title=' + encodeURIComponent(pageTitle) + '&action=edit'; | |||
var createUrlEN = 'https://en.mippedia.org/index.php?title=' + encodeURIComponent(pageTitle) + '&action=edit'; | |||
var createUrlConcise = 'https://concise.mippedia.org/index.php?title=' + encodeURIComponent(pageTitle) + '&action=edit'; | |||
var noticeHtml = | |||
'<div id="mip-empty-state-notice" style="text-align: center; max-width: 600px; margin: 40px auto; padding: 10px; font-family: -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, sans-serif;">' + | |||
'<!-- Ikon Warning Besar -->' + | |||
'<div style="margin-bottom: 22px; display: inline-block;">' + | |||
'<svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="#d9534f" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">' + | |||
'<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path>' + | |||
'<line x1="12" y1="9" x2="12" y2="13"></line>' + | |||
'<line x1="12" y1="17" x2="12.01" y2="17"></line>' + | |||
'</' + 'svg>' + | |||
'</div>' + | |||
'<!-- Judul Utama -->' + | |||
'<h4 style="color: #2c3e50; margin: 0 0 12px 0; font-size: 1.35em; font-weight: 700; letter-spacing: -0.3px;">Item Data Struktur Tidak Memiliki Sumber Artikel</h4>' + | |||
'<!-- Deskripsi Utama -->' + | |||
'<p style="color: #555; margin: 0 0 25px 0; font-size: 0.98em; line-height: 1.6;">' + | |||
'Item data ini saat ini <strong>tidak memiliki artikel rujukan satu pun</strong> di Mippedia Indonesia, Mippedia Inggris, maupun Mippedia Indonesia Ringkas.' + | |||
'</p>' + | |||
'<!-- 🚀 INSTANT ACTION HUB (Flexbox-Responsive Mobile & URL No /w/) -->' + | |||
'<div class="mip-btn-container">' + | |||
'<a href="' + createUrlID + '" target="_blank" class="mip-action-btn">' + | |||
'<span style="margin-right: 6px;"></span>Buat Artikel ID' + | |||
'</a>' + | |||
'<a href="' + createUrlEN + '" target="_blank" class="mip-action-btn">' + | |||
'<span style="margin-right: 6px;"></span>Buat Artikel EN' + | |||
'</a>' + | |||
'<a href="' + createUrlConcise + '" target="_blank" class="mip-action-btn">' + | |||
'<span style="margin-right: 6px;"></span>Buat Artikel Ringkas' + | |||
'</a>' + | |||
'</div>' + | |||
'<!-- Catatan Kaki Info Kecil -->' + | |||
'<div style="font-size: 0.85em; color: #a0a0a0; line-height: 1.5; font-style: italic; border-top: 1px solid #f0f0f0; padding-top: 15px;">' + | |||
'Informasi: Item data yang tidak terikat dengan artikel ensiklopedia apa pun dalam ekosistem Mippedia akan dihapus secara otomatis oleh pengurus dalam waktu dekat.' + | |||
'</div>' + | |||
'</div>'; | |||
$mainContent.prepend(noticeHtml); | |||
} | |||
$wikiDataElements.hide(); | |||
$descSection.hide(); | |||
if (!isSysop) { | |||
$editElements.css({ 'pointer-events': 'none', 'opacity': '0.3', 'cursor': 'not-allowed' }).hide(); | |||
$(document).on('keydown.miplock', function(e) { | |||
if (e.key.toLowerCase() === 'e' && !$(e.target).is('input, textarea')) { | |||
e.preventDefault(); | |||
return false; | |||
} | |||
}); | |||
} | |||
mw.config.set('wgRobotsPolicies', { 'index': 'noindex', 'follow': 'nofollow' }); | |||
if ($metaRobots.length) { | |||
$metaRobots.attr('content', 'noindex, nofollow'); | |||
} else { | |||
$('head').append('<meta name="robots" content="noindex, nofollow">'); | |||
} | |||
} else { | |||
if ($existingNotice.length) $existingNotice.remove(); | |||
$wikiDataElements.show(); | |||
$descSection.show(); | |||
if (!isSysop) { | |||
$(document).off('keydown.miplock'); | |||
} | |||
mw.config.set('wgRobotsPolicies', { 'index': 'index', 'follow': 'follow' }); | |||
if ($metaRobots.length) { | |||
$metaRobots.attr('content', 'index, follow'); | |||
} | |||
} | |||
} | |||
// --- SISTEM PRIORITAS --- | |||
var cached = JSON.parse(localStorage.getItem(cacheKey) || "{}"); | var cached = JSON.parse(localStorage.getItem(cacheKey) || "{}"); | ||
var globalEnTitle = cached.enTitle || ""; | |||
function init() { | function init() { | ||
var | var pPrimary = projects[0]; | ||
$.ajax({ | $.ajax({ | ||
url: | url: pPrimary.url, | ||
data: { action: 'query', prop: 'revisions|pageimages', rvprop: 'timestamp', piprop: 'thumbnail', pithumbsize: 150, titles: pageTitle, format: 'json', origin: '*' }, | data: { action: 'query', prop: 'revisions|pageimages|langlinks', lllang: 'en', rvprop: 'timestamp', piprop: 'thumbnail', pithumbsize: 150, titles: pageTitle, format: 'json', origin: '*' }, | ||
dataType: 'json', | dataType: 'json', | ||
success: function(res) { | success: function(res) { | ||
var pg = res.query.pages, id = Object.keys(pg)[0]; | var pg = res.query.pages, id = Object.keys(pg)[0]; | ||
var latestTS = (id != "-1") ? pg[id].revisions[0].timestamp : "0"; | var latestTS = (id != "-1") ? pg[id].revisions[0].timestamp : "0"; | ||
var thumbUrl = (id != "-1" && pg[id].thumbnail) ? pg[id].thumbnail.source : ""; | var thumbUrl = (id != "-1" && pg[id].thumbnail) ? pg[id].thumbnail.source : ""; | ||
if (id != "-1" && pg[id].langlinks && pg[id].langlinks[0]) { | |||
globalEnTitle = pg[id].langlinks[0]['*']; | |||
} | |||
if (cached.content && cached.ts === latestTS) { | if (cached.content && cached.ts === latestTS && latestTS !== "0" && !cached.isEmpty) { | ||
renderAll(cached.p, cached.content, cached.isTrans, cached.orig, cached.img); | toggleMippediaLayout(false); | ||
renderAll(cached.p, cached.content, cached.isTrans, cached.orig, cached.img, cached.customUrl, cached.enTitle); | |||
} else { | } else { | ||
fetchPriorityData(0, latestTS, thumbUrl); | |||
} | } | ||
}, | }, | ||
error: function() { if (cached.content) renderAll(cached.p, cached.content, cached.isTrans, cached.orig, cached.img); } | error: function() { | ||
if (cached.content) { | |||
toggleMippediaLayout(cached.isEmpty || false); | |||
if (!cached.isEmpty) renderAll(cached.p, cached.content, cached.isTrans, cached.orig, cached.img, cached.customUrl, cached.enTitle); | |||
} | |||
} | |||
}); | }); | ||
} | } | ||
function | function fetchPriorityData(index, newTS, img) { | ||
if (index >= projects.length) { | |||
var | localStorage.setItem(cacheKey, JSON.stringify({isEmpty: true, ts: newTS, content: ""})); | ||
toggleMippediaLayout(true); | |||
return; | |||
} | |||
var p = projects[index]; | |||
if (p.id === 'en') { | |||
if (!globalEnTitle) { | |||
$.ajax({ | |||
url: 'https://concise.mippedia.org/api.php', | |||
data: { action: 'query', prop: 'langlinks', lllang: 'en', titles: pageTitle, format: 'json', origin: '*' }, | |||
dataType: 'json', | |||
async: false, | |||
success: function(cRes) { | |||
var cPg = cRes.query.pages, cId = Object.keys(cPg)[0]; | |||
if (cId != "-1" && cPg[cId].langlinks && cPg[cId].langlinks[0]) { | |||
globalEnTitle = cPg[cId].langlinks[0]['*']; | |||
} | |||
} | |||
}); | |||
} | |||
if (globalEnTitle) { | |||
$.ajax({ | |||
url: p.url, | |||
data: { action: 'query', prop: 'extracts', exintro: 1, explaintext: 1, titles: globalEnTitle, format: 'json', origin: '*' }, | |||
dataType: 'json', | |||
success: function(data) { | |||
var pages = data.query.pages, pageId = Object.keys(pages)[0]; | |||
if (pageId != "-1" && pages[pageId].extract) { | |||
var extract = cleanExtract(pages[pageId].extract); | |||
finalize(p, extract, false, "", newTS, img, p.base + encodeURIComponent(globalEnTitle), globalEnTitle); | |||
} else { | |||
fetchPriorityData(index + 1, newTS, img); | |||
} | |||
}, | |||
error: function() { fetchPriorityData(index + 1, newTS, img); } | |||
}); | |||
} else { | |||
fetchPriorityData(index + 1, newTS, img); | |||
} | |||
} else { | |||
$.ajax({ | $.ajax({ | ||
url: p.url, | url: p.url, | ||
| Baris 97: | Baris 436: | ||
dataType: 'json', | dataType: 'json', | ||
success: function(data) { | success: function(data) { | ||
var pages = data.query.pages, pageId = Object.keys(pages)[0]; | var pages = data.query.pages, pageId = Object.keys(pages)[0]; | ||
if (pageId != "-1" && pages[pageId].extract) { | if (pageId != "-1" && pages[pageId].extract) { | ||
var extract = cleanExtract(pages[pageId].extract); | var extract = cleanExtract(pages[pageId].extract); | ||
finalize(p, extract, false, "", newTS, img, p.base + encodeURIComponent(pageTitle), globalEnTitle); | |||
} else { | |||
fetchPriorityData(index + 1, newTS, img); | |||
} | } | ||
} | }, | ||
error: function() { fetchPriorityData(index + 1, newTS, img); } | |||
}); | }); | ||
} | } | ||
} | } | ||
function finalize(p, c, t, o, ts, img) { | function finalize(p, c, t, o, ts, img, customUrl, enTitle) { | ||
localStorage.setItem(cacheKey, JSON.stringify({p:p, content:c, isTrans:t, orig:o, ts:ts, img:img})); | localStorage.setItem(cacheKey, JSON.stringify({isEmpty: false, p:p, content:c, isTrans:t, orig:o, ts:ts, img:img, customUrl: customUrl, enTitle: enTitle})); | ||
renderAll(p, c, t, o, img); | toggleMippediaLayout(false); | ||
renderAll(p, c, t, o, img, customUrl, enTitle); | |||
} | } | ||
function renderAll(p, currentText, isTranslated, originalText, img) { | function renderAll(p, currentText, isTranslated, originalText, img, customUrl, enTitle) { | ||
var processedText = applyAutoBold(applyDynamicContext(currentText)); | var processedText = applyAutoBold(applyDynamicContext(currentText)); | ||
var thumbHtml = img ? '<div style="float: right; margin-left: 15px; margin-bottom: 10px; border: 1px solid #ddd; padding: 3px; background: #fff; border-radius: 4px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);"><img src="'+img+'" style="max-width: 100px; display: block; height: auto;"></div>' : ''; | var thumbHtml = img ? '<div style="float: right; margin-left: 15px; margin-bottom: 10px; border: 1px solid #ddd; padding: 3px; background: #fff; border-radius: 4px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);"><img src="'+img+'" style="max-width: 100px; display: block; height: auto;"></div>' : ''; | ||
if (!$mainContent.find('#mip-desc-section').length) { | |||
$mainContent.prepend($descSection); | |||
} | |||
$descSection.show(); | $descSection.show(); | ||
$descBox.show().html(thumbHtml + applySummary(processedText) + '<div style="clear:both;"></div>'); | $descBox.show().html(thumbHtml + applySummary(processedText) + '<div style="clear:both;"></div>'); | ||
var footer = '<div style="font-size: 0.9em; color: #777;">Sumber | var targetLink = customUrl ? customUrl : p.base + encodeURIComponent(pageTitle); | ||
var footer = '<div style="font-size: 0.9em; color: #777;">Sumber : <a href="' + targetLink + '" target="_blank" style="color: #6a5acd; font-weight: bold; text-decoration: none;">' + p.name + '.</a>'; | |||
if (isTranslated) { footer += '<br><span style="font-size: 0.85em; font-style: italic;">(Diterjemahkan secara otomatis)</span> <span id="mip-toggle-orig" style="color: #6a5acd; cursor: pointer; text-decoration: underline; margin-left: 5px;">Tampilkan versi asli</span>'; } | if (isTranslated) { footer += '<br><span style="font-size: 0.85em; font-style: italic;">(Diterjemahkan secara otomatis)</span> <span id="mip-toggle-orig" style="color: #6a5acd; cursor: pointer; text-decoration: underline; margin-left: 5px;">Tampilkan versi asli</span>'; } | ||
$sourceInfo.show().html(footer); | $sourceInfo.show().html(footer); | ||
buildSmartLinks(p.id); | buildSmartLinks(p.id, enTitle); | ||
$(document).off('click', '.mip-read-btn').on('click', '.mip-read-btn', function() { $(this).hide(); $('.mip-dots').hide(); $('.mip-more').fadeIn(); }); | $(document).off('click', '.mip-read-btn').on('click', '.mip-read-btn', function() { $(this).hide(); $('.mip-dots').hide(); $('.mip-more').fadeIn(); }); | ||
$(document).off('click', '#mip-toggle-orig').on('click', '#mip-toggle-orig', function() { | $(document).off('click', '#mip-toggle-orig').on('click', '#mip-toggle-orig', function() { | ||
var isOrig = ($(this).text() === 'Tampilkan versi asli'); | var isOrig = ($(this).text() === 'Tampilkan versi asli'); | ||
var textToDisplay = applyAutoBold(applyDynamicContext(isOrig ? originalText : currentText)); | var textToDisplay = applyAutoBold(applyDynamicContext(isOrig ? originalText : currentText)); | ||
$descBox.html(thumbHtml + applySummary(textToDisplay) + '<div style="clear:both;"></div>'); | $descBox.html(thumbHtml + applySummary(textToDisplay) + '<div style="clear:both;"></div>'); | ||
$(this).text(isOrig ? 'Tampilkan terjemahan' : 'Tampilkan versi asli'); | $(this).text(isOrig ? 'Tampilkan terjemahan' : 'Tampilkan versi asli'); | ||
| Baris 531: | Baris 865: | ||
/* ========================================================== | /* ========================================================== | ||
🌐 MIPPEDIA SHORTDESC - DATA CENTER | 🌐 MIPPEDIA SHORTDESC - DATA CENTER (PREMIUM UI) | ||
Status: INDEPENDENT | Status: INDEPENDENT & REFINED VISUAL | ||
Storage: MediaWiki:ShortDesc-Data.json | Storage: MediaWiki:ShortDesc-Data.json | ||
========================================================== */ | ========================================================== */ | ||
| Baris 541: | Baris 875: | ||
var pageTitle = mw.config.get('wgPageName'); | var pageTitle = mw.config.get('wgPageName'); | ||
var storagePage = 'MediaWiki:ShortDesc-Data.json'; | var storagePage = 'MediaWiki:ShortDesc-Data.json'; | ||
var isAdmin = mw.config.get('wgUserGroups').some(g => ['sysop', 'interface-admin'].includes(g)); | var isAdmin = mw.config.get('wgUserGroups').some(g => ['sysop', 'interface-admin'].includes(g)); | ||
// UI Box | var langs = { | ||
id: { label: 'Mippedia bahasa Indonesia', short: 'ID', color: '#6366f1' }, | |||
en: { label: 'Mippedia bahasa Inggris', short: 'EN', color: '#3b82f6' }, | |||
concise: { label: 'Mippedia bahasa Indonesia ringkas', short: 'CON', color: '#14b8a6' } | |||
}; | |||
// UI Box dengan Spasi dan Desain Baru | |||
var html = ` | var html = ` | ||
<div id="desc-manager" style="border: | <div id="desc-manager" style="background:#ffffff; border:1px solid #f1f5f9; border-left:4px solid #6366f1; border-radius:12px; padding:20px; margin: 20px 0 25px 0; box-shadow:0 4px 12px rgba(0,0,0,0.03); font-family: -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;"> | ||
<div style="font-weight: | <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:12px;"> | ||
<span style="font-size:13px; font-weight:700; color:#475569; text-transform:uppercase; letter-spacing:0.5px; display:flex; align-items:center; gap:6px;"> | |||
< | Label Deskripsi | ||
</span> | |||
<span id="desc-open-editor" style="cursor:pointer; padding:5px; transition:0.2s;" title="Edit Deskripsi"> | |||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg> | |||
</span> | |||
</div> | </div> | ||
<div id="desc- | <div id="desc-status-display" style="display:flex; flex-direction:column; gap:8px;"> | ||
< | <small style="color:#94a3b8; font-style:italic;">Sinkronisasi data...</small> | ||
</div> | |||
<div id="desc-editor" style="display:none; margin-top:18px; border-top:1px dashed #e2e8f0; padding-top:18px;"> | |||
${Object.keys(langs).map(k => ` | |||
<div style="margin-bottom:12px;"> | |||
<label style="display:block; font-size:11px; font-weight:bold; color:#64748b; margin-bottom:4px;">${langs[k].label}</label> | |||
<input type="text" id="desc-in-${k}" placeholder="Tulis deskripsi..." style="width:100%; padding:10px; font-size:13px; border:1px solid #e2e8f0; border-radius:8px; box-sizing:border-box; outline:none; focus:border-color:#6366f1;"> | |||
</div> | |||
`).join('')} | |||
<button id="desc-save" style="width:100%; padding:12px; background:#6366f1; color:white; border:none; border-radius:8px; font-size:13px; font-weight:600; cursor:pointer; margin-top:5px;">Terbitkan</button> | |||
<button id="desc-save" style="width:100%; background:# | |||
</div> | </div> | ||
</div>`; | </div>`; | ||
| Baris 572: | Baris 913: | ||
$('.mw-parser-output').prepend(html); | $('.mw-parser-output').prepend(html); | ||
function loadDescStatus() { | |||
new mw.Api().get({ action: 'query', prop: 'revisions', titles: storagePage, rvprop: 'content', formatversion: 2 }).done(function(res) { | |||
var content = res.query.pages[0].revisions ? res.query.pages[0].revisions[0].content : "{}"; | |||
var fullData = JSON.parse(content); | |||
var myData = fullData[pageTitle] || {}; | |||
var $statusBox = $('#desc-status-display').empty(); | |||
var foundAny = false; | |||
Object.keys(langs).forEach(k => { | |||
var val = myData[k]; | |||
if (val && val.trim() !== "") { | |||
foundAny = true; | |||
$statusBox.append(` | |||
<div style="display:flex; align-items:center; gap:10px;"> | |||
<span style="background:${langs[k].color}15; color:${langs[k].color}; padding:3px 10px; border-radius:20px; font-size:10px; font-weight:800; border:1px solid ${langs[k].color}30; min-width:35px; text-align:center;">${langs[k].short}</span> | |||
<span style="font-size:13px; color:#334155; line-height:1.4;">${val}</span> | |||
</div> | |||
`); | |||
$('#desc-in-' + k).val(val); | |||
} | |||
}); | |||
if (!foundAny) { | |||
$statusBox.html('<div style="color:#cbd5e0; font-size:13px; background:#f8fafc; padding:10px; border-radius:8px; border:1px dashed #e2e8f0;">Belum ada metadata deskripsi yang dikaitkan dengan entitas ini.</div>'); | |||
} | |||
}); | |||
} | |||
} | |||
$('#desc- | $(document).on('click', '#desc-open-editor', function() { $('#desc-editor').slideToggle(); }); | ||
$(document).on('click', '#desc-save', function() { | |||
if (!isAdmin) { alert('Akses ditolak. Anda memerlukan izin pengurus.'); return; } | |||
if (!isAdmin) { alert(' | var $btn = $(this).text('Proses Sinkronisasi...').css('opacity','0.7'); | ||
var api = new mw.Api(); | var api = new mw.Api(); | ||
var newData = { | var newData = { id: $('#desc-in-id').val().trim(), en: $('#desc-in-en').val().trim(), concise: $('#desc-in-concise').val().trim() }; | ||
api.get({ action: 'query', prop: 'revisions', titles: storagePage, rvprop: 'content', formatversion: 2 }).done(function(r) { | api.get({ action: 'query', prop: 'revisions', titles: storagePage, rvprop: 'content', formatversion: 2 }).done(function(r) { | ||
var json = r.query.pages[0].revisions ? JSON.parse(r.query.pages[0].revisions[0].content) : {}; | var json = r.query.pages[0].revisions ? JSON.parse(r.query.pages[0].revisions[0].content) : {}; | ||
json[pageTitle] = newData; | if (!newData.id && !newData.en && !newData.concise) { delete json[pageTitle]; } else { json[pageTitle] = newData; } | ||
api.postWithEditToken({ | api.postWithEditToken({ | ||
| Baris 606: | Baris 957: | ||
title: storagePage, | title: storagePage, | ||
text: JSON.stringify(json, null, 2), | text: JSON.stringify(json, null, 2), | ||
summary: 'Update | summary: 'Update Entity Description: ' + pageTitle | ||
}).done(function() { | }).done(function() { location.reload(); }); | ||
}); | }); | ||
}); | }); | ||
loadDescStatus(); | |||
}); | }); | ||
})(jQuery, mediaWiki); | })(jQuery, mediaWiki); | ||
/* ========================================================== | |||
📍 MIPPEDIA DATA - ULTIMATE TEXT-TO-MAP LOCATOR | |||
Fitur: Deteksi lokasi dari teks dengan database label lengkap | |||
========================================================== */ | |||
(function() { | |||
$(document).ready(function() { | |||
var locationText = ""; | |||
// --- DAFTAR LABEL SUPER LENGKAP & MASUK AKAL --- | |||
var locationLabels = [ | |||
// Umum & Geografis | |||
'lokasi', 'tempat', 'wilayah', 'daerah', 'koordinat', 'alamat', | |||
// Orang (Biografi) | |||
'tempat lahir', 'asal', 'kediaman', 'domisili', 'negara asal', | |||
// Organisasi & Perusahaan | |||
'kantor pusat', 'markas', 'cabang utama', 'titik operasi', | |||
// Makanan & Budaya | |||
'asal wilayah', 'daerah asal', 'negara sumber', 'kawasan', | |||
// Sejarah & Bangunan | |||
'letak', 'posisi', 'titik temu', 'orbit' | |||
]; | |||
// Jalankan pencarian di tabel data (infobox atau wikitable) | |||
$('.wikitable tr, .infobox tr, .mip-data-table tr').each(function() { | |||
var label = $(this).find('th').text().toLowerCase().trim(); | |||
// Hilangkan karakter spesial kayak titik dua biar matching-nya akurat | |||
label = label.replace(/[:]/g, ''); | |||
var isMatch = locationLabels.some(function(target) { | |||
return label === target || label.includes(target); | |||
}); | |||
if (isMatch) { | |||
var rawValue = $(this).find('td').text().trim(); | |||
// Ambil bagian utama sebelum tanda koma, kurung, atau titik koma | |||
// Misal: "Bandung, Jawa Barat" -> ambil "Bandung" | |||
locationText = rawValue.split(/[,(\[;]/)[0].trim(); | |||
if (locationText.length > 2) return false; // Stop jika sudah ketemu yang valid | |||
} | |||
}); | |||
// Eksekusi Render Peta | |||
if (locationText && locationText.length > 2) { | |||
var $mapWrapper = $('<div>').css({ | |||
'margin': '15px 0', | |||
'padding': '12px', | |||
'border': '1px solid #ddd', | |||
'background': '#ffffff', | |||
'border-radius': '10px', | |||
'box-shadow': '0 4px 12px rgba(0,0,0,0.08)' | |||
}); | |||
$mapWrapper.append('<div style="font-weight:bold; font-size:0.9em; margin-bottom:10px; color:#444; display:flex; align-items:center;">' + | |||
'<span style="margin-right:8px;">🗺️</span>Pratinjau Lokasi : ' + locationText + '</div>'); | |||
// Embed URL dengan pencarian teks (Nominatim) | |||
var embedUrl = "https://www.openstreetmap.org/export/embed.html?layer=mapnik&q=" + encodeURIComponent(locationText); | |||
var $iframe = $('<iframe>').attr({ | |||
'src': embedUrl, | |||
'width': '100%', | |||
'height': '280', | |||
'frameborder': '0', | |||
'style': 'border-radius: 8px; border: 1px solid #eee;' | |||
}); | |||
$mapWrapper.append($iframe); | |||
// Link eksternal yang lebih rapi | |||
var gMapsUrl = "https://www.google.com/maps/search/" + encodeURIComponent(locationText); | |||
$mapWrapper.append('<div style="text-align:right; margin-top:8px;">' + | |||
'<a href="' + gMapsUrl + '" target="_blank" style="font-size:0.75em; color:#6a5acd; font-weight:bold; text-decoration:none; text-transform:uppercase; letter-spacing:0.5px;">Buka di Google Maps →</a></div>'); | |||
// Masukkan ke bawah section deskripsi otomatis | |||
if ($('#mip-desc-section').length) { | |||
$('#mip-desc-section').after($mapWrapper); | |||
} | |||
} | |||
}); | |||
})(); | |||