/**
* WikiEditor Button Configurator
* @version 2025-06-07
* PURE JAVASCRIPT CONFIGURATION TOOL - NO BUTTON DEFINITIONS
* Usage: Add ?wikieditorconfig=1 to any Wikipedia URL
*/
(function ($, mw) {
'use strict';
// Check for configuration parameter
var urlParams = new URLSearchParams(window.___location.search);
if (urlParams.get('wikieditorconfig') !== '1') {
return;
}
var buttons = [];
var defaults = {
section: 'advanced',
group: 'insert',
type: 'ooui'
};
// Create interface
function init() {
// Add CSS
var css = [
'#wec-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); z-index: 10000; overflow: auto; }',
'#wec-container { max-width: 900px; margin: 20px auto; background: #fff; border-radius: 8px; padding: 20px; }',
'#wec-form { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 20px; }',
'.wec-panel { background: #f8f9fa; padding: 15px; border-radius: 5px; }',
'.wec-field { margin-bottom: 10px; }',
'.wec-label { display: block; margin-bottom: 5px; font-weight: bold; }',
'.wec-input { width: 100%; padding: 5px; border: 1px solid #ccc; border-radius: 3px; }',
'.wec-btn { background: #0645ad; color: #fff; border: none; padding: 8px 12px; margin: 0 5px 5px 0; border-radius: 3px; cursor: pointer; }',
'.wec-btn-danger { background: #d33; }',
'.wec-list { max-height: 300px; overflow-y: auto; border: 1px solid #ccc; padding: 10px; background: #fff; }',
'.wec-item { border: 1px solid #ddd; padding: 8px; margin-bottom: 5px; border-radius: 3px; }',
'#wec-json { width: 100%; height: 200px; font-family: monospace; font-size: 12px; }'
].join(' ');
$('<style>').text(css).appendTo('head');
createInterface();
loadExistingConfig();
}
function createInterface() {
var $overlay = $('<div id="wec-overlay">');
var $container = $('<div id="wec-container">');
// Header
var $header = $('<h1>').css({
textAlign: 'center',
color: '#0645ad',
marginBottom: '20px'
}).text('WikiEditor Button Configuration Tool');
var $closeBtn = $('<button>').text('×').css({
position: 'absolute',
top: '20px',
right: '20px',
background: 'none',
border: 'none',
fontSize: '20px',
cursor: 'pointer'
}).click(function() {
$overlay.remove();
});
// Form
var $form = $('<div id="wec-form">');
$form.append(createFormPanel(), createListPanel());
// JSON section
var $jsonSection = createJsonSection();
$container.append($header, $closeBtn, $form, $jsonSection);
$overlay.append($container);
$('body').append($overlay);
}
function createFormPanel() {
var $panel = $('<div class="wec-panel">');
$panel.append($('<h3>').text('Button Configuration'));
// Form fields
$panel.append(
createField('Button ID*', 'text', 'wec-id'),
createField('Label*', 'text', 'wec-label'),
createField('Tooltip', 'text', 'wec-tooltip'),
createSelectField('Type', 'wec-type', [
{value: 'ooui', text: 'OOUI'},
{value: 'traditional', text: 'Traditional'},
{value: 'element', text: 'Element'}
]),
createField('Icon', 'text', 'wec-icon'),
createField('Insert Before', 'textarea', 'wec-before'),
createField('Sample Text', 'text', 'wec-sample'),
createField('Insert After', 'textarea', 'wec-after'),
createCheckField('Has Prompt', 'wec-prompt'),
createField('Prompt Message', 'text', 'wec-promptmsg'),
createCheckField('Auto Summary', 'wec-summary'),
createField('Summary Text', 'text', 'wec-summarytext'),
createField('Namespaces (comma-separated)', 'text', 'wec-ns')
);
// Buttons
var $buttons = $('<div>').css({marginTop: '15px'});
$buttons.append(
$('<button class="wec-btn">').text('Add Button').click(addButton),
$('<button class="wec-btn">').text('Clear Form').click(clearForm)
);
$panel.append($buttons);
return $panel;
}
function createListPanel() {
var $panel = $('<div class="wec-panel">');
$panel.append(
$('<h3>').text('Button List'),
$('<div class="wec-list" id="wec-list">').text('No buttons added yet'),
$('<div>').css({marginTop: '15px'}).append(
$('<button class="wec-btn wec-btn-danger">').text('Clear All').click(clearAll)
)
);
return $panel;
}
function createJsonSection() {
var $section = $('<div>').css({
borderTop: '1px solid #ccc',
paddingTop: '20px'
});
$section.append(
$('<h3>').text('Generated JSON Configuration'),
$('<textarea id="wec-json">').val('Configuration will appear here'),
$('<div>').css({marginTop: '10px'}).append(
$('<button class="wec-btn">').text('Generate JSON').click(generateJson),
$('<button class="wec-btn">').text('Copy').click(copyJson),
$('<button class="wec-btn">').text('Save to User Page').click(saveToUserPage)
)
);
return $section;
}
function createField(label, type, id) {
var $field = $('<div class="wec-field">');
var $label = $('<label class="wec-label">').text(label);
var $input = type === 'textarea'
? $('<textarea class="wec-input" rows="3">').attr('id', id)
: $('<input class="wec-input">').attr({type: type, id: id});
$field.append($label, $input);
return $field;
}
function createSelectField(label, id, options) {
var $field = $('<div class="wec-field">');
var $label = $('<label class="wec-label">').text(label);
var $select = $('<select class="wec-input">').attr('id', id);
options.forEach(function(opt) {
$select.append($('<option>').val(opt.value).text(opt.text));
});
$field.append($label, $select);
return $field;
}
function createCheckField(label, id) {
var $field = $('<div class="wec-field">');
var $wrapper = $('<label>').css({display: 'flex', alignItems: 'center', gap: '5px'});
var $checkbox = $('<input>').attr({type: 'checkbox', id: id});
$wrapper.append($checkbox, $('<span>').text(label));
$field.append($wrapper);
return $field;
}
// Button management functions
function addButton() {
var data = collectFormData();
if (!data.id || !data.label) {
alert('ID and Label are required!');
return;
}
if (buttons.some(function(b) { return b.id === data.id; })) {
alert('Button ID must be unique!');
return;
}
buttons.push(data);
updateButtonList();
clearForm();
}
function collectFormData() {
var data = {
id: $('#wec-id').val(),
label: $('#wec-label').val(),
type: $('#wec-type').val()
};
// Optional fields
var optionalFields = {
'wec-tooltip': 'tooltip',
'wec-icon': 'icon',
'wec-before': 'insertBefore',
'wec-sample': 'sampleText',
'wec-after': 'insertAfter',
'wec-promptmsg': 'promptMessage',
'wec-summarytext': 'summaryText',
'wec-ns': 'namespaces'
};
Object.keys(optionalFields).forEach(function(fieldId) {
var value = $('#' + fieldId).val();
if (value) {
var key = optionalFields[fieldId];
if (key === 'namespaces') {
data[key] = value.split(',').map(function(n) {
return parseInt(n.trim());
}).filter(function(n) { return !isNaN(n); });
} else {
data[key] = value;
}
}
});
// Handle prompt
if ($('#wec-prompt').is(':checked') && data.promptMessage) {
data.prompt = {
type: 'simple',
message: data.promptMessage
};
delete data.promptMessage;
}
// Handle auto summary
if ($('#wec-summary').is(':checked') && data.summaryText) {
data.autoSummary = {
summary: data.summaryText,
position: 'append'
};
delete data.summaryText;
}
return data;
}
function updateButtonList() {
var $list = $('#wec-list');
$list.empty();
if (buttons.length === 0) {
$list.text('No buttons added yet');
return;
}
buttons.forEach(function(btn, i) {
var $item = $('<div class="wec-item">');
var $title = $('<strong>').text(btn.label + ' (' + btn.id + ')');
var $type = $('<span>').text(' - ' + btn.type).css('color', '#666');
var $deleteBtn = $('<button class="wec-btn wec-btn-danger">').text('Delete').click(function() {
if (confirm('Delete this button?')) {
buttons.splice(i, 1);
updateButtonList();
}
});
$item.append($title, $type, $('<br>'), $deleteBtn);
$list.append($item);
});
}
function clearForm() {
$('input, select, textarea').each(function() {
if ($(this).attr('type') === 'checkbox') {
$(this).prop('checked', false);
} else {
$(this).val('');
}
});
$('#wec-type').val('ooui');
}
function clearAll() {
if (confirm('Clear all buttons?')) {
buttons = [];
updateButtonList();
}
}
function generateJson() {
var config = {
defaults: defaults,
buttons: buttons.map(function(btn) {
// Escape newlines for JSON storage
var processed = JSON.parse(JSON.stringify(btn));
['insertBefore', 'insertAfter', 'sampleText'].forEach(function(field) {
if (processed[field]) {
processed[field] = processed[field].replace(/\n/g, '\\n');
}
});
return processed;
})
};
$('#wec-json').val(JSON.stringify(config, null, 2));
}
function copyJson() {
var textarea = document.getElementById('wec-json');
textarea.select();
try {
document.execCommand('copy');
alert('Copied to clipboard!');
} catch (e) {
alert('Copy failed. Please select and copy manually.');
}
}
function saveToUserPage() {
var username = mw.config.get('wgUserName');
if (!username) {
alert('You must be logged in to save.');
return;
}
var json = $('#wec-json').val();
if (!json || json.indexOf('{') !== 0) {
alert('Please generate JSON first!');
return;
}
var api = new mw.Api();
api.postWithToken('csrf', {
action: 'edit',
title: 'User:' + username + '/Data/WikiEditorToolbarConfig.json',
text: json,
summary: 'Updated WikiEditor button configuration',
contentmodel: 'json'
}).done(function() {
alert('Configuration saved successfully!');
}).fail(function() {
alert('Save failed. Please try again.');
});
}
function loadExistingConfig() {
var username = mw.config.get('wgUserName');
if (!username) return;
var api = new mw.Api();
api.get({
action: 'query',
titles: 'User:' + username + '/Data/WikiEditorToolbarConfig.json',
prop: 'revisions',
rvprop: 'content',
rvslots: 'main',
formatversion: 2
}).done(function(data) {
if (data.query && data.query.pages && data.query.pages[0] && !data.query.pages[0].missing) {
try {
var content = data.query.pages[0].revisions[0].slots.main.content;
var config = JSON.parse(content);
if (config.buttons) {
// Unescape newlines for editing
buttons = config.buttons.map(function(btn) {
['insertBefore', 'insertAfter', 'sampleText'].forEach(function(field) {
if (btn[field]) {
btn[field] = btn[field].replace(/\\n/g, '\n');
}
});
return btn;
});
updateButtonList();
}
if (config.defaults) {
defaults = config.defaults;
}
} catch (e) {
console.warn('Could not parse existing configuration:', e);
}
}
});
}
// Initialize
$(function() {
init();
});
}(jQuery, mediaWiki));