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 | |||
* Terintegrasi murni menggunakan Wikitext & JavaScript | |||
* ==================================================================== | |||
*/ | |||
(function($, mw) { | |||
'use strict'; | |||
// 1. CEK LINGKUNGAN: Hanya jalan di Namespace 0 (Artikel) & Bukan Halaman Utama | |||
if (mw.config.get('wgNamespaceNumber') !== 0 || mw.config.get('wgIsMainPage')) { | |||
return; | |||
} | |||
if (mw.config.get('wgAction') !== 'view') { | |||
return; | |||
} | |||
// 2. | mw.loader.using(['mediawiki.api', 'mediawiki.util']).then(function() { | ||
// 2. STATE & VARIABEL GLOBAL | |||
var pageName = mw.config.get('wgPageName'); | |||
var isSysop = mw.config.get('wgUserGroups').indexOf('sysop') !== -1; | |||
var dataItems = []; | |||
var isLocked = false; | |||
var api = new mw.Api(); | |||
// 3. INJEKSI CSS TAMPILAN (Sesuai UI ID Situs Lu) | |||
var css = ` | |||
#mippedia-data-app { | |||
background: #ffffff; | |||
border-radius: 12px; | |||
box-shadow: 0 4px 12px rgba(0,0,0,0.05); | |||
padding: 20px; | |||
margin: 20px 0; | |||
font-family: inherit; | |||
border: 1px solid #eaeaea; | |||
} | |||
.md-header { | |||
display: flex; | |||
justify-content: space-between; | |||
align-items: center; | |||
margin-bottom: 15px; | |||
} | |||
.md-title { | |||
font-size: 1.2em; | |||
font-weight: bold; | |||
color: #333; | |||
margin: 0; | |||
} | |||
.md-actions { | |||
display: flex; | |||
gap: 10px; | |||
} | |||
.md-btn-icon { | |||
background: none; | |||
border: none; | |||
cursor: pointer; | |||
font-size: 16px; | |||
color: #6b52d1; | |||
padding: 5px; | |||
border-radius: 4px; | |||
transition: background 0.2s; | |||
} | |||
.md-btn-icon:hover { background: #f0f0f0; } | |||
.md-btn-lock { color: #dc3545; } | |||
.md-btn-unlock { color: #28a745; } | |||
.md-row-display { | |||
display: flex; | |||
justify-content: space-between; | |||
align-items: center; | |||
padding: 10px 0; | |||
border-bottom: 1px solid #f0f0f0; | |||
} | |||
.md-row-display:last-child { border-bottom: none; } | |||
.md-data-group { display: flex; flex-direction: column; gap: 4px; } | |||
.md-data-label { font-size: 0.85em; color: #666; font-weight: bold; text-transform: uppercase; } | |||
.md-data-value { font-size: 1em; color: #111; } | |||
.md-data-ref { | |||
font-size: 0.8em; | |||
background: #eef2f5; | |||
padding: 2px 6px; | |||
border-radius: 4px; | |||
text-decoration: none; | |||
color: #007bff; | |||
margin-left: 5px; | |||
display: inline-flex; | |||
align-items: center; | |||
gap: 3px; | |||
} | |||
/* Form Edit */ | |||
.md-form-box { | |||
background: #f8f9fa; | |||
border-radius: 8px; | |||
padding: 15px; | |||
margin-top: 10px; | |||
border: 1px dashed #ccc; | |||
} | |||
.md-form-group { margin-bottom: 12px; } | |||
.md-form-group label { display: block; font-size: 0.85em; font-weight: bold; margin-bottom: 5px; color: #555; } | |||
.md-input { | |||
width: 100%; | |||
padding: 10px; | |||
border: 1px solid #ddd; | |||
border-radius: 6px; | |||
box-sizing: border-box; | |||
font-family: inherit; | |||
} | |||
.md-checkbox-group { display: flex; align-items: center; gap: 8px; font-size: 0.9em; color: #444; } | |||
.md-form-actions { display: flex; gap: 10px; align-items: center; margin-top: 15px; } | |||
.md-btn { | |||
padding: 8px 16px; | |||
border: none; | |||
border-radius: 6px; | |||
font-weight: bold; | |||
cursor: pointer; | |||
color: white; | |||
font-size: 0.9em; | |||
transition: opacity 0.2s; | |||
} | |||
.md-btn:hover { opacity: 0.8; } | |||
.md-btn-save { background: #6b52d1; } | |||
.md-btn-cancel { background: #6c757d; } | |||
.md-btn-delete { background: #dc3545; margin-left: auto; } | |||
/* Spinner Kecil */ | |||
.md-spinner { | |||
display: inline-block; | |||
width: 16px; | |||
height: 16px; | |||
border: 2px solid rgba(107, 82, 209, 0.3); | |||
border-radius: 50%; | |||
border-top-color: #6b52d1; | |||
animation: md-spin 1s ease-in-out infinite; | |||
display: none; | |||
margin-left: 8px; | |||
} | |||
@keyframes md-spin { to { transform: rotate(360deg); } } | |||
/* Sembunyikan render bawaan agar terganti UI interaktif */ | |||
.md-native-render { display: none; } | |||
`; | |||
$('<style>').text(css).appendTo('head'); | |||
// 4. PARSER DATA DARI WIKITEXT | |||
function loadData() { | |||
return api.get({ | |||
action: 'query', | |||
prop: 'revisions', | |||
titles: pageName, | |||
rvprop: 'content', | |||
rvslots: 'main', | |||
formatversion: 2 | |||
}).then(function(data) { | |||
var content = data.query.pages[0].revisions[0].slots.main.content; | |||
var match = content.match(/<!-- MIPPEDIA_DATA_START \| (.*?) -->/); | |||
if (match) { | |||
if (match | |||
try { | try { | ||
var parsed = JSON.parse(match[1]); | |||
} catch (e) { | dataItems = parsed.items || []; | ||
isLocked = parsed.locked || false; | |||
} catch(e) { console.error("Format JSON Mippedia Data rusak", e); } | |||
} | } | ||
} | return content; // Return raw content for saving later | ||
}); | |||
} | |||
// 5. PENYIMPANAN KE WIKITEXT API | |||
function saveData(actionType, spinnerEl, originalContent) { | |||
if (spinnerEl) spinnerEl.show(); | |||
// Bikin JSON string buat disimpen secara rahasia | |||
var jsonStr = JSON.stringify({ locked: isLocked, items: dataItems }); | |||
// | // Bikin wikitext *fallback* (biar render rapi juga di kodenya) | ||
if ( | var wt = "<!-- MIPPEDIA_DATA_START | " + jsonStr + " -->\n"; | ||
wt += "<div class=\"md-native-render\">\n{| class=\"wikitable\"\n! Data !! Nilai !! Referensi\n"; | |||
dataItems.forEach(function(item) { | |||
var valTxt = item.link ? "[[" + item.val + "]]" : item.val; | |||
var refTxt = item.ref ? "[ " + item.ref + " Rujukan]" : ""; | |||
wt += "|-\n| " + item.prop + " || " + valTxt + " || " + refTxt + "\n"; | |||
}); | |||
wt += "|}\n</div>\n<!-- MIPPEDIA_DATA_END -->"; | |||
// Deteksi penempatan di wikitext | |||
var newContent; | |||
var regex = /<!-- MIPPEDIA_DATA_START \| .*? -->[\s\S]*?<!-- MIPPEDIA_DATA_END -->/; | |||
if (regex.test(originalContent)) { | |||
newContent = originalContent.replace(regex, wt); | |||
} else { | } else { | ||
// Kalo belum ada, taruh di paling bawah artikel | |||
newContent = originalContent.trim() + "\n\n" + wt; | |||
} | } | ||
api.postWithToken('csrf', { | |||
action: 'edit', | action: 'edit', | ||
title: pageName, | title: pageName, | ||
text: | text: newContent, | ||
summary: ' | summary: 'Memperbarui Mippedia Data: ' + actionType, | ||
minor: true | minor: true | ||
}).done(function() { | |||
// Berhasil? Refresh halaman biar link merah/biru ter-render sempurna | |||
location.reload(); | |||
}).fail(function(err) { | |||
if (spinnerEl) spinnerEl.hide(); | |||
alert("Waduh, gagal menyimpan data. Coba lagi bro! Error: " + err); | |||
}); | }); | ||
} | |||
// 6. BUILDER UI UTAMA | |||
function buildUI(rawContent) { | |||
var $app = $('<div id="mippedia-data-app"></div>'); | |||
// Header | |||
var $header = $('<div class="md-header"><h3 class="md-title">Mippedia Data</h3></div>'); | |||
var $actions = $('<div class="md-actions"></div>'); | |||
// Logika Gembok & Tombol Edit | |||
if (isSysop) { | |||
var lockIcon = isLocked ? '🔓 (Buka)' : '🔒 (Kunci)'; | |||
var lockClass = isLocked ? 'md-btn-unlock' : 'md-btn-lock'; | |||
var $lockBtn = $('<button class="md-btn-icon ' + lockClass + '" title="Fitur Pengurus: Kunci/Buka Data">' + lockIcon + '</button>'); | |||
$lockBtn.on('click', function() { | |||
isLocked = !isLocked; | |||
saveData(isLocked ? 'Mengunci data' : 'Membuka kunci data', null, rawContent); | |||
}); | |||
$actions.append($lockBtn); | |||
} else if (isLocked) { | |||
$actions.append('<span title="Dikunci oleh Pengurus" style="color:#dc3545;">🔒 Terkunci</span>'); | |||
} | |||
if (!isLocked || isSysop) { | |||
var $addBtn = $('<button class="md-btn-icon" title="Tambah Data Baru">✏️</button>'); | |||
$addBtn.on('click', function() { showForm(-1, rawContent); }); | |||
$actions.append($addBtn); | |||
} | |||
$header.append($actions); | |||
$app.append($header); | |||
// Container List Data | |||
var $list = $('<div class="md-list"></div>'); | |||
if (dataItems.length === 0) { | |||
$list.append('<div style="color:#888; font-size:0.9em;">Belum ada data. Klik ikon pensil untuk menambahkan.</div>'); | |||
} else { | |||
dataItems.forEach(function(item, index) { | |||
var $row = $('<div class="md-row-display"></div>'); | |||
var $info = $('<div class="md-data-group"></div>'); | |||
$info.append('<span class="md-data-label">' + item.prop + '</span>'); | |||
var $valArea = $('<div></div>'); | |||
// Karena kita me-reload halaman saat simpan, API MediaWiki sudah nge-parsing linknya, | |||
// Tapi untuk tampilan instan di UI via JSON: | |||
var displayVal = item.link ? '<a href="' + mw.util.getUrl(item.val) + '" style="color:#0645ad;">' + item.val + '</a>' : item.val; | |||
$valArea.append('<span class="md-data-value">' + displayVal + '</span>'); | |||
if (item.ref) { | |||
$valArea.append('<a href="' + item.ref + '" target="_blank" class="md-data-ref">🔗 Referensi</a>'); | |||
} | |||
$info.append($valArea); | |||
$row.append($info); | |||
if (!isLocked || isSysop) { | |||
var $editIcon = $('<button class="md-btn-icon" style="font-size:12px;">⚙️ Edit</button>'); | |||
$editIcon.on('click', function() { showForm(index, rawContent); }); | |||
$row.append($editIcon); | |||
} | |||
$list.append($row); | |||
}); | |||
} | } | ||
$app.append($list); | |||
// Tempat penampung Form Edit biar muncul ke bawah atau pas lagi diedit | |||
$app.append('<div id="md-form-container"></div>'); | |||
// 7. PENEMPATAN UI SMART DETECTOR (Bawah Infobox / Atas ID Situs) | |||
var $idSitus = $('[id*="situs"], [class*="situs"], .id-situs, .id-situs-card').first(); | |||
var $infobox = $('.infobox').first(); | |||
$('.md-native-render').hide(); // Sembunyikan wikitext murni | |||
if ($idSitus.length) { | |||
$app.insertBefore($idSitus); | |||
} else if ($infobox.length) { | |||
$app.insertAfter($infobox); | |||
} else { | |||
$('#mw-content-text .mw-parser-output').append($app); | |||
} | } | ||
} | |||
// 8. LOGIKA FORM EDIT & TAMBAH DATA | |||
function showForm(index, rawContent) { | |||
var $container = $('#md-form-container'); | |||
$container.empty(); // Bersihin form lama kalo ada | |||
var isNew = index === -1; | |||
var currentData = isNew ? { prop: '', val: '', link: false, ref: '' } : dataItems[index]; | |||
var $form = $('<div class="md-form-box"></div>'); | |||
// Input Data (Kiri/Atas) | |||
$form.append('<div class="md-form-group"><label>Data (Contoh: Profesi)</label><input type="text" id="md-inp-prop" class="md-input" value="' + currentData.prop + '" placeholder="Masukkan nama data..."></div>'); | |||
// Input Nilai (Kanan/Tengah) | |||
$form.append('<div class="md-form-group"><label>Nilai (Contoh: Penyanyi)</label><input type="text" id="md-inp-val" class="md-input" value="' + currentData.val + '" placeholder="Masukkan nilai..."></div>'); | |||
// Checkbox Link | |||
var chk = currentData.link ? 'checked' : ''; | |||
$form.append('<div class="md-form-group md-checkbox-group"><input type="checkbox" id="md-inp-link" ' + chk + '> <label for="md-inp-link" style="margin:0; cursor:pointer;">Jadikan Tautan Wikilink [[ ]]</label></div>'); | |||
// Input Referensi URL | |||
$form.append('<div class="md-form-group"><label>URL Referensi (Opsional)</label><input type="url" id="md-inp-ref" class="md-input" value="' + currentData.ref + '" placeholder="https://..."></div>'); | |||
// Barisan Tombol | |||
var $actions = $('<div class="md-form-actions"></div>'); | |||
var $btnSave = $('<button class="md-btn md-btn-save">Terbitkan <span class="md-spinner"></span></button>'); | |||
$ | var $btnCancel = $('<button class="md-btn md-btn-cancel">Batal</button>'); | ||
$actions.append($btnSave); | |||
$actions.append($btnCancel); | |||
if (!isNew) { | |||
var $btnDelete = $('<button class="md-btn md-btn-delete">Hapus Data <span class="md-spinner" style="border-top-color:#dc3545;"></span></button>'); | |||
$btnDelete.on('click', function() { | |||
if(confirm('Yakin mau hapus data ini secara permanen?')) { | |||
dataItems.splice(index, 1); | |||
saveData('Menghapus data', $(this).find('.md-spinner'), rawContent); | |||
} | |||
}); | }); | ||
$actions.append($btnDelete); | |||
} | |||
$form.append($actions); | |||
}); | $container.append($form); | ||
// Aksi Batal | |||
$btnCancel.on('click', function() { $container.empty(); }); | |||
// Aksi Simpan / Terbitkan | |||
$btnSave.on('click', function() { | |||
var newProp = $('#md-inp-prop').val().trim(); | |||
var newVal = $('#md-inp-val').val().trim(); | |||
var newLink = $('#md-inp-link').is(':checked'); | |||
var newRef = $('#md-inp-ref').val().trim(); | |||
if (!newProp || !newVal) { | |||
alert("Data dan Nilai gak boleh kosong ya bro!"); | |||
return; | |||
} | |||
var newItem = { prop: newProp, val: newVal, link: newLink, ref: newRef }; | |||
if (isNew) { | |||
dataItems.push(newItem); // Tambah bawah sendiri | |||
} else { | |||
} | dataItems[index] = newItem; // Edit yang ada | ||
} | |||
saveData(isNew ? 'Menambahkan data baru' : 'Mengubah data', $(this).find('.md-spinner'), rawContent); | |||
}); | |||
} | } | ||
// 9. EKSEKUSI PROGRAM | |||
loadData().then(function(rawContent) { | |||
} | buildUI(rawContent); | ||
}); | |||
}); | |||
})(jQuery, mediaWiki); | |||
/* ========================================================== | /* ========================================================== | ||