MediaWiki:Common.js: Perbedaan antara revisi

Admin (bicara | kontrib)
Tidak ada ringkasan suntingan
Tanda: Suntingan perangkat seluler Suntingan peramban seluler
Admin (bicara | kontrib)
Tidak ada ringkasan suntingan
 
(1 revisi perantara oleh pengguna yang sama tidak ditampilkan)
Baris 2: Baris 2:
  * ====================================================================
  * ====================================================================
  * MIPPEDIA DATA - ADVANCED STRUCTURED DATA SYSTEM (V8.7 SECURE ULTIMATE)
  * MIPPEDIA DATA - ADVANCED STRUCTURED DATA SYSTEM (V8.7 SECURE ULTIMATE)
  * Fitur: ALL ORIGINAL V8.6 FEATURES + Social Header Separation,
  * Fitur: ALL ORIGINAL V8 FEATURES KEPT + Global Social Media Box,  
  * Smart Media URL Preview (YouTube/Spotify/Images), Wiki Autocomplete,
  * Smart Autocomplete/Preview Wikilink, Ultra-Strict Security, Mobile Ready.
* Ultra-Strict OWASP XSS Security, Mobile Optimized.
  * ====================================================================
  * ====================================================================
  */
  */
Baris 78: Baris 77:
         var svgHelp = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"></circle><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>';
         var svgHelp = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"></circle><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>';


         // 4. HELPER UTILITY & ADVANCED SECURITY (Tingkat Tinggi OWASP)
         // 4. HELPER UTILITY & ADVANCED SECURITY
         function sanitizeHTML(str) {
         function sanitizeHTML(str) {
             if (typeof str !== 'string') return str;
             if (typeof str !== 'string') return str;
Baris 128: Baris 127:
         }
         }


         // 5. STYLESHEET  
         // 5. STYLESHEET (TERMASUK LAYOUT MEDIA SOSIAL GLOBAL & AUTOCOMPLETE)
         var css = `
         var css = `
             #mippedia-data-app {
             #mippedia-data-app {
