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
Baris 1: Baris 1:
/**
/**
  * ====================================================================
  * ====================================================================
  * MIPPEDIA DATA - ADVANCED STRUCTURED DATA SYSTEM (PRO-MAX VERSION)
  * MIPPEDIA DATA - ADVANCED STRUCTURED DATA SYSTEM (V4.0 ULTIMATE)
  * Fitur: Advanced Citation, Bypass Cache Fix, UI Grid, Async Loading
  * Fitur: Mobile Responsive, Smart Cache Bypass, Custom Modal, Fix Refs
  * ====================================================================
  * ====================================================================
  */
  */
Baris 8: Baris 8:
     'use strict';
     'use strict';


     // 1. CEK LINGKUNGAN: Hanya jalan di Namespace 0 (Artikel) & Bukan Halaman Utama
     // 1. CEK LINGKUNGAN
     if (mw.config.get('wgNamespaceNumber') !== 0 || mw.config.get('wgIsMainPage')) {
     if (mw.config.get('wgNamespaceNumber') !== 0 || mw.config.get('wgIsMainPage')) {
         return;
         return;
Baris 29: Baris 29:
         var svgLock = '<svg class="md-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><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 svgLock = '<svg class="md-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><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="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><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 svgUnlock = '<svg class="md-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><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="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><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 svgRef = '<svg class="md-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><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 svgLink = '<svg class="md-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><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 svgInfo = '<svg class="md-icon" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#d32f2f" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg>';


         // 3. INJEKSI CSS TAMPILAN
         // 3. INJEKSI CSS TAMPILAN (DIPERBARUI UNTUK MOBILE)
         var css = `
         var css = `
             #mippedia-data-app {
             #mippedia-data-app {
                 background: #ffffff;
                 background: #ffffff; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.05);
                border-radius: 12px;
                 padding: 24px; margin: 24px 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
                box-shadow: 0 8px 24px rgba(0,0,0,0.06);
                 border: 1px solid #eaeaea; transition: all 0.3s ease;
                 padding: 24px;
                margin: 24px 0;
                font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
                 border: 1px solid #eef0f2;
                transition: all 0.3s ease;
             }
             }
             .md-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; padding-bottom: 15px; border-bottom: 1px solid #f0f0f0; }
             .md-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; padding-bottom: 15px; border-bottom: 1px solid #f0f0f0; }
