MediaWiki:Common.js: Perbedaan antara revisi

Tidak ada ringkasan suntingan
Tanda: Dikembalikan Suntingan perangkat seluler Suntingan peramban seluler
Tidak ada ringkasan suntingan
Tanda: Dikembalikan Suntingan perangkat seluler Suntingan peramban seluler
Baris 1: Baris 1:
/**
/**
  * ====================================================================
  * ====================================================================
  * MIPPEDIA DATA - ADVANCED STRUCTURED DATA SYSTEM (V8.0 ULTIMATE)
  * MIPPEDIA DATA - ADVANCED STRUCTURED DATA SYSTEM (V9.0 PRO MOBILE)
  * Fitur: Clean Socials, 10 Auto Features, Reorder, Live Preview,
  * Fitur: No-Emoji, Mobile Responsive, 15 Auto Features, Clean UI.
* Data Quality Score, Handle Sanitizer, Auto Data-Type, Dark Mode.
  * ====================================================================
  * ====================================================================
  */
  */
Baris 19: Baris 18:
         var isLocked = false;
         var isLocked = false;
         var isDarkMode = false;
         var isDarkMode = false;
        var isReadOnly = false;
         var api = new mw.Api();
         var api = new mw.Api();


         // 1. DATA MASTER PLATFORM MEDIA SOSIAL & MUSIK (TELEGRAM & WIKIPEDIA DIHAPUS)
         // 1. DATA MASTER PLATFORM MEDIA SOSIAL & MUSIK
         var SOCIAL_PLATFORMS = {
         var SOCIAL_PLATFORMS = {
             applemusic: { name: 'Apple Music', base: 'https://music.apple.com/artist/', color: '#fc3c44', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm3.32 12.383c-.328.232-.82.418-1.32.418-1.238 0-1.922-.828-1.922-2.191v-4.14l4.312-.868v1.398l-2.91.586v2.805c0 .734.336 1.148.977 1.148.336 0 .617-.094.863-.258v1.102z"/></svg>' },
             applemusic: { name: 'Apple Music', base: 'https://music.apple.com/artist/', color: '#fc3c44', icon: '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm3.32 12.383c-.328.232-.82.418-1.32.418-1.238 0-1.922-.828-1.922-2.191v-4.14l4.312-.868v1.398l-2.91.586v2.805c0 .734.336 1.148.977 1.148.336 0 .617-.094.863-.258v1.102z"/></svg>' },
Baris 54: Baris 54:
         };
         };


         // 2. IKON SISTEM FAST SVG
         // 2. IKON SISTEM FAST SVG (PURE, NO EMOJIS)
        var svgDatabase = '<svg class="md-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><ellipse cx="12" cy="5" rx="9" ry="3"></ellipse><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"></path><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"></path></svg>';
         var svgEdit = '<svg class="md-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>';
         var svgEdit = '<svg class="md-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>';
         var svgLock = '<svg class="md-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>';
         var svgLock = '<svg class="md-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>';
         var svgUnlock = '<svg class="md-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 9.9-1"></path></svg>';
         var svgUnlock = '<svg class="md-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 9.9-1"></path></svg>';
         var svgRef = '<svg class="md-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>';
         var svgRef = '<svg class="md-icon" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>';
         var svgCopy = '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>';
         var svgCopy = '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>';
        var svgClone = '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path><line x1="15.5" y1="12" x2="15.5" y2="18"></line><line x1="12.5" y1="15" x2="18.5" y2="15"></line></svg>';
         var svgSearch = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>';
         var svgSearch = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>';
         var svgCode = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 18 22 12 16 6"></polyline><polyline points="8 6 2 12 8 18"></polyline></svg>';
         var svgCode = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 18 22 12 16 6"></polyline><polyline points="8 6 2 12 8 18"></polyline></svg>';
         var svgUp = '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="18 15 12 9 6 15"></polyline></svg>';
         var svgCsv = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="8" y1="13" x2="16" y2="13"></line><line x1="8" y1="17" x2="16" y2="17"></line></svg>';
        var svgDown = '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="6 9 12 15 18 9"></polyline></svg>';
         var svgMoon = '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>';
         var svgMoon = '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>';
         var svgWikitext = '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 7V4h16v3M9 20h6M12 4v16"/></svg>';
         var svgWikitext = '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 7V4h16v3M9 20h6M12 4v16"/></svg>';
        var svgView = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle></svg>';


         // 3. HELPER & AUTOMATION UTILITIES
         // 3. HELPER & NEW AUTOMATION UTILITIES
         function formatIndonesianDate(dateStr) {
         function formatIndonesianDate(dateStr) {
             if (!dateStr) return '';
             if (!dateStr) return '';
Baris 88: Baris 90:
                 return urlStr.replace(/^https?:\/\/(www\.)?/, '').split('/')[0];
                 return urlStr.replace(/^https?:\/\/(www\.)?/, '').split('/')[0];
             }
             }
        }
        // FITUR 13: Auto Reference Cleaner (Bersihkan URL Parameter Tracking)
        function cleanReferenceUrl(urlStr) {
            if (!urlStr) return '';
            try {
                if (!urlStr.match(/^https?:\/\//i)) urlStr = 'https://' + urlStr;
                var urlObj = new URL(urlStr);
                var paramsToStrip = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid'];
                paramsToStrip.forEach(p => urlObj.searchParams.delete(p));
                return urlObj.toString();
            } catch(e) { return urlStr; }
         }
         }


Baris 93: Baris 107:
             if (!urlStr) return '';
             if (!urlStr) return '';
             urlStr = urlStr.trim();
             urlStr = urlStr.trim();
            // FITUR 8: Auto Protocol Inserter
             if (urlStr && !urlStr.match(/^https?:\/\//i)) return 'https://' + urlStr;
             if (urlStr && !urlStr.match(/^https?:\/\//i)) return 'https://' + urlStr;
             return urlStr;
             return cleanReferenceUrl(urlStr);
         }
         }


Baris 105: Baris 120:
         }
         }


        // FORMAT: NO EMOJIS. Professional Pure Text Badges
         function detectDataTypeBadge(item) {
         function detectDataTypeBadge(item) {
             var val = item.val || '';
             var val = item.val || '';
             if (val.match(/^\d{4}-\d{2}-\d{2}$/)) return '<span class="md-type-tag" style="background:#dbeafe; color:#1e40af;">📅 Tanggal</span>';
            // FITUR 12: Smart Date Validator RegExp
             if (val.match(/^https?:\/\//i)) return '<span class="md-type-tag" style="background:#fce7f3; color:#9d174d;">🔗 Link External</span>';
             if (val.match(/^\d{4}-\d{2}-\d{2}$/)) return '<span class="md-type-tag md-tag-date">Date</span>';
             if (item.linkTarget) return '<span class="md-type-tag" style="background:#fef3c7; color:#92400e;">🔀 Custom Piped</span>';
             if (val.match(/^https?:\/\//i)) return '<span class="md-type-tag md-tag-link">External Link</span>';
             if (item.link) return '<span class="md-type-tag" style="background:#dcfce7; color:#166534;">🌐 Internal Wikilink</span>';
             if (item.linkTarget) return '<span class="md-type-tag md-tag-piped">Piped Link</span>';
             return '<span class="md-type-tag" style="background:#f1f5f9; color:#475569;">📝 Teks</span>';
             if (item.link) return '<span class="md-type-tag md-tag-wiki">Wikilink</span>';
             return '<span class="md-type-tag md-tag-text">Text</span>';
        }
 
        // FITUR 10: Data Type Demographics
        function calculateDataStats() {
            var stats = { date: 0, link: 0, wiki: 0, text: 0 };
            dataItems.forEach(function(item) {
                var val = item.val || '';
                if (val.match(/^\d{4}-\d{2}-\d{2}$/)) stats.date++;
                else if (val.match(/^https?:\/\//i)) stats.link++;
                else if (item.link || item.linkTarget) stats.wiki++;
                else stats.text++;
            });
            return stats;
         }
         }


Baris 127: Baris 157:
         }
         }


         // 4. STYLESHEET MODERN V8.0 (FAST ANIMATION + DARK MODE)
        // FITUR 4: CSV Export Logic
        function exportToCSV() {
            var headers = ["Label Data", "Nilai Data", "Tipe Link", "Target Piped", "URL Referensi", "Judul Referensi", "Situs Referensi"];
            var rows = [headers];
            dataItems.forEach(function(item) {
                var ref = item.ref || {};
                var linkType = item.linkSplit ? "Split" : (item.link ? "Standard" : "None");
                var row = [
                    '"' + (item.prop || '').replace(/"/g, '""') + '"',
                    '"' + (item.val || '').replace(/"/g, '""') + '"',
                    '"' + linkType + '"',
                    '"' + (item.linkTarget || '').replace(/"/g, '""') + '"',
                    '"' + (ref.url || '').replace(/"/g, '""') + '"',
                    '"' + (ref.title || '').replace(/"/g, '""') + '"',
                    '"' + (ref.site || '').replace(/"/g, '""') + '"'
                ];
                rows.push(row.join(","));
            });
            var csvContent = "data:text/csv;charset=utf-8," + rows.join("\n");
            var encodedUri = encodeURI(csvContent);
            var link = document.createElement("a");
            link.setAttribute("href", encodedUri);
            link.setAttribute("download", "Mippedia_Data_" + pageName + ".csv");
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }
 
         // 4. STYLESHEET V9.0 (MOBILE OPTIMIZED, NO EMOJIS, HIGH-PERFORMANCE ANIMATIONS)
         var css = `
         var css = `
             #mippedia-data-app {
             #mippedia-data-app {
                 background: #ffffff; border-radius: 16px; box-shadow: 0 4px 20px rgba(0,0,0,0.05);
                 background: #ffffff; border-radius: 12px;
                 padding: 20px; margin: 20px 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
                 padding: 24px; margin: 24px 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
                 border: 1px solid #e2e8f0; max-width: 100%; box-sizing: border-box; transition: background 0.2s, border 0.2s;
                 border: 1px solid #e2e8f0; width: 100%; box-sizing: border-box;  
                transition: background 0.3s cubic-bezier(0.4, 0, 0.2, 1), border 0.3s;
                overflow: hidden;
             }
             }
            #mippedia-data-app * { box-sizing: border-box; }
             #mippedia-data-app.md-dark { background: #0f172a; border-color: #334155; color: #f8fafc; }
             #mippedia-data-app.md-dark { background: #0f172a; border-color: #334155; color: #f8fafc; }
             #mippedia-data-app.md-dark .md-title { color: #f8fafc; }
             #mippedia-data-app.md-dark .md-title { color: #f8fafc; }
             #mippedia-data-app.md-dark .md-row-display { background: #1e293b; border-color: #334155; }
             #mippedia-data-app.md-dark .md-row-display { background: #1e293b; border-color: #334155; }
             #mippedia-data-app.md-dark .md-data-value { color: #f1f5f9; }
             #mippedia-data-app.md-dark .md-data-value { color: #f1f5f9; }
             #mippedia-data-app.md-dark .md-ref-card { background: #182234; border-color: #334155; }
             #mippedia-data-app.md-dark .md-ref-card { background: #0f172a; border-color: #334155; }
             #mippedia-data-app.md-dark .md-form-box { background: #1e293b; border-color: #334155; }
             #mippedia-data-app.md-dark .md-form-box { background: #1e293b; border-color: #334155; }
             #mippedia-data-app.md-dark .md-input, #mippedia-data-app.md-dark .md-select, #mippedia-data-app.md-dark .md-textarea { background: #0f172a; border-color: #475569; color: #fff; }
             #mippedia-data-app.md-dark .md-input, #mippedia-data-app.md-dark .md-select, #mippedia-data-app.md-dark .md-textarea { background: #0f172a; border-color: #475569; color: #fff; }
              
              
             .md-header { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 14px; padding-bottom: 12px; border-bottom: 1px solid #f1f5f9; }
             .md-header { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 16px; margin-bottom: 20px; padding-bottom: 16px; border-bottom: 1px solid #f1f5f9; }
             #mippedia-data-app.md-dark .md-header { border-color: #334155; }
             #mippedia-data-app.md-dark .md-header { border-color: #334155; }
             .md-title-container { display: flex; align-items: center; gap: 8px; flex: 1 1 auto; }
             .md-title-container { display: flex; align-items: center; gap: 10px; flex: 1 1 auto; }
             .md-title { font-size: 1.2rem; font-weight: 800; color: #0f172a; margin: 0; letter-spacing: -0.3px; }
             .md-title { font-size: 1.25rem; font-weight: 700; color: #0f172a; margin: 0; letter-spacing: -0.5px; display:flex; align-items:center; gap:8px;}
             .md-actions { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
             .md-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
              
              
             .md-btn-icon {
             .md-btn-icon {
                 background: #f8fafc; border: 1px solid #cbd5e1; cursor: pointer; color: #4f46e5;
                 background: #f8fafc; border: 1px solid #cbd5e1; cursor: pointer; color: #3b82f6;
                 padding: 7px 12px; border-radius: 8px; transition: all 0.15s ease-out;
                 padding: 8px 14px; border-radius: 6px; transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
                 display: inline-flex; align-items: center; gap: 5px; font-size: 0.82rem; font-weight: 600; white-space: nowrap;
                 display: inline-flex; align-items: center; gap: 6px; font-size: 0.85rem; font-weight: 600; white-space: nowrap;
             }
             }
             .md-btn-icon:hover:not(:disabled) { background: #f1f5f9; border-color: #94a3b8; transform: translateY(-1px); }
             .md-btn-icon:hover:not(:disabled) { background: #eff6ff; border-color: #bfdbfe; transform: translateY(-1px); }
             #mippedia-data-app.md-dark .md-btn-icon { background: #1e293b; border-color: #475569; color: #818cf8; }
             #mippedia-data-app.md-dark .md-btn-icon { background: #1e293b; border-color: #475569; color: #60a5fa; }
             .md-btn-lock { color: #dc2626; background: #fef2f2; border-color: #fca5a5; }
            #mippedia-data-app.md-dark .md-btn-icon:hover:not(:disabled) { background: #2dd4bf1a; border-color: #2dd4bf; }
             .md-btn-lock { color: #ef4444; background: #fef2f2; border-color: #fecaca; }


             /* Quality Score Bar */
             /* Stats & Score Bar */
             .md-score-box { margin-bottom: 14px; background: #f8fafc; padding: 10px 14px; border-radius: 10px; border: 1px solid #e2e8f0; }
             .md-dashboard { display: grid; grid-template-columns: 1fr auto; gap: 16px; margin-bottom: 20px; align-items: center; }
            .md-score-box { background: #f8fafc; padding: 12px 16px; border-radius: 8px; border: 1px solid #e2e8f0; width: 100%; }
             #mippedia-data-app.md-dark .md-score-box { background: #182234; border-color: #334155; }
             #mippedia-data-app.md-dark .md-score-box { background: #182234; border-color: #334155; }
             .md-score-bar-bg { background: #e2e8f0; height: 6px; border-radius: 6px; overflow: hidden; margin-top: 6px; }
             .md-score-bar-bg { background: #e2e8f0; height: 4px; border-radius: 4px; overflow: hidden; margin-top: 8px; }
             .md-score-bar-fill { background: linear-gradient(90deg, #6366f1, #10b981); height: 100%; transition: width 0.4s ease; }
             .md-score-bar-fill { background: linear-gradient(90deg, #3b82f6, #10b981); height: 100%; transition: width 0.5s cubic-bezier(0.4, 0, 0.2, 1); }
           
            .md-stats-group { display: flex; gap: 8px; font-size: 0.75rem; font-weight: 600; color: #64748b; flex-wrap: wrap; }
            .md-stat-badge { background: #f1f5f9; padding: 4px 10px; border-radius: 20px; border: 1px solid #cbd5e1; }
            #mippedia-data-app.md-dark .md-stat-badge { background: #0f172a; border-color: #475569; color: #94a3b8; }


             .md-search-box { position: relative; margin-bottom: 14px; width: 100%; }
             .md-search-box { position: relative; margin-bottom: 16px; width: 100%; }
             .md-search-input { width: 100%; padding: 8px 12px 8px 34px; border: 1px solid #cbd5e1; border-radius: 8px; font-size: 0.85rem; background: #f8fafc; transition: 0.15s; }
             .md-search-input { width: 100%; padding: 10px 14px 10px 38px; border: 1px solid #cbd5e1; border-radius: 8px; font-size: 0.9rem; background: #f8fafc; transition: all 0.2s; outline: none; }
             .md-search-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: #94a3b8; }
            .md-search-input:focus { border-color: #3b82f6; box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); background: #ffffff; }
             .md-search-icon { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: #94a3b8; }


            /* List Rows */
            .md-list { display: flex; flex-direction: column; gap: 10px; }
             .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; border: 1px solid #f1f5f9; transition: all 0.15s ease;
                 padding: 16px; border: 1px solid #e2e8f0; transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
                 border-radius: 12px; gap: 12px; margin-bottom: 8px; background: #fff;
                 border-radius: 8px; gap: 16px; background: #ffffff;
             }
             }
             .md-row-display:hover { border-color: #c7d2fe; box-shadow: 0 4px 12px rgba(99,102,241,0.06); }
             .md-row-display:hover { border-color: #bfdbfe; background: #fafafa; }
            #mippedia-data-app.md-dark .md-row-display:hover { border-color: #475569; background: #1e293b; }
            .md-row-highlight { border-color: #3b82f6 !important; box-shadow: 0 0 0 1px #3b82f6; }
           
            .md-data-group { display: flex; flex-direction: column; gap: 6px; flex: 1; min-width: 0; }
            .md-data-label-wrap { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
            .md-data-label { font-size: 0.75rem; color: #64748b; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; }
              
              
             .md-data-group { display: flex; flex-direction: column; gap: 5px; flex: 1; min-width: 0; }
            /* Professional Tags */
             .md-data-label-wrap { display: flex; align-items: center; gap: 8px; }
             .md-type-tag { font-size: 0.65rem; padding: 2px 8px; border-radius: 4px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; border: 1px solid transparent; }
             .md-data-label { font-size: 0.72rem; color: #64748b; font-weight: 800; text-transform: uppercase; letter-spacing: 0.6px; }
             .md-tag-date { color: #1d4ed8; background: #dbeafe; border-color: #bfdbfe; }
             .md-type-tag { font-size: 0.68rem; padding: 2px 6px; border-radius: 4px; font-weight: 700; }
             .md-tag-link { color: #be123c; background: #ffe4e6; border-color: #fecdd3; }
             .md-data-value-wrap { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
            .md-tag-piped { color: #b45309; background: #fef3c7; border-color: #fde68a; }
             .md-data-value { font-size: 0.98rem; color: #0f172a; line-height: 1.4; font-weight: 600; word-break: break-word; }
             .md-tag-wiki { color: #15803d; background: #dcfce7; border-color: #bbf7d0; }
            .md-tag-text { color: #475569; background: #f1f5f9; border-color: #cbd5e1; }
 
             .md-data-value-wrap { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
             .md-data-value { font-size: 1rem; color: #0f172a; line-height: 1.5; font-weight: 600; word-wrap: break-word; overflow-wrap: break-word; }
              
              
             .md-btn-mini { background: none; border: none; color: #94a3b8; cursor: pointer; padding: 3px 5px; border-radius: 4px; transition: 0.15s; display: inline-flex; align-items: center; }
             .md-btn-mini { background: none; border: none; color: #94a3b8; cursor: pointer; padding: 4px 6px; border-radius: 4px; transition: 0.2s; display: inline-flex; align-items: center; }
             .md-btn-mini:hover { color: #4f46e5; background: #e0e7ff; }
             .md-btn-mini:hover { color: #3b82f6; background: #eff6ff; }


             .md-ref-container { margin-top: 8px; width: 100%; }
            /* References */
             .md-ref-card { background: #f8fafc; padding: 10px 12px; border-radius: 8px; border-left: 4px solid #4f46e5; font-size: 0.83rem; display: flex; flex-direction: column; gap: 4px; border-top: 1px solid #f1f5f9; border-right: 1px solid #f1f5f9; border-bottom: 1px solid #f1f5f9; }
             .md-ref-container { margin-top: 10px; width: 100%; }
             .md-ref-tag-header { font-size: 0.7rem; font-weight: 800; text-transform: uppercase; color: #4f46e5; display: flex; align-items: center; gap: 4px; }
             .md-ref-card { background: #f8fafc; padding: 12px; border-radius: 6px; border-left: 3px solid #cbd5e1; font-size: 0.85rem; display: flex; flex-direction: column; gap: 4px; }
             .md-ref-header { font-weight: 700; color: #2563eb; text-decoration: none; word-break: break-word; }
             .md-ref-tag-header { font-size: 0.7rem; font-weight: 700; text-transform: uppercase; color: #64748b; display: flex; align-items: center; gap: 6px; margin-bottom: 4px; }
             .md-ref-header { font-weight: 600; color: #2563eb; text-decoration: none; word-wrap: break-word; overflow-wrap: break-word; }


             .md-social-group { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 6px; }
            /* Socials */
             .md-social-badge { display: inline-flex; align-items: center; gap: 5px; padding: 4px 10px; border-radius: 16px; font-size: 0.75rem; font-weight: 600; color: white; text-decoration: none; transition: transform 0.15s; }
             .md-social-group { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
             .md-social-badge:hover { transform: translateY(-1px); color: white; text-decoration: none; }
             .md-social-badge { display: inline-flex; align-items: center; gap: 6px; padding: 4px 12px; border-radius: 20px; font-size: 0.75rem; font-weight: 600; color: white; text-decoration: none; transition: opacity 0.2s; }
             .md-social-badge:hover { opacity: 0.9; color: white; text-decoration: none; }


             .md-preset-wrapper { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 6px; }
             .md-preset-wrapper { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
             .md-preset-pill { background: #f1f5f9; border: 1px solid #cbd5e1; padding: 3px 8px; border-radius: 10px; font-size: 0.73rem; font-weight: 600; color: #475569; cursor: pointer; transition: 0.15s; }
             .md-preset-pill { background: #f1f5f9; border: 1px solid #cbd5e1; padding: 4px 10px; border-radius: 16px; font-size: 0.75rem; font-weight: 600; color: #475569; cursor: pointer; transition: 0.2s; }
             .md-preset-pill:hover { background: #e0e7ff; color: #3730a3; border-color: #a5b4fc; }
             .md-preset-pill:hover { background: #eff6ff; color: #1e40af; border-color: #bfdbfe; }


             .md-form-box { background: #ffffff; border-radius: 12px; padding: 16px; margin-top: 14px; border: 1px solid #cbd5e1; box-shadow: 0 4px 16px rgba(0,0,0,0.05); }
            /* Form UI */
             .md-form-title { font-size: 1rem; font-weight: 800; color: #0f172a; margin: 0 0 14px 0; border-bottom: 2px solid #e2e8f0; padding-bottom: 6px; }
             .md-form-box { background: #ffffff; border-radius: 8px; padding: 20px; margin-top: 20px; border: 1px solid #cbd5e1; transition: all 0.3s; }
             .md-form-group { margin-bottom: 12px; }
            .md-form-header { display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #e2e8f0; padding-bottom: 12px; margin-bottom: 16px; }
             .md-form-group label { display: block; font-size: 0.8rem; font-weight: 700; margin-bottom: 5px; color: #334155; }
             .md-form-title { font-size: 1.1rem; font-weight: 700; color: #0f172a; margin: 0; }
             .md-input, .md-select, .md-textarea { width: 100%; padding: 8px 11px; border: 1px solid #cbd5e1; border-radius: 8px; box-sizing: border-box; font-family: inherit; font-size: 0.88rem; transition: 0.15s; background: #fff; }
           
             .md-input:focus, .md-select:focus { border-color: #4f46e5; outline: none; box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.12); }
             .md-form-group { margin-bottom: 16px; width: 100%; }
             .md-form-group label { display: block; font-size: 0.85rem; font-weight: 600; margin-bottom: 6px; color: #334155; }
           
             .md-input, .md-select, .md-textarea { width: 100%; padding: 10px 12px; border: 1px solid #cbd5e1; border-radius: 6px; box-sizing: border-box; font-family: inherit; font-size: 0.9rem; transition: all 0.2s; background: #fff; outline: none; }
             .md-input:focus, .md-select:focus { border-color: #3b82f6; box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); }
            .md-input.md-error { border-color: #ef4444; background: #fef2f2; }
              
              
             .md-dup-alert { background: #fffbebf8; border: 1px solid #fcd34d; color: #b45309; padding: 6px 10px; border-radius: 6px; font-size: 0.78rem; margin-top: 4px; display: none; }
            .md-char-count { font-size: 0.7rem; color: #94a3b8; text-align: right; margin-top: 4px; font-weight: 600; }
             .md-preview-card { background: #faf5ff; border: 1px dashed #a855f7; padding: 10px; border-radius: 8px; margin-top: 10px; font-size: 0.83rem; }
             .md-dup-alert { background: #fffbeb; border: 1px solid #fde68a; color: #b45309; padding: 8px 12px; border-radius: 6px; font-size: 0.8rem; margin-top: 6px; display: none; font-weight: 600; }
             .md-preview-card { background: #f8fafc; border: 1px dashed #cbd5e1; padding: 12px; border-radius: 6px; margin-top: 16px; font-size: 0.85rem; }


             .md-ref-grid, .md-social-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; background: #f8fafc; padding: 10px; border-radius: 8px; border: 1px dashed #cbd5e1; }
             .md-ref-grid, .md-social-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; background: #f8fafc; padding: 16px; border-radius: 6px; border: 1px solid #e2e8f0; }
             .md-social-grid { max-height: 240px; overflow-y: auto; background: #f1f5f9; }
             .md-social-grid { max-height: 250px; overflow-y: auto; }
             .md-grid-full { grid-column: span 2; }
             .md-grid-full { grid-column: span 2; }
              
              
             .md-form-actions { display: flex; gap: 8px; align-items: center; margin-top: 16px; padding-top: 12px; border-top: 1px solid #e2e8f0; flex-wrap: wrap; }
             .md-form-actions { display: flex; gap: 10px; align-items: center; margin-top: 20px; padding-top: 16px; border-top: 1px solid #e2e8f0; flex-wrap: wrap; }
             .md-btn { padding: 9px 16px; border: none; border-radius: 8px; font-weight: 700; cursor: pointer; color: white; font-size: 0.83rem; transition: 0.15s; display: inline-flex; align-items: center; gap: 5px; justify-content: center; }
             .md-btn { padding: 10px 18px; border: none; border-radius: 6px; font-weight: 600; cursor: pointer; color: white; font-size: 0.85rem; transition: 0.2s; display: inline-flex; align-items: center; gap: 6px; justify-content: center; }
             .md-btn-save { background: linear-gradient(135deg, #4f46e5, #4338ca); flex: 1; min-width: 130px; }
             .md-btn-save { background: #3b82f6; flex: 1; min-width: 150px; }
            .md-btn-save:hover { background: #2563eb; }
             .md-btn-cancel { background: #64748b; }
             .md-btn-cancel { background: #64748b; }
             .md-btn-delete { background: #dc2626; margin-left: auto; }
            .md-btn-cancel:hover { background: #475569; }
             .md-btn-delete { background: #ef4444; margin-left: auto; }
            .md-btn-delete:hover { background: #dc2626; }
            .md-btn-reset { background: transparent; border: 1px solid #cbd5e1; color: #64748b; }
            .md-btn-reset:hover { background: #f1f5f9; color: #0f172a; }
              
              
             .md-spinner { display: none; width: 13px; height: 13px; border: 2px solid rgba(255,255,255,0.4); border-radius: 50%; border-top-color: #fff; animation: md-spin 0.6s linear infinite; }
             .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.6s linear infinite; }
             .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); } }


             @media (max-width: 600px) {
            /* MOBILE OPTIMIZATION (FITUR RESPONSIVE) */
                 #mippedia-data-app { padding: 12px; margin: 12px 0; border-radius: 12px; }
             @media (max-width: 768px) {
                 .md-header { flex-direction: column; align-items: flex-start; gap: 8px; }
                 #mippedia-data-app { padding: 16px; margin: 16px 0; border-radius: 8px; border-left: none; border-right: none; border-radius: 0; }
                 .md-actions { width: 100%; justify-content: flex-start; }
                 .md-header { flex-direction: column; align-items: flex-start; gap: 12px; }
                 .md-ref-grid, .md-social-grid { grid-template-columns: 1fr; }
                .md-actions { width: 100%; display: grid; grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); gap: 8px; }
                 .md-btn-icon { justify-content: center; width: 100%; }
               
                .md-dashboard { grid-template-columns: 1fr; gap: 12px; }
               
                .md-row-display { flex-direction: column; padding: 14px; }
                .md-row-actions { width: 100%; display: flex; justify-content: flex-end; padding-top: 8px; border-top: 1px solid #f1f5f9; }
               
                 .md-ref-grid, .md-social-grid { grid-template-columns: 1fr; padding: 12px; }
                 .md-grid-full { grid-column: span 1; }
                 .md-grid-full { grid-column: span 1; }
                 .md-form-actions { flex-direction: column; width: 100%; }
               
                 .md-form-actions { flex-direction: column; width: 100%; gap: 10px; }
                 .md-btn { width: 100%; margin: 0 !important; }
                 .md-btn { width: 100%; margin: 0 !important; }
             }
             }
Baris 267: Baris 369:
         function buildWikitextRef(refObj) {
         function buildWikitextRef(refObj) {
             if (!refObj) return "";
             if (!refObj) return "";
             var title = refObj.title || "Rujukan";
             var title = refObj.title || "Referensi";
             var text = refObj.url ? "[" + ensureHttps(refObj.url) + " " + title + "]" : "'''" + title + "'''";
             var text = refObj.url ? "[" + ensureHttps(refObj.url) + " " + title + "]" : "'''" + title + "'''";
             var details = [];
             var details = [];
Baris 273: Baris 375:
             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("Tanggal Akses: " + formatIndonesianDate(refObj.date));
             if (refObj.date) details.push("Akses: " + formatIndonesianDate(refObj.date));
             if (refObj.type) details.push("Jenis: " + refObj.type + (refObj.subType ? " (" + refObj.subType + ")" : ""));
             if (refObj.type) details.push("Jenis: " + refObj.type + (refObj.subType ? " (" + refObj.subType + ")" : ""));
             if (details.length > 0) text += " &mdash; <small>" + details.join(" &bull; ") + "</small>";
             if (details.length > 0) text += " &mdash; <small>" + details.join(" &bull; ") + "</small>";
Baris 282: Baris 384:
             if ($btnEl) $btnEl.addClass('is-loading').prop('disabled', true);
             if ($btnEl) $btnEl.addClass('is-loading').prop('disabled', true);
              
              
            // Bersihkan LocalStorage Draft saat disave
            localStorage.removeItem('md_draft_' + pageName);
             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";
Baris 302: Baris 407:


             api.postWithToken('csrf', {
             api.postWithToken('csrf', {
                 action: 'edit', title: pageName, text: newContent, summary: 'Mippedia Data V8: ' + actionType, minor: false
                 action: 'edit', title: pageName, text: newContent, summary: 'Mippedia Data V9: ' + actionType, minor: false
             }).done(function() {
             }).done(function() {
                 window.location.href = location.pathname + "?refresh=" + new Date().getTime();
                 window.location.href = location.pathname + "?refresh=" + new Date().getTime();
Baris 317: Baris 422:
             // HEADER & TOOLS
             // HEADER & TOOLS
             var $header = $('<div class="md-header"></div>');
             var $header = $('<div class="md-header"></div>');
             var $titleContainer = $('<div class="md-title-container"><h3 class="md-title">' + svgEdit + ' Data Terstruktur</h3></div>');
             var $titleContainer = $('<div class="md-title-container"><h3 class="md-title">' + svgDatabase + ' Structured Data</h3></div>');
             var $actions = $('<div class="md-actions"></div>');
             var $actions = $('<div class="md-actions"></div>');
              
              
            // FITUR 6: Dark Mode Switch
             var $darkBtn = $('<button class="md-btn-icon" title="Theme Toggle">' + svgMoon + ' Theme</button>');
             var $darkBtn = $('<button class="md-btn-icon" title="Ganti Tema">' + svgMoon + ' Dark</button>');
             $darkBtn.on('click', function() {
             $darkBtn.on('click', function() {
                 isDarkMode = !isDarkMode;
                 isDarkMode = !isDarkMode;
Baris 328: Baris 432:
             $actions.append($darkBtn);
             $actions.append($darkBtn);


             // FITUR 10: JSON Export & Bulk Operations
             // FITUR 6: Read-Only Mode
             var $jsonBtn = $('<button class="md-btn-icon" title="Ekspor JSON">' + svgCode + ' JSON</button>');
             var $viewBtn = $('<button class="md-btn-icon" title="View Only Mode">' + svgView + ' View</button>');
             $jsonBtn.on('click', function() { showJsonModal(rawContent); });
             $viewBtn.on('click', function() {
             $actions.append($jsonBtn);
                isReadOnly = !isReadOnly;
                $app.find('.md-row-actions, .md-btn-mini').toggle(!isReadOnly);
                $(this).css('background', isReadOnly ? '#e0e7ff' : '');
            });
            $actions.append($viewBtn);
 
            $actions.append($('<button class="md-btn-icon" title="Export JSON">' + svgCode + ' JSON</button>').on('click', function() { showJsonModal(rawContent); }));
           
            // FITUR 4: CSV Export
             $actions.append($('<button class="md-btn-icon" title="Export CSV">' + svgCsv + ' CSV</button>').on('click', exportToCSV));


             if (isSysop) {
             if (isSysop) {
                 var $lockBtn = $('<button class="md-btn-icon ' + (isLocked ? 'md-btn-unlock' : 'md-btn-lock') + '">' + (isLocked ? svgUnlock + ' Buka' : svgLock + ' Kunci') + ' <span class="md-spinner"></span></button>');
                 var $lockBtn = $('<button class="md-btn-icon ' + (isLocked ? 'md-btn-unlock' : 'md-btn-lock') + '">' + (isLocked ? svgUnlock + ' Unlock' : svgLock + ' Lock') + ' <span class="md-spinner"></span></button>');
                 $lockBtn.on('click', function() {
                 $lockBtn.on('click', function() {
                     isLocked = !isLocked;
                     isLocked = !isLocked;
Baris 343: Baris 456:


             if (!isLocked || isSysop) {
             if (!isLocked || isSysop) {
                 var $addBtn = $('<button class="md-btn-icon" title="Tambah Data Baru">' + svgEdit + ' Tambah</button>');
                 var $addBtn = $('<button class="md-btn-icon" style="background:#3b82f6; color:#fff; border-color:#2563eb;">' + svgEdit + ' Add New</button>');
                 $addBtn.on('click', function() { showForm(-1, rawContent); });
                 $addBtn.on('click', function() { showForm(-1, rawContent); });
                 $actions.append($addBtn);
                 $actions.append($addBtn);
Baris 351: Baris 464:
             $app.append($header);
             $app.append($header);


             // FITUR 3: Skor Kualitas Data
             // FITUR 10: DASHBOARD STATISTIK & SKOR KUALITAS
            var $dashboard = $('<div class="md-dashboard"></div>');
             var score = calculateQualityScore();
             var score = calculateQualityScore();
             var $scoreBox = $('<div class="md-score-box"><div style="display:flex; justify-content:space-between; font-size:0.78rem; font-weight:700;"><span>Kelengkapan Data:</span><span>' + score + '%</span></div><div class="md-score-bar-bg"><div class="md-score-bar-fill" style="width:' + score + '%;"></div></div></div>');
             var $scoreBox = $('<div class="md-score-box"><div style="display:flex; justify-content:space-between; font-size:0.8rem; font-weight:700;"><span>Data Completeness</span><span>' + score + '%</span></div><div class="md-score-bar-bg"><div class="md-score-bar-fill" style="width:' + score + '%;"></div></div></div>');
             $app.append($scoreBox);
            $dashboard.append($scoreBox);
 
            var stats = calculateDataStats();
            var $statsGroup = $('<div class="md-stats-group"></div>');
            $statsGroup.append('<span class="md-stat-badge">Text: ' + stats.text + '</span>');
            $statsGroup.append('<span class="md-stat-badge">Link: ' + stats.wiki + '</span>');
            $statsGroup.append('<span class="md-stat-badge">Ext. Web: ' + stats.link + '</span>');
            $statsGroup.append('<span class="md-stat-badge">Date: ' + stats.date + '</span>');
            $dashboard.append($statsGroup);
             $app.append($dashboard);


             // SEARCH BAR
             // SEARCH BAR & FITUR 15 (Highlighting)
             if (dataItems.length > 2) {
             if (dataItems.length > 0) {
                 var $searchBox = $('<div class="md-search-box"><span class="md-search-icon">' + svgSearch + '</span><input type="text" class="md-search-input" placeholder="Cari data terstruktur..."></div>');
                 var $searchBox = $('<div class="md-search-box"><span class="md-search-icon">' + svgSearch + '</span><input type="text" class="md-search-input" placeholder="Search data..."></div>');
                 $searchBox.find('input').on('keyup input', function() {
                 $searchBox.find('input').on('keyup input', function() {
                     var q = $(this).val().toLowerCase();
                     var q = $(this).val().toLowerCase();
                     $app.find('.md-row-display').each(function() {
                     $app.find('.md-row-display').each(function() {
                         var text = $(this).text().toLowerCase();
                         var text = $(this).text().toLowerCase();
                         $(this).toggle(text.indexOf(q) !== -1);
                        var match = text.indexOf(q) !== -1;
                         $(this).toggle(match);
                        if (q.length > 1 && match) { $(this).addClass('md-row-highlight'); }
                        else { $(this).removeClass('md-row-highlight'); }
                     });
                     });
                 });
                 });
Baris 369: Baris 495:
             }
             }


             // DAFTAR BARIS DATA
             // DAFTAR BARIS DATA (TANPA NAIK/TURUN)
             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:#64748b; text-align:center; padding: 25px 10px; background:#f8fafc; border-radius:10px; border:1px dashed #cbd5e1; font-size:0.85rem;">Belum ada data. Klik <b>Tambah</b> untuk membuat baru.</div>');
                 $list.append('<div style="color:#64748b; text-align:center; padding: 30px 10px; background:#f8fafc; border-radius:8px; border:1px dashed #cbd5e1; font-size:0.9rem;">No data entries found. Click <b>Add New</b> to create.</div>');
             } else {
             } else {
                 dataItems.forEach(function(item, index) {
                 dataItems.forEach(function(item, index) {
Baris 378: Baris 504:
                     var $info = $('<div class="md-data-group"></div>');
                     var $info = $('<div class="md-data-group"></div>');
                      
                      
                    // FITUR 8: Auto Data-Type Badge
                     var typeBadge = detectDataTypeBadge(item);
                     var typeBadge = detectDataTypeBadge(item);
                     $info.append('<div class="md-data-label-wrap"><span class="md-data-label">' + item.prop + '</span>' + typeBadge + '</div>');
                     $info.append('<div class="md-data-label-wrap"><span class="md-data-label">' + item.prop + '</span>' + typeBadge + '</div>');
                      
                      
                    // RENDER VALUE
                     var displayValHtml = '';
                     var displayValHtml = '';
                     var rawVal = item.val || '';
                     var rawVal = item.val || '';


                     if (rawVal.match(/^https?:\/\//i)) {
                     if (rawVal.match(/^https?:\/\//i)) {
                         displayValHtml = '<a href="' + rawVal + '" target="_blank" style="color:#2563eb; font-weight:700; text-decoration:none;">' + extractDomain(rawVal) + '</a>';
                         displayValHtml = '<a href="' + rawVal + '" target="_blank" style="color:#2563eb; font-weight:600; text-decoration:none;">' + extractDomain(rawVal) + '</a>';
                     } else if (item.linkTarget && item.linkTarget.trim() !== '') {
                     } else if (item.linkTarget && item.linkTarget.trim() !== '') {
                         displayValHtml = '<a href="' + mw.util.getUrl(item.linkTarget.trim()) + '" style="color:#2563eb; font-weight:700; text-decoration:none;">' + rawVal + '</a>';
                         displayValHtml = '<a href="' + mw.util.getUrl(item.linkTarget.trim()) + '" style="color:#2563eb; font-weight:600; text-decoration:none;">' + rawVal + '</a>';
                     } else if (item.linkSplit) {
                     } else if (item.linkSplit) {
                         var parts = rawVal.split(/([,/\s]+dan\s+|[,/\s]+atau\s+|[,/]+)/gi);
                         var parts = rawVal.split(/([,/\s]+dan\s+|[,/\s]+atau\s+|[,/]+)/gi);
Baris 395: Baris 519:
                             var trimmed = p.trim();
                             var trimmed = p.trim();
                             if (trimmed && !p.match(/^[,/\s]+$/) && trimmed.toLowerCase() !== 'dan' && trimmed.toLowerCase() !== 'atau') {
                             if (trimmed && !p.match(/^[,/\s]+$/) && trimmed.toLowerCase() !== 'dan' && trimmed.toLowerCase() !== 'atau') {
                                 return '<a href="' + mw.util.getUrl(trimmed) + '" style="color:#2563eb; font-weight:700; text-decoration:none;">' + trimmed + '</a>';
                                 return '<a href="' + mw.util.getUrl(trimmed) + '" style="color:#2563eb; font-weight:600; text-decoration:none;">' + trimmed + '</a>';
                             }
                             }
                             return p;
                             return p;
                         }).join('');
                         }).join('');
                     } else if (item.link) {
                     } else if (item.link) {
                         displayValHtml = '<a href="' + mw.util.getUrl(rawVal) + '" style="color:#2563eb; font-weight:700; text-decoration:none;">' + rawVal + '</a>';
                         displayValHtml = '<a href="' + mw.util.getUrl(rawVal) + '" style="color:#2563eb; font-weight:600; text-decoration:none;">' + rawVal + '</a>';
                     } else {
                     } else {
                         displayValHtml = rawVal;
                         displayValHtml = rawVal;
Baris 408: Baris 532:
                     $valWrap.append('<span class="md-data-value">' + displayValHtml + '</span>');
                     $valWrap.append('<span class="md-data-value">' + displayValHtml + '</span>');
                      
                      
                     var $copyBtn = $('<button class="md-btn-mini" title="Salin Teks">' + svgCopy + '</button>');
                     var $copyBtn = $('<button class="md-btn-mini" title="Copy Text">' + svgCopy + '</button>');
                     $copyBtn.on('click', function() {
                     $copyBtn.on('click', function() {
                         navigator.clipboard.writeText(rawVal);
                         navigator.clipboard.writeText(rawVal);
Baris 416: Baris 540:
                     $valWrap.append($copyBtn);
                     $valWrap.append($copyBtn);


                    // FITUR 7: Copy Wikitext Button
                     var $copyWtBtn = $('<button class="md-btn-mini" title="Copy Wikitext">' + svgWikitext + '</button>');
                     var $copyWtBtn = $('<button class="md-btn-mini" title="Salin Kode Wikitext">' + svgWikitext + '</button>');
                     $copyWtBtn.on('click', function() {
                     $copyWtBtn.on('click', function() {
                         var wtCode = "| " + item.prop + " = " + buildWikitextVal(item);
                         var wtCode = "| " + item.prop + " = " + buildWikitextVal(item);
                         navigator.clipboard.writeText(wtCode);
                         navigator.clipboard.writeText(wtCode);
                         $(this).css('color', '#4f46e5');
                         $(this).css('color', '#3b82f6');
                         setTimeout(function(){ $copyWtBtn.css('color', '#94a3b8'); }, 1000);
                         setTimeout(function(){ $copyWtBtn.css('color', '#94a3b8'); }, 1000);
                     });
                     });
Baris 428: Baris 551:
                     $info.append($valWrap);
                     $info.append($valWrap);


                     // RENDER MEDIA SOSIAL
                     // MEDIA SOSIAL
                     if (item.isPersonOrg && item.socials) {
                     if (item.isPersonOrg && item.socials) {
                         var $socialGroup = $('<div class="md-social-group"></div>');
                         var $socialGroup = $('<div class="md-social-group"></div>');
Baris 445: Baris 568:
                     }
                     }
                      
                      
                     // RENDER REFERENSI
                     // REFERENSI
                     if (item.ref && (item.ref.url || item.ref.title)) {
                     if (item.ref && (item.ref.url || item.ref.title)) {
                         var ref = item.ref;
                         var ref = item.ref;
                         var $refContainer = $('<div class="md-ref-container"></div>');
                         var $refContainer = $('<div class="md-ref-container"></div>');
                         var $refCard = $('<div class="md-ref-card"></div>');
                         var $refCard = $('<div class="md-ref-card"></div>');
                         $refCard.append('<div class="md-ref-tag-header">' + svgRef + ' Referensi / Sumber Rujukan:</div>');
                         $refCard.append('<div class="md-ref-tag-header">' + svgRef + ' Reference Source:</div>');


                         var refTitle = ref.title || "Sumber Rujukan";
                         var refTitle = ref.title || "Reference Link";
                         if (ref.url) {
                         if (ref.url) {
                             $refCard.append('<a href="' + ensureHttps(ref.url) + '" target="_blank" class="md-ref-header">' + refTitle + '</a>');
                             $refCard.append('<a href="' + ensureHttps(ref.url) + '" target="_blank" class="md-ref-header">' + refTitle + '</a>');
Baris 460: Baris 583:


                         var details = [];
                         var details = [];
                         if (ref.author) details.push("Penulis: " + ref.author);
                         if (ref.author) details.push("Author: " + ref.author);
                         if (ref.site) details.push("Situs: " + ref.site);
                         if (ref.site) details.push("Site: " + ref.site);
                         if (ref.publisher) details.push("Penerbit: " + ref.publisher);
                         if (ref.publisher) details.push("Publisher: " + ref.publisher);
                         if (ref.date) details.push("Akses: " + formatIndonesianDate(ref.date));
                         if (ref.date) details.push("Date: " + formatIndonesianDate(ref.date));
                         if (details.length > 0) {
                         if (details.length > 0) {
                             $refCard.append('<div style="font-size:0.76rem; color:#64748b;">' + details.join(' &bull; ') + '</div>');
                             $refCard.append('<div style="font-size:0.75rem; color:#64748b; margin-top:2px;">' + details.join(' &bull; ') + '</div>');
                         }
                         }


Baris 474: Baris 597:
                     $row.append($info);
                     $row.append($info);


                     // FITUR 4: Reorder Up/Down Controls
                     // ACTIONS PER BARIS (Edit, Clone)
                     var $ctrls = $('<div style="display:flex; align-items:center; gap:4px;"></div>');
                     var $ctrls = $('<div class="md-row-actions" style="display:flex; align-items:center; gap:8px;"></div>');
                     if (!isLocked || isSysop) {
                     if (!isLocked || isSysop) {
                         if (index > 0) {
                          
                            var $btnUp = $('<button class="md-btn-mini" title="Naikkan Urutan">' + svgUp + '</button>');
                        // FITUR 5: Quick Clone Data
                            $btnUp.on('click', function() {
                        var $cloneBtn = $('<button class="md-btn-icon" style="padding:6px 10px;" title="Clone Row">' + svgClone + ' Clone</button>');
                                var temp = dataItems[index - 1];
                        $cloneBtn.on('click', function() {
                                dataItems[index - 1] = dataItems[index];
                            var clonedObj = JSON.parse(JSON.stringify(item));
                                dataItems[index] = temp;
                             clonedObj.prop = clonedObj.prop + " (Copy)";
                                saveData('Ubah urutan naik', $(this), rawContent);
                             dataItems.splice(index + 1, 0, clonedObj);
                             });
                            saveData('Kloning data', $(this), rawContent);
                            $ctrls.append($btnUp);
                        });
                        }
                        $ctrls.append($cloneBtn);
                        if (index < dataItems.length - 1) {
                            var $btnDown = $('<button class="md-btn-mini" title="Turunkan Urutan">' + svgDown + '</button>');
                             $btnDown.on('click', function() {
                                var temp = dataItems[index + 1];
                                dataItems[index + 1] = dataItems[index];
                                dataItems[index] = temp;
                                saveData('Ubah urutan turun', $(this), rawContent);
                            });
                            $ctrls.append($btnDown);
                        }


                         var $editIcon = $('<button class="md-btn-icon edit-btn" style="padding:5px 9px;">' + svgEdit + ' Edit</button>');
                         var $editIcon = $('<button class="md-btn-icon" style="padding:6px 10px;">' + svgEdit + ' Edit</button>');
                         $editIcon.on('click', function() { showForm(index, rawContent); });
                         $editIcon.on('click', function() { showForm(index, rawContent); });
                         $ctrls.append($editIcon);
                         $ctrls.append($editIcon);
Baris 515: Baris 628:
         }
         }


         // 7. FORM EDIT & LIVE PREVIEW & AUTO SANITIZER
         // 7. FORM EDIT (SMART FEATURES IMPLEMENTATION)
         function showForm(index, rawContent) {
         function showForm(index, rawContent) {
             var $container = $('#md-form-container');
             var $container = $('#md-form-container');
Baris 522: Baris 635:
             var isNew = index === -1;
             var isNew = index === -1;
             var currentData = isNew ? { prop: '', val: '', link: false, linkTarget: '', linkSplit: false, ref: {}, isPersonOrg: false, socials: {} } : dataItems[index];
             var currentData = isNew ? { prop: '', val: '', link: false, linkTarget: '', linkSplit: false, ref: {}, isPersonOrg: false, socials: {} } : dataItems[index];
           
            // FITUR 3: Cek Draft dari Local Storage jika ini Form Baru
            if (isNew) {
                var draft = localStorage.getItem('md_draft_' + pageName);
                if (draft) {
                    if (confirm("Draft formulir sebelumnya ditemukan. Ingin memuatnya?")) {
                        try { currentData = JSON.parse(draft); } catch(e){}
                    } else { localStorage.removeItem('md_draft_' + pageName); }
                }
            }
             var currentRef = currentData.ref || {};
             var currentRef = currentData.ref || {};
             var currentSocials = currentData.socials || {};
             var currentSocials = currentData.socials || {};


             var $form = $('<div class="md-form-box"></div>');
             var $form = $('<div class="md-form-box" id="md-main-form"></div>');
            $form.append('<h4 class="md-form-title">' + (isNew ? 'Tambah Data Baru' : 'Edit Data Terstruktur') + '</h4>');
              
              
             // FITUR 2: Duplicate Alert Label
             // FITUR 11: Form Accordion
             var $propGroup = $('<div class="md-form-group"><label>Label Data</label><input type="text" id="md-inp-prop" class="md-input" value="' + (currentData.prop || '') + '"><div class="md-dup-alert" id="md-dup-msg">⚠️ Perhatian: Label data ini sudah ada!</div></div>');
            var $header = $('<div class="md-form-header"></div>');
            $header.append('<h4 class="md-form-title">' + (isNew ? 'Add New Data Entry' : 'Edit Data Entry') + '</h4>');
            var $toggleBtn = $('<button class="md-btn-mini">Minimize</button>');
            $header.append($toggleBtn);
            $form.append($header);
 
            var $formBody = $('<div></div>');
 
            // Label Box
             var $propGroup = $('<div class="md-form-group"><label>Data Label (Required)</label><input type="text" id="md-inp-prop" class="md-input" value="' + (currentData.prop || '') + '"><div class="md-dup-alert" id="md-dup-msg">Alert: This label already exists in the dataset!</div></div>');
             var $presetWrap = $('<div class="md-preset-wrapper"></div>');
             var $presetWrap = $('<div class="md-preset-wrapper"></div>');
             PRESET_LABELS.forEach(function(lbl) {
             PRESET_LABELS.forEach(function(lbl) {
                 var $pill = $('<span class="md-preset-pill">' + lbl + '</span>');
                 var $pill = $('<span class="md-preset-pill">' + lbl + '</span>');
                 $pill.on('click', function() {  
                 $pill.on('click', function() { $('#md-inp-prop').val(lbl).trigger('input'); });
                    $('#md-inp-prop').val(lbl).trigger('input');  
                });
                 $presetWrap.append($pill);
                 $presetWrap.append($pill);
             });
             });
             $propGroup.append($presetWrap);
             $propGroup.append($presetWrap);
             $form.append($propGroup);
             $formBody.append($propGroup);
              
              
             // Value Box
             // Value Box + FITUR 7 (Char Counter)
             var $valBox = $('<div class="md-form-group"></div>');
             var $valBox = $('<div class="md-form-group"></div>');
             $valBox.append('<label>Nilai Data</label><input type="text" id="md-inp-val" class="md-input" value="' + (currentData.val || '') + '">');
             $valBox.append('<label>Data Value (Required)</label><input type="text" id="md-inp-val" class="md-input" value="' + (currentData.val || '') + '">');
            $valBox.append('<div class="md-char-count" id="md-char-counter">0 chars</div>');
              
              
             var $wikilinkOpts = $('<div style="margin-top:8px; display:flex; flex-direction:column; gap:6px; background:#f8fafc; padding:8px; border-radius:8px; border:1px solid #e2e8f0;"></div>');
             var $wikilinkOpts = $('<div style="margin-top:10px; display:flex; flex-direction:column; gap:8px; background:#f8fafc; padding:12px; border-radius:6px; border:1px solid #e2e8f0;"></div>');
             var chkLink = currentData.link ? 'checked' : '';
             var chkLink = currentData.link ? 'checked' : '';
             var chkSplit = currentData.linkSplit ? 'checked' : '';
             var chkSplit = currentData.linkSplit ? 'checked' : '';
              
              
             $wikilinkOpts.append('<label style="display:flex; align-items:center; gap:6px; font-size:0.8rem; font-weight:700;"><input type="checkbox" id="md-inp-link" ' + chkLink + '> Wikilink Standard [[Judul]]</label>');
             $wikilinkOpts.append('<label style="display:flex; align-items:center; gap:8px; font-size:0.85rem;"><input type="checkbox" id="md-inp-link" ' + chkLink + '> Standard Wikilink [[Title]]</label>');
             $wikilinkOpts.append('<label style="display:flex; align-items:center; gap:6px; font-size:0.8rem; font-weight:700;"><input type="checkbox" id="md-inp-split" ' + chkSplit + '> Smart Split Wikilink</label>');
             $wikilinkOpts.append('<label style="display:flex; align-items:center; gap:8px; font-size:0.85rem;"><input type="checkbox" id="md-inp-split" ' + chkSplit + '> Smart Split Wikilink</label>');
             $wikilinkOpts.append('<div><label style="font-size:0.75rem; color:#475569;">Target Custom Link (Piped Link):</label><input type="text" id="md-inp-target" class="md-input" style="padding:5px 8px; font-size:0.82rem;" value="' + (currentData.linkTarget || '') + '"></div>');
             $wikilinkOpts.append('<div style="margin-top:4px;"><label style="font-size:0.8rem; color:#475569;">Custom Target (Piped Link):</label><input type="text" id="md-inp-target" class="md-input" style="padding:8px 10px; font-size:0.85rem;" value="' + (currentData.linkTarget || '') + '"></div>');
             $valBox.append($wikilinkOpts);
             $valBox.append($wikilinkOpts);
             $form.append($valBox);
             $formBody.append($valBox);


             // FITUR 1 & MEDIA SOSIAL (Handle Sanitizer)
             // Media Sosial
             var $socialSec = $('<div class="md-form-group" style="margin-top:14px;"></div>');
             var $socialSec = $('<div class="md-form-group" style="margin-top:20px;"></div>');
             var chkPerson = currentData.isPersonOrg ? 'checked' : '';
             var chkPerson = currentData.isPersonOrg ? 'checked' : '';
             $socialSec.append('<label style="display:flex; align-items:center; gap:6px; cursor:pointer; background:#e0e7ff; padding:8px; border-radius:8px; font-weight:700; color:#3730a3; font-size:0.82rem;"><input type="checkbox" id="md-chk-person" ' + chkPerson + '> Profil Media Sosial & Musik</label>');
             $socialSec.append('<label style="display:flex; align-items:center; gap:8px; cursor:pointer; background:#eff6ff; padding:10px; border-radius:6px; color:#1e40af; border:1px solid #bfdbfe;"><input type="checkbox" id="md-chk-person" ' + chkPerson + '> Enable Social Media & Music Links</label>');
              
              
             var $socialGrid = $('<div class="md-social-grid" id="md-social-box" style="display:' + (currentData.isPersonOrg ? 'grid' : 'none') + '; margin-top:6px;"></div>');
             var $socialGrid = $('<div class="md-social-grid" id="md-social-box" style="display:' + (currentData.isPersonOrg ? 'grid' : 'none') + '; margin-top:8px;"></div>');
             Object.keys(SOCIAL_PLATFORMS).forEach(function(key) {
             Object.keys(SOCIAL_PLATFORMS).forEach(function(key) {
                 var p = SOCIAL_PLATFORMS[key];
                 var p = SOCIAL_PLATFORMS[key];
                 var valUname = currentSocials[key] || '';
                 var valUname = currentSocials[key] || '';
                 var $socInp = $('<input type="text" id="md-soc-' + key + '" class="md-input" placeholder="ID / Username" value="' + valUname + '">');
                 var $socInp = $('<input type="text" id="md-soc-' + key + '" class="md-input" placeholder="Username / ID" value="' + valUname + '">');
                  
                  
                // AUTO SANITIZE PASTE URL TO HANDLE
                 $socInp.on('blur paste input', function() { $(this).val(cleanHandle($(this).val())); });
                 $socInp.on('blur paste input', function() {
                 $socialGrid.append('<div><label style="font-size:0.8rem;">' + p.name + '</label></div>').find('div').last().append($socInp);
                    var raw = $(this).val();
                    $(this).val(cleanHandle(raw));
                });
 
                 $socialGrid.append('<div><label style="font-size:0.75rem; color:#334155; font-weight:700;">' + p.name + '</label></div>').find('div').last().append($socInp);
             });
             });
             $socialSec.append($socialGrid);
             $socialSec.append($socialGrid);
             $form.append($socialSec);
             $formBody.append($socialSec);


             // REFERENSI & FITUR 9 (URL Title Auto-Fill)
             // Referensi
             var $refArea = $('<div class="md-form-group" style="margin-top:16px;"></div>');
             var $refArea = $('<div class="md-form-group" style="margin-top:20px;"></div>');
             $refArea.append('<label style="font-size:0.9rem; font-weight:800;">Sumber Referensi Spesifik</label>');
             $refArea.append('<label style="font-size:0.95rem; border-bottom:1px solid #e2e8f0; padding-bottom:6px;">Reference Source</label>');
             var $refGrid = $('<div class="md-ref-grid"></div>');
             var $refGrid = $('<div class="md-ref-grid" style="margin-top:10px;"></div>');
              
              
             var $typeSel = $('<select id="md-ref-type" class="md-select"><option value="">-- Pilih Tipe Utama --</option></select>');
             var $typeSel = $('<select id="md-ref-type" class="md-select"><option value="">-- Main Type --</option></select>');
             Object.keys(REF_TYPES).forEach(function(t) {
             Object.keys(REF_TYPES).forEach(function(t) {
                 var sel = currentRef.type === t ? 'selected' : '';
                 var sel = currentRef.type === t ? 'selected' : '';
Baris 588: Baris 714:
             });
             });


             var $subTypeSel = $('<select id="md-ref-subtype" class="md-select"><option value="">-- Sub-Tipe --</option></select>');
             var $subTypeSel = $('<select id="md-ref-subtype" class="md-select"><option value="">-- Sub Type --</option></select>');
             function updateSubTypes(mainType, selectedSub) {
             function updateSubTypes(mainType, selectedSub) {
                 $subTypeSel.empty().append('<option value="">-- Sub-Tipe --</option>');
                 $subTypeSel.empty().append('<option value="">-- Sub Type --</option>');
                 if (mainType && REF_TYPES[mainType]) {
                 if (mainType && REF_TYPES[mainType]) {
                     REF_TYPES[mainType].forEach(function(st) {
                     REF_TYPES[mainType].forEach(function(st) {
Baris 601: Baris 727:
             updateSubTypes(currentRef.type, currentRef.subType);
             updateSubTypes(currentRef.type, currentRef.subType);


             $refGrid.append('<div><label style="font-size:0.75rem;">Tipe Utama</label></div>').find('div').last().append($typeSel);
             $refGrid.append('<div><label style="font-size:0.8rem;">Main Type</label></div>').find('div').last().append($typeSel);
             $refGrid.append('<div><label style="font-size:0.75rem;">Sub-Tipe</label></div>').find('div').last().append($subTypeSel);
             $refGrid.append('<div><label style="font-size:0.8rem;">Sub Type</label></div>').find('div').last().append($subTypeSel);


             var $urlInput = $('<input type="url" id="md-ref-url" class="md-input" placeholder="https://..." value="' + (currentRef.url || '') + '">');
             var $urlInput = $('<input type="url" id="md-ref-url" class="md-input" placeholder="https://..." value="' + (currentRef.url || '') + '">');
Baris 609: Baris 735:
                 $(this).val(u);
                 $(this).val(u);
                 if (u && !$('#md-ref-site').val().trim()) $('#md-ref-site').val(extractDomain(u));
                 if (u && !$('#md-ref-site').val().trim()) $('#md-ref-site').val(extractDomain(u));
                 if (u && !$('#md-ref-title').val().trim()) $('#md-ref-title').val("Rujukan dari " + extractDomain(u));
                 if (u && !$('#md-ref-title').val().trim()) $('#md-ref-title').val("Reference from " + extractDomain(u));
             });
             });


             $refGrid.append('<div class="md-grid-full"><label style="font-size:0.75rem;">URL Rujukan</label></div>').find('div').last().append($urlInput);
             $refGrid.append('<div class="md-grid-full"><label style="font-size:0.8rem;">Reference URL</label></div>').find('div').last().append($urlInput);
             $refGrid.append('<div><label style="font-size:0.75rem;">Judul Artikel</label><input type="text" id="md-ref-title" class="md-input" value="' + (currentRef.title || '') + '"></div>');
             $refGrid.append('<div><label style="font-size:0.8rem;">Article Title</label><input type="text" id="md-ref-title" class="md-input" value="' + (currentRef.title || '') + '"></div>');
             $refGrid.append('<div><label style="font-size:0.75rem;">Nama Situs</label><input type="text" id="md-ref-site" class="md-input" value="' + (currentRef.site || '') + '"></div>');
             $refGrid.append('<div><label style="font-size:0.8rem;">Site Name</label><input type="text" id="md-ref-site" class="md-input" value="' + (currentRef.site || '') + '"></div>');
             $refGrid.append('<div><label style="font-size:0.75rem;">Penulis</label><input type="text" id="md-ref-author" class="md-input" value="' + (currentRef.author || '') + '"></div>');
             $refGrid.append('<div><label style="font-size:0.8rem;">Author</label><input type="text" id="md-ref-author" class="md-input" value="' + (currentRef.author || '') + '"></div>');
             $refGrid.append('<div><label style="font-size:0.75rem;">Penerbit</label><input type="text" id="md-ref-publisher" class="md-input" value="' + (currentRef.publisher || '') + '"></div>');
             $refGrid.append('<div><label style="font-size:0.8rem;">Publisher</label><input type="text" id="md-ref-publisher" class="md-input" value="' + (currentRef.publisher || '') + '"></div>');
              
              
             $refArea.append($refGrid);
             $refArea.append($refGrid);
             $form.append($refArea);
             $formBody.append($refArea);


            // FITUR 5: Live Form Preview Box
             var $previewBox = $('<div class="md-preview-card"><b>Live Preview:</b><div id="md-live-prev" style="margin-top:6px;"></div></div>');
             var $previewBox = $('<div class="md-preview-card"><b>📌 Pratinjau Tampilan:</b><div id="md-live-prev" style="margin-top:4px;"></div></div>');
             $formBody.append($previewBox);
             $form.append($previewBox);


            // ACTIONS
             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">Save Entry <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">Cancel</button>');
             $actions.append($btnSave).append($btnCancel);
           
            // FITUR 14: Fast Reset
            var $btnReset = $('<button class="md-btn md-btn-reset">Reset</button>');
            $btnReset.on('click', function() {
                $formBody.find('input[type="text"], input[type="url"]').val('');
                $formBody.find('input[type="checkbox"]').prop('checked', false);
                $formBody.find('select').prop('selectedIndex', 0);
                $('#md-social-box').hide();
                updateLivePreview();
            });
 
             $actions.append($btnSave).append($btnReset).append($btnCancel);
              
              
             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">Delete <span class="md-spinner"></span></button>');
                 $btnDelete.on('click', function() {
                 $btnDelete.on('click', function() {
                     if(confirm('Hapus item ini?')) {
                     if(confirm('Are you sure you want to delete this item?')) {
                         dataItems.splice(index, 1);
                         dataItems.splice(index, 1);
                         saveData('Hapus data', $(this), rawContent);
                         saveData('Hapus data', $(this), rawContent);
Baris 641: Baris 776:
                 $actions.append($btnDelete);
                 $actions.append($btnDelete);
             }
             }
             $form.append($actions);
            $formBody.append($actions);
             $form.append($formBody);
             $container.append($form);
             $container.append($form);


             // LIVE PREVIEW & DUPLICATE BINDINGS
             // BINDINGS & LIVE PREVIEW
            $toggleBtn.on('click', function() {
                $formBody.slideToggle(200);
                $(this).text($(this).text() === 'Minimize' ? 'Maximize' : 'Minimize');
            });
 
            function getFormState() {
                var socObj = {};
                if ($('#md-chk-person').is(':checked')) {
                    Object.keys(SOCIAL_PLATFORMS).forEach(k => {
                        var v = cleanHandle($('#md-soc-' + k).val());
                        if(v) socObj[k] = v;
                    });
                }
                return {
                    prop: $('#md-inp-prop').val(),
                    val: $('#md-inp-val').val(),
                    link: $('#md-inp-link').is(':checked'),
                    linkSplit: $('#md-inp-split').is(':checked'),
                    linkTarget: $('#md-inp-target').val(),
                    isPersonOrg: $('#md-chk-person').is(':checked'),
                    socials: socObj,
                    ref: {
                        type: $('#md-ref-type').val(), subType: $('#md-ref-subtype').val(),
                        url: $('#md-ref-url').val(), title: $('#md-ref-title').val(), site: $('#md-ref-site').val()
                    }
                };
            }
 
             function updateLivePreview() {
             function updateLivePreview() {
                 var p = $('#md-inp-prop').val().trim() || 'Label';
                 var p = $('#md-inp-prop').val().trim() || 'Label';
                 var v = $('#md-inp-val').val().trim() || 'Nilai';
                 var v = $('#md-inp-val').val().trim() || 'Value';
                $('#md-char-counter').text(v.length + " chars");
 
                // FITUR 9: Required Validator Highlight
                $('#md-inp-prop').toggleClass('md-error', !$('#md-inp-prop').val().trim());
                $('#md-inp-val').toggleClass('md-error', !$('#md-inp-val').val().trim());
 
                 var refT = $('#md-ref-title').val().trim();
                 var refT = $('#md-ref-title').val().trim();
                 var refU = $('#md-ref-url').val().trim();
                 var refU = $('#md-ref-url').val().trim();


                 var html = '<div><b>' + p + ':</b> ' + v + '</div>';
                 var html = '<div><b style="color:#64748b; text-transform:uppercase; font-size:0.8rem;">' + p + ':</b> <span style="font-weight:600; color:#0f172a;">' + v + '</span></div>';
                 if (refT || refU) {
                 if (refT || refU) { html += '<div style="font-size:0.75rem; color:#3b82f6; margin-top:4px; font-weight:600;">Ref: ' + (refT || refU) + '</div>'; }
                    html += '<div style="font-size:0.75rem; color:#6366f1; margin-top:2px;">📌 Rujukan: ' + (refT || refU) + '</div>';
                }
                 $('#md-live-prev').html(html);
                 $('#md-live-prev').html(html);


                // Duplicate Check
                 var isDup = dataItems.some(function(item, idx) { return idx !== index && item.prop.toLowerCase() === p.toLowerCase(); });
                 var isDup = dataItems.some(function(item, idx) {
                    return idx !== index && item.prop.toLowerCase() === p.toLowerCase();
                });
                 $('#md-dup-msg').toggle(isDup);
                 $('#md-dup-msg').toggle(isDup);
                // FITUR 3: Save state to localStorage
                if (isNew) localStorage.setItem('md_draft_' + pageName, JSON.stringify(getFormState()));
             }
             }
            // FITUR 1: Auto Capitalize Labels & FITUR 2: Smart Space Trimmer
            $form.find('input[type="text"]').on('blur', function() {
                var val = $(this).val().replace(/\s+/g, ' ').trim();
                if ($(this).attr('id') === 'md-inp-prop') {
                    val = val.replace(/\b\w/g, function(l) { return l.toUpperCase(); });
                }
                $(this).val(val);
                updateLivePreview();
            });


             $form.find('input, select').on('keyup input change', updateLivePreview);
             $form.find('input, select').on('keyup input change', updateLivePreview);
Baris 668: Baris 846:


             $('#md-chk-person').on('change', function() {
             $('#md-chk-person').on('change', function() {
                 if($(this).is(':checked')) $('#md-social-box').css('display', 'grid');
                 $('#md-social-box').css('display', $(this).is(':checked') ? 'grid' : 'none');
                 else $('#md-social-box').hide();
                 updateLivePreview();
             });
             });


             $('html, body').animate({ scrollTop: $form.offset().top - 60 }, 200);
             $('html, body').animate({ scrollTop: $form.offset().top - 40 }, 300, 'swing');
             $btnCancel.on('click', function() { $container.empty(); });
             $btnCancel.on('click', function() { $container.empty(); });


Baris 680: Baris 858:


                 if (!newProp || !newVal) {
                 if (!newProp || !newVal) {
                     alert("Label Data dan Nilai wajib diisi!"); return;
                     alert("Data Label and Value are required!"); return;
                 }
                 }


Baris 730: Baris 908:
             var jsonStr = JSON.stringify(dataItems, null, 2);
             var jsonStr = JSON.stringify(dataItems, null, 2);
             var $box = $('<div class="md-form-box"></div>');
             var $box = $('<div class="md-form-box"></div>');
             $box.append('<h4 class="md-form-title">Ekspor / Impor JSON Data</h4>');
             $box.append('<div class="md-form-header"><h4 class="md-form-title">JSON Import / Export</h4></div>');
              
              
             var $txt = $('<textarea class="md-textarea" rows="10" style="font-family:monospace; font-size:0.78rem;"></textarea>').val(jsonStr);
             var $txt = $('<textarea class="md-textarea" rows="12" style="font-family:monospace; font-size:0.8rem; background:#f8fafc;"></textarea>').val(jsonStr);
             $box.append($txt);
             $box.append($txt);


             var $actions = $('<div class="md-form-actions"></div>');
             var $actions = $('<div class="md-form-actions"></div>');
             var $btnImport = $('<button class="md-btn md-btn-save">Impor JSON & Simpan <span class="md-spinner"></span></button>');
             var $btnImport = $('<button class="md-btn md-btn-save">Import & Save <span class="md-spinner"></span></button>');
             var $btnCancel = $('<button class="md-btn md-btn-cancel">Tutup</button>');
             var $btnCancel = $('<button class="md-btn md-btn-cancel">Close</button>');


             $actions.append($btnImport).append($btnCancel);
             $actions.append($btnImport).append($btnCancel);
             $box.append($actions);
             $box.append($actions);
             $container.append($box);
             $container.append($box);
            $('html, body').animate({ scrollTop: $box.offset().top - 40 }, 300);


             $btnCancel.on('click', function() { $container.empty(); });
             $btnCancel.on('click', function() { $container.empty(); });
Baris 750: Baris 930:
                         dataItems = parsed;
                         dataItems = parsed;
                         saveData('Impor JSON', $(this), rawContent);
                         saveData('Impor JSON', $(this), rawContent);
                     } else { alert("Format JSON harus berupa Array!"); }
                     } else { alert("JSON format must be an Array!"); }
                 } catch(e) { alert("Format JSON salah: " + e.message); }
                 } catch(e) { alert("Invalid JSON: " + e.message); }
             });
             });
         }
         }