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. | * MIPPEDIA DATA - ADVANCED STRUCTURED DATA SYSTEM (V8.6 SECURE ULTIMATE) | ||
* Fitur: ALL ORIGINAL V7/V8 FEATURES KEPT + | * Fitur: ALL ORIGINAL V7/V8 FEATURES KEPT + Premium Verified Badge, | ||
* | * 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; | 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=" | // 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( | // Enhanced OWASP-style sanitization to prevent ANY bypasses | ||
return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[ | return str.replace(/[&<>"'`=\/]/g, function(s) { | ||
return { | |||
'&': '&', '<': '<', '>': '>', '"': '"', | |||
"'": ''', '/': '/', '`': '`', '=': '=' | |||
}[s]; | |||
}); | }); | ||
} | } | ||
| Baris 109: | Baris 114: | ||
if (!urlStr) return ''; | if (!urlStr) return ''; | ||
urlStr = urlStr.trim(); | urlStr = urlStr.trim(); | ||
// | // Ultra-Strict URL Protocol validation to completely neutralize all XSS vectors in hrefs | ||
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: | .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; } | ||
.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 | .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. | 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 | <summary> | ||
< | <div style="color:#4f46e5; display:inline-flex; align-items:center; gap:5px; white-space:nowrap; flex-shrink:0;">${svgRef} REFERENSI:</div> | ||
< | <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} | ||
</ | </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 ? ' • ' + 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 ? ' • ' + sanitizeHTML(ref.subType) : '') + '</span></div>' : ''} | ||
${safeUrl ? '<a href="' + sanitizeHTML(safeUrl) + '" target="_blank" rel="noopener noreferrer" class="md-btn-visit"> | ${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 | 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>'); | ||
$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 | |||
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=" | 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(); | initTooltips(); | ||
$('#md-chk-person').on('change', function() { | $('#md-chk-person').on('change', function() { | ||