MediaWiki:Common.js
Tampilan
Catatan: Setelah menerbitkan, Anda mungkin perlu melewati tembolok peramban web Anda untuk melihat perubahan.
- Firefox/Safari: Tekan dan tahan Shift sembari mengeklik Reload, atau tekan Ctrl-F5 atau Ctrl-R (⌘-R di Mac)
- Google Chrome: Tekan Ctrl-Shift-R (⌘-Shift-R di Mac)
- Edge: Tahan Ctrl sembari mengeklik Refresh, atau tekan Ctrl-F5
/**
* ====================================================================
* MIPPEDIA DATA - ADVANCED STRUCTURED DATA SYSTEM (V8.7 SECURE ULTIMATE)
* Fitur: ALL ORIGINAL V8 FEATURES KEPT + Global Social Media Box,
* Smart Autocomplete/Preview Wikilink, Ultra-Strict Security, Mobile Ready.
* ====================================================================
*/
(function($, mw) {
'use strict';
if (mw.config.get('wgNamespaceNumber') !== 0 || mw.config.get('wgIsMainPage')) return;
if (mw.config.get('wgAction') !== 'view') return;
mw.loader.using(['mediawiki.api', 'mediawiki.util']).then(function() {
var pageName = mw.config.get('wgPageName');
var isSysop = mw.config.get('wgUserGroups').indexOf('sysop') !== -1;
var dataItems = [];
var isLocked = false;
var api = new mw.Api();
var DRAFT_KEY = 'mippedia_data_draft_' + pageName;
// 1. DATA MASTER PLATFORM MEDIA SOSIAL & MUSIK (FULL ASLI)
var SOCIAL_PLATFORMS = {
applemusic: { name: 'Apple Music', base: 'https://music.apple.com/artist/', color: '#fc3c44', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm3.32 12.383c-.328.232-.82.418-1.32.418-1.238 0-1.922-.828-1.922-2.191v-4.14l4.312-.868v1.398l-2.91.586v2.805c0 .734.336 1.148.977 1.148.336 0 .617-.094.863-.258v1.102z"/></svg>' },
spotify: { name: 'Spotify', base: 'https://open.spotify.com/artist/', color: '#1db954', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm4.586 14.424c-.18.295-.563.387-.857.207-2.35-1.434-5.308-1.758-8.793-.963-.335.077-.67-.133-.746-.468-.077-.334.132-.67.467-.745 3.816-.87 7.092-.493 9.72 1.112.294.18.386.563.209.857zm1.213-2.71c-.226.368-.703.483-1.07.258-2.688-1.652-6.785-2.13-9.965-1.164-.413.125-.85-.108-.975-.522-.125-.413.108-.85.522-.975 3.633-1.102 8.157-.568 11.23 1.332.368.226.483.703.258 1.071zm.14-2.827c-3.225-1.915-8.54-2.092-11.614-1.16-.503.153-1.037-.133-1.19-.636-.152-.503.133-1.037.636-1.19 3.532-1.072 9.404-.865 13.115 1.338.45.267.598.85.33 1.3-.267.45-.85.598-1.3.33z"/></svg>' },
soundcloud: { name: 'SoundCloud', base: 'https://soundcloud.com/', color: '#ff5500', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M1.175 12.225c-.062 0-.115.053-.115.116v4.636c0 .062.053.115.115.115h.411c.063 0 .116-.053.116-.115v-4.636c0-.063-.053-.116-.116-.116h-.411zm1.312-1.28c-.062 0-.115.052-.115.115v6.52c0 .063.053.115.115.115h.43c.062 0 .115-.052.115-.115v-6.52c0-.063-.053-.115-.115-.115h-.43zm1.33-1.258c-.063 0-.115.052-.115.115v8.322c0 .063.052.115.115.115h.43c.063 0 .115-.052.115-.115V9.802c0-.063-.052-.115-.115-.115h-.43zm1.33-.86c-.063 0-.115.053-.115.116v9.542c0 .063.052.115.115.115h.43c.063 0 .115-.052.115-.115V8.942c0-.063-.052-.116-.115-.116h-.43zm21.43 3.652c-.413 0-.802.08-1.157.228-.312-2.73-2.613-4.858-5.422-4.858-1.08 0-2.083.315-2.928.858-.2-.702-.592-1.332-1.12-1.826-.78-.727-1.812-1.13-2.882-1.13-.815 0-1.6.236-2.272.684v10.822c0 .063.052.115.115.115h15.666c1.666 0 3.018-1.352 3.018-3.018 0-1.666-1.352-3.018-3.018-3.018z"/></svg>' },
bandcamp: { name: 'Bandcamp', base: 'https://bandcamp.com/', color: '#1da0c3', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M0 18.75l7.437-13.5H24l-7.438 13.5H0z"/></svg>' },
instagram: { name: 'Instagram', base: 'https://instagram.com/', color: '#e1306c', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="2" width="20" height="20" rx="5" ry="5"></rect><path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z"></path><line x1="17.5" y1="6.5" x2="17.51" y2="6.5"></line></svg>' },
tiktok: { name: 'TikTok', base: 'https://www.tiktok.com/@', color: '#000000', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M19.59 6.69a4.83 4.83 0 0 1-3.77-4.25V2h-3.45v13.67a2.89 2.89 0 1 1-5.2-1.74 2.89 2.89 0 0 1 2.31-2.22V8.2a6.34 6.34 0 0 0-5 6.13 6.34 6.34 0 1 0 10.7-4.52A8.25 8.25 0 0 0 20 10.56V6.69z"/></svg>' },
youtube: { name: 'YouTube', base: 'https://youtube.com/@', color: '#ff0000', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22.54 6.42a2.78 2.78 0 0 0-1.94-2C18.88 4 12 4 12 4s-6.88 0-8.6.46a2.78 2.78 0 0 0-1.94 2A29 29 0 0 0 1 11.75a29 29 0 0 0 .46 5.33A2.78 2.78 0 0 0 3.4 19c1.72.46 8.6.46 8.6.46s6.88 0 8.6-.46a2.78 2.78 0 0 0 1.94-2 29 29 0 0 0 .46-5.25 29 29 0 0 0-.46-5.33z"></path><polygon points="9.75 15.02 15.5 11.75 9.75 8.48 9.75 15.02"></polygon></svg>' },
twitter: { name: 'X / Twitter', base: 'https://x.com/', color: '#0f1419', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M23 3a10.9 10.9 0 0 1-3.14 1.53 4.48 4.48 0 0 0-7.86 3v1A10.66 10.66 0 0 1 3 4s-4 9 5 13a11.64 11.64 0 0 1-7 2c9 5 20 0 20-11.5a4.5 4.5 0 0 0-.08-.83A7.72 7.72 0 0 0 23 3z"></path></svg>' },
threads: { name: 'Threads', base: 'https://www.threads.net/@', color: '#000000', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm3.842 12.338c-.288 1.83-1.85 3.09-3.842 3.09-2.298 0-4.043-1.68-4.043-4.135 0-2.454 1.745-4.134 4.043-4.134 1.583 0 2.875.836 3.447 2.083l-1.28.625c-.39-.817-1.182-1.328-2.167-1.328-1.55 0-2.613 1.15-2.613 2.754 0 1.603 1.063 2.753 2.613 2.753 1.258 0 2.19-.77 2.37-1.808h-2.37v-1.38h3.842v1.48z"/></svg>' },
facebook: { name: 'Facebook', base: 'https://facebook.com/', color: '#1877f2', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z"></path></svg>' },
linkedin: { name: 'LinkedIn', base: 'https://linkedin.com/in/', color: '#0a66c2', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z"></path><rect x="2" y="9" width="4" height="12"></rect><circle cx="4" cy="4" r="2"></circle></svg>' },
github: { name: 'GitHub', base: 'https://github.com/', color: '#24292e', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path></svg>' },
pinterest: { name: 'Pinterest', base: 'https://pinterest.com/', color: '#e60023', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0C5.373 0 0 5.372 0 12c0 5.084 3.163 9.426 7.627 11.174-.105-.949-.2-2.405.042-3.441.218-.937 1.407-5.965 1.407-5.965s-.359-.719-.359-1.782c0-1.668.967-2.914 2.171-2.914 1.023 0 1.518.769 1.518 1.69 0 1.029-.655 2.568-.994 3.995-.283 1.194.599 2.169 1.777 2.169 2.133 0 3.772-2.249 3.772-5.495 0-2.873-2.064-4.882-5.012-4.882-3.414 0-5.418 2.561-5.418 5.207 0 1.031.397 2.138.893 2.738.098.119.112.224.083.345l-.333 1.36c-.053.22-.174.267-.402.161-1.499-.698-2.436-2.889-2.436-4.649 0-3.785 2.75-7.262 7.929-7.262 4.163 0 7.398 2.967 7.398 6.931 0 4.136-2.607 7.464-6.227 7.464-1.216 0-2.359-.631-2.75-1.378l-.748 2.853c-.271 1.043-1.002 2.35-1.492 3.146C9.57 23.812 10.763 24 12 24c6.627 0 12-5.373 12-12 0-6.628-5.373-12-12-12z"/></svg>' }
};
// 2. PRESET & MASTER RUJUKAN
var PRESET_LABELS = [
"Nama Lahir", "Tanggal Lahir", "Tempat Lahir", "Pekerjaan",
"Kebangsaan", "Genre", "Instrumen", "Label Rekaman",
"Tahun Aktif", "Situs Web Resmi", "Pasangan", "Orang Tua"
];
var REF_TYPES = {
"Berita": ["Berita Online", "Berita Cetak / Koran", "Wawancara Eksklusif", "Liputan Khusus", "Editorial", "Investigasi Media", "Siaran Radio / TV"],
"Website": ["Situs Resmi", "Portal Informasi", "Database Web", "Portal Komunitas", "Forum Diskusi", "Portal Edukasi", "Direktori Web"],
"Buku": ["Buku Cetak", "E-Book", "Bab / Bagian Buku", "Monograf", "Katalog / Biografi Cetak", "Buku Teks Akademik"],
"Jurnal": ["Jurnal Ilmiah Peer-Reviewed", "Paper Konferensi", "Artikel Review", "Pre-print / Draft Penelitian", "Tesis / Disertasi"],
"Siaran Pers": ["Siaran Pers Resmi", "Pengumuman Instansi / BUMN", "Pernyataan Pers Hukrim", "Keterangan Pers Artis/Manajemen"],
"Dokumen PDF": ["Laporan Tahunan / Annual Report", "Dokumen Kebijakan / Regulasi", "Brosur / Pamflet Resmi", "Naskah Akademik"],
"Studio / Database Musik": ["MusicBrainz", "Discogs", "Genius", "AllMusic", "Spotify For Artists", "Database BPI / RIAA", "Songfacts", "Apple Music Catalogue"],
"Video": ["YouTube Video", "Film Dokumenter", "Wawancara Video", "Arsip Siaran TV", "Vlog Resmi"],
"Podcast": ["Episode Podcast", "Siniar Audio Resmi", "Wawancara Audio"],
"Media Sosial": ["Postingan / Tweet", "Video Singkat (Reels/TikTok/Shorts)", "Live Stream", "Thread Resmi"],
"Ensiklopedia": ["Ensiklopedia Cetak", "Ensiklopedia Online", "Kamus / Glosarium"],
"Arsip": ["Arsip Nasional / Sejarah", "Web Archive (Wayback Machine)", "Dokumen Manuskrip"]
};
var VERIFIED_DOMAINS = [
'kompas.com', 'kompas.id', 'detik.com', 'cnnindonesia.com', 'tribunnews.com', 'kumparan.com', 'tirto.id',
'youtube.com', 'spotify.com', 'apple.com', 'musicbrainz.org', 'billboard.com', 'rollingstone.com',
'bbc.com', 'nytimes.com', 'instagram.com', 'x.com', 'twitter.com', 'facebook.com', 'wikipedia.org', 'imdb.com'
];
// 3. IKON SISTEM
var svgEdit = '<svg class="md-icon" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><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>';
var svgLock = '<svg class="md-icon" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>';
var svgUnlock = '<svg class="md-icon" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 9.9-1"></path></svg>';
var svgRef = '<svg class="md-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>';
var svgCopy = '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>';
var svgSearch = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>';
var svgCode = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 18 22 12 16 6"></polyline><polyline points="8 6 2 12 8 18"></polyline></svg>';
var svgVerified = '<svg width="15" height="15" viewBox="0 0 24 24" fill="#3b82f6" stroke="#ffffff" stroke-width="2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>';
var svgHelp = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"></circle><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>';
// 4. HELPER UTILITY & ADVANCED SECURITY
function sanitizeHTML(str) {
if (typeof str !== 'string') return str;
return str.replace(/[&<>"'`=\/]/g, function(s) {
return {
'&': '&', '<': '<', '>': '>', '"': '"',
"'": ''', '/': '/', '`': '`', '=': '='
}[s];
});
}
function isVerifiedSite(urlStr) {
if(!urlStr) return false;
var domain = extractDomain(urlStr);
return VERIFIED_DOMAINS.some(function(vd) { return domain === vd || domain.endsWith('.' + vd); });
}
function formatIndonesianDate(dateStr) {
if (!dateStr) return '';
var parts = dateStr.split('-');
if (parts.length !== 3) return dateStr;
var months = ['Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni', 'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember'];
var day = parseInt(parts[2], 10);
var monthIdx = parseInt(parts[1], 10) - 1;
var year = parts[0];
if (isNaN(day) || monthIdx < 0 || monthIdx > 11 || isNaN(year)) return sanitizeHTML(dateStr);
return day + ' ' + months[monthIdx] + ' ' + year;
}
function extractDomain(urlStr) {
try {
if (!urlStr.match(/^https?:\/\//i)) urlStr = 'https://' + urlStr;
return new URL(urlStr).hostname.replace(/^www\./, '');
} catch(e) { return urlStr.replace(/^https?:\/\/(www\.)?/, '').split('/')[0]; }
}
function ensureHttps(urlStr) {
if (!urlStr) return '';
urlStr = urlStr.trim();
var protoMatch = urlStr.match(/^([a-z0-9]+):/i);
if (protoMatch) {
var badProtos = ['javascript', 'data', 'vbscript', 'file', 'ws', 'wss', 'ftp'];
if (badProtos.indexOf(protoMatch[1].toLowerCase()) !== -1) return '';
}
return (urlStr && !urlStr.match(/^https?:\/\//i)) ? 'https://' + urlStr : urlStr;
}
function getTodayString() {
var now = new Date();
var y = now.getFullYear();
var m = String(now.getMonth() + 1).padStart(2, '0');
var d = String(now.getDate()).padStart(2, '0');
return y + '-' + m + '-' + d;
}
// 5. STYLESHEET (TERMASUK LAYOUT MEDIA SOSIAL GLOBAL & AUTOCOMPLETE)
var css = `
#mippedia-data-app {
background: #ffffff; border-radius: 16px; box-shadow: 0 4px 24px rgba(0,0,0,0.06);
padding: 20px; margin: 20px 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
border: 1px solid #e2e8f0; max-width: 100%; box-sizing: border-box; overflow: hidden;
}
.md-header { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 16px; padding-bottom: 14px; border-bottom: 1px solid #f1f5f9; }
.md-title-container { display: flex; align-items: center; gap: 8px; flex: 1 1 auto; min-width: 180px; }
.md-title { font-size: 1.25rem; font-weight: 800; color: #0f172a; margin: 0; white-space: nowrap; }
.md-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.md-btn-icon {
background: #f8fafc; border: 1px solid #cbd5e1; cursor: pointer; color: #4f46e5;
padding: 8px 14px; border-radius: 8px; transition: all 0.2s ease;
display: inline-flex; align-items: center; gap: 6px; font-size: 0.85rem; font-weight: 600; white-space: nowrap;
}
.md-btn-icon:hover:not(:disabled) { background: #f1f5f9; border-color: #94a3b8; transform: translateY(-1px); }
.md-btn-lock { color: #dc2626; background: #fef2f2; border-color: #fca5a5; }
.md-search-box { position: relative; margin-bottom: 16px; width: 100%; box-sizing: border-box; }
.md-search-input { width: 100%; padding: 8px 12px 8px 34px; border: 1px solid #cbd5e1; border-radius: 8px; font-size: 0.85rem; box-sizing: border-box; background: #f8fafc; transition: 0.2s; }
.md-search-input:focus { background: #fff; border-color: #4f46e5; outline: none; }
.md-search-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: #94a3b8; }
/* Kotak Global Sosial Media Terpisah di Atas */
.md-global-social-box { background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 12px; padding: 16px; margin-bottom: 16px; box-shadow: 0 2px 8px rgba(0,0,0,0.02); }
.md-social-box-title { font-size: 0.85rem; font-weight: 800; color: #475569; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 12px; border-bottom: 1px dashed #cbd5e1; padding-bottom: 8px; }
.md-social-group { display: flex; flex-wrap: wrap; gap: 8px; }
.md-social-badge { display: inline-flex; align-items: center; gap: 6px; padding: 6px 12px; border-radius: 20px; font-size: 0.8rem; font-weight: 600; color: white !important; text-decoration: none !important; transition: transform 0.2s, opacity 0.2s; box-shadow: 0 2px 4px rgba(0,0,0,0.05); }
.md-social-badge:hover { transform: translateY(-2px); opacity: 0.92; }
.md-row-display { display: flex; justify-content: space-between; align-items: flex-start; padding: 16px 12px; border-bottom: 1px solid #f1f5f9; transition: all 0.2s; border-radius: 12px; gap: 12px; margin-bottom: 8px; background: #fff; }
.md-row-display:hover { background: #faf5ff; box-shadow: inset 4px 0 0 #6366f1; }
.md-data-group { display: flex; flex-direction: column; gap: 6px; flex: 1; min-width: 0; }
.md-data-label { font-size: 0.72rem; color: #64748b; font-weight: 800; text-transform: uppercase; letter-spacing: 0.6px; }
.md-data-value-wrap { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.md-data-value { font-size: 1.02rem; color: #0f172a; line-height: 1.4; font-weight: 600; word-break: break-word; }
.md-btn-copy { background: none; border: none; color: #94a3b8; cursor: pointer; padding: 2px 4px; border-radius: 4px; transition: 0.2s; display: inline-flex; align-items: center; }
.md-btn-copy:hover { color: #4f46e5; background: #e0e7ff; }
.md-ref-container { margin-top: 10px; width: 100%; box-sizing: border-box; }
.md-ref-details { background: #f8fafc; border-left: 3px solid #4f46e5; border-radius: 10px; overflow: hidden; border-top:1px solid #f1f5f9; border-right:1px solid #f1f5f9; border-bottom:1px solid #f1f5f9; transition: 0.3s; }
.md-ref-details summary { display: flex; padding: 10px 14px; font-size: 0.85rem; font-weight: 700; color: #374151; cursor: pointer; user-select: none; list-style: none; flex-wrap: nowrap; align-items: center; gap: 8px; }
.md-ref-details summary::-webkit-details-marker { display: none; }
.md-ref-details[open] summary { border-bottom: 1px dashed #cbd5e1; background: #e0e7ff; color: #3730a3; }
.md-ref-body { padding: 12px 14px; font-size: 0.85rem; display: flex; flex-direction: column; gap: 6px; }
.md-verified-badge { cursor: pointer; display: inline-flex; align-items: center; justify-content: center; margin-left: 4px; vertical-align: middle; transition: transform 0.2s; flex-shrink: 0; }
.md-verified-badge:hover { transform: scale(1.1); }
.md-btn-visit { background: #f1f5f9; color: #0f172a; padding: 6px 12px; border-radius: 6px; text-decoration: none; font-size: 0.8rem; display: inline-block; width: fit-content; margin-top: 6px; font-weight:700; border:1px solid #cbd5e1; }
.md-btn-visit:hover { background: #e2e8f0; text-decoration:none; }
.md-preset-wrapper { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.md-preset-pill { background: #f1f5f9; border: 1px solid #cbd5e1; padding: 4px 10px; border-radius: 12px; font-size: 0.75rem; font-weight: 600; color: #475569; cursor: pointer; transition: 0.2s; }
.md-preset-pill:hover { background: #e0e7ff; color: #3730a3; border-color: #a5b4fc; }
.md-form-box { background: #ffffff; border-radius: 12px; padding: 18px; margin-top: 16px; border: 1px solid #cbd5e1; box-shadow: 0 4px 16px rgba(0,0,0,0.05); }
.md-form-title { font-size: 1.05rem; font-weight: 800; color: #0f172a; margin: 0 0 16px 0; border-bottom: 2px solid #e2e8f0; padding-bottom: 8px; }
.md-form-group { margin-bottom: 14px; }
.md-form-group label { display: flex; align-items:center; font-size: 0.82rem; font-weight: 700; margin-bottom: 6px; color: #334155; }
.md-input, .md-select, .md-textarea { width: 100%; padding: 9px 12px; border: 1px solid #cbd5e1; border-radius: 8px; box-sizing: border-box; font-family: inherit; font-size: 0.9rem; transition: 0.2s; background: #fff; }
.md-input:focus, .md-select:focus, .md-textarea:focus { border-color: #4f46e5; outline: none; box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.15); }
/* Autocomplete Styles */
.md-autocomplete-wrapper { position: relative; width: 100%; }
.md-autocomplete-list { position: absolute; top: 100%; left: 0; right: 0; background: #fff; border: 1px solid #cbd5e1; border-radius: 0 0 8px 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); z-index: 1000; display: none; max-height: 200px; overflow-y: auto; margin-top: -2px; }
.md-autocomplete-item { padding: 10px 12px; font-size: 0.88rem; color: #0f172a; cursor: pointer; border-bottom: 1px solid #f1f5f9; transition: 0.2s; display: flex; align-items: center; gap: 8px; }
.md-autocomplete-item:last-child { border-bottom: none; }
.md-autocomplete-item:hover { background: #f8fafc; color: #4f46e5; padding-left: 16px; }
.md-ref-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; background: #f8fafc; padding: 12px; border-radius: 8px; border: 1px dashed #cbd5e1; }
.md-social-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; background: #f1f5f9; padding: 12px; border-radius: 8px; border: 1px solid #cbd5e1; max-height: 280px; overflow-y: auto; }
.md-grid-full { grid-column: span 2; }
.md-cc0-notice { background: #fef2f2; color: #991b1b; padding: 12px; border-radius: 8px; font-size: 0.82rem; line-height: 1.4; border-left: 4px solid #ef4444; margin-top: 15px; margin-bottom: 15px; }
.md-edit-meta { background: #f8fafc; padding: 12px; border-radius: 8px; margin-bottom: 15px; border: 1px solid #e2e8f0; display:flex; flex-direction:column; gap:10px; }
.md-help-icon { color: #64748b; cursor: pointer; margin-left: 6px; display:inline-flex; align-items:center; }
.md-help-icon:hover { color: #4f46e5; }
.md-tooltip-popup { position: absolute; background: #1e293b; color: #fff; padding: 10px 14px; border-radius: 8px; font-size: 0.8rem; width: 260px; max-width: calc(100vw - 30px); z-index: 1000; box-shadow: 0 4px 15px rgba(0,0,0,0.15); display: none; line-height: 1.4; text-align: left; font-weight:normal; }
.md-tooltip-popup::after { content: ''; position: absolute; border-width: 6px; border-style: solid; border-color: #1e293b transparent transparent transparent; top: 100%; left: 15px; }
.md-modal-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); z-index: 9999; display: flex; align-items: center; justify-content: center; backdrop-filter: blur(2px); }
.md-modal-box { background: #fff; padding: 24px; border-radius: 12px; max-width: 400px; text-align: center; box-shadow: 0 10px 25px rgba(0,0,0,0.2); animation: mdPopIn 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); }
@keyframes mdPopIn { 0% { transform: scale(0.8); opacity: 0; } 100% { transform: scale(1); opacity: 1; } }
.md-form-actions { display: flex; gap: 10px; align-items: center; margin-top: 20px; padding-top: 14px; border-top: 1px solid #e2e8f0; flex-wrap: wrap; }
.md-btn { padding: 10px 18px; border: none; border-radius: 8px; font-weight: 700; cursor: pointer; color: white; font-size: 0.85rem; transition: 0.2s; display: inline-flex; align-items: center; gap: 6px; justify-content: center; }
.md-btn:hover:not(:disabled) { transform: translateY(-1px); }
.md-btn-save { background: linear-gradient(135deg, #4f46e5, #4338ca); flex: 1; min-width: 140px; }
.md-btn-cancel { background: #64748b; }
.md-btn-draft { background: #10b981; }
.md-btn-delete { background: #dc2626; margin-left: auto; }
.md-spinner { display: none; width: 14px; height: 14px; border: 2px solid rgba(255,255,255,0.4); border-radius: 50%; border-top-color: #fff; animation: md-spin 0.8s linear infinite; }
.is-loading .md-spinner { display: inline-block; }
@keyframes md-spin { to { transform: rotate(360deg); } }
/* Mobile Optimized Layout (Seluler) */
@media (max-width: 600px) {
#mippedia-data-app { padding: 14px; margin: 14px 0; border-radius: 12px; }
.md-header { flex-direction: column; align-items: flex-start; gap: 10px; }
.md-actions { width: 100%; justify-content: flex-start; overflow-x:auto; padding-bottom:5px; }
.md-btn-icon { padding: 7px 12px; font-size: 0.8rem; }
.md-ref-grid, .md-social-grid { grid-template-columns: 1fr; }
.md-grid-full { grid-column: span 1; }
.md-form-actions { flex-direction: column; width: 100%; }
.md-btn { width: 100%; margin: 0 !important; }
.md-row-display { padding: 12px 10px; flex-direction:column; align-items:stretch;}
.edit-btn { align-self:flex-end; margin-top:8px; }
.md-ref-details summary { align-items: flex-start; }
}
.md-native-render { display: none; }
`;
$('<style>').text(css).appendTo('head');
// 6. POPUP, MODAL & TOOLTIPS
function showModal(title, text) {
$('.md-modal-overlay').remove();
var $overlay = $('<div class="md-modal-overlay"></div>');
var $box = $('<div class="md-modal-box"><h3 style="margin-top:0; color:#0f172a;">' + sanitizeHTML(title) + '</h3><p style="font-size:0.9rem; color:#475569; margin:15px 0;">' + sanitizeHTML(text) + '</p><button class="md-btn md-btn-save" style="width:100%;">Tutup & Mengerti</button></div>');
$box.find('button').on('click', function() { $overlay.remove(); });
$overlay.append($box);
$('body').append($overlay);
}
function initTooltips() {
$('.md-help-icon').off('click').on('click', function(e) {
$('.md-tooltip-popup').remove();
e.stopPropagation();
var $icon = $(this);
var text = sanitizeHTML($icon.data('help'));
var $tt = $('<div class="md-tooltip-popup">' + text + '</div>');
$('body').append($tt);
var offset = $icon.offset();
var ttWidth = $tt.outerWidth();
var winWidth = $(window).width();
var leftPos = offset.left - 15;
if (leftPos + ttWidth > winWidth - 15) leftPos = winWidth - ttWidth - 15;
if (leftPos < 10) leftPos = 10;
$tt.css({ top: offset.top - $tt.outerHeight() - 10, left: leftPos }).fadeIn(200);
});
$(document).off('click.mdtt').on('click.mdtt', function() { $('.md-tooltip-popup').fadeOut(200, function(){ $(this).remove(); }); });
}
// 7. PARSER WIKITEXT & PENYIMPANAN DATA
function loadData() {
return api.get({
action: 'query', prop: 'revisions', titles: pageName, rvprop: 'content', rvslots: 'main', formatversion: 2
}).then(function(data) {
var content = data.query.pages[0].revisions[0].slots.main.content;
var match = content.match(/<!-- MIPPEDIA_DATA_START \| (.*?) -->/);
if (match) {
try {
var parsed = JSON.parse(match[1]);
dataItems = parsed.items || [];
isLocked = parsed.locked || false;
} catch(e) { console.error("JSON Error", e); }
}
return content;
});
}
function buildWikitextVal(item) {
var val = item.val;
if (item.linkTarget && item.linkTarget.trim() !== '') {
return "[[" + item.linkTarget.trim() + "|" + val + "]]";
}
if (item.linkSplit) {
return val.replace(/([a-zA-Z0-9_À-ÿ\- ]+)/g, function(match) {
var trimmed = match.trim();
if (trimmed.length > 2 && trimmed.toLowerCase() !== 'dan' && trimmed.toLowerCase() !== 'atau') {
return "[[" + trimmed + "]]";
}
return match;
});
}
if (item.link) {
return "[[" + val + "]]";
}
return val;
}
function buildWikitextRef(refObj) {
if (!refObj) return "";
var title = refObj.title || "Rujukan";
var text = refObj.url ? "[" + ensureHttps(refObj.url) + " " + title + "]" : "'''" + title + "'''";
var details = [];
if (refObj.author) details.push("Penulis: " + refObj.author);
if (refObj.site) details.push("Situs: " + refObj.site);
if (refObj.publisher) details.push("Penerbit: " + refObj.publisher);
if (refObj.date) details.push("Tanggal Akses: " + formatIndonesianDate(refObj.date));
if (refObj.type) details.push("Jenis: " + refObj.type + (refObj.subType ? " (" + refObj.subType + ")" : ""));
if (details.length > 0) text += " — <small>" + details.join(" • ") + "</small>";
return text;
}
function saveData(actionType, $btnEl, originalContent, customSummary, isMinorEdit) {
if ($btnEl) $btnEl.addClass('is-loading').prop('disabled', true);
var summaryTxt = customSummary ? ('Mippedia Data: ' + customSummary) : ('Mippedia Data V8.7: ' + actionType);
var jsonStr = JSON.stringify({ locked: isLocked, items: dataItems });
var wt = "<!-- MIPPEDIA_DATA_START | " + jsonStr + " -->\n";
wt += "<div class=\"md-native-render\">\n{| class=\"wikitable\"\n! Data !! Nilai !! Referensi\n";
dataItems.forEach(function(item) {
var valTxt = buildWikitextVal(item);
var refTxt = buildWikitextRef(item.ref);
wt += "|-\n| " + item.prop + " || " + valTxt + " || " + refTxt + "\n";
});
wt += "|}\n</div>\n<!-- MIPPEDIA_DATA_END -->";
var newContent;
var regex = /<!-- MIPPEDIA_DATA_START \| .*? -->[\s\S]*?<!-- MIPPEDIA_DATA_END -->/;
if (regex.test(originalContent)) {
newContent = originalContent.replace(regex, wt);
} else {
newContent = originalContent.trim() + "\n\n" + wt;
}
api.postWithToken('csrf', {
action: 'edit', title: pageName, text: newContent, summary: summaryTxt, minor: isMinorEdit || false
}).done(function() {
localStorage.removeItem(DRAFT_KEY);
var nocacheUrl = location.pathname + "?refresh=" + new Date().getTime();
window.location.href = nocacheUrl;
}).fail(function(err) {
if ($btnEl) $btnEl.removeClass('is-loading').prop('disabled', false);
alert("Gagal menyimpan! Error: " + err);
});
}
// 8. BUILDER UI UTAMA
function buildUI(rawContent) {
var $app = $('<div id="mippedia-data-app"></div>');
var $header = $('<div class="md-header"></div>');
var $titleContainer = $('<div class="md-title-container"><h3 class="md-title">' + svgEdit + ' Data Terstruktur</h3></div>');
var $actions = $('<div class="md-actions"></div>');
var $jsonBtn = $('<button class="md-btn-icon" title="Ekspor/Impor JSON">' + svgCode + ' JSON</button>');
$jsonBtn.on('click', function() { showJsonModal(rawContent); });
$actions.append($jsonBtn);
if (isSysop) {
var $lockBtn = $('<button class="md-btn-icon ' + (isLocked ? 'md-btn-unlock' : 'md-btn-lock') + '">' + (isLocked ? svgUnlock + ' Buka Kunci' : svgLock + ' Kunci') + '</button>');
$lockBtn.on('click', function() {
isLocked = !isLocked;
saveData(isLocked ? 'Mengunci data' : 'Membuka kunci data', $(this), rawContent);
});
$actions.append($lockBtn);
}
if (!isLocked || isSysop) {
var $addBtn = $('<button class="md-btn-icon" title="Tambah Data Baru">' + svgEdit + ' Tambah</button>');
$addBtn.on('click', function() { showForm(-1, rawContent); });
$actions.append($addBtn);
}
$header.append($titleContainer).append($actions);
$app.append($header);
// Tampilkan kotak pencarian jika data banyak
if (dataItems.length > 2) {
var $searchBox = $('<div class="md-search-box"><span class="md-search-icon">' + svgSearch + '</span><input type="text" class="md-search-input" placeholder="Cari data terstruktur..."></div>');
$searchBox.find('input').on('keyup input', function() {
var q = $(this).val().toLowerCase();
$app.find('.md-row-display').each(function() {
var text = $(this).text().toLowerCase();
$(this).toggle(text.indexOf(q) !== -1);
});
});
$app.append($searchBox);
}
// MENGUMPULKAN MEDIA SOSIAL GLOBAL (Memisahkan media sosial ke atas)
var globalSocials = {};
dataItems.forEach(function(item) {
if (item.isPersonOrg && item.socials) {
Object.assign(globalSocials, item.socials);
}
});
if (Object.keys(globalSocials).length > 0) {
var $topSocials = $('<div class="md-global-social-box"></div>');
$topSocials.append('<div class="md-social-box-title">Media Sosial & Platform Resmi</div>');
var $socialGroupGlobal = $('<div class="md-social-group"></div>');
Object.keys(SOCIAL_PLATFORMS).forEach(function(key) {
var uname = globalSocials[key];
if (uname) {
var platform = SOCIAL_PLATFORMS[key];
var fullUrl = platform.base + uname.replace(/^@/, '');
var $badge = $('<a class="md-social-badge" href="' + sanitizeHTML(fullUrl) + '" target="_blank" rel="noopener noreferrer" style="background:' + platform.color + ';">' + platform.icon + ' ' + sanitizeHTML(platform.name) + '</a>');
$socialGroupGlobal.append($badge);
}
});
$topSocials.append($socialGroupGlobal);
$app.append($topSocials);
}
var $list = $('<div class="md-list"></div>');
if (dataItems.length === 0) {
$list.append('<div style="color:#64748b; text-align:center; padding: 25px 10px; background:#f8fafc; border-radius:10px; border:1px dashed #cbd5e1; font-size:0.88rem;">Belum ada data terstruktur. Klik <b>Tambah</b> untuk membuat baru.</div>');
} else {
dataItems.forEach(function(item, index) {
var $row = $('<div class="md-row-display"></div>');
var $info = $('<div class="md-data-group"></div>');
$info.append('<span class="md-data-label">' + sanitizeHTML(item.prop) + '</span>');
var displayValHtml = '';
var rawVal = item.val || '';
if (rawVal.match(/^https?:\/\//i)) {
displayValHtml = '<a href="' + sanitizeHTML(rawVal) + '" target="_blank" rel="noopener noreferrer" style="color:#2563eb; font-weight:700; text-decoration:none;">' + extractDomain(rawVal) + '</a>';
} else if (item.linkTarget && item.linkTarget.trim() !== '') {
displayValHtml = '<a href="' + mw.util.getUrl(item.linkTarget.trim()) + '" style="color:#2563eb; font-weight:700; text-decoration:none;">' + sanitizeHTML(rawVal) + '</a>';
} else if (item.linkSplit) {
var parts = rawVal.split(/([,/\s]+dan\s+|[,/\s]+atau\s+|[,/]+)/gi);
displayValHtml = parts.map(function(p) {
var trimmed = p.trim();
if (trimmed && !p.match(/^[,/\s]+$/) && trimmed.toLowerCase() !== 'dan' && trimmed.toLowerCase() !== 'atau') {
return '<a href="' + mw.util.getUrl(trimmed) + '" style="color:#2563eb; font-weight:700; text-decoration:none;">' + sanitizeHTML(trimmed) + '</a>';
}
return sanitizeHTML(p);
}).join('');
} else if (item.link) {
displayValHtml = '<a href="' + mw.util.getUrl(rawVal) + '" style="color:#2563eb; font-weight:700; text-decoration:none;">' + sanitizeHTML(rawVal) + '</a>';
} else {
displayValHtml = sanitizeHTML(rawVal);
}
var $valWrap = $('<div class="md-data-value-wrap"></div>');
$valWrap.append('<span class="md-data-value">' + displayValHtml + '</span>');
var $copyBtn = $('<button class="md-btn-copy" title="Salin teks">' + svgCopy + '</button>');
$copyBtn.on('click', function() {
navigator.clipboard.writeText(rawVal);
$(this).css('color', '#16a34a');
setTimeout(function(){ $copyBtn.css('color', '#94a3b8'); }, 1200);
});
$valWrap.append($copyBtn);
$info.append($valWrap);
if (item.ref && (item.ref.url || item.ref.title)) {
var ref = item.ref;
var isVer = isVerifiedSite(ref.url);
var verBadgeHtml = isVer ? '<span class="md-verified-badge" title="Situs Populer Terverifikasi">' + svgVerified + '</span>' : '';
var safeUrl = ref.url ? ensureHttps(ref.url) : '';
var refTitle = ref.title || "Sumber Rujukan";
var $refContainer = $('<div class="md-ref-container"></div>');
var $refDetails = $(`
<details class="md-ref-details">
<summary>
<div style="color:#4f46e5; display:inline-flex; align-items:center; gap:5px; white-space:nowrap; flex-shrink:0;">${svgRef} REFERENSI:</div>
<div style="color:#374151; display:flex; align-items:center; flex-wrap:wrap; flex:1;">
<span style="word-break:break-word; line-height:1.3;">${sanitizeHTML(refTitle)}</span>${verBadgeHtml}
</div>
</summary>
<div class="md-ref-body">
${ref.author ? '<div><span style="color:#64748b; font-weight:700;">Penulis:</span> ' + sanitizeHTML(ref.author) + '</div>' : ''}
${ref.site ? '<div><span style="color:#64748b; font-weight:700;">Situs:</span> ' + sanitizeHTML(ref.site) + '</div>' : ''}
${ref.publisher ? '<div><span style="color:#64748b; font-weight:700;">Penerbit:</span> ' + sanitizeHTML(ref.publisher) + '</div>' : ''}
${ref.date ? '<div><span style="color:#64748b; font-weight:700;">Tanggal Akses:</span> ' + formatIndonesianDate(ref.date) + '</div>' : ''}
${ref.type ? '<div style="margin-top:4px;"><span style="background:#e2e8f0; padding:2px 8px; border-radius:6px; font-weight:700; font-size:0.75rem;">' + sanitizeHTML(ref.type) + (ref.subType ? ' • ' + sanitizeHTML(ref.subType) : '') + '</span></div>' : ''}
${safeUrl ? '<a href="' + sanitizeHTML(safeUrl) + '" target="_blank" rel="noopener noreferrer" class="md-btn-visit">Kunjungi Situs</a>' : ''}
</div>
</details>
`);
$refDetails.find('.md-verified-badge').on('click', function(e) {
e.preventDefault(); e.stopPropagation();
showModal('Situs Terverifikasi', 'Centang biru menandakan bahwa URL referensi ini valid dan berasal dari entitas, media publikasi besar, portal populer, atau pangkalan data resmi (seperti Kompas, YouTube, Apple Music, Spotify, dll) yang bereputasi kredibel.');
});
$refContainer.append($refDetails);
$info.append($refContainer);
}
$row.append($info);
if (isLocked && !isSysop) {
var $lockNotifyBtn = $('<button class="md-btn-icon edit-btn" style="color:#dc2626; border-color:#fca5a5; background:#fef2f2;">' + svgLock + ' Terkunci</button>');
$lockNotifyBtn.on('click', function() {
showModal('Halaman Dikunci', 'Halaman Data Terstruktur ini sedang dikunci sementara oleh pengurus untuk melindungi dari vandalisme atau perubahan yang tidak sah. Pengguna biasa atau anonim tidak dapat mengedit saat ini.');
});
$row.append($lockNotifyBtn);
} else {
var $editIcon = $('<button class="md-btn-icon edit-btn">' + svgEdit + ' Edit</button>');
$editIcon.on('click', function() { showForm(index, rawContent); });
$row.append($editIcon);
}
$list.append($row);
});
}
$app.append($list);
$app.append('<div id="md-form-container"></div>');
var $infobox = $('.infobox').first();
$('.md-native-render').hide();
if ($infobox.length) $app.insertAfter($infobox);
else $('#mw-content-text .mw-parser-output').append($app);
}
// 9. FORM EDIT & DRAFT LOGIC
function showForm(index, rawContent) {
var $container = $('#md-form-container').empty();
var isNew = index === -1;
var draftData = localStorage.getItem(DRAFT_KEY);
var currentData = isNew ? { prop: '', val: '', link: false, linkTarget: '', linkSplit: false, ref: {}, isPersonOrg: false, socials: {} } : dataItems[index];
if (isNew && draftData) {
if(confirm("Anda memiliki draft yang belum tersimpan sebelumnya. Ingin melanjutkan draft tersebut?")) {
try { currentData = JSON.parse(draftData); } catch(e) {}
}
}
var currentRef = currentData.ref || {};
var currentSocials = currentData.socials || {};
var $form = $('<div class="md-form-box"></div>');
$form.append('<h4 class="md-form-title">' + (isNew ? 'Tambah Data Baru' : 'Edit Data Terstruktur') + '</h4>');
var $propGroup = $('<div class="md-form-group"></div>');
$propGroup.append('<label>Label Data <span class="md-help-icon" data-help="Nama bidang data (contoh: Pekerjaan, Sutradara, Tempat Lahir). Klik tombol preset di bawah untuk isi otomatis.">' + svgHelp + '</span></label>');
$propGroup.append('<input type="text" id="md-inp-prop" class="md-input" value="' + sanitizeHTML(currentData.prop || '') + '">');
var $presetWrap = $('<div class="md-preset-wrapper"></div>');
PRESET_LABELS.forEach(function(lbl) {
var $pill = $('<span class="md-preset-pill">' + lbl + '</span>');
$pill.on('click', function() { $('#md-inp-prop').val(lbl); });
$presetWrap.append($pill);
});
$propGroup.append($presetWrap);
$form.append($propGroup);
var $valBox = $('<div class="md-form-group"></div>');
$valBox.append('<label>Nilai Data <span class="md-help-icon" data-help="Ketik nilai. Jika halaman sudah ada di Wiki, saran akan muncul (pratinjau) dan otomatis mencentang opsi Wikilink.">' + svgHelp + '</span></label>');
// FITUR AUTOCOMPLETE PRATINJAU DIMULAI DI SINI
var $autoWrap = $('<div class="md-autocomplete-wrapper"></div>');
var $valInput = $('<input type="text" id="md-inp-val" class="md-input" autocomplete="off" value="' + sanitizeHTML(currentData.val || '') + '">');
var $autoList = $('<div class="md-autocomplete-list"></div>');
$autoWrap.append($valInput).append($autoList);
$valBox.append($autoWrap);
var $wikilinkOpts = $('<div style="margin-top:8px; display:flex; flex-direction:column; gap:8px; background:#f8fafc; padding:10px; border-radius:8px; border:1px solid #e2e8f0;"></div>');
var chkLink = currentData.link ? 'checked' : '';
var chkSplit = currentData.linkSplit ? 'checked' : '';
var $linkCheck = $('<input type="checkbox" id="md-inp-link" ' + chkLink + '>');
var $lblLinkCheck = $('<label style="display:flex; align-items:center; gap:8px; font-size:0.82rem; font-weight:700; cursor:pointer;"></label>');
$lblLinkCheck.append($linkCheck).append(' Jadikan Wikilink Standar');
// Logika pratinjau ketika diketik
var isAutoChecked = false;
$valInput.on('input', function() {
var q = $(this).val();
// Jika input dikosongkan/berubah, kembalikan centang link ke false jika tadinya otomatis centang
if (isAutoChecked) {
$linkCheck.prop('checked', false);
isAutoChecked = false;
}
if(q.length < 1) {
$autoList.empty().hide();
return;
}
// Panggil API MediaWiki Prefix Search
api.get({
action: 'query',
list: 'prefixsearch',
pssearch: q,
pslimit: 6,
format: 'json'
}).done(function(data) {
$autoList.empty();
if(data.query && data.query.prefixsearch && data.query.prefixsearch.length > 0) {
data.query.prefixsearch.forEach(function(res) {
var $item = $('<div class="md-autocomplete-item">' + svgSearch + ' ' + sanitizeHTML(res.title) + '</div>');
$item.on('click', function() {
$valInput.val(res.title);
$autoList.empty().hide();
$linkCheck.prop('checked', true); // OTOMATIS TERCENTANG
isAutoChecked = true;
});
$autoList.append($item);
});
$autoList.show();
} else {
$autoList.hide();
}
});
});
// Sembunyikan pratinjau saat klik di luar
$(document).on('click.mdauto', function(e) {
if(!$(e.target).closest('.md-autocomplete-wrapper').length) {
$autoList.hide();
}
});
$wikilinkOpts.append($lblLinkCheck);
$wikilinkOpts.append('<label style="display:flex; align-items:center; gap:8px; font-size:0.82rem; font-weight:700; cursor:pointer;"><input type="checkbox" id="md-inp-split" ' + chkSplit + '> Smart Split Wikilink</label>');
var $pipedGroup = $('<div style="margin-top:4px;"><label style="font-size:0.78rem; color:#475569; margin-bottom:4px;">Target Halaman Khusus (Opsional):</label><input type="text" id="md-inp-target" class="md-input" style="padding:6px 10px; font-size:0.85rem;" placeholder="Contoh: Diskografi Rumi Haitami" value="' + sanitizeHTML(currentData.linkTarget || '') + '"></div>');
$wikilinkOpts.append($pipedGroup);
$valBox.append($wikilinkOpts);
$form.append($valBox);
var $socialSec = $('<div class="md-form-group" style="margin-top:16px;"></div>');
var chkPerson = currentData.isPersonOrg ? 'checked' : '';
$socialSec.append('<label style="display:flex; align-items:center; gap:8px; cursor:pointer; background:#e0e7ff; padding:10px; border-radius:8px; font-weight:700; color:#3730a3; font-size:0.85rem;"><input type="checkbox" id="md-chk-person" ' + chkPerson + '> Simpan Profil Media Sosial (Akan muncul di paling atas) <span class="md-help-icon" data-help="Centang ini untuk mengisi data sosial media. Datanya akan dihimpun dan dimunculkan di kotak teratas data struktur.">' + svgHelp + '</span></label>');
var $socialGrid = $('<div class="md-social-grid" id="md-social-box" style="display:' + (currentData.isPersonOrg ? 'grid' : 'none') + '; margin-top:8px;"></div>');
Object.keys(SOCIAL_PLATFORMS).forEach(function(key) {
var p = SOCIAL_PLATFORMS[key];
var valUname = currentSocials[key] || '';
$socialGrid.append('<div><label style="font-size:0.78rem; color:#334155; font-weight:700; margin-bottom:4px;">' + p.name + '</label><input type="text" id="md-soc-' + key + '" class="md-input" placeholder="ID / Username" value="' + sanitizeHTML(valUname) + '"></div>');
});
$socialSec.append($socialGrid);
$form.append($socialSec);
var $refArea = $('<div class="md-form-group" style="margin-top:20px;"></div>');
$refArea.append('<label style="font-size:0.95rem; font-weight:800; color:#0f172a; margin-bottom:10px;">Sumber Referensi <span class="md-help-icon" data-help="Mohon berikan sumber referensi yang valid, kredibel, dan dapat dipertanggungjawabkan (seperti portal berita resmi, jurnal akademik ilmiah, buku cetak ber-ISBN, atau pangkalan data terverifikasi). Referensi yang kuat sangat membantu memastikan keakuratan informasi. Jika URL rujukan Anda mengarah ke situs web besar yang terverifikasi, sistem keamanan akan secara otomatis menampilkan lencana centang biru premium sebagai tanda sumber tepercaya.">' + svgHelp + '</span></label>');
var $refGrid = $('<div class="md-ref-grid"></div>');
var $typeSel = $('<select id="md-ref-type" class="md-select"><option value="">-- Pilih Tipe Utama --</option></select>');
Object.keys(REF_TYPES).forEach(function(t) {
var sel = currentRef.type === t ? 'selected' : '';
$typeSel.append('<option value="' + t + '" ' + sel + '>' + t + '</option>');
});
var $subTypeSel = $('<select id="md-ref-subtype" class="md-select"><option value="">-- Pilih Sub-Tipe --</option></select>');
function updateSubTypes(mainType, selectedSub) {
$subTypeSel.empty().append('<option value="">-- Pilih Sub-Tipe --</option>');
if (mainType && REF_TYPES[mainType]) {
REF_TYPES[mainType].forEach(function(st) {
var sel = selectedSub === st ? 'selected' : '';
$subTypeSel.append('<option value="' + st + '" ' + sel + '>' + st + '</option>');
});
}
}
$typeSel.on('change', function() { updateSubTypes($(this).val(), ''); });
updateSubTypes(currentRef.type, currentRef.subType);
$refGrid.append('<div><label style="font-size:0.78rem; margin-bottom:4px;">Tipe Utama</label></div>').find('div').last().append($typeSel);
$refGrid.append('<div><label style="font-size:0.78rem; margin-bottom:4px;">Sub-Tipe</label></div>').find('div').last().append($subTypeSel);
var $urlInput = $('<input type="text" id="md-ref-url" class="md-input" placeholder="https://...." value="' + sanitizeHTML(currentRef.url || '') + '">');
$urlInput.on('blur', function() {
var u = ensureHttps($(this).val());
$(this).val(u);
var $siteInp = $('#md-ref-site');
if (u && !$siteInp.val().trim()) $siteInp.val(extractDomain(u));
});
$refGrid.append('<div class="md-grid-full"><label style="font-size:0.78rem; margin-bottom:4px;">URL Rujukan</label></div>').find('div').last().append($urlInput);
$refGrid.append('<div><label style="font-size:0.78rem; margin-bottom:4px;">Judul Halaman</label><input type="text" id="md-ref-title" class="md-input" placeholder="Judul Rujukan" value="' + sanitizeHTML(currentRef.title || '') + '"></div>');
$refGrid.append('<div><label style="font-size:0.78rem; margin-bottom:4px;">Nama Situs</label><input type="text" id="md-ref-site" class="md-input" placeholder="Situs" value="' + sanitizeHTML(currentRef.site || '') + '"></div>');
$refGrid.append('<div><label style="font-size:0.78rem; margin-bottom:4px;">Penulis</label><input type="text" id="md-ref-author" class="md-input" placeholder="Penulis" value="' + sanitizeHTML(currentRef.author || '') + '"></div>');
$refGrid.append('<div><label style="font-size:0.78rem; margin-bottom:4px;">Penerbit</label><input type="text" id="md-ref-publisher" class="md-input" placeholder="Penerbit" value="' + sanitizeHTML(currentRef.publisher || '') + '"></div>');
var $dateBox = $('<div><label style="font-size:0.78rem; margin-bottom:4px;">Tanggal Akses</label></div>');
var $dateInpWrap = $('<div style="display:flex; gap:6px;"></div>');
var $dateInp = $('<input type="date" id="md-ref-date" class="md-input" value="' + sanitizeHTML(currentRef.date || '') + '">');
var $todayBtn = $('<button type="button" class="md-btn-icon" style="padding:4px 8px; font-size:0.75rem;" title="Set Hari Ini">Hari Ini</button>');
$todayBtn.on('click', function() { $dateInp.val(getTodayString()); });
$dateInpWrap.append($dateInp).append($todayBtn);
$dateBox.append($dateInpWrap);
$refGrid.append($dateBox);
$refArea.append($refGrid);
$form.append($refArea);
$form.append(`
<div class="md-cc0-notice">
<b>Perhatian Lisensi:</b> Setiap data yang diterbitkan di sini akan terlihat publik dan dirilis di bawah lisensi <b>Creative Commons Zero (CC0)</b>. Jika Anda tidak ingin suntingan Anda digunakan secara bebas dan publik tanpa batas, jangan klik Terbitkan. <span class="md-help-icon" data-help="Dengan menerbitkan, Anda melepaskan seluruh hak cipta atas input data ini ke domain publik murni sesuai prinsip Wikidata.">${svgHelp}</span>
</div>
<div class="md-edit-meta">
<label style="font-size:0.85rem; font-weight:700;">Ringkasan Suntingan <span class="md-help-icon" data-help="Jelaskan secara singkat apa yang Anda ubah atau tambahkan. Contoh: Menambah data tanggal lahir rumi.">${svgHelp}</span></label>
<input type="text" id="md-edit-summary" class="md-input" placeholder="Jelaskan perubahan Anda secara singkat...">
<label style="font-size:0.85rem; cursor:pointer; font-weight:700; margin-top:5px; display:inline-flex; align-items:center; gap:6px;"><input type="checkbox" id="md-minor-edit"> Ini adalah suntingan kecil (minor)</label>
</div>
`);
var $actions = $('<div class="md-form-actions"></div>');
var $btnSave = $('<button class="md-btn md-btn-save">Terbitkan <span class="md-spinner"></span></button>');
var $btnDraft = $('<button class="md-btn md-btn-draft">Simpan Draft</button>');
var $btnCancel = $('<button class="md-btn md-btn-cancel">Batal</button>');
$actions.append($btnSave).append($btnDraft).append($btnCancel);
if (draftData && isNew) {
var $btnDelDraft = $('<button class="md-btn" style="background:#eab308; color:#fff;">Hapus Draft</button>');
$btnDelDraft.on('click', function() {
localStorage.removeItem(DRAFT_KEY);
alert("Draft berhasil dihapus!");
$(this).hide();
});
$actions.append($btnDelDraft);
}
if (!isNew) {
var $btnDelete = $('<button class="md-btn md-btn-delete">Hapus Data <span class="md-spinner"></span></button>');
$btnDelete.on('click', function() {
if(confirm('Yakin ingin menghapus item data ini?')) {
dataItems.splice(index, 1);
saveData('Menghapus data', $(this), rawContent, $('#md-edit-summary').val(), $('#md-minor-edit').is(':checked'));
}
});
$actions.append($btnDelete);
}
$form.append($actions);
$container.append($form);
initTooltips();
$('#md-chk-person').on('change', function() {
if($(this).is(':checked')) $('#md-social-box').css('display', 'grid');
else $('#md-social-box').hide();
});
$('html, body').animate({ scrollTop: $form.offset().top - 60 }, 250);
$btnCancel.on('click', function() { $container.empty(); });
$btnDraft.on('click', function() {
var socObj = {};
if ($('#md-chk-person').is(':checked')) {
Object.keys(SOCIAL_PLATFORMS).forEach(function(k) {
var valU = $('#md-soc-' + k).val().trim();
if (valU) socObj[k] = valU;
});
}
var draftObj = {
prop: $('#md-inp-prop').val().trim(),
val: $('#md-inp-val').val().trim(),
link: $('#md-inp-link').is(':checked'),
linkSplit: $('#md-inp-split').is(':checked'),
linkTarget: $('#md-inp-target').val().trim(),
isPersonOrg: $('#md-chk-person').is(':checked'),
socials: socObj,
ref: {
type: $('#md-ref-type').val(), subType: $('#md-ref-subtype').val(),
url: ensureHttps($('#md-ref-url').val()), title: $('#md-ref-title').val().trim(),
site: $('#md-ref-site').val().trim(), author: $('#md-ref-author').val().trim(),
publisher: $('#md-ref-publisher').val().trim(), date: $('#md-ref-date').val()
}
};
localStorage.setItem(DRAFT_KEY, JSON.stringify(draftObj));
alert("Draft berhasil disimpan di memori peramban Anda!");
});
$btnSave.on('click', function() {
var newProp = $('#md-inp-prop').val().trim();
var newVal = $('#md-inp-val').val().trim();
if (!newProp || !newVal) {
alert("Label Data dan Nilai wajib diisi!"); return;
}
var isPerson = $('#md-chk-person').is(':checked');
var socObj = {};
if (isPerson) {
Object.keys(SOCIAL_PLATFORMS).forEach(function(k) {
var valU = $('#md-soc-' + k).val().trim();
if (valU) socObj[k] = valU;
});
}
var refObj = {
type: $('#md-ref-type').val(),
subType: $('#md-ref-subtype').val(),
url: ensureHttps($('#md-ref-url').val()),
title: $('#md-ref-title').val().trim(),
site: $('#md-ref-site').val().trim(),
author: $('#md-ref-author').val().trim(),
publisher: $('#md-ref-publisher').val().trim(),
date: $('#md-ref-date').val()
};
Object.keys(refObj).forEach(function(key) { if (!refObj[key]) delete refObj[key]; });
if (Object.keys(refObj).length === 0) refObj = null;
var newItem = {
prop: newProp, val: newVal,
link: $('#md-inp-link').is(':checked'),
linkSplit: $('#md-inp-split').is(':checked'),
linkTarget: $('#md-inp-target').val().trim(),
isPersonOrg: isPerson,
socials: socObj,
ref: refObj
};
if (isNew) dataItems.push(newItem);
else dataItems[index] = newItem;
saveData(isNew ? 'Tambah data' : 'Edit data', $(this), rawContent, $('#md-edit-summary').val().trim(), $('#md-minor-edit').is(':checked'));
});
}
// 10. JSON EXPORT & IMPORT MODAL (ASLI UTUH)
function showJsonModal(rawContent) {
var $container = $('#md-form-container').empty();
var jsonStr = JSON.stringify(dataItems, null, 2);
var $box = $('<div class="md-form-box"></div>');
$box.append('<h4 class="md-form-title">Ekspor / Impor JSON Data</h4>');
$box.append('<p style="font-size:0.82rem; color:#64748b; margin-bottom:10px;">Anda dapat menyalin struktur JSON di bawah untuk cadangan atau menempelkan JSON dari artikel lain.</p>');
var $txt = $('<textarea class="md-textarea" rows="10" style="font-family:monospace; font-size:0.8rem;"></textarea>').val(jsonStr);
$box.append($txt);
var $actions = $('<div class="md-form-actions"></div>');
var $btnImport = $('<button class="md-btn md-btn-save">Impor JSON & Simpan <span class="md-spinner"></span></button>');
var $btnCancel = $('<button class="md-btn md-btn-cancel">Tutup</button>');
$actions.append($btnImport).append($btnCancel);
$box.append($actions);
$container.append($box);
$btnCancel.on('click', function() { $container.empty(); });
$btnImport.on('click', function() {
try {
var parsed = JSON.parse($txt.val());
if (Array.isArray(parsed)) {
dataItems = parsed;
saveData('Impor data dari JSON', $(this), rawContent, 'Impor struktur data JSON', false);
} else {
alert("Format JSON harus berupa Array items!");
}
} catch(e) {
alert("Format JSON tidak valid: " + e.message);
}
});
}
// 11. EXECUTE
loadData().then(function(rawContent) { buildUI(rawContent); });
});
})(jQuery, mediaWiki);
/* ==========================================================
⚡ MIPPEDIA SPEED OPTIMIZER - PURE SPEED & STABLE (V1.1)
Features: Instant Link Pre-fetching Engine (No Animation/Pure Performance).
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 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() {
$(document).ready(function() {
var $descSection = $('#mip-desc-section'), $descBox = $('#mip-auto-description'),
$sourceInfo = $('#mip-source-info'), $portalLinks = $('#mip-portal-links'),
$projectPortal = $('#mip-project-portal'), $mainContent = $('#mw-content-text');
if (!$descBox.length) return;
var pageTitle = mw.config.get('wgPageName').replace(/_/g, ' '),
cacheKey = 'mip_smart_context_thumb_' + pageTitle, now = new Date().getTime(),
currentYear = 2026;
// --- KONFIGURASI PROYEK ---
var projects = [
{ 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: 'Mippedia Inggris' },
{ 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 cleanExtract(text) { return text.replace(/\[\d+\]/g, '').replace(/\{\{[^}]+\}\}/g, '').replace(/\(\s*\)/g, '').replace(/\s\s+/g, ' ').trim(); }
function applyDynamicContext(text) {
var date = new Date();
var hari = ["Minggu", "Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu"];
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();
return text.replace(/(hari ini|saat ini|sekarang)/gi, '$1 (' + tglSkrg + ')')
.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>";
})
.replace(/(sejak|tahun)\s(\d{4})/gi, function(m, k, y) {
var gap = currentYear - parseInt(y);
if (gap > 0 && gap < 100) return k + " " + y + " <small style='color: #888;'>(" + gap + " thn lalu)</small>";
return m;
});
}
function applySummary(text) {
var limit = 250;
if (text.length <= limit) return '<span>' + text + '</span>';
return '<span>' + text.substring(0, limit) + '</span><span class="mip-dots">... </span><span class="mip-more" style="display:none;">' + text.substring(limit) + '</span><span class="mip-read-btn" style="color: #6a5acd; cursor: pointer; font-weight: bold; margin-left: 5px;">Baca selengkapnya</span>';
}
// --- SMART LINKS PORTAL ---
function buildSmartLinks(currentId, validatedEnTitle) {
$portalLinks.empty();
var validLinks = [];
var checkRequests = projects.filter(p => p.id !== currentId).map(function(p) {
var targetTitle = (p.id === 'en' && validatedEnTitle) ? validatedEnTitle : pageTitle;
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() {
if (validLinks.length > 0) { $portalLinks.html(validLinks.join(' <span style="color:#ccc; margin: 0 5px;">•</span> ')); $projectPortal.fadeIn(); }
});
}
// --- 🧠 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 globalEnTitle = cached.enTitle || "";
function init() {
var pPrimary = projects[0];
$.ajax({
url: pPrimary.url,
data: { action: 'query', prop: 'revisions|pageimages|langlinks', lllang: 'en', rvprop: 'timestamp', piprop: 'thumbnail', pithumbsize: 150, titles: pageTitle, format: 'json', origin: '*' },
dataType: 'json',
success: function(res) {
var pg = res.query.pages, id = Object.keys(pg)[0];
var latestTS = (id != "-1") ? pg[id].revisions[0].timestamp : "0";
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 && latestTS !== "0" && !cached.isEmpty) {
toggleMippediaLayout(false);
renderAll(cached.p, cached.content, cached.isTrans, cached.orig, cached.img, cached.customUrl, cached.enTitle);
} else {
fetchPriorityData(0, latestTS, thumbUrl);
}
},
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 fetchPriorityData(index, newTS, img) {
if (index >= projects.length) {
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({
url: p.url,
data: { action: 'query', prop: 'extracts', exintro: 1, explaintext: 1, titles: pageTitle, 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(pageTitle), globalEnTitle);
} else {
fetchPriorityData(index + 1, newTS, img);
}
},
error: function() { fetchPriorityData(index + 1, newTS, img); }
});
}
}
function finalize(p, c, t, o, ts, img, customUrl, enTitle) {
localStorage.setItem(cacheKey, JSON.stringify({isEmpty: false, p:p, content:c, isTrans:t, orig:o, ts:ts, img:img, customUrl: customUrl, enTitle: enTitle}));
toggleMippediaLayout(false);
renderAll(p, c, t, o, img, customUrl, enTitle);
}
function renderAll(p, currentText, isTranslated, originalText, img, customUrl, enTitle) {
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>' : '';
if (!$mainContent.find('#mip-desc-section').length) {
$mainContent.prepend($descSection);
}
$descSection.show();
$descBox.show().html(thumbHtml + applySummary(processedText) + '<div style="clear:both;"></div>');
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>'; }
$sourceInfo.show().html(footer);
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-toggle-orig').on('click', '#mip-toggle-orig', function() {
var isOrig = ($(this).text() === 'Tampilkan versi asli');
var textToDisplay = applyAutoBold(applyDynamicContext(isOrig ? originalText : currentText));
$descBox.html(thumbHtml + applySummary(textToDisplay) + '<div style="clear:both;"></div>');
$(this).text(isOrig ? 'Tampilkan terjemahan' : 'Tampilkan versi asli');
});
}
init();
});
})();
/* ==========================================================
🚀 MIPPEDIA DATA - CORE SYSTEM (ULTIMATE BUNDLE)
Status: FINAL & REVISED (Visual Fix + Permission Logic)
Fix: Mobile Responsive Notification Position
========================================================== */
(function() {
$(document).ready(function() {
// --- 1. FILTER GLOBAL (Namespace 0 & Bukan Halaman Utama) ---
if (mw.config.get('wgNamespaceNumber') !== 0 ||
mw.config.get('wgIsMainPage') ||
mw.config.get('wgAction') !== 'view') return;
var pageTitle = mw.config.get('wgPageName');
var dataPage = 'MediaWiki:Sitelinks-Data.json';
var userGroups = mw.config.get('wgUserGroups');
var isAdmin = userGroups.includes('sysop') || userGroups.includes('interface-admin');
var projects = {
'id': { name: 'ID', url: 'https://id.mippedia.org/api.php', base: 'https://id.mippedia.org/wiki/', color: '#6a5acd' },
'en': { name: 'EN', url: 'https://en.mippedia.org/api.php', base: 'https://en.mippedia.org/wiki/', color: '#4169e1' },
'concise': { name: 'Concise', url: 'https://concise.mippedia.org/api.php', base: 'https://concise.mippedia.org/wiki/', color: '#20b2aa' }
};
// --- 2. KOTAK NOTIFIKASI INFORMASI MODERN (FIXED MOBILE POSITION) ---
function showBoxNotif(title, msg, type) {
$('.mip-box-notif').remove();
var theme = type === 'success' ? {bg:'#e6fffa', border:'#38b2ac', text:'#234e52', icon:''} :
{bg:'#fff5f5', border:'#f56565', text:'#742a2a', icon:''};
// Logika CSS Responsif untuk posisi
var isMobile = window.innerWidth <= 768;
var desktopStyle = "top:25px; right:25px; width:350px;";
var mobileStyle = "top:15px; left:50%; transform:translateX(-50%); width:90%; max-width:400px;";
var finalPos = isMobile ? mobileStyle : desktopStyle;
var $notif = $('<div class="mip-box-notif" style="position:fixed; ' + finalPos + ' background:'+theme.bg+'; border-left:6px solid '+theme.border+'; padding:18px; z-index:10001; border-radius:12px; box-shadow:0 15px 35px rgba(0,0,0,0.2); font-family:sans-serif; animation:mipSlideIn 0.4s ease-out;">' +
'<div style="display:flex; align-items:center; gap:12px; margin-bottom:8px;">' +
'<span style="font-size:20px;">'+theme.icon+'</span>' +
'<strong style="color:'+theme.text+'; font-size:15px; letter-spacing:-0.3px;">'+title+'</strong>' +
'</div>' +
'<p style="margin:0; font-size:13px; color:'+theme.text+'; line-height:1.6; opacity:0.9;">'+msg+'</p>' +
'</div>').appendTo('body');
setTimeout(function() { $notif.fadeOut(function(){ $(this).remove(); }); }, 5500);
}
// --- 3. UI GENERATOR (DIPERTEBAL) ---
// A. Header Pro
var headerHtml = '<div id="mip-header" style="background:#f8f9fa; border:1px solid #ddd; border-left:6px solid #6a5acd; padding:18px; margin-bottom:25px; border-radius:6px; box-shadow:inset 0 0 10px rgba(0,0,0,0.02);">' +
'<div style="display:flex; align-items:center; gap:10px; margin-bottom:8px;">' +
'<span style="background:#6a5acd; color:white; padding:3px 10px; border-radius:4px; font-size:11px; font-weight:bold; text-transform:uppercase; letter-spacing:0.5px;">Basis Data Terstruktur</span>' +
'<strong style="color:#111; font-size:16px;"></strong></div>' +
'<p style="margin:0; font-size:13px; color:#444; line-height:1.6;">' +
'Halaman ini adalah metadata entitas repositori pusat yang disinkronisasi secara otomatis lintas ekosistem. ' +
'Informasi di bawah ini bersifat teknis dan bebas digunakan sebagai referensi data terbuka bagi semua orang di seluruh dunia berdasarkan lisensi atribusi yang sesuai.' +
'</p></div>';
// B. Connector Box
var editBtn = '<span id="mip-open-editor" style="cursor:pointer; font-size:20px; color:#6a5acd; opacity:0.8; transition:0.3s;" onmouseover="this.style.opacity=1" onmouseout="this.style.opacity=0.8">✎</span>';
var connectorHtml = '<div id="mip-connector" style="background:#fff; border:1px solid #e2e8f0; border-radius:16px; padding:22px; margin-top:30px; box-shadow:0 10px 25px rgba(0,0,0,0.04);">' +
'<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:20px;">' +
'<strong style="font-size:15px; color:#1a202c; display:flex; align-items:center; gap:8px;">ID situs</strong>'+editBtn+'</div>' +
'<div id="mip-display" style="display:flex; gap:12px; flex-wrap:wrap;"><small style="color:#a0aec0;">Mensinkronisasi basis data...</small></div>' +
'<div id="mip-editor" style="display:none; margin-top:20px; border-top:2px solid #f7fafc; padding-top:20px;">' +
Object.keys(projects).map(p => '<div style="margin-bottom:12px; display:flex; gap:10px;"><span style="width:75px; font-size:11px; font-weight:bold; background:#edf2f7; display:flex; align-items:center; justify-content:center; border-radius:8px; color:#4a5568;">'+projects[p].name+'</span><input type="text" id="in-'+p+'" placeholder="Judul artikel di '+projects[p].name+'..." style="flex:1; padding:10px; font-size:13px; border:1.5px solid #e2e8f0; border-radius:10px; outline:none; transition:0.2s;" onfocus="this.style.borderColor=\'#6a5acd\'"></div>').join('') +
'<button id="mip-save" style="width:100%; padding:14px; background:#6a5acd; color:white; border:none; border-radius:12px; font-size:14px; font-weight:bold; cursor:pointer; box-shadow:0 4px 12px rgba(106,90,205,0.3); transition:0.3s;">Terbitkan</button>' +
'</div></div>';
// C. Footer Lisensi Lengkap
var footerHtml = '<div id="mip-footer" style="background:#fafafa; border:1px solid #eee; border-radius:10px; padding:20px; margin-top:30px; text-align:center;">' +
'<div style="font-size:11px; color:#718096; font-weight:800; text-transform:uppercase; letter-spacing:1px; margin-bottom:10px;">Lisensi, Atribusi & Penggunaan Data</div>' +
'<p style="margin:0 0 10px; font-size:13px; color:#4a5568; line-height:1.6;">' +
'Seluruh data terstruktur yang tersaji dalam repositori ini merupakan milik publik dan tersedia di bawah ' +
'<a href="https://creativecommons.org/publicdomain/zero/1.0/"target="_blank" style="color:#6a5acd; text-decoration:none; font-weight:700;">Lisensi Creative Commons CC0</a>.' +
'</p>' +
'<p style="margin:0; font-size:12px; color:#a0aec0;">Akses API dan penggunaan data secara massal wajib mencantumkan atribusi kepada <strong>Mippedia Community</strong> selaku pengelola proyek.</p></div>';
// Injeksi
$('#mw-content-text').prepend(headerHtml);
$('#mw-content-text').append(connectorHtml).append(footerHtml);
// --- 4. LOGIC ENGINE ---
function loadLinks() {
new mw.Api().get({ action: 'query', prop: 'revisions', titles: dataPage, rvprop: 'content', formatversion: 2 }).done(function(d) {
var json = d.query.pages[0].revisions ? JSON.parse(d.query.pages[0].revisions[0].content) : {};
var data = json[pageTitle] || {};
var $box = $('#mip-display').empty();
var found = false;
Object.keys(projects).forEach(p => {
if (data[p]) {
found = true;
$box.append('<a style="background:'+projects[p].color+'; color:white; padding:8px 16px; border-radius:10px; font-size:12px; font-weight:bold; text-decoration:none; border-bottom:3px solid rgba(0,0,0,0.15);" href="'+projects[p].base+encodeURIComponent(data[p])+'" target="_blank">Mippedia '+projects[p].name+': '+data[p]+'</a>');
$('#in-' + p).val(data[p]);
}
});
if (!found) $box.html('<em style="color:#cbd5e0; font-size:13px;">Entitas ini belum terhubung ke cabang Mippedia manapun.</em>');
});
}
$(document).on('click', '#mip-save', function() {
// CEK IZIN: Tolak user biasa
if (!isAdmin) {
showBoxNotif('Suntingan anda tidak di terbitkan', 'Halaman ini dilindungi. Hanya <b>Pengurus</b> yang memiliki otoritas untuk memodifikasi metadata ekosistem secara langsung.', 'error');
return;
}
var $btn = $(this).text('Sedang Memvalidasi...').prop('disabled', true);
var api = new mw.Api();
var newData = {}, promises = [], invalid = [], isDel = true;
Object.keys(projects).forEach(p => {
var v = $('#in-' + p).val().trim();
if (v) {
isDel = false;
promises.push($.ajax({ url: projects[p].url, data: { action: 'query', titles: v, format: 'json', origin: '*' }, dataType: 'json' }).done(function(res){
if (res.query.pages["-1"]) invalid.push(projects[p].name); else newData[p] = v;
}));
}
});
$.when.apply($, promises).then(function() {
if (invalid.length > 0) {
showBoxNotif('Suntingan anda tidak di terbitkan', 'Artikel dengan judul tersebut tidak ditemukan di basis data Mippedia cabang: ' + invalid.join(', ') + '. Periksa kembali ejaan dan kapitalisasi.', 'error');
$btn.text('Terbitkan').prop('disabled', false); return;
}
api.get({ action: 'query', prop: 'revisions', titles: dataPage, rvprop: 'content', formatversion: 2 }).done(function(r) {
var full = r.query.pages[0].revisions ? JSON.parse(r.query.pages[0].revisions[0].content) : {};
if (isDel) delete full[pageTitle]; else full[pageTitle] = newData;
api.postWithEditToken({ action: 'edit', title: dataPage, text: JSON.stringify(full, null, 2), summary: 'Sync Metadata: ' + pageTitle })
.done(function() {
showBoxNotif('Suntingan anda berhasil di terbitkan!', 'Metadata entitas berhasil disinkronisasi ke seluruh jaringan ekosistem Mippedia.', 'success');
setTimeout(function(){ location.reload(); }, 1500);
}).fail(function(c) {
showBoxNotif('Gagal Menyimpan', 'Terjadi kesalahan pada token API atau koneksi server. Kode: ' + c, 'error');
$btn.text('Simpan & Sinkronkan Data').prop('disabled', false);
});
});
});
});
$(document).on('click', '#mip-open-editor', function() { $('#mip-editor').slideToggle(); });
loadLinks();
});
})();
(function() {
// Fungsi utama suntik teks
function forcePreload() {
var ns = mw.config.get('wgNamespaceNumber');
var pageExists = mw.config.get('wgArticleId') > 0;
// Cek Namespace Utama & Halaman Baru
if (ns === 0 && !pageExists) {
// Cari textarea secara spesifik (Desktop & Mobile punya selector beda)
var $textbox = $('#wpTextbox1, textarea.mw-ui-input, .editor-container textarea');
if ($textbox.length && $textbox.val().trim() === "") {
var pageName = mw.config.get('wgTitle');
$textbox.val('{{Infobox umum\n|nama = ' + pageName + '\n}}');
console.log("Mippedia: Infobox injected!");
}
}
}
// 1. Jalankan pas halaman beres load
$(document).ready(forcePreload);
// 2. Jalankan tiap ada klik (karena mobile buka editor pake klik tanpa refresh)
$(document).on('click', function() {
setTimeout(forcePreload, 800); // Kasih delay dikit nunggu overlay muncul
});
// 3. Pake MutationObserver buat mantau elemen yang baru muncul secara gaib
var observer = new MutationObserver(function() {
forcePreload();
});
observer.observe(document.body, { childList: true, subtree: true });
})();
/* ==========================================================
🚀 MIPPEDIA DATA - CONTENT GUARD (MODIFIED FOR RELATED)
========================================================== */
(function() {
function handleBlock(e) {
var ns = mw.config.get('wgNamespaceNumber');
if (ns !== 0 || mw.config.get('wgIsMainPage')) return;
var $el = $(e.currentTarget);
if ($el.closest('.mw-parser-output').length && $el.text().toLowerCase().includes('buat item')) {
return;
}
var $textbox = $('#wpTextbox1, .editor-container textarea, textarea.mw-ui-input');
if ($textbox.length) {
var content = $textbox.val() || "";
// Pengecekan: Harus ada {{Infobox ATAU setidaknya ada kode #related
var hasInfobox = content.includes('{{Infobox');
var hasRelated = content.includes('#related:');
if (!hasInfobox && !hasRelated) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
window.alert("SUNTINGAN ANDA TIDAK BISA DI TERBITKAN!\n\nMippedia Data bukan tempat untuk menulis artikel ensiklopedia, Jika anda ingin membuat artikel ensiklopedia bukan di sini tempat nya.\n");
return false;
}
}
}
$(document).on('click mousedown touchstart',
'#wpSave, .editor-save, .mw-ui-button.primary, button[type="submit"], .save-button, .header-action button',
function(e) {
handleBlock(e);
}
);
window.addEventListener('submit', function(e) {
handleBlock(e);
}, true);
})();
/* ==========================================================
🛡️ MIPPEDIA CORE SYSTEM - NEURAL ENGINE V12
Update: Brain Upgrade - Semantic Search & Anti-Duplicate
Semua fitur asli (Patroli, Promo, Kategori) tetap UTUH.
========================================================== */
mw.hook('postEdit').add(function() {
var rawUser = mw.config.get('wgUserName');
var userName = rawUser ? rawUser.replace(/_/g, ' ') : 'Anonim';
var isPatroli = (userName === 'Admin');
if (mw.config.get('wgNamespaceNumber') !== 0) return;
var api = new mw.Api();
var pageTitle = mw.config.get('wgPageName').replace(/_/g, ' ');
api.get({
action: 'query',
titles: pageTitle,
prop: 'revisions|categories|links',
rvprop: 'content',
clshow: '!hidden',
pllimit: 'max'
}).done(function(resData) {
var page = resData.query.pages[Object.keys(resData.query.pages)[0]];
if (!page || !page.revisions) return;
var rawContent = page.revisions[0]['*'];
var content = rawContent;
var summaryParts = [];
// --- 2. SUPER INTELLIGENCE RELATED (UPGRADE OTAK V12) ---
// Cek jumlah related saat ini
var currentRelatedMatch = content.match(/\{\{#related:.*?\}\}/g) || [];
// Jika kurang dari 3, kita jalankan otak pencarian canggih
if (currentRelatedMatch.length < 3) {
// Hapus yang lama dulu agar tidak ada duplikat atau "pake yang bekas"
content = content.replace(/\n*\{\{#related:.*?\}\}/g, '');
// Langkah A: Ambil kandidat dari Search Engine (Ranking Skor)
api.get({
action: 'query',
list: 'search',
srsearch: pageTitle, // Gunakan judul sebagai basis pencarian semantic
srlimit: 15,
srprop: ''
}).done(function(searchRes) {
var candidates = {};
// Masukkan hasil pencarian ke pool skor
if (searchRes.query && searchRes.query.search) {
searchRes.query.search.forEach(function(item, index) {
if (item.title === pageTitle) return;
candidates[item.title] = (15 - index); // Skor berdasarkan urutan relevansi
});
}
// Langkah B: Tambah skor jika artikel ada di daftar Link Internal
if (page.links) {
page.links.forEach(function(link) {
if (link.title === pageTitle) return;
candidates[link.title] = (candidates[link.title] || 0) + 5; // Bonus skor link
});
}
// Langkah C: Urutkan berdasarkan skor tertinggi (yang paling nyambung)
var sortedTitles = Object.keys(candidates).sort(function(a, b) {
return candidates[b] - candidates[a];
});
// Ambil TOP 3
var finalRelated = sortedTitles.filter(t => t.indexOf(':') === -1).slice(0, 3);
if (finalRelated.length > 0) {
var relatedTags = '\n\n' + finalRelated.map(t => '{{#related: ' + t + '}}').join('\n');
content += relatedTags;
summaryParts.push('NeuralRelated');
}
runPatroliAndSave();
});
} else {
runPatroliAndSave();
}
function runPatroliAndSave() {
// --- 3. PATROLI ENGINE (FITUR ASLI - TETAP BERFUNGSI) ---
if (isPatroli) {
var placeholders = [];
// Lindungi Brankas: Template, Header, Kategori, Ref, dan Kalimat Pembuka
content = content.replace(/(\{\{[\s\S]*?\}\}|==+.*?==+|\[\[Kategori:.*?\]\]|<ref[\s\S]*?<\/ref>|^.*?adalah)/gi, function(match) {
placeholders.push(match);
return '___MIP_SKIP_' + (placeholders.length - 1) + '___';
});
// Proses Auto-Link (Wajib ambil daftar AllPages dulu)
api.get({ action: 'query', list: 'allpages', apnamespace: 0, aplimit: 'max' }).done(function(apData) {
var allTitles = apData.query.allpages.map(p => p.title);
var lowerTitles = allTitles.map(t => t.toLowerCase());
var tokens = content.split(/(\s+|\[\[|\]\])/);
var inLink = false;
for (var i = 0; i < tokens.length; i++) {
if (tokens[i] === '[[' ) { inLink = true; continue; }
if (tokens[i] === ']]' ) { inLink = false; continue; }
if (inLink || tokens[i].trim().length < 3 || tokens[i].includes('___MIP_SKIP_')) continue;
var wordsInToken = tokens[i].match(/\b\w+\b/g);
if (wordsInToken) {
wordsInToken.forEach(function(word) {
var idx = lowerTitles.indexOf(word.toLowerCase());
if (idx !== -1 && allTitles[idx] !== pageTitle) {
tokens[i] = tokens[i].replace(new RegExp('\\b' + word + '\\b', 'g'), '[[' + allTitles[idx] + '|' + word + ']]');
}
});
}
}
content = tokens.join('');
// Kembalikan dari Brankas
content = content.replace(/___MIP_SKIP_(\d+)___/g, function(match, id) { return placeholders[id]; });
// Bersihkan Link Merah
content = content.replace(/\[\[([^|\]:]+)\]\]/g, function(match, p1) {
return lowerTitles.includes(p1.toLowerCase()) ? match : p1;
});
saveAction();
});
} else {
saveAction();
}
}
function saveAction() {
// --- 4. EKSEKUSI ---
if (content.trim() !== rawContent.trim()) {
api.postWithToken('edit', {
action: 'edit',
title: pageTitle,
text: content,
summary: 'Mippedia Neural Engine V12: ' + summaryParts.join(', '),
bot: true, markasbot: true
}).done(function() { location.reload(); });
}
}
});
});
/* ==========================================================
🌐 MIPPEDIA SHORTDESC - DATA CENTER (PREMIUM UI)
Status: INDEPENDENT & REFINED VISUAL
Storage: MediaWiki:ShortDesc-Data.json
========================================================== */
(function($, mw) {
"use strict";
$(document).ready(function() {
if (mw.config.get('wgNamespaceNumber') !== 0 || mw.config.get('wgIsMainPage')) return;
var pageTitle = mw.config.get('wgPageName');
var storagePage = 'MediaWiki:ShortDesc-Data.json';
var isAdmin = mw.config.get('wgUserGroups').some(g => ['sysop', 'interface-admin'].includes(g));
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 = `
<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="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 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>
</div>
</div>`;
$('.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>');
}
});
}
$(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; }
var $btn = $(this).text('Proses Sinkronisasi...').css('opacity','0.7');
var api = new mw.Api();
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) {
var json = r.query.pages[0].revisions ? JSON.parse(r.query.pages[0].revisions[0].content) : {};
if (!newData.id && !newData.en && !newData.concise) { delete json[pageTitle]; } else { json[pageTitle] = newData; }
api.postWithEditToken({
action: 'edit',
title: storagePage,
text: JSON.stringify(json, null, 2),
summary: 'Update Entity Description: ' + pageTitle
}).done(function() { location.reload(); });
});
});
loadDescStatus();
});
})(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);
}
}
});
})();