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 VERSION)
  * MIPPEDIA DATA - ADVANCED STRUCTURED DATA SYSTEM (PRO-MAX VERSION)
  * Terintegrasi murni menggunakan Wikitext & JavaScript
  * Fitur: Advanced Citation, Bypass Cache Fix, UI Grid, Async Loading
  * ====================================================================
  * ====================================================================
  */
  */
Baris 25: Baris 25:
         var api = new mw.Api();
         var api = new mw.Api();


         // Ikon SVG Profesional (Menggantikan Emoji)
         // Ikon SVG Profesional
         var svgEdit = '<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"><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 svgEdit = '<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"><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="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="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 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>';


         // 3. INJEKSI CSS TAMPILAN
         // 3. INJEKSI CSS TAMPILAN
Baris 36: Baris 37:
                 background: #ffffff;
                 background: #ffffff;
                 border-radius: 12px;
                 border-radius: 12px;
                 box-shadow: 0 4px 12px rgba(0,0,0,0.05);
                 box-shadow: 0 8px 24px rgba(0,0,0,0.06);
                 padding: 20px;
                 padding: 24px;
                 margin: 20px 0;
                 margin: 24px 0;
                 font-family: inherit;
                 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
                 border: 1px solid #eaeaea;
                 border: 1px solid #eef0f2;
                transition: all 0.3s ease;
             }
             }
             .md-header {
             .md-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; padding-bottom: 15px; border-bottom: 1px solid #f0f0f0; }
                display: flex;
             .md-title { font-size: 1.3em; font-weight: 700; color: #1a1a1a; margin: 0; display: flex; align-items: center; gap: 8px;}
                justify-content: space-between;
             .md-actions { display: flex; gap: 10px; align-items: center; }
                align-items: center;
                margin-bottom: 15px;
            }
             .md-title {
                font-size: 1.2em;
                font-weight: bold;
                color: #333;
                margin: 0;
            }
             .md-actions { display: flex; gap: 8px; align-items: center; }
             .md-icon { vertical-align: middle; }
             .md-icon { vertical-align: middle; }
              
              
             .md-btn-icon {
             .md-btn-icon {
                 background: #f8f9fa;
                 background: #f8f9fa; border: 1px solid #e2e2e2; cursor: pointer; color: #5a42b8;
                border: 1px solid #e2e2e2;
                 padding: 7px 12px; border-radius: 8px; transition: all 0.2s ease;
                cursor: pointer;
                 display: inline-flex; align-items: center; gap: 6px; font-size: 0.85em; font-weight: 600;
                color: #6b52d1;
                 padding: 6px 10px;
                border-radius: 6px;
                transition: all 0.2s;
                 display: inline-flex;
                align-items: center;
                gap: 5px;
                font-size: 0.85em;
                font-weight: bold;
             }
             }
             .md-btn-icon:hover { background: #eef2f5; border-color: #d0d0d0; }
             .md-btn-icon:hover:not(:disabled) { background: #eef2f5; border-color: #d0d0d0; transform: translateY(-1px); }
             .md-btn-lock { color: #dc3545; }
             .md-btn-lock { color: #d32f2f; background: #fff5f5; border-color: #fbd6d6; }
             .md-btn-unlock { color: #28a745; }
            .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; }
              
              
             .md-row-display {
             .md-row-display {
                 display: flex;
                 display: flex; justify-content: space-between; align-items: flex-start;
                justify-content: space-between;
                 padding: 14px 10px; border-bottom: 1px solid #f5f5f5; transition: background 0.2s; border-radius: 6px;
                align-items: center;
                 padding: 12px 0;
                border-bottom: 1px solid #f0f0f0;
             }
             }
            .md-row-display:hover { background: #fafafa; }
             .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: 4px; }
            .md-data-label { font-size: 0.85em; color: #666; font-weight: bold; text-transform: uppercase; letter-spacing: 0.5px; }
            .md-data-value { font-size: 1em; color: #222; }
              
              
             .md-data-ref {
             .md-data-group { display: flex; flex-direction: column; gap: 6px; flex: 1; }
                 font-size: 0.85em;
            .md-data-label { font-size: 0.8em; color: #757575; font-weight: 700; text-transform: uppercase; letter-spacing: 0.8px; }
                background: #eef2f5;
            .md-data-value { font-size: 1.05em; color: #202124; line-height: 1.4; }
                padding: 4px 8px;
           
                border-radius: 4px;
            .md-ref-badge {
                 text-decoration: none !important;
                 font-size: 0.75em; background: #f1f3f4; padding: 4px 10px; border-radius: 20px;
                color: #0056b3;
                 text-decoration: none !important; color: #1a73e8; margin-left: 10px;
                margin-left: 8px;
                 display: inline-flex; align-items: center; gap: 4px; border: 1px solid #e8eaed; transition: all 0.2s;
                 display: inline-flex;
                align-items: center;
                gap: 4px;
                transition: background 0.2s;
             }
             }
             .md-data-ref:hover { background: #dfe7ed; color: #004494; }
             .md-ref-badge:hover { background: #e8f0fe; color: #174ea6; border-color: #d2e3fc; }
              
              
             /* Form Edit */
             /* Form Edit (Grid Layout) */
             .md-form-box {
             .md-form-box { background: #fafbfc; border-radius: 10px; padding: 20px; margin-top: 15px; border: 1px solid #eaedf0; }
                background: #fdfdfd;
            .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;}
                border-radius: 8px;
            .md-form-group { margin-bottom: 15px; }
                padding: 16px;
            .md-form-group label { display: block; font-size: 0.85em; font-weight: 600; margin-bottom: 6px; color: #4a4a4a; }
                margin-top: 15px;
            .md-input, .md-select {
                 border: 1px solid #e0e0e0;
                 width: 100%; padding: 10px 14px; border: 1px solid #cfd4d9; border-radius: 8px;
                 box-shadow: inset 0 2px 4px rgba(0,0,0,0.02);
                 box-sizing: border-box; font-family: inherit; font-size: 0.95em; transition: all 0.2s; background: #fff;
             }
             }
             .md-form-group { margin-bottom: 14px; }
             .md-input:focus, .md-select:focus { border-color: #5a42b8; outline: none; box-shadow: 0 0 0 3px rgba(90, 66, 184, 0.1); }
             .md-form-group label { display: block; font-size: 0.85em; font-weight: bold; margin-bottom: 6px; color: #444; }
           
            .md-input {
            /* Grid Referensi 2 Kolom */
                width: 100%;
             .md-ref-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; background: #fff; padding: 15px; border-radius: 8px; border: 1px dashed #ccc; }
                padding: 10px 12px;
             .md-grid-full { grid-column: span 2; }
                border: 1px solid #ccc;
             .md-checkbox-group { display: flex; align-items: center; gap: 8px; font-size: 0.9em; color: #444; margin-top: 10px; cursor: pointer; }
                border-radius: 6px;
                box-sizing: border-box;
                font-family: inherit;
                font-size: 0.95em;
                transition: border-color 0.2s;
            }
             .md-input:focus { border-color: #6b52d1; outline: none; }
             .md-checkbox-group { display: flex; align-items: center; gap: 8px; font-size: 0.9em; color: #444; margin-top: 5px; }
              
              
             /* Tombol Aksi & Spinner */
             /* Tombol & Spinner */
             .md-form-actions { display: flex; gap: 10px; align-items: center; margin-top: 18px; }
             .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: 9px 18px;
                 padding: 10px 20px; border: none; border-radius: 8px; font-weight: 600; cursor: pointer;
                border: none;
                 color: white; font-size: 0.9em; transition: all 0.2s; display: inline-flex; align-items: center; gap: 8px;
                border-radius: 6px;
                font-weight: bold;
                cursor: pointer;
                 color: white;
                font-size: 0.9em;
                transition: all 0.2s;
                display: inline-flex;
                align-items: center;
                gap: 8px;
             }
             }
             .md-btn:hover { opacity: 0.85; transform: translateY(-1px); }
             .md-btn:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 4px 10px rgba(0,0,0,0.15); }
             .md-btn:disabled { opacity: 0.6; cursor: not-allowed; transform: none; }
             .md-btn:disabled { opacity: 0.6; cursor: not-allowed; }
             .md-btn-save { background: #6b52d1; }
             .md-btn-save { background: linear-gradient(135deg, #6b52d1, #5a42b8); }
             .md-btn-cancel { background: #6c757d; }
             .md-btn-cancel { background: #6c757d; }
             .md-btn-delete { background: #dc3545; margin-left: auto; }
             .md-btn-delete { background: linear-gradient(135deg, #e53935, #c62828); margin-left: auto; }
              
              
            /* Animasi Spinner Presisi */
             .md-spinner {
             .md-spinner {
                 display: none;
                 display: none; width: 14px; height: 14px; border: 2px solid rgba(255, 255, 255, 0.4);
                width: 14px;
                 border-radius: 50%; border-top-color: #ffffff; animation: md-spin 0.8s linear infinite;
                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; }
             .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); } }
              
              
            /* Sembunyikan render bawaan agar terganti UI interaktif */
             .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 DARI WIKITEXT
         // 4. PARSER DATA
         function loadData() {
         function loadData() {
             return api.get({
             return api.get({
                 action: 'query',
                 action: 'query', prop: 'revisions', titles: pageName, rvprop: 'content', rvslots: 'main', formatversion: 2
                prop: 'revisions',
                titles: pageName,
                rvprop: 'content',
                rvslots: 'main',
                formatversion: 2
             }).then(function(data) {
             }).then(function(data) {
                 var content = data.query.pages[0].revisions[0].slots.main.content;
                 var content = data.query.pages[0].revisions[0].slots.main.content;
Baris 186: Baris 135:
                 return content;  
                 return content;  
             });
             });
        }
        // --- HELPER UNTUK MEMBENTUK TEKS REFERENSI ---
        function buildWikitextRef(refObj) {
            if (!refObj) return "";
            if (typeof refObj === 'string') return refObj ? "[ " + refObj + " Rujukan]" : "";
           
            if (!refObj.url) return ""; // URL Wajib
           
            var title = refObj.title || "Tautan Rujukan";
            var text = "[" + refObj.url + " " + 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("Diakses pada: " + refObj.date);
            if (refObj.type) details.push("Format: " + refObj.type);
           
            if (details.length > 0) {
                text += " &mdash; <small>" + details.join(" &bull; ") + "</small>";
            }
            return text;
         }
         }


         // 5. PENYIMPANAN KE WIKITEXT API
         // 5. PENYIMPANAN KE WIKITEXT API
         function saveData(actionType, $btnEl, originalContent) {
         function saveData(actionType, $btnEl, originalContent) {
            // Aktifkan Status Loading di Tombol
             if ($btnEl) $btnEl.addClass('is-loading').prop('disabled', true);
             if ($btnEl) {
                $btnEl.addClass('is-loading').prop('disabled', true);
            }
              
              
             var jsonStr = JSON.stringify({ locked: isLocked, items: dataItems });
             var jsonStr = JSON.stringify({ locked: isLocked, items: dataItems });
             var wt = "<!-- MIPPEDIA_DATA_START | " + jsonStr + " -->\n";
             var wt = "<!-- MIPPEDIA_DATA_START | " + jsonStr + " -->\n";
             wt += "<div class=\"md-native-render\">\n{| class=\"wikitable\"\n! Data !! Nilai !! Referensi\n";
             wt += "<div class=\"md-native-render\">\n{| class=\"wikitable\"\n! Data !! Nilai !! Referensi\n";
           
             dataItems.forEach(function(item) {
             dataItems.forEach(function(item) {
                 var valTxt = item.link ? "[[" + item.val + "]]" : item.val;
                 var valTxt = item.link ? "[[" + item.val + "]]" : item.val;
                 var refTxt = item.ref ? "[ " + item.ref + " Rujukan]" : "";
                 var refTxt = buildWikitextRef(item.ref);
                 wt += "|-\n| " + item.prop + " || " + valTxt + " || " + refTxt + "\n";
                 wt += "|-\n| " + item.prop + " || " + valTxt + " || " + refTxt + "\n";
             });
             });
Baris 213: Baris 183:
             }
             }


            // SIMPAN DATA
             api.postWithToken('csrf', {
             api.postWithToken('csrf', {
                 action: 'edit',
                 action: 'edit', title: pageName, text: newContent, summary: 'Mippedia Data: ' + actionType, minor: true
                title: pageName,
                text: newContent,
                summary: 'Mippedia Data: ' + actionType,
                minor: true
             }).done(function() {
             }).done(function() {
                 location.reload();
                 // FIX CACHE: Eksekusi Purge dulu biar cache anonim dibersihkan, baru reload.
                api.post({ action: 'purge', titles: pageName }).always(function() {
                    window.location.href = mw.util.getUrl(pageName) + '?action=purge'; // Bypass Reload
                });
             }).fail(function(err) {
             }).fail(function(err) {
                // Matikan Status Loading jika Gagal
                 if ($btnEl) $btnEl.removeClass('is-loading').prop('disabled', false);
                 if ($btnEl) {
                 alert("Gagal menyimpan! (Error: " + err + "). Pastikan Anda memiliki izin mengedit halaman ini.");
                    $btnEl.removeClass('is-loading').prop('disabled', false);
                }
                 alert("Gagal memproses data. Kesalahan: " + err);
             });
             });
         }
         }
Baris 233: Baris 200:
         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">Mippedia Data</h3></div>');
             var $actions = $('<div class="md-actions"></div>');
             var $actions = $('<div class="md-actions"></div>');
              
              
Baris 242: Baris 208:
                 var lockClass = isLocked ? 'md-btn-unlock' : 'md-btn-lock';
                 var lockClass = isLocked ? 'md-btn-unlock' : 'md-btn-lock';
                  
                  
                 var $lockBtn = $('<button class="md-btn-icon ' + lockClass + '" title="Akses Pengurus">' + lockIcon + lockText + '</button>');
                // Spinner sudah disematkan di dalam tombol kunci
                 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 249: Baris 216:
                 $actions.append($lockBtn);
                 $actions.append($lockBtn);
             } else if (isLocked) {
             } else if (isLocked) {
                 $actions.append('<span title="Dikunci oleh Pengurus" style="color:#dc3545; font-size:0.9em; font-weight:bold;">' + svgLock + ' Terkunci</span>');
                 $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>');
             }
             }


Baris 263: Baris 230:
             var $list = $('<div class="md-list"></div>');
             var $list = $('<div class="md-list"></div>');
             if (dataItems.length === 0) {
             if (dataItems.length === 0) {
                 $list.append('<div style="color:#888; font-size:0.9em; padding: 10px 0;">Belum ada data struktur untuk halaman ini.</div>');
                 $list.append('<div style="color:#888; text-align:center; padding: 30px 0; background:#f9f9f9; border-radius:8px; border:1px dashed #ddd;">Belum ada data struktur. Klik Tambah untuk memulai.</div>');
             } else {
             } else {
                 dataItems.forEach(function(item, index) {
                 dataItems.forEach(function(item, index) {
                     var $row = $('<div class="md-row-display"></div>');
                     var $row = $('<div class="md-row-display"></div>');
                   
                     var $info = $('<div class="md-data-group"></div>');
                     var $info = $('<div class="md-data-group"></div>');
                     $info.append('<span class="md-data-label">' + item.prop + '</span>');
                     $info.append('<span class="md-data-label">' + item.prop + '</span>');
                      
                      
                     var $valArea = $('<div></div>');
                     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:#0645ad;">' + 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>');
                     $valArea.append('<span class="md-data-value">' + displayVal + '</span>');
                      
                      
                    // Render UI Referensi
                     if (item.ref) {
                     if (item.ref) {
                         $valArea.append('<a href="' + item.ref + '" target="_blank" class="md-data-ref">' + svgRef + 'Referensi</a>');
                         var isObj = typeof item.ref === 'object';
                        var refUrl = isObj ? item.ref.url : item.ref;
                        var refTitle = (isObj && item.ref.title) ? item.ref.title : "Sumber Rujukan";
                        var tooltip = [];
                        if (isObj) {
                            if(item.ref.author) tooltip.push("Penulis: " + item.ref.author);
                            if(item.ref.publisher) tooltip.push("Penerbit: " + item.ref.publisher);
                            if(item.ref.date) tooltip.push("Diakses: " + item.ref.date);
                        }
                        var titleAttr = tooltip.length > 0 ? ' title="' + tooltip.join(" | ") + '"' : '';
                       
                        if(refUrl) {
                            $valArea.append('<a href="' + refUrl + '" target="_blank" class="md-ref-badge" ' + titleAttr + '>' + svgRef + refTitle + '</a>');
                        }
                     }
                     }
                   
                     $info.append($valArea);
                     $info.append($valArea);
                     $row.append($info);
                     $row.append($info);


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


         // 8. LOGIKA FORM EDIT & TAMBAH DATA
         // 8. LOGIKA FORM EDIT (DENGAN ADVANCED REF GRID)
         function showForm(index, rawContent) {
         function showForm(index, rawContent) {
             var $container = $('#md-form-container');
             var $container = $('#md-form-container');
Baris 312: Baris 293:


             var isNew = index === -1;
             var isNew = index === -1;
             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 || {};
            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('<div class="md-form-group"><label>Data</label><input type="text" id="md-inp-prop" class="md-input" value="' + currentData.prop + '" placeholder="Contoh: Profesi, Negara, Sutradara..."></div>');
            $form.append('<h4 class="md-form-title">' + (isNew ? 'Tambah Data Baru' : 'Edit Data') + '</h4>');
             $form.append('<div class="md-form-group"><label>Nilai</label><input type="text" id="md-inp-val" class="md-input" value="' + currentData.val + '" placeholder="Masukkan nilai data..."></div>');
           
            // 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>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('<div class="md-form-group md-checkbox-group"><input type="checkbox" id="md-inp-link" ' + chk + '> <label for="md-inp-link" style="margin:0; cursor:pointer;">Jadikan Tautan Wikilink [[ ]]</label></div>');
             $form.append('<label class="md-checkbox-group"><input type="checkbox" id="md-inp-link" ' + chk + '> Jadikan Tautan Internal (Wikilink)</label>');
 
            // Grid Detail Referensi (Opsional)
            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>');
           
            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>');
           
            // 2 Kolom
            $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-author" class="md-input" placeholder="Nama Penulis" value="' + (currentRef.author || '') + '"></div>');
            $refGrid.append('<div><input type="date" id="md-ref-date" class="md-input" value="' + (currentRef.date || '') + '" title="Tanggal Diakses"></div>');
           
            var selWeb = currentRef.type === 'Berita/Web' ? 'selected' : '';
            var selPdf = currentRef.type === 'Dokumen PDF' ? 'selected' : '';
            var selVid = currentRef.type === 'Video' ? 'selected' : '';
            $refGrid.append('<div><select id="md-ref-type" class="md-select"><option value="">-- Format --</option><option value="Berita/Web" '+selWeb+'>Berita / Web</option><option value="Dokumen PDF" '+selPdf+'>Dokumen PDF</option><option value="Video" '+selVid+'>Video</option></select></div>');


             $form.append('<div class="md-form-group"><label>URL Referensi (Opsional)</label><input type="url" id="md-inp-ref" class="md-input" value="' + currentData.ref + '" placeholder="https://..."></div>');
             $refArea.append($refGrid);
            $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>');
            // Tombol dengan Element Spinner di dalamnya
             var $btnSave = $('<button class="md-btn md-btn-save">Terbitkan <span class="md-spinner"></span></button>');
             var $btnCancel = $('<button class="md-btn md-btn-cancel">Batal</button>');
             var $btnCancel = $('<button class="md-btn md-btn-cancel">Batal</button>');
              
              
Baris 334: Baris 343:
              
              
             if (!isNew) {
             if (!isNew) {
                 var $btnDelete = $('<button class="md-btn md-btn-delete">Hapus Data <span class="md-spinner"></span></button>');
                 var $btnDelete = $('<button class="md-btn md-btn-delete">Hapus <span class="md-spinner"></span></button>');
                 $btnDelete.on('click', function() {
                 $btnDelete.on('click', function() {
                     if(confirm('Yakin mau hapus baris data ini secara permanen?')) {
                     if(confirm('Yakin mau hapus baris data ini secara permanen?')) {
Baris 346: Baris 355:
             $form.append($actions);
             $form.append($actions);
             $container.append($form);
             $container.append($form);
            // 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 353: Baris 365:
                 var newVal = $('#md-inp-val').val().trim();
                 var newVal = $('#md-inp-val').val().trim();
                 var newLink = $('#md-inp-link').is(':checked');
                 var newLink = $('#md-inp-link').is(':checked');
                var newRef = $('#md-inp-ref').val().trim();


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


                 var newItem = { prop: newProp, val: newVal, link: newLink, ref: newRef };
                // Ambil Data Referensi
                var refObj = {
                    url: $('#md-ref-url').val().trim(),
                    title: $('#md-ref-title').val().trim(),
                    site: $('#md-ref-site').val().trim(),
                    publisher: $('#md-ref-publisher').val().trim(),
                    author: $('#md-ref-author').val().trim(),
                    date: $('#md-ref-date').val(),
                    type: $('#md-ref-type').val()
                };
 
                // Bersihkan properti referensi yang kosong agar JSON tidak bengkak
                Object.keys(refObj).forEach(key => { if (!refObj[key]) delete refObj[key]; });
               
                // Kalau URL kosong, jadikan ref null
                if (!refObj.url) refObj = null;
 
                 var newItem = { prop: newProp, val: newVal, link: newLink, ref: refObj };
                  
                  
                 if (isNew) {
                 if (isNew) dataItems.push(newItem);  
                    dataItems.push(newItem);  
                 else dataItems[index] = newItem;  
                 } else {
                    dataItems[index] = newItem;  
                }


                 saveData(isNew ? 'Menambahkan data baru' : 'Mengubah data', $(this), rawContent);
                 saveData(isNew ? 'Menambahkan data baru' : 'Mengubah data', $(this), rawContent);
Baris 372: Baris 396:
         }
         }


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