MediaWiki:Common.js: Perbedaan antara revisi

Tidak ada ringkasan suntingan
Tanda: Suntingan perangkat seluler Suntingan peramban seluler
Tidak ada ringkasan suntingan
Tanda: Suntingan perangkat seluler Suntingan peramban seluler
 
(47 revisi perantara oleh pengguna yang sama tidak ditampilkan)
Baris 1: Baris 1:
/* ==========================================================
/* ==========================================================
   🧠 MIPPEDIA DATA - SMART CONTEXT + AUTO THUMBNAIL
  ⚡ MIPPEDIA SPEED OPTIMIZER - PURE SPEED & STABLE (V1.1)
   Features: Smart Sync, Auto-Age, Auto-Bold, Smart Links,
  Features: Instant Link Pre-fetching Engine (No Animation/Pure Performance).
             Dynamic Context, AND NEW: Auto Thumbnail Image
  Optimization: Zero-flicker, scoped lifecycle protection, advanced anti-cache clutter.
   ========================================================== */
  Target: Built for maximum long-term stability and bulletproof caching.
  ========================================================= */
 
(function() {
(function() {
    $(document).ready(function() {
    // Ambil nama host secara dinamis (contoh: data.mippedia.org)
        var $descSection = $('#mip-desc-section'), $descBox = $('#mip-auto-description'),
    var currentHost = window.location.hostname;
            $sourceInfo = $('#mip-source-info'), $portalLinks = $('#mip-portal-links'),
            $projectPortal = $('#mip-project-portal');
        
        if (!$descBox.length) return;


        var pageTitle = mw.config.get('wgPageName').replace(/_/g, ' '),
    // --- 🗲 ENGINE PRE-FETCH CORES ---
            cacheKey = 'mip_smart_context_thumb_' + pageTitle, now = new Date().getTime(),
    function injectPrefetchLink(url) {
            currentYear = 2026;
        // Jaring Pengaman 1: Jangan injeksi jika link prefetch untuk URL ini sudah ada di head browser
        if (document.querySelector('link[href="' + url + '"]')) return;


        var projects = [
        var prefetcher = document.createElement('link');
            { id: 'id', name: 'Mippedia bahasa Indonesia', url: 'https://id.mippedia.org/api.php', base: 'https://id.mippedia.org/wiki/', label: 'Mippedia Indonesia' },
        prefetcher.rel = 'prefetch';
            { id: 'en', name: 'Mippedia bahasa Inggris', url: 'https://en.mippedia.org/api.php', base: 'https://en.mippedia.org/wiki/', label: 'Mippedia Inggris' },
        prefetcher.href = url;
            { id: 'concise', name: 'Mippedia bahasa Indonesia ringkas', url: 'https://concise.mippedia.org/api.php', base: 'https://concise.mippedia.org/wiki/', label: 'Mippedia Ringkas' }
       
        ];
        // Jaring Pengaman 2: Failsafe anti-eror cache jika network mendadak sibuk
        prefetcher.onerror = function() { prefetcher.remove(); };
       
        document.head.appendChild(prefetcher);
    }


        // --- SEMUA FUNGSI AWAL (TETAP UTUH) ---
    function initPrefetchDispatch(href) {
        function applyAutoBold(text) { return text.replace(new RegExp('(' + pageTitle + ')', 'gi'), '<strong>$1</strong>'); }
        // Memanfaatkan requestIdleCallback agar proses prefetch hanya berjalan saat browser senggang
        function cleanExtract(text) { return text.replace(/\[\d+\]/g, '').replace(/\{\{[^}]+\}\}/g, '').replace(/\(\s*\)/g, '').replace(/\s\s+/g, ' ').trim(); }
        // sehingga tidak akan membuat scrolling halaman terasa berat/patah-patah
        
        if (window.requestIdleCallback) {
        function applyDynamicContext(text) {
            window.requestIdleCallback(function() { injectPrefetchLink(href); });
            var date = new Date();
        } else {
            var hari = ["Minggu", "Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu"];
            setTimeout(function() { injectPrefetchLink(href); }, 20);
            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
    // --- 🎮 COGNITIVE EVENT LISTENERS (UNIVERSAL HOVER & TOUCH) ---
                .replace(/(hari ini|saat ini|sekarang)/gi, '$1 (' + tglSkrg + ')')
    // Mendengarkan area konten utama MediaWiki (Desktop & Tampilan Mobile HP)
                .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) { 
    $(document).on('mouseenter pointerdown', '#mw-content-text a, .mw-body-content a, #bodyContent a, .mw-parser-output a', function() {
                    return f + " <span style='color: #444; font-weight: bold;'>– usia " + (currentYear - parseInt(y)) + " tahun</span>"; 
        var $link = $(this);
                })
        var href = $link.attr('href');
                .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) {
        // --- 🛡️ FILTER STRATA TINGGI: ABREVIASI LINK BERGUNA (ANTI CRASH CACHE) ---
            var limit = 250;
        // 1. Abaikan link kosong, javascript void, atau buka tab baru (_blank)
            if (text.length <= limit) return '<span>' + text + '</span>';
        if (!href || href.startsWith('#') || href.startsWith('javascript:') || $link.attr('target') === '_blank') return;
            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>';
       
        }
        // 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;


        function buildSmartLinks(currentId) {
        // Pastikan hanya mengeksekusi routing internal ekosistem Mippedia
            $portalLinks.empty();
        var isInternal = href.indexOf(currentHost) !== -1 || href.startsWith('/') || !href.includes('://');
            var validLinks = [];
        if (isInternal) {
            var checkRequests = projects.filter(p => p.id !== currentId).map(function(p) {
            initPrefetchDispatch(href);
                return $.ajax({ url: p.url, data: { action: 'query', titles: pageTitle, format: 'json', origin: '*' }, dataType: 'json' })
        }
                    .then(function(data) { if (data.query.pages["-1"] === undefined) { validLinks.push('<a href="' + p.base + encodeURIComponent(pageTitle) + '" 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(); }
            });
        }


        // --- SISTEM SMART CHECK DENGAN SUPPORT THUMBNAIL ---
/* ==========================================================
        var cached = JSON.parse(localStorage.getItem(cacheKey) || "{}");
  🌐 MIPPEDIA TAB ROUTER INTEGRATOR - UNIVERSAL TRANSITION (V1.2)
  Features: Pure Pre-Navigation Routing Simulation on Tab Title.
  Optimization: Universal trigger for any navigation, 0% page-load flicker.
  Target: Clean, lightweight, long-term safety.
  ========================================================= */


        function init() {
(function() {
            var pCheck = projects[Math.floor(Math.random() * projects.length)];
    // Ambil nama host secara dinamis (contoh: data.mippedia.org)
            $.ajax({
    var currentHost = window.location.hostname;
                url: pCheck.url,
    var routingTimeout = null;
                data: { action: 'query', prop: 'revisions|pageimages', rvprop: 'timestamp', piprop: 'thumbnail', pithumbsize: 150, titles: pageTitle, format: 'json', origin: '*' },
    var rawOriginalTitle = document.title;
                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";
                    // Ambil thumbnail kalau ada
                    var thumbUrl = (id != "-1" && pg[id].thumbnail) ? pg[id].thumbnail.source : "";


                    if (cached.content && cached.ts === latestTS) {
    // --- ENGINE CORE: UBAH JUDUL JADI DOMAIN ---
                        renderAll(cached.p, cached.content, cached.isTrans, cached.orig, cached.img);
    function executeUniversalRouting() {
                    } else { 
        if (routingTimeout) clearTimeout(routingTimeout);
                        fetchFreshData(latestTS, thumbUrl); 
       
                    }
        // Tangkap judul asli terupdate sebagai jaring pengaman
                },
        var liveTitle = document.title;
                error: function() { if (cached.content) renderAll(cached.p, cached.content, cached.isTrans, cached.orig, cached.img); }
        if (liveTitle !== currentHost) {
            });
            rawOriginalTitle = liveTitle;
        }
        }


        function fetchFreshData(newTS, img) {
        // SET DOMAIN DI AWAL (Saat aksi diklik / sebelum pindah halaman)
            var found = false;
        document.title = currentHost;
            var shuffled = projects.sort(() => Math.random() - 0.5);
            shuffled.forEach(function(p) {
                if (found) return;
                $.ajax({
                    url: p.url,
                    data: { action: 'query', prop: 'extracts', exintro: 1, explaintext: 1, titles: pageTitle, format: 'json', origin: '*' },
                    dataType: 'json',
                    success: function(data) {
                        if (found) return;
                        var pages = data.query.pages, pageId = Object.keys(pages)[0];
                        if (pageId != "-1" && pages[pageId].extract) {
                            found = true;
                            var extract = cleanExtract(pages[pageId].extract);
                            if (p.id === 'en') {
                                $.ajax({ url: "https://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=id&dt=t&q=" + encodeURIComponent(extract), success: function(res) { 
                                    var trans = ""; res[0].forEach(s => { if (s[0]) trans += s[0]; });
                                    finalize(p, trans, true, extract, newTS, img);
                                }});
                            } else { 
                                finalize(p, extract, false, "", newTS, img); 
                            }
                        }
                    }
                });
            });
        }


        function finalize(p, c, t, o, ts, img) {
        // Failsafe: Jika dalam 3 detik halaman tidak jadi pindah (batal/koneksi drop),
            localStorage.setItem(cacheKey, JSON.stringify({p:p, content:c, isTrans:t, orig:o, ts:ts, img:img}));
        // kembalikan ke judul semula agar tidak tersangkut.
            renderAll(p, c, t, o, img);
        routingTimeout = setTimeout(function() {
        }
            if (document.title === currentHost) {
                document.title = rawOriginalTitle;
            }
        }, 3000);
    }


        function renderAll(p, currentText, isTranslated, originalText, img) {
    // --- ⚡ PEMICU 1: UNIVERSAL CLICK CONTROLLER (APAPUN YANG DIKLIK) ---
            var processedText = applyAutoBold(applyDynamicContext(currentText));
    // Menggunakan Vanilla JS Event Listener di tingkat window agar mencakup seluruh elemen tanpa tebang pilih
            
    window.addEventListener('click', function(e) {
            // Layouting Thumbnail (Tetap ringan: hanya render jika gambar ada)
        // Cari tahu apakah elemen yang diklik berada di dalam tag <a> (Link)
            var thumbHtml = img ? '<div style="float: right; margin-left: 15px; margin-bottom: 10px; border: 1px solid #ddd; padding: 3px; background: #fff; border-radius: 4px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);"><img src="'+img+'" style="max-width: 100px; display: block; height: auto;"></div>' : '';
        var anchor = e.target.closest('a');
       
        if (anchor) {
            var href = anchor.getAttribute('href');
            var target = anchor.getAttribute('target');


            $descSection.show();
            // Abaikan jika link kosong, link jangkar internal halaman (#), atau buka di tab baru (_blank)
            // Masukkan thumbnail ke dalam konten box
            if (!href || href.startsWith('#') || href.startsWith('javascript:') || target === '_blank') {
            $descBox.show().html(thumbHtml + applySummary(processedText) + '<div style="clear:both;"></div>');
                return;
            
            }
            var footer = '<div style="font-size: 0.9em; color: #777;">Sumber : <a href="' + p.base + encodeURIComponent(pageTitle) + '" 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>'; }
            // Eksekusi jika perpindahan terjadi di domain yang sama (internal routing)
            $sourceInfo.show().html(footer);
            var isInternal = href.indexOf(currentHost) !== -1 || href.startsWith('/') || !href.includes('://');
            
            if (isInternal) {
            buildSmartLinks(p.id);
                executeUniversalRouting();
            
            }
            // Handlers
        } else {
            $(document).off('click', '.mip-read-btn').on('click', '.mip-read-btn', function() { $(this).hide(); $('.mip-dots').hide(); $('.mip-more').fadeIn(); });
            // JIKA YANG DIKLIK BUKAN LINK (Tapi tombol biasa, widget, atau elemen aksi pemindah halaman lainnya)
            $(document).off('click', '#mip-toggle-orig').on('click', '#mip-toggle-orig', function() {
            // Kita cek apakah elemen tersebut memiliki indikasi memicu fungsi perpindahan/loading
                var isOrig = ($(this).text() === 'Tampilkan versi asli');
            var elementTag = e.target.tagName.toLowerCase();
                var textToDisplay = applyAutoBold(applyDynamicContext(isOrig ? originalText : currentText));
            if (elementTag === 'button' || e.target.closest('button') || e.target.getAttribute('role') === 'button') {
                // Pastikan thumbnail tetap ada saat toggle versi asli
                executeUniversalRouting();
                $descBox.html(thumbHtml + applySummary(textToDisplay) + '<div style="clear:both;"></div>');
            }
                $(this).text(isOrig ? 'Tampilkan terjemahan' : 'Tampilkan versi asli');
        }
            });
    }, { passive: true });
        }
 
    // --- 💾 PEMICU 2: UNIVERSAL SUBMIT CONTROLLER (SIMPAN/FORM) ---
    // Memicu efek domain saat submit form (Simpan halaman, cari artikel, dll)
    window.addEventListener('submit', function() {
        executeUniversalRouting();
    });


        init();
    // --- 🛡️ SAFETY GUARDIAN: ANTI STUCK ON TAB BLUR ---
    });
    // Jika user buru-buru pindah tab sebelum halaman sempat berganti, kembalikan judul asli
    window.addEventListener('blur', function() {
        if (routingTimeout) clearTimeout(routingTimeout);
        if (document.title === currentHost) {
            document.title = rawOriginalTitle;
        }
    });
    window.addEventListener('pagehide', function() {
        if (routingTimeout) clearTimeout(routingTimeout);
        if (document.title === currentHost) {
            document.title = rawOriginalTitle;
        }
    });
})();
 
/* ==========================================================
  🧠 MIPPEDIA DATA - SMART CONTEXT + AUTO THUMBNAIL (V10.1)
  Features: Priority Check (ID > EN > CONCISE), Auto-Age,
            Auto-Bold, Smart Links, Dynamic Context,
            Targeted Isolation (Safe for Forms), Dynamic Noindex,
            Aesthetic Centered Empty State + 🚀 Instant Action Hub (3 Buttons)
  Fixes: Mobile View Visibility & Custom Short URL Route (No /w/)
  ========================================================= */
(function() {
    $(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();
    });
})();
})();


Baris 529: Baris 863:
     });
     });
});
});
/* ==========================================================
  🌐 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);


/* ==========================================================
/* ==========================================================
Baris 609: Baris 1.045:
             }
             }
         }
         }
    });
})();
/* ==========================================================
  ⏳ MIPPEDIA DATA - SMART KRONOLOGI 2.0 (INTELLIGENT MODE)
  Fitur: Floating Button, Deep Analysis Timeline, No-Leak Panel
  ========================================================== */
