Jump to content

MediaWiki:Gadget-InfoboxGenerator.js

From RetroTechCollection

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 ) );