Baris 152: Baris 151:
             .md-search-input:focus { background: #fff; border-color: #4f46e5; outline: none; }
             .md-search-input:focus { background: #fff; border-color: #4f46e5; outline: none; }
             .md-search-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: #94a3b8; }
             .md-search-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: #94a3b8; }
            /* Kotak Global Sosial Media Terpisah di Atas */
            .md-global-social-box { background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 12px; padding: 16px; margin-bottom: 16px; box-shadow: 0 2px 8px rgba(0,0,0,0.02); }
            .md-social-box-title { font-size: 0.85rem; font-weight: 800; color: #475569; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 12px; border-bottom: 1px dashed #cbd5e1; padding-bottom: 8px; }
            .md-social-group { display: flex; flex-wrap: wrap; gap: 8px; }
            .md-social-badge { display: inline-flex; align-items: center; gap: 6px; padding: 6px 12px; border-radius: 20px; font-size: 0.8rem; font-weight: 600; color: white !important; text-decoration: none !important; transition: transform 0.2s, opacity 0.2s; box-shadow: 0 2px 4px rgba(0,0,0,0.05); }
            .md-social-badge:hover { transform: translateY(-2px); opacity: 0.92; }


             .md-row-display { display: flex; justify-content: space-between; align-items: flex-start; padding: 16px 12px; border-bottom: 1px solid #f1f5f9; transition: all 0.2s; border-radius: 12px; gap: 12px; margin-bottom: 8px; background: #fff; }
             .md-row-display { display: flex; justify-content: space-between; align-items: flex-start; padding: 16px 12px; border-bottom: 1px solid #f1f5f9; transition: all 0.2s; border-radius: 12px; gap: 12px; margin-bottom: 8px; background: #fff; }
Baris 159: Baris 165:
             .md-data-label { font-size: 0.72rem; color: #64748b; font-weight: 800; text-transform: uppercase; letter-spacing: 0.6px; }
             .md-data-label { font-size: 0.72rem; color: #64748b; font-weight: 800; text-transform: uppercase; letter-spacing: 0.6px; }
             .md-data-value-wrap { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
             .md-data-value-wrap { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
             .md-data-value { font-size: 1.02rem; color: #0f172a; line-height: 1.4; font-weight: 600; word-break: break-word; width: 100%; }
             .md-data-value { font-size: 1.02rem; color: #0f172a; line-height: 1.4; font-weight: 600; word-break: break-word; }
              
              
             .md-btn-copy { background: none; border: none; color: #94a3b8; cursor: pointer; padding: 2px 4px; border-radius: 4px; transition: 0.2s; display: inline-flex; align-items: center; }
             .md-btn-copy { background: none; border: none; color: #94a3b8; cursor: pointer; padding: 2px 4px; border-radius: 4px; transition: 0.2s; display: inline-flex; align-items: center; }
             .md-btn-copy:hover { color: #4f46e5; background: #e0e7ff; }
             .md-btn-copy:hover { color: #4f46e5; background: #e0e7ff; }
            .md-social-group { display: flex; flex-wrap: wrap; gap: 6px; }
            .md-social-badge { display: inline-flex; align-items: center; gap: 6px; padding: 5px 11px; border-radius: 18px; font-size: 0.78rem; font-weight: 600; color: white; text-decoration: none; transition: transform 0.2s, opacity 0.2s; box-shadow: 0 2px 4px rgba(0,0,0,0.05); }
            .md-social-badge:hover { transform: translateY(-2px); opacity: 0.92; color: white; text-decoration: none; }


             .md-ref-container { margin-top: 10px; width: 100%; box-sizing: border-box; }
             .md-ref-container { margin-top: 10px; width: 100%; box-sizing: border-box; }
Baris 186: Baris 188:
             .md-form-box { background: #ffffff; border-radius: 12px; padding: 18px; margin-top: 16px; border: 1px solid #cbd5e1; box-shadow: 0 4px 16px rgba(0,0,0,0.05); }
             .md-form-box { background: #ffffff; border-radius: 12px; padding: 18px; margin-top: 16px; border: 1px solid #cbd5e1; box-shadow: 0 4px 16px rgba(0,0,0,0.05); }
             .md-form-title { font-size: 1.05rem; font-weight: 800; color: #0f172a; margin: 0 0 16px 0; border-bottom: 2px solid #e2e8f0; padding-bottom: 8px; }
             .md-form-title { font-size: 1.05rem; font-weight: 800; color: #0f172a; margin: 0 0 16px 0; border-bottom: 2px solid #e2e8f0; padding-bottom: 8px; }
             .md-form-group { margin-bottom: 14px; position: relative; }
             .md-form-group { margin-bottom: 14px; }
             .md-form-group label { display: flex; align-items:center; font-size: 0.82rem; font-weight: 700; margin-bottom: 6px; color: #334155; }
             .md-form-group label { display: flex; align-items:center; font-size: 0.82rem; font-weight: 700; margin-bottom: 6px; color: #334155; }
           
             .md-input, .md-select, .md-textarea { width: 100%; padding: 9px 12px; border: 1px solid #cbd5e1; border-radius: 8px; box-sizing: border-box; font-family: inherit; font-size: 0.9rem; transition: 0.2s; background: #fff; }
             .md-input, .md-select, .md-textarea { width: 100%; padding: 9px 12px; border: 1px solid #cbd5e1; border-radius: 8px; box-sizing: border-box; font-family: inherit; font-size: 0.9rem; transition: 0.2s; background: #fff; }
             .md-input:focus, .md-select:focus, .md-textarea:focus { border-color: #4f46e5; outline: none; box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.15); }
             .md-input:focus, .md-select:focus, .md-textarea:focus { border-color: #4f46e5; outline: none; box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.15); }
              
              
             .md-autocomplete-list { position: absolute; top: 100%; left: 0; right: 0; z-index: 100; background: #fff; border: 1px solid #cbd5e1; border-radius: 8px; max-height: 200px; overflow-y: auto; box-shadow: 0 4px 12px rgba(0,0,0,0.1); margin-top: 4px; display: none; }
            /* Autocomplete Styles */
             .md-autocomplete-item { padding: 8px 12px; cursor: pointer; border-bottom: 1px solid #f1f5f9; font-size: 0.85rem; color: #0f172a; transition: background 0.2s; }
            .md-autocomplete-wrapper { position: relative; width: 100%; }
             .md-autocomplete-item:hover { background: #f8fafc; color: #4f46e5; font-weight: 600; }
             .md-autocomplete-list { position: absolute; top: 100%; left: 0; right: 0; background: #fff; border: 1px solid #cbd5e1; border-radius: 0 0 8px 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); z-index: 1000; display: none; max-height: 200px; overflow-y: auto; margin-top: -2px; }
             .md-autocomplete-item { padding: 10px 12px; font-size: 0.88rem; color: #0f172a; cursor: pointer; border-bottom: 1px solid #f1f5f9; transition: 0.2s; display: flex; align-items: center; gap: 8px; }
            .md-autocomplete-item:last-child { border-bottom: none; }
             .md-autocomplete-item:hover { background: #f8fafc; color: #4f46e5; padding-left: 16px; }


             .md-ref-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; background: #f8fafc; padding: 12px; border-radius: 8px; border: 1px dashed #cbd5e1; }
             .md-ref-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; background: #f8fafc; padding: 12px; border-radius: 8px; border: 1px dashed #cbd5e1; }
Baris 265: Baris 271:
                 var leftPos = offset.left - 15;
                 var leftPos = offset.left - 15;
                  
                  
                 if (leftPos + ttWidth > winWidth - 15) { leftPos = winWidth - ttWidth - 15; }
                 if (leftPos + ttWidth > winWidth - 15) leftPos = winWidth - ttWidth - 15;
                 if (leftPos < 10) { leftPos = 10; }
                 if (leftPos < 10) leftPos = 10;
                  
                  
                 $tt.css({ top: offset.top - $tt.outerHeight() - 10, left: leftPos }).fadeIn(200);
                 $tt.css({ top: offset.top - $tt.outerHeight() - 10, left: leftPos }).fadeIn(200);
Baris 293: Baris 299:
         function buildWikitextVal(item) {
         function buildWikitextVal(item) {
             var val = item.val;
             var val = item.val;
            if (item.isMedia) return val; // Raw output agar wikitext tidak rusak oleh iframe
           
             if (item.linkTarget && item.linkTarget.trim() !== '') {
             if (item.linkTarget && item.linkTarget.trim() !== '') {
                 return "[[" + item.linkTarget.trim() + "|" + val + "]]";
                 return "[[" + item.linkTarget.trim() + "|" + val + "]]";
Baris 393: Baris 397:
             $app.append($header);
             $app.append($header);


            // Tampilkan kotak pencarian jika data banyak
             if (dataItems.length > 2) {
             if (dataItems.length > 2) {
                 var $searchBox = $('<div class="md-search-box"><span class="md-search-icon">' + svgSearch + '</span><input type="text" class="md-search-input" placeholder="Cari data terstruktur..."></div>');
                 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>');
Baris 405: Baris 410:
             }
             }


             // Ekstrak dan Tampilkan Kotak Media Sosial Terpisah (Di Atas)
             // MENGUMPULKAN MEDIA SOSIAL GLOBAL (Memisahkan media sosial ke atas)
             var socialDataObj = null;
             var globalSocials = {};
             dataItems.forEach(function(it) {
             dataItems.forEach(function(item) {
                 if (it.isPersonOrg && it.socials && Object.keys(it.socials).length > 0) { socialDataObj = it.socials; }
                 if (item.isPersonOrg && item.socials) {
                    Object.assign(globalSocials, item.socials);
                }
             });
             });


             if (socialDataObj) {
             if (Object.keys(globalSocials).length > 0) {
                 var $socialHeaderBox = $('<div style="background: linear-gradient(135deg, #f8fafc, #f1f5f9); padding: 15px; border-radius: 12px; border: 1px solid #e2e8f0; margin-bottom: 20px; display: flex; flex-direction: column; gap: 10px; align-items: center; box-shadow: 0 2px 10px rgba(0,0,0,0.02);"></div>');
                 var $topSocials = $('<div class="md-global-social-box"></div>');
                 $socialHeaderBox.append('<span style="font-size: 0.8rem; font-weight: 800; color: #64748b; text-transform: uppercase; letter-spacing: 1px;">Profil Media Sosial & Musik Resmi</span>');
                 $topSocials.append('<div class="md-social-box-title">Media Sosial & Platform Resmi</div>');
                 var $socialGroupGlobal = $('<div class="md-social-group" style="justify-content: center;"></div>');
                 var $socialGroupGlobal = $('<div class="md-social-group"></div>');
                  
                  
                 Object.keys(SOCIAL_PLATFORMS).forEach(function(key) {
                 Object.keys(SOCIAL_PLATFORMS).forEach(function(key) {
                     var uname = socialDataObj[key];
                     var uname = globalSocials[key];
                     if (uname) {
                     if (uname) {
                         var platform = SOCIAL_PLATFORMS[key];
                         var platform = SOCIAL_PLATFORMS[key];
Baris 425: Baris 432:
                     }
                     }
                 });
                 });
                 $socialHeaderBox.append($socialGroupGlobal);
                 $topSocials.append($socialGroupGlobal);
                 $app.append($socialHeaderBox);
                 $app.append($topSocials);
             }
             }


Baris 441: Baris 448:
                     var rawVal = item.val || '';
                     var rawVal = item.val || '';


                    // Deteksi Pratinjau Media Cerdas
                     if (rawVal.match(/^https?:\/\//i)) {
                     if (item.isMedia && rawVal.match(/^https?:\/\//i)) {
                         displayValHtml = '<a href="' + sanitizeHTML(rawVal) + '" target="_blank" rel="noopener noreferrer" style="color:#2563eb; font-weight:700; text-decoration:none;">' + extractDomain(rawVal) + '</a>';
                         if (rawVal.match(/\.(jpeg|jpg|gif|png|webp|bmp)$/i)) {
                    } else if (item.linkTarget && item.linkTarget.trim() !== '') {
                            displayValHtml = '<img src="' + sanitizeHTML(rawVal) + '" style="max-width:100%; border-radius:8px; margin-top:6px; box-shadow:0 2px 8px rgba(0,0,0,0.1);">';
                        displayValHtml = '<a href="' + mw.util.getUrl(item.linkTarget.trim()) + '" style="color:#2563eb; font-weight:700; text-decoration:none;">' + sanitizeHTML(rawVal) + '</a>';
                        } else if (rawVal.match(/(youtube\.com|youtu\.be)/i)) {
                    } else if (item.linkSplit) {
                            var ytId = rawVal.match(/(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))([\w-]{11})/);
                        var parts = rawVal.split(/([,/\s]+dan\s+|[,/\s]+atau\s+|[,/]+)/gi);
                            if(ytId) {
                        displayValHtml = parts.map(function(p) {
                                displayValHtml = '<div style="position:relative; width:100%; padding-bottom:56.25%; height:0; border-radius:8px; overflow:hidden; margin-top:6px;"><iframe src="https://www.youtube.com/embed/' + ytId[1] + '" style="position:absolute; top:0; left:0; width:100%; height:100%; border:0;" allowfullscreen></iframe></div>';
                             var trimmed = p.trim();
                            } else { displayValHtml = '<a href="' + sanitizeHTML(rawVal) + '" target="_blank">' + sanitizeHTML(rawVal) + '</a>'; }
                            if (trimmed && !p.match(/^[,/\s]+$/) && trimmed.toLowerCase() !== 'dan' && trimmed.toLowerCase() !== 'atau') {
                        } else if (rawVal.match(/spotify\.com/i)) {
                                return '<a href="' + mw.util.getUrl(trimmed) + '" style="color:#2563eb; font-weight:700; text-decoration:none;">' + sanitizeHTML(trimmed) + '</a>';
                             var spotUrl = rawVal.replace('spotify.com/', 'spotify.com/embed/');
                            }
                            displayValHtml = '<iframe src="' + sanitizeHTML(spotUrl) + '" width="100%" height="152" frameborder="0" allowtransparency="true" allow="encrypted-media" style="border-radius:8px; margin-top:6px;"></iframe>';
                            return sanitizeHTML(p);
                         } else {
                         }).join('');
                            displayValHtml = '<a href="' + sanitizeHTML(rawVal) + '" target="_blank" class="md-btn-visit" style="margin-top:6px;">Buka URL Pratinjau</a>';
                    } else if (item.link) {
                        }
                        displayValHtml = '<a href="' + mw.util.getUrl(rawVal) + '" style="color:#2563eb; font-weight:700; text-decoration:none;">' + sanitizeHTML(rawVal) + '</a>';
                     } else {
                     } else {
                         // Sistem Link Biasa
                         displayValHtml = sanitizeHTML(rawVal);
                        if (rawVal.match(/^https?:\/\//i)) {
                            displayValHtml = '<a href="' + sanitizeHTML(rawVal) + '" target="_blank" rel="noopener noreferrer" style="color:#2563eb; font-weight:700; text-decoration:none;">' + extractDomain(rawVal) + '</a>';
                        } else if (item.linkTarget && item.linkTarget.trim() !== '') {
                            displayValHtml = '<a href="' + mw.util.getUrl(item.linkTarget.trim()) + '" style="color:#2563eb; font-weight:700; text-decoration:none;">' + sanitizeHTML(rawVal) + '</a>';
                        } else if (item.linkSplit) {
                            var parts = rawVal.split(/([,/\s]+dan\s+|[,/\s]+atau\s+|[,/]+)/gi);
                            displayValHtml = parts.map(function(p) {
                                var trimmed = p.trim();
                                if (trimmed && !p.match(/^[,/\s]+$/) && trimmed.toLowerCase() !== 'dan' && trimmed.toLowerCase() !== 'atau') {
                                    return '<a href="' + mw.util.getUrl(trimmed) + '" style="color:#2563eb; font-weight:700; text-decoration:none;">' + sanitizeHTML(trimmed) + '</a>';
                                }
                                return sanitizeHTML(p);
                            }).join('');
                        } else if (item.link) {
                            displayValHtml = '<a href="' + mw.util.getUrl(rawVal) + '" style="color:#2563eb; font-weight:700; text-decoration:none;">' + sanitizeHTML(rawVal) + '</a>';
                        } else {
                            displayValHtml = sanitizeHTML(rawVal);
                        }
                     }
                     }


                     var $valWrap = $('<div class="md-data-value-wrap"></div>');
                     var $valWrap = $('<div class="md-data-value-wrap"></div>');
                     $valWrap.append('<div class="md-data-value">' + displayValHtml + '</div>');
                     $valWrap.append('<span class="md-data-value">' + displayValHtml + '</span>');
                      
                      
                     if (!item.isMedia) {
                     var $copyBtn = $('<button class="md-btn-copy" title="Salin teks">' + svgCopy + '</button>');
                        var $copyBtn = $('<button class="md-btn-copy" title="Salin teks">' + svgCopy + '</button>');
                    $copyBtn.on('click', function() {
                        $copyBtn.on('click', function() {
                        navigator.clipboard.writeText(rawVal);
                            navigator.clipboard.writeText(rawVal);
                        $(this).css('color', '#16a34a');
                            $(this).css('color', '#16a34a');
                        setTimeout(function(){ $copyBtn.css('color', '#94a3b8'); }, 1200);
                            setTimeout(function(){ $copyBtn.css('color', '#94a3b8'); }, 1200);
                    });
                        });
                    $valWrap.append($copyBtn);
                        $valWrap.append($copyBtn);
                    }
                     $info.append($valWrap);
                     $info.append($valWrap);
                      
                      
Baris 561: Baris 548:
              
              
             var draftData = localStorage.getItem(DRAFT_KEY);
             var draftData = localStorage.getItem(DRAFT_KEY);
             var currentData = isNew ? { prop: '', val: '', link: false, linkTarget: '', linkSplit: false, isMedia: false, ref: {}, isPersonOrg: false, socials: {} } : dataItems[index];
             var currentData = isNew ? { prop: '', val: '', link: false, linkTarget: '', linkSplit: false, ref: {}, isPersonOrg: false, socials: {} } : dataItems[index];
              
              
             if (isNew && draftData) {
             if (isNew && draftData) {
Baris 589: Baris 576:
              
              
             var $valBox = $('<div class="md-form-group"></div>');
             var $valBox = $('<div class="md-form-group"></div>');
             $valBox.append('<label>Nilai Data <span class="md-help-icon" data-help="Isi dari data tersebut. Ketik huruf pertama untuk memunculkan pratinjau halaman wiki secara otomatis.">' + svgHelp + '</span></label>');
             $valBox.append('<label>Nilai Data <span class="md-help-icon" data-help="Ketik nilai. Jika halaman sudah ada di Wiki, saran akan muncul (pratinjau) dan otomatis mencentang opsi Wikilink.">' + svgHelp + '</span></label>');
              
              
            // FITUR AUTOCOMPLETE PRATINJAU DIMULAI DI SINI
            var $autoWrap = $('<div class="md-autocomplete-wrapper"></div>');
             var $valInput = $('<input type="text" id="md-inp-val" class="md-input" autocomplete="off" value="' + sanitizeHTML(currentData.val || '') + '">');
             var $valInput = $('<input type="text" id="md-inp-val" class="md-input" autocomplete="off" value="' + sanitizeHTML(currentData.val || '') + '">');
             var $autoCompleteBox = $('<div class="md-autocomplete-list"></div>');
             var $autoList = $('<div class="md-autocomplete-list"></div>');
           
             $autoWrap.append($valInput).append($autoList);
             $valBox.append($valInput).append($autoCompleteBox);
            $valBox.append($autoWrap);
              
              
             var $wikilinkOpts = $('<div style="margin-top:8px; display:flex; flex-direction:column; gap:8px; background:#f8fafc; padding:10px; border-radius:8px; border:1px solid #e2e8f0;"></div>');
             var $wikilinkOpts = $('<div style="margin-top:8px; display:flex; flex-direction:column; gap:8px; background:#f8fafc; padding:10px; border-radius:8px; border:1px solid #e2e8f0;"></div>');
             var chkLink = currentData.link ? 'checked' : '';
             var chkLink = currentData.link ? 'checked' : '';
             var chkSplit = currentData.linkSplit ? 'checked' : '';
             var chkSplit = currentData.linkSplit ? 'checked' : '';
            var chkMedia = currentData.isMedia ? 'checked' : '';
           
            $wikilinkOpts.append('<label style="display:flex; align-items:center; gap:8px; font-size:0.82rem; font-weight:700; cursor:pointer;"><input type="checkbox" id="md-inp-link" ' + chkLink + '> Jadikan Wikilink Standar</label>');
            $wikilinkOpts.append('<label style="display:flex; align-items:center; gap:8px; font-size:0.82rem; font-weight:700; cursor:pointer;"><input type="checkbox" id="md-inp-split" ' + chkSplit + '> Smart Split Wikilink</label>');
              
              
             // Opsi Pratinjau Media Baru
             var $linkCheck = $('<input type="checkbox" id="md-inp-link" ' + chkLink + '>');
            $wikilinkOpts.append('<hr style="border:0; border-top:1px dashed #cbd5e1; margin:4px 0;"><label style="display:flex; align-items:center; gap:8px; font-size:0.82rem; font-weight:800; color:#3730a3; cursor:pointer;"><input type="checkbox" id="md-inp-media" ' + chkMedia + '> Render URL Sbg Pratinjau Gambar/Video/Musik <span class="md-help-icon" data-help="Jika ini dicentang dan Nilai Data adalah URL Gambar atau YouTube/Spotify, sistem akan otomatis merendernya menjadi gambar atau pemutar langsung.">' + svgHelp + '</span></label>');
             var $lblLinkCheck = $('<label style="display:flex; align-items:center; gap:8px; font-size:0.82rem; font-weight:700; cursor:pointer;"></label>');
           
             $lblLinkCheck.append($linkCheck).append(' Jadikan Wikilink Standar');
             var $pipedGroup = $('<div style="margin-top:4px;"><label style="font-size:0.78rem; color:#475569; margin-bottom:4px;">Target Halaman Khusus (Opsional):</label><input type="text" id="md-inp-target" class="md-input" style="padding:6px 10px; font-size:0.85rem;" placeholder="Contoh: Diskografi Rumi Haitami" value="' + sanitizeHTML(currentData.linkTarget || '') + '"></div>');
             $wikilinkOpts.append($pipedGroup);
            $valBox.append($wikilinkOpts);
            $form.append($valBox);


             // AUTOCOMPLETE & SMART MEDIA URL DETECTOR
             // Logika pratinjau ketika diketik
             var acTimer;
             var isAutoChecked = false;  
             $valInput.on('input', function() {
             $valInput.on('input', function() {
                 var v = $(this).val();
                 var q = $(this).val();
                  
                  
                 // Smart Media URL Detector
                 // Jika input dikosongkan/berubah, kembalikan centang link ke false jika tadinya otomatis centang
                 if (v.match(/\.(jpeg|jpg|gif|png|webp|bmp)$/i) || v.match(/(youtube\.com|youtu\.be|spotify\.com)/i)) {
                 if (isAutoChecked) {
                     $('#md-inp-media').prop('checked', true);
                     $linkCheck.prop('checked', false);
                    isAutoChecked = false;
                 }
                 }


                // Autocomplete Logic
                 if(q.length < 1) {
                clearTimeout(acTimer);
                     $autoList.empty().hide();
                 if (v.length < 1) {
                     $autoCompleteBox.hide();
                    $('#md-inp-link').prop('checked', false);
                     return;
                     return;
                 }
                 }
                  
 
                 acTimer = setTimeout(function() {
                 // Panggil API MediaWiki Prefix Search
                    api.get({ action: 'opensearch', search: v, limit: 6, namespace: 0, format: 'json' }).done(function(data) {
                 api.get({
                        var titles = data[1];
                    action: 'query',
                        $autoCompleteBox.empty();
                    list: 'prefixsearch',
                        if (titles.length > 0) {
                    pssearch: q,
                            titles.forEach(function(t) {
                    pslimit: 6,
                                var $item = $('<div class="md-autocomplete-item">' + sanitizeHTML(t) + '</div>');
                    format: 'json'
                                $item.on('click', function() {
                }).done(function(data) {
                                    $valInput.val(t);
                    $autoList.empty();
                                    $('#md-inp-link').prop('checked', true);
                    if(data.query && data.query.prefixsearch && data.query.prefixsearch.length > 0) {
                                    $autoCompleteBox.hide();
                        data.query.prefixsearch.forEach(function(res) {
                                 });
                            var $item = $('<div class="md-autocomplete-item">' + svgSearch + ' ' + sanitizeHTML(res.title) + '</div>');
                                $autoCompleteBox.append($item);
                            $item.on('click', function() {
                                $valInput.val(res.title);
                                $autoList.empty().hide();
                                $linkCheck.prop('checked', true); // OTOMATIS TERCENTANG
                                 isAutoChecked = true;
                             });
                             });
                             $autoCompleteBox.show();
                             $autoList.append($item);
                        } else { $autoCompleteBox.hide(); }
                        });
                    });
                        $autoList.show();
                }, 300);
                    } else {
                        $autoList.hide();
                    }
                });
            });
 
            // Sembunyikan pratinjau saat klik di luar
            $(document).on('click.mdauto', function(e) {
                if(!$(e.target).closest('.md-autocomplete-wrapper').length) {
                    $autoList.hide();
                }
             });
             });
             $(document).on('click.mdac', function(e) { if(!$(e.target).closest($valBox).length) $autoCompleteBox.hide(); });
 
             $wikilinkOpts.append($lblLinkCheck);
            $wikilinkOpts.append('<label style="display:flex; align-items:center; gap:8px; font-size:0.82rem; font-weight:700; cursor:pointer;"><input type="checkbox" id="md-inp-split" ' + chkSplit + '> Smart Split Wikilink</label>');
            var $pipedGroup = $('<div style="margin-top:4px;"><label style="font-size:0.78rem; color:#475569; margin-bottom:4px;">Target Halaman Khusus (Opsional):</label><input type="text" id="md-inp-target" class="md-input" style="padding:6px 10px; font-size:0.85rem;" placeholder="Contoh: Diskografi Rumi Haitami" value="' + sanitizeHTML(currentData.linkTarget || '') + '"></div>');
            $wikilinkOpts.append($pipedGroup);
            $valBox.append($wikilinkOpts);
            $form.append($valBox);


             var $socialSec = $('<div class="md-form-group" style="margin-top:16px;"></div>');
             var $socialSec = $('<div class="md-form-group" style="margin-top:16px;"></div>');
             var chkPerson = currentData.isPersonOrg ? 'checked' : '';
             var chkPerson = currentData.isPersonOrg ? 'checked' : '';
             $socialSec.append('<label style="display:flex; align-items:center; gap:8px; cursor:pointer; background:#e0e7ff; padding:10px; border-radius:8px; font-weight:700; color:#3730a3; font-size:0.85rem;"><input type="checkbox" id="md-chk-person" ' + chkPerson + '> Aktifkan Profil Media Sosial & Musik <span class="md-help-icon" data-help="Media sosial akan otomatis dipisah dan ditampilkan rapi di atas semua rincian data Anda.">' + svgHelp + '</span></label>');
             $socialSec.append('<label style="display:flex; align-items:center; gap:8px; cursor:pointer; background:#e0e7ff; padding:10px; border-radius:8px; font-weight:700; color:#3730a3; font-size:0.85rem;"><input type="checkbox" id="md-chk-person" ' + chkPerson + '> Simpan Profil Media Sosial (Akan muncul di paling atas) <span class="md-help-icon" data-help="Centang ini untuk mengisi data sosial media. Datanya akan dihimpun dan dimunculkan di kotak teratas data struktur.">' + svgHelp + '</span></label>');
              
              
             var $socialGrid = $('<div class="md-social-grid" id="md-social-box" style="display:' + (currentData.isPersonOrg ? 'grid' : 'none') + '; margin-top:8px;"></div>');
             var $socialGrid = $('<div class="md-social-grid" id="md-social-box" style="display:' + (currentData.isPersonOrg ? 'grid' : 'none') + '; margin-top:8px;"></div>');
Baris 665: Baris 664:


             var $refArea = $('<div class="md-form-group" style="margin-top:20px;"></div>');
             var $refArea = $('<div class="md-form-group" style="margin-top:20px;"></div>');
             $refArea.append('<label style="font-size:0.95rem; font-weight:800; color:#0f172a; margin-bottom:10px;">Sumber Referensi <span class="md-help-icon" data-help="Jika URL rujukan mengarah ke situs web terverifikasi, lencana centang biru otomatis muncul.">' + svgHelp + '</span></label>');
             $refArea.append('<label style="font-size:0.95rem; font-weight:800; color:#0f172a; margin-bottom:10px;">Sumber Referensi <span class="md-help-icon" data-help="Mohon berikan sumber referensi yang valid, kredibel, dan dapat dipertanggungjawabkan (seperti portal berita resmi, jurnal akademik ilmiah, buku cetak ber-ISBN, atau pangkalan data terverifikasi). Referensi yang kuat sangat membantu memastikan keakuratan informasi. Jika URL rujukan Anda mengarah ke situs web besar yang terverifikasi, sistem keamanan akan secara otomatis menampilkan lencana centang biru premium sebagai tanda sumber tepercaya.">' + svgHelp + '</span></label>');
             var $refGrid = $('<div class="md-ref-grid"></div>');
             var $refGrid = $('<div class="md-ref-grid"></div>');
              
              
Baris 718: Baris 717:
             $form.append(`
             $form.append(`
                 <div class="md-cc0-notice">
                 <div class="md-cc0-notice">
                     <b>Perhatian Lisensi:</b> Setiap data yang diterbitkan di sini akan dirilis di bawah lisensi <b>Creative Commons Zero (CC0)</b>.
                     <b>Perhatian Lisensi:</b> Setiap data yang diterbitkan di sini akan terlihat publik dan dirilis di bawah lisensi <b>Creative Commons Zero (CC0)</b>. Jika Anda tidak ingin suntingan Anda digunakan secara bebas dan publik tanpa batas, jangan klik Terbitkan. <span class="md-help-icon" data-help="Dengan menerbitkan, Anda melepaskan seluruh hak cipta atas input data ini ke domain publik murni sesuai prinsip Wikidata.">${svgHelp}</span>
                 </div>
                 </div>
                 <div class="md-edit-meta">
                 <div class="md-edit-meta">
                     <label style="font-size:0.85rem; font-weight:700;">Ringkasan Suntingan</label>
                     <label style="font-size:0.85rem; font-weight:700;">Ringkasan Suntingan <span class="md-help-icon" data-help="Jelaskan secara singkat apa yang Anda ubah atau tambahkan. Contoh: Menambah data tanggal lahir rumi.">${svgHelp}</span></label>
                     <input type="text" id="md-edit-summary" class="md-input" placeholder="Jelaskan perubahan Anda secara singkat...">
                     <input type="text" id="md-edit-summary" class="md-input" placeholder="Jelaskan perubahan Anda secara singkat...">
                     <label style="font-size:0.85rem; cursor:pointer; font-weight:700; margin-top:5px; display:inline-flex; align-items:center; gap:6px;"><input type="checkbox" id="md-minor-edit"> Ini adalah suntingan kecil (minor)</label>
                     <label style="font-size:0.85rem; cursor:pointer; font-weight:700; margin-top:5px; display:inline-flex; align-items:center; gap:6px;"><input type="checkbox" id="md-minor-edit"> Ini adalah suntingan kecil (minor)</label>
Baris 765: Baris 764:


             $('html, body').animate({ scrollTop: $form.offset().top - 60 }, 250);
             $('html, body').animate({ scrollTop: $form.offset().top - 60 }, 250);
             $btnCancel.on('click', function() { $(document).off('click.mdac'); $container.empty(); });
             $btnCancel.on('click', function() { $container.empty(); });


             $btnDraft.on('click', function() {
             $btnDraft.on('click', function() {
Baris 781: Baris 780:
                     linkSplit: $('#md-inp-split').is(':checked'),
                     linkSplit: $('#md-inp-split').is(':checked'),
                     linkTarget: $('#md-inp-target').val().trim(),
                     linkTarget: $('#md-inp-target').val().trim(),
                    isMedia: $('#md-inp-media').is(':checked'),
                     isPersonOrg: $('#md-chk-person').is(':checked'),
                     isPersonOrg: $('#md-chk-person').is(':checked'),
                     socials: socObj,
                     socials: socObj,
Baris 831: Baris 829:
                     linkSplit: $('#md-inp-split').is(':checked'),
                     linkSplit: $('#md-inp-split').is(':checked'),
                     linkTarget: $('#md-inp-target').val().trim(),
                     linkTarget: $('#md-inp-target').val().trim(),
                    isMedia: $('#md-inp-media').is(':checked'),
                     isPersonOrg: isPerson,
                     isPersonOrg: isPerson,
                     socials: socObj,
                     socials: socObj,
Baris 1.360: Baris 1.357:
             '<div style="font-size:11px; color:#718096; font-weight:800; text-transform:uppercase; letter-spacing:1px; margin-bottom:10px;">Lisensi, Atribusi & Penggunaan Data</div>' +
             '<div style="font-size:11px; color:#718096; font-weight:800; text-transform:uppercase; letter-spacing:1px; margin-bottom:10px;">Lisensi, Atribusi & Penggunaan Data</div>' +
             '<p style="margin:0 0 10px; font-size:13px; color:#4a5568; line-height:1.6;">' +
             '<p style="margin:0 0 10px; font-size:13px; color:#4a5568; line-height:1.6;">' +
             'Seluruh data terstruktur yang tersaji dalam repositori ini merupakan milik publik dan tersedia di bawah lisensi resmi ' +
             'Seluruh data terstruktur yang tersaji dalam repositori ini merupakan milik publik dan tersedia di bawah ' +
             '<a href="https://creativecommons.org/licenses/by-sa/4.0/" target="_blank" style="color:#6a5acd; text-decoration:none; font-weight:700;">Creative Commons Attribution-ShareAlike 4.0 International</a>.' +
             '<a href="https://creativecommons.org/publicdomain/zero/1.0/"target="_blank" style="color:#6a5acd; text-decoration:none; font-weight:700;">Lisensi Creative Commons CC0</a>.' +
             '</p>' +
             '</p>' +
             '<p style="margin:0; font-size:12px; color:#a0aec0;">Akses API dan penggunaan data secara massal wajib mencantumkan atribusi kepada <strong>Mippedia Community</strong> selaku pengelola proyek.</p></div>';
             '<p style="margin:0; font-size:12px; color:#a0aec0;">Akses API dan penggunaan data secara massal wajib mencantumkan atribusi kepada <strong>Mippedia Community</strong> selaku pengelola proyek.</p></div>';