User:Monster Domosed/common.js: Difference between revisions

No edit summary
Tag: Manual revert
testing
Line 1: Line 1:
/*  Patch Formatter */
( function () {
'use strict';


// ---- CONFIG ---------------------------------------------------------
var GAME_MODE_SECTIONS = [ 'Street Brawl' ];
// (RegExp, replacement) — generic terms turned into wiki links.
var WIKI_LINKS = [
[ /(?<!\w)Urn(?!\w)/g, '[[Urn]]' ],
[ /(?<!\w)parry(?!\w)/g, '[[Parry|parry]]' ]
];
var CHANGE_PREFIX = '';
// ---- API ------------------------------------------------------------
function getWikiData( templateName ) {
var api = new mw.Api();
return api.get( {
action: 'expandtemplates',
text: '#invoke:MonsterDomosed/patchformatter|' + templateName + '',
prop: 'wikitext',
format: 'json',
formatversion: 2
} ).then( function ( data ) {
var wikitext = data.expandtemplates.wikitext || '';
return wikitext.split( ',' )
.map( function ( item ) { return item.trim().replace( /^'+|'+$/g, '' ); } )
.filter( function ( item ) { return item.length > 0; } );
} );
}
// ---- PARSING --------------------------------------------------------
function parseEntries( rawText ) {
var entries = [];
var current = null;
rawText.split( '\n' ).forEach( function ( line ) {
var stripped = line.trim();
if ( stripped.indexOf( '-' ) === 0 ) {
if ( current !== null ) { entries.push( current ); }
current = stripped.slice( 1 ).trim();
} else if ( current !== null ) {
current += ' ' + stripped; // continuation of previous bullet
}
} );
if ( current !== null ) { entries.push( current ); }
return entries
.map( function ( e ) { return e.replace( /\s+/g, ' ' ).trim(); } )
.filter( function ( e ) { return e.length > 0; } );
}
function classify( entry, heroes, items, gameModes ) {
var idx = entry.indexOf( ':' );
if ( idx !== -1 ) {
var prefix = entry.slice( 0, idx ).trim();
var rest = entry.slice( idx + 1 ).trim();
if ( gameModes.indexOf( prefix ) !== -1 ) { return [ 'mode', prefix, rest ]; }
if ( heroes.indexOf( prefix ) !== -1 ) { return [ 'hero', prefix, rest ]; }
if ( items.indexOf( prefix ) !== -1 ) { return [ 'item', prefix, rest ]; }
}
return [ 'general', null, entry ];
}
// ---- ICON / LINK WRAPPING ------------------------------------------
function escapeRegExp( s ) {
return s.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' );
}
function formatBody( text, heroes, abilities, items ) {
var protectedTokens = [];
function protect( value ) {
var token = '\u0000' + protectedTokens.length + '\u0000';
protectedTokens.push( value );
return token;
}
function wrap( terms, template ) {
// Longest names first so multi-word names win.
terms.slice().sort( function ( a, b ) { return b.length - a.length; } )
.forEach( function ( term ) {
var pattern = new RegExp( '(?<!\\w)' + escapeRegExp( term ) + '(?!\\w)', 'g' );
text = text.replace( pattern, function () {
return protect( template.replace( '%s', function () { return term; } ) );
} );
} );
}
wrap( abilities, 'AbilityIcon|%s' );
wrap( items, 'ItemIcon|%s' );
wrap( heroes, 'HeroIcon|%s' );
WIKI_LINKS.forEach( function ( pair ) {
text = text.replace( pair[ 0 ], function () { return protect( pair[ 1 ] ); } );
} );
for ( var i = 0; i < protectedTokens.length; i++ ) {
text = text.split( '\u0000' + i + '\u0000' ).join( protectedTokens[ i ] );
}
return text;
}
// ---- OUTPUT BUILDING ------------------------------------------------
function buildOutput( entries, heroes, abilities, items, gameModes ) {
var general = [];
var modes = {}, modeOrder = [];
var itemSections = {}, itemOrder = [];
var heroSections = {}, heroOrder = [];
entries.forEach( function ( entry ) {
var c = classify( entry, heroes, items, gameModes );
var kind = c[ 0 ], name = c[ 1 ];
var body = formatBody( c[ 2 ], heroes, abilities, items );
if ( kind === 'general' ) {
general.push( body );
} else if ( kind === 'mode' ) {
if ( !modes[ name ] ) { modes[ name ] = []; modeOrder.push( name ); }
modes[ name ].push( body );
} else if ( kind === 'item' ) {
if ( !itemSections[ name ] ) { itemSections[ name ] = []; itemOrder.push( name ); }
itemSections[ name ].push( body );
} else if ( kind === 'hero' ) {
if ( !heroSections[ name ] ) { heroSections[ name ] = []; heroOrder.push( name ); }
heroSections[ name ].push( body );
}
} );
var out = [];
// General changes
general.forEach( function ( entry ) { out.push( '* ' + entry ); } );
// Game mode sections
modeOrder.forEach( function ( mode ) {
out.push( '', '== [[' + mode + ']] ==' );
modes[ mode ].forEach( function ( body ) { out.push( '* ' + body ); } );
} );
// Items
if ( itemOrder.length ) {
out.push( '', '== Items ==' );
itemOrder.forEach( function ( item ) {
out.push( '=== ItemIcon|' + item + ' ===' );
itemSections[ item ].forEach( function ( body ) {
out.push( CHANGE_PREFIX + body );
out.push( '' );
} );
} );
}
// Heroes
if ( heroOrder.length ) {
out.push( '', '== Heroes ==', '' );
heroOrder.forEach( function ( hero ) {
out.push( '=== HeroIcon|' + hero + ' ===' );
heroSections[ hero ].forEach( function ( body ) {
out.push( CHANGE_PREFIX + body );
out.push( '' );
} );
} );
}
return out.join( '\n' ).trim() + '\n';
}
// ---- UI -------------------------------------------------------------
function init() {
var app = document.getElementById( 'patchformatter-app' );
if ( !app ) {
return;
}
app.innerHTML =
'<p><strong>Raw patch notes</strong></p>' +
'<textarea id="pf-input" rows="14" style="width:100%;font-family:monospace;"></textarea>' +
'<p style="margin-top:8px;">' +
'<button id="pf-run" type="button">Format</button> ' +
'<button id="pf-copy" type="button">Copy output</button>' +
'<span id="pf-status" style="margin-left:10px;"></span>' +
'</p>' +
'<p><strong>Formatted wikitext</strong></p>' +
'<textarea id="pf-output" rows="20" style="width:100%;font-family:monospace;" readonly></textarea>';
var input = document.getElementById( 'pf-input' );
var output = document.getElementById( 'pf-output' );
var runBtn = document.getElementById( 'pf-run' );
var copyBtn = document.getElementById( 'pf-copy' );
var status = document.getElementById( 'pf-status' );
var listsPromise = null;
function loadLists() {
if ( !listsPromise ) {
listsPromise = Promise.all( [
getWikiData( 'get_released_heroes_list' ),
getWikiData( 'get_all_released_item_names' ),
getWikiData( 'get_all_released_abilities_list' )
] );
}
return listsPromise;
}
runBtn.addEventListener( 'click', function () {
status.textContent = 'Loading wiki data...';
runBtn.disabled = true;
loadLists().then( function ( lists ) {
var heroes = lists[ 0 ];
var items = lists[ 1 ];
var abilities = lists[ 2 ];
var entries = parseEntries( input.value );
output.value = buildOutput(
entries,
heroes,
abilities,
items,
GAME_MODE_SECTIONS
);
status.textContent =
'Processed ' + entries.length +
' entries (' +
heroes.length + ' heroes, ' +
abilities.length + ' abilities, ' +
items.length + ' items).';
} ).catch( function ( err ) {
status.textContent = 'Error: ' + err;
} ).then( function () {
runBtn.disabled = false;
} );
} );
copyBtn.addEventListener( 'click', function () {
if ( navigator.clipboard ) {
navigator.clipboard.writeText( output.value ).then( function () {
status.textContent = 'Copied.';
} );
} else {
output.select();
document.execCommand( 'copy' );
status.textContent = 'Copied.';
}
} );
}
mw.loader.using( [ 'mediawiki.api', 'mediawiki.util' ] ).then( function () {
$( init );
} );
}() );