(function() {
    $(document).ready(function() {
        var events = [];
        var currentYear = 2026;
       
        // 1. Scan data secara cerdas & tentukan Titik Nol (Lahir/Berdiri)
        $('.infobox tr, .wikitable tr, .mip-data-table tr').each(function() {
            var label = $(this).find('th').text().trim().replace(':', '');
            var value = $(this).find('td').text().trim();
            var yearMatch = value.match(/\b(19|20)\d{2}\b/);
           
            if (yearMatch && label && label.length < 25) {
                events.push({ year: parseInt(yearMatch[0]), label: label, info: value });
            }
        });
        if (events.length === 0) return;
        events.sort((a, b) => a.year - b.year);
        var baseYear = events[0].year; // Tahun pertama sebagai referensi
        // 2. Buat Floating Button (Pastikan bersih)
        $('#mip-timeline-btn, #mip-timeline-panel').remove(); // Bersihkan yang lama
        var $floatBtn = $('<div id="mip-timeline-btn">⏳</div>').css({
            'position': 'fixed', 'bottom': '25px', 'right': '25px',
            'width': '50px', 'height': '50px', 'background': '#6a5acd',
            'color': 'white', 'border-radius': '50%', 'display': 'flex',
            'align-items': 'center', 'justify-content': 'center',
            'font-size': '20px', 'cursor': 'pointer', 'z-index': '9999',
            'box-shadow': '0 4px 15px rgba(106, 92, 205, 0.4)', 'transition': '0.3s'
        });
        // 3. Buat Sidebar Panel (Gunakan % agar tidak bocor di samping)
        var $sidePanel = $('<div id="mip-timeline-panel">').css({
            'position': 'fixed', 'top': '0', 'right': '-110%', // Sembunyi total
            'width': '85%', 'max-width': '350px', 'height': '100%', 'background': '#fff',
            'box-shadow': '-10px 0 30px rgba(0,0,0,0.2)', 'z-index': '10000',
            'transition': '0.5s cubic-bezier(0.19, 1, 0.22, 1)', 'padding': '25px',
            'overflow-y': 'auto'
        });
        // Header
        var panelHtml = '<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:25px; border-bottom:2px solid #6a5acd; padding-bottom:10px;">' +
                        '<span style="font-weight:bold; color:#333; font-size:1.2em;">Kronologi Entitas</span>' +
                        '<span id="close-mip-timeline" style="cursor:pointer; font-size:28px; color:#999; line-height:1;">×</span></div>';
       
        var listHtml = '<div style="border-left:2px solid #6a5acd; margin-left:12px; padding-left:25px;">';
       
        events.forEach(function(ev, index) {
            var gap = ev.year - baseYear;
            var timeKonteks = "";
           
            if (index === 0) {
                timeKonteks = "— Titik Awal";
            } else {
                timeKonteks = "— " + gap + " tahun kemudian";
            }
            listHtml += '<div style="position:relative; margin-bottom:30px;">' +
                        '<div style="position:absolute; left:-32px; top:5px; width:12px; height:12px; background:#6a5acd; border-radius:50%; border:3px solid #fff; box-shadow:0 0 0 2px #6a5acd;"></div>' +
                        '<div style="font-weight:bold; color:#6a5acd; font-size:1.1em;">' + ev.year + ' <span style="font-size:0.75em; color:#888; font-weight:normal;">' + timeKonteks + '</span></div>' +
                        '<div style="font-size:0.9em; color:#333; margin-top:5px; font-weight:600;">' + ev.label + '</div>' +
                        '<div style="font-size:0.85em; color:#666; font-style:italic; line-height:1.4;">' + ev.info + '</div></div>';
        });
       
        listHtml += '</div>';
        $sidePanel.html(panelHtml + listHtml);
        $('body').append($floatBtn).append($sidePanel);
        // 4. Handler Interaksi
        $floatBtn.on('click', function() { $sidePanel.css('right', '0'); });
        $('#close-mip-timeline').on('click', function() { $sidePanel.css('right', '-110%'); });
        $(document).on('mouseup', function(e) {
            if (!$sidePanel.is(e.target) && $sidePanel.has(e.target).length === 0 && !$floatBtn.is(e.target)) {
                $sidePanel.css('right', '-110%');
            }
        });
    });
})();
/* ==========================================================
  🧠 MIPPEDIA DATA - SMART REDIRECT SUGGESTION
  Mendeteksi typo dan menyarankan halaman yang benar
  ========================================================== */
