// <nowiki> $(document).ready(function() { // Add link to tools menu mw.util.addPortletLink( 'p-tb', '/wiki/Special:Bl
// <nowiki>
$(document).ready(function() {
// Add link to tools menu
mw.util.addPortletLink(
'p-tb',
'/wiki/Special:BlankPage/UserEditsAnalysis',
'User Edits Analysis',
't-user-edits-analysis',
'Analyze user edits by namespace'
);
// Customize the page when viewing it
if (mw.config.get('wgCanonicalSpecialPageName') === 'Blankpage' &&
mw.config.get('wgTitle') === 'BlankPage/UserEditsAnalysis') {
document.title = 'User Edits Analysis';
$('#firstHeading').text('User Edits Analysis');
$('#mw-content-text').html(`
<div style="max-width: 800px;">
<p>Enter a username to analyze their edit distribution across namespaces.</p>
<input type="text" id="username-input" placeholder="Username" style="width: 300px; padding: 5px;">
<button id="analyze-btn" style="padding: 5px 15px; margin-left: 10px;">Analyze</button>
<div id="status" style="margin-top: 10px; color: #666;"></div>
<div id="results" style="margin-top: 20px;"></div>
<canvas id="chart" width="400" height="400" style="display:none; margin-top: 20px;"></canvas>
</div>
`);
$('#analyze-btn').on('click', function() {
const username = $('#username-input').val().trim();
if (!username) {
$('#status').text('Please enter a username.');
return;
}
analyzeUser(username);
});
$('#username-input').on('keypress', function(e) {
if (e.which === 13) {
$('#analyze-btn').click();
}
});
}
});
async function analyzeUser(username) {
$('#status').text('Fetching edits...');
$('#results').empty();
$('#chart').hide();
const namespaces = {};
let continueToken = null;
let totalEdits = 0;
const delay = 1000; // 1 second between requests
try {
do {
const params = {
action: 'query',
list: 'usercontribs',
ucuser: username,
uclimit: 500,
ucprop: 'title',
format: 'json',
maxlag: 5
};
if (continueToken) {
params.uccontinue = continueToken;
}
const response = await $.ajax({
url: mw.config.get('wgScriptPath') + '/api.php',
data: params,
dataType: 'json',
headers: {
'Api-User-Agent': 'UserEditsAnalysis/1.0 (Wikipedia User Script)'
}
});
if (response.error) {
throw new Error(response.error.info);
}
const contribs = response.query.usercontribs;
for (const contrib of contribs) {
const title = contrib.title;
const colonIndex = title.indexOf(':');
let namespace;
if (colonIndex === -1) {
namespace = 'Main';
} else {
const potentialNs = title.substring(0, colonIndex);
// Check if it's a valid namespace by checking against known namespaces
const nsId = mw.config.get('wgNamespaceIds')[potentialNs.toLowerCase().replace(/ /g, '_')];
if (nsId !== undefined) {
namespace = potentialNs;
} else {
namespace = 'Main';
}
}
namespaces[namespace] = (namespaces[namespace] || 0) + 1;
totalEdits++;
}
$('#status').text(`Fetched ${totalEdits} edits...`);
continueToken = response.continue ? response.continue.uccontinue : null;
if (continueToken) {
await new Promise(resolve => setTimeout(resolve, delay));
}
} while (continueToken);
if (totalEdits === 0) {
$('#status').text('No edits found for this user.');
return;
}
displayResults(namespaces, totalEdits, username);
} catch (error) {
$('#status').text('Error: ' + error.message);
}
}
function displayResults(namespaces, totalEdits, username) {
$('#status').text(`Analysis complete. Total edits: ${totalEdits}`);
// Sort by edit count
const sorted = Object.entries(namespaces).sort((a, b) => b[1] - a[1]);
let html = '<table style="border-collapse: collapse; width: 100%;">';
html += '<tr style="background-color: #eaecf0;"><th style="border: 1px solid #a2a9b1; padding: 8px; text-align: left;">Namespace</th>';
html += '<th style="border: 1px solid #a2a9b1; padding: 8px; text-align: right;">Edits</th>';
html += '<th style="border: 1px solid #a2a9b1; padding: 8px; text-align: right;">Percentage</th></tr>';
for (const [ns, count] of sorted) {
const percentage = ((count / totalEdits) * 100).toFixed(2);
// Get namespace ID
let nsId = 0; // Main namespace
if (ns !== 'Main') {
const nsIdLookup = mw.config.get('wgNamespaceIds')[ns.toLowerCase().replace(/ /g, '_')];
if (nsIdLookup !== undefined) {
nsId = nsIdLookup;
}
}
const contribUrl = mw.util.getUrl('Special:Contributions', {
target: username,
namespace: nsId
});
html += `<tr><td style="border: 1px solid #a2a9b1; padding: 8px;"><a href="${contribUrl}">${ns}</a></td>`;
html += `<td style="border: 1px solid #a2a9b1; padding: 8px; text-align: right;">${count}</td>`;
html += `<td style="border: 1px solid #a2a9b1; padding: 8px; text-align: right;">${percentage}%</td></tr>`;
}
html += '</table>';
$('#results').html(html);
drawPieChart(sorted, totalEdits);
}
function drawPieChart(data, total) {
const canvas = document.getElementById('chart');
const ctx = canvas.getContext('2d');
canvas.style.display = 'block';
const centerX = 200;
const centerY = 200;
const radius = 150;
const colors = [
'#3366CC', '#DC3912', '#FF9900', '#109618', '#990099',
'#3B3EAC', '#0099C6', '#DD4477', '#66AA00', '#B82E2E',
'#316395', '#994499', '#22AA99', '#AAAA11', '#6633CC'
];
let currentAngle = -Math.PI / 2;
for (let i = 0; i < data.length; i++) {
const [ns, count] = data[i];
const sliceAngle = (count / total) * 2 * Math.PI;
ctx.beginPath();
ctx.moveTo(centerX, centerY);
ctx.arc(centerX, centerY, radius, currentAngle, currentAngle + sliceAngle);
ctx.closePath();
ctx.fillStyle = colors[i % colors.length];
ctx.fill();
// Add label
const labelAngle = currentAngle + sliceAngle / 2;
const labelX = centerX + Math.cos(labelAngle) * (radius * 0.7);
const labelY = centerY + Math.sin(labelAngle) * (radius * 0.7);
ctx.fillStyle = '#fff';
ctx.font = 'bold 12px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const percentage = ((count / total) * 100).toFixed(1);
if (parseFloat(percentage) > 3) { // Only show label if slice is large enough
ctx.fillText(`${percentage}%`, labelX, labelY);
}
currentAngle += sliceAngle;
}
// Add legend
let legendY = 420;
for (let i = 0; i < data.length; i++) {
const [ns, count] = data[i];
ctx.fillStyle = colors[i % colors.length];
ctx.fillRect(20, legendY, 15, 15);
ctx.fillStyle = '#000';
ctx.font = '12px sans-serif';
ctx.textAlign = 'left';
ctx.fillText(`${ns} (${count})`, 40, legendY + 12);
legendY += 20;
if (legendY > canvas.height - 20) break;
}
}
// </nowiki>
Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.