MediaWiki:Gadget-PageCreator.js
Appearance
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
// Page Creator
// SHARED CODEBASE
function fetchTemplateList(callback, errorCallback) {
var api = new mw.Api();
api.parse('{{#invoke:TemplateStarter|listTemplates}}')
.done(function(html) {
var tempDiv = document.createElement('div');
tempDiv.innerHTML = html;
var templateListText = tempDiv.textContent || tempDiv.innerText || '';
var availableTemplates = templateListText.split(',').map(function(t) {
return t.trim().split('\n')[0].trim();
}).filter(function(t) {
return t.length > 0 && t.match(/^[a-zA-Z][a-zA-Z0-9_\-\s\(\)]*$/);
});
callback(availableTemplates);
})
.fail(function(error) {
if (errorCallback) errorCallback(error);
});
}
function fetchTemplateContent(templateType, callback, errorCallback) {
var api = new mw.Api();
api.parse('{{#invoke:TemplateStarter|preload|' + templateType + '}}')
.done(function(html) {
var tempDiv = document.createElement('div');
tempDiv.innerHTML = html;
var templateContent = tempDiv.textContent || tempDiv.innerText || '';
callback(templateContent);
})
.fail(function(error) {
if (errorCallback) errorCallback(error);
});
}
function cleanupUnfilledPlaceholders(templateContent, pageName, formData) {
var processedContent = templateContent;
if (!pageName || pageName.trim() === '') {
throw new Error('Page name is required and cannot be empty');
}
processedContent = processedContent.replace(/\$PAGE_NAME\$/g, pageName);
for (var key in formData) {
if (key !== 'PAGE_NAME' && formData[key] && formData[key].trim() !== '') {
var placeholder = '$' + key + '$';
var escapedPlaceholder = placeholder.replace(/\$/g, '\\$');
processedContent = processedContent.replace(new RegExp(escapedPlaceholder, 'g'), formData[key]);
}
}
processedContent = processedContent.replace(/\$[A-Z_]+\$/g, '');
processedContent = processedContent.replace(/\s+/g, ' ');
processedContent = processedContent.replace(/is a\s+based/g, 'is based');
processedContent = processedContent.replace(/is a\s+in/g, 'is in');
processedContent = processedContent.replace(/\s+\./g, '.');
processedContent = processedContent.trim();
return processedContent;
}
// HERO PAGE CREATOR WIDGET
mw.loader.using(['mediawiki.api', 'mediawiki.util'], function() {
$(function() {
// Only run if the hero page creator container exists
if ($('#hero-page-creator').length === 0) return;
console.log('Initializing page creator widget');
// Build the form HTML (containers exist, we populate them)
var formHtml =
'<input type="text" id="hero-page-name" class="searchboxInput" placeholder="Page name">' +
'<select id="hero-template-type" class="searchboxInput" disabled>' +
'<option value="">Loading...</option>' +
'</select>' +
'<button id="hero-create-btn" class="mw-ui-button mw-ui-progressive" disabled>Create</button>';
$('#hero-page-creator .mw-inputbox-element').html(formHtml);
// Fetch available templates using shared function
fetchTemplateList(
function(availableTemplates) {
var $select = $('#hero-template-type');
$select.empty().append('<option value="">Select a subject (template)</option>');
availableTemplates.forEach(function(template) {
$select.append('<option value="' + template + '">' + template + '</option>');
});
$select.prop('disabled', false);
},
function() {
$('#hero-template-type').empty().append('<option value="">Error</option>');
}
);
// Enable/disable create button
function updateHeroCreateButton() {
var pageName = $('#hero-page-name').val().trim();
var templateType = $('#hero-template-type').val();
$('#hero-create-btn').prop('disabled', !pageName || !templateType);
}
$('#hero-page-name, #hero-template-type').on('input change', updateHeroCreateButton);
// Handle create button click
$('#hero-create-btn').on('click', function() {
var pageName = $('#hero-page-name').val().trim();
var templateType = $('#hero-template-type').val();
if (!pageName || !templateType) return;
$(this).prop('disabled', true).text('Creating...');
fetchTemplateContent(templateType, function(templateContent) {
try {
var formData = {};
templateContent = cleanupUnfilledPlaceholders(templateContent, pageName, formData);
} catch (error) {
alert('Error: ' + error.message);
$('#hero-create-btn').prop('disabled', false).text('Create');
return;
}
var api = new mw.Api();
var summary = templateType === 'None' ? 'Creating new page' : 'Creating new ' + templateType + ' page';
api.create(pageName, { summary: summary }, templateContent)
.done(function() {
window.location.href = mw.util.getUrl(pageName, { veaction: 'edit' });
})
.fail(function(code) {
if (code === 'articleexists') {
if (confirm('Page already exists. Do you want to edit it?')) {
window.location.href = mw.util.getUrl(pageName, { action: 'edit' });
}
} else {
alert('Error creating page: ' + code);
}
$('#hero-create-btn').prop('disabled', false).text('Create');
});
}, function() {
alert('Error loading template content.');
$('#hero-create-btn').prop('disabled', false).text('Create');
});
});
});
});
// CREATE A PAGE METAPAGE
mw.loader.using(['mediawiki.api', 'mediawiki.util'], function() {
$(function() {
// Only run on pages that have the template creator container
if ($('#template-creator-container').length === 0) return;
console.log('Initializing TemplateStarter');
// Build the initial HTML with loading state
var formHtml =
'<div class="template-creator-main-layout">' +
'<div class="template-creator-form-container">' +
'<h3>Create New Page</h3>' +
'<div class="form-group">' +
'<label for="template-type">Template Type:</label>' +
'<select id="template-type" class="form-control" disabled>' +
'<option value="">Loading templates...</option>' +
'</select>' +
'</div>' +
'<div class="form-group">' +
'<label for="page-name">Page Name:</label>' +
'<input type="text" id="page-name" class="form-control" placeholder="Enter page name...">' +
'</div>' +
'<div id="dynamic-fields-container"></div>' +
'<button id="create-page-btn" class="mw-ui-button mw-ui-progressive" disabled>Create Page</button>' +
'</div>' +
'<div id="template-preview" class="template-creator-preview-container" style="display:none;">' +
'<h4>Preview:</h4>' +
'<pre id="template-preview-content"></pre>' +
'</div>' +
'</div>';
// Insert the form
$('#template-creator-container').html(formHtml);
// Fetch available templates using shared function
fetchTemplateList(
function(availableTemplates) {
var $select = $('#template-type');
$select.empty().append('<option value="">Select a template...</option>');
availableTemplates.forEach(function(template) {
$select.append('<option value="' + template + '">' + template + '</option>');
});
$select.prop('disabled', false);
console.log('Loaded templates dynamically:', availableTemplates);
},
function(error) {
console.error('Failed to load templates dynamically:', error);
$('#template-type').empty().append('<option value="">Error loading templates</option>').prop('disabled', true);
}
);
// Enable/disable create button based on form completion
function updateCreateButton() {
var templateType = $('#template-type').val();
var pageName = $('#page-name').val().trim();
$('#create-page-btn').prop('disabled', !templateType || !pageName);
}
// Update button state when inputs change
$('#template-type, #page-name').on('change input', updateCreateButton);
// Preview template when type is selected
$('#template-type').on('change', function() {
var templateType = $(this).val();
if (!templateType) {
$('#template-preview').hide();
$('#dynamic-fields-container').empty();
return;
}
// Load dynamic fields for this template type
loadDynamicFields(templateType);
updatePreview();
});
// Update preview when page name changes
$('#page-name').on('input', function() {
var templateType = $('#template-type').val();
if (templateType) {
updatePreview();
}
});
// Function to update the preview with processed placeholders
function updatePreview() {
var templateType = $('#template-type').val();
var pageName = $('#page-name').val().trim();
if (!templateType) return;
// Call the Lua module to get template structure
var api = new mw.Api();
api.parse('{{#invoke:TemplateStarter|preload|' + templateType + '}}')
.done(function(html) {
// Extract text content from the parsed HTML
var tempDiv = document.createElement('div');
tempDiv.innerHTML = html;
var templateContent = tempDiv.textContent || tempDiv.innerText || '';
// Collect form data and process placeholders
var formData = collectFormData();
templateContent = processPlaceholders(templateContent, pageName, formData);
$('#template-preview-content').text(templateContent);
$('#template-preview').show();
})
.fail(function(error) {
console.error('Failed to preview template:', error);
$('#template-preview-content').text('Error loading template preview');
$('#template-preview').show();
});
}
// Function to load dynamic fields for a template type
function loadDynamicFields(templateType) {
console.log('Loading dynamic fields for template:', templateType);
var api = new mw.Api();
api.parse('{{#invoke:TemplateStarter|getCreatorFieldDefinitionsJSON|' + templateType + '}}')
.done(function(html) {
// Extract JSON content from the parsed HTML
var tempDiv = document.createElement('div');
tempDiv.innerHTML = html;
var jsonText = tempDiv.textContent || tempDiv.innerText || '';
try {
var fieldDefinitions = JSON.parse(jsonText);
console.log('Field definitions loaded:', fieldDefinitions);
// Generate form fields
generateDynamicFields(fieldDefinitions);
} catch (e) {
console.error('Failed to parse field definitions JSON:', e, jsonText);
$('#dynamic-fields-container').html('<p style="color: red;">Error loading field definitions</p>');
}
})
.fail(function(error) {
console.error('Failed to load field definitions:', error);
$('#dynamic-fields-container').html('<p style="color: red;">Error loading field definitions</p>');
});
}
// Function to generate dynamic form fields
function generateDynamicFields(fieldDefinitions) {
var $container = $('#dynamic-fields-container');
$container.empty();
// Skip PAGE_NAME as it's handled separately
for (var fieldKey in fieldDefinitions) {
if (fieldKey === 'PAGE_NAME') continue;
var fieldDef = fieldDefinitions[fieldKey];
var $formGroup = $('<div class="form-group"></div>');
// Create label
var $label = $('<label></label>')
.attr('for', 'dynamic-field-' + fieldKey)
.text(fieldDef.label);
// Create input
var $input = $('<input type="text" class="form-control dynamic-field">')
.attr('id', 'dynamic-field-' + fieldKey)
.attr('data-field-key', fieldKey)
.attr('placeholder', fieldDef.placeholder);
// Mark required fields
if (fieldDef.required) {
$label.append(' <span style="color: red;">*</span>');
$input.attr('required', true);
}
$formGroup.append($label).append($input);
$container.append($formGroup);
// Add event listener for preview updates
$input.on('input', function() {
var templateType = $('#template-type').val();
if (templateType) {
updatePreview();
}
});
}
// Update create button state when dynamic fields change
$container.on('input', '.dynamic-field', updateCreateButton);
console.log('Dynamic fields generated');
}
// Function to collect form data from dynamic fields
function collectFormData() {
var formData = {};
// Only collect dynamic field values (PAGE_NAME is handled separately)
$('.dynamic-field').each(function() {
var $field = $(this);
var fieldKey = $field.data('field-key');
var value = $field.val().trim();
if (value) {
formData[fieldKey] = value;
}
});
return formData;
}
// Function to process $VARIABLE$ placeholders
function processPlaceholders(templateContent, pageName, formData) {
var processedContent = templateContent;
// Replace $PAGE_NAME$ with the actual page name
if (pageName) {
processedContent = processedContent.replace(/\$PAGE_NAME\$/g, pageName);
}
// Replace other placeholders with form data
for (var key in formData) {
if (key !== 'PAGE_NAME' && formData[key]) {
var placeholder = '$' + key + '$';
// Properly escape the $ characters for regex
var escapedPlaceholder = placeholder.replace(/\$/g, '\\$');
processedContent = processedContent.replace(new RegExp(escapedPlaceholder, 'g'), formData[key]);
}
}
return processedContent;
}
// Handle create button click
$('#create-page-btn').on('click', function() {
var templateType = $('#template-type').val();
var pageName = $('#page-name').val().trim();
if (!templateType || !pageName) {
alert('Please select a template type and enter a page name');
return;
}
// Disable button to prevent double-clicks
$(this).prop('disabled', true).text('Creating...');
fetchTemplateContent(templateType, function(templateContent) {
var formData = collectFormData();
try {
var processedContent = cleanupUnfilledPlaceholders(templateContent, pageName, formData);
} catch (error) {
alert('Error: ' + error.message);
$('#create-page-btn').prop('disabled', false).text('Create Page');
return;
}
var api = new mw.Api();
var summary = templateType === 'None' ? 'Creating new page' : 'Creating new ' + templateType + ' page';
api.create(pageName, { summary: summary }, processedContent)
.done(function() {
window.location.href = mw.util.getUrl(pageName, { veaction: 'edit' });
})
.fail(function(code, error) {
if (code === 'articleexists') {
if (confirm('Page already exists. Do you want to edit it and add the template?')) {
window.location.href = mw.util.getUrl(pageName, { veaction: 'edit' });
}
} else {
console.error('Failed to create page:', code, error);
alert('Error creating page: ' + (error && error.error ? error.error.info : code));
}
$('#create-page-btn').prop('disabled', false).text('Create Page');
});
}, function(error) {
console.error('Failed to get template:', error);
alert('Error loading template. Please try again.');
$('#create-page-btn').prop('disabled', false).text('Create Page');
});
});
console.log('TemplateStarter initialized');
});
});