Baris 51: Baris 46:
             .md-btn-icon {
             .md-btn-icon {
                 background: #f8f9fa; border: 1px solid #e2e2e2; cursor: pointer; color: #5a42b8;
                 background: #f8f9fa; border: 1px solid #e2e2e2; cursor: pointer; color: #5a42b8;
                 padding: 7px 12px; border-radius: 8px; transition: all 0.2s ease;
                 padding: 7px 14px; border-radius: 8px; transition: all 0.2s ease;
                 display: inline-flex; align-items: center; gap: 6px; font-size: 0.85em; font-weight: 600;
                 display: inline-flex; align-items: center; gap: 6px; font-size: 0.85em; font-weight: 600;
             }
             }
             .md-btn-icon:hover:not(:disabled) { background: #eef2f5; border-color: #d0d0d0; transform: translateY(-1px); }
             .md-btn-icon:hover:not(:disabled) { background: #eef2f5; border-color: #d0d0d0; }
             .md-btn-lock { color: #d32f2f; background: #fff5f5; border-color: #fbd6d6; }
             .md-btn-lock { color: #d32f2f; background: #fff5f5; border-color: #fbd6d6; }
             .md-btn-lock:hover:not(:disabled) { background: #fee2e2; }
             .md-btn-lock:hover:not(:disabled) { background: #fee2e2; }
             .md-btn-unlock { color: #2e7d32; background: #f1fbf2; border-color: #c8e6c9; }
           
             .md-btn-unlock:hover:not(:disabled) { background: #e8f5e9; }
            /* Badge Terkunci untuk Anon */
             .md-badge-locked {
                color: #d32f2f; font-size: 0.9em; font-weight: bold; background: #fff5f5;
                padding: 7px 12px; border-radius: 8px; border: 1px solid #fbd6d6;
                cursor: pointer; display: inline-flex; align-items: center; gap: 6px; transition: 0.2s;
            }
             .md-badge-locked:hover { background: #fee2e2; transform: scale(1.02); }
              
              
             .md-row-display {
             .md-row-display {
                 display: flex; justify-content: space-between; align-items: flex-start;
                 display: flex; justify-content: space-between; align-items: flex-start;
                 padding: 14px 10px; border-bottom: 1px solid #f5f5f5; transition: background 0.2s; border-radius: 6px;
                 padding: 16px 12px; border-bottom: 1px solid #f5f5f5; transition: background 0.2s; border-radius: 8px; gap: 15px;
             }
             }
             .md-row-display:hover { background: #fafafa; }
             .md-row-display:hover { background: #fafafa; box-shadow: inset 4px 0 0 #5a42b8; }
             .md-row-display:last-child { border-bottom: none; }
             .md-row-display:last-child { border-bottom: none; }
              
              
             .md-data-group { display: flex; flex-direction: column; gap: 6px; flex: 1; }
             .md-data-group { display: flex; flex-direction: column; gap: 4px; flex: 1; }
             .md-data-label { font-size: 0.8em; color: #757575; font-weight: 700; text-transform: uppercase; letter-spacing: 0.8px; }
             .md-data-label { font-size: 0.75em; color: #666; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; }
             .md-data-value { font-size: 1.05em; color: #202124; line-height: 1.4; }
             .md-data-value { font-size: 1.05em; color: #202124; line-height: 1.4; font-weight: 500; }
              
              
             .md-ref-badge {
            /* Desain Referensi Baru (Elegan & Nyata) */
                font-size: 0.75em; background: #f1f3f4; padding: 4px 10px; border-radius: 20px;
             .md-ref-container { margin-top: 6px; display: flex; flex-direction: column; gap: 4px; background: #f8f9fa; padding: 10px 12px; border-radius: 8px; border-left: 3px solid #1a73e8; }
                text-decoration: none !important; color: #1a73e8; margin-left: 10px;
            .md-ref-link { font-size: 0.85em; font-weight: 600; color: #1a73e8; text-decoration: none; display: inline-flex; align-items: center; gap: 5px; }
                display: inline-flex; align-items: center; gap: 4px; border: 1px solid #e8eaed; transition: all 0.2s;
            .md-ref-link:hover { text-decoration: underline; }
            }
             .md-ref-meta { font-size: 0.75em; color: #5f6368; line-height: 1.5; }
             .md-ref-badge:hover { background: #e8f0fe; color: #174ea6; border-color: #d2e3fc; }
              
              
             /* Form Edit (Grid Layout) */
             /* Form Edit */
             .md-form-box { background: #fafbfc; border-radius: 10px; padding: 20px; margin-top: 15px; border: 1px solid #eaedf0; }
             .md-form-box { background: #fafbfc; border-radius: 12px; padding: 24px; margin-top: 20px; border: 1px solid #eaedf0; box-shadow: 0 4px 15px rgba(0,0,0,0.03); }
             .md-form-title { font-size: 1em; font-weight: 600; color: #333; margin: 0 0 15px 0; border-bottom: 2px solid #e2e2e2; padding-bottom: 5px; display: inline-block;}
             .md-form-title { font-size: 1.1em; font-weight: 700; color: #333; margin: 0 0 20px 0; border-bottom: 2px solid #e2e2e2; padding-bottom: 8px; }
             .md-form-group { margin-bottom: 15px; }
             .md-form-group { margin-bottom: 18px; }
             .md-form-group label { display: block; font-size: 0.85em; font-weight: 600; margin-bottom: 6px; color: #4a4a4a; }
             .md-form-group label { display: block; font-size: 0.85em; font-weight: 600; margin-bottom: 8px; color: #444; }
             .md-input, .md-select {
             .md-input, .md-select {
                 width: 100%; padding: 10px 14px; border: 1px solid #cfd4d9; border-radius: 8px;
                 width: 100%; padding: 12px 14px; border: 1px solid #ccc; border-radius: 8px;
                 box-sizing: border-box; font-family: inherit; font-size: 0.95em; transition: all 0.2s; background: #fff;
                 box-sizing: border-box; font-family: inherit; font-size: 0.95em; transition: 0.2s; background: #fff;
             }
             }
             .md-input:focus, .md-select:focus { border-color: #5a42b8; outline: none; box-shadow: 0 0 0 3px rgba(90, 66, 184, 0.1); }
             .md-input:focus, .md-select:focus { border-color: #5a42b8; outline: none; box-shadow: 0 0 0 3px rgba(90, 66, 184, 0.15); }
              
              
            /* Grid Referensi 2 Kolom */
             .md-ref-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; background: #fff; padding: 18px; border-radius: 8px; border: 1px dashed #bbb; }
             .md-ref-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; background: #fff; padding: 15px; border-radius: 8px; border: 1px dashed #ccc; }
             .md-grid-full { grid-column: span 2; }
             .md-grid-full { grid-column: span 2; }
            .md-checkbox-group { display: flex; align-items: center; gap: 8px; font-size: 0.9em; color: #444; margin-top: 10px; cursor: pointer; }
              
              
            /* Tombol & Spinner */
             .md-form-actions { display: flex; gap: 12px; align-items: center; margin-top: 25px; padding-top: 15px; border-top: 1px solid #eaeaea; }
             .md-form-actions { display: flex; gap: 12px; align-items: center; margin-top: 20px; padding-top: 15px; border-top: 1px solid #eaeaea; }
             .md-btn {
             .md-btn {
                 padding: 10px 20px; border: none; border-radius: 8px; font-weight: 600; cursor: pointer;
                 padding: 12px 24px; border: none; border-radius: 8px; font-weight: 600; cursor: pointer;
                 color: white; font-size: 0.9em; transition: all 0.2s; display: inline-flex; align-items: center; gap: 8px;
                 color: white; font-size: 0.9em; transition: 0.2s; display: inline-flex; align-items: center; gap: 8px; justify-content: center;
             }
             }
             .md-btn:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 4px 10px rgba(0,0,0,0.15); }
             .md-btn:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.15); }
             .md-btn:disabled { opacity: 0.6; cursor: not-allowed; }
             .md-btn:disabled { opacity: 0.6; cursor: not-allowed; }
             .md-btn-save { background: linear-gradient(135deg, #6b52d1, #5a42b8); }
             .md-btn-save { background: linear-gradient(135deg, #6b52d1, #5a42b8); }
Baris 106: Baris 103:
             .md-btn-delete { background: linear-gradient(135deg, #e53935, #c62828); margin-left: auto; }
             .md-btn-delete { background: linear-gradient(135deg, #e53935, #c62828); margin-left: auto; }
              
              
             .md-spinner {
             .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; }
                display: none; width: 14px; height: 14px; border: 2px solid rgba(255, 255, 255, 0.4);
                border-radius: 50%; border-top-color: #ffffff; animation: md-spin 0.8s linear infinite;
            }
             .md-btn-icon .md-spinner { border-color: rgba(0,0,0,0.2); border-top-color: currentColor; }
             .md-btn-icon .md-spinner { border-color: rgba(0,0,0,0.2); border-top-color: currentColor; }
             .is-loading .md-spinner { display: inline-block; }
             .is-loading .md-spinner { display: inline-block; }
             @keyframes md-spin { to { transform: rotate(360deg); } }
             @keyframes md-spin { to { transform: rotate(360deg); } }
           
            /* Custom Modal Alert */
            .md-overlay {
                position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(0,0,0,0.5); backdrop-filter: blur(3px);
                z-index: 9999; display: flex; justify-content: center; align-items: center; opacity: 0; visibility: hidden; transition: 0.3s;
            }
            .md-overlay.md-show { opacity: 1; visibility: visible; }
            .md-modal {
                background: #fff; padding: 25px; border-radius: 16px; width: 90%; max-width: 400px;
                text-align: center; box-shadow: 0 10px 40px rgba(0,0,0,0.2); transform: translateY(20px); transition: 0.3s;
            }
            .md-overlay.md-show .md-modal { transform: translateY(0); }
            .md-modal-title { font-size: 1.2em; font-weight: 700; color: #333; margin: 15px 0 10px; }
            .md-modal-text { font-size: 0.95em; color: #555; line-height: 1.5; margin-bottom: 20px; }
            .md-btn-close-modal { background: #5a42b8; color: #fff; padding: 10px 20px; border: none; border-radius: 8px; font-weight: 600; cursor: pointer; width: 100%; transition: 0.2s;}
            .md-btn-close-modal:hover { background: #48349c; }
            /* MEDIA QUERY UNTUK HP (MOBILE RESPONSIVE) */
            @media (max-width: 600px) {
                #mippedia-data-app { padding: 15px; }
                .md-header { flex-direction: column; align-items: flex-start; gap: 15px; }
                .md-actions { width: 100%; justify-content: space-between; }
                .md-row-display { flex-direction: column; position: relative; padding-bottom: 20px; }
                .md-btn-icon.edit-btn { position: absolute; top: 10px; right: 10px; padding: 5px 10px; }
                .md-ref-grid { grid-template-columns: 1fr; gap: 10px; padding: 12px; }
                .md-grid-full { grid-column: span 1; }
                .md-form-actions { flex-direction: column; width: 100%; }
                .md-btn { width: 100%; margin: 0 !important; }
            }
              
              
             .md-native-render { display: none; }
             .md-native-render { display: none; }
            .md-small-text { font-size: 0.8em; color: #888; font-weight: normal; }
         `;
         `;
         $('<style>').text(css).appendTo('head');
         $('<style>').text(css).appendTo('head');


         // 4. PARSER DATA
         // 4. SISTEM MODAL NOTIFIKASI
        function showModal(title, text) {
            var $overlay = $('<div class="md-overlay"></div>');
            var $modal = $('<div class="md-modal"></div>');
            $modal.append(svgInfo);
            $modal.append('<div class="md-modal-title">' + title + '</div>');
            $modal.append('<div class="md-modal-text">' + text + '</div>');
           
            var $btn = $('<button class="md-btn-close-modal">Saya Mengerti</button>');
            $btn.on('click', function() { $overlay.removeClass('md-show'); setTimeout(function(){ $overlay.remove(); }, 300); });
            $modal.append($btn);
            $overlay.append($modal);
            $('body').append($overlay);
           
            // Trigger animasi
            setTimeout(function() { $overlay.addClass('md-show'); }, 10);
        }
 
        // 5. PARSER DATA
         function loadData() {
         function loadData() {
             return api.get({
             return api.get({
Baris 137: Baris 177:
         }
         }


         // --- HELPER UNTUK MEMBENTUK TEKS REFERENSI ---
         // --- HELPER UNTUK MEMBENTUK TEKS REFERENSI (WIKITEXT) ---
         function buildWikitextRef(refObj) {
         function buildWikitextRef(refObj) {
             if (!refObj) return "";
             if (!refObj) return "";
             if (typeof refObj === 'string') return refObj ? "[ " + refObj + " Rujukan]" : "";
             if (typeof refObj === 'string') return refObj ? "[ " + refObj + " Rujukan]" : "";
              
              
             if (!refObj.url) return ""; // URL Wajib
             var text = "";
            var title = refObj.title || "Tautan Rujukan";
              
              
             var title = refObj.title || "Tautan Rujukan";
             if (refObj.url) {
             var text = "[" + refObj.url + " " + title + "]";
                text = "[" + refObj.url + " " + title + "]";
             } else if (refObj.title) {
                text = "'''" + refObj.title + "'''";
            } else {
                text = "Sumber Rujukan";
            }
              
              
             var details = [];
             var details = [];
Baris 151: Baris 197:
             if (refObj.site) details.push("Situs: " + refObj.site);
             if (refObj.site) details.push("Situs: " + refObj.site);
             if (refObj.publisher) details.push("Penerbit: " + refObj.publisher);
             if (refObj.publisher) details.push("Penerbit: " + refObj.publisher);
             if (refObj.date) details.push("Diakses pada: " + refObj.date);
             if (refObj.date) details.push("Diakses: " + refObj.date);
             if (refObj.type) details.push("Format: " + refObj.type);
             if (refObj.type) details.push("Format: " + refObj.type);
              
              
Baris 160: Baris 206:
         }
         }


         // 5. PENYIMPANAN KE WIKITEXT API
         // 6. PENYIMPANAN KE WIKITEXT API
         function saveData(actionType, $btnEl, originalContent) {
         function saveData(actionType, $btnEl, originalContent) {
             if ($btnEl) $btnEl.addClass('is-loading').prop('disabled', true);
             if ($btnEl) $btnEl.addClass('is-loading').prop('disabled', true);
Baris 185: Baris 231:
             // SIMPAN DATA
             // SIMPAN DATA
             api.postWithToken('csrf', {
             api.postWithToken('csrf', {
                 action: 'edit', title: pageName, text: newContent, summary: 'Mippedia Data: ' + actionType, minor: true
                 action: 'edit', title: pageName, text: newContent, summary: 'Mippedia Data: ' + actionType, minor: false // minor=false trigger cache lebih baik
             }).done(function() {
             }).done(function() {
                 // FIX CACHE: Eksekusi Purge dulu biar cache anonim dibersihkan, baru reload.
                 // BYPASS CACHE CERDAS: Gunakan parameter waktu untuk maksa server merefresh file html tanpa halaman konfirmasi "Purge"
                 api.post({ action: 'purge', titles: pageName }).always(function() {
                 var nocacheUrl = location.pathname + "?update=" + new Date().getTime();
                    window.location.href = mw.util.getUrl(pageName) + '?action=purge'; // Bypass Reload
                window.location.href = nocacheUrl;
                });
             }).fail(function(err) {
             }).fail(function(err) {
                 if ($btnEl) $btnEl.removeClass('is-loading').prop('disabled', false);
                 if ($btnEl) $btnEl.removeClass('is-loading').prop('disabled', false);
                 alert("Gagal menyimpan! (Error: " + err + "). Pastikan Anda memiliki izin mengedit halaman ini.");
                 alert("Gagal menyimpan! (Error: " + err + ").");
             });
             });
         }
         }


         // 6. BUILDER UI UTAMA
         // 7. BUILDER UI UTAMA
         function buildUI(rawContent) {
         function buildUI(rawContent) {
             var $app = $('<div id="mippedia-data-app"></div>');
             var $app = $('<div id="mippedia-data-app"></div>');
             var $header = $('<div class="md-header"><h3 class="md-title">' + svgEdit + ' Mippedia Data <span class="md-small-text">Pro</span></h3></div>');
             var $header = $('<div class="md-header"><h3 class="md-title">' + svgEdit + ' Mippedia Data</h3></div>');
             var $actions = $('<div class="md-actions"></div>');
             var $actions = $('<div class="md-actions"></div>');
              
              
Baris 208: Baris 253:
                 var lockClass = isLocked ? 'md-btn-unlock' : 'md-btn-lock';
                 var lockClass = isLocked ? 'md-btn-unlock' : 'md-btn-lock';
                  
                  
                // Spinner sudah disematkan di dalam tombol kunci
                 var $lockBtn = $('<button class="md-btn-icon ' + lockClass + '">' + lockIcon + lockText + ' <span class="md-spinner"></span></button>');
                 var $lockBtn = $('<button class="md-btn-icon ' + lockClass + '" title="Akses Pengurus">' + lockIcon + lockText + ' <span class="md-spinner"></span></button>');
                 $lockBtn.on('click', function() {
                 $lockBtn.on('click', function() {
                     isLocked = !isLocked;
                     isLocked = !isLocked;
Baris 216: Baris 260:
                 $actions.append($lockBtn);
                 $actions.append($lockBtn);
             } else if (isLocked) {
             } else if (isLocked) {
                 $actions.append('<span title="Dikunci oleh Pengurus" style="color:#d32f2f; font-size:0.9em; font-weight:bold; background:#fff5f5; padding:6px 12px; border-radius:6px; border:1px solid #fbd6d6;">' + svgLock + ' Terkunci</span>');
                 var $lockedBadge = $('<span class="md-badge-locked">' + svgLock + ' Terkunci</span>');
                $lockedBadge.on('click', function() {
                    showModal('Halaman Terkunci', 'Data terstruktur pada halaman ini telah dikunci oleh Pengurus untuk menjaga keakuratan dan integritas informasi. Anda tidak dapat melakukan perubahan saat ini.');
                });
                $actions.append($lockedBadge);
             }
             }


Baris 237: Baris 285:
                     $info.append('<span class="md-data-label">' + item.prop + '</span>');
                     $info.append('<span class="md-data-label">' + item.prop + '</span>');
                      
                      
                    var $valArea = $('<div style="display:flex; align-items:center; flex-wrap:wrap; gap:8px;"></div>');
                     var displayVal = item.link ? '<a href="' + mw.util.getUrl(item.val) + '" style="color:#1a73e8; text-decoration:none; font-weight:600;">' + item.val + '</a>' : item.val;
                     var displayVal = item.link ? '<a href="' + mw.util.getUrl(item.val) + '" style="color:#1a73e8; text-decoration:none; font-weight:600;">' + item.val + '</a>' : item.val;
                     $valArea.append('<span class="md-data-value">' + displayVal + '</span>');
                     $info.append('<span class="md-data-value">' + displayVal + '</span>');
                      
                      
                     // Render UI Referensi
                     // RENDER REFERENSI DI TAMPILAN NYATA (Bukan Tooltip)
                     if (item.ref) {
                     if (item.ref) {
                         var isObj = typeof item.ref === 'object';
                         var isObj = typeof item.ref === 'object';
                         var refUrl = isObj ? item.ref.url : item.ref;
                         var refUrl = isObj ? item.ref.url : item.ref;
                         var refTitle = (isObj && item.ref.title) ? item.ref.title : "Sumber Rujukan";
                         var refTitle = (isObj && item.ref.title) ? item.ref.title : "Tautan Rujukan";
                         var tooltip = [];
                       
                         var $refBox = $('<div class="md-ref-container"></div>');
                       
                        // Baris 1: URL atau Judul
                        if (refUrl) {
                            $refBox.append('<a href="' + refUrl + '" target="_blank" class="md-ref-link">' + svgRef + refTitle + '</a>');
                        } else if (isObj && item.ref.title) {
                            $refBox.append('<span style="font-weight:600; font-size:0.85em; color:#444;">' + svgRef + item.ref.title + '</span>');
                        }
 
                        // Baris 2: Detail Metadata (Penulis, Situs, Waktu)
                         if (isObj) {
                         if (isObj) {
                             if(item.ref.author) tooltip.push("Penulis: " + item.ref.author);
                            var meta = [];
                             if(item.ref.publisher) tooltip.push("Penerbit: " + item.ref.publisher);
                             if (item.ref.author) meta.push(item.ref.author);
                             if(item.ref.date) tooltip.push("Diakses: " + item.ref.date);
                            if (item.ref.site) meta.push("<i>" + item.ref.site + "</i>");
                             if (item.ref.publisher) meta.push(item.ref.publisher);
                             if (item.ref.date) meta.push(item.ref.date);
                            if (item.ref.type) meta.push("[" + item.ref.type + "]");
                           
                            if (meta.length > 0) {
                                $refBox.append('<div class="md-ref-meta">' + meta.join(" &bull; ") + '</div>');
                            }
                         }
                         }
                        var titleAttr = tooltip.length > 0 ? ' title="' + tooltip.join(" | ") + '"' : '';
                          
                          
                         if(refUrl) {
                         $info.append($refBox);
                            $valArea.append('<a href="' + refUrl + '" target="_blank" class="md-ref-badge" ' + titleAttr + '>' + svgRef + refTitle + '</a>');
                        }
                     }
                     }
                      
                      
                    $info.append($valArea);
                     $row.append($info);
                     $row.append($info);


                     if (!isLocked || isSysop) {
                     if (!isLocked || isSysop) {
                         var $editIcon = $('<button class="md-btn-icon" style="padding:5px 10px; font-size:0.8em; align-self:center;">' + svgEdit + ' Edit</button>');
                         var $editIcon = $('<button class="md-btn-icon edit-btn">' + svgEdit + ' Edit</button>');
                         $editIcon.on('click', function() { showForm(index, rawContent); });
                         $editIcon.on('click', function() { showForm(index, rawContent); });
                         $row.append($editIcon);
                         $row.append($editIcon);
Baris 287: Baris 347:
         }
         }


         // 8. LOGIKA FORM EDIT (DENGAN ADVANCED REF GRID)
         // 8. LOGIKA FORM EDIT (RESPONSIVE GRID & BUG FIX)
         function showForm(index, rawContent) {
         function showForm(index, rawContent) {
             var $container = $('#md-form-container');
             var $container = $('#md-form-container');
Baris 295: Baris 355:
             var currentData = isNew ? { prop: '', val: '', link: false, ref: {} } : dataItems[index];
             var currentData = isNew ? { prop: '', val: '', link: false, ref: {} } : dataItems[index];
              
              
            // Konversi format ref lama (string) ke objek biar nggak error
             var currentRef = currentData.ref || {};
             var currentRef = currentData.ref || {};
             if (typeof currentRef === 'string') currentRef = { url: currentRef };
             if (typeof currentRef === 'string') currentRef = { url: currentRef };


             var $form = $('<div class="md-form-box"></div>');
             var $form = $('<div class="md-form-box"></div>');
           
             $form.append('<h4 class="md-form-title">' + (isNew ? 'Tambah Data Baru' : 'Edit Data') + '</h4>');
             $form.append('<h4 class="md-form-title">' + (isNew ? 'Tambah Data Baru' : 'Edit Data') + '</h4>');
              
              
            // Input Utama
             $form.append('<div class="md-form-group"><label>Label Data (Contoh: Pekerjaan, Sutradara)</label><input type="text" id="md-inp-prop" class="md-input" value="' + (currentData.prop || '') + '"></div>');
             $form.append('<div class="md-form-group"><label>Label Data (Contoh: Pekerjaan, Sutradara)</label><input type="text" id="md-inp-prop" class="md-input" value="' + (currentData.prop || '') + '"></div>');
             $form.append('<div class="md-form-group"><label>Nilai Data</label><input type="text" id="md-inp-val" class="md-input" value="' + (currentData.val || '') + '"></div>');
             $form.append('<div class="md-form-group"><label>Nilai Data</label><input type="text" id="md-inp-val" class="md-input" value="' + (currentData.val || '') + '"></div>');
              
              
             var chk = currentData.link ? 'checked' : '';
             var chk = currentData.link ? 'checked' : '';
             $form.append('<label class="md-checkbox-group"><input type="checkbox" id="md-inp-link" ' + chk + '> Jadikan Tautan Internal (Wikilink)</label>');
             $form.append('<label style="display:flex; align-items:center; gap:8px; font-size:0.9em; cursor:pointer;"><input type="checkbox" id="md-inp-link" ' + chk + '> Jadikan Tautan Internal (Wikilink)</label>');


             // Grid Detail Referensi (Opsional)
             // Grid Detail Referensi
             var $refArea = $('<div class="md-form-group" style="margin-top:25px;"></div>');
             var $refArea = $('<div class="md-form-group" style="margin-top:25px;"></div>');
             $refArea.append('<label>Sumber Referensi <span class="md-small-text">(Semua kolom di bawah opsional)</span></label>');
             $refArea.append('<label>Sumber Referensi <span style="color:#888; font-weight:normal; font-size:0.9em;">(Opsional, isi yang diketahui saja)</span></label>');
              
              
             var $refGrid = $('<div class="md-ref-grid"></div>');
             var $refGrid = $('<div class="md-ref-grid"></div>');
              
              
            // Full Width (URL)
             $refGrid.append('<div class="md-grid-full"><input type="url" id="md-ref-url" class="md-input" placeholder="URL Tautan (https://...)" value="' + (currentRef.url || '') + '"></div>');
             $refGrid.append('<div class="md-grid-full"><input type="url" id="md-ref-url" class="md-input" placeholder="URL Tautan (https://...)" value="' + (currentRef.url || '') + '"></div>');
           
             $refGrid.append('<div><input type="text" id="md-ref-title" class="md-input" placeholder="Judul Halaman Web/Buku" value="' + (currentRef.title || '') + '"></div>');
            // 2 Kolom
             $refGrid.append('<div><input type="text" id="md-ref-site" class="md-input" placeholder="Nama Situs (cth: Kompas)" value="' + (currentRef.site || '') + '"></div>');
             $refGrid.append('<div><input type="text" id="md-ref-title" class="md-input" placeholder="Judul Halaman Web" value="' + (currentRef.title || '') + '"></div>');
             $refGrid.append('<div><input type="text" id="md-ref-site" class="md-input" placeholder="Nama Situs (cth: Kompas, BBC)" value="' + (currentRef.site || '') + '"></div>');
             $refGrid.append('<div><input type="text" id="md-ref-publisher" class="md-input" placeholder="Penerbit" value="' + (currentRef.publisher || '') + '"></div>');
             $refGrid.append('<div><input type="text" id="md-ref-publisher" class="md-input" placeholder="Penerbit" value="' + (currentRef.publisher || '') + '"></div>');
             $refGrid.append('<div><input type="text" id="md-ref-author" class="md-input" placeholder="Nama Penulis" value="' + (currentRef.author || '') + '"></div>');
             $refGrid.append('<div><input type="text" id="md-ref-author" class="md-input" placeholder="Nama Penulis" value="' + (currentRef.author || '') + '"></div>');
Baris 334: Baris 388:
             $form.append($refArea);
             $form.append($refArea);


            // Tombol Aksi
             var $actions = $('<div class="md-form-actions"></div>');
             var $actions = $('<div class="md-form-actions"></div>');
             var $btnSave = $('<button class="md-btn md-btn-save">Simpan Data <span class="md-spinner"></span></button>');
             var $btnSave = $('<button class="md-btn md-btn-save">Simpan Data <span class="md-spinner"></span></button>');
Baris 355: Baris 408:
             $form.append($actions);
             $form.append($actions);
             $container.append($form);
             $container.append($form);
 
             $('html, body').animate({ scrollTop: $form.offset().top - 80 }, 300);
            // Scroll ke form dengan mulus
             $('html, body').animate({ scrollTop: $form.offset().top - 100 }, 300);
 
             $btnCancel.on('click', function() { $container.empty(); });
             $btnCancel.on('click', function() { $container.empty(); });


Baris 367: Baris 417:


                 if (!newProp || !newVal) {
                 if (!newProp || !newVal) {
                     alert("Label Data dan Nilai harus diisi, bro!"); return;
                     alert("Label Data dan Nilai wajib diisi!"); return;
                 }
                 }


                // Ambil Data Referensi
                 var refObj = {
                 var refObj = {
                     url: $('#md-ref-url').val().trim(),
                     url: $('#md-ref-url').val().trim(),
Baris 381: Baris 430:
                 };
                 };


                 // Bersihkan properti referensi yang kosong agar JSON tidak bengkak
                 // BUG FIX: Hapus key yang kosong. Kalo semua kosong, baru jadikan null.
                 Object.keys(refObj).forEach(key => { if (!refObj[key]) delete refObj[key]; });
                 Object.keys(refObj).forEach(key => { if (!refObj[key]) delete refObj[key]; });
               
                 if (Object.keys(refObj).length === 0) {
                // Kalau URL kosong, jadikan ref null
                    refObj = null;
                 if (!refObj.url) refObj = null;
                }


                 var newItem = { prop: newProp, val: newVal, link: newLink, ref: refObj };
                 var newItem = { prop: newProp, val: newVal, link: newLink, ref: refObj };
               
                 if (isNew) dataItems.push(newItem);  
                 if (isNew) dataItems.push(newItem);  
                 else dataItems[index] = newItem;  
                 else dataItems[index] = newItem;  
Baris 396: Baris 444:
         }
         }


         // 9. JALANKAN PROGRAM
         // 9. EKSEKUSI UTAMA
         loadData().then(function(rawContent) { buildUI(rawContent); });
         loadData().then(function(rawContent) { buildUI(rawContent); });
     });
     });