MediaWiki:Common.js: Perbedaan antara revisi

Tidak ada ringkasan suntingan
Tanda: Suntingan perangkat seluler Suntingan peramban seluler
Tidak ada ringkasan suntingan
Tanda: Suntingan perangkat seluler Suntingan peramban seluler
Baris 1: Baris 1:
/**
/**
  * ====================================================================
  * ====================================================================
  * MIPPEDIA DATA - ADVANCED STRUCTURED DATA SYSTEM (V8.5 SECURE ULTIMATE)
  * MIPPEDIA DATA - ADVANCED STRUCTURED DATA SYSTEM (V8.6 SECURE ULTIMATE)
  * Fitur: ALL ORIGINAL V7/V8 FEATURES KEPT + Responsive Verified Ticks,  
  * Fitur: ALL ORIGINAL V7/V8 FEATURES KEPT + Premium Verified Badge,  
  * Smart Tooltip Position, Advanced XSS Security, Detailed Ref Instructions.
  * Ultra-Strict OWASP XSS Security, Stricter URL Parsing, Mobile Optimized.
  * ====================================================================
  * ====================================================================
  */
  */
Baris 19: Baris 19:
         var isLocked = false;
         var isLocked = false;
         var api = new mw.Api();
         var api = new mw.Api();
         var DRAFT_KEY = 'mippedia_data_draft_' + pageName; // Key untuk Draft
         var DRAFT_KEY = 'mippedia_data_draft_' + pageName;


         // 1. DATA MASTER PLATFORM MEDIA SOSIAL & MUSIK (FULL ASLI)
         // 1. DATA MASTER PLATFORM MEDIA SOSIAL & MUSIK (FULL ASLI)
Baris 66: Baris 66:
         ];
         ];


         // 3. IKON SISTEM  
         // 3. IKON SISTEM (Centang Biru Diperbarui ke Versi Premium Rosette)
         var svgEdit = '<svg class="md-icon" width="15" height="15" 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="15" height="15" 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="15" height="15" 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="15" height="15" 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>';
Baris 74: Baris 74:
         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 svgVerified = '<svg width="15" height="15" viewBox="0 0 24 24" fill="#3b82f6" stroke="#ffffff" stroke-width="2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>';
        // NEW PREMIUM VERIFIED BADGE
         var svgVerified = '<svg width="16" height="16" viewBox="0 0 24 24" fill="#1d9bf0" xmlns="http://www.w3.org/2000/svg"><path d="M22.5 12.5l-1.58 1.95.23 2.5-2.47.53-1.12 2.23-2.43-.88-1.8 1.76-2.33-1.07-2.33 1.07-1.8-1.76-2.43.88-1.12-2.23-2.47-.53.23-2.5L1.5 12.5l1.58-1.95-.23-2.5 2.47-.53 1.12-2.23 2.43.88 1.8-1.76 2.33 1.07 2.33-1.07 1.8 1.76 2.43-.88 1.12 2.23 2.47.53-.23 2.5L22.5 12.5z" stroke="#ffffff" stroke-width="1"></path><path d="M9.5 15.5l-3-3 1.4-1.4 1.6 1.6 4.6-4.6 1.4 1.4-6 6z" fill="#ffffff"></path></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>';
         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
         // 4. HELPER UTILITY & ADVANCED SECURITY (Tingkat Tinggi OWASP)
         function sanitizeHTML(str) {
         function sanitizeHTML(str) {
             if (typeof str !== 'string') return str;
             if (typeof str !== 'string') return str;
             return str.replace(/[&<>"']/g, function(m) {
            // Enhanced OWASP-style sanitization to prevent ANY bypasses
                 return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[m];
             return str.replace(/[&<>"'`=\/]/g, function(s) {
                 return {
                    '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;',  
                    "'": '&#39;', '/': '&#x2F;', '`': '&#x60;', '=': '&#x3D;'
                }[s];
             });
             });
         }
         }