(function() {
    $(document).ready(function() {
        // Cek apakah ini halaman "Halaman tidak ditemukan"
        if (!$('.noarticletext').length && !$('#noarticletext').length) return;
        var brokenTitle = mw.config.get('wgPageName').replace(/_/g, ' ');
        var api = new mw.Api();
        api.get({
            action: 'query',
            list: 'search',
            srsearch: brokenTitle,
            srlimit: 3,
            format: 'json'
        }).done(function(data) {
            var suggestions = data.query.search;
           
            if (suggestions.length > 0) {
                var $suggestBox = $('<div id="mip-suggest-box">').css({
                    'margin': '20px 0', 'padding': '20px', 'background': '#f4f1ff',
                    'border': '1px solid #6a5acd', 'border-radius': '12px', 'text-align': 'center'
                });
                $suggestBox.append('<div style="font-size:1.5em; margin-bottom:10px;">🤔</div>');
                $suggestBox.append('<div style="font-weight:bold; color:#333;">Halaman tidak ditemukan, tapi mungkin maksud Anda:</div>');
               
                var $list = $('<div style="margin-top:15px; display:flex; flex-wrap:wrap; justify-content:center; gap:10px;"></div>');
               
                suggestions.forEach(function(s) {
                    var sLink = '<a href="/wiki/' + encodeURIComponent(s.title.replace(/ /g, '_')) + '" ' +
                                'style="padding:8px 15px; background:#6a5acd; color:white; border-radius:20px; text-decoration:none; font-weight:bold; font-size:0.9em;">' +
                                s.title + '</a>';
                    $list.append(sLink);
                });
                $suggestBox.append($list);
                $('.noarticletext').first().after($suggestBox);
            }
        });
     });
     });
})();
})();