MediaWiki:Gadget-InfoboxGenerator.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.
/**
* RTC Wiki Infobox Generator
*
* A gadget that provides a form-based interface for generating
* infobox wikitext for computers and peripherals.
*
* Accessed via a toolbar button in the wiki editor or via
* Special:BlankPage/InfoboxGenerator
*/
( function ( mw, $ ) {
'use strict';
// ============================================================
// Field definitions for each infobox type
// ============================================================
var FIELDS = {
computer: [
{ name: 'name', label: 'Model Name', required: true, placeholder: 'e.g. Macintosh IIsi' },
{ name: 'image', label: 'Image filename', placeholder: 'e.g. Macintosh_IIsi.jpg' },
{ name: 'caption', label: 'Image caption', placeholder: 'e.g. Macintosh IIsi' },
{ name: 'logo', label: 'Logo filename (optional)', placeholder: 'e.g. Apple_Logo.svg' },
{ name: 'developer', label: 'Developer (if different from manufacturer)', placeholder: '' },
{ name: 'manufacturer', label: 'Manufacturer', defaultValue: 'Apple Computer, Inc.', placeholder: 'Apple Computer, Inc.' },
{ name: 'type', label: 'Type', defaultValue: 'Personal Computer', placeholder: 'e.g. Personal Computer, All-in-one Personal Computer' },
{ name: 'release_date', label: 'Release Date', placeholder: 'e.g. October 15, 1990' },
{ name: 'discontinued', label: 'Discontinued', placeholder: 'e.g. September 10, 1993' },
{ name: 'price', label: 'Intro Price', placeholder: 'e.g. US$3,769' },
{ name: 'units_sold', label: 'Units Sold (if known)', placeholder: '' },
{ name: 'cpu', label: 'CPU', placeholder: 'e.g. Motorola 68030 @ 20 MHz' },
{ name: 'memory', label: 'Memory / RAM', placeholder: 'e.g. 1 MB RAM (expandable to 17 MB)' },
{ name: 'storage', label: 'Storage', placeholder: 'e.g. 40 MB SCSI HDD, 1.44 MB floppy' },
{ name: 'display', label: 'Display', placeholder: 'e.g. 12" monochrome or 13" color' },
{ name: 'sound', label: 'Sound', placeholder: 'e.g. 8-bit stereo, 22 kHz' },
{ name: 'dimensions', label: 'Dimensions', placeholder: 'e.g. 4.0" H ร 12.3" W ร 14.9" D' },
{ name: 'weight', label: 'Weight', placeholder: 'e.g. 13.5 lbs (6.1 kg)' },
{ name: 'os', label: 'OS / Firmware', placeholder: 'e.g. System 6.0.7 โ Mac OS 7.6.1' },
{ name: 'predecessor', label: 'Predecessor', placeholder: 'e.g. Macintosh IIcx (will auto-link)' },
{ name: 'successor', label: 'Successor', placeholder: 'e.g. Macintosh IIvx (will auto-link)' },
{ name: 'codename', label: 'Codename', placeholder: 'e.g. Oceanic' },
{ name: 'model', label: 'Model Number', placeholder: 'e.g. M0360' }
],
peripheral: [
{ name: 'name', label: 'Product Name', required: true, placeholder: 'e.g. Apple Extended Keyboard II' },
{ name: 'image', label: 'Image filename', placeholder: 'e.g. Apple_Extended_Keyboard_II.jpg' },
{ name: 'caption', label: 'Image caption', placeholder: 'e.g. Apple Extended Keyboard II' },
{ name: 'manufacturer', label: 'Manufacturer', defaultValue: 'Apple Computer, Inc.', placeholder: 'Apple Computer, Inc.' },
{ name: 'type', label: 'Type', placeholder: 'e.g. Keyboard, Pointing device, Monitor' },
{ name: 'release_date', label: 'Release Date', placeholder: 'e.g. March 1990' },
{ name: 'discontinued', label: 'Discontinued', placeholder: 'e.g. 1999' },
{ name: 'price', label: 'Price', placeholder: 'e.g. US$163' },
{ name: 'interface', label: 'Interface', placeholder: 'e.g. ADB, SCSI, USB' },
{ name: 'compatible', label: 'Compatible Systems', placeholder: 'e.g. All ADB-equipped Macintosh computers' },
{ name: 'connectivity', label: 'Connectivity', placeholder: 'e.g. 1ร ADB pass-through port' },
{ name: 'dimensions', label: 'Dimensions', placeholder: 'e.g. 457 mm L ร 152 mm W ร 32 mm H' },
{ name: 'weight', label: 'Weight', placeholder: 'e.g. 1.1 kg (2.4 lbs)' },
{ name: 'predecessor', label: 'Predecessor', placeholder: 'e.g. Apple Extended Keyboard (will auto-link)' },
{ name: 'successor', label: 'Successor', placeholder: 'e.g. Apple Keyboard (will auto-link)' },
{ name: 'model', label: 'Model Number(s)', placeholder: 'e.g. M3501' }
]
};
// ============================================================
// Auto-linking helper for predecessor/successor
// ============================================================
function autoLink( value ) {
if ( !value ) {
return '';
}
// Already wikilinked
if ( value.indexOf( '[[' ) !== -1 ) {
return value;
}
return '[[' + value + ']]';
}
// ============================================================
// Format image field correctly
// ============================================================
function formatImage( filename, width ) {
if ( !filename ) {
return '';
}
// Strip any existing File: prefix or [[ ]]
filename = filename.replace( /^\[\[(?:File:|Image:)?/i, '' ).replace( /\]\]$/, '' );
// Remove any sizing suffix the user might have typed
filename = filename.replace( /\|\d+px.*$/, '' );
if ( width ) {
return '[[File:' + filename + '|' + width + ']]';
}
return '[[File:' + filename + ']]';
}
// ============================================================
// Generate the infobox wikitext
// ============================================================
function generateInfobox( type, values ) {
var templateName = ( type === 'computer' ) ? 'Infobox computer' : 'Infobox computer peripheral';
var fields = FIELDS[ type ];
var lines = [ '{{' + templateName ];
fields.forEach( function ( field ) {
var val = ( values[ field.name ] || '' ).trim();
if ( !val ) {
return;
}
// Special formatting
if ( field.name === 'image' ) {
if ( type === 'computer' ) {
val = formatImage( val, '250px' );
} else {
val = formatImage( val );
}
} else if ( field.name === 'logo' ) {
val = formatImage( val, '90px' );
} else if ( field.name === 'predecessor' || field.name === 'successor' ) {
val = autoLink( val );
}
// Use the correct parameter name (some use underscore, some use space)
var paramName = field.name;
if ( type === 'computer' && field.name === 'release_date' ) {
paramName = 'release date';
}
// Pad for alignment
var pad = ' '.substring( 0, Math.max( 1, 14 - paramName.length ) );
lines.push( '| ' + paramName + pad + '= ' + val );
} );
lines.push( '}}' );
return lines.join( '\n' );
}
// ============================================================
// Build the form UI
// ============================================================
function buildForm( $container ) {
$container.empty();
var $wrapper = $( '<div>' )
.attr( 'id', 'rtc-infobox-gen' )
.css( {
'max-width': '800px',
'margin': '0 auto',
'font-family': 'sans-serif'
} );
// Title
$wrapper.append(
$( '<h2>' ).text( 'RTC Wiki Infobox Generator' ),
$( '<p>' ).css( 'color', '#555' ).text(
'Fill in the fields below to generate infobox wikitext. Only filled fields will be included.'
)
);
// Type selector
var $typeSelector = $( '<div>' ).css( { 'margin-bottom': '20px' } );
$typeSelector.append( $( '<label>' ).css( 'font-weight', 'bold' ).text( 'Infobox type: ' ) );
var $select = $( '<select>' )
.attr( 'id', 'rtc-infobox-type' )
.css( { 'padding': '6px 12px', 'font-size': '14px', 'margin-left': '8px' } );
$select.append(
$( '<option>' ).val( 'computer' ).text( 'Computer (Macintosh, Apple II, etc.)' ),
$( '<option>' ).val( 'peripheral' ).text( 'Peripheral / Accessory (Mouse, Keyboard, etc.)' )
);
$typeSelector.append( $select );
$wrapper.append( $typeSelector );
// Form fields container
var $fieldsContainer = $( '<div>' ).attr( 'id', 'rtc-infobox-fields' );
$wrapper.append( $fieldsContainer );
// Buttons
var $buttons = $( '<div>' ).css( { 'margin-top': '20px', 'margin-bottom': '20px' } );
var $generateBtn = $( '<button>' )
.text( 'Generate Infobox' )
.css( {
'padding': '10px 24px',
'font-size': '14px',
'font-weight': 'bold',
'background-color': '#3366cc',
'color': '#fff',
'border': 'none',
'border-radius': '4px',
'cursor': 'pointer',
'margin-right': '10px'
} );
var $clearBtn = $( '<button>' )
.text( 'Clear All' )
.css( {
'padding': '10px 24px',
'font-size': '14px',
'background-color': '#ccc',
'color': '#333',
'border': 'none',
'border-radius': '4px',
'cursor': 'pointer',
'margin-right': '10px'
} );
var $insertBtn = $( '<button>' )
.text( 'Insert into Editor' )
.css( {
'padding': '10px 24px',
'font-size': '14px',
'font-weight': 'bold',
'background-color': '#2d8e36',
'color': '#fff',
'border': 'none',
'border-radius': '4px',
'cursor': 'pointer',
'display': 'none'
} );
$buttons.append( $generateBtn, $clearBtn, $insertBtn );
$wrapper.append( $buttons );
// Output area
var $output = $( '<div>' ).attr( 'id', 'rtc-infobox-output' ).css( { 'display': 'none' } );
$output.append(
$( '<h3>' ).text( 'Generated Wikitext' ),
$( '<textarea>' )
.attr( { 'id': 'rtc-infobox-result', 'rows': 20, 'readonly': true } )
.css( {
'width': '100%',
'font-family': 'monospace',
'font-size': '13px',
'padding': '10px',
'background': '#f5f5f5',
'border': '1px solid #ccc',
'border-radius': '4px',
'box-sizing': 'border-box'
} ),
$( '<p>' )
.attr( 'id', 'rtc-infobox-copy-msg' )
.css( { 'color': '#2d8e36', 'font-weight': 'bold', 'display': 'none' } )
.text( 'โ Copied to clipboard!' )
);
$wrapper.append( $output );
// Preview area
var $preview = $( '<div>' ).attr( 'id', 'rtc-infobox-preview' ).css( { 'display': 'none', 'margin-top': '20px' } );
$preview.append(
$( '<h3>' ).text( 'Preview' ),
$( '<div>' ).attr( 'id', 'rtc-infobox-preview-content' ).css( {
'border': '1px solid #ccc',
'padding': '15px',
'background': '#fff',
'border-radius': '4px'
} )
);
$wrapper.append( $preview );
$container.append( $wrapper );
// Render fields for the initial type
renderFields( 'computer', $fieldsContainer );
// Event: type change
$select.on( 'change', function () {
renderFields( $( this ).val(), $fieldsContainer );
$output.hide();
$preview.hide();
$insertBtn.hide();
} );
// Event: generate
$generateBtn.on( 'click', function () {
var type = $select.val();
var values = collectValues( type );
var wikitext = generateInfobox( type, values );
$( '#rtc-infobox-result' ).val( wikitext );
$output.show();
$insertBtn.show();
// Copy to clipboard
navigator.clipboard.writeText( wikitext ).then( function () {
$( '#rtc-infobox-copy-msg' ).show().delay( 2000 ).fadeOut();
} );
// Preview via API
var api = new mw.Api();
api.post( {
action: 'parse',
text: wikitext,
contentmodel: 'wikitext',
prop: 'text',
disablelimitreport: true
} ).done( function ( data ) {
$( '#rtc-infobox-preview-content' ).html( data.parse.text['*'] );
$preview.show();
} );
} );
// Event: clear
$clearBtn.on( 'click', function () {
$fieldsContainer.find( 'input' ).val( '' );
$output.hide();
$preview.hide();
$insertBtn.hide();
} );
// Event: insert into editor
$insertBtn.on( 'click', function () {
var wikitext = $( '#rtc-infobox-result' ).val();
insertIntoEditor( wikitext );
} );
}
// ============================================================
// Render form fields for a given type
// ============================================================
function renderFields( type, $container ) {
$container.empty();
var fields = FIELDS[ type ];
fields.forEach( function ( field ) {
var $row = $( '<div>' ).css( {
'margin-bottom': '10px',
'display': 'flex',
'align-items': 'center'
} );
var $label = $( '<label>' )
.text( field.label + ( field.required ? ' *' : '' ) )
.css( {
'width': '200px',
'font-weight': field.required ? 'bold' : 'normal',
'flex-shrink': '0'
} );
var $input = $( '<input>' )
.attr( {
type: 'text',
'data-field': field.name,
placeholder: field.placeholder || ''
} )
.val( field.defaultValue || '' )
.css( {
'flex': '1',
'padding': '6px 10px',
'font-size': '14px',
'border': '1px solid #ccc',
'border-radius': '4px'
} );
$row.append( $label, $input );
$container.append( $row );
} );
}
// ============================================================
// Collect values from the form
// ============================================================
function collectValues( type ) {
var values = {};
$( '#rtc-infobox-fields input' ).each( function () {
var name = $( this ).attr( 'data-field' );
var val = $( this ).val();
if ( val ) {
values[ name ] = val;
}
} );
return values;
}
// ============================================================
// Insert text into the wiki editor
// ============================================================
function insertIntoEditor( wikitext ) {
// Try WikiEditor / CodeMirror textarea
var $textarea = $( '#wpTextbox1' );
if ( $textarea.length ) {
var existing = $textarea.val();
// Insert at cursor position or at the start (infobox goes first)
$textarea.val( wikitext + '\n\n' + existing );
$textarea.trigger( 'change' );
mw.notify( 'Infobox inserted at the top of the article.', { type: 'success' } );
return;
}
// Fallback: copy to clipboard
navigator.clipboard.writeText( wikitext ).then( function () {
mw.notify( 'Copied to clipboard. Paste it into the editor.', { type: 'success' } );
} );
}
// ============================================================
// Add toolbar button in the editor
// ============================================================
function addEditorButton() {
var action = mw.config.get( 'wgAction' );
if ( action !== 'edit' && action !== 'submit' ) {
return;
}
// Wait for the toolbar to load
mw.hook( 'wikiEditor.toolbarReady' ).add( function ( $textarea ) {
$textarea.wikiEditor( 'addToToolbar', {
section: 'main',
group: 'insert',
tools: {
'infobox-generator': {
label: 'Infobox Generator',
type: 'button',
oouiIcon: 'table',
action: {
type: 'callback',
execute: function () {
openDialog();
}
}
}
}
} );
} );
}
// ============================================================
// Open the generator as a dialog
// ============================================================
function openDialog() {
var $overlay = $( '<div>' ).css( {
'position': 'fixed',
'top': '0',
'left': '0',
'right': '0',
'bottom': '0',
'background': 'rgba(0,0,0,0.5)',
'z-index': '10000',
'overflow-y': 'auto'
} );
var $dialog = $( '<div>' ).css( {
'background': '#fff',
'margin': '40px auto',
'padding': '30px',
'max-width': '850px',
'border-radius': '8px',
'position': 'relative'
} );
var $closeBtn = $( '<button>' )
.text( 'โ' )
.css( {
'position': 'absolute',
'top': '10px',
'right': '15px',
'font-size': '20px',
'background': 'none',
'border': 'none',
'cursor': 'pointer',
'color': '#666'
} )
.on( 'click', function () {
$overlay.remove();
} );
$dialog.append( $closeBtn );
$overlay.append( $dialog );
$( 'body' ).append( $overlay );
buildForm( $dialog );
// Close on overlay click
$overlay.on( 'click', function ( e ) {
if ( e.target === $overlay[ 0 ] ) {
$overlay.remove();
}
} );
}
// ============================================================
// Special page mode: Special:BlankPage/InfoboxGenerator
// ============================================================
function initSpecialPage() {
if (
mw.config.get( 'wgCanonicalSpecialPageName' ) === 'Blankpage' &&
window.location.href.indexOf( 'InfoboxGenerator' ) !== -1
) {
var $content = $( '#mw-content-text' );
$content.empty();
document.title = 'Infobox Generator โ ' + mw.config.get( 'wgSiteName' );
$( '#firstHeading' ).text( 'Infobox Generator' );
buildForm( $content );
}
}
// ============================================================
// Portlet link in sidebar
// ============================================================
function addSidebarLink() {
var link = mw.util.addPortletLink(
'p-tb',
mw.util.getUrl( 'Special:BlankPage/InfoboxGenerator' ),
'Infobox Generator',
't-infobox-generator',
'Generate infobox wikitext for computers and peripherals'
);
}
// ============================================================
// Init
// ============================================================
$( function () {
addSidebarLink();
addEditorButton();
initSpecialPage();
} );
}( mediaWiki, jQuery ) );