Baris 109: Baris 114:
             if (!urlStr) return '';
             if (!urlStr) return '';
             urlStr = urlStr.trim();
             urlStr = urlStr.trim();
             // Block javascript/vbscript execution vulnerability in URL fields
             // Ultra-Strict URL Protocol validation to completely neutralize all XSS vectors in hrefs
             if (/^(javascript|data|vbscript):/i.test(urlStr)) return '';  
             var protoMatch = urlStr.match(/^([a-z0-9]+):/i);
            if (protoMatch) {
                var badProtos = ['javascript', 'data', 'vbscript', 'file', 'ws', 'wss', 'ftp'];
                if (badProtos.indexOf(protoMatch[1].toLowerCase()) !== -1) return '';
            }
             return (urlStr && !urlStr.match(/^https?:\/\//i)) ? 'https://' + urlStr : urlStr;
             return (urlStr && !urlStr.match(/^https?:\/\//i)) ? 'https://' + urlStr : urlStr;
         }
         }
Baris 163: Baris 172:
             .md-ref-container { margin-top: 10px; width: 100%; box-sizing: border-box; }
             .md-ref-container { margin-top: 10px; width: 100%; box-sizing: border-box; }
             .md-ref-details { background: #f8fafc; border-left: 3px solid #4f46e5; border-radius: 10px; overflow: hidden; border-top:1px solid #f1f5f9; border-right:1px solid #f1f5f9; border-bottom:1px solid #f1f5f9; transition: 0.3s; }
             .md-ref-details { background: #f8fafc; border-left: 3px solid #4f46e5; border-radius: 10px; overflow: hidden; border-top:1px solid #f1f5f9; border-right:1px solid #f1f5f9; border-bottom:1px solid #f1f5f9; transition: 0.3s; }
             .md-ref-details summary { display: flex; padding: 10px 14px; font-size: 0.85rem; font-weight: 700; color: #374151; cursor: pointer; user-select: none; list-style: none; flex-wrap: nowrap; }
             .md-ref-details summary { display: flex; padding: 10px 14px; font-size: 0.85rem; font-weight: 700; color: #374151; cursor: pointer; user-select: none; list-style: none; flex-wrap: nowrap; align-items: center; gap: 8px; }
             .md-ref-details summary::-webkit-details-marker { display: none; }
             .md-ref-details summary::-webkit-details-marker { display: none; }
             .md-ref-details[open] summary { border-bottom: 1px dashed #cbd5e1; background: #e0e7ff; color: #3730a3; }
             .md-ref-details[open] summary { border-bottom: 1px dashed #cbd5e1; background: #e0e7ff; color: #3730a3; }
             .md-ref-body { padding: 12px 14px; font-size: 0.85rem; display: flex; flex-direction: column; gap: 6px; }
             .md-ref-body { padding: 12px 14px; font-size: 0.85rem; display: flex; flex-direction: column; gap: 6px; }
              
              
             .md-verified-badge { cursor: pointer; display: inline-flex; align-items: center; justify-content: center; margin-left: 2px; vertical-align: middle; transition: transform 0.2s; flex-shrink: 0; }
             .md-verified-badge { cursor: pointer; display: inline-flex; align-items: center; justify-content: center; margin-left: 4px; vertical-align: middle; transition: transform 0.2s; flex-shrink: 0; }
             .md-verified-badge:hover { transform: scale(1.1); }
             .md-verified-badge:hover { transform: scale(1.1); }
             .md-btn-visit { background: #f1f5f9; color: #0f172a; padding: 6px 12px; border-radius: 6px; text-decoration: none; font-size: 0.8rem; display: inline-block; width: fit-content; margin-top: 6px; font-weight:700; border:1px solid #cbd5e1; }
             .md-btn-visit { background: #f1f5f9; color: #0f172a; padding: 6px 12px; border-radius: 6px; text-decoration: none; font-size: 0.8rem; display: inline-block; width: fit-content; margin-top: 6px; font-weight:700; border:1px solid #cbd5e1; }
Baris 194: Baris 203:
             .md-help-icon:hover { color: #4f46e5; }
             .md-help-icon:hover { color: #4f46e5; }
             .md-tooltip-popup { position: absolute; background: #1e293b; color: #fff; padding: 10px 14px; border-radius: 8px; font-size: 0.8rem; width: 260px; max-width: calc(100vw - 30px); z-index: 1000; box-shadow: 0 4px 15px rgba(0,0,0,0.15); display: none; line-height: 1.4; text-align: left; font-weight:normal; }
             .md-tooltip-popup { position: absolute; background: #1e293b; color: #fff; padding: 10px 14px; border-radius: 8px; font-size: 0.8rem; width: 260px; max-width: calc(100vw - 30px); z-index: 1000; box-shadow: 0 4px 15px rgba(0,0,0,0.15); display: none; line-height: 1.4; text-align: left; font-weight:normal; }
            /* Memastikan panah selalu berada di tempat wajar meski boks bergeser di mobile */
             .md-tooltip-popup::after { content: ''; position: absolute; border-width: 6px; border-style: solid; border-color: #1e293b transparent transparent transparent; top: 100%; left: 15px; }
             .md-tooltip-popup::after { content: ''; position: absolute; border-width: 6px; border-style: solid; border-color: #1e293b transparent transparent transparent; top: 100%; left: 15px; }


Baris 213: Baris 221:
             @keyframes md-spin { to { transform: rotate(360deg); } }
             @keyframes md-spin { to { transform: rotate(360deg); } }


            /* Mobile Optimized Layout (Seluler) */
             @media (max-width: 600px) {
             @media (max-width: 600px) {
                 #mippedia-data-app { padding: 14px; margin: 14px 0; border-radius: 12px; }
                 #mippedia-data-app { padding: 14px; margin: 14px 0; border-radius: 12px; }
Baris 222: Baris 231:
                 .md-form-actions { flex-direction: column; width: 100%; }
                 .md-form-actions { flex-direction: column; width: 100%; }
                 .md-btn { width: 100%; margin: 0 !important; }
                 .md-btn { width: 100%; margin: 0 !important; }
                 .md-row-display { padding: 12px 8px; flex-direction:column; align-items:stretch;}
                 .md-row-display { padding: 12px 10px; flex-direction:column; align-items:stretch;}
                 .edit-btn { align-self:flex-end; }
                 .edit-btn { align-self:flex-end; margin-top:8px; }
                /* Memastikan elemen panah menu rujukan tidak tergencet di layar kecil */
                .md-ref-details summary { align-items: flex-start; }
             }
             }
             .md-native-render { display: none; }
             .md-native-render { display: none; }
Baris 324: Baris 335:
             if ($btnEl) $btnEl.addClass('is-loading').prop('disabled', true);
             if ($btnEl) $btnEl.addClass('is-loading').prop('disabled', true);
              
              
             var summaryTxt = customSummary ? ('Mippedia Data: ' + customSummary) : ('Mippedia Data V8.5: ' + actionType);
             var summaryTxt = customSummary ? ('Mippedia Data: ' + customSummary) : ('Mippedia Data V8.6: ' + actionType);
             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 468: Baris 479:
                         var $refDetails = $(`
                         var $refDetails = $(`
                             <details class="md-ref-details">
                             <details class="md-ref-details">
                                 <summary style="align-items:flex-start; gap:8px;">
                                 <summary>
                                     <span style="color:#4f46e5; display:inline-flex; align-items:center; gap:5px; white-space:nowrap;">${svgRef} REFERENSI:</span>  
                                     <div style="color:#4f46e5; display:inline-flex; align-items:center; gap:5px; white-space:nowrap; flex-shrink:0;">${svgRef} REFERENSI:</div>  
                                     <span style="color:#374151; display:inline-flex; align-items:center; gap:4px; flex-wrap:wrap; flex:1;">
                                     <div style="color:#374151; display:flex; align-items:center; flex-wrap:wrap; flex:1;">
                                         <span style="word-break:break-word;">${sanitizeHTML(refTitle)}</span>${verBadgeHtml}
                                         <span style="word-break:break-word; line-height:1.3;">${sanitizeHTML(refTitle)}</span>${verBadgeHtml}
                                     </span>
                                     </div>
                                 </summary>
                                 </summary>
                                 <div class="md-ref-body">
                                 <div class="md-ref-body">
Baris 480: Baris 491:
                                     ${ref.date ? '<div><span style="color:#64748b; font-weight:700;">Tanggal Akses:</span> ' + formatIndonesianDate(ref.date) + '</div>' : ''}
                                     ${ref.date ? '<div><span style="color:#64748b; font-weight:700;">Tanggal Akses:</span> ' + formatIndonesianDate(ref.date) + '</div>' : ''}
                                     ${ref.type ? '<div style="margin-top:4px;"><span style="background:#e2e8f0; padding:2px 8px; border-radius:6px; font-weight:700; font-size:0.75rem;">' + sanitizeHTML(ref.type) + (ref.subType ? ' &bull; ' + sanitizeHTML(ref.subType) : '') + '</span></div>' : ''}
                                     ${ref.type ? '<div style="margin-top:4px;"><span style="background:#e2e8f0; padding:2px 8px; border-radius:6px; font-weight:700; font-size:0.75rem;">' + sanitizeHTML(ref.type) + (ref.subType ? ' &bull; ' + sanitizeHTML(ref.subType) : '') + '</span></div>' : ''}
                                     ${safeUrl ? '<a href="' + sanitizeHTML(safeUrl) + '" target="_blank" rel="noopener noreferrer" class="md-btn-visit">Website</a>' : ''}
                                     ${safeUrl ? '<a href="' + sanitizeHTML(safeUrl) + '" target="_blank" rel="noopener noreferrer" class="md-btn-visit">Kunjungi Situs</a>' : ''}
                                 </div>
                                 </div>
                             </details>
                             </details>
Baris 487: Baris 498:
                         $refDetails.find('.md-verified-badge').on('click', function(e) {
                         $refDetails.find('.md-verified-badge').on('click', function(e) {
                             e.preventDefault(); e.stopPropagation();
                             e.preventDefault(); e.stopPropagation();
                             showModal('Situs Terverifikasi', 'Centang biru menandakan bahwa URL referensi berasal dari situs web besar, portal populer, atau pangkalan data resmi (seperti Kompas, YouTube, Apple Music, dll) yang memiliki reputasi kredibel.');
                             showModal('Situs Terverifikasi', 'Centang biru menandakan bahwa URL referensi ini valid dan berasal dari entitas, media publikasi besar, portal populer, atau pangkalan data resmi (seperti Kompas, YouTube, Apple Music, Spotify, dll) yang bereputasi kredibel.');
                         });
                         });


Baris 582: Baris 593:


             var $refArea = $('<div class="md-form-group" style="margin-top:20px;"></div>');
             var $refArea = $('<div class="md-form-group" style="margin-top:20px;"></div>');
            // INSTRUKSI REFERENSI LENGKAP DAN DETAIL
             $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>');
             $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, faktual, dan jelas sangat membantu memastikan keakuratan informasi dan mencegah penyebaran misinformasi di platform ini. Jika URL rujukan Anda mengarah ke situs web besar atau domain resmi yang terverifikasi (contoh: Kompas, YouTube, Spotify, BBC, situs pemerintah, dll), sistem keamanan akan secara otomatis menampilkan lencana centang biru sebagai tanda sumber tepercaya.">' + svgHelp + '</span></label>');
             var $refGrid = $('<div class="md-ref-grid"></div>');
             var $refGrid = $('<div class="md-ref-grid"></div>');
              
              
Baris 608: Baris 618:
             $refGrid.append('<div><label style="font-size:0.78rem; margin-bottom:4px;">Sub-Tipe</label></div>').find('div').last().append($subTypeSel);
             $refGrid.append('<div><label style="font-size:0.78rem; margin-bottom:4px;">Sub-Tipe</label></div>').find('div').last().append($subTypeSel);


             var $urlInput = $('<input type="url" id="md-ref-url" class="md-input" placeholder="https://...." value="' + sanitizeHTML(currentRef.url || '') + '">');
             var $urlInput = $('<input type="text" id="md-ref-url" class="md-input" placeholder="https://...." value="' + sanitizeHTML(currentRef.url || '') + '">');
             $urlInput.on('blur', function() {
             $urlInput.on('blur', function() {
                 var u = ensureHttps($(this).val());
                 var u = ensureHttps($(this).val());
Baris 675: Baris 685:
             $form.append($actions);
             $form.append($actions);
             $container.append($form);
             $container.append($form);
             initTooltips(); // Inisialisasi tooltips aman di dalam DOM baru
             initTooltips();  


             $('#md-chk-person').on('change', function() {
             $('#md-chk-person').on('change', function() {